/* Compute the current effective MSS, taking SACKs and IP options,
* and even PMTU discovery events into account.
*
* LARGESEND note: !urg_mode is overkill, only frames up to snd_up
* cannot be large. However, taking into account rare use of URG, this
* is not a big flaw.
*/
unsigned int tcp_current_mss(struct sock *sk, int large_allowed)
{
struct tcp_sock *tp = tcp_sk(sk);
struct dst_entry *dst = __sk_dst_get(sk);
//下面最终决策出的当前有效发送MSS值会记录到该变量中
u32 mss_now;
u16 xmit_size_goal;
int doing_tso = 0;
//mss_now来自于tp->mss_cache,一脸懵逼,到目前为止还没见过该字段(见下文)
mss_now = tp->mss_cache;
//判断是否允许TSO,忽略
if (large_allowed && sk_can_gso(sk) && !tp->urg_mode)
doing_tso = 1;
if (dst) {
//获取路由中保存的PMTU值
u32 mtu = dst_mtu(dst);
//icsk_pmut_cookie为上次缓存的PMTU值,其初始值为本端MTU大小,
//如果二者不等,则说明PMTU发生了变化,需要调用tcp_sync_mss()更新MSS
if (mtu != inet_csk(sk)->icsk_pmtu_cookie)
mss_now = tcp_sync_mss(sk, mtu);
}
//如果TCP有SACK选项,则从MSS中减去相应的开销
if (tp->rx_opt.eff_sacks)
mss_now -= (TCPOLEN_SACK_BASE_ALIGNED + (tp->rx_opt.eff_sacks * TCPOLEN_SACK_PERBLOCK));
#ifdef CONFIG_TCP_MD5SIG
if (tp->af_specific->md5_lookup(sk, sk))
mss_now -= TCPOLEN_MD5SIG_ALIGNED;
#endif
//下面的代码用来确定xmit_size_goal的值,该值和TSO相关,先忽略
xmit_size_goal = mss_now;
if (doing_tso) {
xmit_size_goal = (65535 -
inet_csk(sk)->icsk_af_ops->net_header_len -
inet_csk(sk)->icsk_ext_hdr_len -
tp->tcp_header_len);
xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal);
xmit_size_goal -= (xmit_size_goal % mss_now);
}
tp->xmit_size_goal = xmit_size_goal;
//返回当前有效的MSS值
return mss_now;
}
/* This function synchronize snd mss to current pmtu/exthdr set.
tp->rx_opt.user_mss is mss set by user by TCP_MAXSEG. It does NOT counts
for TCP options, but includes only bare TCP header.
tp->rx_opt.mss_clamp is mss negotiated at connection setup.
It is minimum of user_mss and mss received with SYN.
It also does not include TCP options.
inet_csk(sk)->icsk_pmtu_cookie is last pmtu, seen by this function.
tp->mss_cache is current effective sending mss, including
all tcp options except for SACKs. It is evaluated,
taking into account current pmtu, but never exceeds
tp->rx_opt.mss_clamp.
NOTE1. rfc1122 clearly states that advertised MSS
DOES NOT include either tcp or ip options.
NOTE2. inet_csk(sk)->icsk_pmtu_cookie and tp->mss_cache
are READ ONLY outside this function. --ANK (980731)
*/
//注释很重要,仔细看
unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
int mss_now;
if (icsk->icsk_mtup.search_high > pmtu)
icsk->icsk_mtup.search_high = pmtu;
//根据PMTU值计算MSS,这里除了标准的IP、TCP首部外,还会考虑IP选项、TCP选项
mss_now = tcp_mtu_to_mss(sk, pmtu);
//调整MSS为当前发送窗口的一半
mss_now = tcp_bound_to_half_wnd(tp, mss_now);
/* And store cached results */
//将PMTU缓存到icsk_pmtu_cookie中
icsk->icsk_pmtu_cookie = pmtu;
if (icsk->icsk_mtup.enabled)
mss_now = min(mss_now, tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_low));
//最终决策出来的MSS保存在mss_cache中
tp->mss_cache = mss_now;
return mss_now;
}
/* Not accounting for SACKs here. */
int tcp_mtu_to_mss(struct sock *sk, int pmtu)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
int mss_now;
/* Calculate base mss without TCP options:
It is MMS_S - sizeof(tcphdr) of rfc1122
*/
//从MTU中减去标准TCP首部、IP首部
mss_now = pmtu - icsk->icsk_af_ops->net_header_len - sizeof(struct tcphdr);
/* Clamp it (mss_clamp does not include tcp options) */
//MSS不能超过对端通告的MSS
if (mss_now > tp->rx_opt.mss_clamp)
mss_now = tp->rx_opt.mss_clamp;
/* Now subtract optional transport overhead */
//减去扩展首部,启用IPsec时,会有扩展首部
mss_now -= icsk->icsk_ext_hdr_len;
/* Then reserve room for full set of TCP options and 8 bytes of data */
//MSS最小不能小于48字节
if (mss_now < 48)
mss_now = 48;
/* Now subtract TCP options size, not including SACKs */
//减去TCP选项长度(不包括选择ACK选项),tp->tcp_header_len的值是该TCP连接中可能的最大TCP
//首部长度,该值是在三次握手过程中根据双方对TCP选项的支持情况确定的
mss_now -= tp->tcp_header_len - sizeof(struct tcphdr);
return mss_now;
}