Index: sbin/route/route.c =================================================================== --- sbin/route/route.c +++ sbin/route/route.c @@ -1590,7 +1590,7 @@ "\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" Index: sys/net/if.c =================================================================== --- sys/net/if.c +++ sys/net/if.c @@ -2526,6 +2526,7 @@ #ifdef INET6 nd6_setmtu(ifp); #endif + rt_updatemtu(ifp); } break; } Index: sys/net/route.h =================================================================== --- sys/net/route.h +++ sys/net/route.h @@ -151,7 +151,7 @@ /* 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 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 */ Index: sys/net/route.c =================================================================== --- sys/net/route.c +++ sys/net/route.c @@ -141,6 +141,14 @@ 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,55 @@ 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 && (rt->rt_flags & RTF_FIXEDMTU) == 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); @@ -1482,7 +1539,7 @@ 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) + if (rt->rt_mtu == 0 || rt->rt_mtu > mtu) rt->rt_mtu = mtu; } @@ -1501,8 +1558,24 @@ rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt) { - if (info->rti_mflags & RTV_MTU) - rt->rt_mtu = info->rti_rmx->rmx_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; + rt->rt_mtu = info->rti_rmx->rmx_mtu; + } else { + + /* + * User explicitly sets MTU to 0. + * Assume rollback to default. + */ + rt->rt_flags &= ~RTF_FIXEDMTU; + } + } if (info->rti_mflags & RTV_WEIGHT) rt->rt_weight = info->rti_rmx->rmx_weight; /* Kernel -> userland timebase conversion. */ Index: sys/netinet/ip_output.c =================================================================== --- sys/netinet/ip_output.c +++ sys/netinet/ip_output.c @@ -322,20 +322,10 @@ * 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)); Index: sys/netinet6/ip6_output.c =================================================================== --- sys/netinet6/ip6_output.c +++ sys/netinet6/ip6_output.c @@ -1275,17 +1275,6 @@ */ 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);