summaryrefslogtreecommitdiffstats
path: root/net/bridge/br_stp_bpdu.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge/br_stp_bpdu.c')
-rw-r--r--net/bridge/br_stp_bpdu.c197
1 files changed, 101 insertions, 96 deletions
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index 296f6a487c52..a7ba0cce0b46 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -15,158 +15,163 @@
#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 <asm/unaligned.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;
+ put_unaligned(htons(ticks), (__be16 *)dest);
}
-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(get_unaligned((__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;