精华内容
下载资源
问答
  • 图解TCP/IP:路径MTU发现
    千次阅读
    2019-09-18 10:09:44

    数据链路不同,MTU则相异

    每个数据链路的最大传输单元(MTU)都不尽相同,是因为每个不同类型的数据链路的使用目的不同。使用目的不同,可承载的MTU也就不同。鉴于IP属于数据链路上一层,它必须不受限于不同数据链路的MTU大小。

    # 各种数据链路及其MTU
    IP的最大MTU | IP over ATM | FDDI | 以太网 | PPP(Default) | IEEE802.3 Ethernet
       65535         9180       4352   1500        1500              1492
    

    IP报文的分片与重组

    任何一台主机都有必要对IP分片(IP Fragmentation)进行相应的处理,经过分片之后的IP数据在被重组的时候只能由目标主机进行。路由器虽然做分片但不会进行重组。

    发送主机:设置一个唯一数字作为IP首部的标识码发送
    路由器:路由器负责进行分片
    接收主机:参考IP首部的识别码进行重组,传给上层
    
    IP首部中的"片偏移"字段表示分片之后在用户数据中的相对位置和该分片之后是否还有后续其他分片。
    根据这个字段可以判断一个IP数据报是否分片以及当前分片为整个数据报的起始、中段还是末尾。
    

    路径MTU发现

    分片机制也有它的不足:

    • 路由器的处理负荷加重

    • 随着人们对网络安全的要求提高路由器需要做的其他处理也越来越多

    • 在分片处理中一旦某个分片丢失则会造成整个IP数据报作废

    为了应对以上问题,产生了一种新的技术路径MTU发现(Path MTU Discovery)。所谓路径MTU是指从发送端主机到接收端主机之间不需要分片时最大MTU的大小,即路径中存在的所有数据链路中最小的MTU。而路径MTU发现从发送主机按照路径MTU的大小将数据报分片后进行发送。进行路径MTU发现就可以避免在中途的路由器上进行分片处理,也可以在TCP中发送更大的包。

    # 路径MTU发现的机制(UDP的情况下)
    发送时IP首部的分片标志位设置为不分片。路由器丢包。
    由ICMP通知下一次MTU的大小。
    UDP中没有重发处理。应用在发送下一个消息时会被分片。
    具体来说,就是指UDP层传过来的"UDP首部+UDP数据"在IP层被分片。对于IP,它并不区分UDP首部和应用的数据。
    所有的分片到达目标主机后被重组,再传给UDP层。
    

    路径MTU发现的工作原理如下:

    • 在发送端主机发送IP数据报时将其首部的分片禁止标志位设置为1。根据这个标志位,途中的路由器即使遇到需要分片才能处理的大包,也不会去分片,而是将包丢弃。

    • 通过一个ICMP的不可达消息将数据链路上的MTU的值给发送主机。

    • 下一次,从发送给同一个目标主机的IP数据报获得ICMP所通知的MTU值以后,将它设置为当前的MTU。发送主机根据这个MTU对数据报进行分片处理。如此反复,直到数据报被发送到目标主机为止没有再收到任何ICMP,就认为最后一次ICMP所通知的MTU即是一个合适的MTU值。那么,当MTU的值比较多时,最少可以缓存约10分钟。在这10分钟内使用刚刚求得的MTU,但过了这10分钟以后则重新根据链路上的MTU做一次路径MTU发现。

    # 路径MTU发现的机制(TCP的情况下)
    发送时IP首部的分片标志位设置为不分片。路由器丢包。
    由ICMP通知下一次MTU的大小。
    根据TCP的重发处理,数据报会被重新发送。TCP负责将数据分成IP层不会再被分片的粒度以后传给IP层。IP层不再做分片处理。
    不需要重组。数据被原样发送给接收端主机的TCP层。
    

    (最近更新:2019年09月18日)

    更多相关内容
  • 为什么要发现路径上的MTU我们知道,IP报文在发送时,需要判断IP报文大小和发送端口的MTU(Maximum Transmission Unit,MTU),如果IP报文大于发送端口的MTU,报文需要分片后再发送,以保证发送的报文大小不超过发送...

    为什么要发现路径上的MTU

    我们知道,IP报文在发送时,需要判断IP报文大小和发送端口的MTU(Maximum Transmission Unit,MTU),如果IP报文大于发送端口的MTU,报文需要分片后再发送,以保证发送的报文大小不超过发送端口的MTU。

    这种场景,对于TCP报文是不适应的。

    因为如果TCP报文被路径上某个路由器分片后发送,那么不同的分片到达目的端的时间不一样,那么TCP的RTT就无法正确评估,进而影响TCP对路径拥塞程度的判断。

    所以TCP需要有个机制,能发现中间路由设备的MTU,避免报文分片。

    TCP是如何进行路径MTU发现的

    Tcp根据对比本地发送端口的MTU和对端的MSS,选择发送报文的大小,同时设置所有报文ip头里的DF标志(不允许中间路由器分片),当中间路由器转发这个报文时候,发现报文大小大于发送端口的MTU,路由器就丢掉这个报文,并发送一个ICMP“不能分片”的差错通告。

    发送端收到这个ICMP报文,就获知中间路由的MTU大小,并按照路由器MTU大小重传报文。

    由于ICMP“不能分片”导致的重传,拥塞窗口不需要变化,但是需要慢启动。

    由于网络路径是不停变化的,隔一段时间,发送方可以尝试再次发送较大长度的报文,如果路径变化,路径已经切换到更大MTU的路由器上,这种尝试是可以提高发送效率的。这种尝试一般会在10分钟~30分钟之后触发。

    一个路径MTU探测的例子

    2d21994fb9716bd243656b4a8ef8da29.png

    tcp路径MTU发现

    1、报文1、2、3为tcp握手,协商接收方mss=512

    2、报文4发送512字节,同时报文5使用FIN拆掉链接

    3、路由器转发报文4时候由于MTU原因丢弃了报文4,路由器发送报文6通知客户端ICMP“不能分片”差错和路由器本地mtu=296

    4、客户端使用报文8和报文10分别重发这2*256个字节,即重传报文报文4

    展开全文
  • 执行路径MTU发现,而不依赖于通常不传递的ICMP错误。 此程序执行所述的打包层路径MTU发现,这是在存在情况下检测MTU大小的更可靠的方法。 基本原理 尽管TCP连接会根据各种指标(网络性能,数据包丢失,ICMP错误消息...
  • 4.5 路径MTU发现

    千次阅读 2015-03-25 22:33:15
    TCP报文需要封装成IP报文才会发送,报文在网络中按照一定路径传输后会抵达目的地。...而IP报文的传输路径是事先不知道的,而且在传输过程中也可能发送变化,所以TCP需要动态测路径MTU的大小,这就是路径MTU发现

      如果两台主机之间的通信要通过多个网络,那么每个网络的链路层就可能有不同的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_UNA_ADVANCED && !recovered) {
    2703             tp->high_seq = tp->snd_nxt;
    2704             __tcp_push_pending_frames(sk, tcp_current_mss(sk),
    2705                           TCP_NAGLE_OFF);
    ...
    (4)发送FIN关闭连接时:

    2545 void tcp_send_fin(struct sock *sk)
    2546 {
    ...
    2578     __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF);
    2579 }
    (5)使用setsockopt设置TCP_NODELAY功能时:
    2371 static int do_tcp_setsockopt(struct sock *sk, int level,
    2372         int optname, char __user *optval, unsigned int optlen)
    2373 {
    ...
    2423     case TCP_NODELAY:
    2424         if (val) {
    2425             /* TCP_NODELAY is weaker than TCP_CORK, so that
    2426              * this option on corked socket is remembered, but
    2427              * it is not activated until cork is cleared.
    2428              *
    2429              * However, when TCP_NODELAY is set we make
    2430              * an explicit push, which overrides even TCP_CORK
    2431              * for currently queued segments.
    2432              */
    2433             tp->nonagle |= TCP_NAGLE_OFF|TCP_NAGLE_PUSH;
    2434             tcp_push_pending_frames(sk);
    2435         } else {
    2436             tp->nonagle &= ~TCP_NAGLE_OFF;
    2437         }
    2438         break;
    ...
    
    (6) 使用setsockopt取消TCP_CORK功能时:
    2371 static int do_tcp_setsockopt(struct sock *sk, int level,
    2372         int optname, char __user *optval, unsigned int optlen)
    2373 {
    ...
    2503     case TCP_CORK:
    ...
    2515         if (val) {
    2516             tp->nonagle |= TCP_NAGLE_CORK;
    2517         } else {
    2518             tp->nonagle &= ~TCP_NAGLE_CORK;
    2519             if (tp->nonagle&TCP_NAGLE_OFF)
    2520                 tp->nonagle |= TCP_NAGLE_PUSH;
    2521             tcp_push_pending_frames(sk);
    2522         }
    2523         break;
    ...
    
    (7)收到对端发过来的ACK或数据包时:

    5076 int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
    5077             const struct tcphdr *th, unsigned int len)
    5078 {
    ...
    5109     if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&
    5110         TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&
    5111         !after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) {
    5112         int tcp_header_len = tp->tcp_header_len;
    ...
    5136         if (len <= tcp_header_len) {
    5137             /* Bulk data transfer: sender */
    5138             if (len == tcp_header_len) {
    5139                 /* Predicted packet is in window by definition.
    5140                  * seq == rcv_nxt and rcv_wup <= rcv_nxt.
    5141                  * Hence, check seq<=rcv_wup reduces to:
    5142                  */
    5143                 if (tcp_header_len ==
    5144                     (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
    5145                     tp->rcv_nxt == tp->rcv_wup)
    5146                     tcp_store_ts_recent(tp);
    5147 
    5148                 /* We know that such packets are checksummed
    5149                  * on entry.
    5150                  */
    5151                 tcp_ack(sk, skb, 0);
    5152                 __kfree_skb(skb);
    5153                 tcp_data_snd_check(sk);
    5154                 return 0;
    ...
    5159         } else {
    ...
    5228             if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) {
    5229                 /* Well, only one small jumplet in fast path... */
    5230                 tcp_ack(sk, skb, FLAG_DATA);
    5231                 tcp_data_snd_check(sk);
    ...
    5274     /* step 7: process the segment text */
    5275     tcp_data_queue(sk, skb);
    5276 
    5277     tcp_data_snd_check(sk);
    5278     tcp_ack_snd_check(sk);
    5279     return 0;
    ...
    
    (8)非TCP_LISTEN和TCP_CLOSE状态下收到合法的包时:
    5600 int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
    5601               const struct tcphdr *th, unsigned int len)
    5602 {
    ...
    5649     case TCP_SYN_SENT:
    5650         queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
    5651         if (queued >= 0)
    5652             return queued;
    5653 
    5654         /* Do step6 onward by hand. */
    5655         tcp_urg(sk, skb, th);
    5656         __kfree_skb(skb);
    5657         tcp_data_snd_check(sk); //发送TFO的数据
    5658         return 0;
    5659     }
    ...
    5861     /* tcp_data could move socket to TIME-WAIT */
    5862     if (sk->sk_state != TCP_CLOSE) {
    5863         tcp_data_snd_check(sk);
    5864         tcp_ack_snd_check(sk);
    5865     }
    ...
    
      这样看来,TCP在发送数据的时候大多会执行路径MTU发现。

      对于问题3,TCP完成PMTU探测任务的基本方法是使用tcp_mtu_probe函数用于发送PMTU探测包:

    1675 static int tcp_mtu_probe(struct sock *sk)
    1676 {
    1677     struct tcp_sock *tp = tcp_sk(sk);
    1678     struct inet_connection_sock *icsk = inet_csk(sk);
    1679     struct sk_buff *skb, *nskb, *next;
    1680     int len;
    1681     int probe_size;      
    1682     int size_needed;     
    1683     int copy;
    1684     int mss_now;
    1685
    1686     /* Not currently probing/verifying,
    1687      * not in recovery,
    1688      * have enough cwnd, and
    1689      * not SACKing (the variable headers throw things off) */
    1690     if (!icsk->icsk_mtup.enabled ||    
    1691         icsk->icsk_mtup.probe_size ||   //正在进行PMTU探测
    1692         inet_csk(sk)->icsk_ca_state != TCP_CA_Open ||
    1693         tp->snd_cwnd < 11 ||
    1694         tp->rx_opt.num_sacks || tp->rx_opt.dsack)
    1695         return -1;
    1696
    1697     /* Very simple search strategy: just double the MSS. */
    1698     mss_now = tcp_current_mss(sk);
    1699     probe_size = 2 * tp->mss_cache; //设置探测包大小为当前MSS的两倍
    1700     size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache;
    1701     if (probe_size > tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_high)) {
    1702         /* TODO: set timer for probe_converge_event */
    1703         return -1;
    1704     }
    1705
    1706     /* Have enough data in the send queue to probe? */
    1707     if (tp->write_seq - tp->snd_nxt < size_needed)
    1708         return -1;
    1709
    1710     if (tp->snd_wnd < size_needed) //发送窗口太小
    1711         return -1;
    1712     if (after(tp->snd_nxt + size_needed, tcp_wnd_end(tp)))
    1713         return 0;
    1714
    1715     /* Do we need to wait to drain cwnd? With none in flight, don't stall */
    1716     if (tcp_packets_in_flight(tp) + 2 > tp->snd_cwnd) {
    1717         if (!tcp_packets_in_flight(tp))
    1718             return -1;
    1719         else
    1720             return 0;
    1721     }
    1722
    1723     /* We're allowed to probe.  Build it now. */
    1724     if ((nskb = sk_stream_alloc_skb(sk, probe_size, GFP_ATOMIC)) == NULL)
    1725         return -1;
    1726     sk->sk_wmem_queued += nskb->truesize;
    1727     sk_mem_charge(sk, nskb->truesize);
    1728
    1729     skb = tcp_send_head(sk);
    1730
    1731     TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(skb)->seq;
    1732     TCP_SKB_CB(nskb)->end_seq = TCP_SKB_CB(skb)->seq + probe_size;
    1733     TCP_SKB_CB(nskb)->tcp_flags = TCPHDR_ACK;
    1734     TCP_SKB_CB(nskb)->sacked = 0;
    1735     nskb->csum = 0;
    1736     nskb->ip_summed = skb->ip_summed;
    1737
    1738     tcp_insert_write_queue_before(nskb, skb, sk);
    1739
    1740     len = 0;
    1741     tcp_for_write_queue_from_safe(skb, next, sk) { //将发送队列中的数据合并到大的探测包中
    1742         copy = min_t(int, skb->len, probe_size - len);
    1743         if (nskb->ip_summed)
    1744             skb_copy_bits(skb, 0, skb_put(nskb, copy), copy);
    1745         else
    1746             nskb->csum = skb_copy_and_csum_bits(skb, 0,
    1747                                 skb_put(nskb, copy),
    1748                                 copy, nskb->csum);
    1749
    1750         if (skb->len <= copy) {
    1751             /* We've eaten all the data from this skb.
    1752              * Throw it away. */
    1753             TCP_SKB_CB(nskb)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
    1754             tcp_unlink_write_queue(skb, sk);
    1755             sk_wmem_free_skb(sk, skb);
    1756         } else {
    1757             TCP_SKB_CB(nskb)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags &
    1758                            ~(TCPHDR_FIN|TCPHDR_PSH);
    1759             if (!skb_shinfo(skb)->nr_frags) {
    1760                 skb_pull(skb, copy);
    1761                 if (skb->ip_summed != CHECKSUM_PARTIAL)
    1762                     skb->csum = csum_partial(skb->data,
    1763                                  skb->len, 0);
    1764             } else {
    1765                 __pskb_trim_head(skb, copy);
    1766                 tcp_set_skb_tso_segs(sk, skb, mss_now);
    1767             }
    1768             TCP_SKB_CB(skb)->seq += copy;
    1769         }
    1770
    1771         len += copy;
    1772
    1773         if (len >= probe_size)
    1774             break;
    1775     }
    1776     tcp_init_tso_segs(sk, nskb, nskb->len);
    1777
    1778     /* We're ready to send.  If this fails, the probe will
    1779      * be resegmented into mss-sized pieces by tcp_write_xmit(). */
    1780     TCP_SKB_CB(nskb)->when = tcp_time_stamp;
    1781     if (!tcp_transmit_skb(sk, nskb, 1, GFP_ATOMIC)) { //发送探测包
    1782         /* Decrement cwnd here because we are sending
    1783          * effectively two packets. */
    1784         tp->snd_cwnd--;
    1785         tcp_event_new_data_sent(sk, nskb);
    1786
    1787         icsk->icsk_mtup.probe_size = tcp_mss_to_mtu(sk, nskb->len); //记录此次探测的PMTU值
    1788         tp->mtu_probe.probe_seq_start = TCP_SKB_CB(nskb)->seq;
    1789         tp->mtu_probe.probe_seq_end = TCP_SKB_CB(nskb)->end_seq;
    1790
    1791         return 1;
    1792     }
    1793
    1794     return -1;
    1795 }

      默认情况下TCP数据包的IP部分都会设置不分片位。在tcp_mtu_probe函数发送大的探测包后需要等待三种结果:(1)收到ACK确认了探测包;这意味着PMTU大于或等于当前探测包的MTU;(2)收到ICMP“需要分片”的报文;这时需要根据报文中通告的MTU来调整PMTU值;(3)数据包丢失导致重传。下面分别讨论这3种情况。

      (1)收到ACK确认了探测包。TCP在收到ACK后会调用tcp_clean_rtx_queue函数来清理发送缓存中的skb:

    3001 static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
    3002                    u32 prior_snd_una)
    3003 {
    ...
    3099         if (unlikely(icsk->icsk_mtup.probe_size &&  //正在PMTU探测中
    3100                  !after(tp->mtu_probe.probe_seq_end, tp->snd_una))) { //探测报文全部到达对端
    3101             tcp_mtup_probe_success(sk); //探测成功
    3102         }
    ...
    
      tcp_mtup_probe_success函数:
    2588 static void tcp_mtup_probe_success(struct sock *sk)
    2589 {
    2590     struct tcp_sock *tp = tcp_sk(sk);
    2591     struct inet_connection_sock *icsk = inet_csk(sk);
    2592 
    2593     /* FIXME: breaks with very large cwnd */
    2594     tp->prior_ssthresh = tcp_current_ssthresh(sk);
    2595     tp->snd_cwnd = tp->snd_cwnd *
    2596                tcp_mss_to_mtu(sk, tp->mss_cache) /
    2597                icsk->icsk_mtup.probe_size;
    2598     tp->snd_cwnd_cnt = 0;
    2599     tp->snd_cwnd_stamp = tcp_time_stamp;
    2600     tp->snd_ssthresh = tcp_current_ssthresh(sk);
    2601 
    2602     icsk->icsk_mtup.search_low = icsk->icsk_mtup.probe_size; //记录最小PMTU的值
    2603     icsk->icsk_mtup.probe_size = 0; //本次探测结束
    2604     tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); //保存探测结果
    2605 }
      tcp_sync_mss函数用于保存PMTU的值:
    1296 unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu)
    1297 {
    1298     struct tcp_sock *tp = tcp_sk(sk);
    1299     struct inet_connection_sock *icsk = inet_csk(sk);
    1300     int mss_now;
    1301 
    1302     if (icsk->icsk_mtup.search_high > pmtu)
    1303         icsk->icsk_mtup.search_high = pmtu; //记录PMTU最大值
    1304     
    1305     mss_now = tcp_mtu_to_mss(sk, pmtu); //将PMTU的值转换为MSS
    1306     mss_now = tcp_bound_to_half_wnd(tp, mss_now);
    1307 
    1308     /* And store cached results */
    1309     icsk->icsk_pmtu_cookie = pmtu; //记录当前PMTU
    1310     if (icsk->icsk_mtup.enabled)
    1311         mss_now = min(mss_now, tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_low));
    1312     tp->mss_cache = mss_now; //记录MSS,即TCP报文最大数据长度
    1313 
    1314     return mss_now;
    1315 }  
    
      在对端收到探测包的情况下TCP会把探测包的PMTU记录下来,当PMTU探测再次启动时发送的探测包的PMTU会更大,最终TCP会得到结果(2)或(3)。先看结果(2), TCPv4中处理ICMP报文的函数是 tcp_v4_err

     326 void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
     327 {
    ...
     389     switch (type) {
    ...
     399     case ICMP_DEST_UNREACH: //路由器丢弃探测包后发送的ICMP报文会由这个分支处理
    ...
     403         if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
     404             /* We are not interested in TCP_LISTEN and open_requests
     405              * (SYN-ACKs send out by Linux are always <576bytes so
     406              * they should go through unfragmented).
     407              */
     408             if (sk->sk_state == TCP_LISTEN)
     409                 goto out;
     410
     411             tp->mtu_info = info; //记录ICMP报文返回的MTU值
     412             if (!sock_owned_by_user(sk)) { //进程没有锁定socket
     413                 tcp_v4_mtu_reduced(sk); //修改MSS的值
     414             } else {
     415                 if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags)) //推迟到进程解除锁定socket时调用tcp_v4_mtu_reduced
     416                     sock_hold(sk);
     417             }
     418             goto out;
    ...
      tcp_v4_mtu_reduced函数:
     271 static void tcp_v4_mtu_reduced(struct sock *sk)
     272 {
     273     struct dst_entry *dst;
     274     struct inet_sock *inet = inet_sk(sk);
     275     u32 mtu = tcp_sk(sk)->mtu_info;
     276
     277     dst = inet_csk_update_pmtu(sk, mtu);  //更新路由表中mtu的信息
     278     if (!dst)
     279         return;
     280
     281     /* Something is about to be wrong... Remember soft error
     282      * for the case, if this connection will not able to recover.
     283      */
     284     if (mtu < dst_mtu(dst) && ip_dont_fragment(sk, dst))
     285         sk->sk_err_soft = EMSGSIZE;    
     286
     287     mtu = dst_mtu(dst);   //得到路由表中记录的MTU
     288
     289     if (inet->pmtudisc != IP_PMTUDISC_DONT && //确实能够发送不分片报文
     290         inet_csk(sk)->icsk_pmtu_cookie > mtu) { //socket中记录的PMTU大于路由表中的PMTU
     291         tcp_sync_mss(sk, mtu); //更新socket中记录的MTU的值
     292
     293         /* Resend the TCP packet because it's
     294          * clear that the old packet has been
     295          * dropped. This is the new "fast" path mtu
     296          * discovery.    
     297          */
     298         tcp_simple_retransmit(sk);     //重传数据,因为有数据丢失
     299     } /* else let the usual retransmit timer handle it */
     300  

      在路径MTU过大被路由器丢弃并收到ICMP报文的情况下,TCP会把ICMP中通告的PMTU作为结果保存下来。但出于安全等考虑,并不是所有的路由器在丢弃分片过大的报文时都会发送ICMP消息。如果探测包被这样的路由器丢弃,TCP不会收到任何响应,就好像探测包进入了“黑洞”一样,这就是TCP PMTU发现中的Black Hole Detection问题。即结果(3)。探测包丢失后TCP有两张方式处理:快速重传和超时重传。先来看快速重传,指向这个功能的是tcp_fastretrans_alert函数:

    2745 static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
    2746                   int prior_sacked, int prior_packets,
    2747                   bool is_dupack, int flag)
    2748 {
    ...
    2833         /* MTU probe failure: don't reduce cwnd */
    2834         if (icsk->icsk_ca_state < TCP_CA_CWR &&
    2835             icsk->icsk_mtup.probe_size &&  //开启PMTU探测
    2836             tp->snd_una == tp->mtu_probe.probe_seq_start) { //探测包完全未收到
    2837             tcp_mtup_probe_failed(sk);
    2838             /* Restores the reduction we did in tcp_mtup_probe() */
    2839             tp->snd_cwnd++;
    2840             tcp_simple_retransmit(sk);
    2841             return;
    2842         }
    ...
      tcp_mtup_probe_failed函数处理探测失败的情况:
    2580 static void tcp_mtup_probe_failed(struct sock *sk)
    2581 {
    2582     struct inet_connection_sock *icsk = inet_csk(sk);
    2583 
    2584     icsk->icsk_mtup.search_high = icsk->icsk_mtup.probe_size - 1;
    2585     icsk->icsk_mtup.probe_size = 0; //结束本次探测
    2586 }
      在快速重传的情况下,TCP会更新一下PMTU探测的上限。超时重传时呢?重传定时器会调用tcp_write_timeout函数:
    156 static int tcp_write_timeout(struct sock *sk)
    157 {
    158     struct inet_connection_sock *icsk = inet_csk(sk);
    159     int retry_until;
    160     bool do_reset, syn_set = false;
    161 
    162     if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
    163         if (icsk->icsk_retransmits)    
    164             dst_negative_advice(sk);       
    165         retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;
    166         syn_set = true;
    167     } else {
    168         if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) {
    169             /* Black hole detection */     
    170             tcp_mtu_probing(icsk, sk);     
    ...
      确定是超时时,tcp_write_timeout函数会调用tcp_mtu_probing函数处理PMTU:
    102 static void tcp_mtu_probing(struct inet_connection_sock *icsk, struct sock *sk)
    103 {
    104     /* Black hole detection */
    105     if (sysctl_tcp_mtu_probing) {
    106         if (!icsk->icsk_mtup.enabled) { //如果未开启PMTU发现机制
    107             icsk->icsk_mtup.enabled = 1; //开启之
    108             tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); //初始化PMTU和MSS
    109         } else {
    110             struct tcp_sock *tp = tcp_sk(sk);
    111             int mss;
    112 
    113             mss = tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_low) >> 1; //缩小MSS
    114             mss = min(sysctl_tcp_base_mss, mss);
    115             mss = max(mss, 68 - tp->tcp_header_len);
    116             icsk->icsk_mtup.search_low = tcp_mss_to_mtu(sk, mss);
    117             tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); //保存缩小后的MSS
    118         }
    119     }
    120 }
      这样看来,超时的情况下TCP会减小MSS,如此就使TCP在路由器不支持PMTU发现机制的情况下实现了PMTU的探测。

      下面来总结一下问题3的答案:TCP在发送数据时会将小段数据合并到大的探测包再发送,TCP发送的包的IP头设置会不分片的DF位。如果包顺利抵达目的地,则用这个包的MTU作为PMTU;如果包过大被丢弃,若路由器会发送ICMP“需要分片,但设置了DF位”的ICMP报文,则使用ICMP报文中的MTU值作为PMTU;若路由器不发送IMCP,则在超时重传时TCP会减小PMTU。在得到PMTU后,TCP会将其保存在socket中,并更新MSS信息,接下来用新的MSS继续发送探测包和普通数据包。

      根据对问题3的代码分析来回答第4个问题:TCP路径MTU探测得到的PMTU保存在inet_connection_sock的icsk_pmtu_cookie中,MSS保存在tcp_sock的mss_cache变量中。如果收到了ICMP报文,则用报文中的值更新路由表中保存的MTU信息。

      问题5:TCP会同时使用保存在路由表和socket中的MTU:

      client端连接建立时:

    2752 void tcp_connect_init(struct sock *sk)
    2753 {
    ...
    2773     tcp_mtup_init(sk);
    2774     tcp_sync_mss(sk, dst_mtu(dst));
    ...
      server端创建socket时:
    1642 struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
    1643                   struct request_sock *req,
    1644                   struct dst_entry *dst)
    1645 {
    ...
    1691     tcp_mtup_init(newsk);
    1692     tcp_sync_mss(newsk, dst_mtu(dst));
    ...
      发送数据时:
    1016 int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
    1017         size_t size)
    1018 {
    ...
    1067     mss_now = tcp_send_mss(sk, &size_goal, flags);
    ...
      tcp_send_mss函数会调用tcp_current_mss函数用于获取当前MSS的值:

    1321 unsigned int tcp_current_mss(struct sock *sk)
    1322 {
    1323     const struct tcp_sock *tp = tcp_sk(sk);
    1324     const struct dst_entry *dst = __sk_dst_get(sk);
    1325     u32 mss_now;
    1326     unsigned int header_len;
    1327     struct tcp_out_options opts;
    1328     struct tcp_md5sig_key *md5;
    1329 
    1330     mss_now = tp->mss_cache;
    1331 
    1332     if (dst) {
    1333         u32 mtu = dst_mtu(dst);
    1334         if (mtu != inet_csk(sk)->icsk_pmtu_cookie)
    1335             mss_now = tcp_sync_mss(sk, mtu);
    1336     }
    1337 
    1338     header_len = tcp_established_options(sk, NULL, &opts, &md5) +
    1339              sizeof(struct tcphdr);
    1340     /* The mss_cache is sized based on tp->tcp_header_len, which assumes
    1341      * some common options. If this is an odd packet (because we have SACK
    1342      * blocks etc) then our calculated header_len will be different, and
    1343      * we have to adjust mss_now correspondingly */
    1344     if (header_len != tp->tcp_header_len) {
    1345         int delta = (int) header_len - tp->tcp_header_len;
    1346         mss_now -= delta; 
    1347     }
    1348 
    1349     return mss_now;
    1350 }

      至此,关于TCP PMTU发现的问题全部回答完毕。在开启PMTU发现功能时,PMTU探测会不断的进行。网络中路径的情况在不停的变化,TCP也会不时地得到新的探测结果,并利用这些结果去影响所发送的报文段的大小。PMTU发现机制使得TCP能尽快获得数据传输路径的MTU大小,从而尽可能使用“不会因为报文过大而被路由器丢弃”的最大长度去发送没一个报文段,进而力求使得数据发送的效率最大化。

          补充:关于DF设置的说明

    一、首先我要说明一点:我认为所有TCP报文的IP报头都会设置DF位,不只是TCP MTU探测报文会设置。

    代码:

      if (ip_dont_fragment(sk, &rt->dst) && !skb->local_df)
            iph->frag_off = htons(IP_DF);
      else
            iph->frag_off = 0;

    只要满足了“ip_dont_fragment(sk, &rt->dst) == 1”且“skb->local_df == 0”,则一定会设置DF位。

    二、我们先来看看skb->local_df。

    TCP中发送数据包时申请SKB(包括MUT探测报文)的函数是sk_stream_alloc_skb,在这个函数中skb->local_df是0。skb->local_df只有在__ip_make_skb函数中才可能被设置为1::
    /* Unless user demanded real pmtu discovery (IP_PMTUDISC_DO), we allow
     * to fragment the frame generated here. No matter, what transforms
     * how transforms change size of the packet, it will comeout.                                                                              
     */
       if (inet->pmtudisc < IP_PMTUDISC_DO)
           skb->local_df = 1;

    而__ip_make_skb只有UDP协议会调用,故对所有TCP报文满足“skb->local_df == 0”。

    三、ip_dont_fragment函数:

     static inline
     int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
     {   
         return  inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO ||
             (inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT &&
              !(dst_metric_locked(dst, RTAX_MTU)));
     }

    在inet_create函数中inet_sk(sk)->pmtudisc会被设置为 IP_PMTUDISC_WANT:

      if (ipv4_config.no_pmtu_disc) //ipv4_config.no_pmtu_disc默认是0
           inet->pmtudisc = IP_PMTUDISC_DONT;
      else
           inet->pmtudisc = IP_PMTUDISC_WANT;

    dst_metric_locked(dst,RTAX_MTU)是查看dst_metric的MTU对应的位是否被锁定了。metric是用来保存与对端通信时的参数,与MTU对应的参数应该只有在修改时才会被锁定。故在通常情况下dst_metric_locked(dst, RTAX_MTU)的值应该是0。

    四、综上,对于所有TCP报文,其IP报头的DF位都会被设置。


    展开全文
  • 11.8 采用UDP的路径MTU发现 下面对使用U D P的应用程序与路径 M T U发现机制之间的交互作用进行研究。看一看如果应用程序写了一个对于一些中间链路来说太长的数据报时会发生什么情况。 例子 由于我们所使用的支持...

    第11章 UDP:用户数据报协议

    11.8 采用UDP的路径MTU发现

    下面对使用U D P的应用程序与路径 M T U发现机制之间的交互作用进行研究。看一看如果应用程序写了一个对于一些中间链路来说太长的数据报时会发生什么情况。

    例子
    由于我们所使用的支持路径 M T U发现机制的唯一系统就是Solaris 2.x,因此,将采用它作为源站发送一份6 5 0字节数据报经s l i p。由于s l i p主机位于M T U为2 9 6的S L I P链路后,因此,任何长于2 6 8字节(2 9 6-2 0-8)且“不分片”比特置为 1的U D P数据都会使b s d i路由器产生I C M P“不能分片”差错报文。图 11 - 1 3给出了拓扑结构和M T U。
    在这里插入图片描述
    可以用下面的命令行来产生 6 5 0字节U D P数据报,每两个U D P数据报之间的间隔是5秒:

    solaris % sock -u -i -n10 -w650 -p5 slip discard
    

    图11 - 1 4是t c p d u m p的输出结果。在运行这个例子时,将b s d i设置成在I C M P“不能分片”差错中,不返回下一跳M T U信息。

    在发送的第一个数据报中将 D F比特置1(第1行),其结果是从b s d i路由器发回我们可以猜测的结果(第 2行)。令人不解的是,发送一个 D F比特置1的数据报(第 3行),其结果是同样的I C M P差错(第4行)。我们预计这个数据报在发送时应该将 D F比特置0。 第5行结果显示,I P已经知道了发往该目的地址的数据报不能将 D F比特置1,因此,I P进而将数据报在源站主机上进行分片。这与前面的例子中, I P发送经过U D P的数据报,允许具有较小M T U的路由器(在本例中是 b s d i)对它进行分片的情况不一样。由于 I C M P“不能分片”报文并没有指出下一跳的 M T U,因此,看来I P猜测M T U为5 7 6就行了。第一次分片(第 5 行)包含5 4 4字节的U D P数据、8字节U D P首部以及2 0字节I P首部,因此,总 I P数据报长度是5 7 2字节。第2次分片(第6行)包含剩余的1 0 6字节U D P数据和2 0字节I P首部。

    不幸的是,第7行的下一个数据报将其 D F比特置1,因此b s d i将它丢弃并返回 I C M P差错。这时发生了 I P定时器超时,通知 I P查看是不是因为路径 M T U增大了而将 D F比特再一次置 1。我们可以从第1 9行和2 0行看出这个结果。将第 7行与1 9行进行比较,可以看出 I P每过3 0秒就将D F比特置1,以查看路径M T U是否增大了。

    这个3 0秒的定时器值看来太短。 R F C 11 9 1建议其值取 1 0分钟。可以通过修改i p _ i r e _ p a t h m t u _ i n t e r v a l(E . 4节)参数来改变该值。同时,Solaris 2.2无法对单个U D P应用或所有U D P应用关闭该路径M T U发现。只能通过修改i p _ p a t h _ m t u _ d i s c o v e r y参数,在系统一级开放或关闭它。正如在这个例子里所能看到的那样,如果允许路径M T U发现,那么当U D P应用程序写入可能被分片数据报时,该数据报将被丢弃。
    在这里插入图片描述

    s o l a r i s的I P层所假设的最大数据报长度( 5 7 6字节)是不正确的。在图 11 - 1 3中,我们看到,实际的 M T U值是2 9 6字节。这意味着经 s o l a r i s分片的数据报还将被 b s d i分片。图11 - 1 5给出了在目的主机( s l i p)上所收集到的 t c p d u m p对于第一个到达数据报的输出结果(图11 - 1 4的第5行和第6行)。
    在这里插入图片描述
    在本例中,s o l a r i s不应该对外出数据报分片,它应该将 D F比特置0,让具有最小M T U的路由器来完成分片工作。

    现在我们运行同一个例子,只是对路由器 b s d i进行修改使其在 I C M P“不能分片”差错中返回下一跳M T U。图11 - 1 6给出了t c p d u m p输出结果的前6行。

    与图11 - 1 4一样,前两个数据报同样是将 D F比特置1后发送出去的。但是在知道了下一跳M T U后,只产生了3个数据报片,而图11 - 1 5中的b s d i路由器则产生了4个数据报片。
    在这里插入图片描述

    展开全文
  • 我正在Linux中进行路径MTU发现的一些实验.据我所知,从RFC 1191中,如果路由器接收到一个非零DF位的数据包,并且数据包无法在没有分段的情况下发送到下一个主机,那么路由器应丢弃数据包并将ICMP消息发送到初始发件人.我...
  • 路径MTU发现

    千次阅读 2011-11-09 17:24:57
    )这种数据报的尺寸称作为路径MTU(PMTU),它等于路径上每一跳的MTU之中的最小值。当前因特网协议族的缺点就是对一台主机来说缺乏发现任意一条路径的PMTU的标准机制。 注意:路径MTU在[1]中被称作为
  • 一、采用UDP的路径MTU发现 MTU介绍见文章:https://blog.csdn.net/qq_41453285/article/details/95997936 UDP的路径MTU发现 让我们考察使用UDP的应用程序与路径MTU发现机制(PMTUD)之间的交互过程 对一个像UDP...
  •  路径MTU是指一条因特网传输路径中,从源地址到目的地址所经过的“路径”上的所有IP跳的最大传输单元的最小值。  或者从另外一个角度来看,就是无需进行分片处理就能穿过这条“路径”的最大传输单元...
  • 路径MTU(PMTU)发现控制与DF位

    万次阅读 2018-05-15 17:46:18
    路径MTU发现是用来确定到达目的地的路径中最大传输单元(MTU)的大小。通过在IP报头中设置不分片DF(Don't Fragment)标志来探测路径中的MTU值, 如果路径中设备的MTU值小于此报文长度,并且发现DF标志,就会发回一个...
  • RFC1191 路径MTU发现

    2019-08-22 19:56:48
    本文介绍了一种动态路径最大MTU发现的机制。介绍了路由器产生一种特殊icmp报文。 概述 1、主机先尝试发送一个576字节的报文,并把DF置位(dont fragment)。 这样如果路由器无法分片,会发送一个目的不可达icmp...
  • 路径MTU当在同一个网络上的两台主机互相通信时,该网络的MTU是非常重要的。当时如果两台主机之间的通信要通过多个网络,那么每个网络的链路层就可能有不同的MTU。重要的不是两台主机所在网络的MTU,而是两台通信主机...
  • python网络编程——路径MTU发现

    千次阅读 2020-02-20 23:53:58
    手动构造一个指定大小的数据包,在IP首部设置强制不分片,根据返回的ICMP差错报文类型来判断数据包大小是否超过MTU 如果返回数据包ICMP层的type==3,code==4则表明数据包已经超过了MTU 如果返回数据包ICMP层的...
  • RFC1191_路径MTU探索 .doc

    2019-08-23 09:42:44
    RFC1191 路径MTU发现 (RFC1191 Path MTU Discovery) 本备忘录状态 This memo provides information for the Internet community. It does not specify an Internet standard of any kind. Distribution of ...
  • 2. 路径MTU发现: 主要参考: http://blog.csdn.net/u011130578/article/details/44629265 原因:过小浪费资源,过大则分片. 何时启用:大多时候发送数据时. 方法:发送pmtu包探测,设置不分片位(DF位).  三种...
  •  路径MTU是指一条因特网传输路径中,从源地址到目的地址所经过的“路径”上的所有IP跳的最大传输单元的最小值。或者从另外一个角度来看,就是无需进行分片处理就能穿过这条“路径”的最大传输单元的最大
  • IP属于网络层,下一层时数据链路层,在数据链路层,不同类型的数据链路的最大传输单元(MTU)都不尽相同。例如,连接两个路由器的通路可以看作时一个链路。从而 ,网络层的数据到了数据链路层之后,可能会出现...
  • 该物理层限制就叫做MTU. 而在网络传输中, 每一条传输线路都有可能有自己不同的传输限制, 虽然路由器可以在IP层进行数据包分片传输, 但是有IP层进行的分片传输, 一旦在对端重组的时候, 有一个出现了错误, 就会发生...
  • IP_MTU_DISCOVER: 设置或者 接收 socket的 Path MTU Discovery 设置。 当启用时, Linux 执行 Path MTU Discovery 作为定义在RFC 1191 在这个socket. 这个数据包设置了不允许分片Don't fragment: Set don't fragment...
  • 1. 简介 2  2. 协议概览 2  3, 主机规范 3  3.1 TCP MSS选项 4  4. 路由器规范 5  5. 主机对老式报文的处理 5  6. 主机实现 6  6.1 分层 7  6.2 存储PMTU信息 7  6.3 清除过时的PMTU信息 8  ...7. 路径MTU
  • 家庭应该是爱、欢乐和笑的殿堂。...IP属于网络层,下一层时数据链路层,在数据链路层,不同类型的数据链路的最大传输单元(MTU)都不尽相同。例如,连接两个路由器的通路可以看作时一个链路。从而 ,网络层的...
  • 调整 用于进行 IPv6 路径 MTU 测试的简单工具 您需要安装 scapy (pip install scapy) 然后调用 mtuping 作为 sudo ./mtuping.py cdnjs.cloudflare.com
  • 路径MTU

    2019-01-08 05:44:05
    链路层的这个特性叫作MTU,也就是最大传输单元。不同类型的网络会有所不同的。如果IP层有一个数据报要传输,而且数据的长度比链路层的MTU还要大的话,那么IP层就需要进行分片。把数据分成更小的片,这样每一个小的片...
  • 有时,更改MTU值可提高设备工作性能。本文介绍了如何利用Ping命令测试本地最佳MTU值。
  • MTU与路径MTU

    千次阅读 2018-11-20 10:43:57
    IP层一次传输的数据报上限被限制为1500字节,链路层为了保持与IP层兼容,最大传输数据报也被限制为1500字节,这种特征被称为最大传输单元MTU。...在所有链路的整个网络路径上,最小的MTU被称为路径...
  • TCP/IP 最大路径MTU

    2020-09-22 22:07:08
    一. 最大路径MTU概念 二. 基于ICMP的PMTUD 三.UDP的路径MTU发现 四.TCP的路径MTU发现
  • 电信设备-确定路径MTU的方法、装置及通信系统.zip
  • MTU和MSS详解

    2019-12-16 16:31:37
    MTU: Maximum Transmit Unit,最大传输单元,即物理接口(数据链路层)提供给其上层最大一次传输数据的大小,比如IP层、MPLS层等等,因为目前应用最多的接口是以太网,所以谈谈以太网口的MTU,假定其上层协议是IP,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,675
精华内容 11,070
关键字:

路径mtu发现

友情链接: non_domination_sort_mod.rar