diff options
author | Felix Fietkau <nbd@nbd.name> | 2020-08-07 14:19:06 +0200 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2020-10-28 04:49:11 +0100 |
commit | 3023b0cc73520cf7b14e1bfa9075072098b6a753 (patch) | |
tree | 497f337b16ada29e941c984dba1e50a799dca62b | |
parent | 24ce1eab4910869576406bafd0489daf0d3e6e28 (diff) |
bridge: add support for defining port member vlans via hotplug ops
Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r-- | bridge.c | 105 | ||||
-rw-r--r-- | config.c | 1 | ||||
-rw-r--r-- | device.h | 4 | ||||
-rw-r--r-- | interface.c | 10 | ||||
-rw-r--r-- | interface.h | 2 | ||||
-rw-r--r-- | ubus.c | 5 | ||||
-rw-r--r-- | wireless.c | 4 |
7 files changed, 120 insertions, 11 deletions
@@ -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; @@ -900,6 +984,20 @@ bridge_vlan_equal(struct bridge_vlan *v1, struct bridge_vlan *v2) } 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 * @@ -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) { @@ -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; }; diff --git a/interface.c b/interface.c index 782174f..c53c091 100644 --- a/interface.c +++ b/interface.c @@ -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); } diff --git a/interface.h b/interface.h index 3d58aa3..9c136b6 100644 --- a/interface.h +++ b/interface.h @@ -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); @@ -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); } @@ -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); } } |