Index: files.npf =================================================================== RCS file: /cvsroot/src/sys/net/npf/files.npf,v retrieving revision 1.24 diff -p -u -r1.24 files.npf --- files.npf 1 Jun 2025 00:24:19 -0000 1.24 +++ files.npf 27 Mar 2026 14:13:42 -0000 @@ -44,6 +44,7 @@ file net/npf/lpm.c npf file net/npf/npf_ext_log.c npf file net/npf/npf_ext_normalize.c npf file net/npf/npf_ext_rndblock.c npf +file net/npf/npf_ext_route.c npf # ALGs file net/npf/npf_alg_icmp.c npf Index: npf_ext_route.c =================================================================== RCS file: npf_ext_route.c diff -N npf_ext_route.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ npf_ext_route.c 27 Mar 2026 14:13:42 -0000 @@ -0,0 +1,353 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mindaugas Rasiukevicius. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * NPF route extension. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "npf_impl.h" + +NPF_EXT_MODULE(npf_ext_route, ""); + +#define NPFEXT_ROUTE_VER 1 + +static void * npf_ext_route_id; + +typedef struct { + char *if_name; + bool duplicate; + char *gateway; + char *gateway6; + struct sockaddr_storage nexthop; + struct sockaddr_storage nexthop6; +} npf_ext_route_t; + +static int +parse_address(const char *s, struct sockaddr_storage *res) +{ + static const char hex[] = "0123456789abcdef"; + size_t n, i, k; + uint8_t b, *addr; + const char *p; + + memset(res, 0, sizeof(*res)); + + if (s == NULL) + return 0; + + n = strlen(s); + switch (n) { + case 8: + res->ss_family = AF_INET; + res->ss_len = sizeof(struct sockaddr_in); + addr = (uint8_t*)&(satosin(sstosa(res))->sin_addr.s_addr); + break; +#ifdef INET6 + case 32: + res->ss_family = AF_INET6; + res->ss_len = sizeof(struct sockaddr_in6); + addr = &(satosin6(sstosa(res))->sin6_addr.s6_addr8[0]); + break; +#endif + default: + return 0; + } + + for (k=0, i=1; iif_name = kmem_strdup(s, KM_SLEEP); + + b = dnvlist_get_bool(params, "dup", false); + route->duplicate = b; + + s = dnvlist_get_string(params, "via", NULL); + if (parse_address(s, &route->nexthop)) + route->gateway = kmem_strdup(s, KM_SLEEP); + + s = dnvlist_get_string(params, "via6", NULL); + if (parse_address(s, &route->nexthop6)) + route->gateway6 = kmem_strdup(s, KM_SLEEP); + +the_route = route; + + npf_rproc_assign(rp, route); + return 0; +} + +static void +npf_route_dtor(npf_rproc_t *rp, void *meta) +{ + npf_ext_route_t *route = meta; + + if (route->gateway6) + kmem_strfree(route->gateway6); + if (route->gateway) + kmem_strfree(route->gateway); + if (route->if_name) + kmem_strfree(route->if_name); + kmem_free(route, sizeof(npf_ext_route_t)); + +if (route == the_route) + the_route = NULL; +} + +static void +npf_route_nexthop(npf_cache_t *npc, + struct sockaddr_storage *ssp, int family, int socklen) +{ + ssp->ss_family = family; + ssp->ss_len = socklen; + + switch (ssp->ss_family) { + case AF_INET: + memcpy(&(satosin(sstosa(ssp))->sin_addr.s_addr), + npc->npc_ips[NPF_DST], + npc->npc_alen); + break; + case AF_INET6: + memcpy(&(satosin6(sstosa(ssp))->sin6_addr.s6_addr8[0]), + npc->npc_ips[NPF_DST], + npc->npc_alen); + break; + } +} + +static void +npf_route_checksums(int family, struct mbuf *m) +{ + struct ip *ip; + + switch (family) { + case AF_INET: + if (m->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4)) { + in_undefer_cksum_tcpudp(m); + m->m_pkthdr.csum_flags &= ~(M_CSUM_TCPv4|M_CSUM_UDPv4); + } + + ip = mtod(m, struct ip *); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); + break; +#ifdef INET6 + case AF_INET6: + if (m->m_pkthdr.csum_flags & (M_CSUM_TCPv6|M_CSUM_UDPv6)) { + in6_undefer_cksum_tcpudp(m); + m->m_pkthdr.csum_flags &= ~(M_CSUM_TCPv6|M_CSUM_UDPv6); + } + break; +#endif + } +} + +static int +npf_route_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *to) +{ + unsigned len; + + if (! (ifp->if_flags & IFF_UP)) { + m_freem(m); + return ENETDOWN; + } + +#ifdef INET6 + if (to->sa_family == AF_INET6) { + in6_setscope(&((struct sockaddr_in6 *)to)->sin6_addr, + ifp, NULL); + } +#endif + + KASSERT((m->m_flags & M_PKTHDR) != 0); + len = (unsigned)m->m_pkthdr.len; + if (len > ifp->if_mtu) { + m_freem(m); + return EMSGSIZE; + } + + return if_output_lock(ifp, ifp, m, to, NULL); +} + +static bool +npf_route(npf_cache_t *npc, void *meta, const npf_match_info_t *mi, int *decision) +{ + struct mbuf *m, *m0 = nbuf_head_mbuf(npc->npc_nbuf); + const npf_ext_route_t *route = meta; + struct sockaddr_storage ss; + const struct sockaddr *dst = NULL; + ifnet_t *ifp; + struct psref psref; + int bound; + int family, socklen; + + KASSERT(the_route == NULL || route == the_route); + + /* + * should be the gateway on the target interface + * but we cannot have a route here + */ + switch (npc->npc_alen) { + case sizeof(struct in_addr): + family = AF_INET; + socklen = sizeof(struct sockaddr_in); + if (route->gateway != NULL) + dst = sstocsa(&route->nexthop); + break; + case sizeof(struct in6_addr): + family = AF_INET6; + socklen = sizeof(struct sockaddr_in6); + if (route->gateway6 != NULL) + dst = sstocsa(&route->nexthop6); + break; + default: + return false; + } + + if (dst != NULL) { + /* copy address from nexthop */ + memcpy(&ss, dst, socklen); + } else { + /* take destination from packet */ + npf_route_nexthop(npc, &ss, family, socklen); + } + + m = m_dup(m0, 0, M_COPYALL, M_NOWAIT); + if (m == NULL) + return false; + + npf_route_checksums(family, m); + + bound = curlwp_bind(); + ifp = if_get(route->if_name, &psref); + if (ifp == NULL) { + curlwp_bindx(bound); + m_freem(m); + return false; + } + + (void)npf_route_output(ifp, m, sstosa(&ss)); + + if_put(ifp, &psref); + curlwp_bindx(bound); + + if (route->duplicate) + return true; + + *decision = NPF_DECISION_PASS; + return false; +} + +__dso_public int +npf_ext_route_init(npf_t *npf) +{ + static const npf_ext_ops_t npf_route_ops = { + .version = NPFEXT_ROUTE_VER, + .ctx = NULL, + .ctor = npf_route_ctor, + .dtor = npf_route_dtor, + .proc = npf_route + }; + npf_ext_route_id = npf_ext_register(npf, "route", + &npf_route_ops); + return npf_ext_route_id ? 0 : EEXIST; +} + +__dso_public int +npf_ext_route_fini(npf_t *npf) +{ + + return npf_ext_unregister(npf, npf_ext_route_id); +} + +#ifdef _KERNEL +static int +npf_ext_route_modcmd(modcmd_t cmd, void *arg) +{ + npf_t *npf = npf_getkernctx(); + + switch (cmd) { + case MODULE_CMD_INIT: + return npf_ext_route_init(npf); + case MODULE_CMD_FINI: + return npf_ext_route_fini(npf); + case MODULE_CMD_AUTOUNLOAD: + return npf_autounload_p() ? 0 : EBUSY; + default: + return ENOTTY; + } + return 0; +} +#endif Index: npfkern.h =================================================================== RCS file: /cvsroot/src/sys/net/npf/npfkern.h,v retrieving revision 1.6 diff -p -u -r1.6 npfkern.h --- npfkern.h 1 Jul 2025 18:42:37 -0000 1.6 +++ npfkern.h 27 Mar 2026 14:13:42 -0000 @@ -101,6 +101,9 @@ int npf_ext_normalize_fini(npf_t *); int npf_ext_rndblock_init(npf_t *); int npf_ext_rndblock_fini(npf_t *); +int npf_ext_route_init(npf_t *); +int npf_ext_route_fini(npf_t *); + /* * ALGs. */