bridge: add support for defining port member vlans via hotplug ops
authorFelix Fietkau <nbd@nbd.name>
Fri, 7 Aug 2020 12:19:06 +0000 (14:19 +0200)
committerFelix Fietkau <nbd@nbd.name>
Wed, 28 Oct 2020 03:49:11 +0000 (04:49 +0100)
Signed-off-by: Felix Fietkau <nbd@nbd.name>
bridge.c
config.c
device.h
interface.c
interface.h
ubus.c
wireless.c

index 14d497298352697cc27a168c1c0a9ce7ed1b3a5c..92eea9f7e22e20b149057b6b55ded2869639e917 100644 (file)
--- a/bridge.c
+++ b/bridge.c
@@ -122,6 +122,11 @@ struct bridge_member {
        char name[];
 };
 
+struct bridge_vlan_hotplug_port {
+       struct list_head list;
+       struct bridge_vlan_port port;
+};
+
 static void
 bridge_reset_primary(struct bridge_state *bst)
 {
@@ -153,6 +158,7 @@ bridge_reset_primary(struct bridge_state *bst)
 static struct bridge_vlan_port *
 bridge_find_vlan_member_port(struct bridge_member *bm, struct bridge_vlan *vlan)
 {
+       struct bridge_vlan_hotplug_port *port;
        const char *ifname = bm->dev.dev->ifname;
        int i;
 
@@ -163,6 +169,13 @@ bridge_find_vlan_member_port(struct bridge_member *bm, struct bridge_vlan *vlan)
                return &vlan->ports[i];
        }
 
+       list_for_each_entry(port, &vlan->hotplug_ports, list) {
+               if (strcmp(port->port.ifname, ifname) != 0)
+                       continue;
+
+               return &port->port;
+       }
+
        return NULL;
 }
 
@@ -415,9 +428,25 @@ bridge_remove_member(struct bridge_member *bm)
 static void
 bridge_free_member(struct bridge_member *bm)
 {
+       struct bridge_state *bst = bm->bst;
        struct device *dev = bm->dev.dev;
+       const char *ifname = dev->ifname;
+       struct bridge_vlan *vlan;
 
        bridge_remove_member(bm);
+
+       vlist_for_each_element(&bst->dev.vlans, vlan, node) {
+               struct bridge_vlan_hotplug_port *port, *tmp;
+
+               list_for_each_entry_safe(port, tmp, &vlan->hotplug_ports, list) {
+                       if (strcmp(port->port.ifname, ifname) != 0)
+                               continue;
+
+                       list_del(&port->list);
+                       free(port);
+               }
+       }
+
        device_remove_user(&bm->dev);
 
        /*
@@ -616,11 +645,66 @@ bridge_add_member(struct bridge_state *bst, const char *name)
        bridge_create_member(bst, name, dev, false);
 }
 
+static void
+bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *vlans, const char *ifname)
+{
+       struct bridge_vlan *vlan;
+       struct blob_attr *cur;
+       int rem;
+
+       if (!vlans)
+               return;
+
+       blobmsg_for_each_attr(cur, vlans, rem) {
+               struct bridge_vlan_hotplug_port *port;
+               uint16_t flags = BRVLAN_F_UNTAGGED;
+               char *name_buf;
+               unsigned int vid;
+               char *end;
+
+               if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
+                       continue;
+
+               vid = strtoul(blobmsg_get_string(cur), &end, 0);
+               if (!vid || vid > 4095)
+                       continue;
+
+               vlan = vlist_find(&bst->dev.vlans, &vid, vlan, node);
+               if (!vlan)
+                       continue;
+
+               if (end && *end) {
+                       if (*end != ':')
+                               continue;
+
+                       for (end++; *end; end++) {
+                               switch (*end) {
+                               case 't':
+                                       flags &= ~BRVLAN_F_UNTAGGED;
+                                       break;
+                               case '*':
+                                       flags |= BRVLAN_F_PVID;
+                                       break;
+                               }
+                       }
+               }
+
+               port = calloc_a(sizeof(*port), &name_buf, strlen(ifname) + 1);
+               if (!port)
+                       continue;
+
+               port->port.flags = flags;
+               port->port.ifname = strcpy(name_buf, ifname);
+               list_add_tail(&port->list, &vlan->hotplug_ports);
+       }
+}
+
 static int
-bridge_hotplug_add(struct device *dev, struct device *member)
+bridge_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan)
 {
        struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
 
+       bridge_hotplug_create_member_vlans(bst, vlan, member->ifname);
        bridge_create_member(bst, member->ifname, member, true);
 
        return 0;
@@ -899,6 +983,20 @@ bridge_vlan_equal(struct bridge_vlan *v1, struct bridge_vlan *v2)
        return true;
 }
 
+static void
+bridge_vlan_free(struct bridge_vlan *vlan)
+{
+       struct bridge_vlan_hotplug_port *port, *tmp;
+
+       if (!vlan)
+               return;
+
+       list_for_each_entry_safe(port, tmp, &vlan->hotplug_ports, list)
+               free(port);
+
+       free(vlan);
+}
+
 static void
 bridge_vlan_update(struct vlist_tree *tree, struct vlist_node *node_new,
                   struct vlist_node *node_old)
@@ -920,13 +1018,16 @@ bridge_vlan_update(struct vlist_tree *tree, struct vlist_node *node_new,
        if (node_old)
                bridge_set_vlan_state(bst, vlan_old, false);
 
+       if (node_old && node_new)
+               list_splice_init(&vlan_old->hotplug_ports, &vlan_new->hotplug_ports);
+
        if (node_new)
                bridge_set_vlan_state(bst, vlan_new, true);
 
        bst->dev.config_pending = true;
 
 out:
-       free(vlan_old);
+       bridge_vlan_free(vlan_old);
 }
 
 static struct device *
index 526a20f0d1aba99f31b2f734a15bdabb5f15db33..3546787e46f508c1a6d5687ef13cbe852a208e89 100644 (file)
--- a/config.c
+++ b/config.c
@@ -323,6 +323,7 @@ config_parse_vlan(struct device *dev, struct uci_section *s)
 
        vlan->n_ports = n_ports;
        vlan->ports = port = (struct bridge_vlan_port *)&vlan[1];
+       INIT_LIST_HEAD(&vlan->hotplug_ports);
        name_buf = (char *)&port[n_ports];
 
        blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_PORTS], rem) {
index b25d2675e2fab26e83c209c8808d4f81e36685cb..617a2724b1fda0f7ae6fc5f98f9bbf64ddbe0274 100644 (file)
--- a/device.h
+++ b/device.h
@@ -227,7 +227,7 @@ struct device {
 
 struct device_hotplug_ops {
        int (*prepare)(struct device *dev);
-       int (*add)(struct device *main, struct device *member);
+       int (*add)(struct device *main, struct device *member, struct blob_attr *vlan);
        int (*del)(struct device *main, struct device *member);
 };
 
@@ -248,6 +248,8 @@ struct bridge_vlan {
        struct bridge_vlan_port *ports;
        int n_ports;
 
+       struct list_head hotplug_ports;
+
        uint16_t vid;
        bool local;
 };
index 782174f3b3659089a9a42d97254b6a6f989af678..c53c091c32325c13e61a70caa628e15699e5871a 100644 (file)
@@ -1038,7 +1038,8 @@ interface_remove_link(struct interface *iface, struct device *dev)
 }
 
 static int
-interface_add_link(struct interface *iface, struct device *dev, bool link_ext)
+interface_add_link(struct interface *iface, struct device *dev,
+                  struct blob_attr *vlan, bool link_ext)
 {
        struct device *mdev = iface->main_dev.dev;
 
@@ -1050,7 +1051,7 @@ interface_add_link(struct interface *iface, struct device *dev, bool link_ext)
 
        if (mdev) {
                if (mdev->hotplug_ops)
-                       return mdev->hotplug_ops->add(mdev, dev);
+                       return mdev->hotplug_ops->add(mdev, dev, vlan);
                else
                        return UBUS_STATUS_NOT_SUPPORTED;
        }
@@ -1064,7 +1065,8 @@ interface_add_link(struct interface *iface, struct device *dev, bool link_ext)
 }
 
 int
-interface_handle_link(struct interface *iface, const char *name, bool add, bool link_ext)
+interface_handle_link(struct interface *iface, const char *name,
+                     struct blob_attr *vlan, bool add, bool link_ext)
 {
        struct device *dev;
        int ret;
@@ -1081,7 +1083,7 @@ interface_handle_link(struct interface *iface, const char *name, bool add, bool
                interface_set_device_config(iface, dev);
                device_set_present(dev, true);
 
-               ret = interface_add_link(iface, dev, link_ext);
+               ret = interface_add_link(iface, dev, vlan, link_ext);
        } else {
                ret = interface_remove_link(iface, dev);
        }
index 3d58aa3dee4099d8c017ab5c0611a04bc64c4a34..9c136b63e3774f6b146efc8636f7a3f76d0af299 100644 (file)
@@ -196,7 +196,7 @@ void interface_set_l3_dev(struct interface *iface, struct device *dev);
 void interface_add_user(struct interface_user *dep, struct interface *iface);
 void interface_remove_user(struct interface_user *dep);
 
-int interface_handle_link(struct interface *iface, const char *name, bool add, bool link_ext);
+int interface_handle_link(struct interface *iface, const char *name, struct blob_attr *vlan, bool add, bool link_ext);
 
 void interface_add_error(struct interface *iface, const char *subsystem,
                         const char *code, const char **data, int n_data);
diff --git a/ubus.c b/ubus.c
index 15c826b427d69e5f2658bb88d5267dcb4da3e516..4f167641e3a1c6680aa7f490e956ec7848c3f045 100644 (file)
--- a/ubus.c
+++ b/ubus.c
@@ -903,12 +903,14 @@ netifd_handle_dump(struct ubus_context *ctx, struct ubus_object *obj,
 enum {
        DEV_LINK_NAME,
        DEV_LINK_EXT,
+       DEV_LINK_VLAN,
        __DEV_LINK_MAX,
 };
 
 static const struct blobmsg_policy dev_link_policy[__DEV_LINK_MAX] = {
        [DEV_LINK_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
        [DEV_LINK_EXT] = { .name = "link-ext", .type = BLOBMSG_TYPE_BOOL },
+       [DEV_LINK_VLAN] = { .name = "vlan", .type = BLOBMSG_TYPE_ARRAY },
 };
 
 static int
@@ -933,7 +935,8 @@ netifd_iface_handle_device(struct ubus_context *ctx, struct ubus_object *obj,
        if (cur)
                link_ext = blobmsg_get_bool(cur);
 
-       return interface_handle_link(iface, blobmsg_data(tb[DEV_LINK_NAME]), add, link_ext);
+       return interface_handle_link(iface, blobmsg_data(tb[DEV_LINK_NAME]),
+                                    tb[DEV_LINK_VLAN], add, link_ext);
 }
 
 
index efb799204d76db073587bccc678c746d73da301e..6bf6b585bf734ff50b9e84ad844e553324af82b6 100644 (file)
@@ -319,7 +319,7 @@ static void wireless_interface_handle_link(struct wireless_interface *vif, bool
                if (!iface)
                        continue;
 
-               interface_handle_link(iface, vif->ifname, up, true);
+               interface_handle_link(iface, vif->ifname, NULL, up, true);
        }
 }
 
@@ -349,7 +349,7 @@ static void wireless_vlan_handle_link(struct wireless_vlan *vlan, bool up)
                if (!iface)
                        continue;
 
-               interface_handle_link(iface, vlan->ifname, up, true);
+               interface_handle_link(iface, vlan->ifname, NULL, up, true);
        }
 }