diff options
Diffstat (limited to 'drivers/net/ipvlan/ipvlan_main.c')
-rw-r--r-- | drivers/net/ipvlan/ipvlan_main.c | 164 |
1 files changed, 110 insertions, 54 deletions
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index c74893c1e620..30cb803e2fe5 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -107,16 +107,6 @@ static int ipvlan_port_create(struct net_device *dev) struct ipvl_port *port; int err, idx; - if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) { - netdev_err(dev, "Master is either lo or non-ether device\n"); - return -EINVAL; - } - - if (netdev_is_rx_handler_busy(dev)) { - netdev_err(dev, "Device is already in use.\n"); - return -EBUSY; - } - port = kzalloc(sizeof(struct ipvl_port), GFP_KERNEL); if (!port) return -ENOMEM; @@ -179,8 +169,9 @@ static void ipvlan_port_destroy(struct net_device *dev) static int ipvlan_init(struct net_device *dev) { struct ipvl_dev *ipvlan = netdev_priv(dev); - const struct net_device *phy_dev = ipvlan->phy_dev; - struct ipvl_port *port = ipvlan->port; + struct net_device *phy_dev = ipvlan->phy_dev; + struct ipvl_port *port; + int err; dev->state = (dev->state & ~IPVLAN_STATE_MASK) | (phy_dev->state & IPVLAN_STATE_MASK); @@ -196,18 +187,27 @@ static int ipvlan_init(struct net_device *dev) if (!ipvlan->pcpu_stats) return -ENOMEM; + if (!netif_is_ipvlan_port(phy_dev)) { + err = ipvlan_port_create(phy_dev); + if (err < 0) { + free_percpu(ipvlan->pcpu_stats); + return err; + } + } + port = ipvlan_port_get_rtnl(phy_dev); port->count += 1; - return 0; } static void ipvlan_uninit(struct net_device *dev) { struct ipvl_dev *ipvlan = netdev_priv(dev); - struct ipvl_port *port = ipvlan->port; + struct net_device *phy_dev = ipvlan->phy_dev; + struct ipvl_port *port; free_percpu(ipvlan->pcpu_stats); + port = ipvlan_port_get_rtnl(phy_dev); port->count -= 1; if (!port->count) ipvlan_port_destroy(port->dev); @@ -407,7 +407,7 @@ static int ipvlan_hard_header(struct sk_buff *skb, struct net_device *dev, * while the packets use the mac-addr on the physical device. */ return dev_hard_header(skb, phy_dev, type, daddr, - saddr ? : dev->dev_addr, len); + saddr ? : phy_dev->dev_addr, len); } static const struct header_ops ipvlan_header_ops = { @@ -462,11 +462,29 @@ static int ipvlan_nl_changelink(struct net_device *dev, struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev); int err = 0; - if (data && data[IFLA_IPVLAN_MODE]) { + if (!data) + return 0; + + if (data[IFLA_IPVLAN_MODE]) { u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]); err = ipvlan_set_port_mode(port, nmode); } + + if (!err && data[IFLA_IPVLAN_FLAGS]) { + u16 flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); + + if (flags & IPVLAN_F_PRIVATE) + ipvlan_mark_private(port); + else + ipvlan_clear_private(port); + + if (flags & IPVLAN_F_VEPA) + ipvlan_mark_vepa(port); + else + ipvlan_clear_vepa(port); + } + return err; } @@ -474,18 +492,34 @@ static size_t ipvlan_nl_getsize(const struct net_device *dev) { return (0 + nla_total_size(2) /* IFLA_IPVLAN_MODE */ + + nla_total_size(2) /* IFLA_IPVLAN_FLAGS */ ); } static int ipvlan_nl_validate(struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - if (data && data[IFLA_IPVLAN_MODE]) { + if (!data) + return 0; + + if (data[IFLA_IPVLAN_MODE]) { u16 mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); if (mode < IPVLAN_MODE_L2 || mode >= IPVLAN_MODE_MAX) return -EINVAL; } + if (data[IFLA_IPVLAN_FLAGS]) { + u16 flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); + + /* Only two bits are used at this moment. */ + if (flags & ~(IPVLAN_F_PRIVATE | IPVLAN_F_VEPA)) + return -EINVAL; + /* Also both flags can't be active at the same time. */ + if ((flags & (IPVLAN_F_PRIVATE | IPVLAN_F_VEPA)) == + (IPVLAN_F_PRIVATE | IPVLAN_F_VEPA)) + return -EINVAL; + } + return 0; } @@ -502,6 +536,8 @@ static int ipvlan_nl_fillinfo(struct sk_buff *skb, ret = -EMSGSIZE; if (nla_put_u16(skb, IFLA_IPVLAN_MODE, port->mode)) goto err; + if (nla_put_u16(skb, IFLA_IPVLAN_FLAGS, port->flags)) + goto err; return 0; @@ -518,7 +554,6 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, struct net_device *phy_dev; int err; u16 mode = IPVLAN_MODE_L3; - bool create = false; if (!tb[IFLA_LINK]) return -EINVAL; @@ -532,23 +567,42 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, phy_dev = tmp->phy_dev; } else if (!netif_is_ipvlan_port(phy_dev)) { - err = ipvlan_port_create(phy_dev); - if (err < 0) - return err; - create = true; - } + /* Exit early if the underlying link is invalid or busy */ + if (phy_dev->type != ARPHRD_ETHER || + phy_dev->flags & IFF_LOOPBACK) { + netdev_err(phy_dev, + "Master is either lo or non-ether device\n"); + return -EINVAL; + } - if (data && data[IFLA_IPVLAN_MODE]) - mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); + if (netdev_is_rx_handler_busy(phy_dev)) { + netdev_err(phy_dev, "Device is already in use.\n"); + return -EBUSY; + } + } - port = ipvlan_port_get_rtnl(phy_dev); ipvlan->phy_dev = phy_dev; ipvlan->dev = dev; - ipvlan->port = port; ipvlan->sfeatures = IPVLAN_FEATURES; ipvlan_adjust_mtu(ipvlan, phy_dev); INIT_LIST_HEAD(&ipvlan->addrs); + /* TODO Probably put random address here to be presented to the + * world but keep using the physical-dev address for the outgoing + * packets. + */ + memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN); + + dev->priv_flags |= IFF_IPVLAN_SLAVE; + + err = register_netdevice(dev); + if (err < 0) + return err; + + /* ipvlan_init() would have created the port, if required */ + port = ipvlan_port_get_rtnl(phy_dev); + ipvlan->port = port; + /* If the port-id base is at the MAX value, then wrap it around and * begin from 0x1 again. This may be due to a busy system where lots * of slaves are getting created and deleted. @@ -567,31 +621,28 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, err = ida_simple_get(&port->ida, 0x1, port->dev_id_start, GFP_KERNEL); if (err < 0) - goto destroy_ipvlan_port; + goto unregister_netdev; dev->dev_id = err; + /* Increment id-base to the next slot for the future assignment */ port->dev_id_start = err + 1; - /* TODO Probably put random address here to be presented to the - * world but keep using the physical-dev address for the outgoing - * packets. - */ - memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN); + err = netdev_upper_dev_link(phy_dev, dev, extack); + if (err) + goto remove_ida; - dev->priv_flags |= IFF_IPVLAN_SLAVE; + /* Flags are per port and latest update overrides. User has + * to be consistent in setting it just like the mode attribute. + */ + if (data && data[IFLA_IPVLAN_FLAGS]) + port->flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); - err = register_netdevice(dev); - if (err < 0) - goto remove_ida; + if (data && data[IFLA_IPVLAN_MODE]) + mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); - err = netdev_upper_dev_link(phy_dev, dev); - if (err) { - goto unregister_netdev; - } err = ipvlan_set_port_mode(port, mode); - if (err) { + if (err) goto unlink_netdev; - } list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); netif_stacked_transfer_operstate(phy_dev, dev); @@ -599,13 +650,10 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, unlink_netdev: netdev_upper_dev_unlink(phy_dev, dev); -unregister_netdev: - unregister_netdevice(dev); remove_ida: ida_simple_remove(&port->ida, dev->dev_id); -destroy_ipvlan_port: - if (create) - ipvlan_port_destroy(phy_dev); +unregister_netdev: + unregister_netdevice(dev); return err; } EXPORT_SYMBOL_GPL(ipvlan_link_new); @@ -644,6 +692,7 @@ EXPORT_SYMBOL_GPL(ipvlan_link_setup); static const struct nla_policy ipvlan_nl_policy[IFLA_IPVLAN_MAX + 1] = { [IFLA_IPVLAN_MODE] = { .type = NLA_U16 }, + [IFLA_IPVLAN_FLAGS] = { .type = NLA_U16 }, }; static struct rtnl_link_ops ipvlan_link_ops = { @@ -730,6 +779,11 @@ static int ipvlan_device_event(struct notifier_block *unused, ipvlan_adjust_mtu(ipvlan, dev); break; + case NETDEV_CHANGEADDR: + list_for_each_entry(ipvlan, &port->ipvlans, pnode) + ether_addr_copy(ipvlan->dev->dev_addr, dev->dev_addr); + break; + case NETDEV_PRE_TYPE_CHANGE: /* Forbid underlying device to change its type. */ return NOTIFY_BAD; @@ -803,10 +857,6 @@ static int ipvlan_addr6_event(struct notifier_block *unused, struct net_device *dev = (struct net_device *)if6->idev->dev; struct ipvl_dev *ipvlan = netdev_priv(dev); - /* FIXME IPv6 autoconf calls us from bh without RTNL */ - if (in_softirq()) - return NOTIFY_DONE; - if (!netif_is_ipvlan(dev)) return NOTIFY_DONE; @@ -846,8 +896,11 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused, switch (event) { case NETDEV_UP: - if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) + if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) { + NL_SET_ERR_MSG(i6vi->extack, + "Address already assigned to an ipvlan device"); return notifier_from_errno(-EADDRINUSE); + } break; } @@ -916,8 +969,11 @@ static int ipvlan_addr4_validator_event(struct notifier_block *unused, switch (event) { case NETDEV_UP: - if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) + if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) { + NL_SET_ERR_MSG(ivi->extack, + "Address already assigned to an ipvlan device"); return notifier_from_errno(-EADDRINUSE); + } break; } |