xref: /openbsd-src/sys/net/if_etherip.c (revision 02e922b0087950a54812b307acddb99258c95cb3)
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(&etherip_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(&etherip_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(&etherip_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, &etherip_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(&etherip_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(&etheripstat, 0, sizeof etheripstat);
798ed97be20Sjca 	counters_read(etheripcounters, (uint64_t *)&etheripstat,
799bf0d449cSmpi 	    etherips_ncounters, NULL);
800ed97be20Sjca 	return (sysctl_rdstruct(oldp, oldlenp, newp, &etheripstat,
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 		    &etherip_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