xref: /openbsd-src/sys/net/if_etherip.c (revision 162f7d2f70fc7706a6a41366a846707b8d8f3755)
1 /*	$OpenBSD: if_etherip.c,v 1.30 2018/01/22 09:06:22 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 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/mbuf.h>
24 #include <sys/socket.h>
25 #include <sys/ioctl.h>
26 #include <sys/device.h>
27 #include <sys/sysctl.h>
28 
29 #include <net/if.h>
30 #include <net/if_var.h>
31 #include <net/if_dl.h>
32 #include <net/if_media.h>
33 #include <net/rtable.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 /*
68  * We can control the acceptance of EtherIP packets by altering the sysctl
69  * net.inet.etherip.allow value. Zero means drop them, all else is acceptance.
70  */
71 int etherip_allow = 0;
72 
73 struct cpumem *etheripcounters;
74 
75 void etheripattach(int);
76 int etherip_clone_create(struct if_clone *, int);
77 int etherip_clone_destroy(struct ifnet *);
78 int etherip_ioctl(struct ifnet *, u_long, caddr_t);
79 void etherip_start(struct ifnet *);
80 int etherip_media_change(struct ifnet *);
81 void etherip_media_status(struct ifnet *, struct ifmediareq *);
82 int etherip_set_tunnel_addr(struct ifnet *, struct sockaddr_storage *,
83    struct sockaddr_storage *);
84 
85 struct if_clone	etherip_cloner = IF_CLONE_INITIALIZER("etherip",
86     etherip_clone_create, etherip_clone_destroy);
87 
88 
89 void
90 etheripattach(int count)
91 {
92 	if_clone_attach(&etherip_cloner);
93 	etheripcounters = counters_alloc(etherips_ncounters);
94 }
95 
96 int
97 etherip_clone_create(struct if_clone *ifc, int unit)
98 {
99 	struct ifnet *ifp;
100 	struct etherip_softc *sc;
101 
102 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
103 	ifp = &sc->sc_ac.ac_if;
104 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "etherip%d", unit);
105 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
106 	ether_fakeaddr(ifp);
107 
108 	ifp->if_softc = sc;
109 	ifp->if_ioctl = etherip_ioctl;
110 	ifp->if_start = etherip_start;
111 	ifp->if_xflags = IFXF_CLONED;
112 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
113 
114 	ifp->if_capabilities = IFCAP_VLAN_MTU;
115 
116 	ifmedia_init(&sc->sc_media, 0, etherip_media_change,
117 	    etherip_media_status);
118 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
119 	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
120 
121 	if_attach(ifp);
122 	ether_ifattach(ifp);
123 
124 	LIST_INSERT_HEAD(&etherip_softc_list, sc, sc_entry);
125 
126 	return 0;
127 }
128 
129 int
130 etherip_clone_destroy(struct ifnet *ifp)
131 {
132 	struct etherip_softc *sc = ifp->if_softc;
133 
134 	LIST_REMOVE(sc, sc_entry);
135 
136 	ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
137 	ether_ifdetach(ifp);
138 	if_detach(ifp);
139 	free(sc, M_DEVBUF, sizeof(*sc));
140 
141 	return 0;
142 }
143 
144 int
145 etherip_media_change(struct ifnet *ifp)
146 {
147 	return 0;
148 }
149 
150 void
151 etherip_media_status(struct ifnet *ifp, struct ifmediareq *imr)
152 {
153 	imr->ifm_active = IFM_ETHER | IFM_AUTO;
154 	imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
155 }
156 
157 void
158 etherip_start(struct ifnet *ifp)
159 {
160 	struct etherip_softc *sc = ifp->if_softc;
161 	struct mbuf *m;
162 	int error;
163 
164 	for (;;) {
165 		IFQ_DEQUEUE(&ifp->if_snd, m);
166 		if (m == NULL)
167 			break;
168 
169 #if NBPFILTER > 0
170 		if (ifp->if_bpf)
171 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
172 #endif
173 		if (sc->sc_src.ss_family == AF_UNSPEC ||
174 		    sc->sc_dst.ss_family == AF_UNSPEC) {
175 			m_freem(m);
176 			continue;
177 		}
178 
179 		switch (sc->sc_src.ss_family) {
180 		case AF_INET:
181 			error = ip_etherip_output(ifp, m);
182 			break;
183 #ifdef INET6
184 		case AF_INET6:
185 			error = ip6_etherip_output(ifp, m);
186 			break;
187 #endif
188 		default:
189 			unhandled_af(sc->sc_src.ss_family);
190 		}
191 
192 		if (error)
193 			ifp->if_oerrors++;
194 	}
195 
196 }
197 
198 
199 int
200 etherip_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
201 {
202 	struct etherip_softc *sc = ifp->if_softc;
203 	struct if_laddrreq *lifr = (struct if_laddrreq *)data;
204 	struct ifreq *ifr = (struct ifreq *)data;
205 	struct sockaddr_storage *src, *dst;
206 	struct proc *p = curproc;
207 	int error = 0;
208 
209 	switch (cmd) {
210 	case SIOCSIFADDR:
211 		ifp->if_flags |= IFF_UP;
212 		/* FALLTHROUGH */
213 
214 	case SIOCSIFFLAGS:
215 		if (ifp->if_flags & IFF_UP)
216 			ifp->if_flags |= IFF_RUNNING;
217 		else
218 			ifp->if_flags &= ~IFF_RUNNING;
219 
220 		break;
221 
222 	case SIOCSLIFPHYRTABLE:
223 		if ((error = suser(p, 0)) != 0)
224 			break;
225 
226 		if (ifr->ifr_rdomainid < 0 ||
227 		    ifr->ifr_rdomainid > RT_TABLEID_MAX ||
228 		    !rtable_exists(ifr->ifr_rdomainid)) {
229 			error = EINVAL;
230 			break;
231 		}
232 		sc->sc_rdomain = ifr->ifr_rdomainid;
233 		break;
234 
235 	case SIOCGLIFPHYRTABLE:
236 		ifr->ifr_rdomainid = sc->sc_rdomain;
237 		break;
238 
239 	case SIOCSLIFPHYADDR:
240 		if ((error = suser(p, 0)) != 0)
241 			break;
242 
243 		src = &lifr->addr;
244 		dst = &lifr->dstaddr;
245 		if (src->ss_family == AF_UNSPEC || dst->ss_family == AF_UNSPEC)
246 			return EADDRNOTAVAIL;
247 
248 		switch (src->ss_family) {
249 		case AF_INET:
250 			if (src->ss_len != sizeof(struct sockaddr_in) ||
251 			    dst->ss_len != sizeof(struct sockaddr_in))
252 				return EINVAL;
253 			break;
254 #ifdef INET6
255 		case AF_INET6:
256 			if (src->ss_len != sizeof(struct sockaddr_in6) ||
257 			    dst->ss_len != sizeof(struct sockaddr_in6))
258 				return EINVAL;
259 			break;
260 #endif
261 		default:
262 			return EAFNOSUPPORT;
263 		}
264 
265 		error = etherip_set_tunnel_addr(ifp, src, dst);
266 		break;
267 
268 	case SIOCDIFPHYADDR:
269 		if ((error = suser(p, 0)) != 0)
270 			break;
271 
272 		ifp->if_flags &= ~IFF_RUNNING;
273 		memset(&sc->sc_src, 0, sizeof(sc->sc_src));
274 		memset(&sc->sc_dst, 0, sizeof(sc->sc_dst));
275 		break;
276 
277 	case SIOCGLIFPHYADDR:
278 		if (sc->sc_dst.ss_family == AF_UNSPEC)
279 			return EADDRNOTAVAIL;
280 
281 		memset(&lifr->addr, 0, sizeof(lifr->addr));
282 		memset(&lifr->dstaddr, 0, sizeof(lifr->dstaddr));
283 		memcpy(&lifr->addr, &sc->sc_src, sc->sc_src.ss_len);
284 		memcpy(&lifr->dstaddr, &sc->sc_dst, sc->sc_dst.ss_len);
285 
286 		break;
287 
288 	case SIOCSIFMEDIA:
289 	case SIOCGIFMEDIA:
290 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
291 		break;
292 
293 	default:
294 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
295 		break;
296 	}
297 
298 	return error;
299 }
300 
301 int
302 etherip_set_tunnel_addr(struct ifnet *ifp, struct sockaddr_storage *src,
303     struct sockaddr_storage *dst)
304 {
305 	struct etherip_softc *sc, *tsc;
306 	int error = 0;
307 
308 	sc  = ifp->if_softc;
309 
310 	LIST_FOREACH(tsc, &etherip_softc_list, sc_entry) {
311 		if (tsc == sc)
312 			continue;
313 
314 		if (tsc->sc_src.ss_family != src->ss_family ||
315 		    tsc->sc_dst.ss_family != dst->ss_family ||
316 		    tsc->sc_src.ss_len != src->ss_len ||
317 		    tsc->sc_dst.ss_len != dst->ss_len)
318 			continue;
319 
320 		if (tsc->sc_rdomain == sc->sc_rdomain &&
321 		    memcmp(&tsc->sc_dst, dst, dst->ss_len) == 0 &&
322 		    memcmp(&tsc->sc_src, src, src->ss_len) == 0) {
323 			error = EADDRNOTAVAIL;
324 			goto out;
325 		}
326 	}
327 
328 	memcpy(&sc->sc_src, src, src->ss_len);
329 	memcpy(&sc->sc_dst, dst, dst->ss_len);
330 out:
331 	return error;
332 }
333 
334 int
335 ip_etherip_output(struct ifnet *ifp, struct mbuf *m)
336 {
337 	struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc;
338 	struct sockaddr_in *src, *dst;
339 	struct etherip_header *eip;
340 	struct ip *ip;
341 
342 	src = (struct sockaddr_in *)&sc->sc_src;
343 	dst = (struct sockaddr_in *)&sc->sc_dst;
344 
345 	if (src == NULL || dst == NULL ||
346 	    src->sin_family != AF_INET || dst->sin_family != AF_INET) {
347 		m_freem(m);
348 		return EAFNOSUPPORT;
349 	}
350 	if (dst->sin_addr.s_addr == INADDR_ANY) {
351 		m_freem(m);
352 		return ENETUNREACH;
353 	}
354 
355 	/*
356 	 * Remove multicast and broadcast flags or encapsulated packet
357 	 * ends up as multicast or broadcast packet.
358 	 */
359 	m->m_flags &= ~(M_BCAST|M_MCAST);
360 
361 	M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT);
362 	if (m == NULL) {
363 		etheripstat_inc(etherips_adrops);
364 		return ENOBUFS;
365 	}
366 	eip = mtod(m, struct etherip_header *);
367 	eip->eip_ver = ETHERIP_VERSION;
368 	eip->eip_res = 0;
369 	eip->eip_pad = 0;
370 
371 	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
372 	if (m == NULL) {
373 		etheripstat_inc(etherips_adrops);
374 		return ENOBUFS;
375 	}
376 	ip = mtod(m, struct ip *);
377 	memset(ip, 0, sizeof(struct ip));
378 
379 	ip->ip_v = IPVERSION;
380 	ip->ip_hl = sizeof(struct ip) >> 2;
381 	ip->ip_id = htons(ip_randomid());
382 	ip->ip_tos = IPTOS_LOWDELAY;
383 	ip->ip_p = IPPROTO_ETHERIP;
384 	ip->ip_len = htons(m->m_pkthdr.len);
385 	ip->ip_ttl = IPDEFTTL;
386 	ip->ip_src = src->sin_addr;
387 	ip->ip_dst = dst->sin_addr;
388 
389 	m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
390 
391 #if NPF > 0
392 	pf_pkt_addr_changed(m);
393 #endif
394 	etheripstat_pkt(etherips_opackets, etherips_obytes, m->m_pkthdr.len -
395 	    (sizeof(struct ip) + sizeof(struct etherip_header)));
396 
397 	ip_send(m);
398 	return (0);
399 }
400 
401 int
402 ip_etherip_input(struct mbuf **mp, int *offp, int proto, int af)
403 {
404 	struct mbuf *m = *mp;
405 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
406 	struct etherip_softc *sc;
407 	const struct ip *ip;
408 	struct etherip_header *eip;
409 	struct sockaddr_in *src, *dst;
410 	struct ifnet *ifp = NULL;
411 
412 	ip = mtod(m, struct ip *);
413 
414 	if (ip->ip_p != IPPROTO_ETHERIP) {
415 		m_freem(m);
416 		ipstat_inc(ips_noproto);
417 		return IPPROTO_DONE;
418 	}
419 
420 	if (!etherip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) {
421 		m_freem(m);
422 		etheripstat_inc(etherips_pdrops);
423 		return IPPROTO_DONE;
424 	}
425 
426 	NET_ASSERT_LOCKED();
427 	LIST_FOREACH(sc, &etherip_softc_list, sc_entry) {
428 		if (sc->sc_src.ss_family != AF_INET ||
429 		    sc->sc_dst.ss_family != AF_INET)
430 			continue;
431 
432 		src = (struct sockaddr_in *)&sc->sc_src;
433 		dst = (struct sockaddr_in *)&sc->sc_dst;
434 
435 		if (sc->sc_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid) ||
436 		    src->sin_addr.s_addr != ip->ip_dst.s_addr ||
437 		    dst->sin_addr.s_addr != ip->ip_src.s_addr)
438 			continue;
439 
440 		ifp = &sc->sc_ac.ac_if;
441 		break;
442 	}
443 
444 	if (ifp == NULL) {
445 		etheripstat_inc(etherips_noifdrops);
446 		m_freem(m);
447 		return IPPROTO_DONE;
448 	}
449 
450 	m_adj(m, *offp);
451 	m = *mp = m_pullup(m, sizeof(struct etherip_header));
452 	if (m == NULL) {
453 		etheripstat_inc(etherips_adrops);
454 		return IPPROTO_DONE;
455 	}
456 
457 	eip = mtod(m, struct etherip_header *);
458 	if (eip->eip_ver != ETHERIP_VERSION || eip->eip_pad) {
459 		etheripstat_inc(etherips_adrops);
460 		m_freem(m);
461 		return IPPROTO_DONE;
462 	}
463 
464 	etheripstat_pkt(etherips_ipackets, etherips_ibytes, m->m_pkthdr.len -
465 	    sizeof(struct etherip_header));
466 
467 	m_adj(m, sizeof(struct etherip_header));
468 	m = *mp = m_pullup(m, sizeof(struct ether_header));
469 	if (m == NULL) {
470 		etheripstat_inc(etherips_adrops);
471 		return IPPROTO_DONE;
472 	}
473 	m->m_flags &= ~(M_BCAST|M_MCAST);
474 
475 #if NPF > 0
476 	pf_pkt_addr_changed(m);
477 #endif
478 
479 	ml_enqueue(&ml, m);
480 	if_input(ifp, &ml);
481 	return IPPROTO_DONE;
482 }
483 
484 #ifdef INET6
485 int
486 ip6_etherip_output(struct ifnet *ifp, struct mbuf *m)
487 {
488 	struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc;
489 	struct sockaddr_in6 *src, *dst;
490 	struct etherip_header *eip;
491 	struct ip6_hdr *ip6;
492 	int error;
493 
494 	src = (struct sockaddr_in6 *)&sc->sc_src;
495 	dst = (struct sockaddr_in6 *)&sc->sc_dst;
496 
497 	if (src == NULL || dst == NULL ||
498 	    src->sin6_family != AF_INET6 || dst->sin6_family != AF_INET6) {
499 		error = EAFNOSUPPORT;
500 		goto drop;
501 	}
502 	if (IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) {
503 		error = ENETUNREACH;
504 		goto drop;
505 	}
506 
507 	/*
508 	 * Remove multicast and broadcast flags or encapsulated packet
509 	 * ends up as multicast or broadcast packet.
510 	 */
511 	m->m_flags &= ~(M_BCAST|M_MCAST);
512 
513 	M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT);
514 	if (m == NULL) {
515 		etheripstat_inc(etherips_adrops);
516 		return ENOBUFS;
517 	}
518 	eip = mtod(m, struct etherip_header *);
519 	eip->eip_ver = ETHERIP_VERSION;
520 	eip->eip_res = 0;
521 	eip->eip_pad = 0;
522 
523 	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
524 	if (m == NULL) {
525 		etheripstat_inc(etherips_adrops);
526 		return ENOBUFS;
527 	}
528 	ip6 = mtod(m, struct ip6_hdr *);
529 	ip6->ip6_flow = 0;
530 	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
531 	ip6->ip6_vfc |= IPV6_VERSION;
532 	ip6->ip6_nxt  = IPPROTO_ETHERIP;
533 	ip6->ip6_hlim = ip6_defhlim;
534 	ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
535 	error = in6_embedscope(&ip6->ip6_src, src, NULL);
536 	if (error != 0)
537 		goto drop;
538 	error = in6_embedscope(&ip6->ip6_dst, dst, NULL);
539 	if (error != 0)
540 		goto drop;
541 
542 	m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
543 
544 #if NPF > 0
545 	pf_pkt_addr_changed(m);
546 #endif
547 	etheripstat_pkt(etherips_opackets, etherips_obytes, m->m_pkthdr.len -
548 	    (sizeof(struct ip6_hdr) + sizeof(struct etherip_header)));
549 
550 	ip6_send(m);
551 	return (0);
552 
553 drop:
554 	m_freem(m);
555 	return (error);
556 }
557 
558 int
559 ip6_etherip_input(struct mbuf **mp, int *offp, int proto, int af)
560 {
561 	struct mbuf *m = *mp;
562 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
563 	struct etherip_softc *sc;
564 	const struct ip6_hdr *ip6;
565 	struct etherip_header *eip;
566 	struct sockaddr_in6 ipsrc, ipdst;
567 	struct sockaddr_in6 *src6, *dst6;
568 	struct ifnet *ifp = NULL;
569 
570 
571 	if (!etherip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) {
572 		m_freem(m);
573 		etheripstat_inc(etherips_pdrops);
574 		return IPPROTO_NONE;
575 	}
576 
577 	ip6 = mtod(m, const struct ip6_hdr *);
578 	in6_recoverscope(&ipsrc, &ip6->ip6_src);
579 	in6_recoverscope(&ipdst, &ip6->ip6_dst);
580 
581 	NET_ASSERT_LOCKED();
582 	LIST_FOREACH(sc, &etherip_softc_list, sc_entry) {
583 		if (sc->sc_src.ss_family != AF_INET6 ||
584 		    sc->sc_dst.ss_family != AF_INET6)
585 			continue;
586 
587 		src6 = (struct sockaddr_in6 *)&sc->sc_src;
588 		dst6 = (struct sockaddr_in6 *)&sc->sc_dst;
589 
590 		if (IN6_ARE_ADDR_EQUAL(&src6->sin6_addr, &ipdst.sin6_addr) &&
591 		    src6->sin6_scope_id == ipdst.sin6_scope_id &&
592 		    IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ipsrc.sin6_addr) &&
593 		    dst6->sin6_scope_id == ipsrc.sin6_scope_id) {
594 			ifp = &sc->sc_ac.ac_if;
595 			break;
596 		}
597 	}
598 
599 	if (ifp == NULL) {
600 		etheripstat_inc(etherips_noifdrops);
601 		m_freem(m);
602 		return IPPROTO_DONE;
603 	}
604 
605 	m_adj(m, *offp);
606 	m = *mp = m_pullup(m, sizeof(struct etherip_header));
607 	if (m == NULL) {
608 		etheripstat_inc(etherips_adrops);
609 		return IPPROTO_DONE;
610 	}
611 
612 	eip = mtod(m, struct etherip_header *);
613 	if ((eip->eip_ver != ETHERIP_VERSION) || eip->eip_pad) {
614 		etheripstat_inc(etherips_adrops);
615 		m_freem(m);
616 		return IPPROTO_DONE;
617 	}
618 	etheripstat_pkt(etherips_ipackets, etherips_ibytes, m->m_pkthdr.len -
619 	    sizeof(struct etherip_header));
620 
621 	m_adj(m, sizeof(struct etherip_header));
622 	m = *mp = m_pullup(m, sizeof(struct ether_header));
623 	if (m == NULL) {
624 		etheripstat_inc(etherips_adrops);
625 		return IPPROTO_DONE;
626 	}
627 
628 	m->m_flags &= ~(M_BCAST|M_MCAST);
629 
630 #if NPF > 0
631 	pf_pkt_addr_changed(m);
632 #endif
633 
634 	ml_enqueue(&ml, m);
635 	if_input(ifp, &ml);
636 	return IPPROTO_DONE;
637 }
638 #endif /* INET6 */
639 
640 int
641 etherip_sysctl_etheripstat(void *oldp, size_t *oldlenp, void *newp)
642 {
643 	struct etheripstat etheripstat;
644 
645 	CTASSERT(sizeof(etheripstat) == (etherips_ncounters *
646 	    sizeof(uint64_t)));
647 	memset(&etheripstat, 0, sizeof etheripstat);
648 	counters_read(etheripcounters, (uint64_t *)&etheripstat,
649 	    etherips_ncounters);
650 	return (sysctl_rdstruct(oldp, oldlenp, newp, &etheripstat,
651 	    sizeof(etheripstat)));
652 }
653 
654 int
655 etherip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
656     void *newp, size_t newlen)
657 {
658 	int error;
659 
660 	/* All sysctl names at this level are terminal. */
661 	if (namelen != 1)
662 		return ENOTDIR;
663 
664 	switch (name[0]) {
665 	case ETHERIPCTL_ALLOW:
666 		NET_LOCK();
667 		error = sysctl_int(oldp, oldlenp, newp, newlen, &etherip_allow);
668 		NET_UNLOCK();
669 		return (error);
670 	case ETHERIPCTL_STATS:
671 		return (etherip_sysctl_etheripstat(oldp, oldlenp, newp));
672 	default:
673 		break;
674 	}
675 
676 	return ENOPROTOOPT;
677 }
678