Files

312 lines
8.0 KiB
C
Raw Permalink Normal View History

2005-04-16 15:20:36 -07:00
/*
* Handle incoming frames
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*
* 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/slab.h>
2005-04-16 15:20:36 -07:00
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/netfilter_bridge.h>
#include <linux/neighbour.h>
#include <net/arp.h>
#include <linux/export.h>
#include <linux/rculist.h>
2005-04-16 15:20:36 -07:00
#include "br_private.h"
#include "br_private_tunnel.h"
2005-04-16 15:20:36 -07:00
/* Hook for brouter */
br_should_route_hook_t __rcu *br_should_route_hook __read_mostly;
EXPORT_SYMBOL(br_should_route_hook);
2015-09-15 20:04:18 -05:00
static int
br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
{
br_drop_fake_rtable(skb);
return netif_receive_skb(skb);
}
2010-02-27 19:41:40 +00:00
static int br_pass_frame_up(struct sk_buff *skb)
2005-04-16 15:20:36 -07:00
{
2010-02-27 19:41:40 +00:00
struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
2010-03-02 13:32:09 +00:00
struct net_bridge *br = netdev_priv(brdev);
struct net_bridge_vlan_group *vg;
struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);
2005-04-16 15:20:36 -07:00
2010-06-23 13:00:48 -07:00
u64_stats_update_begin(&brstats->syncp);
2010-03-02 13:32:09 +00:00
brstats->rx_packets++;
brstats->rx_bytes += skb->len;
2010-06-23 13:00:48 -07:00
u64_stats_update_end(&brstats->syncp);
2005-04-16 15:20:36 -07:00
vg = br_vlan_group_rcu(br);
/* Bridge is just like any other port. Make sure the
* packet is allowed except in promisc modue when someone
* may be running packet capture.
*/
if (!(brdev->flags & IFF_PROMISC) &&
!br_allowed_egress(vg, skb)) {
kfree_skb(skb);
return NET_RX_DROP;
}
2005-04-16 15:20:36 -07:00
indev = skb->dev;
skb->dev = brdev;
skb = br_handle_vlan(br, NULL, vg, skb);
if (!skb)
return NET_RX_DROP;
/* update the multicast stats if the packet is IGMP/MLD */
2016-07-06 12:12:21 -07:00
br_multicast_count(br, NULL, skb, br_multicast_igmp_type(skb),
BR_MCAST_DIR_TX);
2005-04-16 15:20:36 -07:00
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
dev_net(indev), NULL, skb, indev, NULL,
br_netif_receive_skb);
2005-04-16 15:20:36 -07:00
}
2010-07-27 08:26:30 +00:00
/* note: already called with rcu_read_lock */
2015-09-15 20:04:18 -05:00
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
2005-04-16 15:20:36 -07:00
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
enum br_pkt_type pkt_type = BR_PKT_UNICAST;
struct net_bridge_fdb_entry *dst = NULL;
2010-02-27 19:41:48 +00:00
struct net_bridge_mdb_entry *mdst;
bool local_rcv, mcast_hit = false;
const unsigned char *dest;
struct net_bridge *br;
u16 vid = 0;
2005-04-16 15:20:36 -07:00
if (!p || p->state == BR_STATE_DISABLED)
goto drop;
if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid))
goto out;
nbp_switchdev_frame_mark(p, skb);
/* insert into forwarding database after filtering to avoid spoofing */
br = p->br;
2013-06-05 10:08:00 -04:00
if (p->flags & BR_LEARNING)
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false);
local_rcv = !!(br->dev->flags & IFF_PROMISC);
dest = eth_hdr(skb)->h_dest;
if (is_multicast_ether_addr(dest)) {
/* by definition the broadcast is also a multicast address */
if (is_broadcast_ether_addr(dest)) {
pkt_type = BR_PKT_BROADCAST;
local_rcv = true;
} else {
pkt_type = BR_PKT_MULTICAST;
if (br_multicast_rcv(br, p, skb, vid))
goto drop;
}
}
2010-02-27 19:41:48 +00:00
if (p->state == BR_STATE_LEARNING)
goto drop;
2005-12-21 19:00:18 -08:00
2010-02-27 19:41:40 +00:00
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
2018-05-24 11:56:48 +03:00
BR_INPUT_SKB_CB(skb)->src_port_isolated = !!(p->flags & BR_ISOLATED);
2010-02-27 19:41:40 +00:00
if (IS_ENABLED(CONFIG_INET) &&
(skb->protocol == htons(ETH_P_ARP) ||
skb->protocol == htons(ETH_P_RARP))) {
br_do_proxy_suppress_arp(skb, br, vid, p);
} else if (IS_ENABLED(CONFIG_IPV6) &&
skb->protocol == htons(ETH_P_IPV6) &&
br_opt_get(br, BROPT_NEIGH_SUPPRESS_ENABLED) &&
pskb_may_pull(skb, sizeof(struct ipv6hdr) +
sizeof(struct nd_msg)) &&
ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
struct nd_msg *msg, _msg;
msg = br_is_nd_neigh_msg(skb, &_msg);
if (msg)
br_do_suppress_nd(skb, br, vid, p, msg);
}
switch (pkt_type) {
case BR_PKT_MULTICAST:
2013-03-07 03:05:33 +00:00
mdst = br_mdb_get(br, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
br_multicast_querier_exists(br, eth_hdr(skb))) {
2017-11-09 23:10:57 +01:00
if ((mdst && mdst->host_joined) ||
br_multicast_is_router(br)) {
local_rcv = true;
br->dev->stats.multicast++;
}
mcast_hit = true;
} else {
local_rcv = true;
br->dev->stats.multicast++;
}
break;
case BR_PKT_UNICAST:
dst = br_fdb_find_rcu(br, dest, vid);
default:
break;
2005-04-16 15:20:36 -07:00
}
if (dst) {
2017-02-07 08:46:46 -08:00
unsigned long now = jiffies;
if (dst->is_local)
return br_pass_frame_up(skb);
2017-02-07 08:46:46 -08:00
if (now != dst->used)
dst->used = now;
br_forward(dst->dst, skb, local_rcv, false);
} else {
if (!mcast_hit)
br_flood(br, skb, pkt_type, local_rcv, false);
else
br_multicast_flood(mdst, skb, local_rcv, false);
2005-04-16 15:20:36 -07:00
}
if (local_rcv)
return br_pass_frame_up(skb);
2005-04-16 15:20:36 -07:00
out:
return 0;
drop:
kfree_skb(skb);
goto out;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL_GPL(br_handle_frame_finish);
2005-04-16 15:20:36 -07:00
static void __br_handle_local_finish(struct sk_buff *skb)
2006-03-20 22:59:06 -08:00
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
2013-02-13 12:00:16 +00:00
u16 vid = 0;
2006-03-20 22:59:06 -08:00
/* check if vlan is allowed, to avoid spoofing */
if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid))
br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false);
}
/* note: already called with rcu_read_lock */
static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
__br_handle_local_finish(skb);
BR_INPUT_SKB_CB(skb)->brdev = p->br->dev;
br_pass_frame_up(skb);
return 0;
2006-03-20 22:59:06 -08:00
}
2005-04-16 15:20:36 -07:00
/*
2007-03-21 13:38:47 -07:00
* Return NULL if skb is handled
2010-07-27 08:26:30 +00:00
* note: already called with rcu_read_lock
2005-04-16 15:20:36 -07:00
*/
rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
2005-04-16 15:20:36 -07:00
{
struct net_bridge_port *p;
struct sk_buff *skb = *pskb;
2005-04-16 15:20:36 -07:00
const unsigned char *dest = eth_hdr(skb)->h_dest;
br_should_route_hook_t *rhook;
2005-04-16 15:20:36 -07:00
2010-08-22 17:35:32 +00:00
if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
return RX_HANDLER_PASS;
2005-04-16 15:20:36 -07:00
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
2007-03-21 13:42:06 -07:00
goto drop;
2005-04-16 15:20:36 -07:00
2007-10-14 00:39:01 -07:00
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
return RX_HANDLER_CONSUMED;
2007-10-14 00:39:01 -07:00
p = br_port_get_rcu(skb->dev);
if (p->flags & BR_VLAN_TUNNEL) {
if (br_handle_ingress_vlan_tunnel(skb, p,
nbp_vlan_group_rcu(p)))
goto drop;
}
if (unlikely(is_link_local_ether_addr(dest))) {
u16 fwd_mask = p->br->group_fwd_mask_required;
/*
* See IEEE 802.1D Table 7-10 Reserved addresses
*
* Assignment Value
* Bridge Group Address 01-80-C2-00-00-00
* (MAC Control) 802.3 01-80-C2-00-00-01
* (Link Aggregation) 802.3 01-80-C2-00-00-02
* 802.1X PAE address 01-80-C2-00-00-03
*
* 802.1AB LLDP 01-80-C2-00-00-0E
*
* Others reserved for future standardization
*/
fwd_mask |= p->group_fwd_mask;
switch (dest[5]) {
case 0x00: /* Bridge Group Address */
/* If STP is turned off,
then must forward to keep loop detection */
if (p->br->stp_enabled == BR_NO_STP ||
fwd_mask & (1u << dest[5]))
goto forward;
*pskb = skb;
__br_handle_local_finish(skb);
return RX_HANDLER_PASS;
case 0x01: /* IEEE MAC (Pause) */
2007-04-25 22:05:55 -07:00
goto drop;
case 0x0E: /* 802.1AB LLDP */
fwd_mask |= p->br->group_fwd_mask;
if (fwd_mask & (1u << dest[5]))
goto forward;
*pskb = skb;
__br_handle_local_finish(skb);
return RX_HANDLER_PASS;
default:
/* Allow selective forwarding for most other protocols */
fwd_mask |= p->br->group_fwd_mask;
if (fwd_mask & (1u << dest[5]))
goto forward;
}
2009-05-15 06:10:13 +00:00
/* Deliver packet to local host only */
NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, dev_net(skb->dev),
NULL, skb, skb->dev, NULL, br_handle_local_finish);
return RX_HANDLER_CONSUMED;
2007-04-25 22:05:55 -07:00
}
2005-04-16 15:20:36 -07:00
2009-05-15 06:10:13 +00:00
forward:
2007-03-21 13:42:06 -07:00
switch (p->state) {
case BR_STATE_FORWARDING:
rhook = rcu_dereference(br_should_route_hook);
if (rhook) {
if ((*rhook)(skb)) {
*pskb = skb;
return RX_HANDLER_PASS;
}
2005-04-16 15:20:36 -07:00
dest = eth_hdr(skb)->h_dest;
}
2007-03-21 13:42:06 -07:00
/* fall through */
case BR_STATE_LEARNING:
if (ether_addr_equal(p->br->dev->dev_addr, dest))
2005-04-16 15:20:36 -07:00
skb->pkt_type = PACKET_HOST;
NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
2005-04-16 15:20:36 -07:00
br_handle_frame_finish);
2007-03-21 13:42:06 -07:00
break;
default:
drop:
kfree_skb(skb);
2005-04-16 15:20:36 -07:00
}
return RX_HANDLER_CONSUMED;
2005-04-16 15:20:36 -07:00
}