[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>
This commit is contained in:
parent
18fdb2b25b
commit
cf0f02d04a
|
@ -4,6 +4,7 @@
|
|||
|
||||
config BRIDGE
|
||||
tristate "802.1d Ethernet Bridging"
|
||||
select LLC
|
||||
---help---
|
||||
If you say Y here, then your Linux box will be able to act as an
|
||||
Ethernet bridge, which means that the different Ethernet segments it
|
||||
|
|
|
@ -19,13 +19,23 @@
|
|||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/llc.h>
|
||||
#include <net/llc.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
|
||||
|
||||
static struct llc_sap *br_stp_sap;
|
||||
|
||||
static int __init br_init(void)
|
||||
{
|
||||
br_stp_sap = llc_sap_open(LLC_SAP_BSPAN, br_stp_rcv);
|
||||
if (!br_stp_sap) {
|
||||
printk(KERN_ERR "bridge: can't register sap for STP\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
br_fdb_init();
|
||||
|
||||
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||
|
@ -45,6 +55,8 @@ static int __init br_init(void)
|
|||
|
||||
static void __exit br_deinit(void)
|
||||
{
|
||||
llc_sap_close(br_stp_sap);
|
||||
|
||||
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||
br_netfilter_fini();
|
||||
#endif
|
||||
|
|
|
@ -94,6 +94,25 @@ int br_handle_frame_finish(struct sk_buff *skb)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* note: already called with rcu_read_lock (preempt_disabled) */
|
||||
static int br_handle_local_finish(struct sk_buff *skb)
|
||||
{
|
||||
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
|
||||
|
||||
if (p && p->state != BR_STATE_DISABLED)
|
||||
br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
|
||||
|
||||
return 0; /* process further */
|
||||
}
|
||||
|
||||
/* Does address match the link local multicast address.
|
||||
* 01:80:c2:00:00:0X
|
||||
*/
|
||||
static inline int is_link_local(const unsigned char *dest)
|
||||
{
|
||||
return memcmp(dest, bridge_ula, 5) == 0 && (dest[5] & 0xf0) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called via br_handle_frame_hook.
|
||||
* 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)
|
|||
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
|
||||
goto err;
|
||||
|
||||
if (p->br->stp_enabled &&
|
||||
!memcmp(dest, bridge_ula, 5) &&
|
||||
!(dest[5] & 0xF0)) {
|
||||
if (!dest[5]) {
|
||||
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
|
||||
NULL, br_stp_handle_bpdu);
|
||||
return 1;
|
||||
}
|
||||
goto err;
|
||||
if (unlikely(is_link_local(dest))) {
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
|
||||
NULL, br_handle_local_finish) != 0;
|
||||
}
|
||||
|
||||
if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {
|
||||
|
|
|
@ -217,7 +217,8 @@ extern void br_stp_set_path_cost(struct net_bridge_port *p,
|
|||
extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
|
||||
|
||||
/* br_stp_bpdu.c */
|
||||
extern int br_stp_handle_bpdu(struct sk_buff *skb);
|
||||
extern int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev);
|
||||
|
||||
/* br_stp_timer.c */
|
||||
extern void br_stp_timer_init(struct net_bridge *br);
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/llc.h>
|
||||
#include <net/llc_pdu.h>
|
||||
|
||||
#include "br_private.h"
|
||||
#include "br_private_stp.h"
|
||||
|
@ -130,42 +133,54 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
|
|||
br_send_bpdu(p, buf, 7);
|
||||
}
|
||||
|
||||
static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
|
||||
|
||||
/* NO locks, but rcu_read_lock (preempt_disabled) */
|
||||
int br_stp_handle_bpdu(struct sk_buff *skb)
|
||||
/*
|
||||
* Called from llc.
|
||||
*
|
||||
* NO locks, but rcu_read_lock (preempt_disabled)
|
||||
*/
|
||||
int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
|
||||
const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
|
||||
const unsigned char *dest = eth_hdr(skb)->h_dest;
|
||||
struct net_bridge_port *p = rcu_dereference(dev->br_port);
|
||||
struct net_bridge *br;
|
||||
unsigned char *buf;
|
||||
const unsigned char *buf;
|
||||
|
||||
if (!p)
|
||||
goto err;
|
||||
|
||||
if (pdu->ssap != LLC_SAP_BSPAN
|
||||
|| pdu->dsap != LLC_SAP_BSPAN
|
||||
|| pdu->ctrl_1 != LLC_PDU_TYPE_U)
|
||||
goto err;
|
||||
|
||||
if (!pskb_may_pull(skb, 4))
|
||||
goto err;
|
||||
|
||||
/* compare of protocol id and version */
|
||||
buf = skb->data;
|
||||
if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
|
||||
goto err;
|
||||
|
||||
br = p->br;
|
||||
spin_lock(&br->lock);
|
||||
|
||||
if (p->state == BR_STATE_DISABLED || !(br->dev->flags & IFF_UP))
|
||||
if (p->state == BR_STATE_DISABLED
|
||||
|| !br->stp_enabled
|
||||
|| !(br->dev->flags & IFF_UP))
|
||||
goto out;
|
||||
|
||||
/* insert into forwarding database after filtering to avoid spoofing */
|
||||
br_fdb_update(br, p, eth_hdr(skb)->h_source);
|
||||
|
||||
if (!br->stp_enabled)
|
||||
if (compare_ether_addr(dest, bridge_ula) != 0)
|
||||
goto out;
|
||||
|
||||
/* need at least the 802 and STP headers */
|
||||
if (!pskb_may_pull(skb, sizeof(header)+1) ||
|
||||
memcmp(skb->data, header, sizeof(header)))
|
||||
goto out;
|
||||
|
||||
buf = skb_pull(skb, sizeof(header));
|
||||
buf = skb_pull(skb, 3);
|
||||
|
||||
if (buf[0] == BPDU_TYPE_CONFIG) {
|
||||
struct br_config_bpdu bpdu;
|
||||
|
||||
if (!pskb_may_pull(skb, 32))
|
||||
goto out;
|
||||
goto out;
|
||||
|
||||
buf = skb->data;
|
||||
bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;
|
||||
|
|
Loading…
Reference in New Issue
Block a user