aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/bridge/Kconfig1
-rw-r--r--net/bridge/br.c12
-rw-r--r--net/bridge/br_input.c32
-rw-r--r--net/bridge/br_private.h3
-rw-r--r--net/bridge/br_stp_bpdu.c51
5 files changed, 71 insertions, 28 deletions
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 @@
4 4
5config BRIDGE 5config BRIDGE
6 tristate "802.1d Ethernet Bridging" 6 tristate "802.1d Ethernet Bridging"
7 select LLC
7 ---help--- 8 ---help---
8 If you say Y here, then your Linux box will be able to act as an 9 If you say Y here, then your Linux box will be able to act as an
9 Ethernet bridge, which means that the different Ethernet segments it 10 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 @@
19#include <linux/netdevice.h> 19#include <linux/netdevice.h>
20#include <linux/etherdevice.h> 20#include <linux/etherdevice.h>
21#include <linux/init.h> 21#include <linux/init.h>
22#include <linux/llc.h>
23#include <net/llc.h>
22 24
23#include "br_private.h" 25#include "br_private.h"
24 26
25int (*br_should_route_hook) (struct sk_buff **pskb) = NULL; 27int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
26 28
29static struct llc_sap *br_stp_sap;
30
27static int __init br_init(void) 31static int __init br_init(void)
28{ 32{
33 br_stp_sap = llc_sap_open(LLC_SAP_BSPAN, br_stp_rcv);
34 if (!br_stp_sap) {
35 printk(KERN_ERR "bridge: can't register sap for STP\n");
36 return -EBUSY;
37 }
38
29 br_fdb_init(); 39 br_fdb_init();
30 40
31#ifdef CONFIG_BRIDGE_NETFILTER 41#ifdef CONFIG_BRIDGE_NETFILTER
@@ -45,6 +55,8 @@ static int __init br_init(void)
45 55
46static void __exit br_deinit(void) 56static void __exit br_deinit(void)
47{ 57{
58 llc_sap_close(br_stp_sap);
59
48#ifdef CONFIG_BRIDGE_NETFILTER 60#ifdef CONFIG_BRIDGE_NETFILTER
49 br_netfilter_fini(); 61 br_netfilter_fini();
50#endif 62#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:
94 goto out; 94 goto out;
95} 95}
96 96
97/* note: already called with rcu_read_lock (preempt_disabled) */
98static int br_handle_local_finish(struct sk_buff *skb)
99{
100 struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
101
102 if (p && p->state != BR_STATE_DISABLED)
103 br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
104
105 return 0; /* process further */
106}
107
108/* Does address match the link local multicast address.
109 * 01:80:c2:00:00:0X
110 */
111static inline int is_link_local(const unsigned char *dest)
112{
113 return memcmp(dest, bridge_ula, 5) == 0 && (dest[5] & 0xf0) == 0;
114}
115
97/* 116/*
98 * Called via br_handle_frame_hook. 117 * Called via br_handle_frame_hook.
99 * Return 0 if *pskb should be processed furthur 118 * 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)
111 if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) 130 if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
112 goto err; 131 goto err;
113 132
114 if (p->br->stp_enabled && 133 if (unlikely(is_link_local(dest))) {
115 !memcmp(dest, bridge_ula, 5) && 134 skb->pkt_type = PACKET_HOST;
116 !(dest[5] & 0xF0)) { 135 return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
117 if (!dest[5]) { 136 NULL, br_handle_local_finish) != 0;
118 NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
119 NULL, br_stp_handle_bpdu);
120 return 1;
121 }
122 goto err;
123 } 137 }
124 138
125 if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) { 139 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,
217extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id); 217extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
218 218
219/* br_stp_bpdu.c */ 219/* br_stp_bpdu.c */
220extern int br_stp_handle_bpdu(struct sk_buff *skb); 220extern int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
221 struct packet_type *pt, struct net_device *orig_dev);
221 222
222/* br_stp_timer.c */ 223/* br_stp_timer.c */
223extern void br_stp_timer_init(struct net_bridge *br); 224extern 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 @@
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;