From 18fdb2b25be37e49b1669b5c394671f8c5b6550f Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 20 Mar 2006 22:58:49 -0800 Subject: [BRIDGE]: stp timer to jiffies cleanup Cleanup the get/set of bridge timer value in the packets. It is clearer not to bury the conversion in macro. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_stp_bpdu.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'net/bridge/br_stp_bpdu.c') diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index 296f6a487c52..c3da01cc6a6f 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -19,8 +19,7 @@ #include "br_private.h" #include "br_private_stp.h" -#define JIFFIES_TO_TICKS(j) (((j) << 8) / HZ) -#define TICKS_TO_JIFFIES(j) (((j) * HZ) >> 8) +#define STP_HZ 256 static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int length) { @@ -57,18 +56,18 @@ static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int len dev_queue_xmit); } -static __inline__ void br_set_ticks(unsigned char *dest, int jiff) +static inline void br_set_ticks(unsigned char *dest, int j) { - __u16 ticks; + unsigned long ticks = (STP_HZ * j)/ HZ; - ticks = JIFFIES_TO_TICKS(jiff); - dest[0] = (ticks >> 8) & 0xFF; - dest[1] = ticks & 0xFF; + *((__be16 *) dest) = htons(ticks); } -static __inline__ int br_get_ticks(unsigned char *dest) +static inline int br_get_ticks(const unsigned char *src) { - return TICKS_TO_JIFFIES((dest[0] << 8) | dest[1]); + unsigned long ticks = ntohs(*(__be16 *)src); + + return (ticks * HZ + STP_HZ - 1) / STP_HZ; } /* called under bridge lock */ -- cgit v1.2.3 From cf0f02d04a830c8202e6a8f8bb37acc6c1629a91 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 20 Mar 2006 22:59:06 -0800 Subject: [BRIDGE]: use llc for receiving STP packets Use LLC for the receive path of Spanning Tree Protocol packets. This allows link local multicast packets to be received by other protocols (if they care), and uses the existing LLC code to get STP packets back into bridge code. The bridge multicast address is also checked, so bridges using other link local multicast addresses are ignored. This allows for use of different multicast addresses to define separate STP domains. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/Kconfig | 1 + net/bridge/br.c | 12 ++++++++++++ net/bridge/br_input.c | 32 +++++++++++++++++++++--------- net/bridge/br_private.h | 3 ++- net/bridge/br_stp_bpdu.c | 51 +++++++++++++++++++++++++++++++----------------- 5 files changed, 71 insertions(+), 28 deletions(-) (limited to 'net/bridge/br_stp_bpdu.c') diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index db23d59746cf..12265aff7099 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -4,6 +4,7 @@ config BRIDGE tristate "802.1d Ethernet Bridging" + select LLC ---help--- If you say Y here, then your Linux box will be able to act as an Ethernet bridge, which means that the different Ethernet segments it diff --git a/net/bridge/br.c b/net/bridge/br.c index 188cc1ac49eb..22d806cf40ca 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -19,13 +19,23 @@ #include #include #include +#include +#include #include "br_private.h" int (*br_should_route_hook) (struct sk_buff **pskb) = NULL; +static struct llc_sap *br_stp_sap; + static int __init br_init(void) { + br_stp_sap = llc_sap_open(LLC_SAP_BSPAN, br_stp_rcv); + if (!br_stp_sap) { + printk(KERN_ERR "bridge: can't register sap for STP\n"); + return -EBUSY; + } + br_fdb_init(); #ifdef CONFIG_BRIDGE_NETFILTER @@ -45,6 +55,8 @@ static int __init br_init(void) static void __exit br_deinit(void) { + llc_sap_close(br_stp_sap); + #ifdef CONFIG_BRIDGE_NETFILTER br_netfilter_fini(); #endif diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 6e223723cc8d..dad409489753 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -94,6 +94,25 @@ drop: goto out; } +/* note: already called with rcu_read_lock (preempt_disabled) */ +static int br_handle_local_finish(struct sk_buff *skb) +{ + struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); + + if (p && p->state != BR_STATE_DISABLED) + br_fdb_update(p->br, p, eth_hdr(skb)->h_source); + + return 0; /* process further */ +} + +/* Does address match the link local multicast address. + * 01:80:c2:00:00:0X + */ +static inline int is_link_local(const unsigned char *dest) +{ + return memcmp(dest, bridge_ula, 5) == 0 && (dest[5] & 0xf0) == 0; +} + /* * Called via br_handle_frame_hook. * Return 0 if *pskb should be processed furthur @@ -111,15 +130,10 @@ int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb) if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) goto err; - if (p->br->stp_enabled && - !memcmp(dest, bridge_ula, 5) && - !(dest[5] & 0xF0)) { - if (!dest[5]) { - NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, - NULL, br_stp_handle_bpdu); - return 1; - } - goto err; + if (unlikely(is_link_local(dest))) { + skb->pkt_type = PACKET_HOST; + return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, + NULL, br_handle_local_finish) != 0; } if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 8f10e09f251b..3bc9ad483473 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -217,7 +217,8 @@ extern void br_stp_set_path_cost(struct net_bridge_port *p, extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id); /* br_stp_bpdu.c */ -extern int br_stp_handle_bpdu(struct sk_buff *skb); +extern int br_stp_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev); /* br_stp_timer.c */ extern void br_stp_timer_init(struct net_bridge *br); diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index c3da01cc6a6f..a99e90e20952 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -15,6 +15,9 @@ #include #include +#include +#include +#include #include "br_private.h" #include "br_private_stp.h" @@ -130,42 +133,54 @@ void br_send_tcn_bpdu(struct net_bridge_port *p) br_send_bpdu(p, buf, 7); } -static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; - -/* NO locks, but rcu_read_lock (preempt_disabled) */ -int br_stp_handle_bpdu(struct sk_buff *skb) +/* + * Called from llc. + * + * NO locks, but rcu_read_lock (preempt_disabled) + */ +int br_stp_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { - struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); + const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); + const unsigned char *dest = eth_hdr(skb)->h_dest; + struct net_bridge_port *p = rcu_dereference(dev->br_port); struct net_bridge *br; - unsigned char *buf; + const unsigned char *buf; if (!p) goto err; - br = p->br; - spin_lock(&br->lock); + if (pdu->ssap != LLC_SAP_BSPAN + || pdu->dsap != LLC_SAP_BSPAN + || pdu->ctrl_1 != LLC_PDU_TYPE_U) + goto err; - if (p->state == BR_STATE_DISABLED || !(br->dev->flags & IFF_UP)) - goto out; + if (!pskb_may_pull(skb, 4)) + goto err; + + /* compare of protocol id and version */ + buf = skb->data; + if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0) + goto err; - /* insert into forwarding database after filtering to avoid spoofing */ - br_fdb_update(br, p, eth_hdr(skb)->h_source); + br = p->br; + spin_lock(&br->lock); - if (!br->stp_enabled) + if (p->state == BR_STATE_DISABLED + || !br->stp_enabled + || !(br->dev->flags & IFF_UP)) goto out; - /* need at least the 802 and STP headers */ - if (!pskb_may_pull(skb, sizeof(header)+1) || - memcmp(skb->data, header, sizeof(header))) + if (compare_ether_addr(dest, bridge_ula) != 0) goto out; - buf = skb_pull(skb, sizeof(header)); + buf = skb_pull(skb, 3); if (buf[0] == BPDU_TYPE_CONFIG) { struct br_config_bpdu bpdu; if (!pskb_may_pull(skb, 32)) - goto out; + goto out; buf = skb->data; bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0; -- cgit v1.2.3 From fda93d92d7824159d8532995072dde2bee4bc4b3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 20 Mar 2006 22:59:21 -0800 Subject: [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 Signed-off-by: David S. Miller --- net/bridge/br_input.c | 7 ++++--- net/bridge/br_private.h | 3 ++- net/bridge/br_stp_bpdu.c | 4 ++-- net/bridge/br_sysfs_br.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 6 deletions(-) (limited to 'net/bridge/br_stp_bpdu.c') diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index dad409489753..a9feb2015425 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -19,7 +19,8 @@ #include #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 3bc9ad483473..86ecea7ed372 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 a99e90e20952..ed3a5441a47e 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 6f577f16c4c0..96bcb2ff59ab 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 }; -- cgit v1.2.3 From 12ac84c4a9c505e3e30529563b04cc8f6d5ebbf3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 20 Mar 2006 22:59:49 -0800 Subject: [BRIDGE]: use LLC to send STP The bridge code can use existing LLC output code when building spanning tree protocol packets. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_stp_bpdu.c | 128 ++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 69 deletions(-) (limited to 'net/bridge/br_stp_bpdu.c') diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index ed3a5441a47e..8934a54792be 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "br_private.h" @@ -24,36 +25,31 @@ #define STP_HZ 256 -static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int length) +#define LLC_RESERVE sizeof(struct llc_pdu_un) + +static void br_send_bpdu(struct net_bridge_port *p, + const unsigned char *data, int length) { - struct net_device *dev; struct sk_buff *skb; - int size; if (!p->br->stp_enabled) return; - size = length + 2*ETH_ALEN + 2; - if (size < 60) - size = 60; - - dev = p->dev; - - if ((skb = dev_alloc_skb(size)) == NULL) { - printk(KERN_INFO "br: memory squeeze!\n"); + skb = dev_alloc_skb(length+LLC_RESERVE); + if (!skb) return; - } - skb->dev = dev; + skb->dev = p->dev; skb->protocol = htons(ETH_P_802_2); - skb->mac.raw = skb_put(skb, size); - 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; - skb->nh.raw = skb->mac.raw + 2*ETH_ALEN + 2; - memcpy(skb->nh.raw, data, length); - memset(skb->nh.raw + length, 0xa5, size - length - 2*ETH_ALEN - 2); + + skb_reserve(skb, LLC_RESERVE); + memcpy(__skb_put(skb, length), data, length); + + llc_pdu_header_init(skb, LLC_PDU_TYPE_U, LLC_SAP_BSPAN, + LLC_SAP_BSPAN, LLC_PDU_CMD); + llc_pdu_init_as_ui_cmd(skb); + + llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr); NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, dev_queue_xmit); @@ -76,60 +72,54 @@ static inline int br_get_ticks(const unsigned char *src) /* called under bridge lock */ void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) { - unsigned char buf[38]; - - buf[0] = 0x42; - buf[1] = 0x42; - buf[2] = 0x03; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = BPDU_TYPE_CONFIG; - buf[7] = (bpdu->topology_change ? 0x01 : 0) | + unsigned char buf[35]; + + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = BPDU_TYPE_CONFIG; + buf[4] = (bpdu->topology_change ? 0x01 : 0) | (bpdu->topology_change_ack ? 0x80 : 0); - buf[8] = bpdu->root.prio[0]; - buf[9] = bpdu->root.prio[1]; - buf[10] = bpdu->root.addr[0]; - buf[11] = bpdu->root.addr[1]; - buf[12] = bpdu->root.addr[2]; - buf[13] = bpdu->root.addr[3]; - buf[14] = bpdu->root.addr[4]; - buf[15] = bpdu->root.addr[5]; - buf[16] = (bpdu->root_path_cost >> 24) & 0xFF; - buf[17] = (bpdu->root_path_cost >> 16) & 0xFF; - buf[18] = (bpdu->root_path_cost >> 8) & 0xFF; - buf[19] = bpdu->root_path_cost & 0xFF; - buf[20] = bpdu->bridge_id.prio[0]; - buf[21] = bpdu->bridge_id.prio[1]; - buf[22] = bpdu->bridge_id.addr[0]; - buf[23] = bpdu->bridge_id.addr[1]; - buf[24] = bpdu->bridge_id.addr[2]; - buf[25] = bpdu->bridge_id.addr[3]; - buf[26] = bpdu->bridge_id.addr[4]; - buf[27] = bpdu->bridge_id.addr[5]; - buf[28] = (bpdu->port_id >> 8) & 0xFF; - buf[29] = bpdu->port_id & 0xFF; - - br_set_ticks(buf+30, bpdu->message_age); - br_set_ticks(buf+32, bpdu->max_age); - br_set_ticks(buf+34, bpdu->hello_time); - br_set_ticks(buf+36, bpdu->forward_delay); - - br_send_bpdu(p, buf, 38); + buf[5] = bpdu->root.prio[0]; + buf[6] = bpdu->root.prio[1]; + buf[7] = bpdu->root.addr[0]; + buf[8] = bpdu->root.addr[1]; + buf[9] = bpdu->root.addr[2]; + buf[10] = bpdu->root.addr[3]; + buf[11] = bpdu->root.addr[4]; + buf[12] = bpdu->root.addr[5]; + buf[13] = (bpdu->root_path_cost >> 24) & 0xFF; + buf[14] = (bpdu->root_path_cost >> 16) & 0xFF; + buf[15] = (bpdu->root_path_cost >> 8) & 0xFF; + buf[16] = bpdu->root_path_cost & 0xFF; + buf[17] = bpdu->bridge_id.prio[0]; + buf[18] = bpdu->bridge_id.prio[1]; + buf[19] = bpdu->bridge_id.addr[0]; + buf[20] = bpdu->bridge_id.addr[1]; + buf[21] = bpdu->bridge_id.addr[2]; + buf[22] = bpdu->bridge_id.addr[3]; + buf[23] = bpdu->bridge_id.addr[4]; + buf[24] = bpdu->bridge_id.addr[5]; + buf[25] = (bpdu->port_id >> 8) & 0xFF; + buf[26] = bpdu->port_id & 0xFF; + + br_set_ticks(buf+27, bpdu->message_age); + br_set_ticks(buf+29, bpdu->max_age); + br_set_ticks(buf+31, bpdu->hello_time); + br_set_ticks(buf+33, bpdu->forward_delay); + + br_send_bpdu(p, buf, 35); } /* called under bridge lock */ void br_send_tcn_bpdu(struct net_bridge_port *p) { - unsigned char buf[7]; - - buf[0] = 0x42; - buf[1] = 0x42; - buf[2] = 0x03; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = BPDU_TYPE_TCN; + unsigned char buf[4]; + + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = BPDU_TYPE_TCN; br_send_bpdu(p, buf, 7); } -- cgit v1.2.3