1 2 /* 3 * ng_ether.c 4 * 5 * Copyright (c) 1996-2000 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Authors: Archie Cobbs <archie@freebsd.org> 38 * Julian Elischer <julian@freebsd.org> 39 * 40 * $FreeBSD: src/sys/netgraph/ng_ether.c,v 1.2.2.13 2002/07/02 20:10:25 archie Exp $ 41 */ 42 43 /* 44 * ng_ether(4) netgraph node type 45 */ 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/kernel.h> 50 #include <sys/malloc.h> 51 #include <sys/mbuf.h> 52 #include <sys/errno.h> 53 #include <sys/syslog.h> 54 #include <sys/socket.h> 55 #include <sys/thread2.h> 56 57 #include <net/if.h> 58 #include <net/if_types.h> 59 #include <net/if_arp.h> 60 #include <net/if_var.h> 61 #include <net/ethernet.h> 62 63 #include <netgraph/ng_message.h> 64 #include <netgraph/netgraph.h> 65 #include <netgraph/ng_parse.h> 66 #include "ng_ether.h" 67 68 #define IFP2AC(IFP) ((struct arpcom *)IFP) 69 #define IFP2NG(ifp) (IFP2AC((ifp))->ac_netgraph) 70 71 /* Per-node private data */ 72 struct private { 73 struct ifnet *ifp; /* associated interface */ 74 hook_p upper; /* upper hook connection */ 75 hook_p lower; /* lower OR orphan hook connection */ 76 u_char lowerOrphan; /* whether lower is lower or orphan */ 77 u_char autoSrcAddr; /* always overwrite source address */ 78 u_char promisc; /* promiscuous mode enabled */ 79 u_long hwassist; /* hardware checksum capabilities */ 80 }; 81 typedef struct private *priv_p; 82 83 /* Functional hooks called from if_ethersubr.c */ 84 static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 85 static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 86 static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 87 static void ng_ether_attach(struct ifnet *ifp); 88 static void ng_ether_detach(struct ifnet *ifp); 89 90 /* Other functions */ 91 static void ng_ether_input2(node_p node, struct mbuf **mp); 92 static int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); 93 static int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta); 94 95 /* Netgraph node methods */ 96 static ng_constructor_t ng_ether_constructor; 97 static ng_rcvmsg_t ng_ether_rcvmsg; 98 static ng_shutdown_t ng_ether_rmnode; 99 static ng_newhook_t ng_ether_newhook; 100 static ng_rcvdata_t ng_ether_rcvdata; 101 static ng_disconnect_t ng_ether_disconnect; 102 static int ng_ether_mod_event(module_t mod, int event, void *data); 103 104 /* Parse type for an Ethernet address */ 105 static ng_parse_t ng_enaddr_parse; 106 static ng_unparse_t ng_enaddr_unparse; 107 const struct ng_parse_type ng_ether_enaddr_type = { 108 NULL, 109 NULL, 110 NULL, 111 ng_enaddr_parse, 112 ng_enaddr_unparse, 113 NULL, /* no such thing as a "default" EN address */ 114 0 115 }; 116 117 /* List of commands and how to convert arguments to/from ASCII */ 118 static const struct ng_cmdlist ng_ether_cmdlist[] = { 119 { 120 NGM_ETHER_COOKIE, 121 NGM_ETHER_GET_IFNAME, 122 "getifname", 123 NULL, 124 &ng_parse_string_type 125 }, 126 { 127 NGM_ETHER_COOKIE, 128 NGM_ETHER_GET_IFINDEX, 129 "getifindex", 130 NULL, 131 &ng_parse_int32_type 132 }, 133 { 134 NGM_ETHER_COOKIE, 135 NGM_ETHER_GET_ENADDR, 136 "getenaddr", 137 NULL, 138 &ng_ether_enaddr_type 139 }, 140 { 141 NGM_ETHER_COOKIE, 142 NGM_ETHER_SET_ENADDR, 143 "setenaddr", 144 &ng_ether_enaddr_type, 145 NULL 146 }, 147 { 148 NGM_ETHER_COOKIE, 149 NGM_ETHER_GET_PROMISC, 150 "getpromisc", 151 NULL, 152 &ng_parse_int32_type 153 }, 154 { 155 NGM_ETHER_COOKIE, 156 NGM_ETHER_SET_PROMISC, 157 "setpromisc", 158 &ng_parse_int32_type, 159 NULL 160 }, 161 { 162 NGM_ETHER_COOKIE, 163 NGM_ETHER_GET_AUTOSRC, 164 "getautosrc", 165 NULL, 166 &ng_parse_int32_type 167 }, 168 { 169 NGM_ETHER_COOKIE, 170 NGM_ETHER_SET_AUTOSRC, 171 "setautosrc", 172 &ng_parse_int32_type, 173 NULL 174 }, 175 { 0 } 176 }; 177 178 static struct ng_type ng_ether_typestruct = { 179 NG_VERSION, 180 NG_ETHER_NODE_TYPE, 181 ng_ether_mod_event, 182 ng_ether_constructor, 183 ng_ether_rcvmsg, 184 ng_ether_rmnode, 185 ng_ether_newhook, 186 NULL, 187 NULL, 188 ng_ether_rcvdata, 189 ng_ether_rcvdata, 190 ng_ether_disconnect, 191 ng_ether_cmdlist, 192 }; 193 NETGRAPH_INIT(ether, &ng_ether_typestruct); 194 195 /****************************************************************** 196 ETHERNET FUNCTION HOOKS 197 ******************************************************************/ 198 199 /* 200 * Handle a packet that has come in on an interface. We get to 201 * look at it here before any upper layer protocols do. 202 */ 203 static void 204 ng_ether_input(struct ifnet *ifp, struct mbuf **mp) 205 { 206 const node_p node = IFP2NG(ifp); 207 const priv_p priv = node->private; 208 209 /* If "lower" hook not connected, let packet continue */ 210 if (priv->lower == NULL || priv->lowerOrphan) 211 return; 212 ng_ether_input2(node, mp); 213 } 214 215 /* 216 * Handle a packet that has come in on an interface, and which 217 * does not match any of our known protocols (an ``orphan''). 218 */ 219 static void 220 ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 221 { 222 const node_p node = IFP2NG(ifp); 223 const priv_p priv = node->private; 224 225 /* If "orphan" hook not connected, let packet continue */ 226 if (priv->lower == NULL || !priv->lowerOrphan) { 227 m_freem(m); 228 return; 229 } 230 ng_ether_input2(node, &m); 231 if (m != NULL) 232 m_freem(m); 233 } 234 235 /* 236 * Handle a packet that has come in on an interface. 237 * The Ethernet header has already been detached from the mbuf, 238 * so we have to put it back. 239 * 240 * NOTE: this function will get called at splimp() 241 */ 242 static void 243 ng_ether_input2(node_p node, struct mbuf **mp) 244 { 245 const priv_p priv = node->private; 246 meta_p meta = NULL; 247 248 /* Send out lower/orphan hook */ 249 ng_queue_data(priv->lower, *mp, meta); 250 *mp = NULL; 251 } 252 253 /* 254 * Handle a packet that is going out on an interface. 255 * The Ethernet header is already attached to the mbuf. 256 */ 257 static int 258 ng_ether_output(struct ifnet *ifp, struct mbuf **mp) 259 { 260 const node_p node = IFP2NG(ifp); 261 const priv_p priv = node->private; 262 meta_p meta = NULL; 263 int error = 0; 264 265 /* If "upper" hook not connected, let packet continue */ 266 if (priv->upper == NULL) 267 return (0); 268 269 /* Send it out "upper" hook */ 270 NG_SEND_DATA(error, priv->upper, *mp, meta); 271 *mp = NULL; 272 return (error); 273 } 274 275 /* 276 * A new Ethernet interface has been attached. 277 * Create a new node for it, etc. 278 */ 279 static void 280 ng_ether_attach(struct ifnet *ifp) 281 { 282 char name[IFNAMSIZ + 1]; 283 priv_p priv; 284 node_p node; 285 286 /* Create node */ 287 KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 288 strlcpy(name, ifp->if_xname, sizeof(name)); 289 if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 290 log(LOG_ERR, "%s: can't %s for %s\n", 291 __func__, "create node", name); 292 return; 293 } 294 295 /* Allocate private data */ 296 priv = kmalloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 297 if (priv == NULL) { 298 log(LOG_ERR, "%s: can't %s for %s\n", 299 __func__, "allocate memory", name); 300 ng_unref(node); 301 return; 302 } 303 node->private = priv; 304 priv->ifp = ifp; 305 IFP2NG(ifp) = node; 306 priv->autoSrcAddr = 1; 307 priv->hwassist = ifp->if_hwassist; 308 309 /* Try to give the node the same name as the interface */ 310 if (ng_name_node(node, name) != 0) { 311 log(LOG_WARNING, "%s: can't name node %s\n", 312 __func__, name); 313 } 314 } 315 316 /* 317 * An Ethernet interface is being detached. 318 * Destroy its node. 319 */ 320 static void 321 ng_ether_detach(struct ifnet *ifp) 322 { 323 const node_p node = IFP2NG(ifp); 324 priv_p priv; 325 326 if (node == NULL) /* no node (why not?), ignore */ 327 return; 328 ng_rmnode(node); /* break all links to other nodes */ 329 node->flags |= NG_INVALID; 330 ng_unname(node); /* free name (and its reference) */ 331 IFP2NG(ifp) = NULL; /* detach node from interface */ 332 priv = node->private; /* free node private info */ 333 bzero(priv, sizeof(*priv)); 334 kfree(priv, M_NETGRAPH); 335 node->private = NULL; 336 ng_unref(node); /* free node itself */ 337 } 338 339 /****************************************************************** 340 NETGRAPH NODE METHODS 341 ******************************************************************/ 342 343 /* 344 * It is not possible or allowable to create a node of this type. 345 * Nodes get created when the interface is attached (or, when 346 * this node type's KLD is loaded). 347 */ 348 static int 349 ng_ether_constructor(node_p *nodep) 350 { 351 return (EINVAL); 352 } 353 354 /* 355 * Check for attaching a new hook. 356 */ 357 static int 358 ng_ether_newhook(node_p node, hook_p hook, const char *name) 359 { 360 const priv_p priv = node->private; 361 u_char orphan = priv->lowerOrphan; 362 hook_p *hookptr; 363 364 /* Divert hook is an alias for lower */ 365 if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 366 name = NG_ETHER_HOOK_LOWER; 367 368 /* Which hook? */ 369 if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 370 hookptr = &priv->upper; 371 else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 372 hookptr = &priv->lower; 373 orphan = 0; 374 } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 375 hookptr = &priv->lower; 376 orphan = 1; 377 } else 378 return (EINVAL); 379 380 /* Check if already connected (shouldn't be, but doesn't hurt) */ 381 if (*hookptr != NULL) 382 return (EISCONN); 383 384 /* Disable hardware checksums while 'upper' hook is connected */ 385 if (hookptr == &priv->upper) 386 priv->ifp->if_hwassist = 0; 387 388 /* OK */ 389 *hookptr = hook; 390 priv->lowerOrphan = orphan; 391 return (0); 392 } 393 394 /* 395 * Receive an incoming control message. 396 */ 397 static int 398 ng_ether_rcvmsg(node_p node, struct ng_mesg *msg, 399 const char *retaddr, struct ng_mesg **rptr) 400 { 401 const priv_p priv = node->private; 402 struct ng_mesg *resp = NULL; 403 int error = 0; 404 405 switch (msg->header.typecookie) { 406 case NGM_ETHER_COOKIE: 407 switch (msg->header.cmd) { 408 case NGM_ETHER_GET_IFNAME: 409 NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 410 if (resp == NULL) { 411 error = ENOMEM; 412 break; 413 } 414 strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ); 415 break; 416 case NGM_ETHER_GET_IFINDEX: 417 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 418 if (resp == NULL) { 419 error = ENOMEM; 420 break; 421 } 422 *((u_int32_t *)resp->data) = priv->ifp->if_index; 423 break; 424 case NGM_ETHER_GET_ENADDR: 425 NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 426 if (resp == NULL) { 427 error = ENOMEM; 428 break; 429 } 430 bcopy((IFP2AC(priv->ifp))->ac_enaddr, 431 resp->data, ETHER_ADDR_LEN); 432 break; 433 case NGM_ETHER_SET_ENADDR: 434 { 435 if (msg->header.arglen != ETHER_ADDR_LEN) { 436 error = EINVAL; 437 break; 438 } 439 error = if_setlladdr(priv->ifp, 440 (u_char *)msg->data, ETHER_ADDR_LEN); 441 break; 442 } 443 case NGM_ETHER_GET_PROMISC: 444 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 445 if (resp == NULL) { 446 error = ENOMEM; 447 break; 448 } 449 *((u_int32_t *)resp->data) = priv->promisc; 450 break; 451 case NGM_ETHER_SET_PROMISC: 452 { 453 u_char want; 454 455 if (msg->header.arglen != sizeof(u_int32_t)) { 456 error = EINVAL; 457 break; 458 } 459 want = !!*((u_int32_t *)msg->data); 460 if (want ^ priv->promisc) { 461 if ((error = ifpromisc(priv->ifp, want)) != 0) 462 break; 463 priv->promisc = want; 464 } 465 break; 466 } 467 case NGM_ETHER_GET_AUTOSRC: 468 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 469 if (resp == NULL) { 470 error = ENOMEM; 471 break; 472 } 473 *((u_int32_t *)resp->data) = priv->autoSrcAddr; 474 break; 475 case NGM_ETHER_SET_AUTOSRC: 476 if (msg->header.arglen != sizeof(u_int32_t)) { 477 error = EINVAL; 478 break; 479 } 480 priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 481 break; 482 default: 483 error = EINVAL; 484 break; 485 } 486 break; 487 default: 488 error = EINVAL; 489 break; 490 } 491 if (rptr) 492 *rptr = resp; 493 else if (resp != NULL) 494 kfree(resp, M_NETGRAPH); 495 kfree(msg, M_NETGRAPH); 496 return (error); 497 } 498 499 /* 500 * Receive data on a hook. 501 */ 502 static int 503 ng_ether_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 504 { 505 const node_p node = hook->node; 506 const priv_p priv = node->private; 507 508 if (hook == priv->lower) 509 return ng_ether_rcv_lower(node, m, meta); 510 if (hook == priv->upper) 511 return ng_ether_rcv_upper(node, m, meta); 512 panic("%s: weird hook", __func__); 513 } 514 515 /* 516 * Handle an mbuf received on the "lower" hook. 517 */ 518 static int 519 ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta) 520 { 521 const priv_p priv = node->private; 522 struct ifnet *const ifp = priv->ifp; 523 int error; 524 525 /* Discard meta info */ 526 NG_FREE_META(meta); 527 528 /* Check whether interface is ready for packets */ 529 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 530 m_freem(m); 531 return (ENETDOWN); 532 } 533 534 /* Make sure header is fully pulled up */ 535 if (m->m_pkthdr.len < sizeof(struct ether_header)) { 536 m_freem(m); 537 return (EINVAL); 538 } 539 if (m->m_len < sizeof(struct ether_header) 540 && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 541 return (ENOBUFS); 542 543 /* Drop in the MAC address if desired */ 544 if (priv->autoSrcAddr) { 545 546 /* Make the mbuf writable if it's not already */ 547 if (!M_WRITABLE(m) 548 && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 549 return (ENOBUFS); 550 551 /* Overwrite source MAC address */ 552 bcopy((IFP2AC(ifp))->ac_enaddr, 553 mtod(m, struct ether_header *)->ether_shost, 554 ETHER_ADDR_LEN); 555 } 556 557 /* Send it on its way */ 558 error = ether_output_frame(ifp, m); 559 return (error); 560 } 561 562 /* 563 * Handle an mbuf received on the "upper" hook. 564 */ 565 static int 566 ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta) 567 { 568 const priv_p priv = node->private; 569 570 /* Discard meta info */ 571 NG_FREE_META(meta); 572 573 /* Check length and pull off header */ 574 if (m->m_pkthdr.len < ETHER_HDR_LEN) { 575 m_freem(m); 576 return (EINVAL); 577 } 578 if (m->m_len < ETHER_HDR_LEN && 579 (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) 580 return (ENOBUFS); 581 582 m->m_pkthdr.rcvif = priv->ifp; 583 584 /* Route packet back in */ 585 ether_demux(m); 586 return (0); 587 } 588 589 /* 590 * Shutdown node. This resets the node but does not remove it. 591 */ 592 static int 593 ng_ether_rmnode(node_p node) 594 { 595 const priv_p priv = node->private; 596 597 ng_cutlinks(node); 598 node->flags &= ~NG_INVALID; /* bounce back to life */ 599 if (priv->promisc) { /* disable promiscuous mode */ 600 ifpromisc(priv->ifp, 0); 601 priv->promisc = 0; 602 } 603 priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 604 return (0); 605 } 606 607 /* 608 * Hook disconnection. 609 */ 610 static int 611 ng_ether_disconnect(hook_p hook) 612 { 613 const priv_p priv = hook->node->private; 614 615 if (hook == priv->upper) { 616 priv->upper = NULL; 617 priv->ifp->if_hwassist = priv->hwassist; /* restore h/w csum */ 618 } else if (hook == priv->lower) { 619 priv->lower = NULL; 620 priv->lowerOrphan = 0; 621 } else 622 panic("%s: weird hook", __func__); 623 if (hook->node->numhooks == 0) 624 ng_rmnode(hook->node); /* reset node */ 625 return (0); 626 } 627 628 static int 629 ng_enaddr_parse(const struct ng_parse_type *type, 630 const char *s, int *const off, const u_char *const start, 631 u_char *const buf, int *const buflen) 632 { 633 char *eptr; 634 u_long val; 635 int i; 636 637 if (*buflen < ETHER_ADDR_LEN) 638 return (ERANGE); 639 for (i = 0; i < ETHER_ADDR_LEN; i++) { 640 val = strtoul(s + *off, &eptr, 16); 641 if (val > 0xff || eptr == s + *off) 642 return (EINVAL); 643 buf[i] = (u_char)val; 644 *off = (eptr - s); 645 if (i < ETHER_ADDR_LEN - 1) { 646 if (*eptr != ':') 647 return (EINVAL); 648 (*off)++; 649 } 650 } 651 *buflen = ETHER_ADDR_LEN; 652 return (0); 653 } 654 655 static int 656 ng_enaddr_unparse(const struct ng_parse_type *type, 657 const u_char *data, int *off, char *cbuf, int cbuflen) 658 { 659 int len; 660 661 len = ksnprintf(cbuf, cbuflen, "%02x:%02x:%02x:%02x:%02x:%02x", 662 data[*off], data[*off + 1], data[*off + 2], 663 data[*off + 3], data[*off + 4], data[*off + 5]); 664 if (len >= cbuflen) 665 return (ERANGE); 666 *off += ETHER_ADDR_LEN; 667 return (0); 668 } 669 670 /****************************************************************** 671 INITIALIZATION 672 ******************************************************************/ 673 674 /* 675 * Handle loading and unloading for this node type. 676 */ 677 static int 678 ng_ether_mod_event(module_t mod, int event, void *data) 679 { 680 struct ifnet *ifp; 681 int error = 0; 682 683 crit_enter(); 684 switch (event) { 685 case MOD_LOAD: 686 687 /* Register function hooks */ 688 if (ng_ether_attach_p != NULL) { 689 error = EEXIST; 690 break; 691 } 692 ng_ether_attach_p = ng_ether_attach; 693 ng_ether_detach_p = ng_ether_detach; 694 ng_ether_output_p = ng_ether_output; 695 ng_ether_input_p = ng_ether_input; 696 ng_ether_input_orphan_p = ng_ether_input_orphan; 697 698 /* Create nodes for any already-existing Ethernet interfaces */ 699 ifnet_lock(); 700 TAILQ_FOREACH(ifp, &ifnetlist, if_link) { 701 if (ifp->if_type == IFT_ETHER || 702 ifp->if_type == IFT_L2VLAN) 703 ng_ether_attach(ifp); 704 } 705 ifnet_unlock(); 706 break; 707 708 case MOD_UNLOAD: 709 710 /* 711 * Note that the base code won't try to unload us until 712 * all nodes have been removed, and that can't happen 713 * until all Ethernet interfaces are removed. In any 714 * case, we know there are no nodes left if the action 715 * is MOD_UNLOAD, so there's no need to detach any nodes. 716 */ 717 718 /* Unregister function hooks */ 719 ng_ether_attach_p = NULL; 720 ng_ether_detach_p = NULL; 721 ng_ether_output_p = NULL; 722 ng_ether_input_p = NULL; 723 ng_ether_input_orphan_p = NULL; 724 break; 725 726 default: 727 error = EOPNOTSUPP; 728 break; 729 } 730 crit_exit(); 731 return (error); 732 } 733 734