cmake: fix build by reordering the cflags definitions
[project/netifd.git] / bridge.c
index 04a9abfb735f9d75aeeac160813166224522e6ee..6306bec9ca73e73ab7dc628f26d7e0efbc7fbfd6 100644 (file)
--- a/bridge.c
+++ b/bridge.c
@@ -21,6 +21,7 @@
 #include "device.h"
 #include "interface.h"
 #include "system.h"
+#include "ubus.h"
 
 enum {
        BRIDGE_ATTR_PORTS,
@@ -40,6 +41,8 @@ enum {
        BRIDGE_ATTR_LAST_MEMBER_INTERVAL,
        BRIDGE_ATTR_VLAN_FILTERING,
        BRIDGE_ATTR_HAS_VLANS,
+       BRIDGE_ATTR_STP_KERNEL,
+       BRIDGE_ATTR_STP_PROTO,
        __BRIDGE_ATTR_MAX
 };
 
@@ -61,6 +64,8 @@ static const struct blobmsg_policy bridge_attrs[__BRIDGE_ATTR_MAX] = {
        [BRIDGE_ATTR_LAST_MEMBER_INTERVAL] = { "last_member_interval", BLOBMSG_TYPE_INT32 },
        [BRIDGE_ATTR_VLAN_FILTERING] = { "vlan_filtering", BLOBMSG_TYPE_BOOL },
        [BRIDGE_ATTR_HAS_VLANS] = { "__has_vlans", BLOBMSG_TYPE_BOOL }, /* internal */
+       [BRIDGE_ATTR_STP_KERNEL] = { "stp_kernel", BLOBMSG_TYPE_BOOL },
+       [BRIDGE_ATTR_STP_PROTO] = { "stp_proto", BLOBMSG_TYPE_STRING },
 };
 
 static const struct uci_blob_param_info bridge_attr_info[__BRIDGE_ATTR_MAX] = {
@@ -76,10 +81,13 @@ static const struct uci_blob_param_list bridge_attr_list = {
        .next = { &device_attr_list },
 };
 
+static struct blob_buf b;
 static struct device *bridge_create(const char *name, struct device_type *devtype,
        struct blob_attr *attr);
 static void bridge_config_init(struct device *dev);
+static void bridge_dev_vlan_update(struct device *dev);
 static void bridge_free(struct device *dev);
+static void bridge_stp_init(struct device *dev);
 static void bridge_dump_info(struct device *dev, struct blob_buf *b);
 static enum dev_change_type
 bridge_reload(struct device *dev, struct blob_attr *attr);
@@ -93,9 +101,11 @@ static struct device_type bridge_device_type = {
 
        .create = bridge_create,
        .config_init = bridge_config_init,
+       .vlan_update = bridge_dev_vlan_update,
        .reload = bridge_reload,
        .free = bridge_free,
        .dump_info = bridge_dump_info,
+       .stp_init = bridge_stp_init,
 };
 
 struct bridge_state {
@@ -120,17 +130,13 @@ struct bridge_member {
        struct vlist_node node;
        struct bridge_state *bst;
        struct device_user dev;
+       struct uloop_timeout check_timer;
        uint16_t pvid;
        bool present;
        bool active;
        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)
 {
@@ -321,6 +327,25 @@ bridge_disable_member(struct bridge_member *bm, bool keep_dev)
        return 0;
 }
 
+static void bridge_stp_notify(struct bridge_state *bst)
+{
+       struct bridge_config *cfg = &bst->config;
+
+       if (!cfg->stp || cfg->stp_kernel)
+               return;
+
+       blob_buf_init(&b, 0);
+       blobmsg_add_string(&b, "name", bst->dev.ifname);
+       if (cfg->stp_proto)
+               blobmsg_add_string(&b, "proto", cfg->stp_proto);
+       blobmsg_add_u32(&b, "forward_delay", cfg->forward_delay);
+       blobmsg_add_u32(&b, "hello_time", cfg->hello_time);
+       blobmsg_add_u32(&b, "max_age", cfg->max_age);
+       if (cfg->flags & BRIDGE_OPT_AGEING_TIME)
+               blobmsg_add_u32(&b, "ageing_time", cfg->ageing_time);
+       netifd_ubus_device_notify("stp_init", b.head, 1000);
+}
+
 static int
 bridge_enable_interface(struct bridge_state *bst)
 {
@@ -329,6 +354,7 @@ bridge_enable_interface(struct bridge_state *bst)
        if (bst->active)
                return 0;
 
+       bridge_stp_notify(bst);
        ret = system_bridge_addbr(&bst->dev, &bst->config);
        if (ret < 0)
                return ret;
@@ -344,6 +370,20 @@ bridge_enable_interface(struct bridge_state *bst)
        return 0;
 }
 
+static void
+bridge_stp_init(struct device *dev)
+{
+       struct bridge_state *bst;
+
+       bst = container_of(dev, struct bridge_state, dev);
+       if (!bst->config.stp || !bst->active)
+               return;
+
+       bridge_stp_notify(bst);
+       system_bridge_set_stp_state(&bst->dev, false);
+       system_bridge_set_stp_state(&bst->dev, true);
+}
+
 static void
 bridge_disable_interface(struct bridge_state *bst)
 {
@@ -472,9 +512,8 @@ restart:
                goto restart;
        }
 
-       device_lock();
-
        device_remove_user(&bm->dev);
+       uloop_timeout_cancel(&bm->check_timer);
 
        /*
         * When reloading the config and moving a device from one bridge to
@@ -488,8 +527,6 @@ restart:
                device_set_present(dev, true);
        }
 
-       device_unlock();
-
        free(bm);
 }
 
@@ -502,6 +539,22 @@ bridge_check_retry(struct bridge_state *bst)
        uloop_timeout_set(&bst->retry, 100);
 }
 
+static void
+bridge_member_check_cb(struct uloop_timeout *t)
+{
+       struct bridge_member *bm;
+       struct bridge_state *bst;
+
+       bm = container_of(t, struct bridge_member, check_timer);
+       bst = bm->bst;
+
+       if (system_bridge_vlan_check(&bst->dev, bm->dev.dev->ifname) <= 0)
+               return;
+
+       bridge_disable_member(bm, true);
+       bridge_enable_member(bm);
+}
+
 static void
 bridge_member_cb(struct device_user *dep, enum device_event ev)
 {
@@ -534,6 +587,10 @@ bridge_member_cb(struct device_user *dep, enum device_event ev)
                system_if_apply_settings(&bst->dev, &bst->dev.settings,
                                         DEV_OPT_MTU | DEV_OPT_MTU6);
                break;
+       case DEV_EVENT_LINK_UP:
+               if (bst->has_vlans)
+                       uloop_timeout_set(&bm->check_timer, 1000);
+               break;
        case DEV_EVENT_LINK_DOWN:
                if (!dev->settings.auth)
                        break;
@@ -632,6 +689,7 @@ bridge_create_member(struct bridge_state *bst, const char *name,
        bm->bst = bst;
        bm->dev.cb = bridge_member_cb;
        bm->dev.hotplug = hotplug;
+       bm->check_timer.cb = bridge_member_check_cb;
        strcpy(bm->name, name);
        bm->dev.dev = dev;
        vlist_add(&bst->members, &bm->node, bm->name);
@@ -688,7 +746,7 @@ bridge_add_member(struct bridge_state *bst, const char *name)
 }
 
 static struct bridge_vlan *
-bridge_hotplug_get_vlan(struct bridge_state *bst, unsigned int vid)
+bridge_hotplug_get_vlan(struct bridge_state *bst, uint16_t vid)
 {
        struct bridge_vlan *vlan;
 
@@ -706,12 +764,25 @@ bridge_hotplug_get_vlan(struct bridge_state *bst, unsigned int vid)
        return vlan;
 }
 
+static struct bridge_vlan_hotplug_port *
+bridge_hotplug_get_vlan_port(struct bridge_vlan *vlan, const char *ifname)
+{
+       struct bridge_vlan_hotplug_port *port;
+
+       list_for_each_entry(port, &vlan->hotplug_ports, list)
+               if (!strcmp(port->port.ifname, ifname))
+                       return port;
+
+       return NULL;
+}
+
 static void
-bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *vlans, const char *ifname)
+bridge_hotplug_set_member_vlans(struct bridge_state *bst, struct blob_attr *vlans,
+                               const char *ifname, struct bridge_member *bm, bool add)
 {
        struct bridge_vlan *vlan;
        struct blob_attr *cur;
-       int rem;
+       size_t rem;
 
        if (!vlans)
                return;
@@ -734,6 +805,11 @@ bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *v
                if (!vlan)
                        continue;
 
+               if (vlan->pending) {
+                       vlan->pending = false;
+                       bridge_set_vlan_state(bst, vlan, true);
+               }
+
                if (end && *end) {
                        if (*end != ':')
                                continue;
@@ -750,6 +826,27 @@ bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *v
                        }
                }
 
+               port = bridge_hotplug_get_vlan_port(vlan, ifname);
+               if (!add) {
+                       if (!port)
+                               continue;
+
+                       __bridge_set_member_vlan(bm, vlan, &port->port, false);
+                       list_del(&port->list);
+                       free(port);
+                       continue;
+               }
+
+               if (port) {
+                       if (port->port.flags == flags)
+                               continue;
+
+                       __bridge_set_member_vlan(bm, vlan, &port->port, false);
+                       port->port.flags = flags;
+                       __bridge_set_member_vlan(bm, vlan, &port->port, true);
+                       continue;
+               }
+
                port = calloc_a(sizeof(*port), &name_buf, strlen(ifname) + 1);
                if (!port)
                        continue;
@@ -757,6 +854,11 @@ bridge_hotplug_create_member_vlans(struct bridge_state *bst, struct blob_attr *v
                port->port.flags = flags;
                port->port.ifname = strcpy(name_buf, ifname);
                list_add_tail(&port->list, &vlan->hotplug_ports);
+
+               if (!bm)
+                       continue;
+
+               __bridge_set_member_vlan(bm, vlan, &port->port, true);
        }
 }
 
@@ -764,15 +866,18 @@ static int
 bridge_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan)
 {
        struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
+       struct bridge_member *bm;
 
-       bridge_hotplug_create_member_vlans(bst, vlan, member->ifname);
-       bridge_create_member(bst, member->ifname, member, true);
+       bm = vlist_find(&bst->members, member->ifname, bm, node);
+       bridge_hotplug_set_member_vlans(bst, vlan, member->ifname, bm, true);
+       if (!bm)
+               bridge_create_member(bst, member->ifname, member, true);
 
        return 0;
 }
 
 static int
-bridge_hotplug_del(struct device *dev, struct device *member)
+bridge_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan)
 {
        struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
        struct bridge_member *bm;
@@ -781,6 +886,10 @@ bridge_hotplug_del(struct device *dev, struct device *member)
        if (!bm)
                return UBUS_STATUS_NOT_FOUND;
 
+       bridge_hotplug_set_member_vlans(bst, vlan, member->ifname, bm, false);
+       if (!bm->dev.hotplug)
+               return 0;
+
        vlist_delete(&bst->members, &bm->node);
        return 0;
 }
@@ -825,7 +934,7 @@ 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,
+       blobmsg_printf(b, NULL, "%s%s%s%s", port->ifname,
                tagged || pvid ? ":" : "",
                tagged ? "t" : "",
                pvid ? "*" : "");
@@ -895,7 +1004,8 @@ bridge_config_init(struct device *dev)
        struct bridge_state *bst;
        struct bridge_vlan *vlan;
        struct blob_attr *cur;
-       int i, rem;
+       size_t rem;
+       int i;
 
        bst = container_of(dev, struct bridge_state, dev);
 
@@ -927,8 +1037,9 @@ bridge_apply_settings(struct bridge_state *bst, struct blob_attr **tb)
        struct blob_attr *cur;
 
        /* defaults */
+       memset(cfg, 0, sizeof(*cfg));
        cfg->stp = false;
-       cfg->forward_delay = 2;
+       cfg->stp_kernel = false;
        cfg->robustness = 2;
        cfg->igmp_snoop = false;
        cfg->multicast_querier = false;
@@ -940,9 +1051,19 @@ bridge_apply_settings(struct bridge_state *bst, struct blob_attr **tb)
        cfg->priority = 0x7FFF;
        cfg->vlan_filtering = false;
 
+       cfg->forward_delay = 8;
+       cfg->max_age = 10;
+       cfg->hello_time = 1;
+
        if ((cur = tb[BRIDGE_ATTR_STP]))
                cfg->stp = blobmsg_get_bool(cur);
 
+       if ((cur = tb[BRIDGE_ATTR_STP_KERNEL]))
+               cfg->stp = blobmsg_get_bool(cur);
+
+       if ((cur = tb[BRIDGE_ATTR_STP_PROTO]))
+               cfg->stp_proto = blobmsg_get_string(cur);
+
        if ((cur = tb[BRIDGE_ATTR_FORWARD_DELAY]))
                cfg->forward_delay = blobmsg_get_u32(cur);
 
@@ -983,15 +1104,11 @@ bridge_apply_settings(struct bridge_state *bst, struct blob_attr **tb)
                cfg->flags |= BRIDGE_OPT_AGEING_TIME;
        }
 
-       if ((cur = tb[BRIDGE_ATTR_HELLO_TIME])) {
+       if ((cur = tb[BRIDGE_ATTR_HELLO_TIME]))
                cfg->hello_time = blobmsg_get_u32(cur);
-               cfg->flags |= BRIDGE_OPT_HELLO_TIME;
-       }
 
-       if ((cur = tb[BRIDGE_ATTR_MAX_AGE])) {
+       if ((cur = tb[BRIDGE_ATTR_MAX_AGE]))
                cfg->max_age = blobmsg_get_u32(cur);
-               cfg->flags |= BRIDGE_OPT_MAX_AGE;
-       }
 
        if ((cur = tb[BRIDGE_ATTR_BRIDGE_EMPTY]))
                cfg->bridge_empty = blobmsg_get_bool(cur);
@@ -1139,7 +1256,7 @@ bridge_vlan_update(struct vlist_tree *tree, struct vlist_node *node_new,
                list_splice_init(&vlan_old->hotplug_ports, &vlan_new->hotplug_ports);
 
        if (node_new)
-               bridge_set_vlan_state(bst, vlan_new, true);
+               vlan_new->pending = true;
 
        bst->dev.config_pending = true;
 
@@ -1147,6 +1264,21 @@ out:
        bridge_vlan_free(vlan_old);
 }
 
+static void
+bridge_dev_vlan_update(struct device *dev)
+{
+       struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
+       struct bridge_vlan *vlan;
+
+       vlist_for_each_element(&dev->vlans, vlan, node) {
+               if (!vlan->pending)
+                       continue;
+
+               vlan->pending = false;
+               bridge_set_vlan_state(bst, vlan, true);
+       }
+}
+
 static struct device *
 bridge_create(const char *name, struct device_type *devtype,
        struct blob_attr *attr)