aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-03-21 01:59:06 -0500
committerDavid S. Miller <davem@davemloft.net>2006-03-21 01:59:06 -0500
commitcf0f02d04a830c8202e6a8f8bb37acc6c1629a91 (patch)
tree8f3c7af9aee5ea2e1b889c27660e8587307025df /net/bridge
parent18fdb2b25be37e49b1669b5c394671f8c5b6550f (diff)
[BRIDGE]: use llc for receiving STP packets
Use LLC for the receive path of Spanning Tree Protocol packets. This allows link local multicast packets to be received by other protocols (if they care), and uses the existing LLC code to get STP packets back into bridge code. The bridge multicast address is also checked, so bridges using other link local multicast addresses are ignored. This allows for use of different multicast addresses to define separate STP domains. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-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;