Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 17 Nov 2014 01:05:30 +0000 (UTC)
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r274611 - in head: sbin/route sys/net sys/netinet sys/netinet6
Message-ID:  <201411170105.sAH15Ugb017789@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201411170105.sAH15Ugb017789>