4.5 路径MTU发现

本文探讨了TCP路径MTU发现的重要性,介绍了何时执行PMTU发现,详细解析了TCP如何进行PMTU探测,包括探测包的发送、接收ACK、ICMP报文的情况以及探测结果的维护和应用。此外,还解释了为何所有TCP报文的IP头部都会设置DF位。
摘要由CSDN通过智能技术生成

  如果两台主机之间的通信要通过多个网络,那么每个网络的链路层就可能有不同的MTU。两台通信主机路径中的最小MTU。它被称作路径MTU(PMTU)。两台主机之间的路径MTU不一定是个常数,它取决于当时所选择的路由。而选路不一定是对称的(从A到B的路由可能与从B到A的路由不同),因此路径MTU在两个方向上不一定是一致的。

  本文研究路径MTU发现主要是要弄明白以下几个问题:

1、路径MTU发现有什么用处?

2、TCP什么时候执行路径MTU发现?

3、TCP路径MTU发现的原理是什么?

4、TCP路径MTU探测的结果是如何维护的?

5、TCP如何使用路径MTU探测的结果?

  下面回答问题1:TCP报文需要封装成IP报文才会发送,报文在网络中按照一定路径传输后会抵达目的地。最理想的情况是IP报文的大小正好是这条路径所能容纳的最大尺寸,因为报文小了则数据传输效率不高,大了则会引起分片。分片会使得路由器的负担加重,增加延迟,而且会增加报文丢失的概率。而IP报文的传输路径是事先不知道的,而且在传输过程中也可能发送变化,所以TCP需要动态测路径MTU的大小,这就是TCP的路径MTU发现。

     接下来我们来寻找问题2的答案:PMTU探测包的发送是在tcp_write_xmit函数中进行:

1811 static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
1812                int push_one, gfp_t gfp)
1813 {               
1814     struct tcp_sock *tp = tcp_sk(sk);
1815     struct sk_buff *skb;
1816     unsigned int tso_segs, sent_pkts;
1817     int cwnd_quota;
1818     int result;
1819             
1820     sent_pkts = 0;
1821         
1822     if (!push_one) {
1823         /* Do MTU probing. */
1824         result = tcp_mtu_probe(sk);
1825         if (!result) {
1826             return false;
...

  可见只有tcp_write_xmit函数的参数push_one为0时TCP才会开启PMTU探测。直接调用tcp_write_xmit且push_one为0的函数有两个:tcp_tsq_handler和__tcp_push_pending_frames。前者是使用TSQ tasklet发送数据时调用的函数,而直接或间接调用后者的函数有:tcp_push_pending_frames、tcp_push、tcp_data_snd_check。下面一一列举开启PMTU探测的条件:

(1)TSQ tasklet发送数据时:

 684 static void tcp_tsq_handler(struct sock *sk)
 685 {
 686     if ((1 << sk->sk_state) &
 687         (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_CLOSING |
 688          TCPF_CLOSE_WAIT  | TCPF_LAST_ACK))
 689         tcp_write_xmit(sk, tcp_current_mss(sk), 0, 0, GFP_ATOMIC);
 690 }
(2)通过发包系统调用或使用TCP Splice功能调用do_tcp_sendpages发送数据时:

1016 int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
1017         size_t size)
1018 {
...
1204             if (forced_push(tp)) {
1205                 tcp_mark_push(tp, skb);
1206                 __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
1207             } else if (skb == tcp_send_head(sk))
1208                 tcp_push_one(sk, mss_now);
1209             continue;
1210 
1211 wait_for_sndbuf:
1212             set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
1213 wait_for_memory:
1214             if (copied)
1215                 tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
1216 
1217             if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
1218                 goto do_error;
1219 
1220             mss_now = tcp_send_mss(sk, &size_goal, flags);
1221         }
1222     }
1223 
1224 out:
1225     if (copied)
1226         tcp_push(sk, flags, mss_now, tp->nonagle);
1227     release_sock(sk);
1228     return copied + copied_syn;
   TCP Splice
 827 static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
 828                 size_t size, int flags)
 829 {
...
 914         if (forced_push(tp)) {
 915             tcp_mark_push(tp, skb);
 916             __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
 917         } else if (skb == tcp_send_head(sk))
 918             tcp_push_one(sk, mss_now);
 919         continue;
 920 
 921 wait_for_sndbuf:
 922         set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
 923 wait_for_memory:
 924         tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
 925 
 926         if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
 927             goto do_error;
 928 
 929         mss_now = tcp_send_mss(sk, &size_goal, flags);
 930     }
 931 
 932 out:
 933     if (copied && !(flags & MSG_SENDPAGE_NOTLAST))
 934         tcp_push(sk, flags, mss_now, tp->nonagle);
 935     return copied;
...
  这里只有一种情况是不开启PMTU探测的:当前已写出的字节数不大于对端通告的最大窗口的一半且发送队列中只有一个skb。其它情况下发送skb都会开启PMTU探测功能。
(3)发现数据丢失并且使用了Forward RTO-Recovery (F-RTO)算法时:

2685 static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack)
2686 {   
2687     struct inet_connection_sock *icsk = inet_csk(sk);
2688     struct tcp_sock *tp = tcp_sk(sk);
2689     bool recovered = !before(tp->snd_una, tp->high_seq);
2690     
2691     if (tp->frto) { /* F-RTO RFC5682 sec 3.1 (sack enhanced version). */
2692         if (flag & FLAG_ORIG_SACK_ACKED) {
2693             /* Step 3.b. A timeout is spurious if not all data are
2694              * lost, i.e., never-retransmitted data are (s)acked.
2695              */
2696             tcp_try_undo_loss(sk, true);
2697             return;
2698         }
2699         if (after(tp->snd_nxt, tp->high_seq) &&
2700             (flag & FLAG_DATA_SACKED || is_dupack)) {
2701             tp->frto = 0; /* Loss was real: 2nd part of step 3.a */
2702         } else if (flag & FLAG_SND
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值