1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Handle bridge arp/nd proxy/suppress |
4 | * |
5 | * Copyright (C) 2017 Cumulus Networks |
6 | * Copyright (c) 2017 Roopa Prabhu <roopa@cumulusnetworks.com> |
7 | * |
8 | * Authors: |
9 | * Roopa Prabhu <roopa@cumulusnetworks.com> |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/netdevice.h> |
14 | #include <linux/etherdevice.h> |
15 | #include <linux/neighbour.h> |
16 | #include <net/arp.h> |
17 | #include <linux/if_vlan.h> |
18 | #include <linux/inetdevice.h> |
19 | #include <net/addrconf.h> |
20 | #include <net/ipv6_stubs.h> |
21 | #if IS_ENABLED(CONFIG_IPV6) |
22 | #include <net/ip6_checksum.h> |
23 | #endif |
24 | |
25 | #include "br_private.h" |
26 | |
27 | void br_recalculate_neigh_suppress_enabled(struct net_bridge *br) |
28 | { |
29 | struct net_bridge_port *p; |
30 | bool neigh_suppress = false; |
31 | |
32 | list_for_each_entry(p, &br->port_list, list) { |
33 | if (p->flags & (BR_NEIGH_SUPPRESS | BR_NEIGH_VLAN_SUPPRESS)) { |
34 | neigh_suppress = true; |
35 | break; |
36 | } |
37 | } |
38 | |
39 | br_opt_toggle(br, opt: BROPT_NEIGH_SUPPRESS_ENABLED, on: neigh_suppress); |
40 | } |
41 | |
42 | #if IS_ENABLED(CONFIG_INET) |
43 | static void br_arp_send(struct net_bridge *br, struct net_bridge_port *p, |
44 | struct net_device *dev, __be32 dest_ip, __be32 src_ip, |
45 | const unsigned char *dest_hw, |
46 | const unsigned char *src_hw, |
47 | const unsigned char *target_hw, |
48 | __be16 vlan_proto, u16 vlan_tci) |
49 | { |
50 | struct net_bridge_vlan_group *vg; |
51 | struct sk_buff *skb; |
52 | u16 pvid; |
53 | |
54 | netdev_dbg(dev, "arp send dev %s dst %pI4 dst_hw %pM src %pI4 src_hw %pM\n" , |
55 | dev->name, &dest_ip, dest_hw, &src_ip, src_hw); |
56 | |
57 | if (!vlan_tci) { |
58 | arp_send(ARPOP_REPLY, ETH_P_ARP, dest_ip, dev, src_ip, |
59 | dest_hw, src_hw, th: target_hw); |
60 | return; |
61 | } |
62 | |
63 | skb = arp_create(ARPOP_REPLY, ETH_P_ARP, dest_ip, dev, src_ip, |
64 | dest_hw, src_hw, target_hw); |
65 | if (!skb) |
66 | return; |
67 | |
68 | if (p) |
69 | vg = nbp_vlan_group_rcu(p); |
70 | else |
71 | vg = br_vlan_group_rcu(br); |
72 | pvid = br_get_pvid(vg); |
73 | if (pvid == (vlan_tci & VLAN_VID_MASK)) |
74 | vlan_tci = 0; |
75 | |
76 | if (vlan_tci) |
77 | __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); |
78 | |
79 | if (p) { |
80 | arp_xmit(skb); |
81 | } else { |
82 | skb_reset_mac_header(skb); |
83 | __skb_pull(skb, len: skb_network_offset(skb)); |
84 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
85 | skb->pkt_type = PACKET_HOST; |
86 | |
87 | netif_rx(skb); |
88 | } |
89 | } |
90 | |
91 | static int br_chk_addr_ip(struct net_device *dev, |
92 | struct netdev_nested_priv *priv) |
93 | { |
94 | __be32 ip = *(__be32 *)priv->data; |
95 | struct in_device *in_dev; |
96 | __be32 addr = 0; |
97 | |
98 | in_dev = __in_dev_get_rcu(dev); |
99 | if (in_dev) |
100 | addr = inet_confirm_addr(net: dev_net(dev), in_dev, dst: 0, local: ip, |
101 | scope: RT_SCOPE_HOST); |
102 | |
103 | if (addr == ip) |
104 | return 1; |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | static bool br_is_local_ip(struct net_device *dev, __be32 ip) |
110 | { |
111 | struct netdev_nested_priv priv = { |
112 | .data = (void *)&ip, |
113 | }; |
114 | |
115 | if (br_chk_addr_ip(dev, priv: &priv)) |
116 | return true; |
117 | |
118 | /* check if ip is configured on upper dev */ |
119 | if (netdev_walk_all_upper_dev_rcu(dev, fn: br_chk_addr_ip, priv: &priv)) |
120 | return true; |
121 | |
122 | return false; |
123 | } |
124 | |
125 | void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, |
126 | u16 vid, struct net_bridge_port *p) |
127 | { |
128 | struct net_device *dev = br->dev; |
129 | struct net_device *vlandev = dev; |
130 | struct neighbour *n; |
131 | struct arphdr *parp; |
132 | u8 *arpptr, *sha; |
133 | __be32 sip, tip; |
134 | |
135 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 0; |
136 | |
137 | if ((dev->flags & IFF_NOARP) || |
138 | !pskb_may_pull(skb, len: arp_hdr_len(dev))) |
139 | return; |
140 | |
141 | parp = arp_hdr(skb); |
142 | |
143 | if (parp->ar_pro != htons(ETH_P_IP) || |
144 | parp->ar_hln != dev->addr_len || |
145 | parp->ar_pln != 4) |
146 | return; |
147 | |
148 | arpptr = (u8 *)parp + sizeof(struct arphdr); |
149 | sha = arpptr; |
150 | arpptr += dev->addr_len; /* sha */ |
151 | memcpy(&sip, arpptr, sizeof(sip)); |
152 | arpptr += sizeof(sip); |
153 | arpptr += dev->addr_len; /* tha */ |
154 | memcpy(&tip, arpptr, sizeof(tip)); |
155 | |
156 | if (ipv4_is_loopback(addr: tip) || |
157 | ipv4_is_multicast(addr: tip)) |
158 | return; |
159 | |
160 | if (br_opt_get(br, opt: BROPT_NEIGH_SUPPRESS_ENABLED)) { |
161 | if (br_is_neigh_suppress_enabled(p, vid)) |
162 | return; |
163 | if (parp->ar_op != htons(ARPOP_RREQUEST) && |
164 | parp->ar_op != htons(ARPOP_RREPLY) && |
165 | (ipv4_is_zeronet(addr: sip) || sip == tip)) { |
166 | /* prevent flooding to neigh suppress ports */ |
167 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; |
168 | return; |
169 | } |
170 | } |
171 | |
172 | if (parp->ar_op != htons(ARPOP_REQUEST)) |
173 | return; |
174 | |
175 | if (vid != 0) { |
176 | vlandev = __vlan_find_dev_deep_rcu(real_dev: br->dev, vlan_proto: skb->vlan_proto, |
177 | vlan_id: vid); |
178 | if (!vlandev) |
179 | return; |
180 | } |
181 | |
182 | if (br_opt_get(br, opt: BROPT_NEIGH_SUPPRESS_ENABLED) && |
183 | br_is_local_ip(dev: vlandev, ip: tip)) { |
184 | /* its our local ip, so don't proxy reply |
185 | * and don't forward to neigh suppress ports |
186 | */ |
187 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; |
188 | return; |
189 | } |
190 | |
191 | n = neigh_lookup(tbl: &arp_tbl, pkey: &tip, dev: vlandev); |
192 | if (n) { |
193 | struct net_bridge_fdb_entry *f; |
194 | |
195 | if (!(READ_ONCE(n->nud_state) & NUD_VALID)) { |
196 | neigh_release(neigh: n); |
197 | return; |
198 | } |
199 | |
200 | f = br_fdb_find_rcu(br, addr: n->ha, vid); |
201 | if (f) { |
202 | bool replied = false; |
203 | |
204 | if ((p && (p->flags & BR_PROXYARP)) || |
205 | (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)) || |
206 | br_is_neigh_suppress_enabled(p: f->dst, vid)) { |
207 | if (!vid) |
208 | br_arp_send(br, p, dev: skb->dev, dest_ip: sip, src_ip: tip, |
209 | dest_hw: sha, src_hw: n->ha, target_hw: sha, vlan_proto: 0, vlan_tci: 0); |
210 | else |
211 | br_arp_send(br, p, dev: skb->dev, dest_ip: sip, src_ip: tip, |
212 | dest_hw: sha, src_hw: n->ha, target_hw: sha, |
213 | vlan_proto: skb->vlan_proto, |
214 | skb_vlan_tag_get(skb)); |
215 | replied = true; |
216 | } |
217 | |
218 | /* If we have replied or as long as we know the |
219 | * mac, indicate to arp replied |
220 | */ |
221 | if (replied || |
222 | br_opt_get(br, opt: BROPT_NEIGH_SUPPRESS_ENABLED)) |
223 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; |
224 | } |
225 | |
226 | neigh_release(neigh: n); |
227 | } |
228 | } |
229 | #endif |
230 | |
231 | #if IS_ENABLED(CONFIG_IPV6) |
232 | struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *msg) |
233 | { |
234 | struct nd_msg *m; |
235 | |
236 | m = skb_header_pointer(skb, offset: skb_network_offset(skb) + |
237 | sizeof(struct ipv6hdr), len: sizeof(*msg), buffer: msg); |
238 | if (!m) |
239 | return NULL; |
240 | |
241 | if (m->icmph.icmp6_code != 0 || |
242 | (m->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION && |
243 | m->icmph.icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT)) |
244 | return NULL; |
245 | |
246 | return m; |
247 | } |
248 | |
249 | static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, |
250 | struct sk_buff *request, struct neighbour *n, |
251 | __be16 vlan_proto, u16 vlan_tci, struct nd_msg *ns) |
252 | { |
253 | struct net_device *dev = request->dev; |
254 | struct net_bridge_vlan_group *vg; |
255 | struct sk_buff *reply; |
256 | struct nd_msg *na; |
257 | struct ipv6hdr *pip6; |
258 | int na_olen = 8; /* opt hdr + ETH_ALEN for target */ |
259 | int ns_olen; |
260 | int i, len; |
261 | u8 *daddr; |
262 | u16 pvid; |
263 | |
264 | if (!dev) |
265 | return; |
266 | |
267 | len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) + |
268 | sizeof(*na) + na_olen + dev->needed_tailroom; |
269 | |
270 | reply = alloc_skb(size: len, GFP_ATOMIC); |
271 | if (!reply) |
272 | return; |
273 | |
274 | reply->protocol = htons(ETH_P_IPV6); |
275 | reply->dev = dev; |
276 | skb_reserve(skb: reply, LL_RESERVED_SPACE(dev)); |
277 | skb_push(skb: reply, len: sizeof(struct ethhdr)); |
278 | skb_set_mac_header(skb: reply, offset: 0); |
279 | |
280 | daddr = eth_hdr(skb: request)->h_source; |
281 | |
282 | /* Do we need option processing ? */ |
283 | ns_olen = request->len - (skb_network_offset(skb: request) + |
284 | sizeof(struct ipv6hdr)) - sizeof(*ns); |
285 | for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) { |
286 | if (!ns->opt[i + 1]) { |
287 | kfree_skb(skb: reply); |
288 | return; |
289 | } |
290 | if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) { |
291 | daddr = ns->opt + i + sizeof(struct nd_opt_hdr); |
292 | break; |
293 | } |
294 | } |
295 | |
296 | /* Ethernet header */ |
297 | ether_addr_copy(dst: eth_hdr(skb: reply)->h_dest, src: daddr); |
298 | ether_addr_copy(dst: eth_hdr(skb: reply)->h_source, src: n->ha); |
299 | eth_hdr(skb: reply)->h_proto = htons(ETH_P_IPV6); |
300 | reply->protocol = htons(ETH_P_IPV6); |
301 | |
302 | skb_pull(skb: reply, len: sizeof(struct ethhdr)); |
303 | skb_set_network_header(skb: reply, offset: 0); |
304 | skb_put(skb: reply, len: sizeof(struct ipv6hdr)); |
305 | |
306 | /* IPv6 header */ |
307 | pip6 = ipv6_hdr(skb: reply); |
308 | memset(pip6, 0, sizeof(struct ipv6hdr)); |
309 | pip6->version = 6; |
310 | pip6->priority = ipv6_hdr(skb: request)->priority; |
311 | pip6->nexthdr = IPPROTO_ICMPV6; |
312 | pip6->hop_limit = 255; |
313 | pip6->daddr = ipv6_hdr(skb: request)->saddr; |
314 | pip6->saddr = *(struct in6_addr *)n->primary_key; |
315 | |
316 | skb_pull(skb: reply, len: sizeof(struct ipv6hdr)); |
317 | skb_set_transport_header(skb: reply, offset: 0); |
318 | |
319 | na = (struct nd_msg *)skb_put(skb: reply, len: sizeof(*na) + na_olen); |
320 | |
321 | /* Neighbor Advertisement */ |
322 | memset(na, 0, sizeof(*na) + na_olen); |
323 | na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; |
324 | na->icmph.icmp6_router = (n->flags & NTF_ROUTER) ? 1 : 0; |
325 | na->icmph.icmp6_override = 1; |
326 | na->icmph.icmp6_solicited = 1; |
327 | na->target = ns->target; |
328 | ether_addr_copy(dst: &na->opt[2], src: n->ha); |
329 | na->opt[0] = ND_OPT_TARGET_LL_ADDR; |
330 | na->opt[1] = na_olen >> 3; |
331 | |
332 | na->icmph.icmp6_cksum = csum_ipv6_magic(saddr: &pip6->saddr, |
333 | daddr: &pip6->daddr, |
334 | len: sizeof(*na) + na_olen, |
335 | IPPROTO_ICMPV6, |
336 | sum: csum_partial(buff: na, len: sizeof(*na) + na_olen, sum: 0)); |
337 | |
338 | pip6->payload_len = htons(sizeof(*na) + na_olen); |
339 | |
340 | skb_push(skb: reply, len: sizeof(struct ipv6hdr)); |
341 | skb_push(skb: reply, len: sizeof(struct ethhdr)); |
342 | |
343 | reply->ip_summed = CHECKSUM_UNNECESSARY; |
344 | |
345 | if (p) |
346 | vg = nbp_vlan_group_rcu(p); |
347 | else |
348 | vg = br_vlan_group_rcu(br); |
349 | pvid = br_get_pvid(vg); |
350 | if (pvid == (vlan_tci & VLAN_VID_MASK)) |
351 | vlan_tci = 0; |
352 | |
353 | if (vlan_tci) |
354 | __vlan_hwaccel_put_tag(skb: reply, vlan_proto, vlan_tci); |
355 | |
356 | netdev_dbg(dev, "nd send dev %s dst %pI6 dst_hw %pM src %pI6 src_hw %pM\n" , |
357 | dev->name, &pip6->daddr, daddr, &pip6->saddr, n->ha); |
358 | |
359 | if (p) { |
360 | dev_queue_xmit(skb: reply); |
361 | } else { |
362 | skb_reset_mac_header(skb: reply); |
363 | __skb_pull(skb: reply, len: skb_network_offset(skb: reply)); |
364 | reply->ip_summed = CHECKSUM_UNNECESSARY; |
365 | reply->pkt_type = PACKET_HOST; |
366 | |
367 | netif_rx(skb: reply); |
368 | } |
369 | } |
370 | |
371 | static int br_chk_addr_ip6(struct net_device *dev, |
372 | struct netdev_nested_priv *priv) |
373 | { |
374 | struct in6_addr *addr = (struct in6_addr *)priv->data; |
375 | |
376 | if (ipv6_chk_addr(net: dev_net(dev), addr, dev, strict: 0)) |
377 | return 1; |
378 | |
379 | return 0; |
380 | } |
381 | |
382 | static bool br_is_local_ip6(struct net_device *dev, struct in6_addr *addr) |
383 | |
384 | { |
385 | struct netdev_nested_priv priv = { |
386 | .data = (void *)addr, |
387 | }; |
388 | |
389 | if (br_chk_addr_ip6(dev, priv: &priv)) |
390 | return true; |
391 | |
392 | /* check if ip is configured on upper dev */ |
393 | if (netdev_walk_all_upper_dev_rcu(dev, fn: br_chk_addr_ip6, priv: &priv)) |
394 | return true; |
395 | |
396 | return false; |
397 | } |
398 | |
399 | void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, |
400 | u16 vid, struct net_bridge_port *p, struct nd_msg *msg) |
401 | { |
402 | struct net_device *dev = br->dev; |
403 | struct net_device *vlandev = NULL; |
404 | struct in6_addr *saddr, *daddr; |
405 | struct ipv6hdr *iphdr; |
406 | struct neighbour *n; |
407 | |
408 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 0; |
409 | |
410 | if (br_is_neigh_suppress_enabled(p, vid)) |
411 | return; |
412 | |
413 | if (msg->icmph.icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT && |
414 | !msg->icmph.icmp6_solicited) { |
415 | /* prevent flooding to neigh suppress ports */ |
416 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; |
417 | return; |
418 | } |
419 | |
420 | if (msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION) |
421 | return; |
422 | |
423 | iphdr = ipv6_hdr(skb); |
424 | saddr = &iphdr->saddr; |
425 | daddr = &iphdr->daddr; |
426 | |
427 | if (ipv6_addr_any(a: saddr) || !ipv6_addr_cmp(a1: saddr, a2: daddr)) { |
428 | /* prevent flooding to neigh suppress ports */ |
429 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; |
430 | return; |
431 | } |
432 | |
433 | if (vid != 0) { |
434 | /* build neigh table lookup on the vlan device */ |
435 | vlandev = __vlan_find_dev_deep_rcu(real_dev: br->dev, vlan_proto: skb->vlan_proto, |
436 | vlan_id: vid); |
437 | if (!vlandev) |
438 | return; |
439 | } else { |
440 | vlandev = dev; |
441 | } |
442 | |
443 | if (br_is_local_ip6(dev: vlandev, addr: &msg->target)) { |
444 | /* its our own ip, so don't proxy reply |
445 | * and don't forward to arp suppress ports |
446 | */ |
447 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; |
448 | return; |
449 | } |
450 | |
451 | n = neigh_lookup(tbl: ipv6_stub->nd_tbl, pkey: &msg->target, dev: vlandev); |
452 | if (n) { |
453 | struct net_bridge_fdb_entry *f; |
454 | |
455 | if (!(READ_ONCE(n->nud_state) & NUD_VALID)) { |
456 | neigh_release(neigh: n); |
457 | return; |
458 | } |
459 | |
460 | f = br_fdb_find_rcu(br, addr: n->ha, vid); |
461 | if (f) { |
462 | bool replied = false; |
463 | |
464 | if (br_is_neigh_suppress_enabled(p: f->dst, vid)) { |
465 | if (vid != 0) |
466 | br_nd_send(br, p, request: skb, n, |
467 | vlan_proto: skb->vlan_proto, |
468 | skb_vlan_tag_get(skb), ns: msg); |
469 | else |
470 | br_nd_send(br, p, request: skb, n, vlan_proto: 0, vlan_tci: 0, ns: msg); |
471 | replied = true; |
472 | } |
473 | |
474 | /* If we have replied or as long as we know the |
475 | * mac, indicate to NEIGH_SUPPRESS ports that we |
476 | * have replied |
477 | */ |
478 | if (replied || |
479 | br_opt_get(br, opt: BROPT_NEIGH_SUPPRESS_ENABLED)) |
480 | BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; |
481 | } |
482 | neigh_release(neigh: n); |
483 | } |
484 | } |
485 | #endif |
486 | |
487 | bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid) |
488 | { |
489 | if (!p) |
490 | return false; |
491 | |
492 | if (!vid) |
493 | return !!(p->flags & BR_NEIGH_SUPPRESS); |
494 | |
495 | if (p->flags & BR_NEIGH_VLAN_SUPPRESS) { |
496 | struct net_bridge_vlan_group *vg = nbp_vlan_group_rcu(p); |
497 | struct net_bridge_vlan *v; |
498 | |
499 | v = br_vlan_find(vg, vid); |
500 | if (!v) |
501 | return false; |
502 | return !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED); |
503 | } else { |
504 | return !!(p->flags & BR_NEIGH_SUPPRESS); |
505 | } |
506 | } |
507 | |