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)
{
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;
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;
}
}
static void
-brigde_set_local_vlan(struct bridge_state *bst, struct bridge_vlan *vlan, bool add)
+bridge_set_local_vlan(struct bridge_state *bst, struct bridge_vlan *vlan, bool add)
{
if (!vlan->local && add)
return;
struct bridge_vlan *vlan;
vlist_for_each_element(&bst->dev.vlans, vlan, node)
- brigde_set_local_vlan(bst, vlan, add);
+ bridge_set_local_vlan(bst, vlan, add);
}
static struct bridge_vlan *
struct bridge_member *bm;
struct bridge_vlan *vlan2;
- brigde_set_local_vlan(bst, vlan, add);
+ bridge_set_local_vlan(bst, vlan, add);
vlist_for_each_element(&bst->members, bm, node) {
struct bridge_vlan_port *port;
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_lock();
+
device_remove_user(&bm->dev);
/*
device_set_present(dev, true);
}
+ device_unlock();
+
free(bm);
}
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;
bst = container_of(dev, struct bridge_state, dev);
vlist_flush_all(&bst->members);
+ vlist_flush_all(&dev->vlans);
free(bst->config_data);
free(bst);
}
+static void
+bridge_dump_port(struct blob_buf *b, struct bridge_vlan_port *port)
+{
+ bool tagged = !(port->flags & BRVLAN_F_UNTAGGED);
+ bool pvid = (port->flags & BRVLAN_F_PVID);
+
+ blobmsg_printf(b, "%s%s%s%s\n", port->ifname,
+ tagged || pvid ? ":" : "",
+ tagged ? "t" : "",
+ pvid ? "*" : "");
+}
+
+static void
+bridge_dump_vlan(struct blob_buf *b, struct bridge_vlan *vlan)
+{
+ struct bridge_vlan_hotplug_port *port;
+ void *c, *p;
+ int i;
+
+ c = blobmsg_open_table(b, NULL);
+
+ blobmsg_add_u32(b, "id", vlan->vid);
+ blobmsg_add_u8(b, "local", vlan->local);
+
+ p = blobmsg_open_array(b, "ports");
+
+ for (i = 0; i < vlan->n_ports; i++)
+ bridge_dump_port(b, &vlan->ports[i]);
+
+ list_for_each_entry(port, &vlan->hotplug_ports, list)
+ bridge_dump_port(b, &port->port);
+
+ blobmsg_close_array(b, p);
+
+ blobmsg_close_table(b, c);
+}
+
static void
bridge_dump_info(struct device *dev, struct blob_buf *b)
{
struct bridge_state *bst;
struct bridge_member *bm;
+ struct bridge_vlan *vlan;
void *list;
bst = container_of(dev, struct bridge_state, dev);
}
blobmsg_close_array(b, list);
+
+ if (avl_is_empty(&dev->vlans.avl))
+ return;
+
+ list = blobmsg_open_array(b, "bridge-vlans");
+
+ vlist_for_each_element(&bst->dev.vlans, vlan, node)
+ bridge_dump_vlan(b, vlan);
+
+ blobmsg_close_array(b, list);
}
static void
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)
if (node_new)
vlan_new = container_of(node_new, struct bridge_vlan, node);
- if (node_new && node_old && bridge_vlan_equal(vlan_old, vlan_new))
+ if (node_new && node_old && bridge_vlan_equal(vlan_old, vlan_new)) {
+ list_splice_init(&vlan_old->hotplug_ports, &vlan_new->hotplug_ports);
goto out;
+ }
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 *