Author: melifaro
Date: Mon Nov 17 01:05:29 2014
New Revision: 274611
URL: https://svnweb.freebsd.org/changeset/base/274611

Log:
  Finish r274175: do control plane MTU tracking.
  
  Update route MTU in case of ifnet MTU change.
  Add new RTF_FIXEDMTU to track explicitly specified MTU.
  
  Old behavior:
  ifconfig em0 mtu 1500->9000 -> all routes traversing em0 do not change MTU.
  User has to manually update all routes.
  ifconfig em0 mtu 9000->1500 -> all routes traversing em0 do not change MTU.
  However, if ip[6]_output finds route with rt_mtu > interface mtu, rt_mtu
  gets updated.
  
  New behavior:
  ifconfig em0 mtu 1500->9000 -> all interface routes in all fibs gets updated
  with new MTU unless RTF_FIXEDMTU flag set on them.
  ifconfig em0 mtu 9000->1500 -> all routes in all fibs gets updated with new
  MTU unless RTF_FIXEDMTU flag set on them AND rt_mtu is less than ifp mtu.
  
  route add ... -mtu XXX automatically sets RTF_FIXEDMTU flag.
  route change .. -mtu 0 automatically removes RTF_FIXEDMTU flag.
  
  PR:           194238
  MFC after:    1 month
  CR:           D1125

Modified:
  head/sbin/route/route.c
  head/sys/net/if.c
  head/sys/net/route.c
  head/sys/net/route.h
  head/sys/netinet/ip_output.c
  head/sys/netinet6/ip6_output.c

Modified: head/sbin/route/route.c
==============================================================================
--- head/sbin/route/route.c     Mon Nov 17 01:01:45 2014        (r274610)
+++ head/sbin/route/route.c     Mon Nov 17 01:05:29 2014        (r274611)
@@ -1587,7 +1587,7 @@ static const char routeflags[] =
     "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE"
     "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
     "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3"
-    "\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY";
+    "\024FIXEDMTU\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY";
 static const char ifnetflags[] =
     "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
     "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"

Modified: head/sys/net/if.c
==============================================================================
--- head/sys/net/if.c   Mon Nov 17 01:01:45 2014        (r274610)
+++ head/sys/net/if.c   Mon Nov 17 01:05:29 2014        (r274611)
@@ -2494,6 +2494,7 @@ ifhwioctl(u_long cmd, struct ifnet *ifp,
 #ifdef INET6
                        nd6_setmtu(ifp);
 #endif
+                       rt_updatemtu(ifp);
                }
                break;
        }

Modified: head/sys/net/route.c
==============================================================================
--- head/sys/net/route.c        Mon Nov 17 01:01:45 2014        (r274610)
+++ head/sys/net/route.c        Mon Nov 17 01:05:29 2014        (r274611)
@@ -141,6 +141,14 @@ static int rtrequest1_fib_change(struct 
     struct rtentry **, u_int);
 static void rt_setmetrics(const struct rt_addrinfo *, struct rtentry *);
 
+struct if_mtuinfo
+{
+       struct ifnet    *ifp;
+       int             mtu;
+};
+
+static int     if_updatemtu_cb(struct radix_node *, void *);
+
 /*
  * handler for net.my_fibnum
  */
@@ -947,6 +955,70 @@ bad:
        return (error);
 }
 
+static int
+if_updatemtu_cb(struct radix_node *rn, void *arg)
+{
+       struct rtentry *rt;
+       struct if_mtuinfo *ifmtu;
+
+       rt = (struct rtentry *)rn;
+       ifmtu = (struct if_mtuinfo *)arg;
+
+       if (rt->rt_ifp != ifmtu->ifp)
+               return (0);
+
+       if (rt->rt_mtu >= ifmtu->mtu) {
+               /* We have to decrease mtu regardless of flags */
+               rt->rt_mtu = ifmtu->mtu;
+               return (0);
+       }
+
+       /*
+        * New MTU is bigger. Check if are allowed to alter it
+        */
+       if ((rt->rt_flags & (RTF_FIXEDMTU | RTF_GATEWAY | RTF_HOST)) != 0) {
+
+               /*
+                * Skip routes with user-supplied MTU and
+                * non-interface routes
+                */
+               return (0);
+       }
+
+       /* We are safe to update route MTU */
+       rt->rt_mtu = ifmtu->mtu;
+
+       return (0);
+}
+
+void
+rt_updatemtu(struct ifnet *ifp)
+{
+       struct if_mtuinfo ifmtu;
+       struct radix_node_head *rnh;
+       int i, j;
+
+       ifmtu.ifp = ifp;
+
+       /*
+        * Try to update rt_mtu for all routes using this interface
+        * Unfortunately the only way to do this is to traverse all
+        * routing tables in all fibs/domains.
+        */
+       for (i = 1; i <= AF_MAX; i++) {
+               ifmtu.mtu = if_getmtu_family(ifp, i);
+               for (j = 0; j < rt_numfibs; j++) {
+                       rnh = rt_tables_get_rnh(j, i);
+                       if (rnh == NULL)
+                               continue;
+                       RADIX_NODE_HEAD_LOCK(rnh);
+                       rnh->rnh_walktree(rnh, if_updatemtu_cb, &ifmtu);
+                       RADIX_NODE_HEAD_UNLOCK(rnh);
+               }
+       }
+}
+
+
 #if 0
 int p_sockaddr(char *buf, int buflen, struct sockaddr *s);
 int rt_print(char *buf, int buflen, struct rtentry *rt);
@@ -1408,6 +1480,7 @@ rtrequest1_fib_change(struct radix_node_
        int error = 0;
        int free_ifa = 0;
        int family, mtu;
+       struct if_mtuinfo ifmtu;
 
        rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
            info->rti_info[RTAX_NETMASK], rnh);
@@ -1478,12 +1551,19 @@ rtrequest1_fib_change(struct radix_node_
        if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest != NULL)
               rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, info);
 
-       /* Ensure route MTU is not bigger than interface MTU */
+       /* Alter route MTU if necessary */
        if (rt->rt_ifp != NULL) {
                family = info->rti_info[RTAX_DST]->sa_family;
                mtu = if_getmtu_family(rt->rt_ifp, family);
-               if (rt->rt_mtu > mtu)
+               /* Set default MTU */
+               if (rt->rt_mtu == 0)
                        rt->rt_mtu = mtu;
+               if (rt->rt_mtu != mtu) {
+                       /* Check if we really need to update */
+                       ifmtu.ifp = rt->rt_ifp;
+                       ifmtu.mtu = mtu;
+                       if_updatemtu_cb(rt->rt_nodes, &ifmtu);
+               }
        }
 
        if (ret_nrt) {
@@ -1501,8 +1581,24 @@ static void
 rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt)
 {
 
-       if (info->rti_mflags & RTV_MTU)
+       if (info->rti_mflags & RTV_MTU) {
+               if (info->rti_rmx->rmx_mtu != 0) {
+
+                       /*
+                        * MTU was explicitly provided by user.
+                        * Keep it.
+                        */
+                       rt->rt_flags |= RTF_FIXEDMTU;
+               } else {
+
+                       /*
+                        * User explicitly sets MTU to 0.
+                        * Assume rollback to default.
+                        */
+                       rt->rt_flags &= ~RTF_FIXEDMTU;
+               }
                rt->rt_mtu = info->rti_rmx->rmx_mtu;
+       }
        if (info->rti_mflags & RTV_WEIGHT)
                rt->rt_weight = info->rti_rmx->rmx_weight;
        /* Kernel -> userland timebase conversion. */

Modified: head/sys/net/route.h
==============================================================================
--- head/sys/net/route.h        Mon Nov 17 01:01:45 2014        (r274610)
+++ head/sys/net/route.h        Mon Nov 17 01:05:29 2014        (r274611)
@@ -151,7 +151,7 @@ struct rtentry {
 /*                     0x10000            unused, was RTF_PRCLONING */
 /*                     0x20000            unused, was RTF_WASCLONED */
 #define RTF_PROTO3     0x40000         /* protocol specific routing flag */
-/*                     0x80000            unused */
+#define        RTF_FIXEDMTU    0x80000         /* MTU was explicitly specified 
*/
 #define RTF_PINNED     0x100000        /* route is immutable */
 #define        RTF_LOCAL       0x200000        /* route represents a local 
address */
 #define        RTF_BROADCAST   0x400000        /* route represents a bcast 
address */
@@ -378,6 +378,7 @@ int rtsock_routemsg(int, struct ifnet *i
 int     rt_expunge(struct radix_node_head *, struct rtentry *);
 void    rtfree(struct rtentry *);
 int     rt_check(struct rtentry **, struct rtentry **, struct sockaddr *);
+void   rt_updatemtu(struct ifnet *);
 
 /* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */
 /* Thes are used by old code not yet converted to use multiple FIBS */

Modified: head/sys/netinet/ip_output.c
==============================================================================
--- head/sys/netinet/ip_output.c        Mon Nov 17 01:01:45 2014        
(r274610)
+++ head/sys/netinet/ip_output.c        Mon Nov 17 01:05:29 2014        
(r274611)
@@ -322,20 +322,10 @@ again:
         * Calculate MTU.  If we have a route that is up, use that,
         * otherwise use the interface's MTU.
         */
-       if (rte != NULL && (rte->rt_flags & (RTF_UP|RTF_HOST))) {
-               /*
-                * This case can happen if the user changed the MTU
-                * of an interface after enabling IP on it.  Because
-                * most netifs don't keep track of routes pointing to
-                * them, there is no way for one to update all its
-                * routes when the MTU is changed.
-                */
-               if (rte->rt_mtu > ifp->if_mtu)
-                       rte->rt_mtu = ifp->if_mtu;
+       if (rte != NULL && (rte->rt_flags & (RTF_UP|RTF_HOST)))
                mtu = rte->rt_mtu;
-       } else {
+       else
                mtu = ifp->if_mtu;
-       }
        /* Catch a possible divide by zero later. */
        KASSERT(mtu > 0, ("%s: mtu %d <= 0, rte=%p (rt_flags=0x%08x) ifp=%p",
            __func__, mtu, rte, (rte != NULL) ? rte->rt_flags : 0, ifp));

Modified: head/sys/netinet6/ip6_output.c
==============================================================================
--- head/sys/netinet6/ip6_output.c      Mon Nov 17 01:01:45 2014        
(r274610)
+++ head/sys/netinet6/ip6_output.c      Mon Nov 17 01:05:29 2014        
(r274611)
@@ -1275,17 +1275,6 @@ ip6_getpmtu(struct route_in6 *ro_pmtu, s
                         */
                        alwaysfrag = 1;
                        mtu = IPV6_MMTU;
-               } else if (mtu > ifmtu) {
-                       /*
-                        * The MTU on the route is larger than the MTU on
-                        * the interface!  This shouldn't happen, unless the
-                        * MTU of the interface has been changed after the
-                        * interface was brought up.  Change the MTU in the
-                        * route to match the interface MTU (as long as the
-                        * field isn't locked).
-                        */
-                       mtu = ifmtu;
-                       ro_pmtu->ro_rt->rt_mtu = mtu;
                }
        } else if (ifp) {
                mtu = IN6_LINKMTU(ifp);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to