Le Sat, Jan 09, 2021 at 06:50:50PM +0100, Denis Fondras a écrit :
> 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@
>
ping.
> 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)