Commit 9cde0708 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

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: default avatarStephen Hemminger <shemminger@linux-foundation.org>
parent 9cf63747
...@@ -203,7 +203,7 @@ static struct net_device *new_bridge_dev(const char *name) ...@@ -203,7 +203,7 @@ static struct net_device *new_bridge_dev(const char *name)
memcpy(br->group_addr, br_group_address, ETH_ALEN); memcpy(br->group_addr, br_group_address, ETH_ALEN);
br->feature_mask = dev->features; br->feature_mask = dev->features;
br->stp_enabled = 0; br->stp_enabled = BR_NO_STP;
br->designated_root = br->bridge_id; br->designated_root = br->bridge_id;
br->root_path_cost = 0; br->root_path_cost = 0;
br->root_port = 0; br->root_port = 0;
......
...@@ -137,7 +137,8 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -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 = br->topology_change;
b.topology_change_detected = br->topology_change_detected; b.topology_change_detected = br->topology_change_detected;
b.root_port = br->root_port; 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.ageing_time = jiffies_to_clock_t(br->ageing_time);
b.hello_timer_value = br_timer_value(&br->hello_timer); b.hello_timer_value = br_timer_value(&br->hello_timer);
b.tcn_timer_value = br_timer_value(&br->tcn_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) ...@@ -251,7 +252,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
br->stp_enabled = args[1]?1:0; br_stp_set_enabled(br, args[1]);
return 0; return 0;
case BRCTL_SET_BRIDGE_PRIORITY: case BRCTL_SET_BRIDGE_PRIORITY:
......
...@@ -165,7 +165,7 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -165,7 +165,7 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
return -EINVAL; return -EINVAL;
/* if kernel STP is running, don't allow changes */ /* if kernel STP is running, don't allow changes */
if (p->br->stp_enabled) if (p->br->stp_enabled == BR_KERNEL_STP)
return -EBUSY; return -EBUSY;
if (!netif_running(dev) || if (!netif_running(dev) ||
......
...@@ -26,7 +26,10 @@ ...@@ -26,7 +26,10 @@
#define BR_PORT_BITS 10 #define BR_PORT_BITS 10
#define BR_MAX_PORTS (1<<BR_PORT_BITS) #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 bridge_id bridge_id;
typedef struct mac_addr mac_addr; typedef struct mac_addr mac_addr;
...@@ -107,7 +110,13 @@ struct net_bridge ...@@ -107,7 +110,13 @@ struct net_bridge
u8 group_addr[ETH_ALEN]; u8 group_addr[ETH_ALEN];
u16 root_port; 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;
unsigned char topology_change_detected; unsigned char topology_change_detected;
...@@ -127,7 +136,6 @@ static inline int br_is_root_bridge(const struct net_bridge *br) ...@@ -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); return !memcmp(&br->bridge_id, &br->designated_root, 8);
} }
/* br_device.c */ /* br_device.c */
extern void br_dev_setup(struct net_device *dev); extern void br_dev_setup(struct net_device *dev);
extern int br_dev_xmit(struct sk_buff *skb, 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); ...@@ -209,6 +217,7 @@ extern void br_become_designated_port(struct net_bridge_port *p);
/* br_stp_if.c */ /* br_stp_if.c */
extern void br_stp_enable_bridge(struct net_bridge *br); extern void br_stp_enable_bridge(struct net_bridge *br);
extern void br_stp_disable_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_enable_port(struct net_bridge_port *p);
extern void br_stp_disable_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); extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
......
...@@ -370,11 +370,11 @@ static void br_make_blocking(struct net_bridge_port *p) ...@@ -370,11 +370,11 @@ static void br_make_blocking(struct net_bridge_port *p)
static void br_make_forwarding(struct net_bridge_port *p) static void br_make_forwarding(struct net_bridge_port *p)
{ {
if (p->state == BR_STATE_BLOCKING) { if (p->state == BR_STATE_BLOCKING) {
if (p->br->stp_enabled) { if (p->br->stp_enabled == BR_KERNEL_STP)
p->state = BR_STATE_LISTENING; p->state = BR_STATE_LISTENING;
} else { else
p->state = BR_STATE_LEARNING; p->state = BR_STATE_LEARNING;
}
br_log_state(p); br_log_state(p);
mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); } mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); }
} }
...@@ -384,6 +384,10 @@ void br_port_state_selection(struct net_bridge *br) ...@@ -384,6 +384,10 @@ void br_port_state_selection(struct net_bridge *br)
{ {
struct net_bridge_port *p; 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) { list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED) { if (p->state != BR_STATE_DISABLED) {
if (p->port_no == br->root_port) { if (p->port_no == br->root_port) {
......
...@@ -33,9 +33,6 @@ static void br_send_bpdu(struct net_bridge_port *p, ...@@ -33,9 +33,6 @@ static void br_send_bpdu(struct net_bridge_port *p,
{ {
struct sk_buff *skb; struct sk_buff *skb;
if (!p->br->stp_enabled)
return;
skb = dev_alloc_skb(length+LLC_RESERVE); skb = dev_alloc_skb(length+LLC_RESERVE);
if (!skb) if (!skb)
return; return;
...@@ -75,6 +72,9 @@ void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) ...@@ -75,6 +72,9 @@ void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
{ {
unsigned char buf[35]; unsigned char buf[35];
if (p->br->stp_enabled != BR_KERNEL_STP)
return;
buf[0] = 0; buf[0] = 0;
buf[1] = 0; buf[1] = 0;
buf[2] = 0; buf[2] = 0;
...@@ -117,6 +117,9 @@ void br_send_tcn_bpdu(struct net_bridge_port *p) ...@@ -117,6 +117,9 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
{ {
unsigned char buf[4]; unsigned char buf[4];
if (p->br->stp_enabled != BR_KERNEL_STP)
return;
buf[0] = 0; buf[0] = 0;
buf[1] = 0; buf[1] = 0;
buf[2] = 0; buf[2] = 0;
...@@ -157,9 +160,13 @@ int br_stp_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -157,9 +160,13 @@ int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
br = p->br; br = p->br;
spin_lock(&br->lock); spin_lock(&br->lock);
if (p->state == BR_STATE_DISABLED if (br->stp_enabled != BR_KERNEL_STP)
|| !br->stp_enabled goto out;
|| !(br->dev->flags & IFF_UP))
if (!(br->dev->flags & IFF_UP))
goto out;
if (p->state == BR_STATE_DISABLED)
goto out; goto out;
if (compare_ether_addr(dest, br->group_addr) != 0) if (compare_ether_addr(dest, br->group_addr) != 0)
......
...@@ -123,6 +123,62 @@ void br_stp_disable_port(struct net_bridge_port *p) ...@@ -123,6 +123,62 @@ void br_stp_disable_port(struct net_bridge_port *p)
br_become_root_bridge(br); 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 */ /* called under bridge lock */
void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr) void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
{ {
......
...@@ -149,7 +149,9 @@ static ssize_t show_stp_state(struct device *d, ...@@ -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) 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, static ssize_t store_stp_state(struct device *d,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment