blob: cc9a2780bbaec8921526a4948a552797196df53a [file] [log] [blame]
/*
* 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 <linux/igmp.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/if_ether.h>
#include <qtn/iputil.h>
#include "br_private.h"
#ifndef ETH_P_80211_RAW
# define ETH_P_80211_RAW 0x0019
#endif
static int br_vlan_promisc = 0;
void br_vlan_set_promisc(int enable)
{
printk("%s vlan promiscuous mode\n", enable ? "Enabling" : "Disabling");
br_vlan_promisc = enable;
}
EXPORT_SYMBOL(br_vlan_set_promisc);
/* Hook for brouter */
br_should_route_hook_t __rcu *br_should_route_hook __read_mostly;
EXPORT_SYMBOL(br_should_route_hook);
#ifdef CONFIG_ARC_PLAT_QTN
static struct sk_buff *skb_aligned(struct sk_buff *skb)
{
if ((unsigned int)skb->data & 0x3) {
int clen = 4 - ((unsigned int)skb->data & 0x3);
int err;
err = pskb_expand_head(skb, clen, 0, GFP_ATOMIC);
if (err)
panic("%s: skb alignment failure\n", __func__);
}
return skb;
}
#endif
static int
br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
{
return netif_receive_skb(skb);
}
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, vg, skb);
if (!skb)
return NET_RX_DROP;
#ifdef CONFIG_ARC_PLAT_QTN
skb = skb_aligned(skb);
#endif
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
dev_net(indev), NULL, skb, indev, NULL,
br_netif_receive_skb);
}
static int br_handle_to_protoclstack(const unsigned char *dest, void *data)
{
int retval;
retval = !iputil_mac_is_v4_multicast(dest) ||
iputil_is_v4_ssdp(dest, data) ||
iputil_is_lncb(dest, data);
#ifdef CONFIG_IPV6
if (retval)
retval = (!iputil_mac_is_v6_multicast(dest) ||
iputil_mac_is_v6_local((struct ipv6hdr *)data));
#endif
return retval;
}
static int br_handle_mcast_mgmt_frame(struct sk_buff *skb, struct net_bridge *br)
{
uint8_t igmp_snoop;
igmp_snoop = (br->igmp_snoop_enabled == BR_IGMP_SNOOP_ENABLED ||
br->igmp_snoop_enabled == BR_IGMP_SNOOP_HYBRID);
return igmp_snoop && (!BR_INPUT_SKB_CB(skb)->igmp ||
BR_INPUT_SKB_CB_MROUTERS_ONLY(skb));
}
static int br_handle_mcast_exception(struct net_bridge *br,
const unsigned char *dest, void *data)
{
struct iphdr *iph;
int retval;
int ssdp_check;
#ifdef CONFIG_IPV6
struct ipv6hdr *ip6hdr;
#endif
iph = data;
ssdp_check = (br->ssdp_flood_state == BR_SSDP_FLOOD_DISABLED);
retval = iputil_mac_is_v4_multicast(dest) &&
(!iputil_is_v4_ssdp(dest, iph) || ssdp_check);
#ifdef CONFIG_IPV6
if (!retval) {
ip6hdr = data;
retval = iputil_mac_is_v6_multicast(dest) &&
(!iputil_is_v6_ssdp(dest, ip6hdr) || ssdp_check) &&
!iputil_mac_is_v6_local(ip6hdr);
}
#endif
return retval;
}
int br_wlan_report_flood(struct net_bridge *br)
{
return !!br->wlan_report_flood_enable;
}
int br_handle_frame_finish_multicast(struct sk_buff *skb, struct net_bridge *br,
const unsigned char *dest, u16 vid)
{
struct net_bridge_mdb_entry *mdst;
struct sk_buff *skb2 = NULL;
struct ethhdr *eth = eth_hdr(skb);
void *data;
if (eth->h_proto != __constant_htons(ETH_P_8021Q))
data = (void *)(eth + 1);
else
data = (void *)((struct vlan_ethhdr *)eth + 1);
br->dev->stats.multicast++;
if (br_handle_to_protoclstack(dest, data))
skb2 = skb;
if (!iputil_is_lncb(dest, data) &&
br_handle_mcast_mgmt_frame(skb, br) &&
br_handle_mcast_exception(br, dest, data)) {
if (BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) {
if (!br_is_wlan_dev(skb->dev) ||
br_wlan_report_flood(br)) {
mdst = br_mdb_get(br, skb, vid);
br_report_flood(br, mdst, skb);
}
br_multicast_forward(mdst, skb, skb2);
skb = NULL;
} else if(BR_INPUT_SKB_CB_UCAST_FWD(skb)) {
mdst = br_mdb_get_ext(br, skb, vid);
if (mdst) {
br_multicast_copy_to_sub_ports(br, mdst, skb);
if (skb2 == NULL)
kfree_skb(skb);
skb = NULL;
} else if (br->igmp_snoop_enabled == BR_IGMP_SNOOP_ENABLED) {
goto drop;
}
} else {
mdst = br_mdb_get(br, skb, vid);
if (mdst) {
br_multicast_forward(mdst, skb, skb2);
skb = NULL;
} else if (br->igmp_snoop_enabled == BR_IGMP_SNOOP_ENABLED) {
goto drop;
} else if (br->igmp_snoop_enabled == BR_IGMP_SNOOP_ENABLED) {
goto drop;
}
}
}
if (skb) {
bool unicast = true;
if (is_broadcast_ether_addr(dest))
unicast = false;
skb->dest_port = 0;
br_flood_forward(br, skb, skb2, unicast);
}
if (skb2)
return br_pass_frame_up(skb2);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
}
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)
return;
if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
dev->stats.tx_dropped++;
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_get(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);
}
}
/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
struct net_bridge *br;
struct net_bridge_fdb_entry *dst;
struct sk_buff *skb2;
bool unicast = true;
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;
vid = skb->hw_vlan_id;
/* insert into forwarding database after filtering to avoid spoofing */
br = p->br;
/* ETH_P_80211_RAW */
/* This type of frame should be passed up the stack, not forwarded or flooded.
* Note that due to the nature of the skb with this protocol, the header is an
* 802.11 header, not an Ethernet header.
*/
if (skb->protocol == __constant_htons(ETH_P_80211_RAW))
{
/* Pass it up, do not forward or add entries to the bridge table */
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
br_pass_frame_up(skb);
goto out;
}
if (p->flags & BR_LEARNING)
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false, skb->src_port);
br_handle_fwt_capacity(br);
if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
br_multicast_rcv(br, p, skb, vid))
goto drop;
#ifdef CONFIG_ARC_PLAT_QTN
if (M_FLAG_ISSET(skb, M_HAS_MISALIGN))
dest = eth_hdr(skb)->h_dest;
#endif
if (p->state == BR_STATE_LEARNING)
goto drop;
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
/* The packet skb2 goes to the local host (NULL to skip). */
skb2 = NULL;
if (br->dev->flags & IFF_PROMISC)
skb2 = skb;
else if (unlikely(br_vlan_promisc && skb->protocol == __constant_htons(ETH_P_8021Q)))
skb2 = skb;
dst = NULL;
#ifdef CONFIG_IPV6
if ((eth_hdr(skb)->h_proto == __constant_htons(ETH_P_IPV6)) &&
(skb->len < sizeof(struct ipv6hdr))) {
goto drop;
}
#endif
if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP))
br_do_proxy_arp(skb, br, vid, p);
if (is_broadcast_ether_addr(dest)) {
skb2 = skb;
unicast = false;
} else if (is_multicast_ether_addr(dest)) {
return br_handle_frame_finish_multicast(skb, br, dest, vid);
} else if ((dst = __br_fdb_get(br, dest, vid)) &&
dst->is_local) {
skb2 = skb;
/* Do not forward the packet since it's local. */
skb = NULL;
}
if (skb) {
if (dst) {
dst->used = jiffies;
skb->dest_port = dst->sub_port;
br_forward(dst->dst, skb, skb2);
} else {
skb->dest_port = 0;
br_flood_forward(br, skb, skb2, unicast);
}
}
if (skb2)
return br_pass_frame_up(skb2);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
}
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, 0);
}
/* 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;
}
/*
* 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;
/*
* EAP frames destined for a bridge port need no bridge
* handling.
*/
if (unlikely(skb->protocol == __constant_htons(ETH_P_PAE)
&& skb->pkt_type == PACKET_HOST))
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);
/* For ETH_P_80211_RAW, any pointers to addresses are invalid, so
* simply pass the packet onto the br_handle_frame_finish to pass
* it up the stack.
*/
if (skb->protocol == __constant_htons(ETH_P_80211_RAW)) {
if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, dev_net(skb->dev), NULL, skb, skb->dev,
NULL, br_handle_frame_finish);
} else {
goto drop;
}
return RX_HANDLER_CONSUMED;
}
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;
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 */
if (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; /* consumed by filter */
} else {
*pskb = skb;
return RX_HANDLER_PASS; /* continue processing */
}
}
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;
}