Index: sbin/ping/ping.c =================================================================== RCS file: /cvs/src/sbin/ping/ping.c,v diff -u -p -r1.249 ping.c --- sbin/ping/ping.c 23 Apr 2024 13:34:50 -0000 1.249 +++ sbin/ping/ping.c 29 Apr 2025 06:23:11 -0000 @@ -255,7 +255,7 @@ main(int argc, char *argv[]) int df = 0, tos = 0, bufspace = IP_MAXPACKET, hoplimit = -1, mflag = 0; u_char *datap, *packet; u_char ttl = MAXTTL; - char *e, *target, hbuf[NI_MAXHOST], *source = NULL; + char *e, *target, hbuf[NI_MAXHOST], *source = NULL, *rtdst = NULL; char rspace[3 + 4 * NROUTES + 1]; /* record route space */ const char *errstr; double fraction, integral, seconds; @@ -297,8 +297,8 @@ main(int argc, char *argv[]) preload = 0; datap = &outpack[ECHOLEN + ECHOTMLEN]; while ((ch = getopt(argc, argv, v6flag ? - "c:DdEefgHh:I:i:Ll:mNnp:qS:s:T:V:vw:" : - "DEI:LRS:c:defgHi:l:np:qs:T:t:V:vw:")) != -1) { + "c:DdEefG:gHh:I:i:Ll:mNnp:qS:s:T:V:vw:" : + "DEI:LRS:c:defG:gHi:l:np:qs:T:t:V:vw:")) != -1) { switch(ch) { case 'c': npackets = strtonum(optarg, 0, INT64_MAX, &errstr); @@ -326,6 +326,9 @@ main(int argc, char *argv[]) options |= F_FLOOD; setvbuf(stdout, NULL, _IONBF, 0); break; + case 'G': + rtdst = optarg; + break; case 'g': options |= F_SHOWCHAR; break; @@ -435,6 +438,29 @@ main(int argc, char *argv[]) default: usage(); } + } + + if (rtdst) { + int level, optname; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = v6flag ? AF_INET6 : AF_INET; + if ((error = getaddrinfo(rtdst, NULL, &hints, &res))) + errx(1, "route-to %s: %s", rtdst, gai_strerror(error)); + + if (v6flag) { + level = IPPROTO_IPV6; + optname = IPV6_ROUTETO; + } else { + level = IPPROTO_IP; + optname = IP_ROUTETO; + } + + if (setsockopt(s, level, optname, + res->ai_addr, res->ai_addrlen) == -1) + err(1, "setsockopt route-to %s", rtdst); + + freeaddrinfo(res); } if (ouid == 0 && (setgroups(1, &gid) || Index: sys/netinet/in.h =================================================================== RCS file: /cvs/src/sys/netinet/in.h,v diff -u -p -r1.148 in.h --- sys/netinet/in.h 13 Feb 2024 12:22:09 -0000 1.148 +++ sys/netinet/in.h 29 Apr 2025 06:23:18 -0000 @@ -317,6 +317,7 @@ struct ip_opts { #define IP_IPDEFTTL 37 /* int; IP TTL system default */ #define IP_SENDSRCADDR IP_RECVDSTADDR /* struct in_addr; */ /* source address to use */ +#define IP_ROUTETO 38 /* sockaddr_in: override route lookup */ #define IP_RTABLE 0x1021 /* int; routing table, see SO_RTABLE */ Index: sys/netinet/in_pcb.h =================================================================== RCS file: /cvs/src/sys/netinet/in_pcb.h,v diff -u -p -r1.159 in_pcb.h --- sys/netinet/in_pcb.h 5 Nov 2024 10:49:23 -0000 1.159 +++ sys/netinet/in_pcb.h 29 Apr 2025 06:23:18 -0000 @@ -132,10 +132,13 @@ struct inpcb { struct inpcbtable *inp_table; /* [I] inet queue/hash table */ union inpaddru inp_faddru; /* [t] Foreign address. */ union inpaddru inp_laddru; /* [t] Local address. */ + union inpaddru inp_raddru; /* [s] Route-to address. */ #define inp_faddr inp_faddru.iau_addr #define inp_faddr6 inp_faddru.iau_addr6 #define inp_laddr inp_laddru.iau_addr #define inp_laddr6 inp_laddru.iau_addr6 +#define inp_raddr inp_raddru.iau_addr +#define inp_raddr6 inp_raddru.iau_addr6 u_int16_t inp_fport; /* [t] foreign port */ u_int16_t inp_lport; /* [t] local port */ struct socket *inp_socket; /* [I] back pointer to socket */ @@ -222,6 +225,7 @@ struct inpcbtable { #define INP_RECVDSTPORT 0x200 /* receive IP dst addr before rdr */ #define INP_RECVRTABLE 0x400 /* receive routing table */ #define INP_IPSECFLOWINFO 0x800 /* receive IPsec flow info */ +#define INP_ROUTETO 0x1000 #define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR| \ INP_RXSRCRT|INP_HOPLIMIT|INP_RECVIF|INP_RECVTTL|INP_RECVDSTPORT| \ Index: sys/netinet/ip_output.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_output.c,v diff -u -p -r1.401 ip_output.c --- sys/netinet/ip_output.c 2 Jul 2024 18:33:47 -0000 1.401 +++ sys/netinet/ip_output.c 29 Apr 2025 06:23:18 -0000 @@ -858,6 +880,85 @@ ip_optcopy(struct ip *ip, struct ip *jp) return (optlen); } +static int +ip_set_routeto(struct inpcb *inp, struct mbuf *m) +{ + struct sockaddr_in *sin; + + if (m == NULL || m->m_len != sizeof(*sin)) + return (EINVAL); + + sin = mtod(m, struct sockaddr_in *); + + switch (sin->sin_family) { + case AF_UNSPEC: + CLR(inp->inp_flags, INP_ROUTETO); + break; + case AF_INET: + inp->inp_raddr = sin->sin_addr; + SET(inp->inp_flags, INP_ROUTETO); + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +ip_get_routeto(struct inpcb *inp, struct mbuf *m) +{ + struct sockaddr_in *sin; + + if (!ISSET(inp->inp_flags, INP_ROUTETO)) + return (ENOTCONN); + + m->m_len = sizeof(*sin); + sin = mtod(m, struct sockaddr_in *); + + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_addr = inp->inp_raddr; + + return (0); +} + +struct pf_addr * +ip_routeto_addr(struct mbuf *m, struct inpcb *inp) +{ + struct m_tag *mtag; + struct pf_addr *rt_addr; + + KASSERT(ISSET(inp->inp_flags, INP_ROUTETO)); + + mtag = m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL); + if (mtag == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_ROUTE, sizeof(*rt_addr), + M_NOWAIT); + if (mtag == NULL) + return (NULL); + + m_tag_prepend(m, mtag); + } + + rt_addr = (struct pf_addr *)(mtag + 1); + return (rt_addr); +} + +int +ip_routeto(struct mbuf *m, struct inpcb *inp) +{ + struct pf_addr *rt_addr; + + rt_addr = ip_routeto_addr(m, inp); + if (rt_addr == NULL) + return (ENOBUFS); + + rt_addr->v4 = inp->inp_raddr; + + return (0); +} + /* * IP socket option processing. */ @@ -955,6 +1056,15 @@ ip_ctloutput(int op, struct socket *so, break; #undef OPTSET + case IP_ROUTETO: + soassertlocked(so); + if (suser(p)) { + error = EACCES; + break; + } + error = ip_set_routeto(inp, m); + break; + case IP_MULTICAST_IF: case IP_MULTICAST_TTL: case IP_MULTICAST_LOOP: @@ -1156,6 +1266,11 @@ ip_ctloutput(int op, struct socket *so, break; } *mtod(m, int *) = optval; + break; + + case IP_ROUTETO: + soassertlocked(so); + error = ip_get_routeto(inp, m); break; case IP_MULTICAST_IF: Index: sys/netinet/ip_var.h =================================================================== RCS file: /cvs/src/sys/netinet/ip_var.h,v diff -u -p -r1.120 ip_var.h --- sys/netinet/ip_var.h 12 Jul 2024 19:50:35 -0000 1.120 +++ sys/netinet/ip_var.h 29 Apr 2025 06:23:18 -0000 @@ -265,6 +265,9 @@ void ip_savecontrol(struct inpcb *, str int ip_input_if(struct mbuf **, int *, int, int, struct ifnet *); int ip_deliver(struct mbuf **, int *, int, int, int); void ip_forward(struct mbuf *, struct ifnet *, struct route *, int); +struct pf_addr * + ip_routeto_addr(struct mbuf *, struct inpcb *); +int ip_routeto(struct mbuf *, struct inpcb *); int rip_ctloutput(int, struct socket *, int, int, struct mbuf *); void rip_init(void); int rip_input(struct mbuf **, int *, int, int); Index: sys/netinet/raw_ip.c =================================================================== RCS file: /cvs/src/sys/netinet/raw_ip.c,v diff -u -p -r1.162 raw_ip.c --- sys/netinet/raw_ip.c 8 Nov 2024 10:24:13 -0000 1.162 +++ sys/netinet/raw_ip.c 29 Apr 2025 06:23:18 -0000 @@ -325,6 +325,13 @@ rip_output(struct mbuf *m, struct socket #endif /* force routing table */ m->m_pkthdr.ph_rtableid = inp->inp_rtableid; + if (ISSET(inp->inp_flags, INP_ROUTETO)) { + error = ip_routeto(m, inp); + if (error != 0) { + m_freem(m); + return (error); + } + } #if NPF > 0 if (inp->inp_socket->so_state & SS_ISCONNECTED && Index: sys/netinet/tcp_output.c =================================================================== RCS file: /cvs/src/sys/netinet/tcp_output.c,v diff -u -p -r1.145 tcp_output.c --- sys/netinet/tcp_output.c 14 May 2024 09:39:02 -0000 1.145 +++ sys/netinet/tcp_output.c 29 Apr 2025 06:23:18 -0000 @@ -1064,12 +1064,51 @@ send: m->m_pkthdr.ph_rtableid = tp->t_inpcb->inp_rtableid; #if NPF > 0 + if (ISSET(tp->t_inpcb->inp_flags, INP_ROUTETO)) { + struct m_tag *mtag; + struct pf_addr *rt_addr; + + mtag = m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL); + if (mtag == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_ROUTE, sizeof(*rt_addr), + M_NOWAIT); + if (mtag == NULL) { + error = ENOBUFS; + m_freem(m); + goto out; + } + + m_tag_prepend(m, mtag); + } + rt_addr = (struct pf_addr *)(mtag + 1); + + switch (tp->pf) { + case 0: /*default to PF_INET*/ + case AF_INET: + rt_addr->v4 = tp->t_inpcb->inp_raddr; + break; +#ifdef INET6 + case AF_INET6: + rt_addr->v6 = tp->t_inpcb->inp_raddr6; + break; +#endif + } + } + pf_mbuf_link_inpcb(m, tp->t_inpcb); #endif switch (tp->pf) { case 0: /*default to PF_INET*/ case AF_INET: + if (ISSET(tp->t_inpcb->inp_flags, INP_ROUTETO)) { + error = ip_routeto(m, tp->t_inpcb); + if (error != 0) { + m_freem(m); + goto out; + } + } + { struct ip *ip; @@ -1094,6 +1133,14 @@ send: break; #ifdef INET6 case AF_INET6: + if (ISSET(tp->t_inpcb->inp_flags, INP_ROUTETO)) { + error = ip6_routeto(m, tp->t_inpcb); + if (error != 0) { + m_freem(m); + goto out; + } + } + { struct ip6_hdr *ip6; Index: sys/netinet/udp_usrreq.c =================================================================== RCS file: /cvs/src/sys/netinet/udp_usrreq.c,v diff -u -p -r1.327 udp_usrreq.c --- sys/netinet/udp_usrreq.c 5 Nov 2024 22:44:20 -0000 1.327 +++ sys/netinet/udp_usrreq.c 29 Apr 2025 06:23:18 -0000 @@ -1073,6 +1073,11 @@ udp_output(struct inpcb *inp, struct mbu /* force routing table */ m->m_pkthdr.ph_rtableid = inp->inp_rtableid; + if (ISSET(inp->inp_flags, INP_ROUTETO)) { + error = ip_routeto(m, inp); + if (error != 0) + goto release; + } #if NPF > 0 if (inp->inp_socket->so_state & SS_ISCONNECTED) Index: sys/netinet6/in6.h =================================================================== RCS file: /cvs/src/sys/netinet6/in6.h,v diff -u -p -r1.119 in6.h --- sys/netinet6/in6.h 1 Sep 2024 03:09:00 -0000 1.119 +++ sys/netinet6/in6.h 29 Apr 2025 06:23:18 -0000 @@ -313,6 +313,8 @@ extern const struct in6_addr in6addr_lin #define IPV6_PATHMTU 44 /* mtuinfo; get the current path MTU (sopt), 4 bytes int; MTU notification (cmsg) */ +#define IPV6_ROUTETO 45 /* sockaddr_in6; route to */ + /* More new socket options introduced in RFC3542 */ #define IPV6_PKTINFO 46 /* in6_pktinfo; send if, src addr */ #define IPV6_HOPLIMIT 47 /* int; send hop limit */ Index: sys/netinet6/ip6_output.c =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_output.c,v diff -u -p -r1.292 ip6_output.c --- sys/netinet6/ip6_output.c 4 Jul 2024 12:50:08 -0000 1.292 +++ sys/netinet6/ip6_output.c 29 Apr 2025 06:23:18 -0000 @@ -1058,6 +1071,68 @@ ip6_getpmtu(struct rtentry *rt, struct i return (error); } +static int +ip6_set_routeto(struct inpcb *inp, struct mbuf *m) +{ + struct sockaddr_in6 *sin6; + int error = 0; + + if (m == NULL || m->m_len != sizeof(*sin6)) + return (EINVAL); + + sin6 = mtod(m, struct sockaddr_in6 *); + + switch (sin6->sin6_family) { + case AF_UNSPEC: + CLR(inp->inp_flags, INP_ROUTETO); + break; + case AF_INET6: + error = in6_embedscope(&inp->inp_raddr6, sin6, NULL, NULL); + if (error != 0) + break; + + SET(inp->inp_flags, INP_ROUTETO); + break; + default: + error = EINVAL; + break; + } + + return (0); +} + +static int +ip6_get_routeto(struct inpcb *inp, struct mbuf *m) +{ + struct sockaddr_in6 *sin6; + + if (!ISSET(inp->inp_flags, INP_ROUTETO)) + return (ENOTCONN); + + m->m_len = sizeof(*sin6); + sin6 = mtod(m, struct sockaddr_in6 *); + + memset(sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + in6_recoverscope(sin6, &inp->inp_raddr6); + + return (0); +} + +int +ip6_routeto(struct mbuf *m, struct inpcb *inp) +{ + struct pf_addr *rt_addr; + + rt_addr = ip_routeto_addr(m, inp); + if (rt_addr == NULL) + return (ENOBUFS); + + rt_addr->v6 = inp->inp_raddr6; + + return (0); +} + /* * IP6 socket option processing. */ @@ -1140,6 +1215,15 @@ ip6_ctloutput(int op, struct socket *so, inp->inp_ip6_minhlim = optval; break; + case IPV6_ROUTETO: + soassertlocked(so); + if (suser(p)) { + error = EACCES; + break; + } + error = ip6_set_routeto(inp, m); + break; + #define OPTSET(bit) \ do { \ if (optval) \ @@ -1520,6 +1604,10 @@ do { \ bcopy(optdata, mtod(m, void *), optdatalen); break; } + + case IPV6_ROUTETO: + error = ip6_get_routeto(inp, m); + break; case IPV6_PKTINFO: case IPV6_HOPOPTS: Index: sys/netinet6/ip6_var.h =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_var.h,v diff -u -p -r1.120 ip6_var.h --- sys/netinet6/ip6_var.h 12 Jul 2024 19:50:35 -0000 1.120 +++ sys/netinet6/ip6_var.h 29 Apr 2025 06:23:18 -0000 @@ -338,6 +338,7 @@ void ip6_clearpktopts(struct ip6_pktopts void ip6_randomid_init(void); u_int32_t ip6_randomid(void); void ip6_send(struct mbuf *); +int ip6_routeto(struct mbuf *, struct inpcb *); int route6_input(struct mbuf **, int *, int, int); Index: sys/netinet6/raw_ip6.c =================================================================== RCS file: /cvs/src/sys/netinet6/raw_ip6.c,v diff -u -p -r1.186 raw_ip6.c --- sys/netinet6/raw_ip6.c 8 Nov 2024 10:24:13 -0000 1.186 +++ sys/netinet6/raw_ip6.c 29 Apr 2025 06:23:18 -0000 @@ -514,6 +514,11 @@ rip6_output(struct mbuf *m, struct socke /* force routing table */ m->m_pkthdr.ph_rtableid = inp->inp_rtableid; + if (ISSET(inp->inp_flags, INP_ROUTETO)) { + error = ip6_routeto(m, inp); + if (error != 0) + goto bad; + } #if NPF > 0 if (inp->inp_socket->so_state & SS_ISCONNECTED && Index: sys/netinet6/udp6_output.c =================================================================== RCS file: /cvs/src/sys/netinet6/udp6_output.c,v diff -u -p -r1.65 udp6_output.c --- sys/netinet6/udp6_output.c 17 Apr 2024 20:48:51 -0000 1.65 +++ sys/netinet6/udp6_output.c 29 Apr 2025 06:23:18 -0000 @@ -226,6 +226,11 @@ udp6_output(struct inpcb *inp, struct mb /* force routing table */ m->m_pkthdr.ph_rtableid = inp->inp_rtableid; + if (ISSET(inp->inp_flags, INP_ROUTETO)) { + error = ip6_routeto(m, inp); + if (error != 0) + goto release; + } #if NPF > 0 if (inp->inp_socket->so_state & SS_ISCONNECTED) Index: usr.sbin/traceroute/traceroute.c =================================================================== RCS file: /cvs/src/usr.sbin/traceroute/traceroute.c,v diff -u -p -r1.170 traceroute.c --- usr.sbin/traceroute/traceroute.c 21 Aug 2024 15:00:25 -0000 1.170 +++ usr.sbin/traceroute/traceroute.c 29 Apr 2025 06:23:22 -0000 @@ -326,6 +326,7 @@ main(int argc, char *argv[]) u_int32_t tmprnd; int v4sock_errno, v6sock_errno; + const char *gateway = NULL; char *dest; const char *errstr; @@ -425,8 +426,8 @@ main(int argc, char *argv[]) err(1, "sysctl"); conf->max_ttl = i; - while ((ch = getopt(argc, argv, v6flag ? "ADdf:Ilm:np:q:Ss:t:w:vV:" : - "ADdf:g:Ilm:nP:p:q:Ss:t:V:vw:x")) != -1) + while ((ch = getopt(argc, argv, v6flag ? "ADdf:G:Ilm:np:q:Ss:t:w:vV:" : + "ADdf:G:g:Ilm:nP:p:q:Ss:t:V:vw:x")) != -1) switch (ch) { case 'A': conf->Aflag = 1; @@ -445,6 +446,9 @@ main(int argc, char *argv[]) errx(1, "min ttl must be 1 to %u.", conf->max_ttl); break; + case 'G': + gateway = optarg; + break; case 'g': if (conf->lsrr >= MAX_LSRR) errx(1, "too many gateways; max %d", MAX_LSRR); @@ -571,6 +575,34 @@ main(int argc, char *argv[]) default: usage(); } + + if (gateway != NULL) { + int level, optname; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = v6flag ? AF_INET6 : AF_INET; + error = getaddrinfo(gateway, NULL, &hints, &res); + if (error != 0) { + errx(1, "route-to %s: %s", gateway, + gai_strerror(error)); + } + + /* check res->ai_next? */ + + if (v6flag) { + level = IPPROTO_IPV6; + optname = IPV6_ROUTETO; + } else { + level = IPPROTO_IP; + optname = IP_ROUTETO; + } + + if (setsockopt(sndsock, level, optname, + res->ai_addr, res->ai_addrlen) == -1) + err(1, "setsockopt route-to %s", gateway); + + freeaddrinfo(res); + } if (ouid == 0 && (setgroups(1, &gid) || setresgid(gid, gid, gid) ||