diff options
author | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-03-21 17:22:44 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 01:28:48 -0400 |
commit | 9cde070874b822d4677f4f01fe146991785813b1 (patch) | |
tree | 2e3a444ad82e026237ce99a4f8cad201f6ca807d | |
parent | 9cf637473c8535b5abe27fee79254c2d552e042a (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.c | 2 | ||||
-rw-r--r-- | net/bridge/br_ioctl.c | 5 | ||||
-rw-r--r-- | net/bridge/br_netlink.c | 2 | ||||
-rw-r--r-- | net/bridge/br_private.h | 15 | ||||
-rw-r--r-- | net/bridge/br_stp.c | 10 | ||||
-rw-r--r-- | net/bridge/br_stp_bpdu.c | 19 | ||||
-rw-r--r-- | net/bridge/br_stp_if.c | 56 | ||||
-rw-r--r-- | net/bridge/br_sysfs_br.c | 4 |
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 | ||
31 | typedef struct bridge_id bridge_id; | 34 | typedef struct bridge_id bridge_id; |
32 | typedef struct mac_addr mac_addr; | 35 | typedef 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 */ |
132 | extern void br_dev_setup(struct net_device *dev); | 140 | extern void br_dev_setup(struct net_device *dev); |
133 | extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); | 141 | 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); | |||
209 | /* br_stp_if.c */ | 217 | /* br_stp_if.c */ |
210 | extern void br_stp_enable_bridge(struct net_bridge *br); | 218 | extern void br_stp_enable_bridge(struct net_bridge *br); |
211 | extern void br_stp_disable_bridge(struct net_bridge *br); | 219 | extern void br_stp_disable_bridge(struct net_bridge *br); |
220 | extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val); | ||
212 | extern void br_stp_enable_port(struct net_bridge_port *p); | 221 | extern void br_stp_enable_port(struct net_bridge_port *p); |
213 | extern void br_stp_disable_port(struct net_bridge_port *p); | 222 | extern void br_stp_disable_port(struct net_bridge_port *p); |
214 | extern void br_stp_recalculate_bridge_id(struct net_bridge *br); | 223 | extern 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) | |||
370 | static void br_make_forwarding(struct net_bridge_port *p) | 370 | static 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 | ||
126 | static 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 | |||
148 | static 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 | |||
169 | void 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 */ |
127 | void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr) | 183 | void 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 | ||
150 | static void set_stp_state(struct net_bridge *br, unsigned long val) | 150 | static 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 | ||
155 | static ssize_t store_stp_state(struct device *d, | 157 | static ssize_t store_stp_state(struct device *d, |