forked from luck/tmp_suning_uos_patched
85967bb46d
Avoid poisoning of the bridge forwarding table by frames that have been dropped by filtering. This prevents spoofed source addresses on hostile side of bridge from causing packet leakage, a small but possible security risk. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
147 lines
3.2 KiB
C
147 lines
3.2 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"
|
|
|
|
const unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
|
|
|
|
static int br_pass_frame_up_finish(struct sk_buff *skb)
|
|
{
|
|
#ifdef CONFIG_NETFILTER_DEBUG
|
|
skb->nf_debug = 0;
|
|
#endif
|
|
netif_rx(skb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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,
|
|
br_pass_frame_up_finish);
|
|
}
|
|
|
|
/* 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 = skb->dev->br_port;
|
|
struct net_bridge *br = p->br;
|
|
struct net_bridge_fdb_entry *dst;
|
|
int passedup = 0;
|
|
|
|
/* insert into forwarding database after filtering to avoid spoofing */
|
|
br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
|
|
|
|
if (br->dev->flags & IFF_PROMISC) {
|
|
struct sk_buff *skb2;
|
|
|
|
skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
if (skb2 != NULL) {
|
|
passedup = 1;
|
|
br_pass_frame_up(br, skb2);
|
|
}
|
|
}
|
|
|
|
if (dest[0] & 1) {
|
|
br_flood_forward(br, skb, !passedup);
|
|
if (!passedup)
|
|
br_pass_frame_up(br, skb);
|
|
goto out;
|
|
}
|
|
|
|
dst = __br_fdb_get(br, dest);
|
|
if (dst != NULL && dst->is_local) {
|
|
if (!passedup)
|
|
br_pass_frame_up(br, skb);
|
|
else
|
|
kfree_skb(skb);
|
|
goto out;
|
|
}
|
|
|
|
if (dst != NULL) {
|
|
br_forward(dst->dst, skb);
|
|
goto out;
|
|
}
|
|
|
|
br_flood_forward(br, skb, 0);
|
|
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Called via br_handle_frame_hook.
|
|
* Return 0 if *pskb should be processed furthur
|
|
* 1 if *pskb is handled
|
|
* note: already called with rcu_read_lock (preempt_disabled)
|
|
*/
|
|
int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
|
|
{
|
|
struct sk_buff *skb = *pskb;
|
|
const unsigned char *dest = eth_hdr(skb)->h_dest;
|
|
|
|
if (p->state == BR_STATE_DISABLED)
|
|
goto err;
|
|
|
|
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
|
|
goto err;
|
|
|
|
if (p->state == BR_STATE_LEARNING)
|
|
br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
else if (p->state == BR_STATE_FORWARDING) {
|
|
if (br_should_route_hook) {
|
|
if (br_should_route_hook(pskb))
|
|
return 0;
|
|
skb = *pskb;
|
|
dest = eth_hdr(skb)->h_dest;
|
|
}
|
|
|
|
if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN))
|
|
skb->pkt_type = PACKET_HOST;
|
|
|
|
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
|
|
br_handle_frame_finish);
|
|
return 1;
|
|
}
|
|
|
|
err:
|
|
kfree_skb(skb);
|
|
return 1;
|
|
}
|