Fix datapath make_writable() function.
authorBen Pfaff <blp@nicira.com>
Tue, 11 Nov 2008 23:40:28 +0000 (15:40 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 13 Nov 2008 20:44:04 +0000 (12:44 -0800)
- We weren't necessarily pulling enough into the headers in the non-shared
  case, since the IP header by itself can be longer than 40 bytes.

- The skb is guaranteed not to have a destructor at this point.

datapath/dp_act.c

index daa319a2a5e7fe30495eb6f5596524454fae1b90..14eaac33a341674863329bc0c023c1af2f269e4b 100644 (file)
@@ -502,26 +502,17 @@ void execute_actions(struct datapath *dp, struct sk_buff *skb,
 int
 make_writable(struct sk_buff **pskb)
 {
-       /* Based on skb_make_writable() in net/netfilter/core.c. */
-       struct sk_buff *nskb;
-
-       /* Not exclusive use of packet?  Must copy. */
-       if (skb_shared(*pskb) || skb_cloned(*pskb))
-               goto copy_skb;
-
-       return pskb_may_pull(*pskb, 40); /* FIXME? */
-
-copy_skb:
-       nskb = skb_copy(*pskb, GFP_ATOMIC);
-       if (!nskb)
-               return 0;
-       BUG_ON(skb_is_nonlinear(nskb));
-
-       /* Rest of kernel will get very unhappy if we pass it a
-          suddenly-orphaned skbuff */
-       if ((*pskb)->sk)
-               skb_set_owner_w(nskb, (*pskb)->sk);
-       kfree_skb(*pskb);
-       *pskb = nskb;
-       return 1;
+       struct sk_buff *skb = *pskb;
+       if (skb_shared(skb) || skb_cloned(skb)) {
+               struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
+               if (!nskb)
+                       return 0;
+               kfree_skb(skb);
+               *pskb = nskb;
+               return 1;
+       } else {
+               unsigned int hdr_len = (skb_transport_offset(skb)
+                                       + sizeof(struct tcphdr));
+               return pskb_may_pull(skb, min(hdr_len, skb->len));
+       }
 }