diff options
Diffstat (limited to 'drivers/net/vxlan.c')
-rw-r--r-- | drivers/net/vxlan.c | 133 |
1 files changed, 116 insertions, 17 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 026a313c2d2d..1236812c7be6 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c | |||
@@ -469,7 +469,6 @@ static inline struct hlist_head *vxlan_fdb_head(struct vxlan_dev *vxlan, | |||
469 | /* Look up Ethernet address in forwarding table */ | 469 | /* Look up Ethernet address in forwarding table */ |
470 | static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan, | 470 | static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan, |
471 | const u8 *mac) | 471 | const u8 *mac) |
472 | |||
473 | { | 472 | { |
474 | struct hlist_head *head = vxlan_fdb_head(vxlan, mac); | 473 | struct hlist_head *head = vxlan_fdb_head(vxlan, mac); |
475 | struct vxlan_fdb *f; | 474 | struct vxlan_fdb *f; |
@@ -596,10 +595,8 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff | |||
596 | NAPI_GRO_CB(p)->same_flow = 0; | 595 | NAPI_GRO_CB(p)->same_flow = 0; |
597 | continue; | 596 | continue; |
598 | } | 597 | } |
599 | goto found; | ||
600 | } | 598 | } |
601 | 599 | ||
602 | found: | ||
603 | type = eh->h_proto; | 600 | type = eh->h_proto; |
604 | 601 | ||
605 | rcu_read_lock(); | 602 | rcu_read_lock(); |
@@ -1321,6 +1318,9 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb) | |||
1321 | 1318 | ||
1322 | neigh_release(n); | 1319 | neigh_release(n); |
1323 | 1320 | ||
1321 | if (reply == NULL) | ||
1322 | goto out; | ||
1323 | |||
1324 | skb_reset_mac_header(reply); | 1324 | skb_reset_mac_header(reply); |
1325 | __skb_pull(reply, skb_network_offset(reply)); | 1325 | __skb_pull(reply, skb_network_offset(reply)); |
1326 | reply->ip_summed = CHECKSUM_UNNECESSARY; | 1326 | reply->ip_summed = CHECKSUM_UNNECESSARY; |
@@ -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 | ||