1*02e922b0Smvs /* $OpenBSD: if_etherip.c,v 1.56 2024/08/20 07:47:25 mvs Exp $ */ 292ef449aSgoda /* 392ef449aSgoda * Copyright (c) 2015 Kazuya GODA <goda@openbsd.org> 492ef449aSgoda * 592ef449aSgoda * Permission to use, copy, modify, and distribute this software for any 692ef449aSgoda * purpose with or without fee is hereby granted, provided that the above 792ef449aSgoda * copyright notice and this permission notice appear in all copies. 892ef449aSgoda * 992ef449aSgoda * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1092ef449aSgoda * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1192ef449aSgoda * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1292ef449aSgoda * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1392ef449aSgoda * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1492ef449aSgoda * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1592ef449aSgoda * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1692ef449aSgoda */ 1792ef449aSgoda 1892ef449aSgoda #include "bpfilter.h" 1992ef449aSgoda #include "pf.h" 2092ef449aSgoda 2192ef449aSgoda #include <sys/param.h> 2292ef449aSgoda #include <sys/systm.h> 2392ef449aSgoda #include <sys/mbuf.h> 2492ef449aSgoda #include <sys/socket.h> 2592ef449aSgoda #include <sys/ioctl.h> 2692ef449aSgoda #include <sys/device.h> 2792ef449aSgoda #include <sys/sysctl.h> 280f32e530Sdlg #include <sys/tree.h> 2992ef449aSgoda 3092ef449aSgoda #include <net/if.h> 3192ef449aSgoda #include <net/if_var.h> 3292ef449aSgoda #include <net/if_dl.h> 3392ef449aSgoda #include <net/if_media.h> 3494c0e2bdSbluhm #include <net/route.h> 35a0f02464Smpi #include <net/rtable.h> 366ebdf549Smpi 3792ef449aSgoda #include <netinet/in.h> 3892ef449aSgoda #include <netinet/ip.h> 3992ef449aSgoda #include <netinet/ip_var.h> 4092ef449aSgoda #include <netinet/if_ether.h> 4192ef449aSgoda #include <netinet/ip_ether.h> 426ebdf549Smpi 4392ef449aSgoda #ifdef INET6 4492ef449aSgoda #include <netinet/ip6.h> 4592ef449aSgoda #include <netinet6/ip6_var.h> 4692ef449aSgoda #endif 4792ef449aSgoda 4892ef449aSgoda #if NBPFILTER > 0 4992ef449aSgoda #include <net/bpf.h> 5092ef449aSgoda #endif 5192ef449aSgoda 5292ef449aSgoda #if NPF > 0 5392ef449aSgoda #include <net/pfvar.h> 5492ef449aSgoda #endif 5592ef449aSgoda 5692ef449aSgoda #include <net/if_etherip.h> 5792ef449aSgoda 58*02e922b0Smvs /* 59*02e922b0Smvs * Locks used to protect data: 60*02e922b0Smvs * a atomic 61*02e922b0Smvs */ 62*02e922b0Smvs 630f32e530Sdlg union etherip_addr { 640f32e530Sdlg struct in_addr in4; 650f32e530Sdlg struct in6_addr in6; 6692ef449aSgoda }; 6792ef449aSgoda 680f32e530Sdlg struct etherip_tunnel { 690f32e530Sdlg union etherip_addr 700f32e530Sdlg _t_src; 710f32e530Sdlg #define t_src4 _t_src.in4 720f32e530Sdlg #define t_src6 _t_src.in6 730f32e530Sdlg union etherip_addr 740f32e530Sdlg _t_dst; 750f32e530Sdlg #define t_dst4 _t_dst.in4 760f32e530Sdlg #define t_dst6 _t_dst.in6 770f32e530Sdlg 780f32e530Sdlg unsigned int t_rtableid; 790f32e530Sdlg sa_family_t t_af; 808d943c62Sdlg uint8_t t_tos; 810f32e530Sdlg 82137f8175Sdlg TAILQ_ENTRY(etherip_tunnel) 8377f3bafcSdlg t_entry; 840f32e530Sdlg }; 850f32e530Sdlg 86137f8175Sdlg TAILQ_HEAD(etherip_list, etherip_tunnel); 870f32e530Sdlg 880f32e530Sdlg static inline int etherip_cmp(const struct etherip_tunnel *, 890f32e530Sdlg const struct etherip_tunnel *); 900f32e530Sdlg 910f32e530Sdlg struct etherip_softc { 920f32e530Sdlg struct etherip_tunnel sc_tunnel; /* must be first */ 930f32e530Sdlg struct arpcom sc_ac; 940f32e530Sdlg struct ifmedia sc_media; 95d8e64327Sdlg int sc_txhprio; 968d943c62Sdlg int sc_rxhprio; 97ed7a9f2eSdlg uint16_t sc_df; 984fc46bb8Sdlg uint8_t sc_ttl; 990f32e530Sdlg }; 10092ef449aSgoda 10192ef449aSgoda /* 10292ef449aSgoda * We can control the acceptance of EtherIP packets by altering the sysctl 10392ef449aSgoda * net.inet.etherip.allow value. Zero means drop them, all else is acceptance. 10492ef449aSgoda */ 105*02e922b0Smvs int etherip_allow = 0; /* [a] */ 10692ef449aSgoda 107913fc150Sjca struct cpumem *etheripcounters; 108913fc150Sjca 10992ef449aSgoda void etheripattach(int); 1106ebdf549Smpi int etherip_clone_create(struct if_clone *, int); 1116ebdf549Smpi int etherip_clone_destroy(struct ifnet *); 1126ebdf549Smpi int etherip_ioctl(struct ifnet *, u_long, caddr_t); 1131d80faa6Sdlg int etherip_output(struct ifnet *, struct mbuf *, struct sockaddr *, 1141d80faa6Sdlg struct rtentry *); 1156ebdf549Smpi void etherip_start(struct ifnet *); 1166ebdf549Smpi int etherip_media_change(struct ifnet *); 1176ebdf549Smpi void etherip_media_status(struct ifnet *, struct ifmediareq *); 1180f32e530Sdlg int etherip_set_tunnel(struct etherip_softc *, struct if_laddrreq *); 1190f32e530Sdlg int etherip_get_tunnel(struct etherip_softc *, struct if_laddrreq *); 1200f32e530Sdlg int etherip_del_tunnel(struct etherip_softc *); 1210f32e530Sdlg int etherip_up(struct etherip_softc *); 1220f32e530Sdlg int etherip_down(struct etherip_softc *); 123137f8175Sdlg struct etherip_softc *etherip_find(const struct etherip_tunnel *); 1248d943c62Sdlg int etherip_input(struct etherip_tunnel *, struct mbuf *, uint8_t, int); 12592ef449aSgoda 1266ebdf549Smpi struct if_clone etherip_cloner = IF_CLONE_INITIALIZER("etherip", 1276ebdf549Smpi etherip_clone_create, etherip_clone_destroy); 12892ef449aSgoda 129137f8175Sdlg struct etherip_list etherip_list = TAILQ_HEAD_INITIALIZER(etherip_list); 13092ef449aSgoda 13192ef449aSgoda void 13292ef449aSgoda etheripattach(int count) 13392ef449aSgoda { 13492ef449aSgoda if_clone_attach(ðerip_cloner); 135913fc150Sjca etheripcounters = counters_alloc(etherips_ncounters); 13692ef449aSgoda } 13792ef449aSgoda 1386ebdf549Smpi int 13992ef449aSgoda etherip_clone_create(struct if_clone *ifc, int unit) 14092ef449aSgoda { 14192ef449aSgoda struct ifnet *ifp; 14292ef449aSgoda struct etherip_softc *sc; 14392ef449aSgoda 144809d3a3eSbluhm sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 14592ef449aSgoda ifp = &sc->sc_ac.ac_if; 1460f32e530Sdlg 1470f32e530Sdlg snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", 1480f32e530Sdlg ifc->ifc_name, unit); 14992ef449aSgoda 1504fc46bb8Sdlg sc->sc_ttl = ip_defttl; 151d8e64327Sdlg sc->sc_txhprio = IFQ_TOS2PRIO(IPTOS_PREC_ROUTINE); /* 0 */ 1528d943c62Sdlg sc->sc_rxhprio = IF_HDRPRIO_PACKET; 1533228febfSdlg sc->sc_df = htons(0); 1544fc46bb8Sdlg 15592ef449aSgoda ifp->if_softc = sc; 1564e916909Sdlg ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN; 15792ef449aSgoda ifp->if_ioctl = etherip_ioctl; 1581d80faa6Sdlg ifp->if_output = etherip_output; 15992ef449aSgoda ifp->if_start = etherip_start; 1600f32e530Sdlg ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 16199bf4b5fSmpi ifp->if_xflags = IFXF_CLONED; 16292ef449aSgoda ifp->if_capabilities = IFCAP_VLAN_MTU; 1630f32e530Sdlg ether_fakeaddr(ifp); 16492ef449aSgoda 16592ef449aSgoda ifmedia_init(&sc->sc_media, 0, etherip_media_change, 16692ef449aSgoda etherip_media_status); 16792ef449aSgoda ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); 16892ef449aSgoda ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); 16992ef449aSgoda 170938ff1aeSbluhm if_counters_alloc(ifp); 17192ef449aSgoda if_attach(ifp); 17292ef449aSgoda ether_ifattach(ifp); 17392ef449aSgoda 174137f8175Sdlg NET_LOCK(); 175137f8175Sdlg TAILQ_INSERT_TAIL(ðerip_list, &sc->sc_tunnel, t_entry); 176137f8175Sdlg NET_UNLOCK(); 177137f8175Sdlg 1780f32e530Sdlg return (0); 17992ef449aSgoda } 18092ef449aSgoda 1816ebdf549Smpi int 18292ef449aSgoda etherip_clone_destroy(struct ifnet *ifp) 18392ef449aSgoda { 18492ef449aSgoda struct etherip_softc *sc = ifp->if_softc; 18592ef449aSgoda 1860f32e530Sdlg NET_LOCK(); 1870f32e530Sdlg if (ISSET(ifp->if_flags, IFF_RUNNING)) 1880f32e530Sdlg etherip_down(sc); 189137f8175Sdlg 190137f8175Sdlg TAILQ_REMOVE(ðerip_list, &sc->sc_tunnel, t_entry); 1910f32e530Sdlg NET_UNLOCK(); 19292ef449aSgoda 19392ef449aSgoda ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY); 19492ef449aSgoda ether_ifdetach(ifp); 19592ef449aSgoda if_detach(ifp); 1960f32e530Sdlg 1976ebdf549Smpi free(sc, M_DEVBUF, sizeof(*sc)); 19892ef449aSgoda 1990f32e530Sdlg return (0); 20092ef449aSgoda } 20192ef449aSgoda 2026ebdf549Smpi int 20392ef449aSgoda etherip_media_change(struct ifnet *ifp) 20492ef449aSgoda { 2056ebdf549Smpi return 0; 20692ef449aSgoda } 20792ef449aSgoda 2086ebdf549Smpi void 20992ef449aSgoda etherip_media_status(struct ifnet *ifp, struct ifmediareq *imr) 21092ef449aSgoda { 21192ef449aSgoda imr->ifm_active = IFM_ETHER | IFM_AUTO; 21292ef449aSgoda imr->ifm_status = IFM_AVALID | IFM_ACTIVE; 21392ef449aSgoda } 21492ef449aSgoda 2151d80faa6Sdlg int 2161d80faa6Sdlg etherip_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 2171d80faa6Sdlg struct rtentry *rt) 2181d80faa6Sdlg { 2191d80faa6Sdlg struct m_tag *mtag; 2201d80faa6Sdlg 2211d80faa6Sdlg mtag = NULL; 2221d80faa6Sdlg while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) { 2231d80faa6Sdlg if (*(int *)(mtag + 1) == ifp->if_index) { 2241d80faa6Sdlg m_freem(m); 2251d80faa6Sdlg return (EIO); 2261d80faa6Sdlg } 2271d80faa6Sdlg } 2281d80faa6Sdlg 2291d80faa6Sdlg return (ether_output(ifp, m, dst, rt)); 2301d80faa6Sdlg } 2311d80faa6Sdlg 2326ebdf549Smpi void 23392ef449aSgoda etherip_start(struct ifnet *ifp) 23492ef449aSgoda { 23592ef449aSgoda struct etherip_softc *sc = ifp->if_softc; 23692ef449aSgoda struct mbuf *m; 2376ebdf549Smpi int error; 23892ef449aSgoda #if NBPFILTER > 0 2390f32e530Sdlg caddr_t if_bpf; 24092ef449aSgoda #endif 2416ebdf549Smpi 2420f32e530Sdlg while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) { 2430f32e530Sdlg #if NBPFILTER > 0 2440f32e530Sdlg if_bpf = ifp->if_bpf; 2450f32e530Sdlg if (if_bpf) 2460f32e530Sdlg bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT); 2470f32e530Sdlg #endif 2480f32e530Sdlg 2490f32e530Sdlg switch (sc->sc_tunnel.t_af) { 25092ef449aSgoda case AF_INET: 25192ef449aSgoda error = ip_etherip_output(ifp, m); 25292ef449aSgoda break; 25392ef449aSgoda #ifdef INET6 25492ef449aSgoda case AF_INET6: 25592ef449aSgoda error = ip6_etherip_output(ifp, m); 25692ef449aSgoda break; 25792ef449aSgoda #endif 25892ef449aSgoda default: 259137f8175Sdlg /* unhandled_af(sc->sc_tunnel.t_af); */ 260137f8175Sdlg m_freem(m); 261137f8175Sdlg continue; 26292ef449aSgoda } 2636ebdf549Smpi 2646ebdf549Smpi if (error) 2656ebdf549Smpi ifp->if_oerrors++; 26692ef449aSgoda } 26792ef449aSgoda } 26892ef449aSgoda 2696ebdf549Smpi int 27092ef449aSgoda etherip_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 27192ef449aSgoda { 27292ef449aSgoda struct etherip_softc *sc = ifp->if_softc; 27392ef449aSgoda struct ifreq *ifr = (struct ifreq *)data; 27422277c75Smpi int error = 0; 27592ef449aSgoda 27692ef449aSgoda switch (cmd) { 27792ef449aSgoda case SIOCSIFADDR: 27892ef449aSgoda ifp->if_flags |= IFF_UP; 27992ef449aSgoda /* FALLTHROUGH */ 28092ef449aSgoda 28192ef449aSgoda case SIOCSIFFLAGS: 2820f32e530Sdlg if (ISSET(ifp->if_flags, IFF_UP)) { 2830f32e530Sdlg if (!ISSET(ifp->if_flags, IFF_RUNNING)) 2840f32e530Sdlg error = etherip_up(sc); 28592ef449aSgoda else 2860f32e530Sdlg error = 0; 2870f32e530Sdlg } else { 2880f32e530Sdlg if (ISSET(ifp->if_flags, IFF_RUNNING)) 2890f32e530Sdlg error = etherip_down(sc); 2900f32e530Sdlg } 29192ef449aSgoda break; 29292ef449aSgoda 2937f2e0d2cSgoda case SIOCSLIFPHYRTABLE: 2947f2e0d2cSgoda if (ifr->ifr_rdomainid < 0 || 2957f2e0d2cSgoda ifr->ifr_rdomainid > RT_TABLEID_MAX || 2967f2e0d2cSgoda !rtable_exists(ifr->ifr_rdomainid)) { 2977f2e0d2cSgoda error = EINVAL; 2987f2e0d2cSgoda break; 2997f2e0d2cSgoda } 3000f32e530Sdlg sc->sc_tunnel.t_rtableid = ifr->ifr_rdomainid; 3017f2e0d2cSgoda break; 3027f2e0d2cSgoda 3037f2e0d2cSgoda case SIOCGLIFPHYRTABLE: 3040f32e530Sdlg ifr->ifr_rdomainid = sc->sc_tunnel.t_rtableid; 3057f2e0d2cSgoda break; 3067f2e0d2cSgoda 30792ef449aSgoda case SIOCSLIFPHYADDR: 3080f32e530Sdlg error = etherip_set_tunnel(sc, (struct if_laddrreq *)data); 30992ef449aSgoda break; 31092ef449aSgoda case SIOCGLIFPHYADDR: 3110f32e530Sdlg error = etherip_get_tunnel(sc, (struct if_laddrreq *)data); 3120f32e530Sdlg break; 3130f32e530Sdlg case SIOCDIFPHYADDR: 3140f32e530Sdlg error = etherip_del_tunnel(sc); 31592ef449aSgoda break; 31692ef449aSgoda 317d8e64327Sdlg case SIOCSTXHPRIO: 318b9e5cef3Sdlg error = if_txhprio_l2_check(ifr->ifr_hdrprio); 319b9e5cef3Sdlg if (error != 0) 320d8e64327Sdlg break; 321d8e64327Sdlg 322d8e64327Sdlg sc->sc_txhprio = ifr->ifr_hdrprio; 323d8e64327Sdlg break; 324d8e64327Sdlg case SIOCGTXHPRIO: 325d8e64327Sdlg ifr->ifr_hdrprio = sc->sc_txhprio; 326d8e64327Sdlg break; 327d8e64327Sdlg 3288d943c62Sdlg case SIOCSRXHPRIO: 329b9e5cef3Sdlg error = if_rxhprio_l2_check(ifr->ifr_hdrprio); 330b9e5cef3Sdlg if (error != 0) 3318d943c62Sdlg break; 3328d943c62Sdlg 3338d943c62Sdlg sc->sc_rxhprio = ifr->ifr_hdrprio; 3348d943c62Sdlg break; 3358d943c62Sdlg case SIOCGRXHPRIO: 3368d943c62Sdlg ifr->ifr_hdrprio = sc->sc_rxhprio; 3378d943c62Sdlg break; 3388d943c62Sdlg 3394fc46bb8Sdlg case SIOCSLIFPHYTTL: 3404fc46bb8Sdlg if (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff) { 3414fc46bb8Sdlg error = EINVAL; 3424fc46bb8Sdlg break; 3434fc46bb8Sdlg } 3444fc46bb8Sdlg 3454fc46bb8Sdlg /* commit */ 3464fc46bb8Sdlg sc->sc_ttl = (uint8_t)ifr->ifr_ttl; 3474fc46bb8Sdlg break; 3484fc46bb8Sdlg case SIOCGLIFPHYTTL: 3494fc46bb8Sdlg ifr->ifr_ttl = (int)sc->sc_ttl; 3504fc46bb8Sdlg break; 3514fc46bb8Sdlg 352ed7a9f2eSdlg case SIOCSLIFPHYDF: 353ed7a9f2eSdlg /* commit */ 354ed7a9f2eSdlg sc->sc_df = ifr->ifr_df ? htons(IP_DF) : htons(0); 355ed7a9f2eSdlg break; 356ed7a9f2eSdlg case SIOCGLIFPHYDF: 357ed7a9f2eSdlg ifr->ifr_df = sc->sc_df ? 1 : 0; 358ed7a9f2eSdlg break; 359ed7a9f2eSdlg 36092ef449aSgoda case SIOCSIFMEDIA: 36192ef449aSgoda case SIOCGIFMEDIA: 36292ef449aSgoda error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 36392ef449aSgoda break; 36492ef449aSgoda 36525dfeb2bSdlg case SIOCADDMULTI: 36625dfeb2bSdlg case SIOCDELMULTI: 36725dfeb2bSdlg break; 36825dfeb2bSdlg 36992ef449aSgoda default: 37092ef449aSgoda error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); 37192ef449aSgoda break; 37292ef449aSgoda } 37392ef449aSgoda 37412f62104Sdlg if (error == ENETRESET) { 37512f62104Sdlg /* no hardware to program */ 37612f62104Sdlg error = 0; 37712f62104Sdlg } 37812f62104Sdlg 3790f32e530Sdlg return (error); 38092ef449aSgoda } 38192ef449aSgoda 3826ebdf549Smpi int 3830f32e530Sdlg etherip_set_tunnel(struct etherip_softc *sc, struct if_laddrreq *req) 38492ef449aSgoda { 3850f32e530Sdlg struct sockaddr *src = (struct sockaddr *)&req->addr; 3860f32e530Sdlg struct sockaddr *dst = (struct sockaddr *)&req->dstaddr; 3870f32e530Sdlg struct sockaddr_in *src4, *dst4; 3880f32e530Sdlg #ifdef INET6 3890f32e530Sdlg struct sockaddr_in6 *src6, *dst6; 3900f32e530Sdlg int error; 3910f32e530Sdlg #endif 39292ef449aSgoda 3930f32e530Sdlg /* sa_family and sa_len must be equal */ 3940f32e530Sdlg if (src->sa_family != dst->sa_family || src->sa_len != dst->sa_len) 3950f32e530Sdlg return (EINVAL); 39692ef449aSgoda 3970f32e530Sdlg /* validate */ 3980f32e530Sdlg switch (dst->sa_family) { 3990f32e530Sdlg case AF_INET: 4000f32e530Sdlg if (dst->sa_len != sizeof(*dst4)) 4010f32e530Sdlg return (EINVAL); 40292ef449aSgoda 4030f32e530Sdlg src4 = (struct sockaddr_in *)src; 4040f32e530Sdlg if (in_nullhost(src4->sin_addr) || 4050f32e530Sdlg IN_MULTICAST(src4->sin_addr.s_addr)) 4060f32e530Sdlg return (EINVAL); 40792ef449aSgoda 4080f32e530Sdlg dst4 = (struct sockaddr_in *)dst; 4090f32e530Sdlg if (in_nullhost(dst4->sin_addr) || 4100f32e530Sdlg IN_MULTICAST(dst4->sin_addr.s_addr)) 4110f32e530Sdlg return (EINVAL); 4120f32e530Sdlg 4130f32e530Sdlg sc->sc_tunnel.t_src4 = src4->sin_addr; 4140f32e530Sdlg sc->sc_tunnel.t_dst4 = dst4->sin_addr; 4150f32e530Sdlg break; 4160f32e530Sdlg #ifdef INET6 4170f32e530Sdlg case AF_INET6: 4180f32e530Sdlg if (dst->sa_len != sizeof(*dst6)) 4190f32e530Sdlg return (EINVAL); 4200f32e530Sdlg 4210f32e530Sdlg src6 = (struct sockaddr_in6 *)src; 4220f32e530Sdlg if (IN6_IS_ADDR_UNSPECIFIED(&src6->sin6_addr) || 4230f32e530Sdlg IN6_IS_ADDR_MULTICAST(&src6->sin6_addr)) 4240f32e530Sdlg return (EINVAL); 4250f32e530Sdlg 4260f32e530Sdlg dst6 = (struct sockaddr_in6 *)dst; 4270f32e530Sdlg if (IN6_IS_ADDR_UNSPECIFIED(&dst6->sin6_addr) || 4280f32e530Sdlg IN6_IS_ADDR_MULTICAST(&dst6->sin6_addr)) 4290f32e530Sdlg return (EINVAL); 4300f32e530Sdlg 431952c6363Sbluhm error = in6_embedscope(&sc->sc_tunnel.t_src6, src6, NULL, NULL); 4320f32e530Sdlg if (error != 0) 4330f32e530Sdlg return (error); 4340f32e530Sdlg 435952c6363Sbluhm error = in6_embedscope(&sc->sc_tunnel.t_dst6, dst6, NULL, NULL); 4360f32e530Sdlg if (error != 0) 4370f32e530Sdlg return (error); 4380f32e530Sdlg 4390f32e530Sdlg break; 4400f32e530Sdlg #endif 4410f32e530Sdlg default: 4420f32e530Sdlg return (EAFNOSUPPORT); 44392ef449aSgoda } 44492ef449aSgoda 4450f32e530Sdlg /* commit */ 4460f32e530Sdlg sc->sc_tunnel.t_af = dst->sa_family; 4470f32e530Sdlg 4480f32e530Sdlg return (0); 4490f32e530Sdlg } 4500f32e530Sdlg 4510f32e530Sdlg int 4520f32e530Sdlg etherip_get_tunnel(struct etherip_softc *sc, struct if_laddrreq *req) 4530f32e530Sdlg { 4540f32e530Sdlg struct sockaddr *src = (struct sockaddr *)&req->addr; 4550f32e530Sdlg struct sockaddr *dst = (struct sockaddr *)&req->dstaddr; 4560f32e530Sdlg struct sockaddr_in *sin; 4570f32e530Sdlg #ifdef INET6 /* ifconfig already embeds the scopeid */ 4580f32e530Sdlg struct sockaddr_in6 *sin6; 4590f32e530Sdlg #endif 4600f32e530Sdlg 4610f32e530Sdlg switch (sc->sc_tunnel.t_af) { 4620f32e530Sdlg case AF_UNSPEC: 4630f32e530Sdlg return (EADDRNOTAVAIL); 4640f32e530Sdlg case AF_INET: 4650f32e530Sdlg sin = (struct sockaddr_in *)src; 4660f32e530Sdlg memset(sin, 0, sizeof(*sin)); 4670f32e530Sdlg sin->sin_family = AF_INET; 4680f32e530Sdlg sin->sin_len = sizeof(*sin); 4690f32e530Sdlg sin->sin_addr = sc->sc_tunnel.t_src4; 4700f32e530Sdlg 4710f32e530Sdlg sin = (struct sockaddr_in *)dst; 4720f32e530Sdlg memset(sin, 0, sizeof(*sin)); 4730f32e530Sdlg sin->sin_family = AF_INET; 4740f32e530Sdlg sin->sin_len = sizeof(*sin); 4750f32e530Sdlg sin->sin_addr = sc->sc_tunnel.t_dst4; 4760f32e530Sdlg 4770f32e530Sdlg break; 4780f32e530Sdlg #ifdef INET6 4790f32e530Sdlg case AF_INET6: 4800f32e530Sdlg sin6 = (struct sockaddr_in6 *)src; 4810f32e530Sdlg memset(sin6, 0, sizeof(*sin6)); 4820f32e530Sdlg sin6->sin6_family = AF_INET6; 4830f32e530Sdlg sin6->sin6_len = sizeof(*sin6); 4840f32e530Sdlg in6_recoverscope(sin6, &sc->sc_tunnel.t_src6); 4850f32e530Sdlg 4860f32e530Sdlg sin6 = (struct sockaddr_in6 *)dst; 4870f32e530Sdlg memset(sin6, 0, sizeof(*sin6)); 4880f32e530Sdlg sin6->sin6_family = AF_INET6; 4890f32e530Sdlg sin6->sin6_len = sizeof(*sin6); 4900f32e530Sdlg in6_recoverscope(sin6, &sc->sc_tunnel.t_dst6); 4910f32e530Sdlg 4920f32e530Sdlg break; 4930f32e530Sdlg #endif 4940f32e530Sdlg default: 4950f32e530Sdlg return (EAFNOSUPPORT); 4960f32e530Sdlg } 4970f32e530Sdlg 4980f32e530Sdlg return (0); 4990f32e530Sdlg } 5000f32e530Sdlg 5010f32e530Sdlg int 5020f32e530Sdlg etherip_del_tunnel(struct etherip_softc *sc) 5030f32e530Sdlg { 5040f32e530Sdlg /* commit */ 5050f32e530Sdlg sc->sc_tunnel.t_af = AF_UNSPEC; 5060f32e530Sdlg 5070f32e530Sdlg return (0); 5080f32e530Sdlg } 5090f32e530Sdlg 5100f32e530Sdlg int 5110f32e530Sdlg etherip_up(struct etherip_softc *sc) 5120f32e530Sdlg { 513137f8175Sdlg struct ifnet *ifp = &sc->sc_ac.ac_if; 5140f32e530Sdlg 5150f32e530Sdlg NET_ASSERT_LOCKED(); 5160f32e530Sdlg 517137f8175Sdlg SET(ifp->if_flags, IFF_RUNNING); 5180f32e530Sdlg 5190f32e530Sdlg return (0); 5200f32e530Sdlg } 5210f32e530Sdlg 5220f32e530Sdlg int 5230f32e530Sdlg etherip_down(struct etherip_softc *sc) 5240f32e530Sdlg { 5250f32e530Sdlg struct ifnet *ifp = &sc->sc_ac.ac_if; 526137f8175Sdlg 5270f32e530Sdlg NET_ASSERT_LOCKED(); 5280f32e530Sdlg 5290f32e530Sdlg CLR(ifp->if_flags, IFF_RUNNING); 5300f32e530Sdlg 5310f32e530Sdlg return (0); 53292ef449aSgoda } 53392ef449aSgoda 53492ef449aSgoda int 53592ef449aSgoda ip_etherip_output(struct ifnet *ifp, struct mbuf *m) 53692ef449aSgoda { 53792ef449aSgoda struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc; 5381d80faa6Sdlg struct m_tag *mtag; 53992ef449aSgoda struct etherip_header *eip; 54092ef449aSgoda struct ip *ip; 54192ef449aSgoda 5420f32e530Sdlg M_PREPEND(m, sizeof(*ip) + sizeof(*eip), M_DONTWAIT); 54392ef449aSgoda if (m == NULL) { 544dd4faa4aSvisa etheripstat_inc(etherips_adrops); 54592ef449aSgoda return ENOBUFS; 54692ef449aSgoda } 54792ef449aSgoda 54892ef449aSgoda ip = mtod(m, struct ip *); 54992ef449aSgoda memset(ip, 0, sizeof(struct ip)); 55092ef449aSgoda 55192ef449aSgoda ip->ip_v = IPVERSION; 5520f32e530Sdlg ip->ip_hl = sizeof(*ip) >> 2; 553d8e64327Sdlg ip->ip_tos = IFQ_PRIO2TOS(sc->sc_txhprio == IF_HDRPRIO_PACKET ? 554d8e64327Sdlg m->m_pkthdr.pf.prio : sc->sc_txhprio); 55592ef449aSgoda ip->ip_len = htons(m->m_pkthdr.len); 556ed7a9f2eSdlg ip->ip_id = htons(ip_randomid()); 557ed7a9f2eSdlg ip->ip_off = sc->sc_df; 5584fc46bb8Sdlg ip->ip_ttl = sc->sc_ttl; 559ed7a9f2eSdlg ip->ip_p = IPPROTO_ETHERIP; 5600f32e530Sdlg ip->ip_src = sc->sc_tunnel.t_src4; 5610f32e530Sdlg ip->ip_dst = sc->sc_tunnel.t_dst4; 56292ef449aSgoda 5630f32e530Sdlg eip = (struct etherip_header *)(ip + 1); 5640f32e530Sdlg eip->eip_ver = ETHERIP_VERSION; 5650f32e530Sdlg eip->eip_res = 0; 5660f32e530Sdlg eip->eip_pad = 0; 5670f32e530Sdlg 5681d80faa6Sdlg mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT); 5691d80faa6Sdlg if (mtag == NULL) { 5701d80faa6Sdlg m_freem(m); 5711d80faa6Sdlg return (ENOMEM); 5721d80faa6Sdlg } 5731d80faa6Sdlg 5741d80faa6Sdlg *(int *)(mtag + 1) = ifp->if_index; 5751d80faa6Sdlg m_tag_prepend(m, mtag); 5761d80faa6Sdlg 5770f32e530Sdlg m->m_flags &= ~(M_BCAST|M_MCAST); 5780f32e530Sdlg m->m_pkthdr.ph_rtableid = sc->sc_tunnel.t_rtableid; 5797f2e0d2cSgoda 58092ef449aSgoda #if NPF > 0 58192ef449aSgoda pf_pkt_addr_changed(m); 58292ef449aSgoda #endif 583dd4faa4aSvisa etheripstat_pkt(etherips_opackets, etherips_obytes, m->m_pkthdr.len - 58492ef449aSgoda (sizeof(struct ip) + sizeof(struct etherip_header))); 58592ef449aSgoda 586002d1ea8Smpi ip_send(m); 5870f32e530Sdlg 588002d1ea8Smpi return (0); 58992ef449aSgoda } 59092ef449aSgoda 59196be3d96Sbluhm int 5920f32e530Sdlg ip_etherip_input(struct mbuf **mp, int *offp, int type, int af) 59392ef449aSgoda { 59496be3d96Sbluhm struct mbuf *m = *mp; 5950f32e530Sdlg struct etherip_tunnel key; 5960f32e530Sdlg struct ip *ip; 59792ef449aSgoda 59892ef449aSgoda ip = mtod(m, struct ip *); 59992ef449aSgoda 6000f32e530Sdlg key.t_af = AF_INET; 6010f32e530Sdlg key.t_src4 = ip->ip_dst; 6020f32e530Sdlg key.t_dst4 = ip->ip_src; 6030f32e530Sdlg 6048d943c62Sdlg return (etherip_input(&key, m, ip->ip_tos, *offp)); 60592ef449aSgoda } 60692ef449aSgoda 607137f8175Sdlg struct etherip_softc * 608137f8175Sdlg etherip_find(const struct etherip_tunnel *key) 609137f8175Sdlg { 610137f8175Sdlg struct etherip_tunnel *t; 611137f8175Sdlg struct etherip_softc *sc; 612137f8175Sdlg 613137f8175Sdlg TAILQ_FOREACH(t, ðerip_list, t_entry) { 614137f8175Sdlg if (etherip_cmp(key, t) != 0) 615137f8175Sdlg continue; 616137f8175Sdlg 617137f8175Sdlg sc = (struct etherip_softc *)t; 618137f8175Sdlg if (!ISSET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING)) 619137f8175Sdlg continue; 620137f8175Sdlg 621137f8175Sdlg return (sc); 622137f8175Sdlg } 623137f8175Sdlg 624137f8175Sdlg return (NULL); 625137f8175Sdlg } 626137f8175Sdlg 6270f32e530Sdlg int 6288d943c62Sdlg etherip_input(struct etherip_tunnel *key, struct mbuf *m, uint8_t tos, 6298d943c62Sdlg int hlen) 6300f32e530Sdlg { 6310f32e530Sdlg struct etherip_softc *sc; 6320f32e530Sdlg struct ifnet *ifp; 6330f32e530Sdlg struct etherip_header *eip; 6348d943c62Sdlg int rxprio; 6350f32e530Sdlg 636*02e922b0Smvs if (atomic_load_int(ðerip_allow) == 0 && 637*02e922b0Smvs (m->m_flags & (M_AUTH|M_CONF)) == 0) { 638dd4faa4aSvisa etheripstat_inc(etherips_pdrops); 6390f32e530Sdlg goto drop; 64092ef449aSgoda } 64192ef449aSgoda 6420f32e530Sdlg key->t_rtableid = m->m_pkthdr.ph_rtableid; 6430f32e530Sdlg 64494334c66Smpi NET_ASSERT_LOCKED(); 645137f8175Sdlg sc = etherip_find(key); 6460f32e530Sdlg if (sc == NULL) { 647dd4faa4aSvisa etheripstat_inc(etherips_noifdrops); 6480f32e530Sdlg goto drop; 64992ef449aSgoda } 65092ef449aSgoda 6510f32e530Sdlg m_adj(m, hlen); 6520f32e530Sdlg m = m_pullup(m, sizeof(*eip)); 65392ef449aSgoda if (m == NULL) { 654dd4faa4aSvisa etheripstat_inc(etherips_adrops); 65596be3d96Sbluhm return IPPROTO_DONE; 65692ef449aSgoda } 65792ef449aSgoda 65892ef449aSgoda eip = mtod(m, struct etherip_header *); 659c686b353Sreyk if (eip->eip_ver != ETHERIP_VERSION || eip->eip_pad) { 660dd4faa4aSvisa etheripstat_inc(etherips_adrops); 6610f32e530Sdlg goto drop; 66292ef449aSgoda } 66392ef449aSgoda 66492ef449aSgoda m_adj(m, sizeof(struct etherip_header)); 6650f32e530Sdlg 6660f32e530Sdlg etheripstat_pkt(etherips_ipackets, etherips_ibytes, m->m_pkthdr.len); 6670f32e530Sdlg 6680f32e530Sdlg m = m_pullup(m, sizeof(struct ether_header)); 66992ef449aSgoda if (m == NULL) { 670dd4faa4aSvisa etheripstat_inc(etherips_adrops); 67196be3d96Sbluhm return IPPROTO_DONE; 67292ef449aSgoda } 6730f32e530Sdlg 6748d943c62Sdlg rxprio = sc->sc_rxhprio; 6758d943c62Sdlg switch (rxprio) { 6768d943c62Sdlg case IF_HDRPRIO_PACKET: 6778d943c62Sdlg break; 6788d943c62Sdlg case IF_HDRPRIO_OUTER: 6798d943c62Sdlg m->m_pkthdr.pf.prio = IFQ_TOS2PRIO(tos); 6808d943c62Sdlg break; 6818d943c62Sdlg default: 6828d943c62Sdlg m->m_pkthdr.pf.prio = rxprio; 6838d943c62Sdlg break; 6848d943c62Sdlg } 6858d943c62Sdlg 6860f32e530Sdlg ifp = &sc->sc_ac.ac_if; 6870f32e530Sdlg 68892ef449aSgoda m->m_flags &= ~(M_BCAST|M_MCAST); 6890f32e530Sdlg m->m_pkthdr.ph_ifidx = ifp->if_index; 6900f32e530Sdlg m->m_pkthdr.ph_rtableid = ifp->if_rdomain; 69192ef449aSgoda 692723b7749Ssashan #if NPF > 0 693723b7749Ssashan pf_pkt_addr_changed(m); 694723b7749Ssashan #endif 695723b7749Ssashan 69626415d92Sdlg if_vinput(ifp, m); 69796be3d96Sbluhm return IPPROTO_DONE; 6980f32e530Sdlg 6990f32e530Sdlg drop: 7000f32e530Sdlg m_freem(m); 7010f32e530Sdlg return (IPPROTO_DONE); 70292ef449aSgoda } 70392ef449aSgoda 70492ef449aSgoda #ifdef INET6 70592ef449aSgoda int 70692ef449aSgoda ip6_etherip_output(struct ifnet *ifp, struct mbuf *m) 70792ef449aSgoda { 7080f32e530Sdlg struct etherip_softc *sc = ifp->if_softc; 7091d80faa6Sdlg struct m_tag *mtag; 71092ef449aSgoda struct ip6_hdr *ip6; 7110f32e530Sdlg struct etherip_header *eip; 7120f32e530Sdlg uint16_t len; 713d8e64327Sdlg uint32_t flow; 71492ef449aSgoda 7150f32e530Sdlg if (IN6_IS_ADDR_UNSPECIFIED(&sc->sc_tunnel.t_dst6)) { 7160f32e530Sdlg m_freem(m); 7170f32e530Sdlg return (ENETUNREACH); 7186ebdf549Smpi } 71992ef449aSgoda 7200f32e530Sdlg len = m->m_pkthdr.len; 72192ef449aSgoda 7220f32e530Sdlg M_PREPEND(m, sizeof(*ip6) + sizeof(*eip), M_DONTWAIT); 72392ef449aSgoda if (m == NULL) { 724dd4faa4aSvisa etheripstat_inc(etherips_adrops); 72592ef449aSgoda return ENOBUFS; 72692ef449aSgoda } 72792ef449aSgoda 728d8e64327Sdlg flow = IPV6_VERSION << 24; 729d8e64327Sdlg flow |= IFQ_PRIO2TOS(sc->sc_txhprio == IF_HDRPRIO_PACKET ? 730d8e64327Sdlg m->m_pkthdr.pf.prio : sc->sc_txhprio) << 20; 731d8e64327Sdlg 73292ef449aSgoda ip6 = mtod(m, struct ip6_hdr *); 733d8e64327Sdlg htobem32(&ip6->ip6_flow, flow); 73492ef449aSgoda ip6->ip6_nxt = IPPROTO_ETHERIP; 735e23f9dddSdlg ip6->ip6_hlim = sc->sc_ttl; 7360f32e530Sdlg ip6->ip6_plen = htons(len); 7370f32e530Sdlg memcpy(&ip6->ip6_src, &sc->sc_tunnel.t_src6, sizeof(ip6->ip6_src)); 7380f32e530Sdlg memcpy(&ip6->ip6_dst, &sc->sc_tunnel.t_dst6, sizeof(ip6->ip6_dst)); 73992ef449aSgoda 7400f32e530Sdlg eip = (struct etherip_header *)(ip6 + 1); 7410f32e530Sdlg eip->eip_ver = ETHERIP_VERSION; 7420f32e530Sdlg eip->eip_res = 0; 7430f32e530Sdlg eip->eip_pad = 0; 7440f32e530Sdlg 7451d80faa6Sdlg mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT); 7461d80faa6Sdlg if (mtag == NULL) { 7471d80faa6Sdlg m_freem(m); 7481d80faa6Sdlg return (ENOMEM); 7491d80faa6Sdlg } 7501d80faa6Sdlg 7511d80faa6Sdlg *(int *)(mtag + 1) = ifp->if_index; 7521d80faa6Sdlg m_tag_prepend(m, mtag); 7531d80faa6Sdlg 754ed7a9f2eSdlg if (sc->sc_df) 755ed7a9f2eSdlg SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT); 756ed7a9f2eSdlg 7570f32e530Sdlg m->m_flags &= ~(M_BCAST|M_MCAST); 7580f32e530Sdlg m->m_pkthdr.ph_rtableid = sc->sc_tunnel.t_rtableid; 7597f2e0d2cSgoda 76092ef449aSgoda #if NPF > 0 76192ef449aSgoda pf_pkt_addr_changed(m); 76292ef449aSgoda #endif 7630f32e530Sdlg 7640f32e530Sdlg etheripstat_pkt(etherips_opackets, etherips_obytes, len); 76592ef449aSgoda 766002d1ea8Smpi ip6_send(m); 767002d1ea8Smpi return (0); 76892ef449aSgoda } 76992ef449aSgoda 77092ef449aSgoda int 771459fa0feSbluhm ip6_etherip_input(struct mbuf **mp, int *offp, int proto, int af) 77292ef449aSgoda { 77392ef449aSgoda struct mbuf *m = *mp; 7740f32e530Sdlg struct etherip_tunnel key; 77592ef449aSgoda const struct ip6_hdr *ip6; 7768d943c62Sdlg uint32_t flow; 77792ef449aSgoda 77892ef449aSgoda ip6 = mtod(m, const struct ip6_hdr *); 77992ef449aSgoda 7800f32e530Sdlg key.t_af = AF_INET6; 7810f32e530Sdlg key.t_src6 = ip6->ip6_dst; 7820f32e530Sdlg key.t_dst6 = ip6->ip6_src; 78392ef449aSgoda 7848d943c62Sdlg flow = bemtoh32(&ip6->ip6_flow); 7858d943c62Sdlg 7868d943c62Sdlg return (etherip_input(&key, m, flow >> 20, *offp)); 78792ef449aSgoda } 78892ef449aSgoda #endif /* INET6 */ 78992ef449aSgoda 79092ef449aSgoda int 791ed97be20Sjca etherip_sysctl_etheripstat(void *oldp, size_t *oldlenp, void *newp) 792ed97be20Sjca { 793ed97be20Sjca struct etheripstat etheripstat; 794ed97be20Sjca 795ed97be20Sjca CTASSERT(sizeof(etheripstat) == (etherips_ncounters * 796ed97be20Sjca sizeof(uint64_t))); 797ed97be20Sjca memset(ðeripstat, 0, sizeof etheripstat); 798ed97be20Sjca counters_read(etheripcounters, (uint64_t *)ðeripstat, 799bf0d449cSmpi etherips_ncounters, NULL); 800ed97be20Sjca return (sysctl_rdstruct(oldp, oldlenp, newp, ðeripstat, 801ed97be20Sjca sizeof(etheripstat))); 802ed97be20Sjca } 803ed97be20Sjca 804ed97be20Sjca int 805ff2b3331Sjca etherip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 80692ef449aSgoda void *newp, size_t newlen) 80792ef449aSgoda { 80892ef449aSgoda /* All sysctl names at this level are terminal. */ 80992ef449aSgoda if (namelen != 1) 8106ebdf549Smpi return ENOTDIR; 81192ef449aSgoda 81292ef449aSgoda switch (name[0]) { 81392ef449aSgoda case ETHERIPCTL_ALLOW: 814*02e922b0Smvs return (sysctl_int_bounded(oldp, oldlenp, newp, newlen, 815*02e922b0Smvs ðerip_allow, 0, 1)); 81692ef449aSgoda case ETHERIPCTL_STATS: 817dd4faa4aSvisa return (etherip_sysctl_etheripstat(oldp, oldlenp, newp)); 81892ef449aSgoda default: 8196ebdf549Smpi break; 82092ef449aSgoda } 8216ebdf549Smpi 8226ebdf549Smpi return ENOPROTOOPT; 82392ef449aSgoda } 8240f32e530Sdlg 8250f32e530Sdlg static inline int 8260f32e530Sdlg etherip_ip_cmp(int af, const union etherip_addr *a, const union etherip_addr *b) 8270f32e530Sdlg { 8280f32e530Sdlg switch (af) { 8290f32e530Sdlg #ifdef INET6 8300f32e530Sdlg case AF_INET6: 8310f32e530Sdlg return (memcmp(&a->in6, &b->in6, sizeof(a->in6))); 8320f32e530Sdlg /* FALLTHROUGH */ 8330f32e530Sdlg #endif /* INET6 */ 8340f32e530Sdlg case AF_INET: 8350f32e530Sdlg return (memcmp(&a->in4, &b->in4, sizeof(a->in4))); 8360f32e530Sdlg break; 8370f32e530Sdlg default: 8384123b6a7Sderaadt panic("%s: unsupported af %d", __func__, af); 8390f32e530Sdlg } 8400f32e530Sdlg 8410f32e530Sdlg return (0); 8420f32e530Sdlg } 8430f32e530Sdlg 8440f32e530Sdlg static inline int 8450f32e530Sdlg etherip_cmp(const struct etherip_tunnel *a, const struct etherip_tunnel *b) 8460f32e530Sdlg { 8470f32e530Sdlg int rv; 8480f32e530Sdlg 8490f32e530Sdlg if (a->t_rtableid > b->t_rtableid) 8500f32e530Sdlg return (1); 8510f32e530Sdlg if (a->t_rtableid < b->t_rtableid) 8520f32e530Sdlg return (-1); 8530f32e530Sdlg 8540f32e530Sdlg /* sort by address */ 8550f32e530Sdlg if (a->t_af > b->t_af) 8560f32e530Sdlg return (1); 8570f32e530Sdlg if (a->t_af < b->t_af) 8580f32e530Sdlg return (-1); 8590f32e530Sdlg 8600f32e530Sdlg rv = etherip_ip_cmp(a->t_af, &a->_t_dst, &b->_t_dst); 8610f32e530Sdlg if (rv != 0) 8620f32e530Sdlg return (rv); 8630f32e530Sdlg 8640f32e530Sdlg rv = etherip_ip_cmp(a->t_af, &a->_t_src, &b->_t_src); 8650f32e530Sdlg if (rv != 0) 8660f32e530Sdlg return (rv); 8670f32e530Sdlg 8680f32e530Sdlg return (0); 8690f32e530Sdlg } 870