xref: /openbsd-src/sys/net/if_etherip.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: if_etherip.c,v 1.7 2016/04/13 11:41:15 mpi Exp $	*/
2 /*
3  * Copyright (c) 2015 Kazuya GODA <goda@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include "bpfilter.h"
19 #include "pf.h"
20 #include "gif.h"
21 
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/mbuf.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <sys/device.h>
28 #include <sys/sysctl.h>
29 
30 #include <net/if.h>
31 #include <net/if_var.h>
32 #include <net/if_dl.h>
33 #include <net/if_media.h>
34 
35 #include <netinet/in.h>
36 #include <netinet/ip.h>
37 #include <netinet/ip_var.h>
38 #include <netinet/if_ether.h>
39 #include <netinet/ip_ether.h>
40 
41 #ifdef INET6
42 #include <netinet/ip6.h>
43 #include <netinet6/ip6_var.h>
44 #endif
45 
46 #if NBPFILTER > 0
47 #include <net/bpf.h>
48 #endif
49 
50 #if NPF > 0
51 #include <net/pfvar.h>
52 #endif
53 
54 #include <net/if_etherip.h>
55 
56 struct etherip_softc {
57 	struct arpcom sc_ac;
58 	struct ifmedia sc_media;
59 	unsigned int sc_rdomain;
60 	struct sockaddr_storage sc_src;
61 	struct sockaddr_storage sc_dst;
62 	LIST_ENTRY(etherip_softc) sc_entry;
63 };
64 
65 LIST_HEAD(, etherip_softc) etherip_softc_list;
66 
67 #if 0
68 /*
69  * TODO:
70  *   At this stage, etherip_allow and etheripstat are defined
71  *   at netinet/ip_ether.c. When implementation of etherip is
72  *   removed from gif(4), there are moved here.
73  */
74 
75 /*
76  * We can control the acceptance of EtherIP packets by altering the sysctl
77  * net.inet.etherip.allow value. Zero means drop them, all else is acceptance.
78  */
79 int etherip_allow = 0;
80 
81 struct etheripstat etheripstat;
82 #endif
83 
84 void etheripattach(int);
85 int etherip_clone_create(struct if_clone *, int);
86 int etherip_clone_destroy(struct ifnet *);
87 int etherip_ioctl(struct ifnet *, u_long, caddr_t);
88 void etherip_start(struct ifnet *);
89 int etherip_media_change(struct ifnet *);
90 void etherip_media_status(struct ifnet *, struct ifmediareq *);
91 int etherip_set_tunnel_addr(struct ifnet *, struct sockaddr_storage *,
92    struct sockaddr_storage *);
93 
94 struct if_clone	etherip_cloner = IF_CLONE_INITIALIZER("etherip",
95     etherip_clone_create, etherip_clone_destroy);
96 
97 
98 void
99 etheripattach(int count)
100 {
101 	if_clone_attach(&etherip_cloner);
102 }
103 
104 int
105 etherip_clone_create(struct if_clone *ifc, int unit)
106 {
107 	struct ifnet *ifp;
108 	struct etherip_softc *sc;
109 
110 	if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
111 		return ENOMEM;
112 
113 	ifp = &sc->sc_ac.ac_if;
114 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "etherip%d", unit);
115 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
116 	ether_fakeaddr(ifp);
117 
118 	ifp->if_softc = sc;
119 	ifp->if_ioctl = etherip_ioctl;
120 	ifp->if_start = etherip_start;
121 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
122 
123 	ifp->if_capabilities = IFCAP_VLAN_MTU;
124 
125 	ifmedia_init(&sc->sc_media, 0, etherip_media_change,
126 	    etherip_media_status);
127 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
128 	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
129 
130 	if_attach(ifp);
131 	ether_ifattach(ifp);
132 
133 	LIST_INSERT_HEAD(&etherip_softc_list, sc, sc_entry);
134 
135 	return 0;
136 }
137 
138 int
139 etherip_clone_destroy(struct ifnet *ifp)
140 {
141 	struct etherip_softc *sc = ifp->if_softc;
142 
143 	LIST_REMOVE(sc, sc_entry);
144 
145 	ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
146 	ether_ifdetach(ifp);
147 	if_detach(ifp);
148 	free(sc, M_DEVBUF, sizeof(*sc));
149 
150 	return 0;
151 }
152 
153 int
154 etherip_media_change(struct ifnet *ifp)
155 {
156 	return 0;
157 }
158 
159 void
160 etherip_media_status(struct ifnet *ifp, struct ifmediareq *imr)
161 {
162 	imr->ifm_active = IFM_ETHER | IFM_AUTO;
163 	imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
164 }
165 
166 void
167 etherip_start(struct ifnet *ifp)
168 {
169 	struct etherip_softc *sc = ifp->if_softc;
170 	struct mbuf *m;
171 	int error;
172 
173 	for (;;) {
174 		IFQ_DEQUEUE(&ifp->if_snd, m);
175 		if (m == NULL)
176 			break;
177 
178 #if NBPFILTER > 0
179 		if (ifp->if_bpf)
180 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
181 #endif
182 		if (sc->sc_src.ss_family == AF_UNSPEC ||
183 		    sc->sc_dst.ss_family == AF_UNSPEC) {
184 			m_freem(m);
185 			continue;
186 		}
187 
188 		ifp->if_opackets++;
189 
190 		switch (sc->sc_src.ss_family) {
191 		case AF_INET:
192 			error = ip_etherip_output(ifp, m);
193 			break;
194 #ifdef INET6
195 		case AF_INET6:
196 			error = ip6_etherip_output(ifp, m);
197 			break;
198 #endif
199 		default:
200 			unhandled_af(sc->sc_src.ss_family);
201 		}
202 
203 		if (error)
204 			ifp->if_oerrors++;
205 	}
206 
207 }
208 
209 
210 int
211 etherip_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
212 {
213 	struct etherip_softc *sc = ifp->if_softc;
214 	struct if_laddrreq *lifr = (struct if_laddrreq *)data;
215 	struct ifreq *ifr = (struct ifreq *)data;
216 	struct sockaddr_storage *src, *dst;
217 	struct proc *p = curproc;
218 	int s, error = 0;
219 
220 	switch (cmd) {
221 	case SIOCSIFADDR:
222 		ifp->if_flags |= IFF_UP;
223 		/* FALLTHROUGH */
224 
225 	case SIOCSIFFLAGS:
226 		if (ifp->if_flags & IFF_UP)
227 			ifp->if_flags |= IFF_RUNNING;
228 		else
229 			ifp->if_flags &= ~IFF_RUNNING;
230 
231 		break;
232 
233 	case SIOCSLIFPHYRTABLE:
234 		if ((error = suser(p, 0)) != 0)
235 			break;
236 
237 		if (ifr->ifr_rdomainid < 0 ||
238 		    ifr->ifr_rdomainid > RT_TABLEID_MAX ||
239 		    !rtable_exists(ifr->ifr_rdomainid)) {
240 			error = EINVAL;
241 			break;
242 		}
243 		sc->sc_rdomain = ifr->ifr_rdomainid;
244 		break;
245 
246 	case SIOCGLIFPHYRTABLE:
247 		ifr->ifr_rdomainid = sc->sc_rdomain;
248 		break;
249 
250 	case SIOCSLIFPHYADDR:
251 		if ((error = suser(p, 0)) != 0)
252 			break;
253 
254 		src = &lifr->addr;
255 		dst = &lifr->dstaddr;
256 		if (src->ss_family == AF_UNSPEC || dst->ss_family == AF_UNSPEC)
257 			return EADDRNOTAVAIL;
258 
259 		switch (src->ss_family) {
260 		case AF_INET:
261 			if (src->ss_len != sizeof(struct sockaddr_in) ||
262 			    dst->ss_len != sizeof(struct sockaddr_in))
263 				return EINVAL;
264 			break;
265 #ifdef INET6
266 		case AF_INET6:
267 			if (src->ss_len != sizeof(struct sockaddr_in6) ||
268 			    dst->ss_len != sizeof(struct sockaddr_in6))
269 				return EINVAL;
270 			break;
271 #endif
272 		default:
273 			return EAFNOSUPPORT;
274 		}
275 
276 		error = etherip_set_tunnel_addr(ifp, src, dst);
277 		break;
278 
279 	case SIOCDIFPHYADDR:
280 		if ((error = suser(p, 0)) != 0)
281 			break;
282 
283 		s = splsoftnet();
284 		ifp->if_flags &= ~IFF_RUNNING;
285 		memset(&sc->sc_src, 0, sizeof(sc->sc_src));
286 		memset(&sc->sc_dst, 0, sizeof(sc->sc_dst));
287 		splx(s);
288 		break;
289 
290 	case SIOCGLIFPHYADDR:
291 		if (sc->sc_dst.ss_family == AF_UNSPEC)
292 			return EADDRNOTAVAIL;
293 
294 		memset(&lifr->addr, 0, sizeof(lifr->addr));
295 		memset(&lifr->dstaddr, 0, sizeof(lifr->dstaddr));
296 		memcpy(&lifr->addr, &sc->sc_src, sc->sc_src.ss_len);
297 		memcpy(&lifr->dstaddr, &sc->sc_dst, sc->sc_dst.ss_len);
298 
299 		break;
300 
301 	case SIOCSIFMEDIA:
302 	case SIOCGIFMEDIA:
303 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
304 		break;
305 
306 	default:
307 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
308 		break;
309 	}
310 
311 	return error;
312 }
313 
314 int
315 etherip_set_tunnel_addr(struct ifnet *ifp, struct sockaddr_storage *src,
316     struct sockaddr_storage *dst)
317 {
318 	struct etherip_softc *sc, *tsc;
319 	int s, error = 0;
320 
321 	sc  = ifp->if_softc;
322 
323 	s = splsoftnet();
324 	LIST_FOREACH (tsc, &etherip_softc_list, sc_entry) {
325 		if (tsc == sc)
326 			continue;
327 
328 		if (tsc->sc_src.ss_family != src->ss_family ||
329 		    tsc->sc_dst.ss_family != dst->ss_family ||
330 		    tsc->sc_src.ss_len != src->ss_len ||
331 		    tsc->sc_dst.ss_len != dst->ss_len)
332 			continue;
333 
334 		if (tsc->sc_rdomain == sc->sc_rdomain &&
335 		    memcmp(&tsc->sc_dst, dst, dst->ss_len) == 0 &&
336 		    memcmp(&tsc->sc_src, src, src->ss_len) == 0) {
337 			error = EADDRNOTAVAIL;
338 			goto out;
339 		}
340 	}
341 
342 	memcpy(&sc->sc_src, src, src->ss_len);
343 	memcpy(&sc->sc_dst, dst, dst->ss_len);
344 out:
345 	splx(s);
346 
347 	return error;
348 }
349 
350 int
351 ip_etherip_output(struct ifnet *ifp, struct mbuf *m)
352 {
353 	struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc;
354 	struct sockaddr_in *src, *dst;
355 	struct etherip_header *eip;
356 	struct ip *ip;
357 
358 	src = (struct sockaddr_in *)&sc->sc_src;
359 	dst = (struct sockaddr_in *)&sc->sc_dst;
360 
361 	if (src == NULL || dst == NULL ||
362 	    src->sin_family != AF_INET || dst->sin_family != AF_INET) {
363 		m_freem(m);
364 		return EAFNOSUPPORT;
365 	}
366 	if (dst->sin_addr.s_addr == INADDR_ANY) {
367 		m_freem(m);
368 		return ENETUNREACH;
369 	}
370 
371 	m->m_flags &= ~(M_BCAST|M_MCAST);
372 
373 	M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT);
374 	if (m == NULL) {
375 		etheripstat.etherip_adrops++;
376 		return ENOBUFS;
377 	}
378 	eip = mtod(m, struct etherip_header *);
379 	eip->eip_ver = ETHERIP_VERSION;
380 	eip->eip_res = 0;
381 	eip->eip_pad = 0;
382 
383 	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
384 	if (m == NULL) {
385 		etheripstat.etherip_adrops++;
386 		return ENOBUFS;
387 	}
388 	ip = mtod(m, struct ip *);
389 	memset(ip, 0, sizeof(struct ip));
390 
391 	ip->ip_v = IPVERSION;
392 	ip->ip_hl = sizeof(struct ip) >> 2;
393 	ip->ip_id = htons(ip_randomid());
394 	ip->ip_tos = IPTOS_LOWDELAY;
395 	ip->ip_p = IPPROTO_ETHERIP;
396 	ip->ip_len = htons(m->m_pkthdr.len);
397 	ip->ip_ttl = IPDEFTTL;
398 	ip->ip_src = src->sin_addr;
399 	ip->ip_dst = dst->sin_addr;
400 
401 	m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
402 
403 #if NPF > 0
404 	pf_pkt_addr_changed(m);
405 #endif
406 	etheripstat.etherip_opackets++;
407 	etheripstat.etherip_obytes += (m->m_pkthdr.len -
408 	    (sizeof(struct ip) + sizeof(struct etherip_header)));
409 
410 	return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0);
411 }
412 
413 void
414 ip_etherip_input(struct mbuf *m, ...)
415 {
416 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
417 	struct etherip_softc *sc;
418 	const struct ip *ip;
419 	struct etherip_header *eip;
420 	struct sockaddr_in *src, *dst;
421 	struct ifnet *ifp = NULL;
422 	int off;
423 	va_list ap;
424 
425 	va_start(ap, m);
426 	off = va_arg(ap, int);
427 	va_end(ap);
428 
429 	ip = mtod(m, struct ip *);
430 
431 	if (ip->ip_p != IPPROTO_ETHERIP) {
432 		m_freem(m);
433 		ipstat.ips_noproto++;
434 		return;
435 	}
436 
437 	if (!etherip_allow) {
438 		m_freem(m);
439 		etheripstat.etherip_pdrops++;
440 		return;
441 	}
442 
443 	LIST_FOREACH(sc, &etherip_softc_list, sc_entry) {
444 		if (sc->sc_src.ss_family != AF_INET ||
445 		    sc->sc_dst.ss_family != AF_INET)
446 			continue;
447 
448 		src = (struct sockaddr_in *)&sc->sc_src;
449 		dst = (struct sockaddr_in *)&sc->sc_dst;
450 
451 		if (sc->sc_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid) ||
452 		    src->sin_addr.s_addr != ip->ip_dst.s_addr ||
453 		    dst->sin_addr.s_addr != ip->ip_src.s_addr)
454 			continue;
455 
456 		ifp = &sc->sc_ac.ac_if;
457 		break;
458 	}
459 
460 	if (ifp == NULL) {
461 #if NGIF > 0
462 		/*
463 		 * This path is nessesary for gif(4) and etherip(4) coexistence.
464 		 * This is tricky but the path will be removed soon when
465 		 * implementation of etherip is removed from gif(4).
466 		 */
467 		etherip_input(m, off);
468 #else
469 		etheripstat.etherip_noifdrops++;
470 		m_freem(m);
471 #endif /* NGIF */
472 		return;
473 	}
474 
475 	m_adj(m, off);
476 	m = m_pullup(m, sizeof(struct etherip_header));
477 	if (m == NULL) {
478 		etheripstat.etherip_adrops++;
479 		return;
480 	}
481 
482 	eip = mtod(m, struct etherip_header *);
483 	if (eip->eip_ver != ETHERIP_VERSION || eip->eip_pad) {
484 		etheripstat.etherip_adrops++;
485 		m_freem(m);
486 		return;
487 	}
488 
489 	etheripstat.etherip_ipackets++;
490 	etheripstat.etherip_ibytes += (m->m_pkthdr.len -
491 	    sizeof(struct etherip_header));
492 
493 	m_adj(m, sizeof(struct etherip_header));
494 	m = m_pullup(m, sizeof(struct ether_header));
495 	if (m == NULL) {
496 		etheripstat.etherip_adrops++;
497 		return;
498 	}
499 	m->m_flags &= ~(M_BCAST|M_MCAST);
500 
501 #if NPF > 0
502 	pf_pkt_addr_changed(m);
503 #endif
504 
505 	ml_enqueue(&ml, m);
506 	if_input(ifp, &ml);
507 }
508 
509 #ifdef INET6
510 int
511 ip6_etherip_output(struct ifnet *ifp, struct mbuf *m)
512 {
513 	struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc;
514 	struct sockaddr_in6 *src, *dst;
515 	struct etherip_header *eip;
516 	struct ip6_hdr *ip6;
517 
518 	src = (struct sockaddr_in6 *)&sc->sc_src;
519 	dst = (struct sockaddr_in6 *)&sc->sc_dst;
520 
521 	if (src == NULL || dst == NULL ||
522 	    src->sin6_family != AF_INET6 || dst->sin6_family != AF_INET6) {
523 		m_freem(m);
524 		return EAFNOSUPPORT;
525 	}
526 	if (IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) {
527 		m_freem(m);
528 		return ENETUNREACH;
529 	}
530 
531 	m->m_flags &= ~(M_BCAST|M_MCAST);
532 
533 	M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT);
534 	if (m == NULL) {
535 		etheripstat.etherip_adrops++;
536 		return ENOBUFS;
537 	}
538 	eip = mtod(m, struct etherip_header *);
539 	eip->eip_ver = ETHERIP_VERSION;
540 	eip->eip_res = 0;
541 	eip->eip_pad = 0;
542 
543 	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
544 	if (m == NULL) {
545 		etheripstat.etherip_adrops++;
546 		return ENOBUFS;
547 	}
548 	ip6 = mtod(m, struct ip6_hdr *);
549 	ip6->ip6_flow = 0;
550 	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
551 	ip6->ip6_vfc |= IPV6_VERSION;
552 	ip6->ip6_nxt  = IPPROTO_ETHERIP;
553 	ip6->ip6_hlim = ip6_defhlim;
554 	ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
555 	ip6->ip6_src  = src->sin6_addr;
556 	ip6->ip6_dst = dst->sin6_addr;
557 
558 	m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
559 
560 #if NPF > 0
561 	pf_pkt_addr_changed(m);
562 #endif
563 	etheripstat.etherip_opackets++;
564 	etheripstat.etherip_obytes += (m->m_pkthdr.len -
565 	    (sizeof(struct ip6_hdr) + sizeof(struct etherip_header)));
566 
567 	return ip6_output(m, 0, NULL, IPV6_MINMTU, 0, NULL);
568 }
569 
570 int
571 ip6_etherip_input(struct mbuf **mp, int *offp, int proto)
572 {
573 	struct mbuf *m = *mp;
574 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
575 	int off = *offp;
576 	struct etherip_softc *sc;
577 	const struct ip6_hdr *ip6;
578 	struct etherip_header *eip;
579 	struct sockaddr_in6 *src6, *dst6;
580 	struct ifnet *ifp = NULL;
581 
582 
583 	if (!etherip_allow) {
584 		m_freem(m);
585 		etheripstat.etherip_pdrops++;
586 		return IPPROTO_NONE;
587 	}
588 
589 	ip6 = mtod(m, const struct ip6_hdr *);
590 
591 	LIST_FOREACH(sc, &etherip_softc_list, sc_entry) {
592 		if (sc->sc_src.ss_family != AF_INET6 ||
593 		    sc->sc_dst.ss_family != AF_INET6)
594 			continue;
595 
596 		src6 = (struct sockaddr_in6 *)&sc->sc_src;
597 		dst6 = (struct sockaddr_in6 *)&sc->sc_dst;
598 
599 		if (!IN6_ARE_ADDR_EQUAL(&src6->sin6_addr, &ip6->ip6_dst) ||
600 		    !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_src))
601 			continue;
602 
603 		ifp = &sc->sc_ac.ac_if;
604 		break;
605 	}
606 
607 	if (ifp == NULL) {
608 #if NGIF > 0
609 		/*
610 		 * This path is nessesary for gif(4) and etherip(4) coexistence.
611 		 * This is tricky but the path will be removed soon when
612 		 * implementation of etherip is removed from gif(4).
613 		 */
614 		return etherip_input6(mp, offp, proto);
615 #else
616 		etheripstat.etherip_noifdrops++;
617 		m_freem(m);
618 		return IPPROTO_DONE;
619 #endif /* NGIF */
620 	}
621 
622 	m_adj(m, off);
623 	m = m_pullup(m, sizeof(struct etherip_header));
624 	if (m == NULL) {
625 		etheripstat.etherip_adrops++;
626 		return IPPROTO_DONE;
627 	}
628 
629 	eip = mtod(m, struct etherip_header *);
630 	if ((eip->eip_ver != ETHERIP_VERSION) || eip->eip_pad) {
631 		etheripstat.etherip_adrops++;
632 		m_freem(m);
633 		return IPPROTO_DONE;
634 	}
635 	etheripstat.etherip_ipackets++;
636 	etheripstat.etherip_ibytes += (m->m_pkthdr.len -
637 	    sizeof(struct etherip_header));
638 
639 	m_adj(m, sizeof(struct etherip_header));
640 	m = m_pullup(m, sizeof(struct ether_header));
641 	if (m == NULL) {
642 		etheripstat.etherip_adrops++;
643 		return IPPROTO_DONE;
644 	}
645 
646 	m->m_flags &= ~(M_BCAST|M_MCAST);
647 
648 #if NPF > 0
649 	pf_pkt_addr_changed(m);
650 #endif
651 
652 	ml_enqueue(&ml, m);
653 	if_input(ifp, &ml);
654 
655 	return IPPROTO_DONE;
656 }
657 
658 #endif /* INET6 */
659 
660 int
661 ip_etherip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
662     void *newp, size_t newlen)
663 {
664 	/* All sysctl names at this level are terminal. */
665 	if (namelen != 1)
666 		return ENOTDIR;
667 
668 	switch (name[0]) {
669 	case ETHERIPCTL_ALLOW:
670 		return sysctl_int(oldp, oldlenp, newp, newlen, &etherip_allow);
671 	case ETHERIPCTL_STATS:
672 		if (newp != NULL)
673 			return EPERM;
674 		return sysctl_struct(oldp, oldlenp, newp, newlen,
675 		    &etheripstat, sizeof(etheripstat));
676 	default:
677 		break;
678 	}
679 
680 	return ENOPROTOOPT;
681 }
682