Release 4.11 net/bridge/br_input.c
/*
* 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>
#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>
#include "br_private.h"
#include "br_private_tunnel.h"
/* Hook for brouter */
br_should_route_hook_t __rcu *br_should_route_hook __read_mostly;
EXPORT_SYMBOL(br_should_route_hook);
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 27 | 84.38% | 2 | 66.67% |
Florian Westphal | 5 | 15.62% | 1 | 33.33% |
Total | 32 | 100.00% | 3 | 100.00% |
static int br_pass_frame_up(struct sk_buff *skb)
{
struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
struct net_bridge *br = netdev_priv(brdev);
struct net_bridge_vlan_group *vg;
struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);
u64_stats_update_begin(&brstats->syncp);
brstats->rx_packets++;
brstats->rx_bytes += skb->len;
u64_stats_update_end(&brstats->syncp);
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;
}
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 */
br_multicast_count(br, NULL, skb, br_multicast_igmp_type(skb),
BR_MCAST_DIR_TX);
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
dev_net(indev), NULL, skb, indev, NULL,
br_netif_receive_skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 63 | 32.81% | 3 | 16.67% |
Nikolay Aleksandrov | 27 | 14.06% | 3 | 16.67% |
Linus Torvalds (pre-git) | 24 | 12.50% | 1 | 5.56% |
Stephen Hemminger | 21 | 10.94% | 1 | 5.56% |
Eric Dumazet | 16 | 8.33% | 1 | 5.56% |
Linus Torvalds | 13 | 6.77% | 1 | 5.56% |
Herbert Xu | 8 | 4.17% | 1 | 5.56% |
Pavel Emelyanov | 8 | 4.17% | 1 | 5.56% |
Eric W. Biedermann | 6 | 3.12% | 2 | 11.11% |
David S. Miller | 2 | 1.04% | 1 | 5.56% |
Roopa Prabhu | 2 | 1.04% | 1 | 5.56% |
Li RongQing | 1 | 0.52% | 1 | 5.56% |
Jan Engelhardt | 1 | 0.52% | 1 | 5.56% |
Total | 192 | 100.00% | 18 | 100.00% |
static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
u16 vid, struct net_bridge_port *p)
{
struct net_device *dev = br->dev;
struct neighbour *n;
struct arphdr *parp;
u8 *arpptr, *sha;
__be32 sip, tip;
BR_INPUT_SKB_CB(skb)->proxyarp_replied = false;
if ((dev->flags & IFF_NOARP) ||
!pskb_may_pull(skb, arp_hdr_len(dev)))
return;
parp = arp_hdr(skb);
if (parp->ar_pro != htons(ETH_P_IP) ||
parp->ar_op != htons(ARPOP_REQUEST) ||
parp->ar_hln != dev->addr_len ||
parp->ar_pln != 4)
return;
arpptr = (u8 *)parp + sizeof(struct arphdr);
sha = arpptr;
arpptr += dev->addr_len; /* sha */
memcpy(&sip, arpptr, sizeof(sip));
arpptr += sizeof(sip);
arpptr += dev->addr_len; /* tha */
memcpy(&tip, arpptr, sizeof(tip));
if (ipv4_is_loopback(tip) ||
ipv4_is_multicast(tip))
return;
n = neigh_lookup(&arp_tbl, &tip, dev);
if (n) {
struct net_bridge_fdb_entry *f;
if (!(n->nud_state & NUD_VALID)) {
neigh_release(n);
return;
}
f = br_fdb_find_rcu(br, n->ha, vid);
if (f && ((p->flags & BR_PROXYARP) ||
(f->dst && (f->dst->flags & BR_PROXYARP_WIFI)))) {
arp_send(ARPOP_REPLY, ETH_P_ARP, sip, skb->dev, tip,
sha, n->ha, sha);
BR_INPUT_SKB_CB(skb)->proxyarp_replied = true;
}
neigh_release(n);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Kyeyoon Park | 276 | 83.38% | 1 | 25.00% |
Jouni Malinen | 51 | 15.41% | 1 | 25.00% |
Nikolay Aleksandrov | 4 | 1.21% | 2 | 50.00% |
Total | 331 | 100.00% | 4 | 100.00% |
/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
const unsigned char *dest = eth_hdr(skb)->h_dest;
enum br_pkt_type pkt_type = BR_PKT_UNICAST;
struct net_bridge_fdb_entry *dst = NULL;
struct net_bridge_mdb_entry *mdst;
bool local_rcv, mcast_hit = false;
struct net_bridge *br;
u16 vid = 0;
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;
if (p->flags & BR_LEARNING)
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false);
local_rcv = !!(br->dev->flags & IFF_PROMISC);
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;
}
}
if (p->state == BR_STATE_LEARNING)
goto drop;
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP))
br_do_proxy_arp(skb, br, vid, p);
switch (pkt_type) {
case BR_PKT_MULTICAST:
mdst = br_mdb_get(br, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
br_multicast_querier_exists(br, eth_hdr(skb))) {
if ((mdst && mdst->mglist) ||
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;
}
if (dst) {
unsigned long now = jiffies;
if (dst->is_local)
return br_pass_frame_up(skb);
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);
}
if (local_rcv)
return br_pass_frame_up(skb);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Nikolay Aleksandrov | 159 | 32.52% | 9 | 19.57% |
Herbert Xu | 91 | 18.61% | 6 | 13.04% |
Stephen Hemminger | 88 | 18.00% | 7 | 15.22% |
Linus Torvalds (pre-git) | 43 | 8.79% | 2 | 4.35% |
Vlad Yasevich | 34 | 6.95% | 6 | 13.04% |
Kyeyoon Park | 18 | 3.68% | 1 | 2.17% |
Linus Lüssing | 12 | 2.45% | 2 | 4.35% |
Linus Torvalds | 8 | 1.64% | 1 | 2.17% |
Ido Schimmel | 7 | 1.43% | 1 | 2.17% |
David S. Miller | 5 | 1.02% | 1 | 2.17% |
Arnd Bergmann | 5 | 1.02% | 1 | 2.17% |
Eric W. Biedermann | 5 | 1.02% | 1 | 2.17% |
Toshiaki Makita | 3 | 0.61% | 2 | 4.35% |
Jouni Malinen | 3 | 0.61% | 1 | 2.17% |
Pavel Emelyanov | 3 | 0.61% | 1 | 2.17% |
Américo Wang | 2 | 0.41% | 1 | 2.17% |
Hideaki Yoshifuji / 吉藤英明 | 1 | 0.20% | 1 | 2.17% |
Jiri Pirko | 1 | 0.20% | 1 | 2.17% |
Michael Braun | 1 | 0.20% | 1 | 2.17% |
Total | 489 | 100.00% | 46 | 100.00% |
EXPORT_SYMBOL_GPL(br_handle_frame_finish);
static void __br_handle_local_finish(struct sk_buff *skb)
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
u16 vid = 0;
/* 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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Stephen Hemminger | 35 | 52.24% | 1 | 14.29% |
Vlad Yasevich | 15 | 22.39% | 2 | 28.57% |
Toshiaki Makita | 13 | 19.40% | 2 | 28.57% |
Ido Schimmel | 3 | 4.48% | 1 | 14.29% |
Jiri Pirko | 1 | 1.49% | 1 | 14.29% |
Total | 67 | 100.00% | 7 | 100.00% |
/* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ido Schimmel | 37 | 62.71% | 1 | 33.33% |
Florian Westphal | 18 | 30.51% | 1 | 33.33% |
Stephen Hemminger | 4 | 6.78% | 1 | 33.33% |
Total | 59 | 100.00% | 3 | 100.00% |
/*
* Return NULL if skb is handled
* note: already called with rcu_read_lock
*/
rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
{
struct net_bridge_port *p;
struct sk_buff *skb = *pskb;
const unsigned char *dest = eth_hdr(skb)->h_dest;
br_should_route_hook_t *rhook;
if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
return RX_HANDLER_PASS;
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
goto drop;
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
return RX_HANDLER_CONSUMED;
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
*/
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) */
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;
}
/* 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;
}
forward:
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;
}
dest = eth_hdr(skb)->h_dest;
}
/* fall through */
case BR_STATE_LEARNING:
if (ether_addr_equal(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST;
NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
br_handle_frame_finish);
break;
default:
drop:
kfree_skb(skb);
}
return RX_HANDLER_CONSUMED;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Stephen Hemminger | 112 | 26.73% | 12 | 32.43% |
Ido Schimmel | 54 | 12.89% | 2 | 5.41% |
Jiri Pirko | 49 | 11.69% | 3 | 8.11% |
Linus Torvalds | 45 | 10.74% | 2 | 5.41% |
Roopa Prabhu | 27 | 6.44% | 1 | 2.70% |
Bart De Schuymer | 27 | 6.44% | 3 | 8.11% |
Toshiaki Makita | 26 | 6.21% | 1 | 2.70% |
Herbert Xu | 16 | 3.82% | 1 | 2.70% |
Eric W. Biedermann | 14 | 3.34% | 1 | 2.70% |
Pavel Emelyanov | 11 | 2.63% | 1 | 2.70% |
Arnaldo Carvalho de Melo | 9 | 2.15% | 1 | 2.70% |
David S. Miller | 8 | 1.91% | 2 | 5.41% |
Linus Torvalds (pre-git) | 8 | 1.91% | 1 | 2.70% |
Eric Dumazet | 5 | 1.19% | 1 | 2.70% |
Simon Horman | 3 | 0.72% | 1 | 2.70% |
Jan Engelhardt | 2 | 0.48% | 1 | 2.70% |
Florian Westphal | 1 | 0.24% | 1 | 2.70% |
Joe Perches | 1 | 0.24% | 1 | 2.70% |
Ben Hutchings | 1 | 0.24% | 1 | 2.70% |
Total | 419 | 100.00% | 37 | 100.00% |
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Kyeyoon Park | 300 | 18.26% | 1 | 1.05% |
Stephen Hemminger | 261 | 15.89% | 20 | 21.05% |
Nikolay Aleksandrov | 190 | 11.56% | 12 | 12.63% |
Herbert Xu | 115 | 7.00% | 7 | 7.37% |
Vlad Yasevich | 115 | 7.00% | 8 | 8.42% |
Ido Schimmel | 102 | 6.21% | 3 | 3.16% |
Linus Torvalds (pre-git) | 87 | 5.30% | 2 | 2.11% |
Linus Torvalds | 69 | 4.20% | 2 | 2.11% |
Jouni Malinen | 54 | 3.29% | 1 | 1.05% |
Eric W. Biedermann | 52 | 3.16% | 3 | 3.16% |
Jiri Pirko | 51 | 3.10% | 3 | 3.16% |
Toshiaki Makita | 42 | 2.56% | 4 | 4.21% |
Eric Dumazet | 33 | 2.01% | 2 | 2.11% |
Roopa Prabhu | 32 | 1.95% | 1 | 1.05% |
Bart De Schuymer | 27 | 1.64% | 3 | 3.16% |
Florian Westphal | 24 | 1.46% | 2 | 2.11% |
Pavel Emelyanov | 22 | 1.34% | 2 | 2.11% |
David S. Miller | 16 | 0.97% | 3 | 3.16% |
Linus Lüssing | 12 | 0.73% | 2 | 2.11% |
Arnaldo Carvalho de Melo | 9 | 0.55% | 1 | 1.05% |
Pablo Neira Ayuso | 5 | 0.30% | 1 | 1.05% |
Arnd Bergmann | 5 | 0.30% | 1 | 1.05% |
Simon Horman | 3 | 0.18% | 1 | 1.05% |
Tejun Heo | 3 | 0.18% | 1 | 1.05% |
Paul Gortmaker | 3 | 0.18% | 1 | 1.05% |
Jan Engelhardt | 3 | 0.18% | 1 | 1.05% |
Américo Wang | 2 | 0.12% | 1 | 1.05% |
Hideaki Yoshifuji / 吉藤英明 | 1 | 0.06% | 1 | 1.05% |
Adrian Bunk | 1 | 0.06% | 1 | 1.05% |
Li RongQing | 1 | 0.06% | 1 | 1.05% |
Ben Hutchings | 1 | 0.06% | 1 | 1.05% |
Joe Perches | 1 | 0.06% | 1 | 1.05% |
Michael Braun | 1 | 0.06% | 1 | 1.05% |
Total | 1643 | 100.00% | 95 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.