summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-03-20 22:59:21 -0800
committerDavid S. Miller <davem@davemloft.net>2006-03-20 22:59:21 -0800
commitfda93d92d7824159d8532995072dde2bee4bc4b3 (patch)
treeba28fcd6637d848bbee4a6e22f642a69cbe1c71f
parentcf0f02d04a830c8202e6a8f8bb37acc6c1629a91 (diff)
[BRIDGE]: allow show/store of group multicast address
Bridge's communicate with each other using Spanning Tree Protocol over a standard multicast address. There are times when testing or layering bridges over existing topologies or tunnels, when it is useful to use alternative multicast addresses for STP packets. The 802.1d standard has some unused addresses, that can be used for this. This patch is restrictive in that it only allows one of the possible addresses in the standard. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/bridge/br_input.c7
-rw-r--r--net/bridge/br_private.h3
-rw-r--r--net/bridge/br_stp_bpdu.c4
-rw-r--r--net/bridge/br_sysfs_br.c49
4 files changed, 57 insertions, 6 deletions
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index dad40948975..a9feb201542 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -19,7 +19,8 @@
#include <linux/netfilter_bridge.h>
#include "br_private.h"
-const unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+/* Bridge group multicast address 802.1d (pg 51). */
+const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
{
@@ -108,9 +109,9 @@ static int br_handle_local_finish(struct sk_buff *skb)
/* Does address match the link local multicast address.
* 01:80:c2:00:00:0X
*/
-static inline int is_link_local(const unsigned char *dest)
+static inline int is_link_local(const const unsigned char *dest)
{
- return memcmp(dest, bridge_ula, 5) == 0 && (dest[5] & 0xf0) == 0;
+ return memcmp(dest, br_group_address, 5) == 0 && (dest[5] & 0xf0) == 0;
}
/*
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3bc9ad48347..86ecea7ed37 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -109,6 +109,7 @@ struct net_bridge
unsigned long bridge_hello_time;
unsigned long bridge_forward_delay;
+ u8 group_addr[ETH_ALEN];
u16 root_port;
unsigned char stp_enabled;
unsigned char topology_change;
@@ -122,7 +123,7 @@ struct net_bridge
};
extern struct notifier_block br_device_notifier;
-extern const unsigned char bridge_ula[6];
+extern const u8 br_group_address[ETH_ALEN];
/* called under bridge lock */
static inline int br_is_root_bridge(const struct net_bridge *br)
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index a99e90e2095..ed3a5441a47 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -47,7 +47,7 @@ static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int len
skb->dev = dev;
skb->protocol = htons(ETH_P_802_2);
skb->mac.raw = skb_put(skb, size);
- memcpy(skb->mac.raw, bridge_ula, ETH_ALEN);
+ memcpy(skb->mac.raw, p->br->group_addr, ETH_ALEN);
memcpy(skb->mac.raw+ETH_ALEN, dev->dev_addr, ETH_ALEN);
skb->mac.raw[2*ETH_ALEN] = 0;
skb->mac.raw[2*ETH_ALEN+1] = length;
@@ -171,7 +171,7 @@ int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
|| !(br->dev->flags & IFF_UP))
goto out;
- if (compare_ether_addr(dest, bridge_ula) != 0)
+ if (compare_ether_addr(dest, br->group_addr) != 0)
goto out;
buf = skb_pull(skb, 3);
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 6f577f16c4c..96bcb2ff59a 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -242,6 +242,54 @@ static ssize_t show_gc_timer(struct class_device *cd, char *buf)
}
static CLASS_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
+static ssize_t show_group_addr(struct class_device *cd, char *buf)
+{
+ struct net_bridge *br = to_bridge(cd);
+ return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
+ br->group_addr[0], br->group_addr[1],
+ br->group_addr[2], br->group_addr[3],
+ br->group_addr[4], br->group_addr[5]);
+}
+
+static ssize_t store_group_addr(struct class_device *cd, const char *buf,
+ size_t len)
+{
+ struct net_bridge *br = to_bridge(cd);
+ unsigned new_addr[6];
+ int i;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
+ &new_addr[0], &new_addr[1], &new_addr[2],
+ &new_addr[3], &new_addr[4], &new_addr[5]) != 6)
+ return -EINVAL;
+
+ /* Must be 01:80:c2:00:00:0X */
+ for (i = 0; i < 5; i++)
+ if (new_addr[i] != br_group_address[i])
+ return -EINVAL;
+
+ if (new_addr[5] & ~0xf)
+ return -EINVAL;
+
+ if (new_addr[5] == 1 /* 802.3x Pause address */
+ || new_addr[5] == 2 /* 802.3ad Slow protocols */
+ || new_addr[5] == 3) /* 802.1X PAE address */
+ return -EINVAL;
+
+ spin_lock_bh(&br->lock);
+ for (i = 0; i < 6; i++)
+ br->group_addr[i] = new_addr[i];
+ spin_unlock_bh(&br->lock);
+ return len;
+}
+
+static CLASS_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
+ show_group_addr, store_group_addr);
+
+
static struct attribute *bridge_attrs[] = {
&class_device_attr_forward_delay.attr,
&class_device_attr_hello_time.attr,
@@ -259,6 +307,7 @@ static struct attribute *bridge_attrs[] = {
&class_device_attr_tcn_timer.attr,
&class_device_attr_topology_change_timer.attr,
&class_device_attr_gc_timer.attr,
+ &class_device_attr_group_addr.attr,
NULL
};