]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/ipv6/route.c
udp: fix rx queue len reported by diag and proc interface
[mirror_ubuntu-bionic-kernel.git] / net / ipv6 / route.c
index 0458b761f3c56ce765841e0a3a7e5e78f90b95eb..d24c70fd216ad6fc838fee8d49661789ee71753a 100644 (file)
@@ -128,7 +128,7 @@ struct uncached_list {
 
 static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
 
-static void rt6_uncached_list_add(struct rt6_info *rt)
+void rt6_uncached_list_add(struct rt6_info *rt)
 {
        struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
 
@@ -139,7 +139,7 @@ static void rt6_uncached_list_add(struct rt6_info *rt)
        spin_unlock_bh(&ul->lock);
 }
 
-static void rt6_uncached_list_del(struct rt6_info *rt)
+void rt6_uncached_list_del(struct rt6_info *rt)
 {
        if (!list_empty(&rt->rt6i_uncached)) {
                struct uncached_list *ul = rt->rt6i_uncached_list;
@@ -922,6 +922,9 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
        struct rt6_info *rt, *rt_cache;
        struct fib6_node *fn;
 
+       if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
+               flags &= ~RT6_LOOKUP_F_IFACE;
+
        rcu_read_lock();
        fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
 restart:
@@ -1510,7 +1513,30 @@ static void rt6_exceptions_remove_prefsrc(struct rt6_info *rt)
        }
 }
 
-static void rt6_exceptions_update_pmtu(struct rt6_info *rt, int mtu)
+static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
+                                        struct rt6_info *rt, int mtu)
+{
+       /* If the new MTU is lower than the route PMTU, this new MTU will be the
+        * lowest MTU in the path: always allow updating the route PMTU to
+        * reflect PMTU decreases.
+        *
+        * If the new MTU is higher, and the route PMTU is equal to the local
+        * MTU, this means the old MTU is the lowest in the path, so allow
+        * updating it: if other nodes now have lower MTUs, PMTU discovery will
+        * handle this.
+        */
+
+       if (dst_mtu(&rt->dst) >= mtu)
+               return true;
+
+       if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
+               return true;
+
+       return false;
+}
+
+static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
+                                      struct rt6_info *rt, int mtu)
 {
        struct rt6_exception_bucket *bucket;
        struct rt6_exception *rt6_ex;
@@ -1519,20 +1545,22 @@ static void rt6_exceptions_update_pmtu(struct rt6_info *rt, int mtu)
        bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
                                        lockdep_is_held(&rt6_exception_lock));
 
-       if (bucket) {
-               for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
-                       hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
-                               struct rt6_info *entry = rt6_ex->rt6i;
-                               /* For RTF_CACHE with rt6i_pmtu == 0
-                                * (i.e. a redirected route),
-                                * the metrics of its rt->dst.from has already
-                                * been updated.
-                                */
-                               if (entry->rt6i_pmtu && entry->rt6i_pmtu > mtu)
-                                       entry->rt6i_pmtu = mtu;
-                       }
-                       bucket++;
+       if (!bucket)
+               return;
+
+       for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
+               hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
+                       struct rt6_info *entry = rt6_ex->rt6i;
+
+                       /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
+                        * route), the metrics of its rt->dst.from have already
+                        * been updated.
+                        */
+                       if (entry->rt6i_pmtu &&
+                           rt6_mtu_change_route_allowed(idev, entry, mtu))
+                               entry->rt6i_pmtu = mtu;
                }
+               bucket++;
        }
 }
 
@@ -1586,31 +1614,34 @@ static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
         * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
         * expired, independently from their aging, as per RFC 8201 section 4
         */
-       if (!(rt->rt6i_flags & RTF_EXPIRES) &&
-           time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
-               RT6_TRACE("aging clone %p\n", rt);
+       if (!(rt->rt6i_flags & RTF_EXPIRES)) {
+               if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
+                       RT6_TRACE("aging clone %p\n", rt);
+                       rt6_remove_exception(bucket, rt6_ex);
+                       return;
+               }
+       } else if (time_after(jiffies, rt->dst.expires)) {
+               RT6_TRACE("purging expired route %p\n", rt);
                rt6_remove_exception(bucket, rt6_ex);
                return;
-       } else if (rt->rt6i_flags & RTF_GATEWAY) {
+       }
+
+       if (rt->rt6i_flags & RTF_GATEWAY) {
                struct neighbour *neigh;
                __u8 neigh_flags = 0;
 
-               neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway);
-               if (neigh) {
+               neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
+               if (neigh)
                        neigh_flags = neigh->flags;
-                       neigh_release(neigh);
-               }
+
                if (!(neigh_flags & NTF_ROUTER)) {
                        RT6_TRACE("purging route %p via non-router but gateway\n",
                                  rt);
                        rt6_remove_exception(bucket, rt6_ex);
                        return;
                }
-       } else if (__rt6_check_expired(rt)) {
-               RT6_TRACE("purging expired route %p\n", rt);
-               rt6_remove_exception(bucket, rt6_ex);
-               return;
        }
+
        gc_args->more++;
 }
 
@@ -1626,7 +1657,8 @@ void rt6_age_exceptions(struct rt6_info *rt,
        if (!rcu_access_pointer(rt->rt6i_exception_bucket))
                return;
 
-       spin_lock_bh(&rt6_exception_lock);
+       rcu_read_lock_bh();
+       spin_lock(&rt6_exception_lock);
        bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
                                    lockdep_is_held(&rt6_exception_lock));
 
@@ -1640,7 +1672,8 @@ void rt6_age_exceptions(struct rt6_info *rt,
                        bucket++;
                }
        }
-       spin_unlock_bh(&rt6_exception_lock);
+       spin_unlock(&rt6_exception_lock);
+       rcu_read_unlock_bh();
 }
 
 struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
@@ -1790,11 +1823,16 @@ static void ip6_multipath_l3_keys(const struct sk_buff *skb,
        const struct ipv6hdr *inner_iph;
        const struct icmp6hdr *icmph;
        struct ipv6hdr _inner_iph;
+       struct icmp6hdr _icmph;
 
        if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
                goto out;
 
-       icmph = icmp6_hdr(skb);
+       icmph = skb_header_pointer(skb, skb_transport_offset(skb),
+                                  sizeof(_icmph), &_icmph);
+       if (!icmph)
+               goto out;
+
        if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
            icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
            icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
@@ -1813,7 +1851,7 @@ out:
        keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
        keys->addrs.v6addrs.src = key_iph->saddr;
        keys->addrs.v6addrs.dst = key_iph->daddr;
-       keys->tags.flow_label = ip6_flowinfo(key_iph);
+       keys->tags.flow_label = ip6_flowlabel(key_iph);
        keys->basic.ip_proto = key_iph->nexthdr;
 }
 
@@ -2040,9 +2078,6 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
        const struct in6_addr *daddr, *saddr;
        struct rt6_info *rt6 = (struct rt6_info *)dst;
 
-       if (rt6->rt6i_flags & RTF_LOCAL)
-               return;
-
        if (dst_metric_locked(dst, RTAX_MTU))
                return;
 
@@ -3517,25 +3552,13 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
           Since RFC 1981 doesn't include administrative MTU increase
           update PMTU increase is a MUST. (i.e. jumbo frame)
         */
-       /*
-          If new MTU is less than route PMTU, this new MTU will be the
-          lowest MTU in the path, update the route PMTU to reflect PMTU
-          decreases; if new MTU is greater than route PMTU, and the
-          old MTU is the lowest MTU in the path, update the route PMTU
-          to reflect the increase. In this case if the other nodes' MTU
-          also have the lowest MTU, TOO BIG MESSAGE will be lead to
-          PMTU discovery.
-        */
        if (rt->dst.dev == arg->dev &&
-           dst_metric_raw(&rt->dst, RTAX_MTU) &&
            !dst_metric_locked(&rt->dst, RTAX_MTU)) {
                spin_lock_bh(&rt6_exception_lock);
-               if (dst_mtu(&rt->dst) >= arg->mtu ||
-                   (dst_mtu(&rt->dst) < arg->mtu &&
-                    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
+               if (dst_metric_raw(&rt->dst, RTAX_MTU) &&
+                   rt6_mtu_change_route_allowed(idev, rt, arg->mtu))
                        dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
-               }
-               rt6_exceptions_update_pmtu(rt, arg->mtu);
+               rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
                spin_unlock_bh(&rt6_exception_lock);
        }
        return 0;
@@ -3553,6 +3576,7 @@ void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
 
 static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
        [RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
+       [RTA_PREFSRC]           = { .len = sizeof(struct in6_addr) },
        [RTA_OIF]               = { .type = NLA_U32 },
        [RTA_IIF]               = { .type = NLA_U32 },
        [RTA_PRIORITY]          = { .type = NLA_U32 },
@@ -3564,6 +3588,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
        [RTA_EXPIRES]           = { .type = NLA_U32 },
        [RTA_UID]               = { .type = NLA_U32 },
        [RTA_MARK]              = { .type = NLA_U32 },
+       [RTA_TABLE]             = { .type = NLA_U32 },
 };
 
 static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,