bridge: add support for user mode STP
authorStephen Hemminger <shemminger@linux-foundation.org>
Wed, 21 Mar 2007 21:22:44 +0000 (14:22 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Thu, 26 Apr 2007 05:28:48 +0000 (22:28 -0700)
This patchset based on work by Aji_Srinivas@emc.com provides allows
spanning tree to be controled from userspace.  Like hotplug, it
uses call_usermodehelper when spanning tree is enabled so there
is no visible API change. If call to start usermode STP fails
it falls back to existing kernel STP.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
net/bridge/br_if.c
net/bridge/br_ioctl.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_stp.c
net/bridge/br_stp_bpdu.c
net/bridge/br_stp_if.c
net/bridge/br_sysfs_br.c

index f3a2e29be40c30b173f447bc5ee412cf28c2dcc4..cf10b8f2a1c7668898bce87553f7f50dd56b0131 100644 (file)
@@ -203,7 +203,7 @@ static struct net_device *new_bridge_dev(const char *name)
        memcpy(br->group_addr, br_group_address, ETH_ALEN);
 
        br->feature_mask = dev->features;
-       br->stp_enabled = 0;
+       br->stp_enabled = BR_NO_STP;
        br->designated_root = br->bridge_id;
        br->root_path_cost = 0;
        br->root_port = 0;
index 147015fe5c75c910cb3e4c990f104523d24b212c..eda0fbfc923a3b9603ad38150037803133edc28e 100644 (file)
@@ -137,7 +137,8 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                b.topology_change = br->topology_change;
                b.topology_change_detected = br->topology_change_detected;
                b.root_port = br->root_port;
-               b.stp_enabled = br->stp_enabled;
+
+               b.stp_enabled = (br->stp_enabled != BR_NO_STP);
                b.ageing_time = jiffies_to_clock_t(br->ageing_time);
                b.hello_timer_value = br_timer_value(&br->hello_timer);
                b.tcn_timer_value = br_timer_value(&br->tcn_timer);
@@ -251,7 +252,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
 
-               br->stp_enabled = args[1]?1:0;
+               br_stp_set_enabled(br, args[1]);
                return 0;
 
        case BRCTL_SET_BRIDGE_PRIORITY:
index a14ac51753e656343d75a6495e2c9167542e0a1d..5e84ade129caa260a130c4dca843df0fae7deb49 100644 (file)
@@ -165,7 +165,7 @@ static int br_rtm_setlink(struct sk_buff *skb,  struct nlmsghdr *nlh, void *arg)
                return -EINVAL;
 
        /* if kernel STP is running, don't allow changes */
-       if (p->br->stp_enabled)
+       if (p->br->stp_enabled == BR_KERNEL_STP)
                return -EBUSY;
 
        if (!netif_running(dev) ||
index 3adacdf3406f9285981e15f7a29413091df87489..974feccd28b13e81d2d04cfb1e200422a6960f2a 100644 (file)
 #define BR_PORT_BITS   10
 #define BR_MAX_PORTS   (1<<BR_PORT_BITS)
 
-#define BR_VERSION     "2.2"
+#define BR_VERSION     "2.3"
+
+/* Path to usermode spanning tree program */
+#define BR_STP_PROG    "/sbin/bridge-stp"
 
 typedef struct bridge_id bridge_id;
 typedef struct mac_addr mac_addr;
@@ -107,7 +110,13 @@ struct net_bridge
 
        u8                              group_addr[ETH_ALEN];
        u16                             root_port;
-       unsigned char                   stp_enabled;
+
+       enum {
+               BR_NO_STP,              /* no spanning tree */
+               BR_KERNEL_STP,          /* old STP in kernel */
+               BR_USER_STP,            /* new RSTP in userspace */
+       } stp_enabled;
+
        unsigned char                   topology_change;
        unsigned char                   topology_change_detected;
 
@@ -127,7 +136,6 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
        return !memcmp(&br->bridge_id, &br->designated_root, 8);
 }
 
-
 /* br_device.c */
 extern void br_dev_setup(struct net_device *dev);
 extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
@@ -209,6 +217,7 @@ extern void br_become_designated_port(struct net_bridge_port *p);
 /* br_stp_if.c */
 extern void br_stp_enable_bridge(struct net_bridge *br);
 extern void br_stp_disable_bridge(struct net_bridge *br);
+extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
 extern void br_stp_enable_port(struct net_bridge_port *p);
 extern void br_stp_disable_port(struct net_bridge_port *p);
 extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
index f9ff4d57b0d7fc60cbbd5e5718f2e67c0b247121..ebb0861e9bd573959eb2a4c1bdb7ef00cbcc3454 100644 (file)
@@ -370,11 +370,11 @@ static void br_make_blocking(struct net_bridge_port *p)
 static void br_make_forwarding(struct net_bridge_port *p)
 {
        if (p->state == BR_STATE_BLOCKING) {
-               if (p->br->stp_enabled) {
+               if (p->br->stp_enabled == BR_KERNEL_STP)
                        p->state = BR_STATE_LISTENING;
-               } else {
+               else
                        p->state = BR_STATE_LEARNING;
-               }
+
                br_log_state(p);
                mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay);     }
 }
@@ -384,6 +384,10 @@ void br_port_state_selection(struct net_bridge *br)
 {
        struct net_bridge_port *p;
 
+       /* Don't change port states if userspace is handling STP */
+       if (br->stp_enabled == BR_USER_STP)
+               return;
+
        list_for_each_entry(p, &br->port_list, list) {
                if (p->state != BR_STATE_DISABLED) {
                        if (p->port_no == br->root_port) {
index b9fb0dc4ab1290dc4bebcb151a579bf39fb92ae2..60112bce66984343e8ab13121c1304a1cde29739 100644 (file)
@@ -33,9 +33,6 @@ static void br_send_bpdu(struct net_bridge_port *p,
 {
        struct sk_buff *skb;
 
-       if (!p->br->stp_enabled)
-               return;
-
        skb = dev_alloc_skb(length+LLC_RESERVE);
        if (!skb)
                return;
@@ -75,6 +72,9 @@ void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
 {
        unsigned char buf[35];
 
+       if (p->br->stp_enabled != BR_KERNEL_STP)
+               return;
+
        buf[0] = 0;
        buf[1] = 0;
        buf[2] = 0;
@@ -117,6 +117,9 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
 {
        unsigned char buf[4];
 
+       if (p->br->stp_enabled != BR_KERNEL_STP)
+               return;
+
        buf[0] = 0;
        buf[1] = 0;
        buf[2] = 0;
@@ -157,9 +160,13 @@ int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
        br = p->br;
        spin_lock(&br->lock);
 
-       if (p->state == BR_STATE_DISABLED
-           || !br->stp_enabled
-           || !(br->dev->flags & IFF_UP))
+       if (br->stp_enabled != BR_KERNEL_STP)
+               goto out;
+
+       if (!(br->dev->flags & IFF_UP))
+               goto out;
+
+       if (p->state == BR_STATE_DISABLED)
                goto out;
 
        if (compare_ether_addr(dest, br->group_addr) != 0)
index a285897a2fb462ada2b81b10dc7c0bbc9286c8d4..1c1806d7c489bdc1cc04aca1bd780fbcd0209aed 100644 (file)
@@ -123,6 +123,62 @@ void br_stp_disable_port(struct net_bridge_port *p)
                br_become_root_bridge(br);
 }
 
+static void br_stp_start(struct net_bridge *br)
+{
+       int r;
+       char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL };
+       char *envp[] = { NULL };
+
+       r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
+       if (r == 0) {
+               br->stp_enabled = BR_USER_STP;
+               printk(KERN_INFO "%s: userspace STP started\n", br->dev->name);
+       } else {
+               br->stp_enabled = BR_KERNEL_STP;
+               printk(KERN_INFO "%s: starting userspace STP failed, "
+                               "staring kernel STP\n", br->dev->name);
+
+               /* To start timers on any ports left in blocking */
+               spin_lock_bh(&br->lock);
+               br_port_state_selection(br);
+               spin_unlock_bh(&br->lock);
+       }
+}
+
+static void br_stp_stop(struct net_bridge *br)
+{
+       int r;
+       char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL };
+       char *envp[] = { NULL };
+
+       if (br->stp_enabled == BR_USER_STP) {
+               r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
+               printk(KERN_INFO "%s: userspace STP stopped, return code %d\n",
+                       br->dev->name, r);
+
+
+               /* To start timers on any ports left in blocking */
+               spin_lock_bh(&br->lock);
+               br_port_state_selection(br);
+               spin_unlock_bh(&br->lock);
+       }
+
+       br->stp_enabled = BR_NO_STP;
+}
+
+void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
+{
+       ASSERT_RTNL();
+
+       if (val) {
+               if (br->stp_enabled == BR_NO_STP)
+                       br_stp_start(br);
+       } else {
+               if (br->stp_enabled != BR_NO_STP)
+                       br_stp_stop(br);
+       }
+}
+
 /* called under bridge lock */
 void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
 {
index 6cc5cfe665cd4eba53dabc269f519045aecdcb1b..7ec0b76cdd279c266ac9138416637ad56c95d8ac 100644 (file)
@@ -149,7 +149,9 @@ static ssize_t show_stp_state(struct device *d,
 
 static void set_stp_state(struct net_bridge *br, unsigned long val)
 {
-       br->stp_enabled = val;
+       spin_unlock_bh(&br->lock);
+       br_stp_set_enabled(br, val);
+       spin_lock_bh(&br->lock);
 }
 
 static ssize_t store_stp_state(struct device *d,