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"