summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bridge.c44
-rw-r--r--system-dummy.c2
-rw-r--r--system-linux.c55
-rw-r--r--system.h19
4 files changed, 110 insertions, 10 deletions
diff --git a/bridge.c b/bridge.c
index c3b02bd..caf6197 100644
--- a/bridge.c
+++ b/bridge.c
@@ -12,12 +12,20 @@
enum {
BRIDGE_ATTR_IFNAME,
BRIDGE_ATTR_STP,
+ BRIDGE_ATTR_FORWARD_DELAY,
+ BRIDGE_ATTR_AGEING_TIME,
+ BRIDGE_ATTR_HELLO_TIME,
+ BRIDGE_ATTR_MAX_AGE,
__BRIDGE_ATTR_MAX
};
static const struct blobmsg_policy bridge_attrs[__BRIDGE_ATTR_MAX] = {
[BRIDGE_ATTR_IFNAME] = { "ifname", BLOBMSG_TYPE_ARRAY },
[BRIDGE_ATTR_STP] = { "stp", BLOBMSG_TYPE_BOOL },
+ [BRIDGE_ATTR_FORWARD_DELAY] = { "forward_delay", BLOBMSG_TYPE_INT32 },
+ [BRIDGE_ATTR_AGEING_TIME] = { "ageing_time", BLOBMSG_TYPE_INT32 },
+ [BRIDGE_ATTR_HELLO_TIME] = { "hello_time", BLOBMSG_TYPE_INT32 },
+ [BRIDGE_ATTR_MAX_AGE] = { "max_age", BLOBMSG_TYPE_INT32 },
};
static const union config_param_info bridge_attr_info[__BRIDGE_ATTR_MAX] = {
@@ -52,6 +60,7 @@ struct bridge_state {
struct device dev;
device_state_cb set_state;
+ struct bridge_config config;
struct blob_attr *ifnames;
bool active;
@@ -166,7 +175,7 @@ bridge_set_up(struct bridge_state *bst)
if (!bst->n_present)
return -ENOENT;
- ret = system_bridge_addbr(&bst->dev);
+ ret = system_bridge_addbr(&bst->dev, &bst->config);
if (ret < 0)
goto out;
@@ -322,6 +331,38 @@ bridge_config_init(struct device *dev)
}
}
+static void
+bridge_apply_settings(struct bridge_state *bst, struct blob_attr **tb)
+{
+ struct bridge_config *cfg = &bst->config;
+ struct blob_attr *cur;
+
+ /* defaults */
+ cfg->stp = true;
+ cfg->forward_delay = 1;
+
+ if ((cur = tb[BRIDGE_ATTR_STP]))
+ cfg->stp = blobmsg_get_bool(cur);
+
+ if ((cur = tb[BRIDGE_ATTR_FORWARD_DELAY]))
+ cfg->forward_delay = blobmsg_get_u32(cur);
+
+ if ((cur = tb[BRIDGE_ATTR_AGEING_TIME])) {
+ cfg->ageing_time = blobmsg_get_u32(cur);
+ cfg->flags |= BRIDGE_OPT_AGEING_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])) {
+ cfg->max_age = blobmsg_get_u32(cur);
+ cfg->flags |= BRIDGE_OPT_MAX_AGE;
+ }
+}
+
static struct device *
bridge_create(struct blob_attr *attr)
{
@@ -353,6 +394,7 @@ bridge_create(struct blob_attr *attr)
device_init_settings(dev, tb_dev);
dev->config_pending = true;
bst->ifnames = tb_br[BRIDGE_ATTR_IFNAME];
+ bridge_apply_settings(bst, tb_br);
bst->set_state = dev->set_state;
dev->set_state = bridge_set_state;
diff --git a/system-dummy.c b/system-dummy.c
index d1557f9..9a646dc 100644
--- a/system-dummy.c
+++ b/system-dummy.c
@@ -16,7 +16,7 @@ int system_init(void)
return 0;
}
-int system_bridge_addbr(struct device *bridge)
+int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
{
D(SYSTEM, "brctl addbr %s\n", bridge->ifname);
return 0;
diff --git a/system-linux.c b/system-linux.c
index 725723d..b823099 100644
--- a/system-linux.c
+++ b/system-linux.c
@@ -5,6 +5,7 @@
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <linux/if_vlan.h>
+#include <linux/if_bridge.h>
#include <unistd.h>
#include <string.h>
@@ -121,22 +122,25 @@ int system_bridge_delbr(struct device *bridge)
return ioctl(sock_ioctl, SIOCBRDELBR, bridge->ifname);
}
-static int system_bridge_if(const char *bridge, struct device *dev, int cmd)
+static int system_bridge_if(const char *bridge, struct device *dev, int cmd, void *data)
{
struct ifreq ifr;
- ifr.ifr_ifindex = dev->ifindex;
+ if (dev)
+ ifr.ifr_ifindex = dev->ifindex;
+ else
+ ifr.ifr_data = data;
strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name));
return ioctl(sock_ioctl, cmd, &ifr);
}
int system_bridge_addif(struct device *bridge, struct device *dev)
{
- return system_bridge_if(bridge->ifname, dev, SIOCBRADDIF);
+ return system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL);
}
int system_bridge_delif(struct device *bridge, struct device *dev)
{
- return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF);
+ return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF, NULL);
}
static bool system_is_bridge(const char *name, char *buf, int buflen)
@@ -218,14 +222,51 @@ static void system_if_clear_state(struct device *dev)
bridge = system_get_bridge(dev->ifname, buf, sizeof(buf));
if (bridge) {
D(SYSTEM, "Remove device '%s' from bridge '%s'\n", dev->ifname, bridge);
- system_bridge_if(bridge, dev, SIOCBRDELIF);
+ system_bridge_if(bridge, dev, SIOCBRDELIF, NULL);
}
}
-int system_bridge_addbr(struct device *bridge)
+static inline unsigned long
+sec_to_jiffies(int val)
+{
+ return (unsigned long) val * 100;
+}
+
+int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
{
+ unsigned long args[4] = {};
+
system_if_clear_state(bridge);
- return ioctl(sock_ioctl, SIOCBRADDBR, bridge->ifname);
+ if (ioctl(sock_ioctl, SIOCBRADDBR, bridge->ifname) < 0)
+ return -1;
+
+ args[0] = BRCTL_SET_BRIDGE_STP_STATE;
+ args[1] = !!cfg->stp;
+ system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+
+ args[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+ args[1] = sec_to_jiffies(cfg->forward_delay);
+ system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+
+ if (cfg->flags & BRIDGE_OPT_AGEING_TIME) {
+ args[0] = BRCTL_SET_AGEING_TIME;
+ args[1] = sec_to_jiffies(cfg->ageing_time);
+ system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+ }
+
+ if (cfg->flags & BRIDGE_OPT_HELLO_TIME) {
+ args[0] = BRCTL_SET_BRIDGE_HELLO_TIME;
+ args[1] = sec_to_jiffies(cfg->hello_time);
+ system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+ }
+
+ if (cfg->flags & BRIDGE_OPT_MAX_AGE) {
+ args[0] = BRCTL_SET_BRIDGE_MAX_AGE;
+ args[1] = sec_to_jiffies(cfg->max_age);
+ system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
+ }
+
+ return 0;
}
static int system_vlan(struct device *dev, int id)
diff --git a/system.h b/system.h
index c9b28c8..a2bcf04 100644
--- a/system.h
+++ b/system.h
@@ -5,9 +5,26 @@
#include "device.h"
#include "interface-ip.h"
+enum bridge_opt {
+ /* stp and forward delay always set */
+ BRIDGE_OPT_AGEING_TIME = (1 << 0),
+ BRIDGE_OPT_HELLO_TIME = (1 << 1),
+ BRIDGE_OPT_MAX_AGE = (1 << 2),
+};
+
+struct bridge_config {
+ enum bridge_opt flags;
+ bool stp;
+ int forward_delay;
+
+ int ageing_time;
+ int hello_time;
+ int max_age;
+};
+
int system_init(void);
-int system_bridge_addbr(struct device *bridge);
+int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg);
int system_bridge_delbr(struct device *bridge);
int system_bridge_addif(struct device *bridge, struct device *dev);
int system_bridge_delif(struct device *bridge, struct device *dev);