Skip to content

Commit 4b29dba

Browse files
David Stevensdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 866b7cd commit 4b29dba

File tree

1 file changed

+113
-14
lines changed

1 file changed

+113
-14
lines changed

drivers/net/vxlan.c

Lines changed: 113 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,15 +1342,103 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
13421342
}
13431343

13441344
#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+
13451434
static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
13461435
{
13471436
struct vxlan_dev *vxlan = netdev_priv(dev);
1348-
struct neighbour *n;
1349-
union vxlan_addr ipa;
1437+
struct nd_msg *msg;
13501438
const struct ipv6hdr *iphdr;
13511439
const struct in6_addr *saddr, *daddr;
1352-
struct nd_msg *msg;
1353-
struct inet6_dev *in6_dev = NULL;
1440+
struct neighbour *n;
1441+
struct inet6_dev *in6_dev;
13541442

13551443
in6_dev = __in6_dev_get(dev);
13561444
if (!in6_dev)
@@ -1363,19 +1451,20 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
13631451
saddr = &iphdr->saddr;
13641452
daddr = &iphdr->daddr;
13651453

1366-
if (ipv6_addr_loopback(daddr) ||
1367-
ipv6_addr_is_multicast(daddr))
1368-
goto out;
1369-
13701454
msg = (struct nd_msg *)skb_transport_header(skb);
13711455
if (msg->icmph.icmp6_code != 0 ||
13721456
msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION)
13731457
goto out;
13741458

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);
13761464

13771465
if (n) {
13781466
struct vxlan_fdb *f;
1467+
struct sk_buff *reply;
13791468

13801469
if (!(n->nud_state & NUD_CONNECTED)) {
13811470
neigh_release(n);
@@ -1389,13 +1478,23 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
13891478
goto out;
13901479
}
13911480

1392-
ipv6_stub->ndisc_send_na(dev, n, saddr, &msg->target,
1393-
!!in6_dev->cnf.forwarding,
1394-
true, false, false);
1481+
reply = vxlan_na_create(skb, n,
1482+
!!(f ? f->flags & NTF_ROUTER : 0));
1483+
13951484
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+
13961492
} else if (vxlan->flags & VXLAN_F_L3MISS) {
1397-
ipa.sin6.sin6_addr = *daddr;
1398-
ipa.sa.sa_family = AF_INET6;
1493+
union vxlan_addr ipa = {
1494+
.sin6.sin6_addr = msg->target,
1495+
.sa.sa_family = AF_INET6,
1496+
};
1497+
13991498
vxlan_ip_miss(dev, &ipa);
14001499
}
14011500

0 commit comments

Comments
 (0)