[1]Summary of the problem: Kernel does not delete the space of the options which not allowed in fragments.
[2]Full description of the problem: ip_options_fragment() just fill options which not allowed in fragments with NOOPs, does not delete the space of the options, following patch has corrected the problem. Refer to FreeBSD, the space of the options which not allowed in fragments will be delete. If the space does not be deleted, the network traffic will be increased. --- linux-2.6.15.1/net/ipv4/ip_options.c.orig 2006-01-27 09:14:33.000000000 +0900 +++ linux-2.6.15.1/net/ipv4/ip_options.c 2006-01-27 09:45:26.000000000 +0900 @@ -211,29 +211,59 @@ struct ip_options * opt = &(IPCB(skb)->opt); int l = opt->optlen; int optlen; + int optneed = 0; + unsigned char * pp_ptr = optptr; while (l > 0) { switch (*optptr) { case IPOPT_END: - return; + goto end; case IPOPT_NOOP: l--; + if(optptr != pp_ptr) + memcpy(pp_ptr, optptr, 1); optptr++; + pp_ptr++; continue; } optlen = optptr[1]; - if (optlen<2 || optlen>l) - return; - if (!IPOPT_COPIED(*optptr)) - memset(optptr, IPOPT_NOOP, optlen); + if (optlen<2 || optlen>l) { + if(optptr != pp_ptr) + memcpy(pp_ptr, optptr, l); + optptr += l; + pp_ptr += l; + optneed = 1; + goto error; + } + if (IPOPT_COPIED(*optptr)) { + if(optptr != pp_ptr) + memcpy(pp_ptr, optptr, optlen); + pp_ptr += optlen; + optneed = 1; + } l -= optlen; optptr += optlen; } +end: opt->ts = 0; opt->rr = 0; opt->rr_needaddr = 0; opt->ts_needaddr = 0; opt->ts_needtime = 0; +error: + if (pp_ptr != optptr) { + if (optneed == 1) { + opt->optlen -= optptr - pp_ptr; + if (opt->optlen & 0x03) { + for (l = 0; l < 4 - (opt->optlen & 0x03); l++) + *pp_ptr++ = IPOPT_END; + opt->optlen = (opt->optlen + 3) & ~3; + } + } else { + opt->optlen = 0; + } + skb->nh.iph->ihl = 5 + (opt->optlen >> 2); + } return; } --- linux-2.6.15.1/net/ipv4/ip_output.c.orig 2006-01-27 09:49:17.339815360 +0900 +++ linux-2.6.15.1/net/ipv4/ip_output.c 2006-01-23 09:09:13.000000000 +0900 @@ -503,12 +503,19 @@ frag->h.raw = frag->data; frag->nh.raw = __skb_push(frag, hlen); memcpy(frag->nh.raw, iph, hlen); + offset += skb->len - hlen; + if (offset == skb->len - hlen) { + ip_options_fragment(frag); + len = frag->nh.iph->ihl * 4; + if (hlen != len) { + memmove(frag->nh.raw, frag->h.raw - len, len); + frag->nh.raw = __skb_pull(frag, hlen - len); + hlen = len; + } + } iph = frag->nh.iph; 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 != NULL) iph->frag_off |= htons(IP_MF); @@ -619,6 +626,7 @@ */ iph = skb2->nh.iph; iph->frag_off = htons((offset >> 3)); + iph->tot_len = htons(len + hlen); /* ANK: dirty, but effective trick. Upgrade options only if * the segment to be fragmented was THE FIRST (otherwise, @@ -626,8 +634,11 @@ * on the initial skb, so that all the following fragments * will inherit fixed options. */ - if (offset == 0) + if (offset == 0) { ip_options_fragment(skb); + hlen = skb->nh.iph->ihl * 4; + mtu = dst_pmtu(&rt->u.dst) - hlen; + } /* * Added AC : If we are fragmenting a fragment that's not the @@ -644,8 +655,6 @@ IP_INC_STATS(IPSTATS_MIB_FRAGCREATES); - iph->tot_len = htons(len + hlen); - ip_send_check(iph); err = output(skb2);
option_not_copy.patch
Description: Binary data