Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bridge: suppress nd pkts on BR_NEIGH_SUPPRESS ports

This patch avoids flooding and proxies ndisc packets
for BR_NEIGH_SUPPRESS ports.

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Roopa Prabhu and committed by
David S. Miller
ed842fae 057658cb

+274
+249
net/bridge/br_arp_nd_proxy.c
··· 21 21 #include <linux/if_vlan.h> 22 22 #include <linux/inetdevice.h> 23 23 #include <net/addrconf.h> 24 + #if IS_ENABLED(CONFIG_IPV6) 25 + #include <net/ip6_checksum.h> 26 + #endif 24 27 25 28 #include "br_private.h" 26 29 ··· 217 214 BR_INPUT_SKB_CB(skb)->proxyarp_replied = true; 218 215 } 219 216 217 + neigh_release(n); 218 + } 219 + } 220 + #endif 221 + 222 + #if IS_ENABLED(CONFIG_IPV6) 223 + struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *msg) 224 + { 225 + struct nd_msg *m; 226 + 227 + m = skb_header_pointer(skb, skb_network_offset(skb) + 228 + sizeof(struct ipv6hdr), sizeof(*msg), msg); 229 + if (!m) 230 + return NULL; 231 + 232 + if (m->icmph.icmp6_code != 0 || 233 + (m->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION && 234 + m->icmph.icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT)) 235 + return NULL; 236 + 237 + return m; 238 + } 239 + 240 + static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, 241 + struct sk_buff *request, struct neighbour *n, 242 + __be16 vlan_proto, u16 vlan_tci, struct nd_msg *ns) 243 + { 244 + struct net_device *dev = request->dev; 245 + struct net_bridge_vlan_group *vg; 246 + struct sk_buff *reply; 247 + struct nd_msg *na; 248 + struct ipv6hdr *pip6; 249 + int na_olen = 8; /* opt hdr + ETH_ALEN for target */ 250 + int ns_olen; 251 + int i, len; 252 + u8 *daddr; 253 + u16 pvid; 254 + 255 + if (!dev) 256 + return; 257 + 258 + len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) + 259 + sizeof(*na) + na_olen + dev->needed_tailroom; 260 + 261 + reply = alloc_skb(len, GFP_ATOMIC); 262 + if (!reply) 263 + return; 264 + 265 + reply->protocol = htons(ETH_P_IPV6); 266 + reply->dev = dev; 267 + skb_reserve(reply, LL_RESERVED_SPACE(dev)); 268 + skb_push(reply, sizeof(struct ethhdr)); 269 + skb_set_mac_header(reply, 0); 270 + 271 + daddr = eth_hdr(request)->h_source; 272 + 273 + /* Do we need option processing ? */ 274 + ns_olen = request->len - (skb_network_offset(request) + 275 + sizeof(struct ipv6hdr)) - sizeof(*ns); 276 + for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) { 277 + if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) { 278 + daddr = ns->opt + i + sizeof(struct nd_opt_hdr); 279 + break; 280 + } 281 + } 282 + 283 + /* Ethernet header */ 284 + ether_addr_copy(eth_hdr(reply)->h_dest, daddr); 285 + ether_addr_copy(eth_hdr(reply)->h_source, n->ha); 286 + eth_hdr(reply)->h_proto = htons(ETH_P_IPV6); 287 + reply->protocol = htons(ETH_P_IPV6); 288 + 289 + skb_pull(reply, sizeof(struct ethhdr)); 290 + skb_set_network_header(reply, 0); 291 + skb_put(reply, sizeof(struct ipv6hdr)); 292 + 293 + /* IPv6 header */ 294 + pip6 = ipv6_hdr(reply); 295 + memset(pip6, 0, sizeof(struct ipv6hdr)); 296 + pip6->version = 6; 297 + pip6->priority = ipv6_hdr(request)->priority; 298 + pip6->nexthdr = IPPROTO_ICMPV6; 299 + pip6->hop_limit = 255; 300 + pip6->daddr = ipv6_hdr(request)->saddr; 301 + pip6->saddr = *(struct in6_addr *)n->primary_key; 302 + 303 + skb_pull(reply, sizeof(struct ipv6hdr)); 304 + skb_set_transport_header(reply, 0); 305 + 306 + na = (struct nd_msg *)skb_put(reply, sizeof(*na) + na_olen); 307 + 308 + /* Neighbor Advertisement */ 309 + memset(na, 0, sizeof(*na) + na_olen); 310 + na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; 311 + na->icmph.icmp6_router = 0; /* XXX: should be 1 ? */ 312 + na->icmph.icmp6_override = 1; 313 + na->icmph.icmp6_solicited = 1; 314 + na->target = ns->target; 315 + ether_addr_copy(&na->opt[2], n->ha); 316 + na->opt[0] = ND_OPT_TARGET_LL_ADDR; 317 + na->opt[1] = na_olen >> 3; 318 + 319 + na->icmph.icmp6_cksum = csum_ipv6_magic(&pip6->saddr, 320 + &pip6->daddr, 321 + sizeof(*na) + na_olen, 322 + IPPROTO_ICMPV6, 323 + csum_partial(na, sizeof(*na) + na_olen, 0)); 324 + 325 + pip6->payload_len = htons(sizeof(*na) + na_olen); 326 + 327 + skb_push(reply, sizeof(struct ipv6hdr)); 328 + skb_push(reply, sizeof(struct ethhdr)); 329 + 330 + reply->ip_summed = CHECKSUM_UNNECESSARY; 331 + 332 + if (p) 333 + vg = nbp_vlan_group_rcu(p); 334 + else 335 + vg = br_vlan_group_rcu(br); 336 + pvid = br_get_pvid(vg); 337 + if (pvid == (vlan_tci & VLAN_VID_MASK)) 338 + vlan_tci = 0; 339 + 340 + if (vlan_tci) 341 + __vlan_hwaccel_put_tag(reply, vlan_proto, vlan_tci); 342 + 343 + netdev_dbg(dev, "nd send dev %s dst %pI6 dst_hw %pM src %pI6 src_hw %pM\n", 344 + dev->name, &pip6->daddr, daddr, &pip6->saddr, n->ha); 345 + 346 + if (p) { 347 + dev_queue_xmit(reply); 348 + } else { 349 + skb_reset_mac_header(reply); 350 + __skb_pull(reply, skb_network_offset(reply)); 351 + reply->ip_summed = CHECKSUM_UNNECESSARY; 352 + reply->pkt_type = PACKET_HOST; 353 + 354 + netif_rx_ni(reply); 355 + } 356 + } 357 + 358 + static int br_chk_addr_ip6(struct net_device *dev, void *data) 359 + { 360 + struct in6_addr *addr = (struct in6_addr *)data; 361 + 362 + if (ipv6_chk_addr(dev_net(dev), addr, dev, 0)) 363 + return 1; 364 + 365 + return 0; 366 + } 367 + 368 + static bool br_is_local_ip6(struct net_device *dev, struct in6_addr *addr) 369 + 370 + { 371 + if (br_chk_addr_ip6(dev, addr)) 372 + return true; 373 + 374 + /* check if ip is configured on upper dev */ 375 + if (netdev_walk_all_upper_dev_rcu(dev, br_chk_addr_ip6, addr)) 376 + return true; 377 + 378 + return false; 379 + } 380 + 381 + void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, 382 + u16 vid, struct net_bridge_port *p, struct nd_msg *msg) 383 + { 384 + struct net_device *dev = br->dev; 385 + struct net_device *vlandev = NULL; 386 + struct in6_addr *saddr, *daddr; 387 + struct ipv6hdr *iphdr; 388 + struct neighbour *n; 389 + 390 + BR_INPUT_SKB_CB(skb)->proxyarp_replied = false; 391 + 392 + if (p && (p->flags & BR_NEIGH_SUPPRESS)) 393 + return; 394 + 395 + if (msg->icmph.icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT && 396 + !msg->icmph.icmp6_solicited) { 397 + /* prevent flooding to neigh suppress ports */ 398 + BR_INPUT_SKB_CB(skb)->proxyarp_replied = true; 399 + return; 400 + } 401 + 402 + if (msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION) 403 + return; 404 + 405 + iphdr = ipv6_hdr(skb); 406 + saddr = &iphdr->saddr; 407 + daddr = &iphdr->daddr; 408 + 409 + if (ipv6_addr_any(saddr) || !ipv6_addr_cmp(saddr, daddr)) { 410 + /* prevent flooding to neigh suppress ports */ 411 + BR_INPUT_SKB_CB(skb)->proxyarp_replied = true; 412 + return; 413 + } 414 + 415 + if (vid != 0) { 416 + /* build neigh table lookup on the vlan device */ 417 + vlandev = __vlan_find_dev_deep_rcu(br->dev, skb->vlan_proto, 418 + vid); 419 + if (!vlandev) 420 + return; 421 + } else { 422 + vlandev = dev; 423 + } 424 + 425 + if (br_is_local_ip6(vlandev, &msg->target)) { 426 + /* its our own ip, so don't proxy reply 427 + * and don't forward to arp suppress ports 428 + */ 429 + BR_INPUT_SKB_CB(skb)->proxyarp_replied = true; 430 + return; 431 + } 432 + 433 + n = neigh_lookup(ipv6_stub->nd_tbl, &msg->target, vlandev); 434 + if (n) { 435 + struct net_bridge_fdb_entry *f; 436 + 437 + if (!(n->nud_state & NUD_VALID)) { 438 + neigh_release(n); 439 + return; 440 + } 441 + 442 + f = br_fdb_find_rcu(br, n->ha, vid); 443 + if (f) { 444 + bool replied = false; 445 + 446 + if (f->dst && (f->dst->flags & BR_NEIGH_SUPPRESS)) { 447 + if (vid != 0) 448 + br_nd_send(br, p, skb, n, 449 + skb->vlan_proto, 450 + skb_vlan_tag_get(skb), msg); 451 + else 452 + br_nd_send(br, p, skb, n, 0, 0, msg); 453 + replied = true; 454 + } 455 + 456 + /* If we have replied or as long as we know the 457 + * mac, indicate to NEIGH_SUPPRESS ports that we 458 + * have replied 459 + */ 460 + if (replied || br->neigh_suppress_enabled) 461 + BR_INPUT_SKB_CB(skb)->proxyarp_replied = true; 462 + } 220 463 neigh_release(n); 221 464 } 222 465 }
+11
net/bridge/br_device.c
··· 69 69 eth->h_proto == htons(ETH_P_RARP)) && 70 70 br->neigh_suppress_enabled) { 71 71 br_do_proxy_suppress_arp(skb, br, vid, NULL); 72 + } else if (IS_ENABLED(CONFIG_IPV6) && 73 + skb->protocol == htons(ETH_P_IPV6) && 74 + br->neigh_suppress_enabled && 75 + pskb_may_pull(skb, sizeof(struct ipv6hdr) + 76 + sizeof(struct nd_msg)) && 77 + ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) { 78 + struct nd_msg *msg, _msg; 79 + 80 + msg = br_is_nd_neigh_msg(skb, &_msg); 81 + if (msg) 82 + br_do_suppress_nd(skb, br, vid, NULL, msg); 72 83 } 73 84 74 85 dest = eth_hdr(skb)->h_dest;
+11
net/bridge/br_input.c
··· 119 119 (skb->protocol == htons(ETH_P_ARP) || 120 120 skb->protocol == htons(ETH_P_RARP))) { 121 121 br_do_proxy_suppress_arp(skb, br, vid, p); 122 + } else if (IS_ENABLED(CONFIG_IPV6) && 123 + skb->protocol == htons(ETH_P_IPV6) && 124 + br->neigh_suppress_enabled && 125 + pskb_may_pull(skb, sizeof(struct ipv6hdr) + 126 + sizeof(struct nd_msg)) && 127 + ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) { 128 + struct nd_msg *msg, _msg; 129 + 130 + msg = br_is_nd_neigh_msg(skb, &_msg); 131 + if (msg) 132 + br_do_suppress_nd(skb, br, vid, p, msg); 122 133 } 123 134 124 135 switch (pkt_type) {
+3
net/bridge/br_private.h
··· 1144 1144 void br_recalculate_neigh_suppress_enabled(struct net_bridge *br); 1145 1145 void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, 1146 1146 u16 vid, struct net_bridge_port *p); 1147 + void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, 1148 + u16 vid, struct net_bridge_port *p, struct nd_msg *msg); 1149 + struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m); 1147 1150 #endif