aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/vxlan.c
diff options
context:
space:
mode:
authorDavid Stevens <dlstevens@us.ibm.com>2014-03-24 10:39:58 -0400
committerDavid S. Miller <davem@davemloft.net>2014-03-24 15:35:10 -0400
commit4b29dba9c085a4fb79058fb1c45a2f6257ca3dfa (patch)
tree3ee4d0ba0ee4ec64d8c1103dc1b2dc1e5231d3e5 /drivers/net/vxlan.c
parent866b7cdf5f8e440c4921765b7f4e1fe102a7e089 (diff)
vxlan: fix nonfunctional neigh_reduce()
The VXLAN neigh_reduce() code is completely non-functional since check-in. Specific errors: 1) The original code drops all packets with a multicast destination address, even though neighbor solicitations are sent to the solicited-node address, a multicast address. The code after this check was never run. 2) The neighbor table lookup used the IPv6 header destination, which is the solicited node address, rather than the target address from the neighbor solicitation. So neighbor lookups would always fail if it got this far. Also for L3MISSes. 3) The code calls ndisc_send_na(), which does a send on the tunnel device. The context for neigh_reduce() is the transmit path, vxlan_xmit(), where the host or a bridge-attached neighbor is trying to transmit a neighbor solicitation. To respond to it, the tunnel endpoint needs to do a *receive* of the appropriate neighbor advertisement. Doing a send, would only try to send the advertisement, encapsulated, to the remote destinations in the fdb -- hosts that definitely did not do the corresponding solicitation. 4) The code uses the tunnel endpoint IPv6 forwarding flag to determine the isrouter flag in the advertisement. This has nothing to do with whether or not the target is a router, and generally won't be set since the tunnel endpoint is bridging, not routing, traffic. The patch below creates a proxy neighbor advertisement to respond to neighbor solicitions as intended, providing proper IPv6 support for neighbor reduction. Signed-off-by: David L Stevens <dlstevens@us.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/vxlan.c')
-rw-r--r--drivers/net/vxlan.c127
1 files changed, 113 insertions, 14 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index a7eb3f28db6c..1236812c7be6 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1342,15 +1342,103 @@ out:
1342} 1342}
1343 1343
1344#if IS_ENABLED(CONFIG_IPV6) 1344#if IS_ENABLED(CONFIG_IPV6)
1345
1346static struct sk_buff *vxlan_na_create(struct sk_buff *request,
1347 struct neighbour *n, bool isrouter)
1348{
1349 struct net_device *dev = request->dev;
1350 struct sk_buff *reply;
1351 struct nd_msg *ns, *na;
1352 struct ipv6hdr *pip6;
1353 u8 *daddr;
1354 int na_olen = 8; /* opt hdr + ETH_ALEN for target */
1355 int ns_olen;
1356 int i, len;
1357
1358 if (dev == NULL)
1359 return NULL;
1360
1361 len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
1362 sizeof(*na) + na_olen + dev->needed_tailroom;
1363 reply = alloc_skb(len, GFP_ATOMIC);
1364 if (reply == NULL)
1365 return NULL;
1366
1367 reply->protocol = htons(ETH_P_IPV6);
1368 reply->dev = dev;
1369 skb_reserve(reply, LL_RESERVED_SPACE(request->dev));
1370 skb_push(reply, sizeof(struct ethhdr));
1371 skb_set_mac_header(reply, 0);
1372
1373 ns = (struct nd_msg *)skb_transport_header(request);
1374
1375 daddr = eth_hdr(request)->h_source;
1376 ns_olen = request->len - skb_transport_offset(request) - sizeof(*ns);
1377 for (i = 0; i < ns_olen-1; i += (ns->opt[i+1]<<3)) {
1378 if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
1379 daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
1380 break;
1381 }
1382 }
1383
1384 /* Ethernet header */
1385 ether_addr_copy(eth_hdr(reply)->h_dest, daddr);
1386 ether_addr_copy(eth_hdr(reply)->h_source, n->ha);
1387 eth_hdr(reply)->h_proto = htons(ETH_P_IPV6);
1388 reply->protocol = htons(ETH_P_IPV6);
1389
1390 skb_pull(reply, sizeof(struct ethhdr));
1391 skb_set_network_header(reply, 0);
1392 skb_put(reply, sizeof(struct ipv6hdr));
1393
1394 /* IPv6 header */
1395
1396 pip6 = ipv6_hdr(reply);
1397 memset(pip6, 0, sizeof(struct ipv6hdr));
1398 pip6->version = 6;
1399 pip6->priority = ipv6_hdr(request)->priority;
1400 pip6->nexthdr = IPPROTO_ICMPV6;
1401 pip6->hop_limit = 255;
1402 pip6->daddr = ipv6_hdr(request)->saddr;
1403 pip6->saddr = *(struct in6_addr *)n->primary_key;
1404
1405 skb_pull(reply, sizeof(struct ipv6hdr));
1406 skb_set_transport_header(reply, 0);
1407
1408 na = (struct nd_msg *)skb_put(reply, sizeof(*na) + na_olen);
1409
1410 /* Neighbor Advertisement */
1411 memset(na, 0, sizeof(*na)+na_olen);
1412 na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
1413 na->icmph.icmp6_router = isrouter;
1414 na->icmph.icmp6_override = 1;
1415 na->icmph.icmp6_solicited = 1;
1416 na->target = ns->target;
1417 ether_addr_copy(&na->opt[2], n->ha);
1418 na->opt[0] = ND_OPT_TARGET_LL_ADDR;
1419 na->opt[1] = na_olen >> 3;
1420
1421 na->icmph.icmp6_cksum = csum_ipv6_magic(&pip6->saddr,
1422 &pip6->daddr, sizeof(*na)+na_olen, IPPROTO_ICMPV6,
1423 csum_partial(na, sizeof(*na)+na_olen, 0));
1424
1425 pip6->payload_len = htons(sizeof(*na)+na_olen);
1426
1427 skb_push(reply, sizeof(struct ipv6hdr));
1428
1429 reply->ip_summed = CHECKSUM_UNNECESSARY;
1430
1431 return reply;
1432}
1433
1345static int neigh_reduce(struct net_device *dev, struct sk_buff *skb) 1434static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
1346{ 1435{
1347 struct vxlan_dev *vxlan = netdev_priv(dev); 1436 struct vxlan_dev *vxlan = netdev_priv(dev);
1348 struct neighbour *n; 1437 struct nd_msg *msg;
1349 union vxlan_addr ipa;
1350 const struct ipv6hdr *iphdr; 1438 const struct ipv6hdr *iphdr;
1351 const struct in6_addr *saddr, *daddr; 1439 const struct in6_addr *saddr, *daddr;
1352 struct nd_msg *msg; 1440 struct neighbour *n;
1353 struct inet6_dev *in6_dev = NULL; 1441 struct inet6_dev *in6_dev;
1354 1442
1355 in6_dev = __in6_dev_get(dev); 1443 in6_dev = __in6_dev_get(dev);
1356 if (!in6_dev) 1444 if (!in6_dev)
@@ -1363,19 +1451,20 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
1363 saddr = &iphdr->saddr; 1451 saddr = &iphdr->saddr;
1364 daddr = &iphdr->daddr; 1452 daddr = &iphdr->daddr;
1365 1453
1366 if (ipv6_addr_loopback(daddr) ||
1367 ipv6_addr_is_multicast(daddr))
1368 goto out;
1369
1370 msg = (struct nd_msg *)skb_transport_header(skb); 1454 msg = (struct nd_msg *)skb_transport_header(skb);
1371 if (msg->icmph.icmp6_code != 0 || 1455 if (msg->icmph.icmp6_code != 0 ||
1372 msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION) 1456 msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION)
1373 goto out; 1457 goto out;
1374 1458
1375 n = neigh_lookup(ipv6_stub->nd_tbl, daddr, dev); 1459 if (ipv6_addr_loopback(daddr) ||
1460 ipv6_addr_is_multicast(&msg->target))
1461 goto out;
1462
1463 n = neigh_lookup(ipv6_stub->nd_tbl, &msg->target, dev);
1376 1464
1377 if (n) { 1465 if (n) {
1378 struct vxlan_fdb *f; 1466 struct vxlan_fdb *f;
1467 struct sk_buff *reply;
1379 1468
1380 if (!(n->nud_state & NUD_CONNECTED)) { 1469 if (!(n->nud_state & NUD_CONNECTED)) {
1381 neigh_release(n); 1470 neigh_release(n);
@@ -1389,13 +1478,23 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
1389 goto out; 1478 goto out;
1390 } 1479 }
1391 1480
1392 ipv6_stub->ndisc_send_na(dev, n, saddr, &msg->target, 1481 reply = vxlan_na_create(skb, n,
1393 !!in6_dev->cnf.forwarding, 1482 !!(f ? f->flags & NTF_ROUTER : 0));
1394 true, false, false); 1483
1395 neigh_release(n); 1484 neigh_release(n);
1485
1486 if (reply == NULL)
1487 goto out;
1488
1489 if (netif_rx_ni(reply) == NET_RX_DROP)
1490 dev->stats.rx_dropped++;
1491
1396 } else if (vxlan->flags & VXLAN_F_L3MISS) { 1492 } else if (vxlan->flags & VXLAN_F_L3MISS) {
1397 ipa.sin6.sin6_addr = *daddr; 1493 union vxlan_addr ipa = {
1398 ipa.sa.sa_family = AF_INET6; 1494 .sin6.sin6_addr = msg->target,
1495 .sa.sa_family = AF_INET6,
1496 };
1497
1399 vxlan_ip_miss(dev, &ipa); 1498 vxlan_ip_miss(dev, &ipa);
1400 } 1499 }
1401 1500