- 论坛徽章:
- 0
|
本帖最后由 wangjianchangdx 于 2015-07-04 00:38 编辑
ip_fragment 进行分片时分为fast path和slow path。
如果skb已经有frag list, 则直接使用,即为fast path;
slow path则对skb直接进行分片处理。
在fast path中,如下代码遍历frag list,并使用传入的output函数发送frag。- for (;;) {
- /* Prepare header of the next frame,
- * before previous one went down. */
- if (frag) {
- frag->ip_summed = CHECKSUM_NONE;
- skb_reset_transport_header(frag);
- __skb_push(frag, hlen);
- skb_reset_network_header(frag);
- memcpy(skb_network_header(frag), iph, hlen);
- iph = ip_hdr(frag);
- iph->tot_len = htons(frag->len);
- ip_copy_metadata(frag, skb);
- if (offset == 0)
- ip_options_fragment(frag);
- offset += skb->len - hlen;
- iph->frag_off = htons(offset>>3);
- if (frag->next)
- iph->frag_off |= htons(IP_MF);
- /* Ready, complete checksum */
- ip_send_check(iph);
- }
- err = output(sk, skb);
- if (!err)
- IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
- if (err || !frag)
- break;
复制代码 其中,ip_options_fragment为对第二个分片处理。
- if (offset == 0)
- ip_options_fragment(frag);
复制代码 而在slow path中,ip_options_fragment为对第一个分片的处理。
- /* ANK: dirty, but effective trick. Upgrade options only if
- * the segment to be fragmented was THE FIRST (otherwise,
- * options are already fixed) and make it ONCE
- * on the initial skb, so that all the following fragments
- * will inherit fixed options.
- */
- if (offset == 0)
- ip_options_fragment(skb);
复制代码 从代码上看,fast path中的处理也有矛盾之处:
既然已经从前一个分片中拷贝了iphdr,为何还要再进行选项处理?
全部IP分片的iphdr除了frag flag & offset之外,不应该是一样的吗?
实测中,在桥中,mtu为1500时,Port 1 ping Port 2: ping 192.168.1.2 -l 1516字节的数据包会出错。
1516字节的ICMP Echo Reply中,第二个分片frag->len为64:64/4=16=0x10, 0x10&7=0,走fast path。
- skb_walk_frags(skb, frag) {
- /* Correct geometry. */
- if (frag->len > mtu ||
- ((frag->len & 7) && frag->next) ||
- skb_headroom(frag) < hlen)
- goto slow_path_clean;
- /* Partially cloned skb? */
- if (skb_shared(frag))
- goto slow_path_clean;
- BUG_ON(frag->sk);
- if (skb->sk) {
- frag->sk = skb->sk;
- frag->destructor = sock_wfree;
- }
- skb->truesize -= frag->truesize;
- }
复制代码 第二个分片的内容会被ip_options_fragment中的memset(optptr, IPOPT_NOOP, optlen);置为1,即IPOPT_NOOP.
|
|