Commit d8269e2c authored by Edward Cree's avatar Edward Cree Committed by David S. Miller

net: ipv6: listify ipv6_rcv() and ip6_rcv_finish()

Essentially the same as the ipv4 equivalents.
Signed-off-by: default avatarEdward Cree <ecree@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent efe6aaca
...@@ -922,6 +922,8 @@ static inline __be32 flowi6_get_flowlabel(const struct flowi6 *fl6) ...@@ -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, int ipv6_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_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); int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
......
...@@ -764,6 +764,7 @@ EXPORT_SYMBOL_GPL(ipv6_opt_accepted); ...@@ -764,6 +764,7 @@ EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
static struct packet_type ipv6_packet_type __read_mostly = { static struct packet_type ipv6_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_IPV6), .type = cpu_to_be16(ETH_P_IPV6),
.func = ipv6_rcv, .func = ipv6_rcv,
.list_func = ipv6_list_rcv,
}; };
static int __init ipv6_packet_init(void) static int __init ipv6_packet_init(void)
......
...@@ -47,17 +47,11 @@ ...@@ -47,17 +47,11 @@
#include <net/inet_ecn.h> #include <net/inet_ecn.h>
#include <net/dst_metadata.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); 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) { if (net->ipv4.sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
const struct inet6_protocol *ipprot; const struct inet6_protocol *ipprot;
...@@ -67,20 +61,73 @@ int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) ...@@ -67,20 +61,73 @@ int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
} }
if (!skb_valid_dst(skb)) if (!skb_valid_dst(skb))
ip6_route_input(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); 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; const struct ipv6hdr *hdr;
u32 pkt_len; u32 pkt_len;
struct inet6_dev *idev; struct inet6_dev *idev;
struct net *net = dev_net(skb->dev);
if (skb->pkt_type == PACKET_OTHERHOST) { if (skb->pkt_type == PACKET_OTHERHOST) {
kfree_skb(skb); kfree_skb(skb);
return NET_RX_DROP; return NULL;
} }
rcu_read_lock(); rcu_read_lock();
...@@ -196,7 +243,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt ...@@ -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) { if (ipv6_parse_hopopts(skb) < 0) {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
rcu_read_unlock(); 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 ...@@ -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. */ /* Must drop socket now because of tproxy. */
skb_orphan(skb); skb_orphan(skb);
return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, return skb;
net, NULL, skb, dev, NULL,
ip6_rcv_finish);
err: err:
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
drop: drop:
rcu_read_unlock(); rcu_read_unlock();
kfree_skb(skb); 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);
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment