diff options
Diffstat (limited to 'drivers/net/vxlan.c')
-rw-r--r-- | drivers/net/vxlan.c | 127 |
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 | |||
1346 | static 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 | |||
1345 | static int neigh_reduce(struct net_device *dev, struct sk_buff *skb) | 1434 | static 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 | ||