OSDN Git Service

net/ipv6: Add helper to return path MTU based on fib result
authorDavid Ahern <dsahern@gmail.com>
Mon, 21 May 2018 16:08:14 +0000 (09:08 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Tue, 22 May 2018 08:51:09 +0000 (10:51 +0200)
Determine path MTU from a FIB lookup result. Logic is based on
ip6_dst_mtu_forward plus lookup of nexthop exception.

Add ip6_dst_mtu_forward to ipv6_stubs to handle access by core
bpf code.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
include/net/addrconf.h
include/net/ip6_fib.h
include/net/ip6_route.h
net/ipv6/addrconf_core.c
net/ipv6/af_inet6.c
net/ipv6/route.c

index ff766ab..c07d4dd 100644 (file)
@@ -236,6 +236,8 @@ struct ipv6_stub {
                                                   struct flowi6 *fl6, int oif,
                                                   const struct sk_buff *skb,
                                                   int strict);
+       u32 (*ip6_mtu_from_fib6)(struct fib6_info *f6i, struct in6_addr *daddr,
+                                struct in6_addr *saddr);
 
        void (*udpv6_encap_enable)(void);
        void (*ndisc_send_na)(struct net_device *dev, const struct in6_addr *daddr,
index cc70f6d..7897efe 100644 (file)
@@ -412,6 +412,12 @@ static inline struct net_device *fib6_info_nh_dev(const struct fib6_info *f6i)
        return f6i->fib6_nh.nh_dev;
 }
 
+static inline
+struct lwtunnel_state *fib6_info_nh_lwt(const struct fib6_info *f6i)
+{
+       return f6i->fib6_nh.nh_lwtstate;
+}
+
 void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
                     unsigned int flags);
 
index 4cf1ef9..7b9c82d 100644 (file)
@@ -300,6 +300,9 @@ static inline unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst)
        return mtu;
 }
 
+u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
+                     struct in6_addr *saddr);
+
 struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
                                   struct net_device *dev, struct sk_buff *skb,
                                   const void *daddr);
index 2fe754f..5cd0029 100644 (file)
@@ -161,12 +161,20 @@ eafnosupport_fib6_multipath_select(const struct net *net, struct fib6_info *f6i,
        return f6i;
 }
 
+static u32
+eafnosupport_ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
+                              struct in6_addr *saddr)
+{
+       return 0;
+}
+
 const struct ipv6_stub *ipv6_stub __read_mostly = &(struct ipv6_stub) {
        .ipv6_dst_lookup   = eafnosupport_ipv6_dst_lookup,
        .fib6_get_table    = eafnosupport_fib6_get_table,
        .fib6_table_lookup = eafnosupport_fib6_table_lookup,
        .fib6_lookup       = eafnosupport_fib6_lookup,
        .fib6_multipath_select = eafnosupport_fib6_multipath_select,
+       .ip6_mtu_from_fib6 = eafnosupport_ip6_mtu_from_fib6,
 };
 EXPORT_SYMBOL_GPL(ipv6_stub);
 
index 50de8b0..9ed0eae 100644 (file)
@@ -894,6 +894,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
        .fib6_table_lookup = fib6_table_lookup,
        .fib6_lookup       = fib6_lookup,
        .fib6_multipath_select = fib6_multipath_select,
+       .ip6_mtu_from_fib6 = ip6_mtu_from_fib6,
        .udpv6_encap_enable = udpv6_encap_enable,
        .ndisc_send_na = ndisc_send_na,
        .nd_tbl = &nd_tbl,
index cc24ed3..dc5d5c8 100644 (file)
@@ -2603,6 +2603,54 @@ out:
        return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
 }
 
+/* MTU selection:
+ * 1. mtu on route is locked - use it
+ * 2. mtu from nexthop exception
+ * 3. mtu from egress device
+ *
+ * based on ip6_dst_mtu_forward and exception logic of
+ * rt6_find_cached_rt; called with rcu_read_lock
+ */
+u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
+                     struct in6_addr *saddr)
+{
+       struct rt6_exception_bucket *bucket;
+       struct rt6_exception *rt6_ex;
+       struct in6_addr *src_key;
+       struct inet6_dev *idev;
+       u32 mtu = 0;
+
+       if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
+               mtu = f6i->fib6_pmtu;
+               if (mtu)
+                       goto out;
+       }
+
+       src_key = NULL;
+#ifdef CONFIG_IPV6_SUBTREES
+       if (f6i->fib6_src.plen)
+               src_key = saddr;
+#endif
+
+       bucket = rcu_dereference(f6i->rt6i_exception_bucket);
+       rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
+       if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
+               mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
+
+       if (likely(!mtu)) {
+               struct net_device *dev = fib6_info_nh_dev(f6i);
+
+               mtu = IPV6_MIN_MTU;
+               idev = __in6_dev_get(dev);
+               if (idev && idev->cnf.mtu6 > mtu)
+                       mtu = idev->cnf.mtu6;
+       }
+
+       mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
+out:
+       return mtu - lwtunnel_headroom(fib6_info_nh_lwt(f6i), mtu);
+}
+
 struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
                                  struct flowi6 *fl6)
 {