Is anyone interested in this ?
This diff allows to select the default source IP address (for TCP/UDP
connections) on multi-homed & "multi-addressed" machines.
Looking for feedbacks on what I broke.
Do not test with ping(8) as it uses another source address selection codepath.
`curl ifconfig.co` is a good candidate to check if source is set correctly.
Example usage :
Set 2001:db8::1 as source : route source 2001:db8::1
Unset previously set IPv6 address on rdomain 10 : route -T10 source -inet6
default
Show set address : route source
Comments ? OK ?
Denis
Index: sbin/route/keywords.h
===================================================================
RCS file: /cvs/src/sbin/route/keywords.h,v
retrieving revision 1.34
diff -u -p -r1.34 keywords.h
--- sbin/route/keywords.h 10 Aug 2017 13:44:48 -0000 1.34
+++ sbin/route/keywords.h 17 Sep 2020 09:59:25 -0000
@@ -1,4 +1,4 @@
-/* $OpenBSD: keywords.h,v 1.34 2017/08/10 13:44:48 benno Exp $ */
+/* $OpenBSD$ */
/* WARNING! This file was generated by keywords.sh */
@@ -66,6 +66,7 @@ enum {
K_SA,
K_SENDPIPE,
K_SHOW,
+ K_SOURCE,
K_SSTHRESH,
K_STATIC,
K_SWAP,
@@ -129,6 +130,7 @@ struct keytab keywords[] = {
{ "sa", K_SA },
{ "sendpipe", K_SENDPIPE },
{ "show", K_SHOW },
+ { "source", K_SOURCE },
{ "ssthresh", K_SSTHRESH },
{ "static", K_STATIC },
{ "swap", K_SWAP },
Index: sbin/route/keywords.sh
===================================================================
RCS file: /cvs/src/sbin/route/keywords.sh,v
retrieving revision 1.32
diff -u -p -r1.32 keywords.sh
--- sbin/route/keywords.sh 10 Aug 2017 13:44:48 -0000 1.32
+++ sbin/route/keywords.sh 17 Sep 2020 09:59:25 -0000
@@ -67,6 +67,7 @@ rttvar
sa
sendpipe
show
+source
ssthresh
static
swap
Index: sbin/route/route.8
===================================================================
RCS file: /cvs/src/sbin/route/route.8,v
retrieving revision 1.91
diff -u -p -r1.91 route.8
--- sbin/route/route.8 19 Jan 2020 18:22:31 -0000 1.91
+++ sbin/route/route.8 17 Sep 2020 09:59:25 -0000
@@ -195,6 +195,17 @@ or
.Cm bgp .
If the priority is negative, then routes that do not match the numeric
priority are shown.
+.It Xo
+.Nm route
+.Op Fl T Ar rtable
+.Tg
+.Cm source
+.Ar address
+.Xc
+Set the preferred source address. If
+.Ar address
+is the word "default", 0.0.0.0 or ::, source address will be chosen by
+the kernel for the matching address family.
.El
.Pp
.Tg destination
Index: sbin/route/route.c
===================================================================
RCS file: /cvs/src/sbin/route/route.c,v
retrieving revision 1.248
diff -u -p -r1.248 route.c
--- sbin/route/route.c 7 Jul 2020 14:53:36 -0000 1.248
+++ sbin/route/route.c 17 Sep 2020 09:59:25 -0000
@@ -68,7 +68,8 @@
const struct if_status_description
if_status_descriptions[] = LINK_STATE_DESCRIPTIONS;
-union sockunion so_dst, so_gate, so_mask, so_ifa, so_ifp, so_src, so_label;
+union sockunion so_dst, so_gate, so_mask, so_ifa, so_ifp, so_src, so_label,
+ so_source;
typedef union sockunion *sup;
pid_t pid;
@@ -85,6 +86,7 @@ struct rt_metrics rt_metrics;
int flushroutes(int, char **);
int newroute(int, char **);
+int setsource(int, char **);
int show(int, char *[]);
int keycmp(const void *, const void *);
int keyword(char *);
@@ -132,7 +134,8 @@ usage(char *cp)
"usage: %s [-dnqtv] [-T rtable] command [[modifiers] args]\n",
__progname);
fprintf(stderr,
- "commands: add, change, delete, exec, flush, get, monitor, show\n");
+ "commands: add, change, delete, exec, flush, get, monitor, show, "
+ "source\n");
exit(1);
}
@@ -258,6 +261,10 @@ main(int argc, char **argv)
case K_FLUSH:
exit(flushroutes(argc, argv));
break;
+ case K_SOURCE:
+ nflag = 1;
+ exit(setsource(argc, argv));
+ break;
}
if (pledge("stdio dns", NULL) == -1)
@@ -450,6 +457,52 @@ set_metric(char *value, int key)
locking = 0;
}
+
+int
+setsource(int argc, char **argv)
+{
+ char *cmd, *srcaddr = "";
+ int af = AF_UNSPEC, ret = 0;
+ struct hostent *hp = NULL;
+ int key;
+
+ if (uid)
+ errx(1, "must be root to alter source address");
+ cmd = argv[0];
+ while (--argc > 0) {
+ if (**(++argv)== '-') {
+ switch (key = keyword(1 + *argv)) {
+ case K_INET:
+ af = AF_INET;
+ aflen = sizeof(struct sockaddr_in);
+ break;
+ case K_INET6:
+ af = AF_INET6;
+ aflen = sizeof(struct sockaddr_in6);
+ break;
+ }
+ } else if ((rtm_addrs & RTA_IFA) == 0) {
+ srcaddr = *argv;
+ getaddr(RTA_IFA, af, *argv, &hp);
+ } else
+ usage(NULL);
+ }
+
+ if (strlen(srcaddr) == 0)
+ printsource(af, tableid);
+
+ errno = 0;
+ ret = rtmsg(*cmd, 0, 0, 0);
+ if (!qflag && ret != 0) {
+ printf("%s %s", cmd, srcaddr);
+ if (ret == 0)
+ printf("\n");
+ else
+ printf(": %s\n", strerror(errno));
+ }
+ return (ret != 0);
+}
+
int
newroute(int argc, char **argv)
{
@@ -1067,6 +1120,8 @@ rtmsg(int cmd, int flags, int fmask, uin
so_ifp.sa.sa_len = sizeof(struct sockaddr_dl);
rtm_addrs |= RTA_IFP;
}
+ } else if (cmd == 's') {
+ cmd = RTM_SOURCE;
} else
cmd = RTM_DELETE;
#define rtm m_rtmsg.m_rtm
Index: sbin/route/show.c
===================================================================
RCS file: /cvs/src/sbin/route/show.c,v
retrieving revision 1.115
diff -u -p -r1.115 show.c
--- sbin/route/show.c 15 Sep 2020 20:28:27 -0000 1.115
+++ sbin/route/show.c 17 Sep 2020 09:59:25 -0000
@@ -37,6 +37,7 @@
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
+#include <net/rtable.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netmpls/mpls.h>
@@ -130,6 +131,55 @@ get_sysctl(const int *mib, u_int mcnt, c
return needed;
}
+/*
+ * Print preferred source address
+ */
+void
+printsource(int af, u_int tableid)
+{
+ struct sockaddr *sa;
+ char *buf = NULL, *next, *lim = NULL;
+ size_t needed;
+ int mib[7], mcnt, size;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = af;
+ mib[4] = NET_RT_SOURCE;
+ mib[5] = tableid;
+ mcnt = 6;
+
+ needed = get_sysctl(mib, mcnt, &buf);
+ lim = buf + needed;
+
+ if (pledge("stdio", NULL) == -1)
+ err(1, "pledge");
+
+ printf("Preferred source address set for rdomain %d\n", tableid);
+
+ if (buf) {
+ for (next = buf; next < lim; next += size) {
+ sa = (struct sockaddr *)next;
+ switch (sa->sa_family) {
+ case AF_INET:
+ size = sizeof(struct sockaddr_in);
+ printf("IPv4: ");
+ break;
+ case AF_INET6:
+ size = sizeof(struct sockaddr_in6);
+ printf("IPv6: ");
+ break;
+ }
+ p_sockaddr(sa, NULL, RTF_HOST, WID_DST(sa->sa_family));
+ printf("\n");
+ }
+ }
+ free(buf);
+ printf("\n");
+
+ exit(0);
+}
/*
* Print routing tables.
*/
Index: sbin/route/show.h
===================================================================
RCS file: /cvs/src/sbin/route/show.h,v
retrieving revision 1.15
diff -u -p -r1.15 show.h
--- sbin/route/show.h 31 Aug 2019 13:46:14 -0000 1.15
+++ sbin/route/show.h 17 Sep 2020 09:59:25 -0000
@@ -29,6 +29,7 @@ union sockunion {
struct sockaddr_storage padding;
};
+void printsource(int, u_int);
void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
void p_rttables(int, u_int, char);
void p_sockaddr(struct sockaddr *, struct sockaddr *, int, int);
Index: sys/kern/kern_pledge.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_pledge.c,v
retrieving revision 1.266
diff -u -p -r1.266 kern_pledge.c
--- sys/kern/kern_pledge.c 16 Sep 2020 13:50:42 -0000 1.266
+++ sys/kern/kern_pledge.c 17 Sep 2020 09:59:27 -0000
@@ -828,7 +828,7 @@ pledge_sysctl(struct proc *p, int miblen
mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
mib[2] == 0 &&
(mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) &&
- mib[4] == NET_RT_TABLE)
+ (mib[4] == NET_RT_TABLE || mib[4] == NET_RT_SOURCE))
return (0);
if (miblen == 7 && /* exposes MACs */
Index: sys/net/art.h
===================================================================
RCS file: /cvs/src/sys/net/art.h,v
retrieving revision 1.18
diff -u -p -r1.18 art.h
--- sys/net/art.h 31 Mar 2019 14:03:40 -0000 1.18
+++ sys/net/art.h 17 Sep 2020 09:59:27 -0000
@@ -21,6 +21,7 @@
#include <sys/rwlock.h>
#include <sys/srp.h>
+#include <netinet/in.h>
#define ART_MAXLVL 32 /* We currently use 32 levels for IPv6. */
@@ -35,6 +36,7 @@ struct art_root {
uint8_t ar_alen; /* Address length in bits */
uint8_t ar_off; /* Offset of the key in bytes */
unsigned int ar_rtableid; /* ID of this routing table */
+ struct sockaddr *source; /* optionnal src addr to use */
};
#define ISLEAF(e) (((unsigned long)(e) & 1) == 0)
Index: sys/net/route.c
===================================================================
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.396
diff -u -p -r1.396 route.c
--- sys/net/route.c 13 Aug 2020 04:26:11 -0000 1.396
+++ sys/net/route.c 17 Sep 2020 09:59:27 -0000
@@ -1192,6 +1192,7 @@ 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) {
rtm_send(rt, RTM_DELETE, 0, rdomain);
Index: sys/net/route.h
===================================================================
RCS file: /cvs/src/sys/net/route.h,v
retrieving revision 1.182
diff -u -p -r1.182 route.h
--- sys/net/route.h 13 Aug 2020 04:58:22 -0000 1.182
+++ sys/net/route.h 17 Sep 2020 09:59:27 -0000
@@ -238,6 +238,7 @@ struct rt_msghdr {
#define RTM_PROPOSAL 0x13 /* proposal for netconfigd */
#define RTM_CHGADDRATTR 0x14 /* address attribute change */
#define RTM_80211INFO 0x15 /* 80211 iface change */
+#define RTM_SOURCE 0x16 /* set source address */
#define RTV_MTU 0x1 /* init or lock _mtu */
#define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */
Index: sys/net/rtable.c
===================================================================
RCS file: /cvs/src/sys/net/rtable.c,v
retrieving revision 1.69
diff -u -p -r1.69 rtable.c
--- sys/net/rtable.c 21 Jun 2019 17:11:42 -0000 1.69
+++ sys/net/rtable.c 17 Sep 2020 09:59:27 -0000
@@ -31,6 +31,10 @@
#include <net/rtable.h>
#include <net/route.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_var.h>
+
/*
* Structures used by rtable_get() to retrieve the corresponding
* routing table for a given pair of ``af'' and ``rtableid''.
@@ -363,6 +367,79 @@ void *
rtable_alloc(unsigned int rtableid, unsigned int alen, unsigned int off)
{
return (art_alloc(rtableid, alen, off));
+}
+
+int
+rtable_setsource(unsigned int rtableid, struct sockaddr *src)
+{
+ struct art_root *ar;
+
+ if ((ar = rtable_get(rtableid, src->sa_family)) == NULL)
+ return (EAFNOSUPPORT);
+
+ /*
+ * Check if source address is assigned to an interface in the
+ * same rdomain
+ */
+ if (ifa_ifwithaddr(src, rtableid) == NULL) {
+ /* Check if source address is 0.0.0.0 or :: */
+ switch(src->sa_family) {
+ case AF_INET:
+ if(((struct sockaddr_in *)
+ src->sa_data)->sin_addr.s_addr != INADDR_ANY)
+ return (EINVAL);
+ break;
+ case AF_INET6:
+ if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)
+ src->sa_data)->sin6_addr))
+ return (EINVAL);
+ break;
+ default:
+ return (EAFNOSUPPORT);
+ }
+ src = NULL;
+ }
+
+ if (ar->source) {
+ free(ar->source, M_IFADDR, ar->source->sa_len);
+ ar->source = NULL;
+ }
+
+ if (src) {
+ ar->source = malloc(src->sa_len, M_IFADDR, M_WAITOK|M_ZERO);
+ memcpy(ar->source, src, src->sa_len);
+ }
+ else
+ ar->source = NULL;
+
+ 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) {
+ memset(addr->sa_data, 0, addr->sa_len-
+ sizeof(addr->sa_len)-sizeof(addr->sa_family));
+ rtable_setsource(rtableid, addr);
+ }
+ }
}
struct rtentry *
Index: sys/net/rtable.h
===================================================================
RCS file: /cvs/src/sys/net/rtable.h,v
retrieving revision 1.24
diff -u -p -r1.24 rtable.h
--- sys/net/rtable.h 21 Jun 2019 17:11:42 -0000 1.24
+++ sys/net/rtable.h 17 Sep 2020 09:59:27 -0000
@@ -39,6 +39,9 @@ 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, 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: sys/net/rtsock.c
===================================================================
RCS file: /cvs/src/sys/net/rtsock.c,v
retrieving revision 1.300
diff -u -p -r1.300 rtsock.c
--- sys/net/rtsock.c 13 Aug 2020 04:58:22 -0000 1.300
+++ sys/net/rtsock.c 17 Sep 2020 09:59:27 -0000
@@ -657,7 +657,10 @@ 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] = rt->rt_ifa->ifa_addr;
+ 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;
if (ifp->if_flags & IFF_POINTOPOINT)
info.rti_info[RTAX_BRD] = rt->rt_ifa->ifa_dstaddr;
}
@@ -691,6 +694,7 @@ route_output(struct mbuf *m, struct sock
struct rt_msghdr *rtm = NULL;
struct rtentry *rt = NULL;
struct rt_addrinfo info;
+ struct ifnet *ifp;
int len, seq, error = 0;
u_int tableid;
u_int8_t prio;
@@ -733,6 +737,7 @@ route_output(struct mbuf *m, struct sock
case RTM_GET:
case RTM_CHANGE:
case RTM_PROPOSAL:
+ case RTM_SOURCE:
break;
default:
error = EOPNOTSUPP;
@@ -771,7 +776,6 @@ route_output(struct mbuf *m, struct sock
}
}
-
/* Do not let userland play with kernel-only flags. */
if ((rtm->rtm_flags & (RTF_LOCAL|RTF_BROADCAST)) != 0) {
error = EINVAL;
@@ -802,9 +806,12 @@ route_output(struct mbuf *m, struct sock
if ((error = rtm_xaddrs(rtm->rtm_hdrlen + (caddr_t)rtm,
len + (caddr_t)rtm, &info)) != 0)
goto fail;
+
info.rti_flags = rtm->rtm_flags;
- if (rtm->rtm_type != RTM_PROPOSAL &&
- (info.rti_info[RTAX_DST] == NULL ||
+
+ if (rtm->rtm_type != RTM_SOURCE &&
+ rtm->rtm_type != RTM_PROPOSAL &&
+ (info.rti_info[RTAX_DST] == NULL ||
info.rti_info[RTAX_DST]->sa_family >= AF_MAX ||
(info.rti_info[RTAX_GATEWAY] != NULL &&
info.rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX) ||
@@ -836,13 +843,20 @@ route_output(struct mbuf *m, struct sock
* umb(4) will send a response to this event.
*/
if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
- struct ifnet *ifp;
NET_LOCK();
TAILQ_FOREACH(ifp, &ifnet, if_list) {
ifp->if_rtrequest(ifp, RTM_PROPOSAL, NULL);
}
NET_UNLOCK();
}
+ } else if (rtm->rtm_type == RTM_SOURCE) {
+ if (info.rti_info[RTAX_IFA] == NULL) {
+ error = EINVAL;
+ goto fail;
+ }
+ if ((error =
+ rtable_setsource(tableid, info.rti_info[RTAX_IFA])) != 0)
+ goto fail;
} else {
error = rtm_output(rtm, &rt, &info, prio, tableid);
if (!error) {
@@ -1666,7 +1680,10 @@ 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] = rt->rt_ifa->ifa_addr;
+ 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;
}
rtm_miss(cmd, &info, rt->rt_flags, rt->rt_priority, rt->rt_ifidx, error,
@@ -1904,7 +1921,10 @@ 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] = rt->rt_ifa->ifa_addr;
+ 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;
if (ifp->if_flags & IFF_POINTOPOINT)
info.rti_info[RTAX_BRD] = rt->rt_ifa->ifa_dstaddr;
}
@@ -2040,6 +2060,32 @@ sysctl_ifnames(struct walkarg *w)
}
int
+sysctl_source(int af, u_int tableid, struct walkarg *w)
+{
+ struct sockaddr *sa;
+ int size, error = 0;
+
+ sa = rtable_getsource(tableid, af);
+ if (sa) {
+ switch (sa->sa_family) {
+ case AF_INET:
+ size = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ size = sizeof(struct sockaddr_in6);
+ break;
+ }
+ w->w_needed += size;
+ if (w->w_where && w->w_needed <= 0) {
+ if ((error = copyout(sa, w->w_where, size)))
+ return (error);
+ w->w_where += size;
+ }
+ }
+ return (0);
+}
+
+int
sysctl_rtable(int *name, u_int namelen, void *where, size_t *given, void *new,
size_t newlen)
{
@@ -2107,6 +2153,23 @@ sysctl_rtable(int *name, u_int namelen,
case NET_RT_IFNAMES:
NET_LOCK();
error = sysctl_ifnames(&w);
+ NET_UNLOCK();
+ break;
+ case NET_RT_SOURCE:
+ tableid = w.w_arg;
+ 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;
+ }
NET_UNLOCK();
break;
}
Index: sys/netinet/in_pcb.c
===================================================================
RCS file: /cvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.249
diff -u -p -r1.249 in_pcb.c
--- sys/netinet/in_pcb.c 27 May 2020 20:44:07 -0000 1.249
+++ sys/netinet/in_pcb.c 17 Sep 2020 09:59:27 -0000
@@ -887,6 +887,7 @@ 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;
@@ -923,6 +924,7 @@ in_pcbselsrc(struct in_addr **insrc, str
return (0);
}
}
+
/*
* If route is known or can be allocated now,
* our src addr is taken from the i/f, else punt.
@@ -947,12 +949,34 @@ in_pcbselsrc(struct in_addr **insrc, str
sin2 = satosin(&ro->ro_dst);
memset(sin2->sin_zero, 0, sizeof(sin2->sin_zero));
}
+
/*
* If we found a route, use the address
* corresponding to the outgoing interface.
*/
if (ro->ro_rt != NULL)
ia = ifatoia(ro->ro_rt->rt_ifa);
+
+ /*
+ * Use preferred source address if :
+ * - destination is not onlink
+ * - output interface is not PtoP
+ * - preferred source addresss is set
+ * - output interface is UP
+ */
+ if ((ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO)) &&
+ (ia && !(ia->ia_ifp->if_flags & IFF_POINTOPOINT))) {
+ 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 = &((struct sockaddr_in *)
+ ip4_source)->sin_addr;
+ return (0);
+ }
+ }
+ }
if (ia == NULL)
return (EADDRNOTAVAIL);
Index: sys/netinet6/in6_src.c
===================================================================
RCS file: /cvs/src/sys/netinet6/in6_src.c,v
retrieving revision 1.81
diff -u -p -r1.81 in6_src.c
--- sys/netinet6/in6_src.c 2 Dec 2016 11:16:04 -0000 1.81
+++ sys/netinet6/in6_src.c 17 Sep 2020 09:59:27 -0000
@@ -100,6 +100,7 @@ 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;
@@ -215,6 +216,28 @@ in6_pcbselsrc(struct in6_addr **in6src,
if (ia6 == NULL) /* xxx scope error ?*/
ia6 = ifatoia6(ro->ro_rt->rt_ifa);
}
+
+ /*
+ * Use preferred source address if :
+ * - destination is not onlink
+ * - output interface is not PtoP
+ * - preferred source addresss is set
+ * - output interface is UP
+ */
+ if ((ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO)) &&
+ (ia6 && !(ia6->ia_ifp->if_flags & IFF_POINTOPOINT))) {
+ 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 = &((struct sockaddr_in6 *)
+ ip6_source)->sin6_addr;
+ return (0);
+ }
+ }
+ }
+
if (ia6 == NULL)
return (EHOSTUNREACH); /* no route */
Index: sys/sys/socket.h
===================================================================
RCS file: /cvs/src/sys/sys/socket.h,v
retrieving revision 1.98
diff -u -p -r1.98 socket.h
--- sys/sys/socket.h 22 Jul 2019 15:34:07 -0000 1.98
+++ sys/sys/socket.h 17 Sep 2020 09:59:27 -0000
@@ -368,7 +368,8 @@ struct sockpeercred {
#define NET_RT_STATS 4 /* routing table statistics */
#define NET_RT_TABLE 5
#define NET_RT_IFNAMES 6
-#define NET_RT_MAXID 7
+#define NET_RT_SOURCE 7
+#define NET_RT_MAXID 8
#define CTL_NET_RT_NAMES { \
{ 0, 0 }, \
@@ -378,6 +379,7 @@ struct sockpeercred {
{ "stats", CTLTYPE_STRUCT }, \
{ "table", CTLTYPE_STRUCT }, \
{ "ifnames", CTLTYPE_STRUCT }, \
+ { "source", CTLTYPE_STRUCT }, \
}
/*