162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Forwarding decision
462306a36Sopenharmony_ci *	Linux ethernet bridge
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	Authors:
762306a36Sopenharmony_ci *	Lennert Buytenhek		<buytenh@gnu.org>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/netdevice.h>
1462306a36Sopenharmony_ci#include <linux/netpoll.h>
1562306a36Sopenharmony_ci#include <linux/skbuff.h>
1662306a36Sopenharmony_ci#include <linux/if_vlan.h>
1762306a36Sopenharmony_ci#include <linux/netfilter_bridge.h>
1862306a36Sopenharmony_ci#include "br_private.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Don't forward packets to originating port or forwarding disabled */
2162306a36Sopenharmony_cistatic inline int should_deliver(const struct net_bridge_port *p,
2262306a36Sopenharmony_ci				 const struct sk_buff *skb)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	vg = nbp_vlan_group_rcu(p);
2762306a36Sopenharmony_ci	return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
2862306a36Sopenharmony_ci		p->state == BR_STATE_FORWARDING && br_allowed_egress(vg, skb) &&
2962306a36Sopenharmony_ci		nbp_switchdev_allowed_egress(p, skb) &&
3062306a36Sopenharmony_ci		!br_skb_isolated(p, skb);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciint br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	skb_push(skb, ETH_HLEN);
3662306a36Sopenharmony_ci	if (!is_skb_forwardable(skb->dev, skb))
3762306a36Sopenharmony_ci		goto drop;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	br_drop_fake_rtable(skb);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL &&
4262306a36Sopenharmony_ci	    eth_type_vlan(skb->protocol)) {
4362306a36Sopenharmony_ci		int depth;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci		if (!vlan_get_protocol_and_depth(skb, skb->protocol, &depth))
4662306a36Sopenharmony_ci			goto drop;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		skb_set_network_header(skb, depth);
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	br_switchdev_frame_set_offload_fwd_mark(skb);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	dev_queue_xmit(skb);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cidrop:
5862306a36Sopenharmony_ci	kfree_skb(skb);
5962306a36Sopenharmony_ci	return 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_dev_queue_push_xmit);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciint br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	skb_clear_tstamp(skb);
6662306a36Sopenharmony_ci	return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING,
6762306a36Sopenharmony_ci		       net, sk, skb, NULL, skb->dev,
6862306a36Sopenharmony_ci		       br_dev_queue_push_xmit);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_forward_finish);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void __br_forward(const struct net_bridge_port *to,
7462306a36Sopenharmony_ci			 struct sk_buff *skb, bool local_orig)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
7762306a36Sopenharmony_ci	struct net_device *indev;
7862306a36Sopenharmony_ci	struct net *net;
7962306a36Sopenharmony_ci	int br_hook;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* Mark the skb for forwarding offload early so that br_handle_vlan()
8262306a36Sopenharmony_ci	 * can know whether to pop the VLAN header on egress or keep it.
8362306a36Sopenharmony_ci	 */
8462306a36Sopenharmony_ci	nbp_switchdev_frame_mark_tx_fwd_offload(to, skb);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	vg = nbp_vlan_group_rcu(to);
8762306a36Sopenharmony_ci	skb = br_handle_vlan(to->br, to, vg, skb);
8862306a36Sopenharmony_ci	if (!skb)
8962306a36Sopenharmony_ci		return;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	indev = skb->dev;
9262306a36Sopenharmony_ci	skb->dev = to->dev;
9362306a36Sopenharmony_ci	if (!local_orig) {
9462306a36Sopenharmony_ci		if (skb_warn_if_lro(skb)) {
9562306a36Sopenharmony_ci			kfree_skb(skb);
9662306a36Sopenharmony_ci			return;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci		br_hook = NF_BR_FORWARD;
9962306a36Sopenharmony_ci		skb_forward_csum(skb);
10062306a36Sopenharmony_ci		net = dev_net(indev);
10162306a36Sopenharmony_ci	} else {
10262306a36Sopenharmony_ci		if (unlikely(netpoll_tx_running(to->br->dev))) {
10362306a36Sopenharmony_ci			skb_push(skb, ETH_HLEN);
10462306a36Sopenharmony_ci			if (!is_skb_forwardable(skb->dev, skb))
10562306a36Sopenharmony_ci				kfree_skb(skb);
10662306a36Sopenharmony_ci			else
10762306a36Sopenharmony_ci				br_netpoll_send_skb(to, skb);
10862306a36Sopenharmony_ci			return;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci		br_hook = NF_BR_LOCAL_OUT;
11162306a36Sopenharmony_ci		net = dev_net(skb->dev);
11262306a36Sopenharmony_ci		indev = NULL;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	NF_HOOK(NFPROTO_BRIDGE, br_hook,
11662306a36Sopenharmony_ci		net, NULL, skb, indev, skb->dev,
11762306a36Sopenharmony_ci		br_forward_finish);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int deliver_clone(const struct net_bridge_port *prev,
12162306a36Sopenharmony_ci			 struct sk_buff *skb, bool local_orig)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	skb = skb_clone(skb, GFP_ATOMIC);
12662306a36Sopenharmony_ci	if (!skb) {
12762306a36Sopenharmony_ci		DEV_STATS_INC(dev, tx_dropped);
12862306a36Sopenharmony_ci		return -ENOMEM;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	__br_forward(prev, skb, local_orig);
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/**
13662306a36Sopenharmony_ci * br_forward - forward a packet to a specific port
13762306a36Sopenharmony_ci * @to: destination port
13862306a36Sopenharmony_ci * @skb: packet being forwarded
13962306a36Sopenharmony_ci * @local_rcv: packet will be received locally after forwarding
14062306a36Sopenharmony_ci * @local_orig: packet is locally originated
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci * Should be called with rcu_read_lock.
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_civoid br_forward(const struct net_bridge_port *to,
14562306a36Sopenharmony_ci		struct sk_buff *skb, bool local_rcv, bool local_orig)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	if (unlikely(!to))
14862306a36Sopenharmony_ci		goto out;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* redirect to backup link if the destination port is down */
15162306a36Sopenharmony_ci	if (rcu_access_pointer(to->backup_port) && !netif_carrier_ok(to->dev)) {
15262306a36Sopenharmony_ci		struct net_bridge_port *backup_port;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		backup_port = rcu_dereference(to->backup_port);
15562306a36Sopenharmony_ci		if (unlikely(!backup_port))
15662306a36Sopenharmony_ci			goto out;
15762306a36Sopenharmony_ci		BR_INPUT_SKB_CB(skb)->backup_nhid = READ_ONCE(to->backup_nhid);
15862306a36Sopenharmony_ci		to = backup_port;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (should_deliver(to, skb)) {
16262306a36Sopenharmony_ci		if (local_rcv)
16362306a36Sopenharmony_ci			deliver_clone(to, skb, local_orig);
16462306a36Sopenharmony_ci		else
16562306a36Sopenharmony_ci			__br_forward(to, skb, local_orig);
16662306a36Sopenharmony_ci		return;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ciout:
17062306a36Sopenharmony_ci	if (!local_rcv)
17162306a36Sopenharmony_ci		kfree_skb(skb);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_forward);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic struct net_bridge_port *maybe_deliver(
17662306a36Sopenharmony_ci	struct net_bridge_port *prev, struct net_bridge_port *p,
17762306a36Sopenharmony_ci	struct sk_buff *skb, bool local_orig)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	u8 igmp_type = br_multicast_igmp_type(skb);
18062306a36Sopenharmony_ci	int err;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (!should_deliver(p, skb))
18362306a36Sopenharmony_ci		return prev;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	nbp_switchdev_frame_mark_tx_fwd_to_hwdom(p, skb);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (!prev)
18862306a36Sopenharmony_ci		goto out;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	err = deliver_clone(prev, skb, local_orig);
19162306a36Sopenharmony_ci	if (err)
19262306a36Sopenharmony_ci		return ERR_PTR(err);
19362306a36Sopenharmony_ciout:
19462306a36Sopenharmony_ci	br_multicast_count(p->br, p, skb, igmp_type, BR_MCAST_DIR_TX);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return p;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/* called under rcu_read_lock */
20062306a36Sopenharmony_civoid br_flood(struct net_bridge *br, struct sk_buff *skb,
20162306a36Sopenharmony_ci	      enum br_pkt_type pkt_type, bool local_rcv, bool local_orig,
20262306a36Sopenharmony_ci	      u16 vid)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct net_bridge_port *prev = NULL;
20562306a36Sopenharmony_ci	struct net_bridge_port *p;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	br_tc_skb_miss_set(skb, pkt_type != BR_PKT_BROADCAST);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	list_for_each_entry_rcu(p, &br->port_list, list) {
21062306a36Sopenharmony_ci		/* Do not flood unicast traffic to ports that turn it off, nor
21162306a36Sopenharmony_ci		 * other traffic if flood off, except for traffic we originate
21262306a36Sopenharmony_ci		 */
21362306a36Sopenharmony_ci		switch (pkt_type) {
21462306a36Sopenharmony_ci		case BR_PKT_UNICAST:
21562306a36Sopenharmony_ci			if (!(p->flags & BR_FLOOD))
21662306a36Sopenharmony_ci				continue;
21762306a36Sopenharmony_ci			break;
21862306a36Sopenharmony_ci		case BR_PKT_MULTICAST:
21962306a36Sopenharmony_ci			if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
22062306a36Sopenharmony_ci				continue;
22162306a36Sopenharmony_ci			break;
22262306a36Sopenharmony_ci		case BR_PKT_BROADCAST:
22362306a36Sopenharmony_ci			if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev)
22462306a36Sopenharmony_ci				continue;
22562306a36Sopenharmony_ci			break;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		/* Do not flood to ports that enable proxy ARP */
22962306a36Sopenharmony_ci		if (p->flags & BR_PROXYARP)
23062306a36Sopenharmony_ci			continue;
23162306a36Sopenharmony_ci		if (BR_INPUT_SKB_CB(skb)->proxyarp_replied &&
23262306a36Sopenharmony_ci		    ((p->flags & BR_PROXYARP_WIFI) ||
23362306a36Sopenharmony_ci		     br_is_neigh_suppress_enabled(p, vid)))
23462306a36Sopenharmony_ci			continue;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		prev = maybe_deliver(prev, p, skb, local_orig);
23762306a36Sopenharmony_ci		if (IS_ERR(prev))
23862306a36Sopenharmony_ci			goto out;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (!prev)
24262306a36Sopenharmony_ci		goto out;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (local_rcv)
24562306a36Sopenharmony_ci		deliver_clone(prev, skb, local_orig);
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		__br_forward(prev, skb, local_orig);
24862306a36Sopenharmony_ci	return;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciout:
25162306a36Sopenharmony_ci	if (!local_rcv)
25262306a36Sopenharmony_ci		kfree_skb(skb);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
25662306a36Sopenharmony_cistatic void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb,
25762306a36Sopenharmony_ci			       const unsigned char *addr, bool local_orig)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
26062306a36Sopenharmony_ci	const unsigned char *src = eth_hdr(skb)->h_source;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (!should_deliver(p, skb))
26362306a36Sopenharmony_ci		return;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */
26662306a36Sopenharmony_ci	if (skb->dev == p->dev && ether_addr_equal(src, addr))
26762306a36Sopenharmony_ci		return;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	skb = skb_copy(skb, GFP_ATOMIC);
27062306a36Sopenharmony_ci	if (!skb) {
27162306a36Sopenharmony_ci		DEV_STATS_INC(dev, tx_dropped);
27262306a36Sopenharmony_ci		return;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (!is_broadcast_ether_addr(addr))
27662306a36Sopenharmony_ci		memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	__br_forward(p, skb, local_orig);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/* called with rcu_read_lock */
28262306a36Sopenharmony_civoid br_multicast_flood(struct net_bridge_mdb_entry *mdst,
28362306a36Sopenharmony_ci			struct sk_buff *skb,
28462306a36Sopenharmony_ci			struct net_bridge_mcast *brmctx,
28562306a36Sopenharmony_ci			bool local_rcv, bool local_orig)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct net_bridge_port *prev = NULL;
28862306a36Sopenharmony_ci	struct net_bridge_port_group *p;
28962306a36Sopenharmony_ci	bool allow_mode_include = true;
29062306a36Sopenharmony_ci	struct hlist_node *rp;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	rp = br_multicast_get_first_rport_node(brmctx, skb);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (mdst) {
29562306a36Sopenharmony_ci		p = rcu_dereference(mdst->ports);
29662306a36Sopenharmony_ci		if (br_multicast_should_handle_mode(brmctx, mdst->addr.proto) &&
29762306a36Sopenharmony_ci		    br_multicast_is_star_g(&mdst->addr))
29862306a36Sopenharmony_ci			allow_mode_include = false;
29962306a36Sopenharmony_ci	} else {
30062306a36Sopenharmony_ci		p = NULL;
30162306a36Sopenharmony_ci		br_tc_skb_miss_set(skb, true);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	while (p || rp) {
30562306a36Sopenharmony_ci		struct net_bridge_port *port, *lport, *rport;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		lport = p ? p->key.port : NULL;
30862306a36Sopenharmony_ci		rport = br_multicast_rport_from_node_skb(rp, skb);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		if ((unsigned long)lport > (unsigned long)rport) {
31162306a36Sopenharmony_ci			port = lport;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci			if (port->flags & BR_MULTICAST_TO_UNICAST) {
31462306a36Sopenharmony_ci				maybe_deliver_addr(lport, skb, p->eth_addr,
31562306a36Sopenharmony_ci						   local_orig);
31662306a36Sopenharmony_ci				goto delivered;
31762306a36Sopenharmony_ci			}
31862306a36Sopenharmony_ci			if ((!allow_mode_include &&
31962306a36Sopenharmony_ci			     p->filter_mode == MCAST_INCLUDE) ||
32062306a36Sopenharmony_ci			    (p->flags & MDB_PG_FLAGS_BLOCKED))
32162306a36Sopenharmony_ci				goto delivered;
32262306a36Sopenharmony_ci		} else {
32362306a36Sopenharmony_ci			port = rport;
32462306a36Sopenharmony_ci		}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		prev = maybe_deliver(prev, port, skb, local_orig);
32762306a36Sopenharmony_ci		if (IS_ERR(prev))
32862306a36Sopenharmony_ci			goto out;
32962306a36Sopenharmony_cidelivered:
33062306a36Sopenharmony_ci		if ((unsigned long)lport >= (unsigned long)port)
33162306a36Sopenharmony_ci			p = rcu_dereference(p->next);
33262306a36Sopenharmony_ci		if ((unsigned long)rport >= (unsigned long)port)
33362306a36Sopenharmony_ci			rp = rcu_dereference(hlist_next_rcu(rp));
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (!prev)
33762306a36Sopenharmony_ci		goto out;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (local_rcv)
34062306a36Sopenharmony_ci		deliver_clone(prev, skb, local_orig);
34162306a36Sopenharmony_ci	else
34262306a36Sopenharmony_ci		__br_forward(prev, skb, local_orig);
34362306a36Sopenharmony_ci	return;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciout:
34662306a36Sopenharmony_ci	if (!local_rcv)
34762306a36Sopenharmony_ci		kfree_skb(skb);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci#endif
350