summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sbin/route/keywords.h4
-rw-r--r--sbin/route/keywords.sh3
-rw-r--r--sbin/route/route.844
-rw-r--r--sbin/route/route.c104
-rw-r--r--sbin/route/show.c52
-rw-r--r--sbin/route/show.h3
-rw-r--r--sys/kern/kern_pledge.c4
-rw-r--r--sys/net/art.h4
-rw-r--r--sys/net/route.c3
-rw-r--r--sys/net/route.h3
-rw-r--r--sys/net/rtable.c80
-rw-r--r--sys/net/rtable.h5
-rw-r--r--sys/net/rtsock.c79
-rw-r--r--sys/netinet/in_pcb.c26
-rw-r--r--sys/netinet6/in6_src.c25
-rw-r--r--sys/sys/socket.h6
16 files changed, 417 insertions, 28 deletions
diff --git a/sbin/route/keywords.h b/sbin/route/keywords.h
index 5d21d86d4d1..0fd50932596 100644
--- a/sbin/route/keywords.h
+++ b/sbin/route/keywords.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: keywords.h,v 1.34 2017/08/10 13:44:48 benno Exp $ */
+/* $OpenBSD: keywords.h,v 1.35 2020/10/29 21:15:26 denis Exp $ */
/* WARNING! This file was generated by keywords.sh */
@@ -66,6 +66,7 @@ enum {
K_SA,
K_SENDPIPE,
K_SHOW,
+ K_SOURCEADDR,
K_SSTHRESH,
K_STATIC,
K_SWAP,
@@ -129,6 +130,7 @@ struct keytab keywords[] = {
{ "sa", K_SA },
{ "sendpipe", K_SENDPIPE },
{ "show", K_SHOW },
+ { "sourceaddr", K_SOURCEADDR },
{ "ssthresh", K_SSTHRESH },
{ "static", K_STATIC },
{ "swap", K_SWAP },
diff --git a/sbin/route/keywords.sh b/sbin/route/keywords.sh
index 2d8d16a65bb..369f1b08aaa 100644
--- a/sbin/route/keywords.sh
+++ b/sbin/route/keywords.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# $OpenBSD: keywords.sh,v 1.32 2017/08/10 13:44:48 benno Exp $
+# $OpenBSD: keywords.sh,v 1.33 2020/10/29 21:15:26 denis Exp $
# $NetBSD: keywords.sh,v 1.2 1996/11/15 18:57:21 gwr Exp $
# @(#)keywords 8.2 (Berkeley) 3/19/94
#
@@ -67,6 +67,7 @@ rttvar
sa
sendpipe
show
+sourceaddr
ssthresh
static
swap
diff --git a/sbin/route/route.8 b/sbin/route/route.8
index 8f95eef343d..87517f0cb1f 100644
--- a/sbin/route/route.8
+++ b/sbin/route/route.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: route.8,v 1.91 2020/01/19 18:22:31 schwarze Exp $
+.\" $OpenBSD: route.8,v 1.92 2020/10/29 21:15:26 denis Exp $
.\" $NetBSD: route.8,v 1.6 1995/03/18 15:00:13 cgd Exp $
.\"
.\" Copyright (c) 1983, 1991, 1993
@@ -30,7 +30,7 @@
.\"
.\" @(#)route.8 8.3 (Berkeley) 3/19/94
.\"
-.Dd $Mdocdate: January 19 2020 $
+.Dd $Mdocdate: October 29 2020 $
.Dt ROUTE 8
.Os
.Sh NAME
@@ -197,6 +197,46 @@ If the priority is negative, then routes that do not match the numeric
priority are shown.
.El
.Pp
+.Bl -tag -width Fl -compact
+.It Xo
+.Ic route
+.Op Fl T Ar rtable
+.Tg
+.Cm sourceaddr
+.Op Fl inet Ns | Ns Fl inet6
+.Ar address
+.Xc
+.It Xo
+.Ic route
+.Op Fl T Ar rtable
+.Tg
+.Cm sourceaddr
+.Op Fl inet Ns | Ns Fl inet6
+.Fl ifp
+.Ar interface
+.Xc
+.Pp
+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.
+If
+.Fl ifp
+is used, source will use IP assigned to
+.Ar interface .
+The preferred source will not be used when:
+.Pp
+.Bl -bullet -compact
+.It
+destination is on-link
+.It
+output interface is point-to-point
+.It
+source address is assigned to a disabled interface
+.El
+.El
+.Pp
.Tg destination
.Tg gateway
The other commands relating to adding, changing, or deleting routes
diff --git a/sbin/route/route.c b/sbin/route/route.c
index f1ec5bd38d8..85646bb9dd9 100644
--- a/sbin/route/route.c
+++ b/sbin/route/route.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: route.c,v 1.248 2020/07/07 14:53:36 yasuoka Exp $ */
+/* $OpenBSD: route.c,v 1.249 2020/10/29 21:15:26 denis Exp $ */
/* $NetBSD: route.c,v 1.16 1996/04/15 18:27:05 cgd Exp $ */
/*
@@ -49,6 +49,7 @@
#include <arpa/inet.h>
#include <netdb.h>
+#include <ifaddrs.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
@@ -68,7 +69,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 +87,8 @@ struct rt_metrics rt_metrics;
int flushroutes(int, char **);
int newroute(int, char **);
+int setsource(int, char **);
+int pushsrc(int, char *, int);
int show(int, char *[]);
int keycmp(const void *, const void *);
int keyword(char *);
@@ -132,7 +136,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, "
+ "sourceaddr\n");
exit(1);
}
@@ -258,6 +263,10 @@ main(int argc, char **argv)
case K_FLUSH:
exit(flushroutes(argc, argv));
break;
+ case K_SOURCEADDR:
+ nflag = 1;
+ exit(setsource(argc, argv));
+ break;
}
if (pledge("stdio dns", NULL) == -1)
@@ -450,6 +459,92 @@ set_metric(char *value, int key)
locking = 0;
}
+
+int
+setsource(int argc, char **argv)
+{
+ struct ifaddrs *ifap, *ifa = NULL;
+ char *cmd;
+ int af = AF_UNSPEC, ret = 0, key;
+ unsigned int ifindex = 0;
+
+ 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;
+ case K_IFP:
+ if (!--argc)
+ usage(1+*argv);
+ ifindex = if_nametoindex(*++argv);
+ if (ifindex == 0)
+ errx(1, "no such interface %s", *argv);
+ break;
+ }
+ } else
+ break;
+ }
+
+ if (argc <= 0 && ifindex == 0)
+ printsource(af, tableid);
+ if (argc > 1 && ifindex == 0)
+ usage(NULL);
+
+ if (uid)
+ errx(1, "must be root to alter source address");
+
+ if (ifindex) {
+ if (getifaddrs(&ifap) == -1)
+ err(1, "getifaddrs");
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (if_nametoindex(ifa->ifa_name) != ifindex)
+ continue;
+ if (!(ifa->ifa_addr->sa_family == AF_INET ||
+ ifa->ifa_addr->sa_family == AF_INET6))
+ continue;
+ if ((af != AF_UNSPEC) &&
+ (ifa->ifa_addr->sa_family != af))
+ continue;
+ if (ifa->ifa_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 =
+ (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ continue;
+ }
+ if (pushsrc(*cmd, routename(ifa->ifa_addr),
+ ifa->ifa_addr->sa_family))
+ break;
+ }
+ freeifaddrs(ifap);
+ } else {
+ ret = pushsrc(*cmd, *argv, af);
+ }
+
+ return (ret != 0);
+}
+
+int
+pushsrc(int cmd, char *src, int af)
+{
+ int ret = 0;
+
+ getaddr(RTA_IFA, af, src, NULL);
+
+ errno = 0;
+ ret = rtmsg(cmd, 0, 0, 0);
+ if (!qflag && ret != 0)
+ printf("sourceaddr %s: %s\n", src, strerror(errno));
+
+ return (ret);
+}
int
newroute(int argc, char **argv)
{
@@ -830,6 +925,7 @@ getaddr(int which, int af, char *s, struct hostent **hpp)
errx(1, "internal error");
/* NOTREACHED */
}
+ memset(su, 0, sizeof(union sockunion));
su->sa.sa_len = aflength;
su->sa.sa_family = afamily;
@@ -1067,6 +1163,8 @@ rtmsg(int cmd, int flags, int fmask, uint8_t prio)
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
diff --git a/sbin/route/show.c b/sbin/route/show.c
index a3ebb7d3f9f..6aae8655dc8 100644
--- a/sbin/route/show.c
+++ b/sbin/route/show.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: show.c,v 1.115 2020/09/15 20:28:27 pamela Exp $ */
+/* $OpenBSD: show.c,v 1.116 2020/10/29 21:15:26 denis Exp $ */
/* $NetBSD: show.c,v 1.1 1996/11/15 18:01:41 gwr Exp $ */
/*
@@ -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>
@@ -131,6 +132,55 @@ get_sysctl(const int *mib, u_int mcnt, char **buf)
}
/*
+ * 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.
*/
void
diff --git a/sbin/route/show.h b/sbin/route/show.h
index 6108f3d7994..f452444a783 100644
--- a/sbin/route/show.h
+++ b/sbin/route/show.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: show.h,v 1.15 2019/08/31 13:46:14 bluhm Exp $ */
+/* $OpenBSD: show.h,v 1.16 2020/10/29 21:15:26 denis Exp $ */
/*
* Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -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);
diff --git a/sys/kern/kern_pledge.c b/sys/kern/kern_pledge.c
index db35de5babc..e6f37fd3645 100644
--- a/sys/kern/kern_pledge.c
+++ b/sys/kern/kern_pledge.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_pledge.c,v 1.266 2020/09/16 13:50:42 mpi Exp $ */
+/* $OpenBSD: kern_pledge.c,v 1.267 2020/10/29 21:15:27 denis Exp $ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
@@ -828,7 +828,7 @@ pledge_sysctl(struct proc *p, int miblen, int *mib, void *new)
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 */
diff --git a/sys/net/art.h b/sys/net/art.h
index f7cb914d867..b53974edf10 100644
--- a/sys/net/art.h
+++ b/sys/net/art.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: art.h,v 1.18 2019/03/31 14:03:40 mpi Exp $ */
+/* $OpenBSD: art.h,v 1.19 2020/10/29 21:15:27 denis Exp $ */
/*
* Copyright (c) 2015 Martin Pieuchot
@@ -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; /* optional src addr to use */
};
#define ISLEAF(e) (((unsigned long)(e) & 1) == 0)
diff --git a/sys/net/route.c b/sys/net/route.c
index 0bbd5d3064e..99c3671d80b 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: route.c,v 1.396 2020/08/13 04:26:11 jmatthew Exp $ */
+/* $OpenBSD: route.c,v 1.397 2020/10/29 21:15:27 denis Exp $ */
/* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */
/*
@@ -1192,6 +1192,7 @@ rt_ifa_del(struct ifaddr *ifa, int flags, struct sockaddr *dst,
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);
diff --git a/sys/net/route.h b/sys/net/route.h
index 25e486cabf4..492edcfcb6a 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: route.h,v 1.182 2020/08/13 04:58:22 jmatthew Exp $ */
+/* $OpenBSD: route.h,v 1.183 2020/10/29 21:15:27 denis Exp $ */
/* $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $ */
/*
@@ -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 */
diff --git a/sys/net/rtable.c b/sys/net/rtable.c
index ec57747f447..c176d17cf79 100644
--- a/sys/net/rtable.c
+++ b/sys/net/rtable.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rtable.c,v 1.69 2019/06/21 17:11:42 mpi Exp $ */
+/* $OpenBSD: rtable.c,v 1.70 2020/10/29 21:15:27 denis Exp $ */
/*
* Copyright (c) 2014-2016 Martin Pieuchot
@@ -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''.
@@ -365,6 +369,80 @@ 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) {
+ /*
+ * If source address is 0.0.0.0 or ::
+ * use automatic source selection
+ */
+ 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);
+ }
+
+ 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 *
rtable_lookup(unsigned int rtableid, struct sockaddr *dst,
struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio)
diff --git a/sys/net/rtable.h b/sys/net/rtable.h
index c492d684739..0686d0c75a2 100644
--- a/sys/net/rtable.h
+++ b/sys/net/rtable.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rtable.h,v 1.24 2019/06/21 17:11:42 mpi Exp $ */
+/* $OpenBSD: rtable.h,v 1.25 2020/10/29 21:15:27 denis Exp $ */
/*
* Copyright (c) 2014-2016 Martin Pieuchot
@@ -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 *);
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
index 71b15a90441..bed2efff028 100644
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rtsock.c,v 1.302 2020/09/23 17:52:58 mvs Exp $ */
+/* $OpenBSD: rtsock.c,v 1.303 2020/10/29 21:15:27 denis Exp $ */
/* $NetBSD: rtsock.c,v 1.18 1996/03/29 00:32:10 cgd Exp $ */
/*
@@ -662,7 +662,10 @@ rtm_report(struct rtentry *rt, u_char type, int seq, int tableid)
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;
}
@@ -696,6 +699,7 @@ route_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr,
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;
@@ -738,6 +742,7 @@ route_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr,
case RTM_GET:
case RTM_CHANGE:
case RTM_PROPOSAL:
+ case RTM_SOURCE:
break;
default:
error = EOPNOTSUPP;
@@ -776,7 +781,6 @@ route_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr,
}
}
-
/* Do not let userland play with kernel-only flags. */
if ((rtm->rtm_flags & (RTF_LOCAL|RTF_BROADCAST)) != 0) {
error = EINVAL;
@@ -807,9 +811,12 @@ route_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr,
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) ||
@@ -841,13 +848,20 @@ route_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr,
* 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) {
@@ -1671,7 +1685,10 @@ rtm_send(struct rtentry *rt, int cmd, int error, unsigned int rtableid)
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,
@@ -1909,7 +1926,10 @@ sysctl_dumpentry(struct rtentry *rt, void *v, unsigned int id)
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;
}
@@ -2045,6 +2065,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)
{
@@ -2114,6 +2160,23 @@ sysctl_rtable(int *name, u_int namelen, void *where, size_t *given, void *new,
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;
}
free(w.w_tmem, M_RTABLE, w.w_tmemsize);
w.w_needed += w.w_given;
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index df5e08fb91f..52d4b8c9e40 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.c,v 1.249 2020/05/27 20:44:07 bluhm Exp $ */
+/* $OpenBSD: in_pcb.c,v 1.250 2020/10/29 21:15:27 denis Exp $ */
/* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */
/*
@@ -887,6 +887,7 @@ in_pcbselsrc(struct in_addr **insrc, struct sockaddr_in *sin,
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, struct sockaddr_in *sin,
return (0);
}
}
+
/*
* If route is known or can be allocated now,
* our src addr is taken from the i/f, else punt.
@@ -947,6 +949,7 @@ in_pcbselsrc(struct in_addr **insrc, struct sockaddr_in *sin,
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.
@@ -954,6 +957,27 @@ in_pcbselsrc(struct in_addr **insrc, struct sockaddr_in *sin,
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);
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
index 419ca22a430..7bdfdb9d33e 100644
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_src.c,v 1.81 2016/12/02 11:16:04 mpi Exp $ */
+/* $OpenBSD: in6_src.c,v 1.82 2020/10/29 21:15:27 denis Exp $ */
/* $KAME: in6_src.c,v 1.36 2001/02/06 04:08:17 itojun Exp $ */
/*
@@ -100,6 +100,7 @@ in6_pcbselsrc(struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
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, struct sockaddr_in6 *dstsock,
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 */
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
index 927fd26ed92..f19ed5994a8 100644
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: socket.h,v 1.98 2019/07/22 15:34:07 robert Exp $ */
+/* $OpenBSD: socket.h,v 1.99 2020/10/29 21:15:27 denis Exp $ */
/* $NetBSD: socket.h,v 1.14 1996/02/09 18:25:36 christos Exp $ */
/*
@@ -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 }, \
}
/*