From 5772e17ca63150de34a4e7bf80815ae0dd9e1bd4 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Sun, 8 Sep 2019 11:56:18 +0100 Subject: [PATCH] route: Seperate route messages from creation/lookup. This allows a more fine grained choice about whether to announce messsages via route(4) or not. For example, a freshly added clone route is useless without the resolved hardware address it's talking to. Instead, announce the route when the hardware address has been resolved, changed or if it's not resolvable. For IPv6 destinations, we can now interpret RTM_MISS as unreachable. This allows listening software to take action, such as soliciting a new router if the destination missed was originally a router. Reviewed-by: sephe --- sys/net/route.c | 44 ++++++++++++------------------------------ sys/net/route.h | 11 ++++------- sys/net/rtsock.c | 8 +++++--- sys/netinet/if_ether.c | 43 +++++++++++++++++++++++++++-------------- sys/netinet6/nd6.c | 19 +++++++++++++++--- 5 files changed, 65 insertions(+), 60 deletions(-) diff --git a/sys/net/route.c b/sys/net/route.c index e2e007bcee..763bf98ec1 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -220,7 +220,7 @@ rtalloc_ign(struct route *ro, u_long ignoreflags) rtfree(ro->ro_rt); ro->ro_rt = NULL; } - ro->ro_rt = _rtlookup(&ro->ro_dst, RTL_REPORTMSG, ignoreflags); + ro->ro_rt = _rtlookup(&ro->ro_dst, ignoreflags); } /* @@ -233,7 +233,7 @@ rtalloc_ign(struct route *ro, u_long ignoreflags) * Any route returned has its reference count incremented. */ struct rtentry * -_rtlookup(struct sockaddr *dst, boolean_t generate_report, u_long ignore) +_rtlookup(struct sockaddr *dst, u_long ignore) { struct radix_node_head *rnh = rt_tables[mycpuid][dst->sa_family]; struct rtentry *rt; @@ -261,18 +261,12 @@ _rtlookup(struct sockaddr *dst, boolean_t generate_report, u_long ignore) error = rtrequest(RTM_RESOLVE, dst, NULL, NULL, 0, &clonedroute); /* clone the route */ if (error != 0) { /* cloning failed */ - if (generate_report) - rt_dstmsg(RTM_MISS, dst, error); + rt_dstmsg(RTM_MISS, dst, error); rt->rt_refcnt++; return (rt); /* return the uncloned route */ } - if (generate_report) { - if (clonedroute->rt_flags & RTF_XRESOLVE) - rt_dstmsg(RTM_RESOLVE, dst, 0); - else - rt_rtmsg(RTM_ADD, clonedroute, - clonedroute->rt_ifp, 0); - } + if (clonedroute->rt_flags & RTF_XRESOLVE) + rt_dstmsg(RTM_RESOLVE, dst, 0); return (clonedroute); /* return cloned route */ } @@ -284,8 +278,7 @@ _rtlookup(struct sockaddr *dst, boolean_t generate_report, u_long ignore) unreach: rtstat.rts_unreach++; - if (generate_report) - rt_dstmsg(RTM_MISS, dst, 0); + rt_dstmsg(RTM_MISS, dst, 0); return (NULL); } @@ -429,8 +422,9 @@ create: flags |= RTF_MODIFIED; /* We only need to report rtmsg on CPU0 */ - rt_setgate(rt, rt_key(rt), gateway, - mycpuid == 0 ? RTL_REPORTMSG : RTL_DONTREPORT); + rt_setgate(rt, rt_key(rt), gateway); + if (mycpuid == 0) + rt_rtmsg(RTM_CHANGE, rt, rt->rt_ifp, 0); error = 0; stat = &rtstat.rts_newgateway; } @@ -744,7 +738,6 @@ rtrequest1(int req, struct rt_addrinfo *rtinfo, struct rtentry **ret_nrt) struct radix_node_head *rnh; struct ifaddr *ifa; struct sockaddr *ndst; - boolean_t reportmsg; int error = 0; ASSERT_NETISR_NCPUS(mycpuid); @@ -873,18 +866,7 @@ makeroute: rt->rt_flags = RTF_UP | rtinfo->rti_flags; rt->rt_cpuid = mycpuid; - if (mycpuid != 0 && req == RTM_ADD) { - /* For RTM_ADD, we have already sent rtmsg on CPU0. */ - reportmsg = RTL_DONTREPORT; - } else { - /* - * For RTM_ADD, we only send rtmsg on CPU0. - * For RTM_RESOLVE, we always send rtmsg. XXX - */ - reportmsg = RTL_REPORTMSG; - } - error = rt_setgate(rt, dst, rtinfo->rti_info[RTAX_GATEWAY], - reportmsg); + error = rt_setgate(rt, dst, rtinfo->rti_info[RTAX_GATEWAY]); if (error != 0) { Free(rt); gotoerr(error); @@ -1153,8 +1135,7 @@ rt_fixchange(struct radix_node *rn, void *vp) } int -rt_setgate(struct rtentry *rt0, struct sockaddr *dst, struct sockaddr *gate, - boolean_t generate_report) +rt_setgate(struct rtentry *rt0, struct sockaddr *dst, struct sockaddr *gate) { char *space, *oldspace; int dlen = RT_ROUNDUP(dst->sa_len), glen = RT_ROUNDUP(gate->sa_len); @@ -1237,8 +1218,7 @@ rt_setgate(struct rtentry *rt0, struct sockaddr *dst, struct sockaddr *gate, * * This breaks TTCP for hosts outside the gateway! XXX JH */ - rt->rt_gwroute = _rtlookup(gate, generate_report, - RTF_PRCLONING); + rt->rt_gwroute = _rtlookup(gate, RTF_PRCLONING); if (rt->rt_gwroute == rt) { rt->rt_gwroute = NULL; --rt->rt_refcnt; diff --git a/sys/net/route.h b/sys/net/route.h index 363ee5afb8..27660f5518 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -354,15 +354,12 @@ void rt_missmsg (int, struct rt_addrinfo *, int, int); void rt_newaddrmsg (int, struct ifaddr *, int, struct rtentry *); void rt_newmaddrmsg (int, struct ifmultiaddr *); void rt_rtmsg(int cmd, struct rtentry *rt, struct ifnet *ifp, int error); -int rt_setgate (struct rtentry *, - struct sockaddr *, struct sockaddr *, boolean_t); +int rt_setgate (struct rtentry *, struct sockaddr *, struct sockaddr *); void rtalloc (struct route *); void rtalloc_ign (struct route *, u_long); struct rtentry * - _rtlookup (struct sockaddr *, __boolean_t, u_long); -#define RTL_REPORTMSG TRUE -#define RTL_DONTREPORT FALSE + _rtlookup (struct sockaddr *, u_long); /* flags to ignore */ #define RTL_DOCLONE 0UL @@ -374,7 +371,7 @@ struct rtentry * static __inline struct rtentry * rtpurelookup(struct sockaddr *dst) { - return _rtlookup(dst, RTL_DONTREPORT, RTL_DONTCLONE); + return _rtlookup(dst, RTL_DONTCLONE); } /* @@ -383,7 +380,7 @@ rtpurelookup(struct sockaddr *dst) static __inline struct rtentry * rtlookup(struct sockaddr *dst) { - return _rtlookup(dst, RTL_REPORTMSG, RTL_DOCLONE); + return _rtlookup(dst, RTL_DOCLONE); } typedef void (*rtrequest1_callback_func_t)(int, int, struct rt_addrinfo *, diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 232cd740aa..43d48c1399 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -823,8 +823,7 @@ route_output_change_callback(int cmd, struct rt_addrinfo *rtinfo, * We only need to generate rtmsg upon the * first route to be changed. */ - error = rt_setgate(rt, rt_key(rt), rtinfo->rti_gateway, - found_cnt == 1 ? RTL_REPORTMSG : RTL_DONTREPORT); + error = rt_setgate(rt, rt_key(rt), rtinfo->rti_gateway); if (error != 0) goto done; } @@ -855,6 +854,8 @@ route_output_change_callback(int cmd, struct rt_addrinfo *rtinfo, } } rtm->rtm_index = rt->rt_ifp->if_index; + if (found_cnt == 1) + rt_rtmsg(RTM_CHANGE, rt, rt->rt_ifp, 0); done: return error; } @@ -1176,7 +1177,8 @@ rt_rtmsg(int cmd, struct rtentry *rt, struct ifnet *ifp, int error) rtinfo.rti_ifpaddr = TAILQ_FIRST(&ifp->if_addrheads[mycpuid])->ifa->ifa_addr; } - rtinfo.rti_ifaaddr = rt->rt_ifa->ifa_addr; + if (rt->rt_ifa != NULL) + rtinfo.rti_ifaaddr = rt->rt_ifa->ifa_addr; m = rt_msg_mbuf(cmd, &rtinfo); if (m == NULL) diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index c6a4b5aaea..98815001a7 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -162,7 +162,7 @@ static void arpintr(netmsg_t msg); static void arptfree(struct llinfo_arp *); static void arptimer(void *); static struct llinfo_arp * - arplookup(in_addr_t, boolean_t, boolean_t, boolean_t); + arplookup(in_addr_t, boolean_t, boolean_t); #ifdef INET static void in_arpinput(struct mbuf *); static void in_arpreply(struct mbuf *m, in_addr_t, in_addr_t); @@ -246,8 +246,7 @@ arp_rtrequest(int req, struct rtentry *rt) * Case 1: This route should come from a route to iface. */ rt_setgate(rt, rt_key(rt), - (struct sockaddr *)&null_sdl, - RTL_DONTREPORT); + (struct sockaddr *)&null_sdl); gate = rt->rt_gateway; SDL(gate)->sdl_type = rt->rt_ifp->if_type; SDL(gate)->sdl_index = rt->rt_ifp->if_index; @@ -529,8 +528,7 @@ arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m, la = rt->rt_llinfo; } if (la == NULL) { - la = arplookup(SIN(dst)->sin_addr.s_addr, - TRUE, RTL_REPORTMSG, FALSE); + la = arplookup(SIN(dst)->sin_addr.s_addr, TRUE, FALSE); if (la != NULL) rt = la->la_rt; } @@ -599,6 +597,7 @@ arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m, rt->rt_expire += arpt_down; la->la_asked = 0; la->la_preempt = arp_maxtries; + rt_rtmsg(RTM_MISS, rt, rt->rt_ifp, 0); } } } @@ -691,7 +690,7 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_creation_failure, CTLFLAG_RW, */ static int arp_update_oncpu(struct mbuf *m, in_addr_t saddr, boolean_t create, - boolean_t generate_report, boolean_t dologging) + boolean_t dologging) { struct arphdr *ah = mtod(m, struct arphdr *); struct ifnet *ifp = m->m_pkthdr.rcvif; @@ -705,9 +704,11 @@ arp_update_oncpu(struct mbuf *m, in_addr_t saddr, boolean_t create, KASSERT(curthread->td_type == TD_TYPE_NETISR, ("arp update not in netisr")); - la = arplookup(saddr, create, generate_report, FALSE); + la = arplookup(saddr, create, FALSE); if (la && (rt = la->la_rt) && (sdl = SDL(rt->rt_gateway))) { struct in_addr isaddr = { saddr }; + int rt_cmd = sdl->sdl_alen == 0 ? RTM_ADD : RTM_CHANGE; + bool do_rtmsg = false; /* * Normally arps coming in on the wrong interface are ignored, @@ -760,10 +761,12 @@ arp_update_oncpu(struct mbuf *m, in_addr_t saddr, boolean_t create, if (sdl->sdl_type != ifp->if_type) { sdl->sdl_type = ifp->if_type; changed = 1; + do_rtmsg = true; } if (sdl->sdl_index != ifp->if_index) { sdl->sdl_index = ifp->if_index; changed = 1; + do_rtmsg = true; } } if (sdl->sdl_alen && @@ -791,6 +794,7 @@ arp_update_oncpu(struct mbuf *m, in_addr_t saddr, boolean_t create, } return changed; } + do_rtmsg = true; } /* * sanity check for the address length. @@ -815,6 +819,8 @@ arp_update_oncpu(struct mbuf *m, in_addr_t saddr, boolean_t create, } return changed; } + if (sdl->sdl_alen == 0) + do_rtmsg = true; memcpy(LLADDR(sdl), ar_sha(ah), sdl->sdl_alen = ah->ar_hln); if (rt->rt_expire != 0) { if (rt->rt_expire != time_uptime + arpt_keep && @@ -848,6 +854,9 @@ arp_update_oncpu(struct mbuf *m, in_addr_t saddr, boolean_t create, ifp->if_output(ifp, m, rt_key(rt), rt); changed = 1; } + + if (do_rtmsg && mycpuid == 0) + rt_rtmsg(rt_cmd, rt, rt->rt_ifp, 0); } return changed; } @@ -1056,7 +1065,7 @@ match: ASSERT_NETISR0; changed = arp_update_oncpu(m, isaddr.s_addr, itaddr.s_addr == myaddr.s_addr, - RTL_REPORTMSG, TRUE); + TRUE); if (netisr_ncpus > 1 && changed) { struct netmsg_inarp *msg = &m->m_hdr.mh_arpmsg; @@ -1106,7 +1115,7 @@ arp_update_msghandler(netmsg_t msg) KASSERT(mycpuid > 0, ("arp update msg on cpu%d", mycpuid)); arp_update_oncpu(rmsg->m, rmsg->saddr, rmsg->taddr == rmsg->myaddr, - RTL_DONTREPORT, FALSE); + FALSE); nextcpu = mycpuid + 1; if (nextcpu < netisr_ncpus) { @@ -1161,7 +1170,7 @@ in_arpreply(struct mbuf *m, in_addr_t taddr, in_addr_t myaddr) struct llinfo_arp *la; struct rtentry *rt; - la = arplookup(taddr, FALSE, RTL_DONTREPORT, SIN_PROXY); + la = arplookup(taddr, FALSE, SIN_PROXY); if (la == NULL) { struct sockaddr_in sin; #ifdef DEBUG_PROXY @@ -1248,8 +1257,9 @@ in_arpreply(struct mbuf *m, in_addr_t taddr, in_addr_t myaddr) static void arptfree(struct llinfo_arp *la) { - struct rtentry *rt = la->la_rt; + struct rtentry *rt = la->la_rt, *nrt; struct sockaddr_dl *sdl; + int error; if (rt == NULL) panic("arptfree"); @@ -1262,14 +1272,18 @@ arptfree(struct llinfo_arp *la) rt->rt_flags &= ~RTF_REJECT; return; } - rtrequest(RTM_DELETE, rt_key(rt), NULL, rt_mask(rt), 0, NULL); + error = rtrequest(RTM_DELETE, rt_key(rt), NULL, rt_mask(rt), 0, &nrt); + if (error == 0 && nrt != NULL) { + rt_rtmsg(RTM_DELETE, nrt, nrt->rt_ifp, 0); + rtfree(nrt); + } } /* * Lookup or enter a new address in arptab. */ static struct llinfo_arp * -arplookup(in_addr_t addr, boolean_t create, boolean_t generate_report, +arplookup(in_addr_t addr, boolean_t create, boolean_t proxy) { struct rtentry *rt; @@ -1283,8 +1297,7 @@ arplookup(in_addr_t addr, boolean_t create, boolean_t generate_report, sin.sin_addr.s_addr = addr; sin.sin_other = proxy ? SIN_PROXY : 0; if (create) { - rt = _rtlookup((struct sockaddr *)&sin, - generate_report, RTL_DOCLONE); + rt = rtlookup((struct sockaddr *)&sin); } else { rt = rtpurelookup((struct sockaddr *)&sin); } diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index fbf2d52130..845ec677fd 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -499,6 +499,7 @@ nd6_timer_dispatch(netmsg_t nmsg) ICMP6_DST_UNREACH_ADDR, 0); ln->ln_hold = NULL; } + rt_rtmsg(RTM_MISS, rt, rt->rt_ifp, 0); next = nd6_free(rt); } break; @@ -1004,6 +1005,8 @@ nd6_free(struct rtentry *rt) struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; struct nd_defrouter *dr; + int error; + struct rtentry *nrt = NULL; /* * we used to have kpfctlinput(PRC_HOSTDEAD) here. @@ -1079,7 +1082,15 @@ nd6_free(struct rtentry *rt) * caches, and disable the route entry not to be used in already * cached routes. */ - rtrequest(RTM_DELETE, rt_key(rt), NULL, rt_mask(rt), 0, NULL); + error = rtrequest(RTM_DELETE, rt_key(rt), NULL, rt_mask(rt), 0, &nrt); + if (error == 0 && nrt != NULL) { + struct sockaddr_dl *sdl; + + sdl = (struct sockaddr_dl *)nrt->rt_gateway; + if (sdl->sdl_alen != 0) + rt_rtmsg(RTM_DELETE, nrt, nrt->rt_ifp, 0); + rtfree(nrt); + } return (next); } @@ -1194,8 +1205,7 @@ nd6_rtrequest(int req, struct rtentry *rt) * treated as on-link. */ rt_setgate(rt, rt_key(rt), - (struct sockaddr *)&null_sdl, - RTL_DONTREPORT); + (struct sockaddr *)&null_sdl); gate = rt->rt_gateway; SDL(gate)->sdl_type = ifp->if_type; SDL(gate)->sdl_index = ifp->if_index; @@ -1814,6 +1824,9 @@ fail: break; } + if (llchange || lladdr) + rt_rtmsg(llchange ? RTM_CHANGE : RTM_ADD, rt, rt->rt_ifp, 0); + /* * When the link-layer address of a router changes, select the * best router again. In particular, when the neighbor entry is newly -- 2.41.0