net: ipv6: listify ipv6_rcv() and ip6_rcv_finish()
authorEdward Cree <ecree@solarflare.com>
Thu, 5 Jul 2018 14:49:42 +0000 (15:49 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 6 Jul 2018 02:19:07 +0000 (11:19 +0900)
Essentially the same as the ipv4 equivalents.

Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ipv6.h
net/ipv6/af_inet6.c
net/ipv6/ip6_input.c

index 16475c269749a72f3c487e102e50cabff797317e..b7843e0b16ee5f180b12410317d36639545b6c19 100644 (file)
@@ -922,6 +922,8 @@ static inline __be32 flowi6_get_flowlabel(const struct flowi6 *fl6)
 
 int ipv6_rcv(struct sk_buff *skb, struct net_device *dev,
             struct packet_type *pt, struct net_device *orig_dev);
+void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
+                  struct net_device *orig_dev);
 
 int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
 
index 9ed0eae91758f8506b4f6ca0fe3a9c2dc3fe1323..c9535354149fd3149f4f9d491c2aa14562fb2c46 100644 (file)
@@ -764,6 +764,7 @@ EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
 static struct packet_type ipv6_packet_type __read_mostly = {
        .type = cpu_to_be16(ETH_P_IPV6),
        .func = ipv6_rcv,
+       .list_func = ipv6_list_rcv,
 };
 
 static int __init ipv6_packet_init(void)
index f08d34491eceeea4192bdb0482a61394da057476..6242682be876fd1193c8c3dac2b156265830076a 100644 (file)
 #include <net/inet_ecn.h>
 #include <net/dst_metadata.h>
 
-int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+static void ip6_rcv_finish_core(struct net *net, struct sock *sk,
+                               struct sk_buff *skb)
 {
        void (*edemux)(struct sk_buff *skb);
 
-       /* if ingress device is enslaved to an L3 master device pass the
-        * skb to its handler for processing
-        */
-       skb = l3mdev_ip6_rcv(skb);
-       if (!skb)
-               return NET_RX_SUCCESS;
-
        if (net->ipv4.sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
                const struct inet6_protocol *ipprot;
 
@@ -67,20 +61,73 @@ int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
        }
        if (!skb_valid_dst(skb))
                ip6_route_input(skb);
+}
+
+int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+       /* if ingress device is enslaved to an L3 master device pass the
+        * skb to its handler for processing
+        */
+       skb = l3mdev_ip6_rcv(skb);
+       if (!skb)
+               return NET_RX_SUCCESS;
+       ip6_rcv_finish_core(net, sk, skb);
 
        return dst_input(skb);
 }
 
-int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
+static void ip6_sublist_rcv_finish(struct list_head *head)
+{
+       struct sk_buff *skb, *next;
+
+       list_for_each_entry_safe(skb, next, head, list)
+               dst_input(skb);
+}
+
+static void ip6_list_rcv_finish(struct net *net, struct sock *sk,
+                               struct list_head *head)
+{
+       struct dst_entry *curr_dst = NULL;
+       struct sk_buff *skb, *next;
+       struct list_head sublist;
+
+       INIT_LIST_HEAD(&sublist);
+       list_for_each_entry_safe(skb, next, head, list) {
+               struct dst_entry *dst;
+
+               list_del(&skb->list);
+               /* if ingress device is enslaved to an L3 master device pass the
+                * skb to its handler for processing
+                */
+               skb = l3mdev_ip6_rcv(skb);
+               if (!skb)
+                       continue;
+               ip6_rcv_finish_core(net, sk, skb);
+               dst = skb_dst(skb);
+               if (curr_dst != dst) {
+                       /* dispatch old sublist */
+                       if (!list_empty(&sublist))
+                               ip6_sublist_rcv_finish(&sublist);
+                       /* start new sublist */
+                       INIT_LIST_HEAD(&sublist);
+                       curr_dst = dst;
+               }
+               list_add_tail(&skb->list, &sublist);
+       }
+       /* dispatch final sublist */
+       ip6_sublist_rcv_finish(&sublist);
+}
+
+static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
+                                   struct net *net)
 {
        const struct ipv6hdr *hdr;
        u32 pkt_len;
        struct inet6_dev *idev;
-       struct net *net = dev_net(skb->dev);
 
        if (skb->pkt_type == PACKET_OTHERHOST) {
                kfree_skb(skb);
-               return NET_RX_DROP;
+               return NULL;
        }
 
        rcu_read_lock();
@@ -196,7 +243,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
                if (ipv6_parse_hopopts(skb) < 0) {
                        __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
                        rcu_read_unlock();
-                       return NET_RX_DROP;
+                       return NULL;
                }
        }
 
@@ -205,15 +252,67 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
        /* Must drop socket now because of tproxy. */
        skb_orphan(skb);
 
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
-                      net, NULL, skb, dev, NULL,
-                      ip6_rcv_finish);
+       return skb;
 err:
        __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
 drop:
        rcu_read_unlock();
        kfree_skb(skb);
-       return NET_RX_DROP;
+       return NULL;
+}
+
+int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
+{
+       struct net *net = dev_net(skb->dev);
+
+       skb = ip6_rcv_core(skb, dev, net);
+       if (skb == NULL)
+               return NET_RX_DROP;
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
+                      net, NULL, skb, dev, NULL,
+                      ip6_rcv_finish);
+}
+
+static void ip6_sublist_rcv(struct list_head *head, struct net_device *dev,
+                           struct net *net)
+{
+       NF_HOOK_LIST(NFPROTO_IPV6, NF_INET_PRE_ROUTING, net, NULL,
+                    head, dev, NULL, ip6_rcv_finish);
+       ip6_list_rcv_finish(net, NULL, head);
+}
+
+/* Receive a list of IPv6 packets */
+void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
+                  struct net_device *orig_dev)
+{
+       struct net_device *curr_dev = NULL;
+       struct net *curr_net = NULL;
+       struct sk_buff *skb, *next;
+       struct list_head sublist;
+
+       INIT_LIST_HEAD(&sublist);
+       list_for_each_entry_safe(skb, next, head, list) {
+               struct net_device *dev = skb->dev;
+               struct net *net = dev_net(dev);
+
+               list_del(&skb->list);
+               skb = ip6_rcv_core(skb, dev, net);
+               if (skb == NULL)
+                       continue;
+
+               if (curr_dev != dev || curr_net != net) {
+                       /* dispatch old sublist */
+                       if (!list_empty(&sublist))
+                               ip6_sublist_rcv(&sublist, curr_dev, curr_net);
+                       /* start new sublist */
+                       INIT_LIST_HEAD(&sublist);
+                       curr_dev = dev;
+                       curr_net = net;
+               }
+               list_add_tail(&skb->list, &sublist);
+       }
+       /* dispatch final sublist */
+       ip6_sublist_rcv(&sublist, curr_dev, curr_net);
 }
 
 /*