xref: /openbmc/linux/net/bridge/br_forward.c (revision 6db6f0ea)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	Forwarding decision
31da177e4SLinus Torvalds  *	Linux ethernet bridge
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *	Lennert Buytenhek		<buytenh@gnu.org>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
91da177e4SLinus Torvalds  *	modify it under the terms of the GNU General Public License
101da177e4SLinus Torvalds  *	as published by the Free Software Foundation; either version
111da177e4SLinus Torvalds  *	2 of the License, or (at your option) any later version.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
14025d89c2SHerbert Xu #include <linux/err.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/netdevice.h>
18c06ee961SWANG Cong #include <linux/netpoll.h>
191da177e4SLinus Torvalds #include <linux/skbuff.h>
2085ca719eSStephen Hemminger #include <linux/if_vlan.h>
211da177e4SLinus Torvalds #include <linux/netfilter_bridge.h>
221da177e4SLinus Torvalds #include "br_private.h"
231da177e4SLinus Torvalds 
241a81a2e0Stanxiaojun /* Don't forward packets to originating port or forwarding disabled */
251da177e4SLinus Torvalds static inline int should_deliver(const struct net_bridge_port *p,
261da177e4SLinus Torvalds 				 const struct sk_buff *skb)
271da177e4SLinus Torvalds {
282594e906SNikolay Aleksandrov 	struct net_bridge_vlan_group *vg;
292594e906SNikolay Aleksandrov 
30907b1e6eSNikolay Aleksandrov 	vg = nbp_vlan_group_rcu(p);
31a97bfc1dStanxiaojun 	return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
326bc506b4SIdo Schimmel 		br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING &&
336bc506b4SIdo Schimmel 		nbp_switchdev_allowed_egress(p, skb);
341da177e4SLinus Torvalds }
351da177e4SLinus Torvalds 
360c4b51f0SEric W. Biederman int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
371da177e4SLinus Torvalds {
38df356d5eSToshiaki Makita 	if (!is_skb_forwardable(skb->dev, skb))
39df356d5eSToshiaki Makita 		goto drop;
40df356d5eSToshiaki Makita 
411da177e4SLinus Torvalds 	skb_push(skb, ETH_HLEN);
42a881e963SPeter Huang (Peng) 	br_drop_fake_rtable(skb);
43df356d5eSToshiaki Makita 
44df356d5eSToshiaki Makita 	if (skb->ip_summed == CHECKSUM_PARTIAL &&
45df356d5eSToshiaki Makita 	    (skb->protocol == htons(ETH_P_8021Q) ||
46df356d5eSToshiaki Makita 	     skb->protocol == htons(ETH_P_8021AD))) {
47df356d5eSToshiaki Makita 		int depth;
48df356d5eSToshiaki Makita 
49df356d5eSToshiaki Makita 		if (!__vlan_get_protocol(skb, skb->protocol, &depth))
50df356d5eSToshiaki Makita 			goto drop;
51df356d5eSToshiaki Makita 
52df356d5eSToshiaki Makita 		skb_set_network_header(skb, depth);
531da177e4SLinus Torvalds 	}
541da177e4SLinus Torvalds 
55df356d5eSToshiaki Makita 	dev_queue_xmit(skb);
56df356d5eSToshiaki Makita 
57df356d5eSToshiaki Makita 	return 0;
58df356d5eSToshiaki Makita 
59df356d5eSToshiaki Makita drop:
60df356d5eSToshiaki Makita 	kfree_skb(skb);
611da177e4SLinus Torvalds 	return 0;
621da177e4SLinus Torvalds }
6334666d46SPablo Neira Ayuso EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit);
641da177e4SLinus Torvalds 
650c4b51f0SEric W. Biederman int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
661da177e4SLinus Torvalds {
6729a26a56SEric W. Biederman 	return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING,
6829a26a56SEric W. Biederman 		       net, sk, skb, NULL, skb->dev,
691da177e4SLinus Torvalds 		       br_dev_queue_push_xmit);
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds }
7234666d46SPablo Neira Ayuso EXPORT_SYMBOL_GPL(br_forward_finish);
731da177e4SLinus Torvalds 
7437b090e6SNikolay Aleksandrov static void __br_forward(const struct net_bridge_port *to,
7537b090e6SNikolay Aleksandrov 			 struct sk_buff *skb, bool local_orig)
761da177e4SLinus Torvalds {
772594e906SNikolay Aleksandrov 	struct net_bridge_vlan_group *vg;
781da177e4SLinus Torvalds 	struct net_device *indev;
7937b090e6SNikolay Aleksandrov 	struct net *net;
8037b090e6SNikolay Aleksandrov 	int br_hook;
814906f998SHerbert Xu 
82907b1e6eSNikolay Aleksandrov 	vg = nbp_vlan_group_rcu(to);
832594e906SNikolay Aleksandrov 	skb = br_handle_vlan(to->br, vg, skb);
8478851988SVlad Yasevich 	if (!skb)
8578851988SVlad Yasevich 		return;
8678851988SVlad Yasevich 
871da177e4SLinus Torvalds 	indev = skb->dev;
881da177e4SLinus Torvalds 	skb->dev = to->dev;
8937b090e6SNikolay Aleksandrov 	if (!local_orig) {
9037b090e6SNikolay Aleksandrov 		if (skb_warn_if_lro(skb)) {
9137b090e6SNikolay Aleksandrov 			kfree_skb(skb);
9237b090e6SNikolay Aleksandrov 			return;
9337b090e6SNikolay Aleksandrov 		}
9437b090e6SNikolay Aleksandrov 		br_hook = NF_BR_FORWARD;
9535fc92a9SHerbert Xu 		skb_forward_csum(skb);
9637b090e6SNikolay Aleksandrov 		net = dev_net(indev);
9737b090e6SNikolay Aleksandrov 	} else {
9837b090e6SNikolay Aleksandrov 		if (unlikely(netpoll_tx_running(to->br->dev))) {
9937b090e6SNikolay Aleksandrov 			if (!is_skb_forwardable(skb->dev, skb)) {
10037b090e6SNikolay Aleksandrov 				kfree_skb(skb);
10137b090e6SNikolay Aleksandrov 			} else {
10237b090e6SNikolay Aleksandrov 				skb_push(skb, ETH_HLEN);
10337b090e6SNikolay Aleksandrov 				br_netpoll_send_skb(to, skb);
10437b090e6SNikolay Aleksandrov 			}
10537b090e6SNikolay Aleksandrov 			return;
10637b090e6SNikolay Aleksandrov 		}
10737b090e6SNikolay Aleksandrov 		br_hook = NF_BR_LOCAL_OUT;
10837b090e6SNikolay Aleksandrov 		net = dev_net(skb->dev);
10937b090e6SNikolay Aleksandrov 		indev = NULL;
11037b090e6SNikolay Aleksandrov 	}
1111da177e4SLinus Torvalds 
11237b090e6SNikolay Aleksandrov 	NF_HOOK(NFPROTO_BRIDGE, br_hook,
11337b090e6SNikolay Aleksandrov 		net, NULL, skb, indev, skb->dev,
1141da177e4SLinus Torvalds 		br_forward_finish);
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds 
11787faf3ccSDavid S. Miller static int deliver_clone(const struct net_bridge_port *prev,
11837b090e6SNikolay Aleksandrov 			 struct sk_buff *skb, bool local_orig)
119025d89c2SHerbert Xu {
120025d89c2SHerbert Xu 	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
121025d89c2SHerbert Xu 
122fed396a5SHerbert Xu 	skb = skb_clone(skb, GFP_ATOMIC);
123fed396a5SHerbert Xu 	if (!skb) {
124025d89c2SHerbert Xu 		dev->stats.tx_dropped++;
125025d89c2SHerbert Xu 		return -ENOMEM;
126025d89c2SHerbert Xu 	}
127025d89c2SHerbert Xu 
12837b090e6SNikolay Aleksandrov 	__br_forward(prev, skb, local_orig);
129025d89c2SHerbert Xu 	return 0;
130025d89c2SHerbert Xu }
131025d89c2SHerbert Xu 
13237b090e6SNikolay Aleksandrov /**
13337b090e6SNikolay Aleksandrov  * br_forward - forward a packet to a specific port
13437b090e6SNikolay Aleksandrov  * @to: destination port
13537b090e6SNikolay Aleksandrov  * @skb: packet being forwarded
13637b090e6SNikolay Aleksandrov  * @local_rcv: packet will be received locally after forwarding
13737b090e6SNikolay Aleksandrov  * @local_orig: packet is locally originated
13837b090e6SNikolay Aleksandrov  *
13937b090e6SNikolay Aleksandrov  * Should be called with rcu_read_lock.
14037b090e6SNikolay Aleksandrov  */
14137b090e6SNikolay Aleksandrov void br_forward(const struct net_bridge_port *to,
14237b090e6SNikolay Aleksandrov 		struct sk_buff *skb, bool local_rcv, bool local_orig)
14337b090e6SNikolay Aleksandrov {
14437b090e6SNikolay Aleksandrov 	if (to && should_deliver(to, skb)) {
14537b090e6SNikolay Aleksandrov 		if (local_rcv)
14637b090e6SNikolay Aleksandrov 			deliver_clone(to, skb, local_orig);
14737b090e6SNikolay Aleksandrov 		else
14837b090e6SNikolay Aleksandrov 			__br_forward(to, skb, local_orig);
14937b090e6SNikolay Aleksandrov 		return;
15037b090e6SNikolay Aleksandrov 	}
15137b090e6SNikolay Aleksandrov 
15237b090e6SNikolay Aleksandrov 	if (!local_rcv)
15337b090e6SNikolay Aleksandrov 		kfree_skb(skb);
15437b090e6SNikolay Aleksandrov }
15537b090e6SNikolay Aleksandrov EXPORT_SYMBOL_GPL(br_forward);
15637b090e6SNikolay Aleksandrov 
157025d89c2SHerbert Xu static struct net_bridge_port *maybe_deliver(
158025d89c2SHerbert Xu 	struct net_bridge_port *prev, struct net_bridge_port *p,
15937b090e6SNikolay Aleksandrov 	struct sk_buff *skb, bool local_orig)
160025d89c2SHerbert Xu {
161025d89c2SHerbert Xu 	int err;
162025d89c2SHerbert Xu 
163025d89c2SHerbert Xu 	if (!should_deliver(p, skb))
164025d89c2SHerbert Xu 		return prev;
165025d89c2SHerbert Xu 
166025d89c2SHerbert Xu 	if (!prev)
167025d89c2SHerbert Xu 		goto out;
168025d89c2SHerbert Xu 
16937b090e6SNikolay Aleksandrov 	err = deliver_clone(prev, skb, local_orig);
170025d89c2SHerbert Xu 	if (err)
171025d89c2SHerbert Xu 		return ERR_PTR(err);
172025d89c2SHerbert Xu 
173025d89c2SHerbert Xu out:
174025d89c2SHerbert Xu 	return p;
175025d89c2SHerbert Xu }
176025d89c2SHerbert Xu 
1776db6f0eaSFelix Fietkau static void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb,
1786db6f0eaSFelix Fietkau 			       const unsigned char *addr, bool local_orig)
1796db6f0eaSFelix Fietkau {
1806db6f0eaSFelix Fietkau 	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
1816db6f0eaSFelix Fietkau 	const unsigned char *src = eth_hdr(skb)->h_source;
1826db6f0eaSFelix Fietkau 
1836db6f0eaSFelix Fietkau 	if (!should_deliver(p, skb))
1846db6f0eaSFelix Fietkau 		return;
1856db6f0eaSFelix Fietkau 
1866db6f0eaSFelix Fietkau 	/* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */
1876db6f0eaSFelix Fietkau 	if (skb->dev == p->dev && ether_addr_equal(src, addr))
1886db6f0eaSFelix Fietkau 		return;
1896db6f0eaSFelix Fietkau 
1906db6f0eaSFelix Fietkau 	skb = skb_copy(skb, GFP_ATOMIC);
1916db6f0eaSFelix Fietkau 	if (!skb) {
1926db6f0eaSFelix Fietkau 		dev->stats.tx_dropped++;
1936db6f0eaSFelix Fietkau 		return;
1946db6f0eaSFelix Fietkau 	}
1956db6f0eaSFelix Fietkau 
1966db6f0eaSFelix Fietkau 	if (!is_broadcast_ether_addr(addr))
1976db6f0eaSFelix Fietkau 		memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN);
1986db6f0eaSFelix Fietkau 
1996db6f0eaSFelix Fietkau 	__br_forward(p, skb, local_orig);
2006db6f0eaSFelix Fietkau }
2016db6f0eaSFelix Fietkau 
20237b090e6SNikolay Aleksandrov /* called under rcu_read_lock */
20337b090e6SNikolay Aleksandrov void br_flood(struct net_bridge *br, struct sk_buff *skb,
2048addd5e7SNikolay Aleksandrov 	      enum br_pkt_type pkt_type, bool local_rcv, bool local_orig)
2051da177e4SLinus Torvalds {
2061080ab95SNikolay Aleksandrov 	u8 igmp_type = br_multicast_igmp_type(skb);
20737b090e6SNikolay Aleksandrov 	struct net_bridge_port *prev = NULL;
2081080ab95SNikolay Aleksandrov 	struct net_bridge_port *p;
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	list_for_each_entry_rcu(p, &br->port_list, list) {
211867a5943SVlad Yasevich 		/* Do not flood unicast traffic to ports that turn it off */
2128addd5e7SNikolay Aleksandrov 		if (pkt_type == BR_PKT_UNICAST && !(p->flags & BR_FLOOD))
213867a5943SVlad Yasevich 			continue;
214b6cb5ac8SNikolay Aleksandrov 		if (pkt_type == BR_PKT_MULTICAST &&
215b6cb5ac8SNikolay Aleksandrov 		    !(p->flags & BR_MCAST_FLOOD))
216b6cb5ac8SNikolay Aleksandrov 			continue;
21795850116SKyeyoon Park 
21895850116SKyeyoon Park 		/* Do not flood to ports that enable proxy ARP */
21995850116SKyeyoon Park 		if (p->flags & BR_PROXYARP)
22095850116SKyeyoon Park 			continue;
221842a9ae0SJouni Malinen 		if ((p->flags & BR_PROXYARP_WIFI) &&
222842a9ae0SJouni Malinen 		    BR_INPUT_SKB_CB(skb)->proxyarp_replied)
223842a9ae0SJouni Malinen 			continue;
22495850116SKyeyoon Park 
22537b090e6SNikolay Aleksandrov 		prev = maybe_deliver(prev, p, skb, local_orig);
226025d89c2SHerbert Xu 		if (IS_ERR(prev))
227b33084beSHerbert Xu 			goto out;
2281080ab95SNikolay Aleksandrov 		if (prev == p)
229a65056ecSNikolay Aleksandrov 			br_multicast_count(p->br, p, skb, igmp_type,
2301080ab95SNikolay Aleksandrov 					   BR_MCAST_DIR_TX);
2311da177e4SLinus Torvalds 	}
2321da177e4SLinus Torvalds 
233b33084beSHerbert Xu 	if (!prev)
234b33084beSHerbert Xu 		goto out;
235b33084beSHerbert Xu 
236b35c5f63SNikolay Aleksandrov 	if (local_rcv)
23737b090e6SNikolay Aleksandrov 		deliver_clone(prev, skb, local_orig);
238025d89c2SHerbert Xu 	else
23937b090e6SNikolay Aleksandrov 		__br_forward(prev, skb, local_orig);
2401da177e4SLinus Torvalds 	return;
2411da177e4SLinus Torvalds 
242b33084beSHerbert Xu out:
243b35c5f63SNikolay Aleksandrov 	if (!local_rcv)
2441da177e4SLinus Torvalds 		kfree_skb(skb);
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds 
2475cb5e947SHerbert Xu #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
2485cb5e947SHerbert Xu /* called with rcu_read_lock */
24937b090e6SNikolay Aleksandrov void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
250b35c5f63SNikolay Aleksandrov 			struct sk_buff *skb,
25137b090e6SNikolay Aleksandrov 			bool local_rcv, bool local_orig)
2525cb5e947SHerbert Xu {
2535cb5e947SHerbert Xu 	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
2541080ab95SNikolay Aleksandrov 	u8 igmp_type = br_multicast_igmp_type(skb);
2555cb5e947SHerbert Xu 	struct net_bridge *br = netdev_priv(dev);
256afe0159dSstephen hemminger 	struct net_bridge_port *prev = NULL;
2575cb5e947SHerbert Xu 	struct net_bridge_port_group *p;
2585cb5e947SHerbert Xu 	struct hlist_node *rp;
2595cb5e947SHerbert Xu 
260e8051688SEric Dumazet 	rp = rcu_dereference(hlist_first_rcu(&br->router_list));
26183f6a740Sstephen hemminger 	p = mdst ? rcu_dereference(mdst->ports) : NULL;
2625cb5e947SHerbert Xu 	while (p || rp) {
263afe0159dSstephen hemminger 		struct net_bridge_port *port, *lport, *rport;
264afe0159dSstephen hemminger 
2655cb5e947SHerbert Xu 		lport = p ? p->port : NULL;
2665cb5e947SHerbert Xu 		rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) :
2675cb5e947SHerbert Xu 			     NULL;
2685cb5e947SHerbert Xu 
2696db6f0eaSFelix Fietkau 		if ((unsigned long)lport > (unsigned long)rport) {
2706db6f0eaSFelix Fietkau 			port = lport;
2716db6f0eaSFelix Fietkau 
2726db6f0eaSFelix Fietkau 			if (port->flags & BR_MULTICAST_TO_UNICAST) {
2736db6f0eaSFelix Fietkau 				maybe_deliver_addr(lport, skb, p->eth_addr,
2746db6f0eaSFelix Fietkau 						   local_orig);
2756db6f0eaSFelix Fietkau 				goto delivered;
2766db6f0eaSFelix Fietkau 			}
2776db6f0eaSFelix Fietkau 		} else {
2786db6f0eaSFelix Fietkau 			port = rport;
2796db6f0eaSFelix Fietkau 		}
2805cb5e947SHerbert Xu 
28137b090e6SNikolay Aleksandrov 		prev = maybe_deliver(prev, port, skb, local_orig);
2826db6f0eaSFelix Fietkau delivered:
2835cb5e947SHerbert Xu 		if (IS_ERR(prev))
2845cb5e947SHerbert Xu 			goto out;
2851080ab95SNikolay Aleksandrov 		if (prev == port)
286a65056ecSNikolay Aleksandrov 			br_multicast_count(port->br, port, skb, igmp_type,
2871080ab95SNikolay Aleksandrov 					   BR_MCAST_DIR_TX);
2885cb5e947SHerbert Xu 
2895cb5e947SHerbert Xu 		if ((unsigned long)lport >= (unsigned long)port)
29083f6a740Sstephen hemminger 			p = rcu_dereference(p->next);
2915cb5e947SHerbert Xu 		if ((unsigned long)rport >= (unsigned long)port)
292e8051688SEric Dumazet 			rp = rcu_dereference(hlist_next_rcu(rp));
2935cb5e947SHerbert Xu 	}
2945cb5e947SHerbert Xu 
2955cb5e947SHerbert Xu 	if (!prev)
2965cb5e947SHerbert Xu 		goto out;
2975cb5e947SHerbert Xu 
298b35c5f63SNikolay Aleksandrov 	if (local_rcv)
29937b090e6SNikolay Aleksandrov 		deliver_clone(prev, skb, local_orig);
3005cb5e947SHerbert Xu 	else
30137b090e6SNikolay Aleksandrov 		__br_forward(prev, skb, local_orig);
3025cb5e947SHerbert Xu 	return;
3035cb5e947SHerbert Xu 
3045cb5e947SHerbert Xu out:
305b35c5f63SNikolay Aleksandrov 	if (!local_rcv)
3065cb5e947SHerbert Xu 		kfree_skb(skb);
3075cb5e947SHerbert Xu }
3085cb5e947SHerbert Xu #endif
309