On 16/12/15(Wed) 23:57, Alexander Bluhm wrote: > On Wed, Dec 16, 2015 at 09:46:26PM +0100, Alexander Bluhm wrote: > > 10.188.70.17 fe:e1:ba:d0:d5:6d UHLS 0 3 - 8 vio0 > > This is this route that crashed the machine when the arp entry expired. > > When I move the rtref()/rtfree() calls into rtdeletemsg() it also > protects the calls from arptfree(). > > > __assert() at __assert+0x25 > > rtfree() at rtfree+0x11e > > rtable_delete() at rtable_delete+0x5d > > rt_delete() at rt_delete+0x6e > > rtdeletemsg() at rtdeletemsg+0x2d > > arptfree() at arptfree+0x4f > > arptimer() at arptimer+0x63 > > After rt_delete() has called rtable_delete(), the route is still > used and should not be destroyed.
The diff below indeed corrects that, but I found another bug in it. The problem is that rtable_lookup() might return a non perfect match so we would now end up deleting the RTF_CLONING route instead of failing. The proper solution would be to always pass a mask (or prefix length) to rtable_lookup(). > Index: net/route.c > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v > retrieving revision 1.292 > diff -u -p -r1.292 route.c > --- net/route.c 11 Dec 2015 08:58:23 -0000 1.292 > +++ net/route.c 16 Dec 2015 22:42:34 -0000 > @@ -159,8 +159,7 @@ struct sockaddr *rt_plentosa(sa_family_t > > struct ifaddr *ifa_ifwithroute(int, struct sockaddr *, struct sockaddr > *, > u_int); > -int rtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *, > - struct rtentry **, u_int); > +int rt_delete(struct rtentry *, struct ifnet *, unsigned int); > > #ifdef DDB > void db_print_sa(struct sockaddr *); > @@ -613,34 +612,22 @@ out: > } > > /* > - * Delete a route and generate a message > + * Delete a route and generate a message. The caller must hold a reference > + * on ``rt''. > */ > int > -rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid) > +rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid) > { > int error; > - struct rt_addrinfo info; > - unsigned int ifidx; > - struct sockaddr_in6 sa_mask; > > KASSERT(rt->rt_ifidx == ifp->if_index); > > - /* > - * Request the new route so that the entry is not actually > - * deleted. That will allow the information being reported to > - * be accurate (and consistent with route_output()). > - */ > - bzero((caddr_t)&info, sizeof(info)); > - info.rti_info[RTAX_DST] = rt_key(rt); > - if (!ISSET(rt->rt_flags, RTF_HOST)) > - info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, &sa_mask); > - info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; > - info.rti_flags = rt->rt_flags; > - ifidx = rt->rt_ifidx; > - error = rtrequest_delete(&info, rt->rt_priority, ifp, &rt, tableid); > - rt_missmsg(RTM_DELETE, &info, info.rti_flags, ifidx, error, tableid); > + rtref(rt); > + error = rt_delete(rt, ifp, rtableid); > if (error == 0) > - rtfree(rt); > + rt_sendmsg(rt, RTM_DELETE, rtableid); > + rtfree(rt); > + > return (error); > } > > @@ -671,10 +658,10 @@ rtflushclone1(struct rtentry *rt, void * > * the routes are being purged by rt_if_remove(). > */ > if (ifp == NULL) > - return 0; > + return 0; > > if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent)) > - rtdeletemsg(rt, ifp, id); > + rtdeletemsg(rt, ifp, id); > > if_put(ifp); > return 0; > @@ -818,60 +805,20 @@ rt_getifa(struct rt_addrinfo *info, u_in > } > > int > -rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp, > - struct rtentry **ret_nrt, u_int tableid) > +rt_delete(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid) > { > - struct rtentry *rt; > - int error; > - > - splsoftassert(IPL_SOFTNET); > - > - if (!rtable_exists(tableid)) > - return (EAFNOSUPPORT); > - rt = rtable_lookup(tableid, info->rti_info[RTAX_DST], > - info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], prio); > - if (rt == NULL) > - return (ESRCH); > - > - /* Make sure that's the route the caller want to delete. */ > - if (ifp != NULL && ifp->if_index != rt->rt_ifidx) { > - rtfree(rt); > - return (ESRCH); > - } > + struct sockaddr_in6 sa_mask; > > -#ifndef SMALL_KERNEL > - /* > - * If we got multipath routes, we require users to specify > - * a matching gateway. > - */ > - if ((rt->rt_flags & RTF_MPATH) && > - info->rti_info[RTAX_GATEWAY] == NULL) { > - rtfree(rt); > - return (ESRCH); > - } > -#endif > + KASSERT(ifp->if_index == rt->rt_ifidx); > > - /* > - * Since RTP_LOCAL cannot be set by userland, make > - * sure that local routes are only modified by the > - * kernel. > - */ > - if ((rt->rt_flags & (RTF_LOCAL|RTF_BROADCAST)) && > - prio != RTP_LOCAL) { > - rtfree(rt); > - return (EINVAL); > - } > + splsoftassert(IPL_SOFTNET); > > - error = rtable_delete(tableid, info->rti_info[RTAX_DST], > - info->rti_info[RTAX_NETMASK], rt); > - if (error != 0) { > - rtfree(rt); > + if (rtable_delete(rtableid, rt_key(rt), rt_plen2mask(rt, &sa_mask), rt)) > return (ESRCH); > - } > > /* clean up any cloned children */ > if ((rt->rt_flags & RTF_CLONING) != 0) > - rtflushclone(tableid, rt); > + rtflushclone(rtableid, rt); > > rtfree(rt->rt_gwroute); > rt->rt_gwroute = NULL; > @@ -881,23 +828,10 @@ rtrequest_delete(struct rt_addrinfo *inf > > rt->rt_flags &= ~RTF_UP; > > - if (ifp == NULL) { > - ifp = if_get(rt->rt_ifidx); > - KASSERT(ifp != NULL); > - ifp->if_rtrequest(ifp, RTM_DELETE, rt); > - if_put(ifp); > - } else { > - KASSERT(ifp->if_index == rt->rt_ifidx); > - ifp->if_rtrequest(ifp, RTM_DELETE, rt); > - } > + ifp->if_rtrequest(ifp, RTM_DELETE, rt); > > atomic_inc_int(&rttrash); > > - if (ret_nrt != NULL) > - *ret_nrt = rt; > - else > - rtfree(rt); > - > return (0); > } > > @@ -924,9 +858,50 @@ rtrequest(int req, struct rt_addrinfo *i > info->rti_info[RTAX_NETMASK] = NULL; > switch (req) { > case RTM_DELETE: > - error = rtrequest_delete(info, prio, NULL, ret_nrt, tableid); > - if (error) > + rt = rtable_lookup(tableid, info->rti_info[RTAX_DST], > + info->rti_info[RTAX_NETMASK], > + info->rti_info[RTAX_GATEWAY], prio); > + if (rt == NULL) > + return (ESRCH); > + > +#ifndef SMALL_KERNEL > + /* > + * If we got multipath routes, we require users to specify > + * a matching gateway. > + */ > + if (ISSET(rt->rt_flags, RTF_MPATH) && > + info->rti_info[RTAX_GATEWAY] == NULL) { > + rtfree(rt); > + return (ESRCH); > + } > +#endif > + > + /* > + * Since RTP_LOCAL cannot be set by userland, make > + * sure that local routes are only modified by the > + * kernel. > + */ > + if (ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST) && > + prio != RTP_LOCAL) { > + rtfree(rt); > + return (EINVAL); > + } > + > + ifp = if_get(rt->rt_ifidx); > + KASSERT(ifp != NULL); > + error = rt_delete(rt, ifp, tableid); > + if_put(ifp); > + > + if (error) { > + rtfree(rt); > return (error); > + } > + > + if (ret_nrt != NULL) > + *ret_nrt = rt; > + else > + rtfree(rt); > + > break; > > case RTM_RESOLVE: > @@ -1265,8 +1240,8 @@ rt_ifa_del(struct ifaddr *ifa, int flags > struct rtentry *rt; > struct mbuf *m = NULL; > struct sockaddr *deldst; > - struct rt_addrinfo info; > - struct sockaddr_rtlabel sa_rl; > + struct sockaddr *mask = NULL; > + struct sockaddr *gateway = NULL; > unsigned int rtableid = ifp->if_rdomain; > uint8_t prio = ifp->if_priority + RTP_STATIC; > int error; > @@ -1286,16 +1261,10 @@ rt_ifa_del(struct ifaddr *ifa, int flags > dst = deldst; > } > > - memset(&info, 0, sizeof(info)); > - info.rti_ifa = ifa; > - info.rti_flags = flags; > - info.rti_info[RTAX_DST] = dst; > if ((flags & RTF_LLINFO) == 0) > - info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; > - info.rti_info[RTAX_LABEL] = rtlabel_id2sa(ifp->if_rtlabelid, &sa_rl); > - > + gateway = ifa->ifa_addr; > if ((flags & RTF_HOST) == 0) > - info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask; > + mask = ifa->ifa_netmask; > > if (flags & (RTF_LOCAL|RTF_BROADCAST)) > prio = RTP_LOCAL; > @@ -1303,13 +1272,32 @@ rt_ifa_del(struct ifaddr *ifa, int flags > if (flags & RTF_CONNECTED) > prio = RTP_CONNECTED; > > - error = rtrequest_delete(&info, prio, ifp, &rt, rtableid); > - if (error == 0) { > - rt_sendmsg(rt, RTM_DELETE, rtableid); > - if (flags & RTF_LOCAL) > - rt_sendaddrmsg(rt, RTM_DELADDR); > + rt = rtable_lookup(rtableid, dst, mask, gateway, prio); > + if (rt == NULL) > + return (ESRCH); > + > +#ifndef SMALL_KERNEL > + /* > + * If we got multipath routes, we require users to specify > + * a matching gateway. > + */ > + if (ISSET(rt->rt_flags, RTF_MPATH) && gateway == NULL) { > rtfree(rt); > + return (ESRCH); > } > +#endif > + > + /* Make sure that's the route the caller want to delete. */ > + if (rt->rt_ifidx != ifp->if_index) { > + rtfree(rt); > + return (ESRCH); > + } > + > + error = rtdeletemsg(rt, ifp, rtableid); > + if (error == 0 && (flags & RTF_LOCAL)) > + rt_sendaddrmsg(rt, RTM_DELADDR); > + rtfree(rt); > + > if (m != NULL) > m_free(m); > > @@ -1721,7 +1709,7 @@ rt_if_remove_rtdelete(struct rtentry *rt > struct ifnet *ifp = vifp; > > if (rt->rt_ifidx == ifp->if_index) { > - int cloning = (rt->rt_flags & RTF_CLONING); > + int cloning = ISSET(rt->rt_flags, RTF_CLONING); > > if (rtdeletemsg(rt, ifp, id) == 0 && cloning) > return (EAGAIN); >