Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
[muen/linux.git] / net / ipv6 / route.c
index 38b75e9d6eae1d4046d7b3d2a0f4fb74f0f6f0e9..b3c1137ad0b8dd7ed4981cab7eac5e989a88b48b 100644 (file)
@@ -1509,7 +1509,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;
@@ -1518,20 +1541,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++;
        }
 }
 
@@ -2479,7 +2504,7 @@ static int ip6_route_check_nh_onlink(struct net *net,
                                     struct net_device *dev,
                                     struct netlink_ext_ack *extack)
 {
-       u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL;
+       u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
        const struct in6_addr *gw_addr = &cfg->fc_gateway;
        u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
        struct rt6_info *grt;
@@ -2488,8 +2513,10 @@ static int ip6_route_check_nh_onlink(struct net *net,
        err = 0;
        grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
        if (grt) {
-               if (grt->rt6i_flags & flags || dev != grt->dst.dev) {
-                       NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
+               if (!grt->dst.error &&
+                   (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Nexthop has invalid gateway or device mismatch");
                        err = -EINVAL;
                }
 
@@ -3807,25 +3834,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;