1 /* $OpenBSD: if_etherip.c,v 1.21 2017/10/25 09:24:09 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 #include <net/rtable.h> 35 36 #include <netinet/in.h> 37 #include <netinet/ip.h> 38 #include <netinet/ip_var.h> 39 #include <netinet/if_ether.h> 40 #include <netinet/ip_ether.h> 41 42 #ifdef INET6 43 #include <netinet/ip6.h> 44 #include <netinet6/ip6_var.h> 45 #endif 46 47 #if NBPFILTER > 0 48 #include <net/bpf.h> 49 #endif 50 51 #if NPF > 0 52 #include <net/pfvar.h> 53 #endif 54 55 #include <net/if_etherip.h> 56 57 struct etherip_softc { 58 struct arpcom sc_ac; 59 struct ifmedia sc_media; 60 unsigned int sc_rdomain; 61 struct sockaddr_storage sc_src; 62 struct sockaddr_storage sc_dst; 63 LIST_ENTRY(etherip_softc) sc_entry; 64 }; 65 66 LIST_HEAD(, etherip_softc) etherip_softc_list; 67 68 #if 0 69 /* 70 * TODO: 71 * At this stage, etherip_allow and etheripstat are defined 72 * at netinet/ip_ether.c. When implementation of etherip is 73 * removed from gif(4), there are moved here. 74 */ 75 76 /* 77 * We can control the acceptance of EtherIP packets by altering the sysctl 78 * net.inet.etherip.allow value. Zero means drop them, all else is acceptance. 79 */ 80 int etherip_allow = 0; 81 82 struct etheripstat etheripstat; 83 #endif 84 85 void etheripattach(int); 86 int etherip_clone_create(struct if_clone *, int); 87 int etherip_clone_destroy(struct ifnet *); 88 int etherip_ioctl(struct ifnet *, u_long, caddr_t); 89 void etherip_start(struct ifnet *); 90 int etherip_media_change(struct ifnet *); 91 void etherip_media_status(struct ifnet *, struct ifmediareq *); 92 int etherip_set_tunnel_addr(struct ifnet *, struct sockaddr_storage *, 93 struct sockaddr_storage *); 94 95 struct if_clone etherip_cloner = IF_CLONE_INITIALIZER("etherip", 96 etherip_clone_create, etherip_clone_destroy); 97 98 99 void 100 etheripattach(int count) 101 { 102 if_clone_attach(ðerip_cloner); 103 } 104 105 int 106 etherip_clone_create(struct if_clone *ifc, int unit) 107 { 108 struct ifnet *ifp; 109 struct etherip_softc *sc; 110 111 if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 112 return ENOMEM; 113 114 ifp = &sc->sc_ac.ac_if; 115 snprintf(ifp->if_xname, sizeof ifp->if_xname, "etherip%d", unit); 116 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 117 ether_fakeaddr(ifp); 118 119 ifp->if_softc = sc; 120 ifp->if_ioctl = etherip_ioctl; 121 ifp->if_start = etherip_start; 122 ifp->if_xflags = IFXF_CLONED; 123 IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 124 125 ifp->if_capabilities = IFCAP_VLAN_MTU; 126 127 ifmedia_init(&sc->sc_media, 0, etherip_media_change, 128 etherip_media_status); 129 ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); 130 ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); 131 132 if_attach(ifp); 133 ether_ifattach(ifp); 134 135 LIST_INSERT_HEAD(ðerip_softc_list, sc, sc_entry); 136 137 return 0; 138 } 139 140 int 141 etherip_clone_destroy(struct ifnet *ifp) 142 { 143 struct etherip_softc *sc = ifp->if_softc; 144 145 LIST_REMOVE(sc, sc_entry); 146 147 ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY); 148 ether_ifdetach(ifp); 149 if_detach(ifp); 150 free(sc, M_DEVBUF, sizeof(*sc)); 151 152 return 0; 153 } 154 155 int 156 etherip_media_change(struct ifnet *ifp) 157 { 158 return 0; 159 } 160 161 void 162 etherip_media_status(struct ifnet *ifp, struct ifmediareq *imr) 163 { 164 imr->ifm_active = IFM_ETHER | IFM_AUTO; 165 imr->ifm_status = IFM_AVALID | IFM_ACTIVE; 166 } 167 168 void 169 etherip_start(struct ifnet *ifp) 170 { 171 struct etherip_softc *sc = ifp->if_softc; 172 struct mbuf *m; 173 int error; 174 175 for (;;) { 176 IFQ_DEQUEUE(&ifp->if_snd, m); 177 if (m == NULL) 178 break; 179 180 #if NBPFILTER > 0 181 if (ifp->if_bpf) 182 bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); 183 #endif 184 if (sc->sc_src.ss_family == AF_UNSPEC || 185 sc->sc_dst.ss_family == AF_UNSPEC) { 186 m_freem(m); 187 continue; 188 } 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 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 ifp->if_flags &= ~IFF_RUNNING; 284 memset(&sc->sc_src, 0, sizeof(sc->sc_src)); 285 memset(&sc->sc_dst, 0, sizeof(sc->sc_dst)); 286 break; 287 288 case SIOCGLIFPHYADDR: 289 if (sc->sc_dst.ss_family == AF_UNSPEC) 290 return EADDRNOTAVAIL; 291 292 memset(&lifr->addr, 0, sizeof(lifr->addr)); 293 memset(&lifr->dstaddr, 0, sizeof(lifr->dstaddr)); 294 memcpy(&lifr->addr, &sc->sc_src, sc->sc_src.ss_len); 295 memcpy(&lifr->dstaddr, &sc->sc_dst, sc->sc_dst.ss_len); 296 297 break; 298 299 case SIOCSIFMEDIA: 300 case SIOCGIFMEDIA: 301 error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 302 break; 303 304 default: 305 error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); 306 break; 307 } 308 309 return error; 310 } 311 312 int 313 etherip_set_tunnel_addr(struct ifnet *ifp, struct sockaddr_storage *src, 314 struct sockaddr_storage *dst) 315 { 316 struct etherip_softc *sc, *tsc; 317 int error = 0; 318 319 sc = ifp->if_softc; 320 321 LIST_FOREACH(tsc, ðerip_softc_list, sc_entry) { 322 if (tsc == sc) 323 continue; 324 325 if (tsc->sc_src.ss_family != src->ss_family || 326 tsc->sc_dst.ss_family != dst->ss_family || 327 tsc->sc_src.ss_len != src->ss_len || 328 tsc->sc_dst.ss_len != dst->ss_len) 329 continue; 330 331 if (tsc->sc_rdomain == sc->sc_rdomain && 332 memcmp(&tsc->sc_dst, dst, dst->ss_len) == 0 && 333 memcmp(&tsc->sc_src, src, src->ss_len) == 0) { 334 error = EADDRNOTAVAIL; 335 goto out; 336 } 337 } 338 339 memcpy(&sc->sc_src, src, src->ss_len); 340 memcpy(&sc->sc_dst, dst, dst->ss_len); 341 out: 342 return error; 343 } 344 345 int 346 ip_etherip_output(struct ifnet *ifp, struct mbuf *m) 347 { 348 struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc; 349 struct sockaddr_in *src, *dst; 350 struct etherip_header *eip; 351 struct ip *ip; 352 353 src = (struct sockaddr_in *)&sc->sc_src; 354 dst = (struct sockaddr_in *)&sc->sc_dst; 355 356 if (src == NULL || dst == NULL || 357 src->sin_family != AF_INET || dst->sin_family != AF_INET) { 358 m_freem(m); 359 return EAFNOSUPPORT; 360 } 361 if (dst->sin_addr.s_addr == INADDR_ANY) { 362 m_freem(m); 363 return ENETUNREACH; 364 } 365 366 /* 367 * Remove multicast and broadcast flags or encapsulated packet 368 * ends up as multicast or broadcast packet. 369 */ 370 m->m_flags &= ~(M_BCAST|M_MCAST); 371 372 M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT); 373 if (m == NULL) { 374 etheripstat.etherips_adrops++; 375 return ENOBUFS; 376 } 377 eip = mtod(m, struct etherip_header *); 378 eip->eip_ver = ETHERIP_VERSION; 379 eip->eip_res = 0; 380 eip->eip_pad = 0; 381 382 M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); 383 if (m == NULL) { 384 etheripstat.etherips_adrops++; 385 return ENOBUFS; 386 } 387 ip = mtod(m, struct ip *); 388 memset(ip, 0, sizeof(struct ip)); 389 390 ip->ip_v = IPVERSION; 391 ip->ip_hl = sizeof(struct ip) >> 2; 392 ip->ip_id = htons(ip_randomid()); 393 ip->ip_tos = IPTOS_LOWDELAY; 394 ip->ip_p = IPPROTO_ETHERIP; 395 ip->ip_len = htons(m->m_pkthdr.len); 396 ip->ip_ttl = IPDEFTTL; 397 ip->ip_src = src->sin_addr; 398 ip->ip_dst = dst->sin_addr; 399 400 m->m_pkthdr.ph_rtableid = sc->sc_rdomain; 401 402 #if NPF > 0 403 pf_pkt_addr_changed(m); 404 #endif 405 etheripstat.etherips_opackets++; 406 etheripstat.etherips_obytes += (m->m_pkthdr.len - 407 (sizeof(struct ip) + sizeof(struct etherip_header))); 408 409 return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0); 410 } 411 412 int 413 ip_etherip_input(struct mbuf **mp, int *offp, int proto, int af) 414 { 415 struct mbuf *m = *mp; 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 423 ip = mtod(m, struct ip *); 424 425 if (ip->ip_p != IPPROTO_ETHERIP) { 426 m_freem(m); 427 ipstat_inc(ips_noproto); 428 return IPPROTO_DONE; 429 } 430 431 if (!etherip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) { 432 m_freem(m); 433 etheripstat.etherips_pdrops++; 434 return IPPROTO_DONE; 435 } 436 437 LIST_FOREACH(sc, ðerip_softc_list, sc_entry) { 438 if (sc->sc_src.ss_family != AF_INET || 439 sc->sc_dst.ss_family != AF_INET) 440 continue; 441 442 src = (struct sockaddr_in *)&sc->sc_src; 443 dst = (struct sockaddr_in *)&sc->sc_dst; 444 445 if (sc->sc_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid) || 446 src->sin_addr.s_addr != ip->ip_dst.s_addr || 447 dst->sin_addr.s_addr != ip->ip_src.s_addr) 448 continue; 449 450 ifp = &sc->sc_ac.ac_if; 451 break; 452 } 453 454 if (ifp == NULL) { 455 #if NGIF > 0 456 /* 457 * This path is nessesary for gif(4) and etherip(4) coexistence. 458 * This is tricky but the path will be removed soon when 459 * implementation of etherip is removed from gif(4). 460 */ 461 return etherip_input(mp, offp, proto, af); 462 #else 463 etheripstat.etherips_noifdrops++; 464 m_freem(m); 465 return IPPROTO_DONE; 466 #endif /* NGIF */ 467 } 468 469 m_adj(m, *offp); 470 m = *mp = m_pullup(m, sizeof(struct etherip_header)); 471 if (m == NULL) { 472 etheripstat.etherips_adrops++; 473 return IPPROTO_DONE; 474 } 475 476 eip = mtod(m, struct etherip_header *); 477 if (eip->eip_ver != ETHERIP_VERSION || eip->eip_pad) { 478 etheripstat.etherips_adrops++; 479 m_freem(m); 480 return IPPROTO_DONE; 481 } 482 483 etheripstat.etherips_ipackets++; 484 etheripstat.etherips_ibytes += (m->m_pkthdr.len - 485 sizeof(struct etherip_header)); 486 487 m_adj(m, sizeof(struct etherip_header)); 488 m = *mp = m_pullup(m, sizeof(struct ether_header)); 489 if (m == NULL) { 490 etheripstat.etherips_adrops++; 491 return IPPROTO_DONE; 492 } 493 m->m_flags &= ~(M_BCAST|M_MCAST); 494 495 #if NPF > 0 496 pf_pkt_addr_changed(m); 497 #endif 498 499 ml_enqueue(&ml, m); 500 if_input(ifp, &ml); 501 return IPPROTO_DONE; 502 } 503 504 #ifdef INET6 505 int 506 ip6_etherip_output(struct ifnet *ifp, struct mbuf *m) 507 { 508 struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc; 509 struct sockaddr_in6 *src, *dst; 510 struct etherip_header *eip; 511 struct ip6_hdr *ip6; 512 int error; 513 514 src = (struct sockaddr_in6 *)&sc->sc_src; 515 dst = (struct sockaddr_in6 *)&sc->sc_dst; 516 517 if (src == NULL || dst == NULL || 518 src->sin6_family != AF_INET6 || dst->sin6_family != AF_INET6) { 519 error = EAFNOSUPPORT; 520 goto drop; 521 } 522 if (IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) { 523 error = ENETUNREACH; 524 goto drop; 525 } 526 527 /* 528 * Remove multicast and broadcast flags or encapsulated packet 529 * ends up as multicast or broadcast packet. 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.etherips_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.etherips_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 error = in6_embedscope(&ip6->ip6_src, src, NULL); 556 if (error != 0) 557 goto drop; 558 error = in6_embedscope(&ip6->ip6_dst, dst, NULL); 559 if (error != 0) 560 goto drop; 561 562 m->m_pkthdr.ph_rtableid = sc->sc_rdomain; 563 564 #if NPF > 0 565 pf_pkt_addr_changed(m); 566 #endif 567 etheripstat.etherips_opackets++; 568 etheripstat.etherips_obytes += (m->m_pkthdr.len - 569 (sizeof(struct ip6_hdr) + sizeof(struct etherip_header))); 570 571 return ip6_output(m, 0, NULL, IPV6_MINMTU, 0, NULL); 572 573 drop: 574 m_freem(m); 575 return (error); 576 } 577 578 int 579 ip6_etherip_input(struct mbuf **mp, int *offp, int proto, int af) 580 { 581 struct mbuf *m = *mp; 582 struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 583 struct etherip_softc *sc; 584 const struct ip6_hdr *ip6; 585 struct etherip_header *eip; 586 struct sockaddr_in6 ipsrc, ipdst; 587 struct sockaddr_in6 *src6, *dst6; 588 struct ifnet *ifp = NULL; 589 590 591 if (!etherip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) { 592 m_freem(m); 593 etheripstat.etherips_pdrops++; 594 return IPPROTO_NONE; 595 } 596 597 ip6 = mtod(m, const struct ip6_hdr *); 598 in6_recoverscope(&ipsrc, &ip6->ip6_src); 599 in6_recoverscope(&ipdst, &ip6->ip6_dst); 600 601 LIST_FOREACH(sc, ðerip_softc_list, sc_entry) { 602 if (sc->sc_src.ss_family != AF_INET6 || 603 sc->sc_dst.ss_family != AF_INET6) 604 continue; 605 606 src6 = (struct sockaddr_in6 *)&sc->sc_src; 607 dst6 = (struct sockaddr_in6 *)&sc->sc_dst; 608 609 if (IN6_ARE_ADDR_EQUAL(&src6->sin6_addr, &ipdst.sin6_addr) && 610 src6->sin6_scope_id == ipdst.sin6_scope_id && 611 IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ipsrc.sin6_addr) && 612 dst6->sin6_scope_id == ipsrc.sin6_scope_id) { 613 ifp = &sc->sc_ac.ac_if; 614 break; 615 } 616 } 617 618 if (ifp == NULL) { 619 #if NGIF > 0 620 /* 621 * This path is nessesary for gif(4) and etherip(4) coexistence. 622 * This is tricky but the path will be removed soon when 623 * implementation of etherip is removed from gif(4). 624 */ 625 return etherip_input(mp, offp, proto, af); 626 #else 627 etheripstat.etherips_noifdrops++; 628 m_freem(m); 629 return IPPROTO_DONE; 630 #endif /* NGIF */ 631 } 632 633 m_adj(m, *offp); 634 m = *mp = m_pullup(m, sizeof(struct etherip_header)); 635 if (m == NULL) { 636 etheripstat.etherips_adrops++; 637 return IPPROTO_DONE; 638 } 639 640 eip = mtod(m, struct etherip_header *); 641 if ((eip->eip_ver != ETHERIP_VERSION) || eip->eip_pad) { 642 etheripstat.etherips_adrops++; 643 m_freem(m); 644 return IPPROTO_DONE; 645 } 646 etheripstat.etherips_ipackets++; 647 etheripstat.etherips_ibytes += (m->m_pkthdr.len - 648 sizeof(struct etherip_header)); 649 650 m_adj(m, sizeof(struct etherip_header)); 651 m = *mp = m_pullup(m, sizeof(struct ether_header)); 652 if (m == NULL) { 653 etheripstat.etherips_adrops++; 654 return IPPROTO_DONE; 655 } 656 657 m->m_flags &= ~(M_BCAST|M_MCAST); 658 659 #if NPF > 0 660 pf_pkt_addr_changed(m); 661 #endif 662 663 ml_enqueue(&ml, m); 664 if_input(ifp, &ml); 665 return IPPROTO_DONE; 666 } 667 #endif /* INET6 */ 668 669 int 670 ip_etherip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 671 void *newp, size_t newlen) 672 { 673 int error; 674 675 /* All sysctl names at this level are terminal. */ 676 if (namelen != 1) 677 return ENOTDIR; 678 679 switch (name[0]) { 680 case ETHERIPCTL_ALLOW: 681 NET_LOCK(); 682 error = sysctl_int(oldp, oldlenp, newp, newlen, ðerip_allow); 683 NET_UNLOCK(); 684 return (error); 685 case ETHERIPCTL_STATS: 686 if (newp != NULL) 687 return EPERM; 688 NET_LOCK(); 689 error = sysctl_struct(oldp, oldlenp, newp, newlen, 690 ðeripstat, sizeof(etheripstat)); 691 NET_UNLOCK(); 692 return (error); 693 default: 694 break; 695 } 696 697 return ENOPROTOOPT; 698 } 699