br_forward.c 7.04 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
/*
 *	Forwarding decision
 *	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.
 */

14
#include <linux/err.h>
15
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
16
17
#include <linux/kernel.h>
#include <linux/netdevice.h>
18
#include <linux/netpoll.h>
Linus Torvalds's avatar
Linus Torvalds committed
19
#include <linux/skbuff.h>
20
#include <linux/if_vlan.h>
Linus Torvalds's avatar
Linus Torvalds committed
21
22
23
#include <linux/netfilter_bridge.h>
#include "br_private.h"

tanxiaojun's avatar
tanxiaojun committed
24
/* Don't forward packets to originating port or forwarding disabled */
25
static inline int should_deliver(const struct net_bridge_port *p,
Linus Torvalds's avatar
Linus Torvalds committed
26
27
				 const struct sk_buff *skb)
{
28
29
	struct net_bridge_vlan_group *vg;

30
	vg = nbp_vlan_group_rcu(p);
31
	return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
32
33
		br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING &&
		nbp_switchdev_allowed_egress(p, skb);
Linus Torvalds's avatar
Linus Torvalds committed
34
35
}

36
int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
37
{
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
	if (!is_skb_forwardable(skb->dev, skb))
		goto drop;

	skb_push(skb, ETH_HLEN);
	br_drop_fake_rtable(skb);

	if (skb->ip_summed == CHECKSUM_PARTIAL &&
	    (skb->protocol == htons(ETH_P_8021Q) ||
	     skb->protocol == htons(ETH_P_8021AD))) {
		int depth;

		if (!__vlan_get_protocol(skb, skb->protocol, &depth))
			goto drop;

		skb_set_network_header(skb, depth);
Linus Torvalds's avatar
Linus Torvalds committed
53
54
	}

55
56
57
58
59
60
	dev_queue_xmit(skb);

	return 0;

drop:
	kfree_skb(skb);
Linus Torvalds's avatar
Linus Torvalds committed
61
62
	return 0;
}
63
EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit);
Linus Torvalds's avatar
Linus Torvalds committed
64

65
int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds's avatar
Linus Torvalds committed
66
{
67
68
	return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING,
		       net, sk, skb, NULL, skb->dev,
69
		       br_dev_queue_push_xmit);
Linus Torvalds's avatar
Linus Torvalds committed
70
71

}
72
EXPORT_SYMBOL_GPL(br_forward_finish);
Linus Torvalds's avatar
Linus Torvalds committed
73

74
75
static void __br_forward(const struct net_bridge_port *to,
			 struct sk_buff *skb, bool local_orig)
Linus Torvalds's avatar
Linus Torvalds committed
76
{
77
	struct net_bridge_vlan_group *vg;
78
79
80
	struct net_device *indev;
	struct net *net;
	int br_hook;
81

82
	vg = nbp_vlan_group_rcu(to);
83
	skb = br_handle_vlan(to->br, vg, skb);
84
85
86
	if (!skb)
		return;

87
	indev = skb->dev;
Linus Torvalds's avatar
Linus Torvalds committed
88
	skb->dev = to->dev;
89
90
	if (!local_orig) {
		if (skb_warn_if_lro(skb)) {
Herbert Xu's avatar
Herbert Xu committed
91
			kfree_skb(skb);
92
			return;
Herbert Xu's avatar
Herbert Xu committed
93
		}
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
		br_hook = NF_BR_FORWARD;
		skb_forward_csum(skb);
		net = dev_net(indev);
	} else {
		if (unlikely(netpoll_tx_running(to->br->dev))) {
			if (!is_skb_forwardable(skb->dev, skb)) {
				kfree_skb(skb);
			} else {
				skb_push(skb, ETH_HLEN);
				br_netpoll_send_skb(to, skb);
			}
			return;
		}
		br_hook = NF_BR_LOCAL_OUT;
		net = dev_net(skb->dev);
		indev = NULL;
Herbert Xu's avatar
Herbert Xu committed
110
111
	}

112
113
	NF_HOOK(NFPROTO_BRIDGE, br_hook,
		net, NULL, skb, indev, skb->dev,
114
		br_forward_finish);
Linus Torvalds's avatar
Linus Torvalds committed
115
116
}

117
118
static int deliver_clone(const struct net_bridge_port *prev,
			 struct sk_buff *skb, bool local_orig)
Linus Torvalds's avatar
Linus Torvalds committed
119
{
120
	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
Linus Torvalds's avatar
Linus Torvalds committed
121

122
123
124
125
	skb = skb_clone(skb, GFP_ATOMIC);
	if (!skb) {
		dev->stats.tx_dropped++;
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
126
127
	}

128
129
	__br_forward(prev, skb, local_orig);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
130
131
}

132
133
134
135
136
137
138
139
140
141
142
/**
 * br_forward - forward a packet to a specific port
 * @to: destination port
 * @skb: packet being forwarded
 * @local_rcv: packet will be received locally after forwarding
 * @local_orig: packet is locally originated
 *
 * Should be called with rcu_read_lock.
 */
void br_forward(const struct net_bridge_port *to,
		struct sk_buff *skb, bool local_rcv, bool local_orig)
Linus Torvalds's avatar
Linus Torvalds committed
143
{
144
	if (to && should_deliver(to, skb)) {
145
		if (local_rcv)
146
			deliver_clone(to, skb, local_orig);
147
		else
148
			__br_forward(to, skb, local_orig);
Linus Torvalds's avatar
Linus Torvalds committed
149
150
151
		return;
	}

152
	if (!local_rcv)
153
		kfree_skb(skb);
Linus Torvalds's avatar
Linus Torvalds committed
154
}
155
EXPORT_SYMBOL_GPL(br_forward);
156
157
158

static struct net_bridge_port *maybe_deliver(
	struct net_bridge_port *prev, struct net_bridge_port *p,
159
	struct sk_buff *skb, bool local_orig)
160
161
162
163
164
165
166
167
168
{
	int err;

	if (!should_deliver(p, skb))
		return prev;

	if (!prev)
		goto out;

169
	err = deliver_clone(prev, skb, local_orig);
170
171
172
173
174
175
176
	if (err)
		return ERR_PTR(err);

out:
	return p;
}

Felix Fietkau's avatar
Felix Fietkau committed
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
static void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb,
			       const unsigned char *addr, bool local_orig)
{
	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
	const unsigned char *src = eth_hdr(skb)->h_source;

	if (!should_deliver(p, skb))
		return;

	/* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */
	if (skb->dev == p->dev && ether_addr_equal(src, addr))
		return;

	skb = skb_copy(skb, GFP_ATOMIC);
	if (!skb) {
		dev->stats.tx_dropped++;
		return;
	}

	if (!is_broadcast_ether_addr(addr))
		memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN);

	__br_forward(p, skb, local_orig);
}

202
203
/* called under rcu_read_lock */
void br_flood(struct net_bridge *br, struct sk_buff *skb,
204
	      enum br_pkt_type pkt_type, bool local_rcv, bool local_orig)
Linus Torvalds's avatar
Linus Torvalds committed
205
{
206
	u8 igmp_type = br_multicast_igmp_type(skb);
207
	struct net_bridge_port *prev = NULL;
208
	struct net_bridge_port *p;
Linus Torvalds's avatar
Linus Torvalds committed
209
210

	list_for_each_entry_rcu(p, &br->port_list, list) {
211
		/* Do not flood unicast traffic to ports that turn it off */
212
		if (pkt_type == BR_PKT_UNICAST && !(p->flags & BR_FLOOD))
213
			continue;
214
215
216
		if (pkt_type == BR_PKT_MULTICAST &&
		    !(p->flags & BR_MCAST_FLOOD))
			continue;
217
218
219
220

		/* Do not flood to ports that enable proxy ARP */
		if (p->flags & BR_PROXYARP)
			continue;
221
222
223
		if ((p->flags & BR_PROXYARP_WIFI) &&
		    BR_INPUT_SKB_CB(skb)->proxyarp_replied)
			continue;
224

225
		prev = maybe_deliver(prev, p, skb, local_orig);
226
227
		if (IS_ERR(prev))
			goto out;
228
		if (prev == p)
229
			br_multicast_count(p->br, p, skb, igmp_type,
230
					   BR_MCAST_DIR_TX);
Linus Torvalds's avatar
Linus Torvalds committed
231
232
	}

233
234
235
	if (!prev)
		goto out;

236
	if (local_rcv)
237
		deliver_clone(prev, skb, local_orig);
238
	else
239
		__br_forward(prev, skb, local_orig);
240
	return;
Linus Torvalds's avatar
Linus Torvalds committed
241

242
out:
243
	if (!local_rcv)
244
		kfree_skb(skb);
Linus Torvalds's avatar
Linus Torvalds committed
245
246
}

247
248
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
/* called with rcu_read_lock */
249
250
251
void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
			struct sk_buff *skb,
			bool local_rcv, bool local_orig)
252
253
{
	struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
254
	u8 igmp_type = br_multicast_igmp_type(skb);
255
	struct net_bridge *br = netdev_priv(dev);
256
	struct net_bridge_port *prev = NULL;
257
258
259
	struct net_bridge_port_group *p;
	struct hlist_node *rp;

260
	rp = rcu_dereference(hlist_first_rcu(&br->router_list));
261
	p = mdst ? rcu_dereference(mdst->ports) : NULL;
262
	while (p || rp) {
263
264
		struct net_bridge_port *port, *lport, *rport;

265
266
267
268
		lport = p ? p->port : NULL;
		rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) :
			     NULL;

Felix Fietkau's avatar
Felix Fietkau committed
269
270
271
272
273
274
275
276
277
278
279
		if ((unsigned long)lport > (unsigned long)rport) {
			port = lport;

			if (port->flags & BR_MULTICAST_TO_UNICAST) {
				maybe_deliver_addr(lport, skb, p->eth_addr,
						   local_orig);
				goto delivered;
			}
		} else {
			port = rport;
		}
280

281
		prev = maybe_deliver(prev, port, skb, local_orig);
Felix Fietkau's avatar
Felix Fietkau committed
282
delivered:
283
284
		if (IS_ERR(prev))
			goto out;
285
		if (prev == port)
286
			br_multicast_count(port->br, port, skb, igmp_type,
287
					   BR_MCAST_DIR_TX);
288
289

		if ((unsigned long)lport >= (unsigned long)port)
290
			p = rcu_dereference(p->next);
291
		if ((unsigned long)rport >= (unsigned long)port)
292
			rp = rcu_dereference(hlist_next_rcu(rp));
293
294
295
296
297
	}

	if (!prev)
		goto out;

298
	if (local_rcv)
299
		deliver_clone(prev, skb, local_orig);
300
	else
301
		__br_forward(prev, skb, local_orig);
302
303
304
	return;

out:
305
	if (!local_rcv)
306
307
308
		kfree_skb(skb);
}
#endif