OSDN Git Service

net: ipv4: place control buffer handling away from fragmentation iterators
authorPablo Neira Ayuso <pablo@netfilter.org>
Wed, 29 May 2019 11:25:35 +0000 (13:25 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 30 May 2019 21:18:18 +0000 (14:18 -0700)
Deal with the IPCB() area away from the iterators.

The bridge codebase has its own control buffer layout, move specific
IP control buffer handling into the IPv4 codepath.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/ip_output.c

index c3f1398..8bae773 100644 (file)
@@ -525,9 +525,6 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
 
        skb_copy_hash(to, from);
 
-       /* Copy the flags to each fragment. */
-       IPCB(to)->flags = IPCB(from)->flags;
-
 #ifdef CONFIG_NET_SCHED
        to->tc_index = from->tc_index;
 #endif
@@ -582,6 +579,18 @@ void ip_fraglist_init(struct sk_buff *skb, struct iphdr *iph,
 }
 EXPORT_SYMBOL(ip_fraglist_init);
 
+static void ip_fraglist_ipcb_prepare(struct sk_buff *skb,
+                                    struct ip_fraglist_iter *iter)
+{
+       struct sk_buff *to = iter->frag;
+
+       /* Copy the flags to each fragment. */
+       IPCB(to)->flags = IPCB(skb)->flags;
+
+       if (iter->offset == 0)
+               ip_options_fragment(to);
+}
+
 void ip_fraglist_prepare(struct sk_buff *skb, struct ip_fraglist_iter *iter)
 {
        unsigned int hlen = iter->hlen;
@@ -598,8 +607,6 @@ void ip_fraglist_prepare(struct sk_buff *skb, struct ip_fraglist_iter *iter)
        iph = iter->iph;
        iph->tot_len = htons(frag->len);
        ip_copy_metadata(frag, skb);
-       if (iter->offset == 0)
-               ip_options_fragment(frag);
        iter->offset += skb->len - hlen;
        iph->frag_off = htons(iter->offset >> 3);
        if (frag->next)
@@ -627,6 +634,25 @@ void ip_frag_init(struct sk_buff *skb, unsigned int hlen,
 }
 EXPORT_SYMBOL(ip_frag_init);
 
+static void ip_frag_ipcb(struct sk_buff *from, struct sk_buff *to,
+                        bool first_frag, struct ip_frag_state *state)
+{
+       /* Copy the flags to each fragment. */
+       IPCB(to)->flags = IPCB(from)->flags;
+
+       if (IPCB(from)->flags & IPSKB_FRAG_PMTU)
+               state->iph->frag_off |= htons(IP_DF);
+
+       /* 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 (first_frag)
+               ip_options_fragment(from);
+}
+
 struct sk_buff *ip_frag_next(struct sk_buff *skb, struct ip_frag_state *state)
 {
        unsigned int len = state->left;
@@ -685,18 +711,6 @@ struct sk_buff *ip_frag_next(struct sk_buff *skb, struct ip_frag_state *state)
        iph = ip_hdr(skb2);
        iph->frag_off = htons((state->offset >> 3));
 
-       if (IPCB(skb)->flags & IPSKB_FRAG_PMTU)
-               iph->frag_off |= htons(IP_DF);
-
-       /* 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 (state->offset == 0)
-               ip_options_fragment(skb);
-
        /*
         *      Added AC : If we are fragmenting a fragment that's not the
         *                 last fragment then keep MF on each bit
@@ -799,8 +813,10 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                for (;;) {
                        /* Prepare header of the next frame,
                         * before previous one went down. */
-                       if (iter.frag)
+                       if (iter.frag) {
+                               ip_fraglist_ipcb_prepare(skb, &iter);
                                ip_fraglist_prepare(skb, &iter);
+                       }
 
                        err = output(net, sk, skb);
 
@@ -844,11 +860,14 @@ slow_path:
         */
 
        while (state.left > 0) {
+               bool first_frag = (state.offset == 0);
+
                skb2 = ip_frag_next(skb, &state);
                if (IS_ERR(skb2)) {
                        err = PTR_ERR(skb2);
                        goto fail;
                }
+               ip_frag_ipcb(skb, skb2, first_frag, &state);
 
                /*
                 *      Put this fragment into the sending queue.