xref: /openbmc/linux/net/bridge/br_forward.c (revision 1da177e4)
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  *	$Id: br_forward.c,v 1.4 2001/08/14 22:05:57 davem Exp $
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
111da177e4SLinus Torvalds  *	modify it under the terms of the GNU General Public License
121da177e4SLinus Torvalds  *	as published by the Free Software Foundation; either version
131da177e4SLinus Torvalds  *	2 of the License, or (at your option) any later version.
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/netdevice.h>
181da177e4SLinus Torvalds #include <linux/skbuff.h>
191da177e4SLinus Torvalds #include <linux/netfilter_bridge.h>
201da177e4SLinus Torvalds #include "br_private.h"
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds static inline int should_deliver(const struct net_bridge_port *p,
231da177e4SLinus Torvalds 				 const struct sk_buff *skb)
241da177e4SLinus Torvalds {
251da177e4SLinus Torvalds 	if (skb->dev == p->dev ||
261da177e4SLinus Torvalds 	    p->state != BR_STATE_FORWARDING)
271da177e4SLinus Torvalds 		return 0;
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds 	return 1;
301da177e4SLinus Torvalds }
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds int br_dev_queue_push_xmit(struct sk_buff *skb)
331da177e4SLinus Torvalds {
341da177e4SLinus Torvalds 	if (skb->len > skb->dev->mtu)
351da177e4SLinus Torvalds 		kfree_skb(skb);
361da177e4SLinus Torvalds 	else {
371da177e4SLinus Torvalds #ifdef CONFIG_BRIDGE_NETFILTER
381da177e4SLinus Torvalds 		/* ip_refrag calls ip_fragment, doesn't copy the MAC header. */
391da177e4SLinus Torvalds 		nf_bridge_maybe_copy_header(skb);
401da177e4SLinus Torvalds #endif
411da177e4SLinus Torvalds 		skb_push(skb, ETH_HLEN);
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 		dev_queue_xmit(skb);
441da177e4SLinus Torvalds 	}
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 	return 0;
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds int br_forward_finish(struct sk_buff *skb)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
521da177e4SLinus Torvalds 			br_dev_queue_push_xmit);
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	return 0;
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	skb->dev = to->dev;
601da177e4SLinus Torvalds #ifdef CONFIG_NETFILTER_DEBUG
611da177e4SLinus Torvalds 	skb->nf_debug = 0;
621da177e4SLinus Torvalds #endif
631da177e4SLinus Torvalds 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
641da177e4SLinus Torvalds 			br_forward_finish);
651da177e4SLinus Torvalds }
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds 	struct net_device *indev;
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	indev = skb->dev;
721da177e4SLinus Torvalds 	skb->dev = to->dev;
731da177e4SLinus Torvalds 	skb->ip_summed = CHECKSUM_NONE;
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
761da177e4SLinus Torvalds 			br_forward_finish);
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds /* called with rcu_read_lock */
801da177e4SLinus Torvalds void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds 	if (should_deliver(to, skb)) {
831da177e4SLinus Torvalds 		__br_deliver(to, skb);
841da177e4SLinus Torvalds 		return;
851da177e4SLinus Torvalds 	}
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	kfree_skb(skb);
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds /* called with rcu_read_lock */
911da177e4SLinus Torvalds void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	if (should_deliver(to, skb)) {
941da177e4SLinus Torvalds 		__br_forward(to, skb);
951da177e4SLinus Torvalds 		return;
961da177e4SLinus Torvalds 	}
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	kfree_skb(skb);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds /* called under bridge lock */
1021da177e4SLinus Torvalds static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
1031da177e4SLinus Torvalds 	void (*__packet_hook)(const struct net_bridge_port *p,
1041da177e4SLinus Torvalds 			      struct sk_buff *skb))
1051da177e4SLinus Torvalds {
1061da177e4SLinus Torvalds 	struct net_bridge_port *p;
1071da177e4SLinus Torvalds 	struct net_bridge_port *prev;
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	if (clone) {
1101da177e4SLinus Torvalds 		struct sk_buff *skb2;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
1131da177e4SLinus Torvalds 			br->statistics.tx_dropped++;
1141da177e4SLinus Torvalds 			return;
1151da177e4SLinus Torvalds 		}
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 		skb = skb2;
1181da177e4SLinus Torvalds 	}
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	prev = NULL;
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds 	list_for_each_entry_rcu(p, &br->port_list, list) {
1231da177e4SLinus Torvalds 		if (should_deliver(p, skb)) {
1241da177e4SLinus Torvalds 			if (prev != NULL) {
1251da177e4SLinus Torvalds 				struct sk_buff *skb2;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 				if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
1281da177e4SLinus Torvalds 					br->statistics.tx_dropped++;
1291da177e4SLinus Torvalds 					kfree_skb(skb);
1301da177e4SLinus Torvalds 					return;
1311da177e4SLinus Torvalds 				}
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 				__packet_hook(prev, skb2);
1341da177e4SLinus Torvalds 			}
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 			prev = p;
1371da177e4SLinus Torvalds 		}
1381da177e4SLinus Torvalds 	}
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	if (prev != NULL) {
1411da177e4SLinus Torvalds 		__packet_hook(prev, skb);
1421da177e4SLinus Torvalds 		return;
1431da177e4SLinus Torvalds 	}
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	kfree_skb(skb);
1461da177e4SLinus Torvalds }
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds /* called with rcu_read_lock */
1501da177e4SLinus Torvalds void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
1511da177e4SLinus Torvalds {
1521da177e4SLinus Torvalds 	br_flood(br, skb, clone, __br_deliver);
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds /* called under bridge lock */
1561da177e4SLinus Torvalds void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone)
1571da177e4SLinus Torvalds {
1581da177e4SLinus Torvalds 	br_flood(br, skb, clone, __br_forward);
1591da177e4SLinus Torvalds }
160