Hello. According the code `ifaddr’ struct has `ifa_refcnt’ field. Also it seems `ifa’ could exist while corresponding `ifp’ was destroyed. Is this true for `rt’ case? Should `ifa_refcnt' be bumped while you return `ifa’?
> On 9 Jan 2021, at 20:50, Denis Fondras <[email protected]> wrote: > > This diff place the user-set source address outside of struct art_root and > make > the code more readable (to me). > > Based on a concept by mpi@ > > Index: net/art.h > =================================================================== > RCS file: /cvs/src/sys/net/art.h,v > retrieving revision 1.20 > diff -u -p -r1.20 art.h > --- net/art.h 12 Nov 2020 15:25:28 -0000 1.20 > +++ net/art.h 9 Jan 2021 16:04:02 -0000 > @@ -42,7 +42,6 @@ struct art_root { > uint8_t ar_nlvl; /* [I] Number of levels */ > uint8_t ar_alen; /* [I] Address length in bits */ > uint8_t ar_off; /* [I] Offset of key in bytes */ > - struct sockaddr *source; /* [K] optional src addr to use > */ > }; > > #define ISLEAF(e) (((unsigned long)(e) & 1) == 0) > Index: net/route.c > =================================================================== > RCS file: /cvs/src/sys/net/route.c,v > retrieving revision 1.397 > diff -u -p -r1.397 route.c > --- net/route.c 29 Oct 2020 21:15:27 -0000 1.397 > +++ net/route.c 9 Jan 2021 16:04:02 -0000 > @@ -1192,9 +1192,9 @@ rt_ifa_del(struct ifaddr *ifa, int flags > if (flags & RTF_CONNECTED) > prio = ifp->if_priority + RTP_CONNECTED; > > - rtable_clearsource(rdomain, ifa->ifa_addr); > error = rtrequest_delete(&info, prio, ifp, &rt, rdomain); > if (error == 0) { > + rt_sourceclear(rt, rdomain); > rtm_send(rt, RTM_DELETE, 0, rdomain); > if (flags & RTF_LOCAL) > rtm_addr(RTM_DELADDR, ifa); > Index: net/route.h > =================================================================== > RCS file: /cvs/src/sys/net/route.h,v > retrieving revision 1.183 > diff -u -p -r1.183 route.h > --- net/route.h 29 Oct 2020 21:15:27 -0000 1.183 > +++ net/route.h 9 Jan 2021 16:04:02 -0000 > @@ -478,6 +478,9 @@ int rtrequest_delete(struct rt_addrinfo > int rt_if_track(struct ifnet *); > int rt_if_linkstate_change(struct rtentry *, void *, u_int); > int rtdeletemsg(struct rtentry *, struct ifnet *, u_int); > + > +struct ifaddr *rt_get_ifa(struct rtentry *, unsigned int); > +void rt_sourceclear(struct rtentry *, unsigned int); > #endif /* _KERNEL */ > > #endif /* _NET_ROUTE_H_ */ > Index: net/rtable.c > =================================================================== > RCS file: /cvs/src/sys/net/rtable.c,v > retrieving revision 1.72 > diff -u -p -r1.72 rtable.c > --- net/rtable.c 7 Nov 2020 09:51:40 -0000 1.72 > +++ net/rtable.c 9 Jan 2021 16:04:02 -0000 > @@ -365,44 +365,6 @@ rtable_alloc(unsigned int rtableid, unsi > return (art_alloc(rtableid, alen, off)); > } > > -int > -rtable_setsource(unsigned int rtableid, int af, struct sockaddr *src) > -{ > - struct art_root *ar; > - > - if ((ar = rtable_get(rtableid, af)) == NULL) > - return (EAFNOSUPPORT); > - > - ar->source = src; > - > - return (0); > -} > - > -struct sockaddr * > -rtable_getsource(unsigned int rtableid, int af) > -{ > - struct art_root *ar; > - > - ar = rtable_get(rtableid, af); > - if (ar == NULL) > - return (NULL); > - > - return (ar->source); > -} > - > -void > -rtable_clearsource(unsigned int rtableid, struct sockaddr *src) > -{ > - struct sockaddr *addr; > - > - addr = rtable_getsource(rtableid, src->sa_family); > - if (addr && (addr->sa_len == src->sa_len)) { > - if (memcmp(src, addr, addr->sa_len) == 0) { > - rtable_setsource(rtableid, src->sa_family, NULL); > - } > - } > -} > - > struct rtentry * > rtable_lookup(unsigned int rtableid, struct sockaddr *dst, > struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio) > Index: net/rtable.h > =================================================================== > RCS file: /cvs/src/sys/net/rtable.h,v > retrieving revision 1.26 > diff -u -p -r1.26 rtable.h > --- net/rtable.h 7 Nov 2020 09:51:40 -0000 1.26 > +++ net/rtable.h 9 Jan 2021 16:04:02 -0000 > @@ -39,9 +39,6 @@ unsigned int rtable_l2(unsigned int); > unsigned int rtable_loindex(unsigned int); > void rtable_l2set(unsigned int, unsigned int, unsigned int); > > -int rtable_setsource(unsigned int, int, struct sockaddr *); > -struct sockaddr *rtable_getsource(unsigned int, int); > -void rtable_clearsource(unsigned int, struct sockaddr *); > struct rtentry *rtable_lookup(unsigned int, struct sockaddr *, > struct sockaddr *, struct sockaddr *, uint8_t); > struct rtentry *rtable_match(unsigned int, struct sockaddr *, uint32_t > *); > Index: net/rtsock.c > =================================================================== > RCS file: /cvs/src/sys/net/rtsock.c,v > retrieving revision 1.304 > diff -u -p -r1.304 rtsock.c > --- net/rtsock.c 7 Nov 2020 09:51:40 -0000 1.304 > +++ net/rtsock.c 9 Jan 2021 16:04:02 -0000 > @@ -138,7 +138,8 @@ int sysctl_iflist(int, struct walkarg > int sysctl_ifnames(struct walkarg *); > int sysctl_rtable_rtstat(void *, size_t *, void *); > > -int rt_setsource(unsigned int, struct sockaddr *); > +int rt_sourceset(struct rtentry *, unsigned int); > +struct rtentry *rt_get_rt(int, unsigned int); > > /* > * Locks used to protect struct members > @@ -170,6 +171,14 @@ struct rtptable { > struct pool rtpcb_pool; > struct rtptable rtptable; > > +struct rt_srcaddr { > + LIST_ENTRY(rt_srcaddr) rts_next; > + unsigned int rts_rtableid; > + struct rtentry *rts_rt; > +}; > + > +LIST_HEAD(, rt_srcaddr) srcaddr_h = LIST_HEAD_INITIALIZER(srcaddr_h); > + > /* > * These flags and timeout are used for indicating to userland (via a > * RTM_DESYNC msg) when the route socket has overflowed and messages > @@ -664,10 +673,7 @@ rtm_report(struct rtentry *rt, u_char ty > ifp = if_get(rt->rt_ifidx); > if (ifp != NULL) { > info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl); > - info.rti_info[RTAX_IFA] = > - rtable_getsource(tableid, > info.rti_info[RTAX_DST]->sa_family); > - if (info.rti_info[RTAX_IFA] == NULL) > - info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; > + info.rti_info[RTAX_IFA] = rt_get_ifa(rt, tableid)->ifa_addr; > if (ifp->if_flags & IFF_POINTOPOINT) > info.rti_info[RTAX_BRD] = rt->rt_ifa->ifa_dstaddr; > } > @@ -860,10 +866,28 @@ route_output(struct mbuf *m, struct sock > if (info.rti_info[RTAX_IFA] == NULL) { > error = EINVAL; > goto fail; > + } else if ((info.rti_info[RTAX_IFA]->sa_family == AF_INET6 && > + IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) > + info.rti_info[RTAX_IFA])->sin6_addr)) || > + (info.rti_info[RTAX_IFA]->sa_family == AF_INET && > + ((struct sockaddr_in *) > + info.rti_info[RTAX_IFA])->sin_addr.s_addr == 0)) { > + > rt_sourceclear(rt_get_rt(info.rti_info[RTAX_IFA]->sa_family, > + tableid), tableid); > + rtfree(rt); > + rt = NULL; > + } else { > + rt = rtalloc(info.rti_info[RTAX_IFA], 0, tableid); > + if (rt == NULL || !ISSET(rt->rt_flags, RTF_LOCAL)) { > + error = EINVAL; > + goto fail; > + } > + NET_LOCK(); > + error = rt_sourceset(rt, tableid); > + NET_UNLOCK(); > + if (error != 0) > + goto fail; > } > - if ((error = > - rt_setsource(tableid, info.rti_info[RTAX_IFA])) != 0) > - goto fail; > } else { > error = rtm_output(rtm, &rt, &info, prio, tableid); > if (!error) { > @@ -873,9 +897,9 @@ route_output(struct mbuf *m, struct sock > rtm = rtm_report(rt, type, seq, tableid); > len = rtm->rtm_msglen; > } > + rtfree(rt); > } > > - rtfree(rt); > if (error) { > rtm->rtm_errno = error; > } else { > @@ -1687,10 +1711,7 @@ rtm_send(struct rtentry *rt, int cmd, in > ifp = if_get(rt->rt_ifidx); > if (ifp != NULL) { > info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl); > - info.rti_info[RTAX_IFA] = > - rtable_getsource(rtableid, > info.rti_info[RTAX_DST]->sa_family); > - if (info.rti_info[RTAX_IFA] == NULL) > - info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; > + info.rti_info[RTAX_IFA] = rt_get_ifa(rt, rtableid)->ifa_addr; > } > > rtm_miss(cmd, &info, rt->rt_flags, rt->rt_priority, rt->rt_ifidx, error, > @@ -1928,10 +1949,7 @@ sysctl_dumpentry(struct rtentry *rt, voi > ifp = if_get(rt->rt_ifidx); > if (ifp != NULL) { > info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl); > - info.rti_info[RTAX_IFA] = > - rtable_getsource(id, info.rti_info[RTAX_DST]->sa_family); > - if (info.rti_info[RTAX_IFA] == NULL) > - info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; > + info.rti_info[RTAX_IFA] = rt_get_ifa(rt, id)->ifa_addr; > if (ifp->if_flags & IFF_POINTOPOINT) > info.rti_info[RTAX_BRD] = rt->rt_ifa->ifa_dstaddr; > } > @@ -2067,33 +2085,30 @@ sysctl_ifnames(struct walkarg *w) > } > > int > -sysctl_source(int af, u_int tableid, struct walkarg *w) > +sysctl_source(int af, struct walkarg *w) > { > - struct sockaddr *sa; > - int size, error = 0; > + struct rt_srcaddr *rtsa = NULL; > + unsigned int tableid = w->w_arg; > + int error = 0; > > - sa = rtable_getsource(tableid, af); > - if (sa) { > - switch (sa->sa_family) { > - case AF_INET: > - size = sizeof(struct sockaddr_in); > - break; > -#ifdef INET6 > - case AF_INET6: > - size = sizeof(struct sockaddr_in6); > - break; > -#endif > - default: > - return (0); > - } > - w->w_needed += size; > + LIST_FOREACH(rtsa, &srcaddr_h, rts_next) { > + if (rtsa->rts_rtableid != tableid) > + continue; > + if (af != 0 && rtsa->rts_rt->rt_dest->sa_family != af) > + continue; > + > + w->w_needed += rtsa->rts_rt->rt_dest->sa_len; > if (w->w_where && w->w_needed <= 0) { > - if ((error = copyout(sa, w->w_where, size))) > - return (error); > - w->w_where += size; > + error = copyout(rtsa->rts_rt->rt_ifa->ifa_addr, > + w->w_where, rtsa->rts_rt->rt_dest->sa_len); > + if (error == EAFNOSUPPORT) > + error = 0; > + if (error) > + break; > + w->w_where += rtsa->rts_rt->rt_dest->sa_len; > } > } > - return (0); > + return (error); > } > > int > @@ -2171,16 +2186,7 @@ sysctl_rtable(int *name, u_int namelen, > if (!rtable_exists(tableid)) > return (ENOENT); > NET_LOCK(); > - for (i = 1; i <= AF_MAX; i++) { > - if (af != 0 && af != i) > - continue; > - > - error = sysctl_source(i, tableid, &w); > - if (error == EAFNOSUPPORT) > - error = 0; > - if (error) > - break; > - } > + error = sysctl_source(af, &w); > NET_UNLOCK(); > break; > } > @@ -2314,40 +2320,96 @@ rtm_validate_proposal(struct rt_addrinfo > } > > int > -rt_setsource(unsigned int rtableid, struct sockaddr *src) > +rt_sourceset(struct rtentry *rt, unsigned int rtableid) > { > - struct ifaddr *ifa; > - /* > - * If source address is 0.0.0.0 or :: > - * use automatic source selection > - */ > - switch(src->sa_family) { > - case AF_INET: > - if(satosin(src)->sin_addr.s_addr == INADDR_ANY) { > - rtable_setsource(rtableid, AF_INET, NULL); > - return (0); > - } > - break; > -#ifdef INET6 > - case AF_INET6: > - if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr)) { > - rtable_setsource(rtableid, AF_INET6, NULL); > - return (0); > + struct rt_srcaddr *rtsa = NULL; > + > + LIST_FOREACH(rtsa, &srcaddr_h, rts_next) { > + if (rtsa->rts_rtableid == rtableid && > + rtsa->rts_rt->rt_dest->sa_family == rt->rt_dest->sa_family) > + break; > + } > + if (rtsa == NULL) { > + if ((rtsa = malloc(sizeof(struct rt_srcaddr), M_IFADDR, > + M_NOWAIT|M_ZERO)) == NULL) > + return (ENOMEM); > + rtsa->rts_rtableid = rtableid; > + rtsa->rts_rt = rt; > + LIST_INSERT_HEAD(&srcaddr_h, rtsa, rts_next); > + } else { > + /* Update existing entry */ > + rtfree(rtsa->rts_rt); > + rtsa->rts_rt = rt; > + } > + > + return (0); > +} > + > +/* > + * Return the 'ifa' associated to a given 'rt' unless a preferred > + * source address has been specified for the same routing table. > + */ > +struct rtentry * > +rt_get_rt(int af, unsigned int rtableid) > +{ > + struct rt_srcaddr *rtsa = NULL; > + > + LIST_FOREACH(rtsa, &srcaddr_h, rts_next) { > + if (rtsa->rts_rtableid == rtableid && > + rtsa->rts_rt->rt_dest->sa_family == af) > + break; > + } > + if (rtsa) > + return (rtsa->rts_rt); > + > + return (NULL); > +} > + > +struct ifaddr * > +rt_get_ifa(struct rtentry *rt, unsigned int rtableid) > +{ > + struct rt_srcaddr *rtsa = NULL; > + struct ifnet *ifp = NULL; > + > + if (ISSET(rt->rt_flags, RTF_HOST|RTF_LLINFO)) > + return (rt->rt_ifa); > + > + LIST_FOREACH(rtsa, &srcaddr_h, rts_next) { > + if (rtsa->rts_rtableid == rtableid && > + rtsa->rts_rt->rt_dest->sa_family == rt->rt_dest->sa_family) > + break; > + } > + if (rtsa != NULL && rtisvalid(rtsa->rts_rt)) { > + struct rtentry *nrt = rtsa->rts_rt; > + > + ifp = if_get(nrt->rt_ifidx); > + if (ifp != NULL) { > + if (ISSET(ifp->if_flags, IFF_UP)) { > + if_put(ifp); > + return (nrt->rt_ifa); > + } > } > - break; > -#endif > - default: > - return (EAFNOSUPPORT); > } > + return (rt->rt_ifa); > +} > > - /* > - * Check if source address is assigned to an interface in the > - * same rdomain > - */ > - if ((ifa = ifa_ifwithaddr(src, rtableid)) == NULL) > - return (EINVAL); > +void > +rt_sourceclear(struct rtentry *rt, unsigned int rtableid) > +{ > + struct rt_srcaddr *rtsa = NULL; > > - return (rtable_setsource(rtableid, src->sa_family, ifa->ifa_addr)); > + if (rt == NULL) > + return; > + > + LIST_FOREACH(rtsa, &srcaddr_h, rts_next) { > + if (rtsa->rts_rtableid != rtableid) > + continue; > + if (rtsa->rts_rt == rt) { > + LIST_REMOVE(rtsa, rts_next); > + free(rtsa, M_IFADDR, sizeof(struct rt_srcaddr)); > + break; > + } > + } > } > > /* > Index: netinet/in_pcb.c > =================================================================== > RCS file: /cvs/src/sys/netinet/in_pcb.c,v > retrieving revision 1.252 > diff -u -p -r1.252 in_pcb.c > --- netinet/in_pcb.c 7 Nov 2020 09:51:40 -0000 1.252 > +++ netinet/in_pcb.c 9 Jan 2021 16:04:02 -0000 > @@ -887,7 +887,6 @@ in_pcbselsrc(struct in_addr **insrc, str > struct route *ro = &inp->inp_route; > struct in_addr *laddr = &inp->inp_laddr; > u_int rtableid = inp->inp_rtableid; > - struct sockaddr *ip4_source = NULL; > > struct sockaddr_in *sin2; > struct in_ifaddr *ia = NULL; > @@ -951,30 +950,11 @@ in_pcbselsrc(struct in_addr **insrc, str > } > > /* > - * If we found a route, use the address > - * corresponding to the outgoing interface. > + * If we found a route, use the address corresponding to the > + * outgoing interface or the preferred source address if set. > */ > if (ro->ro_rt != NULL) > - ia = ifatoia(ro->ro_rt->rt_ifa); > - > - /* > - * Use preferred source address if : > - * - destination is not onlink > - * - preferred source addresss is set > - * - output interface is UP > - */ > - if (ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO) && > - !(ro->ro_rt->rt_flags & RTF_HOST)) { > - ip4_source = rtable_getsource(rtableid, AF_INET); > - if (ip4_source != NULL) { > - struct ifaddr *ifa; > - if ((ifa = ifa_ifwithaddr(ip4_source, rtableid)) != > - NULL && ISSET(ifa->ifa_ifp->if_flags, IFF_UP)) { > - *insrc = &satosin(ip4_source)->sin_addr; > - return (0); > - } > - } > - } > + ia = ifatoia(rt_get_ifa(ro->ro_rt, rtableid)); > > if (ia == NULL) > return (EADDRNOTAVAIL); > Index: netinet/ip_icmp.c > =================================================================== > RCS file: /cvs/src/sys/netinet/ip_icmp.c,v > retrieving revision 1.184 > diff -u -p -r1.184 ip_icmp.c > --- netinet/ip_icmp.c 20 Dec 2020 21:15:47 -0000 1.184 > +++ netinet/ip_icmp.c 9 Jan 2021 16:04:02 -0000 > @@ -745,7 +745,7 @@ icmp_reflect(struct mbuf *m, struct mbuf > return (EHOSTUNREACH); > } > > - ia = ifatoia(rt->rt_ifa); > + ia = ifatoia(rt_get_ifa(rt, rtableid)); > } > > ip->ip_dst = ip->ip_src; > Index: netinet6/icmp6.c > =================================================================== > RCS file: /cvs/src/sys/netinet6/icmp6.c,v > retrieving revision 1.233 > diff -u -p -r1.233 icmp6.c > --- netinet6/icmp6.c 28 Oct 2020 17:27:35 -0000 1.233 > +++ netinet6/icmp6.c 9 Jan 2021 16:04:02 -0000 > @@ -1163,7 +1163,10 @@ icmp6_reflect(struct mbuf **mp, size_t o > rtfree(rt); > goto bad; > } > - ia6 = in6_ifawithscope(rt->rt_ifa->ifa_ifp, &t, rtableid); > + ia6 = ifatoia6(rt_get_ifa(rt, rtableid)); > + if (ia6 == NULL) > + ia6 = in6_ifawithscope(rt->rt_ifa->ifa_ifp, &t, > + rtableid); > if (ia6 != NULL) > src = &ia6->ia_addr.sin6_addr; > if (src == NULL) > Index: netinet6/in6_src.c > =================================================================== > RCS file: /cvs/src/sys/netinet6/in6_src.c,v > retrieving revision 1.84 > diff -u -p -r1.84 in6_src.c > --- netinet6/in6_src.c 7 Nov 2020 09:51:40 -0000 1.84 > +++ netinet6/in6_src.c 9 Jan 2021 16:04:02 -0000 > @@ -100,7 +100,6 @@ in6_pcbselsrc(struct in6_addr **in6src, > struct in6_addr *laddr = &inp->inp_laddr6; > u_int rtableid = inp->inp_rtableid; > struct ifnet *ifp = NULL; > - struct sockaddr *ip6_source = NULL; > struct in6_addr *dst; > struct in6_ifaddr *ia6 = NULL; > struct in6_pktinfo *pi = NULL; > @@ -208,32 +207,16 @@ in6_pcbselsrc(struct in6_addr **in6src, > */ > > if (ro->ro_rt) { > - ifp = if_get(ro->ro_rt->rt_ifidx); > - if (ifp != NULL) { > - ia6 = in6_ifawithscope(ifp, dst, rtableid); > - if_put(ifp); > + ia6 = ifatoia6(rt_get_ifa(ro->ro_rt, rtableid)); > + if (ia6 == NULL) { > + ifp = if_get(ro->ro_rt->rt_ifidx); > + if (ifp != NULL) { > + ia6 = in6_ifawithscope(ifp, dst, rtableid); > + if_put(ifp); > + } > } > if (ia6 == NULL) /* xxx scope error ?*/ > ia6 = ifatoia6(ro->ro_rt->rt_ifa); > - } > - > - /* > - * Use preferred source address if : > - * - destination is not onlink > - * - preferred source addresss is set > - * - output interface is UP > - */ > - if (ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO) && > - !(ro->ro_rt->rt_flags & RTF_HOST)) { > - ip6_source = rtable_getsource(rtableid, AF_INET6); > - if (ip6_source != NULL) { > - struct ifaddr *ifa; > - if ((ifa = ifa_ifwithaddr(ip6_source, rtableid)) != > - NULL && ISSET(ifa->ifa_ifp->if_flags, IFF_UP)) { > - *in6src = &satosin6(ip6_source)->sin6_addr; > - return (0); > - } > - } > } > > if (ia6 == NULL) >
