net: add confirm_neigh method to dst_ops
authorJulian Anastasov <ja@ssi.bg>
Mon, 6 Feb 2017 21:14:15 +0000 (23:14 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 7 Feb 2017 18:07:46 +0000 (13:07 -0500)
Add confirm_neigh method to dst_ops and use it from IPv4 and IPv6
to lookup and confirm the neighbour. Its usage via the new helper
dst_confirm_neigh() should be restricted to MSG_PROBE users for
performance reasons.

For XFRM prefer the last tunnel address, if present. With help
from Steffen Klassert.

Signed-off-by: Julian Anastasov <ja@ssi.bg>
Acked-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/arp.h
include/net/dst.h
include/net/dst_ops.h
include/net/ndisc.h
net/ipv4/route.c
net/ipv6/route.c
net/xfrm/xfrm_policy.c

index 5e0f891d476c299d91fde14ce9cdeedc9a7e26c6..65619a2de6f44178d037823c3ad30c0f83f12456 100644 (file)
@@ -35,6 +35,22 @@ static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32
        return n;
 }
 
+static inline void __ipv4_confirm_neigh(struct net_device *dev, u32 key)
+{
+       struct neighbour *n;
+
+       rcu_read_lock_bh();
+       n = __ipv4_neigh_lookup_noref(dev, key);
+       if (n) {
+               unsigned long now = jiffies;
+
+               /* avoid dirtying neighbour */
+               if (n->confirmed != now)
+                       n->confirmed = now;
+       }
+       rcu_read_unlock_bh();
+}
+
 void arp_init(void);
 int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg);
 void arp_send(int type, int ptype, __be32 dest_ip,
index 6835d224d47b502fa3e396a02a71fed6130d6650..3a3b34b83b00e597487278c9822aa1d2e4c8d0d0 100644 (file)
@@ -477,6 +477,13 @@ static inline struct neighbour *dst_neigh_lookup_skb(const struct dst_entry *dst
        return IS_ERR(n) ? NULL : n;
 }
 
+static inline void dst_confirm_neigh(const struct dst_entry *dst,
+                                    const void *daddr)
+{
+       if (dst->ops->confirm_neigh)
+               dst->ops->confirm_neigh(dst, daddr);
+}
+
 static inline void dst_link_failure(struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
index 8a2b66d8d78de33f18f1343f97fd7d29e935fbfa..c84b3287e38b9e80961516b594f0e3b7950ee5e1 100644 (file)
@@ -33,6 +33,8 @@ struct dst_ops {
        struct neighbour *      (*neigh_lookup)(const struct dst_entry *dst,
                                                struct sk_buff *skb,
                                                const void *daddr);
+       void                    (*confirm_neigh)(const struct dst_entry *dst,
+                                                const void *daddr);
 
        struct kmem_cache       *kmem_cachep;
 
index d562a2fe48604b278b08834af5c60f14c7405711..8a0214654b6b10bc480d7e6dc8195555ca58dc9a 100644 (file)
@@ -391,6 +391,23 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
        return n;
 }
 
+static inline void __ipv6_confirm_neigh(struct net_device *dev,
+                                       const void *pkey)
+{
+       struct neighbour *n;
+
+       rcu_read_lock_bh();
+       n = __ipv6_neigh_lookup_noref(dev, pkey);
+       if (n) {
+               unsigned long now = jiffies;
+
+               /* avoid dirtying neighbour */
+               if (n->confirmed != now)
+                       n->confirmed = now;
+       }
+       rcu_read_unlock_bh();
+}
+
 int ndisc_init(void);
 int ndisc_late_init(void);
 
index 4b7c231c1aef66f2eaf287f9820d001f64650f8e..cb494a5050f7a9d7cbfc7d961ee5ae0d66196be7 100644 (file)
@@ -154,6 +154,7 @@ static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old)
 static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
                                           struct sk_buff *skb,
                                           const void *daddr);
+static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr);
 
 static struct dst_ops ipv4_dst_ops = {
        .family =               AF_INET,
@@ -168,6 +169,7 @@ static struct dst_ops ipv4_dst_ops = {
        .redirect =             ip_do_redirect,
        .local_out =            __ip_local_out,
        .neigh_lookup =         ipv4_neigh_lookup,
+       .confirm_neigh =        ipv4_confirm_neigh,
 };
 
 #define ECN_OR_COST(class)     TC_PRIO_##class
@@ -461,6 +463,23 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
        return neigh_create(&arp_tbl, pkey, dev);
 }
 
+static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr)
+{
+       struct net_device *dev = dst->dev;
+       const __be32 *pkey = daddr;
+       const struct rtable *rt;
+
+       rt = (const struct rtable *)dst;
+       if (rt->rt_gateway)
+               pkey = (const __be32 *)&rt->rt_gateway;
+       else if (!daddr ||
+                (rt->rt_flags &
+                 (RTCF_MULTICAST | RTCF_BROADCAST | RTCF_LOCAL)))
+               return;
+
+       __ipv4_confirm_neigh(dev, *(__force u32 *)pkey);
+}
+
 #define IP_IDENTS_SZ 2048u
 
 static atomic_t *ip_idents __read_mostly;
index 8ffa24cc8899de9a64df4cbb897382653fb25c0a..98b183f1bc8bf01fcdc9b9045249c3bc2747e739 100644 (file)
@@ -223,6 +223,21 @@ static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
        return neigh_create(&nd_tbl, daddr, dst->dev);
 }
 
+static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
+{
+       struct net_device *dev = dst->dev;
+       struct rt6_info *rt = (struct rt6_info *)dst;
+
+       daddr = choose_neigh_daddr(rt, NULL, daddr);
+       if (!daddr)
+               return;
+       if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
+               return;
+       if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
+               return;
+       __ipv6_confirm_neigh(dev, daddr);
+}
+
 static struct dst_ops ip6_dst_ops_template = {
        .family                 =       AF_INET6,
        .gc                     =       ip6_dst_gc,
@@ -239,6 +254,7 @@ static struct dst_ops ip6_dst_ops_template = {
        .redirect               =       rt6_do_redirect,
        .local_out              =       __ip6_local_out,
        .neigh_lookup           =       ip6_neigh_lookup,
+       .confirm_neigh          =       ip6_confirm_neigh,
 };
 
 static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
index 99ad1af2927f8fc9e27726ec905d60245a004108..0a0f63d3cc96a7e173e73b6ff26bb24559f19121 100644 (file)
@@ -2856,6 +2856,23 @@ static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
        return dst->path->ops->neigh_lookup(dst, skb, daddr);
 }
 
+static void xfrm_confirm_neigh(const struct dst_entry *dst, const void *daddr)
+{
+       const struct dst_entry *path = dst->path;
+
+       for (; dst != path; dst = dst->child) {
+               const struct xfrm_state *xfrm = dst->xfrm;
+
+               if (xfrm->props.mode == XFRM_MODE_TRANSPORT)
+                       continue;
+               if (xfrm->type->flags & XFRM_TYPE_REMOTE_COADDR)
+                       daddr = xfrm->coaddr;
+               else if (!(xfrm->type->flags & XFRM_TYPE_LOCAL_COADDR))
+                       daddr = &xfrm->id.daddr;
+       }
+       path->ops->confirm_neigh(path, daddr);
+}
+
 int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
 {
        int err = 0;
@@ -2882,6 +2899,8 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
                        dst_ops->link_failure = xfrm_link_failure;
                if (likely(dst_ops->neigh_lookup == NULL))
                        dst_ops->neigh_lookup = xfrm_neigh_lookup;
+               if (likely(!dst_ops->confirm_neigh))
+                       dst_ops->confirm_neigh = xfrm_confirm_neigh;
                if (likely(afinfo->garbage_collect == NULL))
                        afinfo->garbage_collect = xfrm_garbage_collect_deferred;
                rcu_assign_pointer(xfrm_policy_afinfo[afinfo->family], afinfo);