e081e1e3ef
The clone argument is only used by one caller and that caller can clone the packet itself. This patch moves the clone call into the caller and kills the clone argument. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
165 lines
3.9 KiB
C
165 lines
3.9 KiB
C
/*
|
|
* Handle incoming frames
|
|
* Linux ethernet bridge
|
|
*
|
|
* Authors:
|
|
* Lennert Buytenhek <buytenh@gnu.org>
|
|
*
|
|
* $Id: br_input.c,v 1.10 2001/12/24 04:50:20 davem Exp $
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/netfilter_bridge.h>
|
|
#include "br_private.h"
|
|
|
|
/* Bridge group multicast address 802.1d (pg 51). */
|
|
const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
|
|
|
|
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
|
|
{
|
|
struct net_device *indev;
|
|
|
|
br->statistics.rx_packets++;
|
|
br->statistics.rx_bytes += skb->len;
|
|
|
|
indev = skb->dev;
|
|
skb->dev = br->dev;
|
|
|
|
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
|
|
netif_receive_skb);
|
|
}
|
|
|
|
/* note: already called with rcu_read_lock (preempt_disabled) */
|
|
int br_handle_frame_finish(struct sk_buff *skb)
|
|
{
|
|
const unsigned char *dest = eth_hdr(skb)->h_dest;
|
|
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
|
|
struct net_bridge *br;
|
|
struct net_bridge_fdb_entry *dst;
|
|
struct sk_buff *skb2;
|
|
|
|
if (!p || p->state == BR_STATE_DISABLED)
|
|
goto drop;
|
|
|
|
/* insert into forwarding database after filtering to avoid spoofing */
|
|
br = p->br;
|
|
br_fdb_update(br, p, eth_hdr(skb)->h_source);
|
|
|
|
if (p->state == BR_STATE_LEARNING)
|
|
goto drop;
|
|
|
|
/* The packet skb2 goes to the local host (NULL to skip). */
|
|
skb2 = NULL;
|
|
|
|
if (br->dev->flags & IFF_PROMISC)
|
|
skb2 = skb;
|
|
|
|
dst = NULL;
|
|
|
|
if (is_multicast_ether_addr(dest)) {
|
|
br->statistics.multicast++;
|
|
skb2 = skb;
|
|
} else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) {
|
|
skb2 = skb;
|
|
/* Do not forward the packet since it's local. */
|
|
skb = NULL;
|
|
}
|
|
|
|
if (skb2 == skb)
|
|
skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
|
|
if (skb2)
|
|
br_pass_frame_up(br, skb2);
|
|
|
|
if (skb) {
|
|
if (dst)
|
|
br_forward(dst->dst, skb);
|
|
else
|
|
br_flood_forward(br, skb);
|
|
}
|
|
|
|
out:
|
|
return 0;
|
|
drop:
|
|
kfree_skb(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)
|
|
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)
|
|
{
|
|
__be16 *a = (__be16 *)dest;
|
|
static const __be16 *b = (const __be16 *)br_group_address;
|
|
static const __be16 m = __constant_cpu_to_be16(0xfff0);
|
|
|
|
return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | ((a[2] ^ b[2]) & m)) == 0;
|
|
}
|
|
|
|
/*
|
|
* Called via br_handle_frame_hook.
|
|
* Return NULL if skb is handled
|
|
* note: already called with rcu_read_lock (preempt_disabled)
|
|
*/
|
|
struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
|
|
{
|
|
const unsigned char *dest = eth_hdr(skb)->h_dest;
|
|
|
|
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
|
|
goto drop;
|
|
|
|
if (unlikely(is_link_local(dest))) {
|
|
/* Pause frames shouldn't be passed up by driver anyway */
|
|
if (skb->protocol == htons(ETH_P_PAUSE))
|
|
goto drop;
|
|
|
|
/* Process STP BPDU's through normal netif_receive_skb() path */
|
|
if (p->br->stp_enabled != BR_NO_STP) {
|
|
if (NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
|
|
NULL, br_handle_local_finish))
|
|
return NULL;
|
|
else
|
|
return skb;
|
|
}
|
|
}
|
|
|
|
switch (p->state) {
|
|
case BR_STATE_FORWARDING:
|
|
|
|
if (br_should_route_hook) {
|
|
if (br_should_route_hook(&skb))
|
|
return skb;
|
|
dest = eth_hdr(skb)->h_dest;
|
|
}
|
|
/* fall through */
|
|
case BR_STATE_LEARNING:
|
|
if (!compare_ether_addr(p->br->dev->dev_addr, dest))
|
|
skb->pkt_type = PACKET_HOST;
|
|
|
|
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
|
|
br_handle_frame_finish);
|
|
break;
|
|
default:
|
|
drop:
|
|
kfree_skb(skb);
|
|
}
|
|
return NULL;
|
|
}
|