diff options
-rw-r--r-- | sbin/route/keywords.h | 4 | ||||
-rw-r--r-- | sbin/route/keywords.sh | 3 | ||||
-rw-r--r-- | sbin/route/route.8 | 44 | ||||
-rw-r--r-- | sbin/route/route.c | 104 | ||||
-rw-r--r-- | sbin/route/show.c | 52 | ||||
-rw-r--r-- | sbin/route/show.h | 3 | ||||
-rw-r--r-- | sys/kern/kern_pledge.c | 4 | ||||
-rw-r--r-- | sys/net/art.h | 4 | ||||
-rw-r--r-- | sys/net/route.c | 3 | ||||
-rw-r--r-- | sys/net/route.h | 3 | ||||
-rw-r--r-- | sys/net/rtable.c | 80 | ||||
-rw-r--r-- | sys/net/rtable.h | 5 | ||||
-rw-r--r-- | sys/net/rtsock.c | 79 | ||||
-rw-r--r-- | sys/netinet/in_pcb.c | 26 | ||||
-rw-r--r-- | sys/netinet6/in6_src.c | 25 | ||||
-rw-r--r-- | sys/sys/socket.h | 6 |
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 }, \ } /* |