aboutsummaryrefslogtreecommitdiffstats
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.c51
1 files changed, 33 insertions, 18 deletions
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 @@
15 15
16#include <linux/kernel.h> 16#include <linux/kernel.h>
17#include <linux/netfilter_bridge.h> 17#include <linux/netfilter_bridge.h>
18#include <linux/etherdevice.h>
19#include <linux/llc.h>
20#include <net/llc_pdu.h>
18 21
19#include "br_private.h" 22#include "br_private.h"
20#include "br_private_stp.h" 23#include "br_private_stp.h"
@@ -130,42 +133,54 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
130 br_send_bpdu(p, buf, 7); 133 br_send_bpdu(p, buf, 7);
131} 134}
132 135
133static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; 136/*
134 137 * Called from llc.
135/* NO locks, but rcu_read_lock (preempt_disabled) */ 138 *
136int br_stp_handle_bpdu(struct sk_buff *skb) 139 * NO locks, but rcu_read_lock (preempt_disabled)
140 */
141int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
142 struct packet_type *pt, struct net_device *orig_dev)
137{ 143{
138 struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); 144 const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
145 const unsigned char *dest = eth_hdr(skb)->h_dest;
146 struct net_bridge_port *p = rcu_dereference(dev->br_port);
139 struct net_bridge *br; 147 struct net_bridge *br;
140 unsigned char *buf; 148 const unsigned char *buf;
141 149
142 if (!p) 150 if (!p)
143 goto err; 151 goto err;
144 152
145 br = p->br; 153 if (pdu->ssap != LLC_SAP_BSPAN
146 spin_lock(&br->lock); 154 || pdu->dsap != LLC_SAP_BSPAN
155 || pdu->ctrl_1 != LLC_PDU_TYPE_U)
156 goto err;
147 157
148 if (p->state == BR_STATE_DISABLED || !(br->dev->flags & IFF_UP)) 158 if (!pskb_may_pull(skb, 4))
149 goto out; 159 goto err;
160
161 /* compare of protocol id and version */
162 buf = skb->data;
163 if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
164 goto err;
150 165
151 /* insert into forwarding database after filtering to avoid spoofing */ 166 br = p->br;
152 br_fdb_update(br, p, eth_hdr(skb)->h_source); 167 spin_lock(&br->lock);
153 168
154 if (!br->stp_enabled) 169 if (p->state == BR_STATE_DISABLED
170 || !br->stp_enabled
171 || !(br->dev->flags & IFF_UP))
155 goto out; 172 goto out;
156 173
157 /* need at least the 802 and STP headers */ 174 if (compare_ether_addr(dest, bridge_ula) != 0)
158 if (!pskb_may_pull(skb, sizeof(header)+1) ||
159 memcmp(skb->data, header, sizeof(header)))
160 goto out; 175 goto out;
161 176
162 buf = skb_pull(skb, sizeof(header)); 177 buf = skb_pull(skb, 3);
163 178
164 if (buf[0] == BPDU_TYPE_CONFIG) { 179 if (buf[0] == BPDU_TYPE_CONFIG) {
165 struct br_config_bpdu bpdu; 180 struct br_config_bpdu bpdu;
166 181
167 if (!pskb_may_pull(skb, 32)) 182 if (!pskb_may_pull(skb, 32))
168 goto out; 183 goto out;
169 184
170 buf = skb->data; 185 buf = skb->data;
171 bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0; 186 bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;