run isakmpd -Ka, then a config like this: ipsec.conf: h_self="130.102.96.46" h_s2s1="13.236.196.237" ike esp tunnel \ proto ipencap \ from 0.0.0.0/0 to 0.0.0.0/0 \ local $h_self peer $h_s2s1 \ main auth hmac-sha2-256 enc aes-256 group modp3072 lifetime 28800 \ quick auth hmac-sha2-256 enc aes-256 group modp3072 lifetime 3600 \ psk yep flow esp in \ proto ipencap \ from $h_s2s1 to $h_self \ local $h_self peer $h_s2s1 \ type require Index: conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v retrieving revision 1.714 diff -u -p -r1.714 files --- conf/files 19 Mar 2022 10:25:09 -0000 1.714 +++ conf/files 30 Mar 2022 02:22:49 -0000 @@ -553,6 +553,7 @@ pseudo-device vlan: ifnet, ether pseudo-device carp: ifnet, ether pseudo-device sppp: ifnet pseudo-device gif: ifnet +pseudo-device jif: ifnet pseudo-device gre: ifnet, ether, etherbridge pseudo-device crypto: ifnet pseudo-device trunk: ifnet, ether, ifmedia @@ -984,6 +985,7 @@ file uvm/uvm_vnode.c # IPv6 file net/if_gif.c gif needs-count +file net/if_jif.c jif needs-count file netinet/ip_ecn.c file netinet6/in6_pcb.c inet6 file netinet6/in6.c inet6 Index: conf/GENERIC =================================================================== RCS file: /cvs/src/sys/conf/GENERIC,v retrieving revision 1.281 diff -u -p -r1.281 GENERIC --- conf/GENERIC 23 Dec 2021 10:04:14 -0000 1.281 +++ conf/GENERIC 30 Mar 2022 02:22:49 -0000 @@ -89,6 +91,7 @@ pseudo-device veb # virtual Ethernet br pseudo-device carp # CARP protocol support pseudo-device etherip # EtherIP (RFC 3378) pseudo-device gif # IPv[46] over IPv[46] tunnel (RFC1933) +pseudo-device jif # route based IPsec VPN interface pseudo-device gre # GRE encapsulation interface pseudo-device loop # network loopback pseudo-device mpe # MPLS PE interface Index: net/if_jif.c =================================================================== RCS file: net/if_jif.c diff -N net/if_jif.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ net/if_jif.c 30 Mar 2022 02:22:49 -0000 @@ -0,0 +1,546 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2022 The University of Queensland + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This code was written by David Gwynne as part + * of the Information Technology Infrastructure Group (ITIG) in the + * Faculty of Engineering, Architecture and Information Technology + * (EAIT). + */ + +#ifndef IPSEC +#error jif enabled without IPSEC defined +#endif + +#include "bpfilter.h" +#include "pf.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#include +#include +#endif + +#ifdef MPLS +#include +#endif /* MPLS */ + +#if NBPFILTER > 0 +#include +#endif + +#if NPF > 0 +#include +#endif + +#define JIF_MTU 1280 +#define JIF_MTU_MIN 1280 +#define JIF_MTU_MAX 32768 /* could get closer to 64k... */ + +struct jif_softc { + struct ifnet sc_if; + + int sc_af; + union sockaddr_union sc_src; +#define sc_src4 sc_src.sin +#define sc_src6 sc_src.sin6 + union sockaddr_union sc_dst; +#define sc_dst4 sc_dst.sin +#define sc_dst6 sc_dst.sin6 + + unsigned int sc_rdomain; + int sc_ttl; + int sc_txhprio; + int sc_rxhprio; + + struct task sc_send; +}; + +static int jif_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +static int jif_enqueue(struct ifnet *, struct mbuf *); +static void jif_send(void *); +static void jif_start(struct ifnet *); + +static int jif_ioctl(struct ifnet *, u_long, caddr_t); +static int jif_set_tunnel(struct jif_softc *, + const struct if_laddrreq *); +static int jif_get_tunnel(struct jif_softc *, struct if_laddrreq *); +static int jif_del_tunnel(struct jif_softc *); +static int jif_up(struct jif_softc *); +static int jif_down(struct jif_softc *); + +static int jif_clone_create(struct if_clone *, int); +static int jif_clone_destroy(struct ifnet *); + +static struct if_clone jif_cloner = + IF_CLONE_INITIALIZER("jif", jif_clone_create, jif_clone_destroy); + +void +jifattach(int n) +{ + if_clone_attach(&jif_cloner); +} + +static int +jif_clone_create(struct if_clone *ifc, int unit) +{ + struct jif_softc *sc; + struct ifnet *ifp; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); + + sc->sc_af = AF_UNSPEC; + sc->sc_ttl = ip_defttl; + sc->sc_txhprio = IF_HDRPRIO_PAYLOAD; + sc->sc_rxhprio = IF_HDRPRIO_PAYLOAD; + + task_set(&sc->sc_send, jif_send, sc); + + snprintf(sc->sc_if.if_xname, sizeof sc->sc_if.if_xname, "%s%d", + ifc->ifc_name, unit); + + ifp = &sc->sc_if; + ifp->if_softc = sc; + ifp->if_type = IFT_TUNNEL; + ifp->if_mtu = JIF_MTU; + ifp->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; + ifp->if_xflags = IFXF_CLONED; + ifp->if_bpf_mtap = p2p_bpf_mtap; + ifp->if_input = p2p_input; + ifp->if_output = jif_output; + ifp->if_enqueue = jif_enqueue; + ifp->if_start = jif_start; + ifp->if_ioctl = jif_ioctl; + ifp->if_rtrequest = p2p_rtrequest; + + if_counters_alloc(ifp); + if_attach(ifp); + if_alloc_sadl(ifp); + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t)); +#endif + + return (0); +} + +static int +jif_clone_destroy(struct ifnet *ifp) +{ + struct jif_softc *sc = ifp->if_softc; + + NET_LOCK(); + if (ISSET(ifp->if_flags, IFF_RUNNING)) + jif_down(sc); + NET_UNLOCK(); + + if_detach(ifp); + + free(sc, M_DEVBUF, sizeof(*sc)); + + return (0); +} + +static int +jif_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct jif_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int error = 0; + + switch (cmd) { + case SIOCSIFADDR: + break; + + case SIOCSIFFLAGS: + if (ISSET(ifp->if_flags, IFF_UP)) { + if (!ISSET(ifp->if_flags, IFF_RUNNING)) + error = jif_up(sc); + else + error = 0; + } else { + if (ISSET(ifp->if_flags, IFF_RUNNING)) + error = jif_down(sc); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + break; + + case SIOCSLIFPHYADDR: + error = jif_set_tunnel(sc, (struct if_laddrreq *)data); + break; + case SIOCGLIFPHYADDR: + error = jif_get_tunnel(sc, (struct if_laddrreq *)data); + break; + case SIOCDIFPHYADDR: + error = jif_del_tunnel(sc); + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu < JIF_MTU_MIN || + ifr->ifr_mtu > JIF_MTU_MAX) { + error = EINVAL; + break; + } + + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSLIFPHYRTABLE: + if (ISSET(ifp->if_flags, IFF_RUNNING)) { + error = EBUSY; + break; + } + + if (ifr->ifr_rdomainid < 0 || + ifr->ifr_rdomainid > RT_TABLEID_MAX || + !rtable_exists(ifr->ifr_rdomainid)) { + error = EINVAL; + break; + } + sc->sc_rdomain = ifr->ifr_rdomainid; + break; + case SIOCGLIFPHYRTABLE: + ifr->ifr_rdomainid = sc->sc_rdomain; + break; + + case SIOCSLIFPHYTTL: + if (ifr->ifr_ttl != -1 && + (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff)) { + error = EINVAL; + break; + } + + /* commit */ + sc->sc_ttl = ifr->ifr_ttl; + break; + case SIOCGLIFPHYTTL: + ifr->ifr_ttl = sc->sc_ttl; + break; + + case SIOCSTXHPRIO: + error = if_txhprio_l3_check(ifr->ifr_hdrprio); + if (error != 0) + break; + + sc->sc_txhprio = ifr->ifr_hdrprio; + break; + case SIOCGTXHPRIO: + ifr->ifr_hdrprio = sc->sc_txhprio; + break; + + case SIOCSRXHPRIO: + error = if_rxhprio_l3_check(ifr->ifr_hdrprio); + if (error != 0) + break; + + sc->sc_rxhprio = ifr->ifr_hdrprio; + break; + case SIOCGRXHPRIO: + ifr->ifr_hdrprio = sc->sc_rxhprio; + break; + + default: + error = ENOTTY; + break; + } + + return (error); +} + +static int +jif_up(struct jif_softc *sc) +{ + struct ifnet *ifp = &sc->sc_if; + + SET(ifp->if_flags, IFF_RUNNING); + + return (0); +} + +static int +jif_down(struct jif_softc *sc) +{ + struct ifnet *ifp = &sc->sc_if; + + CLR(ifp->if_flags, IFF_RUNNING); + + taskq_del_barrier(systq, &sc->sc_send); + + return (0); +} + +static int +jif_set_tunnel(struct jif_softc *sc, const struct if_laddrreq *req) +{ + struct ifnet *ifp = &sc->sc_if; + struct sockaddr *src = (struct sockaddr *)&req->addr; + struct sockaddr *dst = (struct sockaddr *)&req->dstaddr; + struct sockaddr_in *src4, *dst4; +#ifdef INET6 + struct sockaddr_in6 *src6, *dst6; +#endif + + if (ISSET(ifp->if_flags, IFF_RUNNING)) + return (EBUSY); + + /* sa_family and sa_len must be equal */ + if (src->sa_family != dst->sa_family) + return (EINVAL); + + /* validate */ + switch (dst->sa_family) { + case AF_INET: + src4 = (struct sockaddr_in *)src; + if (src4->sin_port != htons(0)) + return (EINVAL); + + if (in_nullhost(src4->sin_addr) || + IN_MULTICAST(src4->sin_addr.s_addr)) + return (EINVAL); + + dst4 = (struct sockaddr_in *)dst; + if (dst4->sin_port != htons(0)) + return (EINVAL); + + if (in_nullhost(dst4->sin_addr) || + IN_MULTICAST(dst4->sin_addr.s_addr)) + return (EINVAL); + + sc->sc_src4 = *src4; + sc->sc_dst4 = *dst4; + + break; +#ifdef INET6 + case AF_INET6: + src6 = (struct sockaddr_in6 *)src; + if (src6->sin6_port != htons(0)) + return (EINVAL); + if (IN6_IS_ADDR_UNSPECIFIED(&src6->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&src6->sin6_addr)) + return (EINVAL); + + dst6 = (struct sockaddr_in6 *)dst; + if (dst6->sin6_port != htons(0)) + return (EINVAL); + if (IN6_IS_ADDR_UNSPECIFIED(&dst6->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&dst6->sin6_addr)) + return (EINVAL); + + sc->sc_src6 = *src6; + sc->sc_dst6 = *dst6; + + break; +#endif + default: + return (EAFNOSUPPORT); + } + + /* commit */ + sc->sc_af = dst->sa_family; + + return (0); +} + +static int +jif_get_tunnel(struct jif_softc *sc, struct if_laddrreq *req) +{ + if (sc->sc_af == AF_UNSPEC) + return (EADDRNOTAVAIL); + + memset(&req->addr, 0, sizeof(req->addr)); + memset(&req->dstaddr, 0, sizeof(req->dstaddr)); + + /* XXX do we leak bytes in padding between processes here? */ + + switch (sc->sc_af) { + case AF_INET: + *(struct sockaddr_in *)&req->addr = sc->sc_src4; + *(struct sockaddr_in *)&req->dstaddr = sc->sc_src4; + break; + +#ifdef INET6 + case AF_INET6: + *(struct sockaddr_in6 *)&req->addr = sc->sc_src6; + *(struct sockaddr_in6 *)&req->dstaddr = sc->sc_src6; + break; +#endif + default: + unhandled_af(sc->sc_af); + } + + return (0); +} + +static int +jif_del_tunnel(struct jif_softc *sc) +{ + struct ifnet *ifp = &sc->sc_if; + + if (ISSET(ifp->if_flags, IFF_RUNNING)) + return (EBUSY); + + /* commit */ + sc->sc_af = AF_UNSPEC; + + return (0); +} + +static int +jif_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + struct m_tag *mtag; + int error = 0; + + if (!ISSET(ifp->if_flags, IFF_RUNNING)) { + error = ENETDOWN; + goto drop; + } + + switch (dst->sa_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif +#ifdef MPLS + case AF_MPLS: +#endif + break; + default: + error = EAFNOSUPPORT; + goto drop; + } + + mtag = NULL; + while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) { + if (ifp->if_index == *(int *)(mtag + 1)) { + error = EIO; + goto drop; + } + } + + m->m_pkthdr.ph_family = dst->sa_family; + + error = if_enqueue(ifp, m); + if (error != 0) + counters_inc(ifp->if_counters, ifc_oerrors); + + return (error); + +drop: + m_freem(m); + return (error); +} + +static int +jif_enqueue(struct ifnet *ifp, struct mbuf *m) +{ + struct jif_softc *sc = ifp->if_softc; + struct ifqueue *ifq = &ifp->if_snd; + int error; + + error = ifq_enqueue(ifq, m); + if (error) + return (error); + + task_add(systq, &sc->sc_send); + + return (0); +} + +static void +jif_send(void *arg) +{ + struct jif_softc *sc = arg; + struct ifnet *ifp = &sc->sc_if; + struct ifqueue *ifq = &ifp->if_snd; + struct tdb *tdbp; + struct mbuf *m; + int error; + + if (!ISSET(ifp->if_flags, IFF_RUNNING)) + return; + + tdbp = gettdbbydst(sc->sc_rdomain, &sc->sc_dst, IPPROTO_ESP, + NULL, /* filter */ NULL, /* filtermask */ NULL); /* XXX */ + if (tdbp == NULL) + goto purge; + if (ISSET(tdbp->tdb_flags, TDBF_DELETED) || + !ISSET(tdbp->tdb_flags, TDBF_TUNNELING)) { + tdb_unref(tdbp); + goto purge; + } + + NET_LOCK(); + while ((m = ifq_dequeue(ifq)) != NULL) { + CLR(m->m_flags, M_BCAST|M_MCAST); + +#if NPF > 0 + pf_pkt_addr_changed(m); +#endif + + error = ipsp_process_packet(m, tdbp, + m->m_pkthdr.ph_family, /* already tunnelled? */ 0); + if (error != 0) + counters_inc(ifp->if_counters, ifc_oerrors); + } + NET_UNLOCK(); + + tdb_unref(tdbp); + return; + +purge: + counters_add(ifp->if_counters, ifc_oerrors, ifq_purge(ifq)); +} + +static void +jif_start(struct ifnet *ifp) +{ + counters_add(ifp->if_counters, ifc_oerrors, ifq_purge(&ifp->if_snd)); +}