diff options
author | Mahesh Bandewar <maheshb@google.com> | 2016-09-16 15:59:19 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-09-19 01:25:22 -0400 |
commit | 4fbae7d83c98c30efcf0a2a2ac55fbb75ef5a1a5 (patch) | |
tree | 3ea819d38ad4fbbae8d4db166f58451c2a78ee20 /drivers/net/ipvlan/ipvlan_main.c | |
parent | e8bffe0cf964f0330595bb376b74921cccdaac88 (diff) |
ipvlan: Introduce l3s mode
In a typical IPvlan L3 setup where master is in default-ns and
each slave is into different (slave) ns. In this setup egress
packet processing for traffic originating from slave-ns will
hit all NF_HOOKs in slave-ns as well as default-ns. However same
is not true for ingress processing. All these NF_HOOKs are
hit only in the slave-ns skipping them in the default-ns.
IPvlan in L3 mode is restrictive and if admins want to deploy
iptables rules in default-ns, this asymmetric data path makes it
impossible to do so.
This patch makes use of the l3_rcv() (added as part of l3mdev
enhancements) to perform input route lookup on RX packets without
changing the skb->dev and then uses nf_hook at NF_INET_LOCAL_IN
to change the skb->dev just before handing over skb to L4.
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
CC: David Ahern <dsa@cumulusnetworks.com>
Reviewed-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ipvlan/ipvlan_main.c')
-rw-r--r-- | drivers/net/ipvlan/ipvlan_main.c | 87 |
1 files changed, 80 insertions, 7 deletions
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 18b4e8c7f68a..f442eb366863 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c | |||
@@ -9,24 +9,87 @@ | |||
9 | 9 | ||
10 | #include "ipvlan.h" | 10 | #include "ipvlan.h" |
11 | 11 | ||
12 | static u32 ipvl_nf_hook_refcnt = 0; | ||
13 | |||
14 | static struct nf_hook_ops ipvl_nfops[] __read_mostly = { | ||
15 | { | ||
16 | .hook = ipvlan_nf_input, | ||
17 | .pf = NFPROTO_IPV4, | ||
18 | .hooknum = NF_INET_LOCAL_IN, | ||
19 | .priority = INT_MAX, | ||
20 | }, | ||
21 | { | ||
22 | .hook = ipvlan_nf_input, | ||
23 | .pf = NFPROTO_IPV6, | ||
24 | .hooknum = NF_INET_LOCAL_IN, | ||
25 | .priority = INT_MAX, | ||
26 | }, | ||
27 | }; | ||
28 | |||
29 | static struct l3mdev_ops ipvl_l3mdev_ops __read_mostly = { | ||
30 | .l3mdev_l3_rcv = ipvlan_l3_rcv, | ||
31 | }; | ||
32 | |||
12 | static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev) | 33 | static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev) |
13 | { | 34 | { |
14 | ipvlan->dev->mtu = dev->mtu - ipvlan->mtu_adj; | 35 | ipvlan->dev->mtu = dev->mtu - ipvlan->mtu_adj; |
15 | } | 36 | } |
16 | 37 | ||
17 | static void ipvlan_set_port_mode(struct ipvl_port *port, u16 nval) | 38 | static int ipvlan_register_nf_hook(void) |
39 | { | ||
40 | int err = 0; | ||
41 | |||
42 | if (!ipvl_nf_hook_refcnt) { | ||
43 | err = _nf_register_hooks(ipvl_nfops, ARRAY_SIZE(ipvl_nfops)); | ||
44 | if (!err) | ||
45 | ipvl_nf_hook_refcnt = 1; | ||
46 | } else { | ||
47 | ipvl_nf_hook_refcnt++; | ||
48 | } | ||
49 | |||
50 | return err; | ||
51 | } | ||
52 | |||
53 | static void ipvlan_unregister_nf_hook(void) | ||
54 | { | ||
55 | WARN_ON(!ipvl_nf_hook_refcnt); | ||
56 | |||
57 | ipvl_nf_hook_refcnt--; | ||
58 | if (!ipvl_nf_hook_refcnt) | ||
59 | _nf_unregister_hooks(ipvl_nfops, ARRAY_SIZE(ipvl_nfops)); | ||
60 | } | ||
61 | |||
62 | static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval) | ||
18 | { | 63 | { |
19 | struct ipvl_dev *ipvlan; | 64 | struct ipvl_dev *ipvlan; |
65 | struct net_device *mdev = port->dev; | ||
66 | int err = 0; | ||
20 | 67 | ||
68 | ASSERT_RTNL(); | ||
21 | if (port->mode != nval) { | 69 | if (port->mode != nval) { |
70 | if (nval == IPVLAN_MODE_L3S) { | ||
71 | /* New mode is L3S */ | ||
72 | err = ipvlan_register_nf_hook(); | ||
73 | if (!err) { | ||
74 | mdev->l3mdev_ops = &ipvl_l3mdev_ops; | ||
75 | mdev->priv_flags |= IFF_L3MDEV_MASTER; | ||
76 | } else | ||
77 | return err; | ||
78 | } else if (port->mode == IPVLAN_MODE_L3S) { | ||
79 | /* Old mode was L3S */ | ||
80 | mdev->priv_flags &= ~IFF_L3MDEV_MASTER; | ||
81 | ipvlan_unregister_nf_hook(); | ||
82 | mdev->l3mdev_ops = NULL; | ||
83 | } | ||
22 | list_for_each_entry(ipvlan, &port->ipvlans, pnode) { | 84 | list_for_each_entry(ipvlan, &port->ipvlans, pnode) { |
23 | if (nval == IPVLAN_MODE_L3) | 85 | if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S) |
24 | ipvlan->dev->flags |= IFF_NOARP; | 86 | ipvlan->dev->flags |= IFF_NOARP; |
25 | else | 87 | else |
26 | ipvlan->dev->flags &= ~IFF_NOARP; | 88 | ipvlan->dev->flags &= ~IFF_NOARP; |
27 | } | 89 | } |
28 | port->mode = nval; | 90 | port->mode = nval; |
29 | } | 91 | } |
92 | return err; | ||
30 | } | 93 | } |
31 | 94 | ||
32 | static int ipvlan_port_create(struct net_device *dev) | 95 | static int ipvlan_port_create(struct net_device *dev) |
@@ -74,6 +137,11 @@ static void ipvlan_port_destroy(struct net_device *dev) | |||
74 | struct ipvl_port *port = ipvlan_port_get_rtnl(dev); | 137 | struct ipvl_port *port = ipvlan_port_get_rtnl(dev); |
75 | 138 | ||
76 | dev->priv_flags &= ~IFF_IPVLAN_MASTER; | 139 | dev->priv_flags &= ~IFF_IPVLAN_MASTER; |
140 | if (port->mode == IPVLAN_MODE_L3S) { | ||
141 | dev->priv_flags &= ~IFF_L3MDEV_MASTER; | ||
142 | ipvlan_unregister_nf_hook(); | ||
143 | dev->l3mdev_ops = NULL; | ||
144 | } | ||
77 | netdev_rx_handler_unregister(dev); | 145 | netdev_rx_handler_unregister(dev); |
78 | cancel_work_sync(&port->wq); | 146 | cancel_work_sync(&port->wq); |
79 | __skb_queue_purge(&port->backlog); | 147 | __skb_queue_purge(&port->backlog); |
@@ -132,7 +200,8 @@ static int ipvlan_open(struct net_device *dev) | |||
132 | struct net_device *phy_dev = ipvlan->phy_dev; | 200 | struct net_device *phy_dev = ipvlan->phy_dev; |
133 | struct ipvl_addr *addr; | 201 | struct ipvl_addr *addr; |
134 | 202 | ||
135 | if (ipvlan->port->mode == IPVLAN_MODE_L3) | 203 | if (ipvlan->port->mode == IPVLAN_MODE_L3 || |
204 | ipvlan->port->mode == IPVLAN_MODE_L3S) | ||
136 | dev->flags |= IFF_NOARP; | 205 | dev->flags |= IFF_NOARP; |
137 | else | 206 | else |
138 | dev->flags &= ~IFF_NOARP; | 207 | dev->flags &= ~IFF_NOARP; |
@@ -372,13 +441,14 @@ static int ipvlan_nl_changelink(struct net_device *dev, | |||
372 | { | 441 | { |
373 | struct ipvl_dev *ipvlan = netdev_priv(dev); | 442 | struct ipvl_dev *ipvlan = netdev_priv(dev); |
374 | struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev); | 443 | struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev); |
444 | int err = 0; | ||
375 | 445 | ||
376 | if (data && data[IFLA_IPVLAN_MODE]) { | 446 | if (data && data[IFLA_IPVLAN_MODE]) { |
377 | u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]); | 447 | u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]); |
378 | 448 | ||
379 | ipvlan_set_port_mode(port, nmode); | 449 | err = ipvlan_set_port_mode(port, nmode); |
380 | } | 450 | } |
381 | return 0; | 451 | return err; |
382 | } | 452 | } |
383 | 453 | ||
384 | static size_t ipvlan_nl_getsize(const struct net_device *dev) | 454 | static size_t ipvlan_nl_getsize(const struct net_device *dev) |
@@ -473,10 +543,13 @@ static int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
473 | unregister_netdevice(dev); | 543 | unregister_netdevice(dev); |
474 | return err; | 544 | return err; |
475 | } | 545 | } |
546 | err = ipvlan_set_port_mode(port, mode); | ||
547 | if (err) { | ||
548 | unregister_netdevice(dev); | ||
549 | return err; | ||
550 | } | ||
476 | 551 | ||
477 | list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); | 552 | list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); |
478 | ipvlan_set_port_mode(port, mode); | ||
479 | |||
480 | netif_stacked_transfer_operstate(phy_dev, dev); | 553 | netif_stacked_transfer_operstate(phy_dev, dev); |
481 | return 0; | 554 | return 0; |
482 | } | 555 | } |