Skip to content

Commit 5c9f7c1

Browse files
dsaherndavem330
authored andcommitted
ipv4: Add helpers for neigh lookup for nexthop
A common theme in the output path is looking up a neigh entry for a nexthop, either the gateway in an rtable or a fallback to the daddr in the skb: nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr); neigh = __ipv4_neigh_lookup_noref(dev, nexthop); if (unlikely(!neigh)) neigh = __neigh_create(&arp_tbl, &nexthop, dev, false); To allow the nexthop to be an IPv6 address we need to consider the family of the nexthop and then call __ipv{4,6}_neigh_lookup_noref based on it. To make this simpler, add a ip_neigh_gw4 helper similar to ip_neigh_gw6 added in an earlier patch which handles: neigh = __ipv4_neigh_lookup_noref(dev, nexthop); if (unlikely(!neigh)) neigh = __neigh_create(&arp_tbl, &nexthop, dev, false); And then add a second one, ip_neigh_for_gw, that calls either ip_neigh_gw4 or ip_neigh_gw6 based on the address family of the gateway. Update the output paths in the VRF driver and core v4 code to use ip_neigh_for_gw simplifying the family based lookup and making both ready for a v6 nexthop. ipv4_neigh_lookup has a different need - the potential to resolve a passed in address in addition to any gateway in the rtable or skb. Since this is a one-off, add ip_neigh_gw4 and ip_neigh_gw6 diectly. The difference between __neigh_create used by the helpers and neigh_create called by ipv4_neigh_lookup is taking a refcount, so add rcu_read_lock_bh and bump the refcnt on the neigh entry. Signed-off-by: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0353f28 commit 5c9f7c1

File tree

4 files changed

+59
-23
lines changed

4 files changed

+59
-23
lines changed

drivers/net/vrf.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
549549
struct net_device *dev = dst->dev;
550550
unsigned int hh_len = LL_RESERVED_SPACE(dev);
551551
struct neighbour *neigh;
552-
u32 nexthop;
552+
bool is_v6gw = false;
553553
int ret = -EINVAL;
554554

555555
nf_reset(skb);
@@ -572,13 +572,11 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
572572

573573
rcu_read_lock_bh();
574574

575-
nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr);
576-
neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
577-
if (unlikely(!neigh))
578-
neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
575+
neigh = ip_neigh_for_gw(rt, skb, &is_v6gw);
579576
if (!IS_ERR(neigh)) {
580577
sock_confirm_neigh(skb, neigh);
581-
ret = neigh_output(neigh, skb, false);
578+
/* if crossing protocols, can not use the cached header */
579+
ret = neigh_output(neigh, skb, is_v6gw);
582580
rcu_read_unlock_bh();
583581
return ret;
584582
}

include/net/route.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include <net/flow.h>
3030
#include <net/inet_sock.h>
3131
#include <net/ip_fib.h>
32+
#include <net/arp.h>
33+
#include <net/ndisc.h>
3234
#include <linux/in_route.h>
3335
#include <linux/rtnetlink.h>
3436
#include <linux/rcupdate.h>
@@ -350,4 +352,34 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst)
350352
return hoplimit;
351353
}
352354

355+
static inline struct neighbour *ip_neigh_gw4(struct net_device *dev,
356+
__be32 daddr)
357+
{
358+
struct neighbour *neigh;
359+
360+
neigh = __ipv4_neigh_lookup_noref(dev, daddr);
361+
if (unlikely(!neigh))
362+
neigh = __neigh_create(&arp_tbl, &daddr, dev, false);
363+
364+
return neigh;
365+
}
366+
367+
static inline struct neighbour *ip_neigh_for_gw(struct rtable *rt,
368+
struct sk_buff *skb,
369+
bool *is_v6gw)
370+
{
371+
struct net_device *dev = rt->dst.dev;
372+
struct neighbour *neigh;
373+
374+
if (likely(rt->rt_gw_family == AF_INET)) {
375+
neigh = ip_neigh_gw4(dev, rt->rt_gw4);
376+
} else if (rt->rt_gw_family == AF_INET6) {
377+
neigh = ip_neigh_gw6(dev, &rt->rt_gw6);
378+
*is_v6gw = true;
379+
} else {
380+
neigh = ip_neigh_gw4(dev, ip_hdr(skb)->daddr);
381+
}
382+
return neigh;
383+
}
384+
353385
#endif /* _ROUTE_H */

net/ipv4/ip_output.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s
188188
struct net_device *dev = dst->dev;
189189
unsigned int hh_len = LL_RESERVED_SPACE(dev);
190190
struct neighbour *neigh;
191-
u32 nexthop;
191+
bool is_v6gw = false;
192192

193193
if (rt->rt_type == RTN_MULTICAST) {
194194
IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);
@@ -218,16 +218,13 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s
218218
}
219219

220220
rcu_read_lock_bh();
221-
nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr);
222-
neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
223-
if (unlikely(!neigh))
224-
neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
221+
neigh = ip_neigh_for_gw(rt, skb, &is_v6gw);
225222
if (!IS_ERR(neigh)) {
226223
int res;
227224

228225
sock_confirm_neigh(skb, neigh);
229-
res = neigh_output(neigh, skb, false);
230-
226+
/* if crossing protocols, can not use the cached header */
227+
res = neigh_output(neigh, skb, is_v6gw);
231228
rcu_read_unlock_bh();
232229
return res;
233230
}

net/ipv4/route.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -436,18 +436,27 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
436436
{
437437
const struct rtable *rt = container_of(dst, struct rtable, dst);
438438
struct net_device *dev = dst->dev;
439-
const __be32 *pkey = daddr;
440439
struct neighbour *n;
441440

442-
if (rt->rt_gw_family == AF_INET)
443-
pkey = (const __be32 *) &rt->rt_gw4;
444-
else if (skb)
445-
pkey = &ip_hdr(skb)->daddr;
446-
447-
n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey);
448-
if (n)
449-
return n;
450-
return neigh_create(&arp_tbl, pkey, dev);
441+
rcu_read_lock_bh();
442+
443+
if (likely(rt->rt_gw_family == AF_INET)) {
444+
n = ip_neigh_gw4(dev, rt->rt_gw4);
445+
} else if (rt->rt_gw_family == AF_INET6) {
446+
n = ip_neigh_gw6(dev, &rt->rt_gw6);
447+
} else {
448+
__be32 pkey;
449+
450+
pkey = skb ? ip_hdr(skb)->daddr : *((__be32 *) daddr);
451+
n = ip_neigh_gw4(dev, pkey);
452+
}
453+
454+
if (n && !refcount_inc_not_zero(&n->refcnt))
455+
n = NULL;
456+
457+
rcu_read_unlock_bh();
458+
459+
return n;
451460
}
452461

453462
static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr)

0 commit comments

Comments
 (0)