xref: /openbsd-src/sys/net/if_etherip.c (revision 90f5036706e4b1909a4dee01fa4e6f5237a503eb)
1 /*	$OpenBSD: if_etherip.c,v 1.26 2017/11/17 14:52:50 jca 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 	if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
103 		return ENOMEM;
104 
105 	ifp = &sc->sc_ac.ac_if;
106 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "etherip%d", unit);
107 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
108 	ether_fakeaddr(ifp);
109 
110 	ifp->if_softc = sc;
111 	ifp->if_ioctl = etherip_ioctl;
112 	ifp->if_start = etherip_start;
113 	ifp->if_xflags = IFXF_CLONED;
114 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
115 
116 	ifp->if_capabilities = IFCAP_VLAN_MTU;
117 
118 	ifmedia_init(&sc->sc_media, 0, etherip_media_change,
119 	    etherip_media_status);
120 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
121 	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
122 
123 	if_attach(ifp);
124 	ether_ifattach(ifp);
125 
126 	LIST_INSERT_HEAD(&etherip_softc_list, sc, sc_entry);
127 
128 	return 0;
129 }
130 
131 int
132 etherip_clone_destroy(struct ifnet *ifp)
133 {
134 	struct etherip_softc *sc = ifp->if_softc;
135 
136 	LIST_REMOVE(sc, sc_entry);
137 
138 	ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
139 	ether_ifdetach(ifp);
140 	if_detach(ifp);
141 	free(sc, M_DEVBUF, sizeof(*sc));
142 
143 	return 0;
144 }
145 
146 int
147 etherip_media_change(struct ifnet *ifp)
148 {
149 	return 0;
150 }
151 
152 void
153 etherip_media_status(struct ifnet *ifp, struct ifmediareq *imr)
154 {
155 	imr->ifm_active = IFM_ETHER | IFM_AUTO;
156 	imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
157 }
158 
159 void
160 etherip_start(struct ifnet *ifp)
161 {
162 	struct etherip_softc *sc = ifp->if_softc;
163 	struct mbuf *m;
164 	int error;
165 
166 	for (;;) {
167 		IFQ_DEQUEUE(&ifp->if_snd, m);
168 		if (m == NULL)
169 			break;
170 
171 #if NBPFILTER > 0
172 		if (ifp->if_bpf)
173 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
174 #endif
175 		if (sc->sc_src.ss_family == AF_UNSPEC ||
176 		    sc->sc_dst.ss_family == AF_UNSPEC) {
177 			m_freem(m);
178 			continue;
179 		}
180 
181 		switch (sc->sc_src.ss_family) {
182 		case AF_INET:
183 			error = ip_etherip_output(ifp, m);
184 			break;
185 #ifdef INET6
186 		case AF_INET6:
187 			error = ip6_etherip_output(ifp, m);
188 			break;
189 #endif
190 		default:
191 			unhandled_af(sc->sc_src.ss_family);
192 		}
193 
194 		if (error)
195 			ifp->if_oerrors++;
196 	}
197 
198 }
199 
200 
201 int
202 etherip_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
203 {
204 	struct etherip_softc *sc = ifp->if_softc;
205 	struct if_laddrreq *lifr = (struct if_laddrreq *)data;
206 	struct ifreq *ifr = (struct ifreq *)data;
207 	struct sockaddr_storage *src, *dst;
208 	struct proc *p = curproc;
209 	int error = 0;
210 
211 	switch (cmd) {
212 	case SIOCSIFADDR:
213 		ifp->if_flags |= IFF_UP;
214 		/* FALLTHROUGH */
215 
216 	case SIOCSIFFLAGS:
217 		if (ifp->if_flags & IFF_UP)
218 			ifp->if_flags |= IFF_RUNNING;
219 		else
220 			ifp->if_flags &= ~IFF_RUNNING;
221 
222 		break;
223 
224 	case SIOCSLIFPHYRTABLE:
225 		if ((error = suser(p, 0)) != 0)
226 			break;
227 
228 		if (ifr->ifr_rdomainid < 0 ||
229 		    ifr->ifr_rdomainid > RT_TABLEID_MAX ||
230 		    !rtable_exists(ifr->ifr_rdomainid)) {
231 			error = EINVAL;
232 			break;
233 		}
234 		sc->sc_rdomain = ifr->ifr_rdomainid;
235 		break;
236 
237 	case SIOCGLIFPHYRTABLE:
238 		ifr->ifr_rdomainid = sc->sc_rdomain;
239 		break;
240 
241 	case SIOCSLIFPHYADDR:
242 		if ((error = suser(p, 0)) != 0)
243 			break;
244 
245 		src = &lifr->addr;
246 		dst = &lifr->dstaddr;
247 		if (src->ss_family == AF_UNSPEC || dst->ss_family == AF_UNSPEC)
248 			return EADDRNOTAVAIL;
249 
250 		switch (src->ss_family) {
251 		case AF_INET:
252 			if (src->ss_len != sizeof(struct sockaddr_in) ||
253 			    dst->ss_len != sizeof(struct sockaddr_in))
254 				return EINVAL;
255 			break;
256 #ifdef INET6
257 		case AF_INET6:
258 			if (src->ss_len != sizeof(struct sockaddr_in6) ||
259 			    dst->ss_len != sizeof(struct sockaddr_in6))
260 				return EINVAL;
261 			break;
262 #endif
263 		default:
264 			return EAFNOSUPPORT;
265 		}
266 
267 		error = etherip_set_tunnel_addr(ifp, src, dst);
268 		break;
269 
270 	case SIOCDIFPHYADDR:
271 		if ((error = suser(p, 0)) != 0)
272 			break;
273 
274 		ifp->if_flags &= ~IFF_RUNNING;
275 		memset(&sc->sc_src, 0, sizeof(sc->sc_src));
276 		memset(&sc->sc_dst, 0, sizeof(sc->sc_dst));
277 		break;
278 
279 	case SIOCGLIFPHYADDR:
280 		if (sc->sc_dst.ss_family == AF_UNSPEC)
281 			return EADDRNOTAVAIL;
282 
283 		memset(&lifr->addr, 0, sizeof(lifr->addr));
284 		memset(&lifr->dstaddr, 0, sizeof(lifr->dstaddr));
285 		memcpy(&lifr->addr, &sc->sc_src, sc->sc_src.ss_len);
286 		memcpy(&lifr->dstaddr, &sc->sc_dst, sc->sc_dst.ss_len);
287 
288 		break;
289 
290 	case SIOCSIFMEDIA:
291 	case SIOCGIFMEDIA:
292 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
293 		break;
294 
295 	default:
296 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
297 		break;
298 	}
299 
300 	return error;
301 }
302 
303 int
304 etherip_set_tunnel_addr(struct ifnet *ifp, struct sockaddr_storage *src,
305     struct sockaddr_storage *dst)
306 {
307 	struct etherip_softc *sc, *tsc;
308 	int error = 0;
309 
310 	sc  = ifp->if_softc;
311 
312 	LIST_FOREACH(tsc, &etherip_softc_list, sc_entry) {
313 		if (tsc == sc)
314 			continue;
315 
316 		if (tsc->sc_src.ss_family != src->ss_family ||
317 		    tsc->sc_dst.ss_family != dst->ss_family ||
318 		    tsc->sc_src.ss_len != src->ss_len ||
319 		    tsc->sc_dst.ss_len != dst->ss_len)
320 			continue;
321 
322 		if (tsc->sc_rdomain == sc->sc_rdomain &&
323 		    memcmp(&tsc->sc_dst, dst, dst->ss_len) == 0 &&
324 		    memcmp(&tsc->sc_src, src, src->ss_len) == 0) {
325 			error = EADDRNOTAVAIL;
326 			goto out;
327 		}
328 	}
329 
330 	memcpy(&sc->sc_src, src, src->ss_len);
331 	memcpy(&sc->sc_dst, dst, dst->ss_len);
332 out:
333 	return error;
334 }
335 
336 int
337 ip_etherip_output(struct ifnet *ifp, struct mbuf *m)
338 {
339 	struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc;
340 	struct sockaddr_in *src, *dst;
341 	struct etherip_header *eip;
342 	struct ip *ip;
343 
344 	src = (struct sockaddr_in *)&sc->sc_src;
345 	dst = (struct sockaddr_in *)&sc->sc_dst;
346 
347 	if (src == NULL || dst == NULL ||
348 	    src->sin_family != AF_INET || dst->sin_family != AF_INET) {
349 		m_freem(m);
350 		return EAFNOSUPPORT;
351 	}
352 	if (dst->sin_addr.s_addr == INADDR_ANY) {
353 		m_freem(m);
354 		return ENETUNREACH;
355 	}
356 
357 	/*
358 	 * Remove multicast and broadcast flags or encapsulated packet
359 	 * ends up as multicast or broadcast packet.
360 	 */
361 	m->m_flags &= ~(M_BCAST|M_MCAST);
362 
363 	M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT);
364 	if (m == NULL) {
365 		etheripstat_inc(etherips_adrops);
366 		return ENOBUFS;
367 	}
368 	eip = mtod(m, struct etherip_header *);
369 	eip->eip_ver = ETHERIP_VERSION;
370 	eip->eip_res = 0;
371 	eip->eip_pad = 0;
372 
373 	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
374 	if (m == NULL) {
375 		etheripstat_inc(etherips_adrops);
376 		return ENOBUFS;
377 	}
378 	ip = mtod(m, struct ip *);
379 	memset(ip, 0, sizeof(struct ip));
380 
381 	ip->ip_v = IPVERSION;
382 	ip->ip_hl = sizeof(struct ip) >> 2;
383 	ip->ip_id = htons(ip_randomid());
384 	ip->ip_tos = IPTOS_LOWDELAY;
385 	ip->ip_p = IPPROTO_ETHERIP;
386 	ip->ip_len = htons(m->m_pkthdr.len);
387 	ip->ip_ttl = IPDEFTTL;
388 	ip->ip_src = src->sin_addr;
389 	ip->ip_dst = dst->sin_addr;
390 
391 	m->m_pkthdr.ph_rtableid = sc->sc_rdomain;
392 
393 #if NPF > 0
394 	pf_pkt_addr_changed(m);
395 #endif
396 	etheripstat_pkt(etherips_opackets, etherips_obytes, m->m_pkthdr.len -
397 	    (sizeof(struct ip) + sizeof(struct etherip_header)));
398 
399 	return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0);
400 }
401 
402 int
403 ip_etherip_input(struct mbuf **mp, int *offp, int proto, int af)
404 {
405 	struct mbuf *m = *mp;
406 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
407 	struct etherip_softc *sc;
408 	const struct ip *ip;
409 	struct etherip_header *eip;
410 	struct sockaddr_in *src, *dst;
411 	struct ifnet *ifp = NULL;
412 
413 	ip = mtod(m, struct ip *);
414 
415 	if (ip->ip_p != IPPROTO_ETHERIP) {
416 		m_freem(m);
417 		ipstat_inc(ips_noproto);
418 		return IPPROTO_DONE;
419 	}
420 
421 	if (!etherip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) {
422 		m_freem(m);
423 		etheripstat_inc(etherips_pdrops);
424 		return IPPROTO_DONE;
425 	}
426 
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 	return ip6_output(m, 0, NULL, IPV6_MINMTU, 0, NULL);
551 
552 drop:
553 	m_freem(m);
554 	return (error);
555 }
556 
557 int
558 ip6_etherip_input(struct mbuf **mp, int *offp, int proto, int af)
559 {
560 	struct mbuf *m = *mp;
561 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
562 	struct etherip_softc *sc;
563 	const struct ip6_hdr *ip6;
564 	struct etherip_header *eip;
565 	struct sockaddr_in6 ipsrc, ipdst;
566 	struct sockaddr_in6 *src6, *dst6;
567 	struct ifnet *ifp = NULL;
568 
569 
570 	if (!etherip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) {
571 		m_freem(m);
572 		etheripstat_inc(etherips_pdrops);
573 		return IPPROTO_NONE;
574 	}
575 
576 	ip6 = mtod(m, const struct ip6_hdr *);
577 	in6_recoverscope(&ipsrc, &ip6->ip6_src);
578 	in6_recoverscope(&ipdst, &ip6->ip6_dst);
579 
580 	LIST_FOREACH(sc, &etherip_softc_list, sc_entry) {
581 		if (sc->sc_src.ss_family != AF_INET6 ||
582 		    sc->sc_dst.ss_family != AF_INET6)
583 			continue;
584 
585 		src6 = (struct sockaddr_in6 *)&sc->sc_src;
586 		dst6 = (struct sockaddr_in6 *)&sc->sc_dst;
587 
588 		if (IN6_ARE_ADDR_EQUAL(&src6->sin6_addr, &ipdst.sin6_addr) &&
589 		    src6->sin6_scope_id == ipdst.sin6_scope_id &&
590 		    IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ipsrc.sin6_addr) &&
591 		    dst6->sin6_scope_id == ipsrc.sin6_scope_id) {
592 			ifp = &sc->sc_ac.ac_if;
593 			break;
594 		}
595 	}
596 
597 	if (ifp == NULL) {
598 		etheripstat_inc(etherips_noifdrops);
599 		m_freem(m);
600 		return IPPROTO_DONE;
601 	}
602 
603 	m_adj(m, *offp);
604 	m = *mp = m_pullup(m, sizeof(struct etherip_header));
605 	if (m == NULL) {
606 		etheripstat_inc(etherips_adrops);
607 		return IPPROTO_DONE;
608 	}
609 
610 	eip = mtod(m, struct etherip_header *);
611 	if ((eip->eip_ver != ETHERIP_VERSION) || eip->eip_pad) {
612 		etheripstat_inc(etherips_adrops);
613 		m_freem(m);
614 		return IPPROTO_DONE;
615 	}
616 	etheripstat_pkt(etherips_ipackets, etherips_ibytes, m->m_pkthdr.len -
617 	    sizeof(struct etherip_header));
618 
619 	m_adj(m, sizeof(struct etherip_header));
620 	m = *mp = m_pullup(m, sizeof(struct ether_header));
621 	if (m == NULL) {
622 		etheripstat_inc(etherips_adrops);
623 		return IPPROTO_DONE;
624 	}
625 
626 	m->m_flags &= ~(M_BCAST|M_MCAST);
627 
628 #if NPF > 0
629 	pf_pkt_addr_changed(m);
630 #endif
631 
632 	ml_enqueue(&ml, m);
633 	if_input(ifp, &ml);
634 	return IPPROTO_DONE;
635 }
636 #endif /* INET6 */
637 
638 int
639 etherip_sysctl_etheripstat(void *oldp, size_t *oldlenp, void *newp)
640 {
641 	struct etheripstat etheripstat;
642 
643 	CTASSERT(sizeof(etheripstat) == (etherips_ncounters *
644 	    sizeof(uint64_t)));
645 	memset(&etheripstat, 0, sizeof etheripstat);
646 	counters_read(etheripcounters, (uint64_t *)&etheripstat,
647 	    etherips_ncounters);
648 	return (sysctl_rdstruct(oldp, oldlenp, newp, &etheripstat,
649 	    sizeof(etheripstat)));
650 }
651 
652 int
653 ip_etherip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
654     void *newp, size_t newlen)
655 {
656 	int error;
657 
658 	/* All sysctl names at this level are terminal. */
659 	if (namelen != 1)
660 		return ENOTDIR;
661 
662 	switch (name[0]) {
663 	case ETHERIPCTL_ALLOW:
664 		NET_LOCK();
665 		error = sysctl_int(oldp, oldlenp, newp, newlen, &etherip_allow);
666 		NET_UNLOCK();
667 		return (error);
668 	case ETHERIPCTL_STATS:
669 		return (etherip_sysctl_etheripstat(oldp, oldlenp, newp));
670 	default:
671 		break;
672 	}
673 
674 	return ENOPROTOOPT;
675 }
676