|
9 | 9 |
|
10 | 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 | 33 | static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev) |
13 | 34 | { |
14 | 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 | 64 | struct ipvl_dev *ipvlan; |
| 65 | + struct net_device *mdev = port->dev; |
| 66 | + int err = 0; |
20 | 67 |
|
| 68 | + ASSERT_RTNL(); |
21 | 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 | 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 | 86 | ipvlan->dev->flags |= IFF_NOARP; |
25 | 87 | else |
26 | 88 | ipvlan->dev->flags &= ~IFF_NOARP; |
27 | 89 | } |
28 | 90 | port->mode = nval; |
29 | 91 | } |
| 92 | + return err; |
30 | 93 | } |
31 | 94 |
|
32 | 95 | static int ipvlan_port_create(struct net_device *dev) |
@@ -74,6 +137,11 @@ static void ipvlan_port_destroy(struct net_device *dev) |
74 | 137 | struct ipvl_port *port = ipvlan_port_get_rtnl(dev); |
75 | 138 |
|
76 | 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 | 145 | netdev_rx_handler_unregister(dev); |
78 | 146 | cancel_work_sync(&port->wq); |
79 | 147 | __skb_queue_purge(&port->backlog); |
@@ -132,7 +200,8 @@ static int ipvlan_open(struct net_device *dev) |
132 | 200 | struct net_device *phy_dev = ipvlan->phy_dev; |
133 | 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 | 205 | dev->flags |= IFF_NOARP; |
137 | 206 | else |
138 | 207 | dev->flags &= ~IFF_NOARP; |
@@ -372,13 +441,14 @@ static int ipvlan_nl_changelink(struct net_device *dev, |
372 | 441 | { |
373 | 442 | struct ipvl_dev *ipvlan = netdev_priv(dev); |
374 | 443 | struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev); |
| 444 | + int err = 0; |
375 | 445 |
|
376 | 446 | if (data && data[IFLA_IPVLAN_MODE]) { |
377 | 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 | 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 | 543 | unregister_netdevice(dev); |
474 | 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 | 552 | list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); |
478 | | - ipvlan_set_port_mode(port, mode); |
479 | | - |
480 | 553 | netif_stacked_transfer_operstate(phy_dev, dev); |
481 | 554 | return 0; |
482 | 555 | } |
|
0 commit comments