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/net/if.c =================================================================== RCS file: /cvs/src/sys/net/if.c,v diff -u -p -r1.721 if.c --- sys/net/if.c 17 Oct 2024 05:02:12 -0000 1.721 +++ sys/net/if.c 29 Apr 2025 06:23:18 -0000 @@ -134,7 +134,13 @@ #if NPF > 0 #include -#endif + +#include "pfsync.h" +#if NPFSYNC > 0 +#include /* for union sockaddr_union */ +#include +#endif /* NPFSYNC > 0 */ +#endif /* NPF > 0 */ #include @@ -723,6 +729,14 @@ if_enqueue(struct ifnet *ifp, struct mbu CLR(m->m_pkthdr.csum_flags, M_TIMESTAMP); #if NPF > 0 +#if NPFSYNC > 0 + if (ISSET(m->m_pkthdr.ph_tagsset, PACKET_TAG_PF_DEFER)) { + m = pfsync_defer_out(ifp, m); + if (m == NULL) + return (0); + } +#endif + if (m->m_pkthdr.pf.delay > 0) return (pf_delay_pkt(m, ifp->if_index)); #endif Index: sys/net/if_pfsync.c =================================================================== RCS file: /cvs/src/sys/net/if_pfsync.c,v diff -u -p -r1.327 if_pfsync.c --- sys/net/if_pfsync.c 19 Nov 2024 02:11:03 -0000 1.327 +++ sys/net/if_pfsync.c 29 Apr 2025 06:23:18 -0000 @@ -107,8 +107,9 @@ struct pfsync_softc; struct pfsync_deferral { TAILQ_ENTRY(pfsync_deferral) pd_entry; struct pf_state *pd_st; - struct mbuf *pd_m; + struct mbuf_list pd_ml; uint64_t pd_deadline; + unsigned int pd_ifidx; }; TAILQ_HEAD(pfsync_deferrals, pfsync_deferral); @@ -427,7 +428,6 @@ pfsync_clone_create(struct if_clone *ifc TAILQ_INIT(&s->s_qs[q]); TAILQ_INIT(&s->s_tdb_q); - /* stupid NET_LOCK */ timeout_set(&s->s_deferrals_tmo, pfsync_deferrals_tmo, s); task_set(&s->s_deferrals_task, pfsync_deferrals_task, s); TAILQ_INIT(&s->s_deferrals); @@ -1946,10 +1946,9 @@ int pfsync_defer(struct pf_state *st, struct mbuf *m) { struct pfsync_softc *sc; - struct pfsync_slice *s; - struct pfsync_deferral *pd; - int sched = 0; - int rv = 0; + struct m_tag *mtag; + struct pf_state_cmp *cmp; + int defer; if (ISSET(st->state_flags, PFSTATE_NOSYNC) || ISSET(m->m_flags, M_BCAST|M_MCAST)) @@ -1957,26 +1956,83 @@ pfsync_defer(struct pf_state *st, struct smr_read_enter(); sc = SMR_PTR_GET(&pfsyncif); - if (sc == NULL || !sc->sc_defer) - goto leave; + defer = (sc != NULL && sc->sc_defer); + smr_read_leave(); - pd = pool_get(&pfsync_deferrals_pool, M_NOWAIT); - if (pd == NULL) { + if (!defer) + return (0); + + KASSERTMSG(m_tag_find(m, PACKET_TAG_PF_DEFER, NULL) == NULL, + "mbuf %p already has a PACKET_TAG_PF_DEFER mtag", m); + + mtag = m_tag_get(PACKET_TAG_PF_DEFER, sizeof(*cmp), M_NOWAIT); + if (mtag == NULL) + return (ENOMEM); + + cmp = (struct pf_state_cmp *)(mtag + 1); + cmp->id = st->id; + cmp->creatorid = st->creatorid; + + m_tag_prepend(m, mtag); + return (0); +} + +struct mbuf * +pfsync_defer_out(struct ifnet *ifp, struct mbuf *m) +{ + struct pfsync_softc *sc; + struct pfsync_slice *s; + struct pf_state *st; + struct pfsync_deferral *pd; + struct m_tag *mtag; + struct pf_state_cmp *cmp; + int sched = 0; + + mtag = m_tag_find(m, PACKET_TAG_PF_DEFER, NULL); + KASSERTMSG(mtag != NULL, + "mbuf %p has PACKET_TAG_PF_DEFER set but no tag", m); + cmp = (struct pf_state_cmp *)(mtag + 1); + + PF_STATE_ENTER_READ(); + st = pf_find_state_byid(cmp); + pf_state_ref(st); + PF_STATE_EXIT_READ(); + + m_tag_delete(m, mtag); + + /* the state doesn't exist already^Wanymore */ + if (st == NULL) + return (m); + + smr_read_enter(); + sc = SMR_PTR_GET(&pfsyncif); + if (sc == NULL || !sc->sc_defer) goto leave; - } s = pfsync_slice_enter(sc, st); s->s_stat_defer_add++; - pd->pd_st = pf_state_ref(st); - pd->pd_m = m; - pd->pd_deadline = getnsecuptime() + PFSYNC_DEFER_NSEC; + /* pd is protected by the slice mutex */ + pd = st->sync_defer; + if (pd == NULL) { + pd = pool_get(&pfsync_deferrals_pool, M_NOWAIT); + if (pd == NULL) + goto leave; + + pd->pd_ifidx = ifp->if_index; + pd->pd_st = pf_state_ref(st); + ml_init(&pd->pd_ml); + pd->pd_deadline = getnsecuptime() + PFSYNC_DEFER_NSEC; - m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; - st->sync_defer = pd; + st->sync_defer = pd; - sched = s->s_deferred++; - TAILQ_INSERT_TAIL(&s->s_deferrals, pd, pd_entry); + sched = s->s_deferred++; + TAILQ_INSERT_TAIL(&s->s_deferrals, pd, pd_entry); + } + + //m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; + ml_enqueue(&pd->pd_ml, m); + m = NULL; /* take the packet away from the caller */ if (sched == 0) timeout_add_nsec(&s->s_deferrals_tmo, PFSYNC_DEFER_NSEC); @@ -1988,11 +2044,11 @@ pfsync_defer(struct pf_state *st, struct pfsync_slice_sched(s); pfsync_slice_leave(sc, s); - rv = 1; leave: smr_read_leave(); + pf_state_unref(st); - return (rv); + return (m); } static void @@ -2074,57 +2130,31 @@ pfsync_deferrals_task(void *arg) if (TAILQ_EMPTY(&pds)) return; - NET_LOCK(); while ((pd = TAILQ_FIRST(&pds)) != NULL) { TAILQ_REMOVE(&pds, pd, pd_entry); pfsync_defer_output(pd); } - NET_UNLOCK(); } static void pfsync_defer_output(struct pfsync_deferral *pd) { - struct pf_pdesc pdesc; struct pf_state *st = pd->pd_st; + struct ifnet *ifp; + struct mbuf *m; - if (st->rt == PF_ROUTETO) { - if (pf_setup_pdesc(&pdesc, st->key[PF_SK_WIRE]->af, - st->direction, NULL, pd->pd_m, NULL) != PF_PASS) - return; - switch (st->key[PF_SK_WIRE]->af) { - case AF_INET: - pf_route(&pdesc, st); - break; -#ifdef INET6 - case AF_INET6: - pf_route6(&pdesc, st); - break; -#endif /* INET6 */ - default: - unhandled_af(st->key[PF_SK_WIRE]->af); - } - pd->pd_m = pdesc.m; - } else { - switch (st->key[PF_SK_WIRE]->af) { - case AF_INET: - ip_output(pd->pd_m, NULL, NULL, 0, NULL, NULL, 0); - break; -#ifdef INET6 - case AF_INET6: - ip6_output(pd->pd_m, NULL, NULL, 0, NULL, NULL); - break; -#endif /* INET6 */ - default: - unhandled_af(st->key[PF_SK_WIRE]->af); + ifp = if_get(pd->pd_ifidx); + if (ifp != NULL) { + while ((m = ml_dequeue(&pd->pd_ml)) != NULL) { + if (if_enqueue(ifp, m) != 0) + break; } - - pd->pd_m = NULL; } + if_put(ifp); pf_state_unref(st); - m_freem(pd->pd_m); + ml_purge(&pd->pd_ml); pool_put(&pfsync_deferrals_pool, pd); } Index: sys/net/if_pfsync.h =================================================================== RCS file: /cvs/src/sys/net/if_pfsync.h,v diff -u -p -r1.63 if_pfsync.h --- sys/net/if_pfsync.h 12 Oct 2024 23:10:07 -0000 1.63 +++ sys/net/if_pfsync.h 29 Apr 2025 06:23:18 -0000 @@ -331,6 +331,7 @@ void pfsync_update_tdb(struct tdb *, i void pfsync_delete_tdb(struct tdb *); int pfsync_defer(struct pf_state *, struct mbuf *); +struct mbuf *pfsync_defer_out(struct ifnet *, struct mbuf *); int pfsync_is_up(void); int pfsync_state_in_use(struct pf_state *); Index: sys/net/pf.c =================================================================== RCS file: /cvs/src/sys/net/pf.c,v diff -u -p -r1.1206 pf.c --- sys/net/pf.c 8 Nov 2024 13:22:09 -0000 1.1206 +++ sys/net/pf.c 29 Apr 2025 06:23:18 -0000 @@ -4573,8 +4573,7 @@ pf_test_rule(struct pf_pdesc *pd, struct * firewall has to know about it to allow * replies through it. */ - if (pfsync_defer(*sm, pd->m)) - return (PF_DEFER); + pfsync_defer(*sm, pd->m); } #endif /* NPFSYNC > 0 */ @@ -6565,17 +6564,12 @@ pf_rtlabel_match(struct pf_addr *addr, s return (ret); } -/* pf_route() may change pd->m, adjust local copies after calling */ -void -pf_route(struct pf_pdesc *pd, struct pf_state *st) +static void +pf_route_af(struct pf_pdesc *pd, struct pf_state *st, + void (*send)(struct mbuf *)) { struct mbuf *m0; - struct mbuf_list ml; - struct sockaddr_in *dst, sin; - struct rtentry *rt = NULL; - struct ip *ip; - struct ifnet *ifp = NULL; - unsigned int rtableid; + struct m_tag *mtag; if (pd->m->m_pkthdr.pf.routed++ > 3) { m_freem(pd->m); @@ -6584,220 +6578,49 @@ pf_route(struct pf_pdesc *pd, struct pf_ } if (st->rt == PF_DUPTO) { - if ((m0 = m_dup_pkt(pd->m, max_linkhdr, M_NOWAIT)) == NULL) - return; - } else { - if ((st->rt == PF_REPLYTO) == (st->direction == pd->dir)) + m0 = m_dup_pkt(pd->m, max_linkhdr, M_NOWAIT); + if (m0 == NULL) return; + } else if ((st->rt == PF_REPLYTO) == (st->direction == pd->dir)) + return; + else m0 = pd->m; - pd->m = NULL; - } - - if (m0->m_len < sizeof(struct ip)) { - DPFPRINTF(LOG_ERR, - "%s: m0->m_len < sizeof(struct ip)", __func__); - goto bad; - } - - ip = mtod(m0, struct ip *); - - if (pd->dir == PF_IN) { - if (ip->ip_ttl <= IPTTLDEC) { - if (st->rt != PF_DUPTO) { - pf_send_icmp(m0, ICMP_TIMXCEED, - ICMP_TIMXCEED_INTRANS, 0, - pd->af, st->rule.ptr, pd->rdomain); - } - goto bad; - } - ip->ip_ttl -= IPTTLDEC; - } - - memset(&sin, 0, sizeof(sin)); - dst = &sin; - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = st->rt_addr.v4; - rtableid = m0->m_pkthdr.ph_rtableid; - - rt = rtalloc_mpath(sintosa(dst), &ip->ip_src.s_addr, rtableid); - if (!rtisvalid(rt)) { - if (st->rt != PF_DUPTO) { - pf_send_icmp(m0, ICMP_UNREACH, ICMP_UNREACH_HOST, - 0, pd->af, st->rule.ptr, pd->rdomain); - } - ipstat_inc(ips_noroute); - goto bad; - } - - ifp = if_get(rt->rt_ifidx); - if (ifp == NULL) - goto bad; - - /* A locally generated packet may have invalid source address. */ - if ((ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET && - (ifp->if_flags & IFF_LOOPBACK) == 0) - ip->ip_src = ifatoia(rt->rt_ifa)->ia_addr.sin_addr; - if (st->rt != PF_DUPTO && pd->dir == PF_IN) { - if (pf_test(AF_INET, PF_OUT, ifp, &m0) != PF_PASS) - goto bad; - else if (m0 == NULL) - goto done; - if (m0->m_len < sizeof(struct ip)) { - DPFPRINTF(LOG_ERR, - "%s: m0->m_len < sizeof(struct ip)", __func__); - goto bad; + mtag = m_tag_find(m0, PACKET_TAG_PF_ROUTE, NULL); + if (mtag == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_ROUTE, sizeof(st->rt_addr), + M_NOWAIT); + if (mtag == NULL) { + if (m0 == pd->m) + pd->m = NULL; + m_freem(m0); + return; } - ip = mtod(m0, struct ip *); - } - if (if_output_tso(ifp, &m0, sintosa(dst), rt, ifp->if_mtu) || - m0 == NULL) - goto done; - - /* - * Too large for interface; fragment if possible. - * Must be able to put at least 8 bytes per fragment. - */ - if (ip->ip_off & htons(IP_DF)) { - ipstat_inc(ips_cantfrag); - if (st->rt != PF_DUPTO) - pf_send_icmp(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, - ifp->if_mtu, pd->af, st->rule.ptr, pd->rdomain); - goto bad; + m_tag_prepend(m0, mtag); } - if (ip_fragment(m0, &ml, ifp, ifp->if_mtu) || - if_output_ml(ifp, &ml, sintosa(dst), rt)) - goto done; - ipstat_inc(ips_fragmented); + *(struct pf_addr *)(mtag + 1) = st->rt_addr; -done: - if_put(ifp); - rtfree(rt); - return; + if (st->rt == PF_DUPTO) { + SET(m0->m_pkthdr.pf.flags, PF_TAG_GENERATED); + (*send)(m0); + } else if (pd->dir == PF_OUT) + SET(m0->m_pkthdr.pf.flags, PF_TAG_REROUTE); +} -bad: - m_freem(m0); - goto done; +/* pf_route() may change pd->m, adjust local copies after calling */ +void +pf_route(struct pf_pdesc *pd, struct pf_state *st) +{ + pf_route_af(pd, st, ip_send); } #ifdef INET6 -/* pf_route6() may change pd->m, adjust local copies after calling */ void pf_route6(struct pf_pdesc *pd, struct pf_state *st) { - struct mbuf *m0; - struct sockaddr_in6 *dst, sin6; - struct rtentry *rt = NULL; - struct ip6_hdr *ip6; - struct ifnet *ifp = NULL; - struct m_tag *mtag; - unsigned int rtableid; - - if (pd->m->m_pkthdr.pf.routed++ > 3) { - m_freem(pd->m); - pd->m = NULL; - return; - } - - if (st->rt == PF_DUPTO) { - if ((m0 = m_dup_pkt(pd->m, max_linkhdr, M_NOWAIT)) == NULL) - return; - } else { - if ((st->rt == PF_REPLYTO) == (st->direction == pd->dir)) - return; - m0 = pd->m; - pd->m = NULL; - } - - if (m0->m_len < sizeof(struct ip6_hdr)) { - DPFPRINTF(LOG_ERR, - "%s: m0->m_len < sizeof(struct ip6_hdr)", __func__); - goto bad; - } - ip6 = mtod(m0, struct ip6_hdr *); - - if (pd->dir == PF_IN) { - if (ip6->ip6_hlim <= IPV6_HLIMDEC) { - if (st->rt != PF_DUPTO) { - pf_send_icmp(m0, ICMP6_TIME_EXCEEDED, - ICMP6_TIME_EXCEED_TRANSIT, 0, - pd->af, st->rule.ptr, pd->rdomain); - } - goto bad; - } - ip6->ip6_hlim -= IPV6_HLIMDEC; - } - - memset(&sin6, 0, sizeof(sin6)); - dst = &sin6; - dst->sin6_family = AF_INET6; - dst->sin6_len = sizeof(*dst); - dst->sin6_addr = st->rt_addr.v6; - rtableid = m0->m_pkthdr.ph_rtableid; - - rt = rtalloc_mpath(sin6tosa(dst), &ip6->ip6_src.s6_addr32[0], - rtableid); - if (!rtisvalid(rt)) { - if (st->rt != PF_DUPTO) { - pf_send_icmp(m0, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0, - pd->af, st->rule.ptr, pd->rdomain); - } - ip6stat_inc(ip6s_noroute); - goto bad; - } - - ifp = if_get(rt->rt_ifidx); - if (ifp == NULL) - goto bad; - - /* A locally generated packet may have invalid source address. */ - if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) && - (ifp->if_flags & IFF_LOOPBACK) == 0) - ip6->ip6_src = ifatoia6(rt->rt_ifa)->ia_addr.sin6_addr; - - if (st->rt != PF_DUPTO && pd->dir == PF_IN) { - if (pf_test(AF_INET6, PF_OUT, ifp, &m0) != PF_PASS) - goto bad; - else if (m0 == NULL) - goto done; - if (m0->m_len < sizeof(struct ip6_hdr)) { - DPFPRINTF(LOG_ERR, - "%s: m0->m_len < sizeof(struct ip6_hdr)", __func__); - goto bad; - } - } - - /* - * If packet has been reassembled by PF earlier, we have to - * use pf_refragment6() here to turn it back to fragments. - */ - if ((mtag = m_tag_find(m0, PACKET_TAG_PF_REASSEMBLED, NULL))) { - (void) pf_refragment6(&m0, mtag, dst, ifp, rt); - goto done; - } - - if (if_output_tso(ifp, &m0, sin6tosa(dst), rt, ifp->if_mtu) || - m0 == NULL) - goto done; - - ip6stat_inc(ip6s_cantfrag); - if (st->rt != PF_DUPTO) - pf_send_icmp(m0, ICMP6_PACKET_TOO_BIG, 0, - ifp->if_mtu, pd->af, st->rule.ptr, pd->rdomain); - goto bad; - -done: - if_put(ifp); - rtfree(rt); - return; - -bad: - m_freem(m0); - goto done; + pf_route_af(pd, st, ip6_send); } #endif /* INET6 */ @@ -7937,10 +7760,6 @@ done: case PF_SYNPROXY_DROP: m_freem(pd.m); /* FALLTHROUGH */ - case PF_DEFER: - pd.m = NULL; - action = PF_PASS; - break; case PF_DIVERT: switch (pd.af) { case AF_INET: @@ -8061,6 +7880,9 @@ pf_ouraddr(struct mbuf *m) { struct pf_state_key *sk; + if (ISSET(m->m_pkthdr.ph_tagsset, PACKET_TAG_PF_ROUTE)) + return (0); + if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) return (1); @@ -8080,6 +7902,16 @@ pf_ouraddr(struct mbuf *m) void pf_pkt_addr_changed(struct mbuf *m) { + struct m_tag *mtag; + + mtag = m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL); + if (mtag != NULL) { + m_tag_delete(m, mtag); + + KASSERTMSG(m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL) == NULL, + "mbuf %p had multiple PACKET_TAG_PF_ROUTE mbuf tags", m); + } + pf_mbuf_unlink_state_key(m); pf_mbuf_unlink_inpcb(m); } Index: sys/net/pfvar.h =================================================================== RCS file: /cvs/src/sys/net/pfvar.h,v diff -u -p -r1.541 pfvar.h --- sys/net/pfvar.h 12 Nov 2024 04:14:51 -0000 1.541 +++ sys/net/pfvar.h 29 Apr 2025 06:23:18 -0000 @@ -66,7 +66,7 @@ typedef struct refcnt pf_refcnt_t; enum { PF_INOUT, PF_IN, PF_OUT, PF_FWD }; enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT, - PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_DEFER, + PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_MATCH, PF_DIVERT, PF_RT, PF_AFRT }; enum { PF_TRANS_RULESET, PF_TRANS_TABLE }; enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, 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_icmp.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_icmp.c,v diff -u -p -r1.196 ip_icmp.c --- sys/netinet/ip_icmp.c 14 Jul 2024 18:53:39 -0000 1.196 +++ sys/netinet/ip_icmp.c 29 Apr 2025 06:23:18 -0000 @@ -684,7 +684,8 @@ icmp_reflect(struct mbuf *m, struct mbuf struct ip *ip = mtod(m, struct ip *); struct mbuf *opts = NULL; struct sockaddr_in sin; - struct rtentry *rt = NULL; + struct rtentry *rt; + struct in_addr ip_src = { INADDR_ANY }; int optlen = (ip->ip_hl << 2) - sizeof(struct ip); u_int rtableid; u_int8_t pfflags; @@ -701,10 +702,6 @@ icmp_reflect(struct mbuf *m, struct mbuf return (ELOOP); } rtableid = m->m_pkthdr.ph_rtableid; - pfflags = m->m_pkthdr.pf.flags; - m_resethdr(m); - m->m_pkthdr.ph_rtableid = rtableid; - m->m_pkthdr.pf.flags = pfflags & PF_TAG_GENERATED; /* * If the incoming packet was addressed directly to us, @@ -718,41 +715,80 @@ icmp_reflect(struct mbuf *m, struct mbuf sin.sin_addr = ip->ip_dst; rt = rtalloc(sintosa(&sin), 0, rtableid); - if (rtisvalid(rt) && - ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST)) - ia = ifatoia(rt->rt_ifa); - } + if (rtisvalid(rt)) { + if (ISSET(rt->rt_flags, RTF_LOCAL)) + ip_src = ip->ip_dst; + else if (ISSET(rt->rt_flags, RTF_BROADCAST)) { + ia = ifatoia(rt->rt_ifa); + ip_src = ia->ia_addr.sin_addr; + } + } + rtfree(rt); + } else + ip_src = ia->ia_addr.sin_addr; /* * The following happens if the packet was not addressed to us. - * Use the new source address and do a route lookup. If it fails - * drop the packet as there is no path to the host. + * If we're directly connected use the closest address, otherwise + * try to use the sourceaddr from the routing table. */ - if (ia == NULL) { - rtfree(rt); - + if (ip_src.s_addr == INADDR_ANY) { memset(&sin, 0, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_addr = ip->ip_src; - /* keep packet in the original virtual instance */ - rt = rtalloc(sintosa(&sin), RT_RESOLVE, rtableid); - if (rt == NULL) { - ipstat_inc(ips_noroute); - m_freem(m); - return (EHOSTUNREACH); + rt = rtalloc_mpath(sintosa(&sin), &ip->ip_dst.s_addr, rtableid); + if (rtisvalid(rt) && + ISSET(rt->rt_flags, RTF_LLINFO|RTF_HOST)) { + ia = ifatoia(rt->rt_ifa); + ip_src = ia->ia_addr.sin_addr; + } else { + struct sockaddr *sourceaddr; + struct ifaddr *ifa; + + sourceaddr = rtable_getsource(rtableid, AF_INET); + if (sourceaddr != NULL) { + ifa = ifa_ifwithaddr(sourceaddr, rtableid); + if (ifa != NULL && + ISSET(ifa->ifa_ifp->if_flags, IFF_UP)) + ip_src = satosin(sourceaddr)->sin_addr; + } } + rtfree(rt); + } - ia = ifatoia(rt->rt_ifa); + /* + * If the above didn't find an ip_src, get the IP of the + * interface the original packet was received on. If all this + * comes up with nothing, ip_output() will try and fill it + * in for us. + */ + if (ip_src.s_addr == INADDR_ANY) { + struct ifnet *ifp; + struct ifaddr *ifa; + + ifp = if_get(m->m_pkthdr.ph_ifidx); + if (ifp != NULL) { + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + + ip_src = satosin(ifa->ifa_addr)->sin_addr; + break; + } + } + if_put(ifp); } + pfflags = m->m_pkthdr.pf.flags; + + m_resethdr(m); + m->m_pkthdr.ph_rtableid = rtableid; + m->m_pkthdr.pf.flags = pfflags & PF_TAG_GENERATED; ip->ip_dst = ip->ip_src; + ip->ip_src = ip_src; ip->ip_ttl = MAXTTL; - - /* It is safe to dereference ``ia'' iff ``rt'' is valid. */ - ip->ip_src = ia->ia_addr.sin_addr; - rtfree(rt); if (optlen > 0) { u_char *cp; Index: sys/netinet/ip_input.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_input.c,v diff -u -p -r1.401 ip_input.c --- sys/netinet/ip_input.c 6 Aug 2024 16:56:09 -0000 1.401 +++ sys/netinet/ip_input.c 29 Apr 2025 06:23:18 -0000 @@ -1560,6 +1560,10 @@ ip_forward(struct mbuf *m, struct ifnet struct mbuf *mcopy; int error = 0, type = 0, code = 0, destmtu = 0; u_int32_t dest; + struct in_addr *rt_dst; +#if NPF > 0 + struct m_tag *rt_mtag; +#endif dest = 0; if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) { @@ -1571,12 +1575,21 @@ ip_forward(struct mbuf *m, struct ifnet icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0); goto done; } + rt_dst = &ip->ip_dst; +#if NPF > 0 + rt_mtag = m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL); + if (rt_mtag != NULL) { + struct pf_addr *rt_addr = (struct pf_addr *)(rt_mtag + 1); + rt_dst = &rt_addr->v4; + SET(flags, IP_REDIRECT); + } +#endif if (ro == NULL) { ro = &iproute; ro->ro_rt = NULL; } - rt = route_mpath(ro, &ip->ip_dst, &ip->ip_src, rtableid); + rt = route_mpath(ro, rt_dst, &ip->ip_src, rtableid); if (rt == NULL) { ipstat_inc(ips_noroute); icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0); 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 @@ -110,7 +110,9 @@ ip_output(struct mbuf *m, struct mbuf *o struct sockaddr_in *dst; struct tdb *tdb = NULL; u_long mtu; + struct in_addr *rt_dst; #if NPF > 0 + struct m_tag *rt_mtag; u_int orig_rtableid; #endif @@ -128,7 +130,7 @@ ip_output(struct mbuf *m, struct mbuf *o /* * Fill in IP header. */ - if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { + if (!ISSET(flags, IP_FORWARDING|IP_RAWOUTPUT)) { ip->ip_v = IPVERSION; ip->ip_off &= htons(IP_DF); ip->ip_id = htons(ip_randomid()); @@ -151,6 +153,7 @@ ip_output(struct mbuf *m, struct mbuf *o orig_rtableid = m->m_pkthdr.ph_rtableid; reroute: #endif + rt_dst = &ip->ip_dst; /* * Do a route lookup now in case we need the source address to @@ -163,11 +166,19 @@ reroute: ro->ro_rt = NULL; } +#if NPF > 0 + rt_mtag = m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL); + if (rt_mtag != NULL) { + struct pf_addr *rt_addr = (struct pf_addr *)(rt_mtag + 1); + rt_dst = &rt_addr->v4; + } +#endif + /* * If there is a cached route, check that it is to the same * destination and is still up. If not, free it and try again. */ - route_cache(ro, &ip->ip_dst, &ip->ip_src, m->m_pkthdr.ph_rtableid); + route_cache(ro, rt_dst, &ip->ip_src, m->m_pkthdr.ph_rtableid); dst = &ro->ro_dstsin; if ((IN_MULTICAST(ip->ip_dst.s_addr) || @@ -398,6 +409,20 @@ sendit: } #endif /* IPSEC */ + if (ro != NULL && ro->ro_rt != NULL) { + struct rtentry *rt = ro->ro_rt; + + if (ISSET(rt->rt_flags, RTF_REJECT)) { + error = ISSET(rt->rt_flags, RTF_HOST) ? + EHOSTUNREACH : ENETUNREACH; + goto bad; + } + if (ISSET(rt->rt_flags, RTF_BLACKHOLE)) { + error = 0; + goto bad; + } + } + /* * Packet filter */ @@ -418,9 +443,6 @@ sendit: else if (m->m_pkthdr.pf.flags & PF_TAG_REROUTE) { /* tag as generated to skip over pf_test on rerun */ m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; - if (ro == &iproute) - rtfree(ro->ro_rt); - ro = NULL; if_put(ifp); /* drop reference since target changed */ ifp = NULL; goto reroute; @@ -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_forward.c =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_forward.c,v diff -u -p -r1.124 ip6_forward.c --- sys/netinet6/ip6_forward.c 19 Jul 2024 16:58:32 -0000 1.124 +++ sys/netinet6/ip6_forward.c 29 Apr 2025 06:23:18 -0000 @@ -101,6 +101,10 @@ ip6_forward(struct mbuf *m, struct route struct tdb *tdb = NULL; #endif /* IPSEC */ char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; + struct in6_addr *rt_dst; +#if NPF > 0 + struct m_tag *rt_mtag; +#endif /* * Do not forward packets to multicast destination (should be handled @@ -202,11 +206,20 @@ reroute: } #endif /* IPSEC */ + rt_dst = &ip6->ip6_dst; +#if NPF > 0 + rt_mtag = m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL); + if (rt_mtag != NULL) { + struct pf_addr *rt_addr = (struct pf_addr *)(rt_mtag + 1); + rt_dst = &rt_addr->v6; + } +#endif + if (ro == NULL) { ro = &iproute; ro->ro_rt = NULL; } - rt = route6_mpath(ro, &ip6->ip6_dst, &ip6->ip6_src, + rt = route6_mpath(ro, rt_dst, &ip6->ip6_src, m->m_pkthdr.ph_rtableid); if (rt == NULL) { ip6stat_inc(ip6s_noroute); Index: sys/netinet6/ip6_input.c =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_input.c,v diff -u -p -r1.266 ip6_input.c --- sys/netinet6/ip6_input.c 19 Jul 2024 16:58:32 -0000 1.266 +++ sys/netinet6/ip6_input.c 29 Apr 2025 06:23:18 -0000 @@ -99,7 +99,6 @@ #include #include "gif.h" -#include "bpfilter.h" #ifdef MROUTING #include @@ -364,7 +363,9 @@ ip6_input_if(struct mbuf **mp, int *offp u_int16_t src_scope, dst_scope; #if NPF > 0 struct in6_addr odst; + struct m_tag *rt_mtag; #endif + struct in6_addr *rt_dst; int flags = 0; KASSERT(*offp == 0); @@ -523,11 +524,19 @@ ip6_input_if(struct mbuf **mp, int *offp goto out; } - /* * Unicast check */ - rt = route6_mpath(&ro, &ip6->ip6_dst, &ip6->ip6_src, + rt_dst = &ip6->ip6_dst; +#if NPF > 0 + rt_mtag = m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL); + if (rt_mtag != NULL) { + struct pf_addr *rt_addr = (struct pf_addr *)(rt_mtag + 1); + rt_dst = &rt_addr->v6; + } +#endif + + rt = route6_mpath(&ro, rt_dst, &ip6->ip6_src, m->m_pkthdr.ph_rtableid); /* 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 @@ -177,6 +177,7 @@ ip6_output(struct mbuf *m, struct ip6_pk u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; struct ip6_exthdrs exthdrs; struct in6_addr finaldst; + struct in6_addr *rt_dst; struct route *ro_pmtu = NULL; int hdrsplit = 0; u_int8_t sproto = 0; @@ -184,6 +185,9 @@ ip6_output(struct mbuf *m, struct ip6_pk #ifdef IPSEC struct tdb *tdb = NULL; #endif /* IPSEC */ +#if NPF > 0 + struct m_tag *rt_mtag; +#endif ip6 = mtod(m, struct ip6_hdr *); finaldst = ip6->ip6_dst; @@ -387,6 +391,7 @@ ip6_output(struct mbuf *m, struct ip6_pk #if NPF > 0 reroute: #endif + rt_dst = &ip6->ip6_dst; /* initialize cached route */ if (ro == NULL) { @@ -456,8 +461,16 @@ reroute: ifp = if_get(im6o->im6o_ifidx); } +#if NPF > 0 + rt_mtag = m_tag_find(m, PACKET_TAG_PF_ROUTE, NULL); + if (rt_mtag != NULL) { + struct pf_addr *rt_addr = (struct pf_addr *)(rt_mtag + 1); + rt_dst = &rt_addr->v6; + } +#endif + if (ifp == NULL) { - rt = in6_selectroute(&ip6->ip6_dst, opt, ro, + rt = in6_selectroute(rt_dst, opt, ro, m->m_pkthdr.ph_rtableid); if (rt == NULL) { ip6stat_inc(ip6s_noroute); @@ -480,7 +493,7 @@ reroute: goto bad; } } else { - route6_cache(ro, &ip6->ip6_dst, NULL, m->m_pkthdr.ph_rtableid); + route6_cache(ro, rt_dst, NULL, m->m_pkthdr.ph_rtableid); } if (rt && (rt->rt_flags & RTF_GATEWAY) && @@ -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: sys/sys/mbuf.h =================================================================== RCS file: /cvs/src/sys/sys/mbuf.h,v diff -u -p -r1.265 mbuf.h --- sys/sys/mbuf.h 5 Nov 2024 13:15:13 -0000 1.265 +++ sys/sys/mbuf.h 29 Apr 2025 06:23:18 -0000 @@ -482,12 +482,14 @@ struct m_tag *m_tag_next(struct mbuf *, #define PACKET_TAG_IPSEC_IN_DONE 0x0001 /* IPsec applied, in */ #define PACKET_TAG_IPSEC_OUT_DONE 0x0002 /* IPsec applied, out */ #define PACKET_TAG_IPSEC_FLOWINFO 0x0004 /* IPsec flowinfo */ +#define PACKET_TAG_PF_DEFER 0x0008 /* pfsync deferred packet */ #define PACKET_TAG_IP_OFFNXT 0x0010 /* IPv4 offset and next proto */ #define PACKET_TAG_IP6_OFFNXT 0x0020 /* IPv6 offset and next proto */ #define PACKET_TAG_WIREGUARD 0x0040 /* WireGuard data */ #define PACKET_TAG_GRE 0x0080 /* GRE processing done */ #define PACKET_TAG_DLT 0x0100 /* data link layer type */ #define PACKET_TAG_PF_DIVERT 0x0200 /* pf(4) diverted packet */ +#define PACKET_TAG_PF_ROUTE 0x0400 /* pf(4) route-to */ #define PACKET_TAG_PF_REASSEMBLED 0x0800 /* pf reassembled ipv6 packet */ #define PACKET_TAG_SRCROUTE 0x1000 /* IPv4 source routing options */ #define PACKET_TAG_TUNNEL 0x2000 /* Tunnel endpoint address */ Index: sys/sys/socket.h =================================================================== RCS file: /cvs/src/sys/sys/socket.h,v diff -u -p -r1.105 socket.h --- sys/sys/socket.h 3 Sep 2022 21:13:48 -0000 1.105 +++ sys/sys/socket.h 29 Apr 2025 06:23:18 -0000 @@ -200,7 +200,8 @@ struct splice { #define AF_MPLS 33 /* MPLS */ #define pseudo_AF_PFLOW 34 /* pflow */ #define pseudo_AF_PIPEX 35 /* PIPEX */ -#define AF_MAX 36 +#define AF_PACKET 36 +#define AF_MAX 37 /* * Structure used by kernel to store most 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) ||