diff options
Diffstat (limited to 'net/bridge/br_stp_bpdu.c')
-rw-r--r-- | net/bridge/br_stp_bpdu.c | 51 |
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 | ||
133 | static 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 | * |
136 | int br_stp_handle_bpdu(struct sk_buff *skb) | 139 | * NO locks, but rcu_read_lock (preempt_disabled) |
140 | */ | ||
141 | int 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; |