ipvlan: improvise dev_id generation logic in IPvlan
authorMahesh Bandewar <maheshb@google.com>
Mon, 9 Jan 2017 23:05:54 +0000 (15:05 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 11 Jan 2017 01:47:12 +0000 (20:47 -0500)
The patch 009146d117b ("ipvlan: assign unique dev-id for each slave
device.") used ida_simple_get() to generate dev_ids assigned to the
slave devices. However (Eric has pointed out that) there is a shortcoming
with that approach as it always uses the first available ID. This
becomes a problem when a slave gets deleted and a new slave gets added.
The ID gets reassigned causing the new slave to get the same link-local
address. This side-effect is undesirable.

This patch adds a per-port variable that keeps track of the IDs
assigned and used as the stat-base for the IDR api. This base will be
wrapped around when it reaches the MAX (0xFFFE) value possibly on a
busy system where slaves are added and deleted routinely.

Fixes: 009146d117b ("ipvlan: assign unique dev-id for each slave device.")
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
CC: Eric Dumazet <edumazet@google.com>
CC: David Miller <davem@davemloft.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ipvlan/ipvlan.h
drivers/net/ipvlan/ipvlan_main.c

index 0a9068fdee0f15a8c402685734b0aee4970e05ac..406ae4ff0ae891aa6bd1522bbb367699f16109b1 100644 (file)
@@ -94,6 +94,7 @@ struct ipvl_port {
        struct hlist_head       hlhead[IPVLAN_HASH_SIZE];
        struct list_head        ipvlans;
        u16                     mode;
+       u16                     dev_id_start;
        struct work_struct      wq;
        struct sk_buff_head     backlog;
        int                     count;
index 1cdb8c5ec40354492dab50037b8ccf14a491ae6e..92b221a033503d982a48a4112c01b0dbbfcb3287 100644 (file)
@@ -120,6 +120,7 @@ static int ipvlan_port_create(struct net_device *dev)
        skb_queue_head_init(&port->backlog);
        INIT_WORK(&port->wq, ipvlan_process_multicast);
        ida_init(&port->ida);
+       port->dev_id_start = 1;
 
        err = netdev_rx_handler_register(dev, ipvlan_handle_frame, port);
        if (err)
@@ -534,15 +535,25 @@ static int ipvlan_link_new(struct net *src_net, struct net_device *dev,
        ipvlan_adjust_mtu(ipvlan, phy_dev);
        INIT_LIST_HEAD(&ipvlan->addrs);
 
+       /* 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.
+        */
+       if (port->dev_id_start == 0xFFFE)
+               port->dev_id_start = 0x1;
+
        /* Since L2 address is shared among all IPvlan slaves including
         * master, use unique 16 bit dev-ids to diffentiate among them.
         * Assign IDs between 0x1 and 0xFFFE (used by the master) to each
         * slave link [see addrconf_ifid_eui48()].
         */
-       err = ida_simple_get(&port->ida, 1, 0xFFFE, GFP_KERNEL);
+       err = ida_simple_get(&port->ida, port->dev_id_start, 0xFFFE,
+                            GFP_KERNEL);
        if (err < 0)
                goto destroy_ipvlan_port;
        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