aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@linux-foundation.org>2007-03-21 17:22:44 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-04-26 01:28:48 -0400
commit9cde070874b822d4677f4f01fe146991785813b1 (patch)
tree2e3a444ad82e026237ce99a4f8cad201f6ca807d
parent9cf637473c8535b5abe27fee79254c2d552e042a (diff)
bridge: add support for user mode STP
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>
-rw-r--r--net/bridge/br_if.c2
-rw-r--r--net/bridge/br_ioctl.c5
-rw-r--r--net/bridge/br_netlink.c2
-rw-r--r--net/bridge/br_private.h15
-rw-r--r--net/bridge/br_stp.c10
-rw-r--r--net/bridge/br_stp_bpdu.c19
-rw-r--r--net/bridge/br_stp_if.c56
-rw-r--r--net/bridge/br_sysfs_br.c4
8 files changed, 96 insertions, 17 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f3a2e29be40c..cf10b8f2a1c7 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -203,7 +203,7 @@ static struct net_device *new_bridge_dev(const char *name)
203 memcpy(br->group_addr, br_group_address, ETH_ALEN); 203 memcpy(br->group_addr, br_group_address, ETH_ALEN);
204 204
205 br->feature_mask = dev->features; 205 br->feature_mask = dev->features;
206 br->stp_enabled = 0; 206 br->stp_enabled = BR_NO_STP;
207 br->designated_root = br->bridge_id; 207 br->designated_root = br->bridge_id;
208 br->root_path_cost = 0; 208 br->root_path_cost = 0;
209 br->root_port = 0; 209 br->root_port = 0;
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 147015fe5c75..eda0fbfc923a 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -137,7 +137,8 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
137 b.topology_change = br->topology_change; 137 b.topology_change = br->topology_change;
138 b.topology_change_detected = br->topology_change_detected; 138 b.topology_change_detected = br->topology_change_detected;
139 b.root_port = br->root_port; 139 b.root_port = br->root_port;
140 b.stp_enabled = br->stp_enabled; 140
141 b.stp_enabled = (br->stp_enabled != BR_NO_STP);
141 b.ageing_time = jiffies_to_clock_t(br->ageing_time); 142 b.ageing_time = jiffies_to_clock_t(br->ageing_time);
142 b.hello_timer_value = br_timer_value(&br->hello_timer); 143 b.hello_timer_value = br_timer_value(&br->hello_timer);
143 b.tcn_timer_value = br_timer_value(&br->tcn_timer); 144 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)
251 if (!capable(CAP_NET_ADMIN)) 252 if (!capable(CAP_NET_ADMIN))
252 return -EPERM; 253 return -EPERM;
253 254
254 br->stp_enabled = args[1]?1:0; 255 br_stp_set_enabled(br, args[1]);
255 return 0; 256 return 0;
256 257
257 case BRCTL_SET_BRIDGE_PRIORITY: 258 case BRCTL_SET_BRIDGE_PRIORITY:
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index a14ac51753e6..5e84ade129ca 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -165,7 +165,7 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
165 return -EINVAL; 165 return -EINVAL;
166 166
167 /* if kernel STP is running, don't allow changes */ 167 /* if kernel STP is running, don't allow changes */
168 if (p->br->stp_enabled) 168 if (p->br->stp_enabled == BR_KERNEL_STP)
169 return -EBUSY; 169 return -EBUSY;
170 170
171 if (!netif_running(dev) || 171 if (!netif_running(dev) ||
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3adacdf3406f..974feccd28b1 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -26,7 +26,10 @@
26#define BR_PORT_BITS 10 26#define BR_PORT_BITS 10
27#define BR_MAX_PORTS (1<<BR_PORT_BITS) 27#define BR_MAX_PORTS (1<<BR_PORT_BITS)
28 28
29#define BR_VERSION "2.2" 29#define BR_VERSION "2.3"
30
31/* Path to usermode spanning tree program */
32#define BR_STP_PROG "/sbin/bridge-stp"
30 33
31typedef struct bridge_id bridge_id; 34typedef struct bridge_id bridge_id;
32typedef struct mac_addr mac_addr; 35typedef struct mac_addr mac_addr;
@@ -107,7 +110,13 @@ struct net_bridge
107 110
108 u8 group_addr[ETH_ALEN]; 111 u8 group_addr[ETH_ALEN];
109 u16 root_port; 112 u16 root_port;
110 unsigned char stp_enabled; 113
114 enum {
115 BR_NO_STP, /* no spanning tree */
116 BR_KERNEL_STP, /* old STP in kernel */
117 BR_USER_STP, /* new RSTP in userspace */
118 } stp_enabled;
119
111 unsigned char topology_change; 120 unsigned char topology_change;
112 unsigned char topology_change_detected; 121 unsigned char topology_change_detected;
113 122
@@ -127,7 +136,6 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
127 return !memcmp(&br->bridge_id, &br->designated_root, 8); 136 return !memcmp(&br->bridge_id, &br->designated_root, 8);
128} 137}
129 138
130
131/* br_device.c */ 139/* br_device.c */
132extern void br_dev_setup(struct net_device *dev); 140extern void br_dev_setup(struct net_device *dev);
133extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); 141extern 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);
209/* br_stp_if.c */ 217/* br_stp_if.c */
210extern void br_stp_enable_bridge(struct net_bridge *br); 218extern void br_stp_enable_bridge(struct net_bridge *br);
211extern void br_stp_disable_bridge(struct net_bridge *br); 219extern void br_stp_disable_bridge(struct net_bridge *br);
220extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
212extern void br_stp_enable_port(struct net_bridge_port *p); 221extern void br_stp_enable_port(struct net_bridge_port *p);
213extern void br_stp_disable_port(struct net_bridge_port *p); 222extern void br_stp_disable_port(struct net_bridge_port *p);
214extern void br_stp_recalculate_bridge_id(struct net_bridge *br); 223extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index f9ff4d57b0d7..ebb0861e9bd5 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -370,11 +370,11 @@ static void br_make_blocking(struct net_bridge_port *p)
370static void br_make_forwarding(struct net_bridge_port *p) 370static void br_make_forwarding(struct net_bridge_port *p)
371{ 371{
372 if (p->state == BR_STATE_BLOCKING) { 372 if (p->state == BR_STATE_BLOCKING) {
373 if (p->br->stp_enabled) { 373 if (p->br->stp_enabled == BR_KERNEL_STP)
374 p->state = BR_STATE_LISTENING; 374 p->state = BR_STATE_LISTENING;
375 } else { 375 else
376 p->state = BR_STATE_LEARNING; 376 p->state = BR_STATE_LEARNING;
377 } 377
378 br_log_state(p); 378 br_log_state(p);
379 mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); } 379 mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); }
380} 380}
@@ -384,6 +384,10 @@ void br_port_state_selection(struct net_bridge *br)
384{ 384{
385 struct net_bridge_port *p; 385 struct net_bridge_port *p;
386 386
387 /* Don't change port states if userspace is handling STP */
388 if (br->stp_enabled == BR_USER_STP)
389 return;
390
387 list_for_each_entry(p, &br->port_list, list) { 391 list_for_each_entry(p, &br->port_list, list) {
388 if (p->state != BR_STATE_DISABLED) { 392 if (p->state != BR_STATE_DISABLED) {
389 if (p->port_no == br->root_port) { 393 if (p->port_no == br->root_port) {
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index b9fb0dc4ab12..60112bce6698 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -33,9 +33,6 @@ static void br_send_bpdu(struct net_bridge_port *p,
33{ 33{
34 struct sk_buff *skb; 34 struct sk_buff *skb;
35 35
36 if (!p->br->stp_enabled)
37 return;
38
39 skb = dev_alloc_skb(length+LLC_RESERVE); 36 skb = dev_alloc_skb(length+LLC_RESERVE);
40 if (!skb) 37 if (!skb)
41 return; 38 return;
@@ -75,6 +72,9 @@ void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
75{ 72{
76 unsigned char buf[35]; 73 unsigned char buf[35];
77 74
75 if (p->br->stp_enabled != BR_KERNEL_STP)
76 return;
77
78 buf[0] = 0; 78 buf[0] = 0;
79 buf[1] = 0; 79 buf[1] = 0;
80 buf[2] = 0; 80 buf[2] = 0;
@@ -117,6 +117,9 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
117{ 117{
118 unsigned char buf[4]; 118 unsigned char buf[4];
119 119
120 if (p->br->stp_enabled != BR_KERNEL_STP)
121 return;
122
120 buf[0] = 0; 123 buf[0] = 0;
121 buf[1] = 0; 124 buf[1] = 0;
122 buf[2] = 0; 125 buf[2] = 0;
@@ -157,9 +160,13 @@ int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
157 br = p->br; 160 br = p->br;
158 spin_lock(&br->lock); 161 spin_lock(&br->lock);
159 162
160 if (p->state == BR_STATE_DISABLED 163 if (br->stp_enabled != BR_KERNEL_STP)
161 || !br->stp_enabled 164 goto out;
162 || !(br->dev->flags & IFF_UP)) 165
166 if (!(br->dev->flags & IFF_UP))
167 goto out;
168
169 if (p->state == BR_STATE_DISABLED)
163 goto out; 170 goto out;
164 171
165 if (compare_ether_addr(dest, br->group_addr) != 0) 172 if (compare_ether_addr(dest, br->group_addr) != 0)
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index a285897a2fb4..1c1806d7c489 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -123,6 +123,62 @@ void br_stp_disable_port(struct net_bridge_port *p)
123 br_become_root_bridge(br); 123 br_become_root_bridge(br);
124} 124}
125 125
126static void br_stp_start(struct net_bridge *br)
127{
128 int r;
129 char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL };
130 char *envp[] = { NULL };
131
132 r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
133 if (r == 0) {
134 br->stp_enabled = BR_USER_STP;
135 printk(KERN_INFO "%s: userspace STP started\n", br->dev->name);
136 } else {
137 br->stp_enabled = BR_KERNEL_STP;
138 printk(KERN_INFO "%s: starting userspace STP failed, "
139 "staring kernel STP\n", br->dev->name);
140
141 /* To start timers on any ports left in blocking */
142 spin_lock_bh(&br->lock);
143 br_port_state_selection(br);
144 spin_unlock_bh(&br->lock);
145 }
146}
147
148static void br_stp_stop(struct net_bridge *br)
149{
150 int r;
151 char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL };
152 char *envp[] = { NULL };
153
154 if (br->stp_enabled == BR_USER_STP) {
155 r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
156 printk(KERN_INFO "%s: userspace STP stopped, return code %d\n",
157 br->dev->name, r);
158
159
160 /* To start timers on any ports left in blocking */
161 spin_lock_bh(&br->lock);
162 br_port_state_selection(br);
163 spin_unlock_bh(&br->lock);
164 }
165
166 br->stp_enabled = BR_NO_STP;
167}
168
169void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
170{
171 ASSERT_RTNL();
172
173 if (val) {
174 if (br->stp_enabled == BR_NO_STP)
175 br_stp_start(br);
176 } else {
177 if (br->stp_enabled != BR_NO_STP)
178 br_stp_stop(br);
179 }
180}
181
126/* called under bridge lock */ 182/* called under bridge lock */
127void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr) 183void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
128{ 184{
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 6cc5cfe665cd..7ec0b76cdd27 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -149,7 +149,9 @@ static ssize_t show_stp_state(struct device *d,
149 149
150static void set_stp_state(struct net_bridge *br, unsigned long val) 150static void set_stp_state(struct net_bridge *br, unsigned long val)
151{ 151{
152 br->stp_enabled = val; 152 spin_unlock_bh(&br->lock);
153 br_stp_set_enabled(br, val);
154 spin_lock_bh(&br->lock);
153} 155}
154 156
155static ssize_t store_stp_state(struct device *d, 157static ssize_t store_stp_state(struct device *d,