diff options
-rw-r--r-- | net/bridge/Kconfig | 1 | ||||
-rw-r--r-- | net/bridge/br.c | 12 | ||||
-rw-r--r-- | net/bridge/br_input.c | 32 | ||||
-rw-r--r-- | net/bridge/br_private.h | 3 | ||||
-rw-r--r-- | net/bridge/br_stp_bpdu.c | 51 |
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 | ||
5 | config BRIDGE | 5 | config 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 | ||
25 | int (*br_should_route_hook) (struct sk_buff **pskb) = NULL; | 27 | int (*br_should_route_hook) (struct sk_buff **pskb) = NULL; |
26 | 28 | ||
29 | static struct llc_sap *br_stp_sap; | ||
30 | |||
27 | static int __init br_init(void) | 31 | static 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 | ||
46 | static void __exit br_deinit(void) | 56 | static 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) */ | ||
98 | static 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 | */ | ||
111 | static 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, | |||
217 | extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id); | 217 | extern 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 */ |
220 | extern int br_stp_handle_bpdu(struct sk_buff *skb); | 220 | extern 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 */ |
223 | extern void br_stp_timer_init(struct net_bridge *br); | 224 | extern 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 | ||
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; |