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)