diff options
Diffstat (limited to 'net/bridge/br_stp_bpdu.c')
-rw-r--r-- | net/bridge/br_stp_bpdu.c | 196 |
1 files changed, 100 insertions, 96 deletions
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index 296f6a487c5..8934a54792b 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -15,158 +15,162 @@ #include <linux/kernel.h> #include <linux/netfilter_bridge.h> +#include <linux/etherdevice.h> +#include <linux/llc.h> +#include <net/llc.h> +#include <net/llc_pdu.h> #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) +#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, bridge_ula, 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); } -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 */ 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); } -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, br->group_addr) != 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; |