Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | /*
* Handle incoming frames
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*
* $Id: br_input.c,v 1.8 2001/06/01 09:28:28 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/if_bridge.h>
#include <linux/netfilter_bridge.h>
#include "br_private.h"
unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
static int br_pass_frame_up_finish(struct sk_buff *skb)
{
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;
skb->pkt_type = PACKET_HOST;
skb_pull(skb, skb->mac.raw - skb->data);
skb->protocol = eth_type_trans(skb, &br->dev);
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
br_pass_frame_up_finish);
}
static void __br_handle_frame(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
struct net_bridge_fdb_entry *dst;
struct net_bridge_port *p;
int passedup;
dest = skb->mac.ethernet->h_dest;
p = skb->dev->br_port;
br = p->br;
passedup = 0;
if (!(br->dev.flags & IFF_UP) ||
p->state == BR_STATE_DISABLED)
goto freeandout;
skb_push(skb, skb->data - skb->mac.raw);
if (br->dev.flags & IFF_PROMISC) {
struct sk_buff *skb2;
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) {
passedup = 1;
br_pass_frame_up(br, skb2);
}
}
if (skb->mac.ethernet->h_source[0] & 1)
goto freeandout;
if (!passedup &&
(dest[0] & 1) &&
(br->dev.flags & IFF_ALLMULTI || br->dev.mc_list != NULL)) {
struct sk_buff *skb2;
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) {
passedup = 1;
br_pass_frame_up(br, skb2);
}
}
if (br->stp_enabled &&
!memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0))
goto handle_special_frame;
if (p->state == BR_STATE_LEARNING ||
p->state == BR_STATE_FORWARDING)
br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
if (p->state != BR_STATE_FORWARDING)
goto freeandout;
if (dest[0] & 1) {
br_flood_forward(br, skb, 1);
if (!passedup)
br_pass_frame_up(br, skb);
else
kfree_skb(skb);
return;
}
dst = br_fdb_get(br, dest);
if (dst != NULL && dst->is_local) {
if (!passedup)
br_pass_frame_up(br, skb);
else
kfree_skb(skb);
br_fdb_put(dst);
return;
}
if (dst != NULL) {
br_forward(dst->dst, skb);
br_fdb_put(dst);
return;
}
br_flood_forward(br, skb, 0);
return;
handle_special_frame:
if (!dest[5]) {
br_stp_handle_bpdu(skb);
return;
}
freeandout:
kfree_skb(skb);
}
static int br_handle_frame_finish(struct sk_buff *skb)
{
struct net_bridge *br;
br = skb->dev->br_port->br;
read_lock(&br->lock);
__br_handle_frame(skb);
read_unlock(&br->lock);
return 0;
}
void br_handle_frame(struct sk_buff *skb)
{
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
}
|