[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);

Attachment: option_not_copy.patch
Description: Binary data

Reply via email to