Stephen Hemminger
2008-Aug-31 17:43 UTC
[Bridge] [RFC] bridge: STP timer management range checking
The Spanning Tree Protocol timers need to be set within certain boundaries to keep the internal protocol engine working, and to be interoperable. This patch restricts changes to those timers to the values defined in IEEE 802.1D specification. The only exception to the standards are: * if STP is disabled allow forwarding delay to be turned off * allow wider range of ageing timer since this isn't directly part of STP, and setting it to zero allows for non-remembering bridge. Warning: this may cause user backlash since apparently working but standards conforming configurations will get configuration errors that they didn't see before. --- a/net/bridge/br_ioctl.c 2008-08-31 10:00:44.000000000 -0700 +++ b/net/bridge/br_ioctl.c 2008-08-31 10:34:00.000000000 -0700 @@ -177,38 +177,63 @@ static int old_dev_ioctl(struct net_devi } case BRCTL_SET_BRIDGE_FORWARD_DELAY: + { + unsigned long t = clock_t_to_jiffies(args[1]); if (!capable(CAP_NET_ADMIN)) return -EPERM; + /* enforce range checking per IEEE 802.1D 17.14 */ + if (br->stp_enabled != BR_NO_STP && + (t < 4*HZ || t > 30 * HZ)) + return -EINVAL; + spin_lock_bh(&br->lock); - br->bridge_forward_delay = clock_t_to_jiffies(args[1]); + br->bridge_forward_delay = t; if (br_is_root_bridge(br)) br->forward_delay = br->bridge_forward_delay; spin_unlock_bh(&br->lock); return 0; - + } case BRCTL_SET_BRIDGE_HELLO_TIME: + { + unsigned long t = clock_t_to_jiffies(args[1]); + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (t < HZ || t > 15 * HZ) + return -EINVAL; + spin_lock_bh(&br->lock); - br->bridge_hello_time = clock_t_to_jiffies(args[1]); + br->bridge_hello_time = t; if (br_is_root_bridge(br)) br->hello_time = br->bridge_hello_time; spin_unlock_bh(&br->lock); return 0; - + } case BRCTL_SET_BRIDGE_MAX_AGE: + { + unsigned long t = clock_t_to_jiffies(args[1]); if (!capable(CAP_NET_ADMIN)) return -EPERM; + /* enforce range checking per IEEE 802.1D 17.14 */ + if (t < 6 * HZ || t > 40 * HZ) + return -EINVAL; + + if (t < 2 * (br->bridge_hello_time + HZ)) + return -EINVAL; + + if (t / 2 + HZ > br->bridge_forward_delay) + return -EINVAL; + spin_lock_bh(&br->lock); br->bridge_max_age = clock_t_to_jiffies(args[1]); if (br_is_root_bridge(br)) br->max_age = br->bridge_max_age; spin_unlock_bh(&br->lock); return 0; - + } case BRCTL_SET_AGEING_TIME: if (!capable(CAP_NET_ADMIN)) return -EPERM; --- a/net/bridge/br_sysfs_br.c 2008-08-31 10:23:59.000000000 -0700 +++ b/net/bridge/br_sysfs_br.c 2008-08-31 10:32:53.000000000 -0700 @@ -29,11 +29,12 @@ */ static ssize_t store_bridge_parm(struct device *d, const char *buf, size_t len, - void (*set)(struct net_bridge *, unsigned long)) + int (*set)(struct net_bridge *, unsigned long)) { struct net_bridge *br = to_bridge(d); char *endp; unsigned long val; + int rc; if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -43,9 +44,10 @@ static ssize_t store_bridge_parm(struct return -EINVAL; spin_lock_bh(&br->lock); - (*set)(br, val); + rc = (*set)(br, val); spin_unlock_bh(&br->lock); - return len; + + return rc ? rc : len; } @@ -56,12 +58,19 @@ static ssize_t show_forward_delay(struct return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay)); } -static void set_forward_delay(struct net_bridge *br, unsigned long val) +static int set_forward_delay(struct net_bridge *br, unsigned long val) { unsigned long delay = clock_t_to_jiffies(val); + + if (br->stp_enabled != BR_NO_STP && + (delay < 4*HZ || delay > 30 * HZ)) + return -EINVAL; + br->forward_delay = delay; if (br_is_root_bridge(br)) br->bridge_forward_delay = delay; + + return 0; } static ssize_t store_forward_delay(struct device *d, @@ -80,12 +89,18 @@ static ssize_t show_hello_time(struct de jiffies_to_clock_t(to_bridge(d)->hello_time)); } -static void set_hello_time(struct net_bridge *br, unsigned long val) +static int set_hello_time(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); + + if (t < HZ || t > 15 * HZ) + return -EINVAL; + br->hello_time = t; if (br_is_root_bridge(br)) br->bridge_hello_time = t; + + return 0; } static ssize_t store_hello_time(struct device *d, @@ -104,12 +119,24 @@ static ssize_t show_max_age(struct devic jiffies_to_clock_t(to_bridge(d)->max_age)); } -static void set_max_age(struct net_bridge *br, unsigned long val) +static int set_max_age(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); + + /* enforce range checking per IEEE 802.1D 17.14 */ + if (t < 6 * HZ || t > 40 * HZ) + return -EINVAL; + + if (t < 2 * (br->bridge_hello_time + HZ)) + return -EINVAL; + + if (t / 2 + HZ > br->bridge_forward_delay) + return -EINVAL; + br->max_age = t; if (br_is_root_bridge(br)) br->bridge_max_age = t; + return 0; } static ssize_t store_max_age(struct device *d, struct device_attribute *attr, @@ -126,9 +153,10 @@ static ssize_t show_ageing_time(struct d return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time)); } -static void set_ageing_time(struct net_bridge *br, unsigned long val) +static int set_ageing_time(struct net_bridge *br, unsigned long val) { br->ageing_time = clock_t_to_jiffies(val); + return 0; } static ssize_t store_ageing_time(struct device *d, @@ -180,9 +208,10 @@ static ssize_t show_priority(struct devi (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]); } -static void set_priority(struct net_bridge *br, unsigned long val) +static int set_priority(struct net_bridge *br, unsigned long val) { br_stp_set_bridge_priority(br, (u16) val); + return 0; } static ssize_t store_priority(struct device *d, struct device_attribute *attr,
On Sun, 31 Aug 2008 10:43:09 -0700 Stephen Hemminger <shemminger at vyatta.com> wrote:> The Spanning Tree Protocol timers need to be set within certain boundaries > to keep the internal protocol engine working, and to be interoperable. > This patch restricts changes to those timers to the values defined in IEEE 802.1D > specification.Why do we care ? You have to be the network administrator to set values, there are cases you may want to be out of the spec and you are privileged. The kernel does need to stop things being done which are fatal but running around restricting privileged administrators who have the ability to bring the network down anyway isn't its job. Seems bogus extra code to me - stops things working that should be allowed too.
Stephen Hemminger
2008-Aug-31 23:29 UTC
[Bridge] [RFC] bridge: STP timer management range checking
Alan Cox wrote:> On Sun, 31 Aug 2008 10:43:09 -0700 > Stephen Hemminger <shemminger at vyatta.com> wrote: > > >> The Spanning Tree Protocol timers need to be set within certain boundaries >> to keep the internal protocol engine working, and to be interoperable. >> This patch restricts changes to those timers to the values defined in IEEE 802.1D >> specification. >> > > Why do we care ? You have to be the network administrator to set values, > there are cases you may want to be out of the spec and you are > privileged. The kernel does need to stop things being done which are > fatal but running around restricting privileged administrators who have > the ability to bring the network down anyway isn't its job. > > Seems bogus extra code to me - stops things working that should be > allowed too. >The timer configuration is propagated in network protocol, so misconfigured Linux box could survive but effect other devices on the network that are less robust. Maybe the small values would cause some other bridge to crash, go infinite loop, ... More likely robust devices might ignore our packets (because values out of range), leading to routing loops and other disasters. The kernel does need to stop administrative settings from taking out a network. If someone has a custom device or other non-standard usage, they can always rebuild the kernel and remove the range check.
Valdis.Kletnieks at vt.edu
2008-Sep-01 02:25 UTC
[Bridge] [RFC] bridge: STP timer management range checking
On Sun, 31 Aug 2008 10:43:09 PDT, Stephen Hemminger said:> Warning: this may cause user backlash since apparently working but standards > conforming configurations will get configuration errors that they didn't > see before.Did you mean "apparently working but *non*-standards conforming"? Other than that, seems to be a sane application of "Be conservative in what you send". Our network is some 30K cat-5 ports, 1100 switches, 1300 wireless access points, and we appreciate it every time somebody makes things more bulletproof. And yes, we prefer things to out-and-out *fail* rather than run in a wonky configuration - hard failures usually get fixed in a few minutes, wonkiness can drag on for months of mystifying symptoms... -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 226 bytes Desc: not available Url : http://lists.linux-foundation.org/pipermail/bridge/attachments/20080831/18e30e24/attachment.pgp
David Miller
2008-Sep-03 00:28 UTC
[Bridge] [RFC] bridge: STP timer management range checking
From: Stephen Hemminger <shemminger at vyatta.com> Date: Sun, 31 Aug 2008 10:43:09 -0700> The Spanning Tree Protocol timers need to be set within certain boundaries > to keep the internal protocol engine working, and to be interoperable. > This patch restricts changes to those timers to the values defined in IEEE 802.1D > specification. > > The only exception to the standards are: > * if STP is disabled allow forwarding delay to be turned off > * allow wider range of ageing timer since this isn't directly part of > STP, and setting it to zero allows for non-remembering bridge. > > Warning: this may cause user backlash since apparently working but standards > conforming configurations will get configuration errors that they didn't > see before.I don't think we can really add these kinds of restrictions wholesale like this. And the user is reporting that using brctl to turn off STP doesn't appear to actually turn off STP and thus fix all of the crazy ksoftirqd high cpu load problems. So what we need to do is resolve the user configuration issue that is causing this problem to begin with.
Stephen Hemminger
2008-Sep-04 22:47 UTC
[Bridge] [PATCH] bridge: don't allow setting hello time to zero
The bridge hello time can't be safely set to values less than 1 second, otherwise it is possible to end up with a runaway timer. Signed-off-by: Stephen Hemminger <shemminger at vyatta.com> --- a/net/bridge/br_ioctl.c 2008-09-04 15:25:41.000000000 -0700 +++ b/net/bridge/br_ioctl.c 2008-09-04 15:44:33.000000000 -0700 @@ -188,15 +188,21 @@ static int old_dev_ioctl(struct net_devi return 0; case BRCTL_SET_BRIDGE_HELLO_TIME: + { + unsigned long t = clock_t_to_jiffies(args[1]); if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (t < HZ) + return -EINVAL; + spin_lock_bh(&br->lock); - br->bridge_hello_time = clock_t_to_jiffies(args[1]); + br->bridge_hello_time = t; if (br_is_root_bridge(br)) br->hello_time = br->bridge_hello_time; spin_unlock_bh(&br->lock); return 0; + } case BRCTL_SET_BRIDGE_MAX_AGE: if (!capable(CAP_NET_ADMIN)) --- a/net/bridge/br_sysfs_br.c 2008-09-04 15:27:20.000000000 -0700 +++ b/net/bridge/br_sysfs_br.c 2008-09-04 15:33:31.000000000 -0700 @@ -29,11 +29,12 @@ */ static ssize_t store_bridge_parm(struct device *d, const char *buf, size_t len, - void (*set)(struct net_bridge *, unsigned long)) + int (*set)(struct net_bridge *, unsigned long)) { struct net_bridge *br = to_bridge(d); char *endp; unsigned long val; + int err; if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -43,9 +44,9 @@ static ssize_t store_bridge_parm(struct return -EINVAL; spin_lock_bh(&br->lock); - (*set)(br, val); + err = (*set)(br, val); spin_unlock_bh(&br->lock); - return len; + return err ? err : len; } @@ -56,12 +57,13 @@ static ssize_t show_forward_delay(struct return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay)); } -static void set_forward_delay(struct net_bridge *br, unsigned long val) +static int set_forward_delay(struct net_bridge *br, unsigned long val) { unsigned long delay = clock_t_to_jiffies(val); br->forward_delay = delay; if (br_is_root_bridge(br)) br->bridge_forward_delay = delay; + return 0; } static ssize_t store_forward_delay(struct device *d, @@ -80,12 +82,17 @@ static ssize_t show_hello_time(struct de jiffies_to_clock_t(to_bridge(d)->hello_time)); } -static void set_hello_time(struct net_bridge *br, unsigned long val) +static int set_hello_time(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); + + if (t < HZ) + return -EINVAL; + br->hello_time = t; if (br_is_root_bridge(br)) br->bridge_hello_time = t; + return 0; } static ssize_t store_hello_time(struct device *d, @@ -104,12 +111,13 @@ static ssize_t show_max_age(struct devic jiffies_to_clock_t(to_bridge(d)->max_age)); } -static void set_max_age(struct net_bridge *br, unsigned long val) +static int set_max_age(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); br->max_age = t; if (br_is_root_bridge(br)) br->bridge_max_age = t; + return 0; } static ssize_t store_max_age(struct device *d, struct device_attribute *attr, @@ -126,9 +134,10 @@ static ssize_t show_ageing_time(struct d return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time)); } -static void set_ageing_time(struct net_bridge *br, unsigned long val) +static int set_ageing_time(struct net_bridge *br, unsigned long val) { br->ageing_time = clock_t_to_jiffies(val); + return 0; } static ssize_t store_ageing_time(struct device *d, @@ -180,9 +189,10 @@ static ssize_t show_priority(struct devi (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]); } -static void set_priority(struct net_bridge *br, unsigned long val) +static int set_priority(struct net_bridge *br, unsigned long val) { br_stp_set_bridge_priority(br, (u16) val); + return 0; } static ssize_t store_priority(struct device *d, struct device_attribute *attr,