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 }, \
 }
 
 /*

Reply via email to