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$ 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 56 #include <net/if.h> 57 #include <net/if_types.h> 58 #include <net/if_arp.h> 59 #include <net/if_var.h> 60 #include <net/ethernet.h> 61 62 #include <netgraph/ng_message.h> 63 #include <netgraph/netgraph.h> 64 #include <netgraph/ng_parse.h> 65 #include <netgraph/ng_ether.h> 66 67 #define IFP2AC(IFP) ((struct arpcom *)IFP) 68 #define IFP2NG(ifp) ((struct ng_node *)((struct arpcom *)(ifp))->ac_netgraph) 69 70 /* Per-node private data */ 71 struct private { 72 struct ifnet *ifp; /* associated interface */ 73 hook_p upper; /* upper hook connection */ 74 hook_p lower; /* lower OR orphan hook connection */ 75 u_char lowerOrphan; /* whether lower is lower or orphan */ 76 u_char autoSrcAddr; /* always overwrite source address */ 77 u_char promisc; /* promiscuous mode enabled */ 78 u_long hwassist; /* hardware checksum capabilities */ 79 u_int flags; /* flags e.g. really die */ 80 }; 81 typedef struct private *priv_p; 82 83 /* Hook pointers used by if_ethersubr.c to callback to netgraph */ 84 extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); 85 extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); 86 extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); 87 extern void (*ng_ether_attach_p)(struct ifnet *ifp); 88 extern void (*ng_ether_detach_p)(struct ifnet *ifp); 89 90 /* Functional hooks called from if_ethersubr.c */ 91 static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); 92 static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); 93 static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); 94 static void ng_ether_attach(struct ifnet *ifp); 95 static void ng_ether_detach(struct ifnet *ifp); 96 97 /* Other functions */ 98 static void ng_ether_input2(node_p node, struct mbuf **mp); 99 static int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); 100 static int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta); 101 102 /* Netgraph node methods */ 103 static ng_constructor_t ng_ether_constructor; 104 static ng_rcvmsg_t ng_ether_rcvmsg; 105 static ng_shutdown_t ng_ether_shutdown; 106 static ng_newhook_t ng_ether_newhook; 107 static ng_connect_t ng_ether_connect; 108 static ng_rcvdata_t ng_ether_rcvdata; 109 static ng_disconnect_t ng_ether_disconnect; 110 static int ng_ether_mod_event(module_t mod, int event, void *data); 111 112 /* Parse type for an Ethernet address */ 113 static ng_parse_t ng_enaddr_parse; 114 static ng_unparse_t ng_enaddr_unparse; 115 const struct ng_parse_type ng_ether_enaddr_type = { 116 NULL, 117 NULL, 118 NULL, 119 ng_enaddr_parse, 120 ng_enaddr_unparse, 121 NULL, /* no such thing as a "default" EN address */ 122 0 123 }; 124 125 /* List of commands and how to convert arguments to/from ASCII */ 126 static const struct ng_cmdlist ng_ether_cmdlist[] = { 127 { 128 NGM_ETHER_COOKIE, 129 NGM_ETHER_GET_IFNAME, 130 "getifname", 131 NULL, 132 &ng_parse_string_type 133 }, 134 { 135 NGM_ETHER_COOKIE, 136 NGM_ETHER_GET_IFINDEX, 137 "getifindex", 138 NULL, 139 &ng_parse_int32_type 140 }, 141 { 142 NGM_ETHER_COOKIE, 143 NGM_ETHER_GET_ENADDR, 144 "getenaddr", 145 NULL, 146 &ng_ether_enaddr_type 147 }, 148 { 149 NGM_ETHER_COOKIE, 150 NGM_ETHER_SET_ENADDR, 151 "setenaddr", 152 &ng_ether_enaddr_type, 153 NULL 154 }, 155 { 156 NGM_ETHER_COOKIE, 157 NGM_ETHER_GET_PROMISC, 158 "getpromisc", 159 NULL, 160 &ng_parse_int32_type 161 }, 162 { 163 NGM_ETHER_COOKIE, 164 NGM_ETHER_SET_PROMISC, 165 "setpromisc", 166 &ng_parse_int32_type, 167 NULL 168 }, 169 { 170 NGM_ETHER_COOKIE, 171 NGM_ETHER_GET_AUTOSRC, 172 "getautosrc", 173 NULL, 174 &ng_parse_int32_type 175 }, 176 { 177 NGM_ETHER_COOKIE, 178 NGM_ETHER_SET_AUTOSRC, 179 "setautosrc", 180 &ng_parse_int32_type, 181 NULL 182 }, 183 { 0 } 184 }; 185 186 static struct ng_type ng_ether_typestruct = { 187 NG_ABI_VERSION, 188 NG_ETHER_NODE_TYPE, 189 ng_ether_mod_event, 190 ng_ether_constructor, 191 ng_ether_rcvmsg, 192 ng_ether_shutdown, 193 ng_ether_newhook, 194 NULL, 195 ng_ether_connect, 196 ng_ether_rcvdata, 197 ng_ether_disconnect, 198 ng_ether_cmdlist, 199 }; 200 MODULE_VERSION(ng_ether, 1); 201 NETGRAPH_INIT(ether, &ng_ether_typestruct); 202 203 /****************************************************************** 204 ETHERNET FUNCTION HOOKS 205 ******************************************************************/ 206 207 /* 208 * Handle a packet that has come in on an interface. We get to 209 * look at it here before any upper layer protocols do. 210 * 211 * NOTE: this function will get called at splimp() 212 */ 213 static void 214 ng_ether_input(struct ifnet *ifp, struct mbuf **mp) 215 { 216 const node_p node = IFP2NG(ifp); 217 const priv_p priv = NG_NODE_PRIVATE(node); 218 219 /* If "lower" hook not connected, let packet continue */ 220 if (priv->lower == NULL || priv->lowerOrphan) 221 return; 222 ng_ether_input2(node, mp); 223 } 224 225 /* 226 * Handle a packet that has come in on an interface, and which 227 * does not match any of our known protocols (an ``orphan''). 228 * 229 * NOTE: this function will get called at splimp() 230 */ 231 static void 232 ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) 233 { 234 const node_p node = IFP2NG(ifp); 235 const priv_p priv = NG_NODE_PRIVATE(node); 236 237 /* If "orphan" hook not connected, let packet continue */ 238 if (priv->lower == NULL || !priv->lowerOrphan) { 239 m_freem(m); 240 return; 241 } 242 ng_ether_input2(node, &m); 243 if (m != NULL) 244 m_freem(m); 245 } 246 247 /* 248 * Handle a packet that has come in on an ethernet interface. 249 * The Ethernet header has already been detached from the mbuf, 250 * so we have to put it back. 251 * 252 * NOTE: this function will get called at splimp() 253 */ 254 static void 255 ng_ether_input2(node_p node, struct mbuf **mp) 256 { 257 const priv_p priv = NG_NODE_PRIVATE(node); 258 int error; 259 260 /* Send out lower/orphan hook */ 261 NG_SEND_DATA_ONLY(error, priv->lower, *mp); 262 *mp = NULL; 263 } 264 265 /* 266 * Handle a packet that is going out on an interface. 267 * The Ethernet header is already attached to the mbuf. 268 */ 269 static int 270 ng_ether_output(struct ifnet *ifp, struct mbuf **mp) 271 { 272 const node_p node = IFP2NG(ifp); 273 const priv_p priv = NG_NODE_PRIVATE(node); 274 int error = 0; 275 276 /* If "upper" hook not connected, let packet continue */ 277 if (priv->upper == NULL) 278 return (0); 279 280 /* Send it out "upper" hook */ 281 NG_SEND_DATA_ONLY(error, priv->upper, *mp); 282 return (error); 283 } 284 285 /* 286 * A new Ethernet interface has been attached. 287 * Create a new node for it, etc. 288 */ 289 static void 290 ng_ether_attach(struct ifnet *ifp) 291 { 292 char name[IFNAMSIZ + 1]; 293 priv_p priv; 294 node_p node; 295 296 /* Create node */ 297 KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); 298 snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit); 299 if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { 300 log(LOG_ERR, "%s: can't %s for %s\n", 301 __func__, "create node", name); 302 return; 303 } 304 305 /* Allocate private data */ 306 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 307 if (priv == NULL) { 308 log(LOG_ERR, "%s: can't %s for %s\n", 309 __func__, "allocate memory", name); 310 NG_NODE_UNREF(node); 311 return; 312 } 313 NG_NODE_SET_PRIVATE(node, priv); 314 priv->ifp = ifp; 315 IFP2NG(ifp) = node; 316 priv->autoSrcAddr = 1; 317 priv->hwassist = ifp->if_hwassist; 318 319 /* Try to give the node the same name as the interface */ 320 if (ng_name_node(node, name) != 0) { 321 log(LOG_WARNING, "%s: can't name node %s\n", 322 __func__, name); 323 } 324 } 325 326 /* 327 * An Ethernet interface is being detached. 328 * REALLY Destroy its node. 329 */ 330 static void 331 ng_ether_detach(struct ifnet *ifp) 332 { 333 const node_p node = IFP2NG(ifp); 334 const priv_p priv = NG_NODE_PRIVATE(node); 335 336 if (node == NULL) /* no node (why not?), ignore */ 337 return; 338 NG_NODE_REALLY_DIE(node); /* Force real removal of node */ 339 /* 340 * We can't assume the ifnet is still around when we run shutdown 341 * So zap it now. XXX We HOPE that anything running at this time 342 * handles it (as it should in the non netgraph case). 343 */ 344 IFP2NG(ifp) = NULL; 345 priv->ifp = NULL; /* XXX race if interrupted an output packet */ 346 ng_rmnode_self(node); /* remove all netgraph parts */ 347 } 348 349 /****************************************************************** 350 NETGRAPH NODE METHODS 351 ******************************************************************/ 352 353 /* 354 * It is not possible or allowable to create a node of this type. 355 * Nodes get created when the interface is attached (or, when 356 * this node type's KLD is loaded). 357 */ 358 static int 359 ng_ether_constructor(node_p node) 360 { 361 return (EINVAL); 362 } 363 364 /* 365 * Check for attaching a new hook. 366 */ 367 static int 368 ng_ether_newhook(node_p node, hook_p hook, const char *name) 369 { 370 const priv_p priv = NG_NODE_PRIVATE(node); 371 u_char orphan = priv->lowerOrphan; 372 hook_p *hookptr; 373 374 /* Divert hook is an alias for lower */ 375 if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) 376 name = NG_ETHER_HOOK_LOWER; 377 378 /* Which hook? */ 379 if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) 380 hookptr = &priv->upper; 381 else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { 382 hookptr = &priv->lower; 383 orphan = 0; 384 } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { 385 hookptr = &priv->lower; 386 orphan = 1; 387 } else 388 return (EINVAL); 389 390 /* Check if already connected (shouldn't be, but doesn't hurt) */ 391 if (*hookptr != NULL) 392 return (EISCONN); 393 394 /* Disable hardware checksums while 'upper' hook is connected */ 395 if (hookptr == &priv->upper) 396 priv->ifp->if_hwassist = 0; 397 398 /* OK */ 399 *hookptr = hook; 400 priv->lowerOrphan = orphan; 401 return (0); 402 } 403 404 /* 405 * Hooks are attached, adjust to force queueing. 406 * We don't really care which hook it is. 407 * they should all be queuing for outgoing data. 408 */ 409 static int 410 ng_ether_connect(hook_p hook) 411 { 412 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 413 return (0); 414 } 415 416 /* 417 * Receive an incoming control message. 418 */ 419 static int 420 ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) 421 { 422 const priv_p priv = NG_NODE_PRIVATE(node); 423 struct ng_mesg *resp = NULL; 424 int error = 0; 425 struct ng_mesg *msg; 426 427 NGI_GET_MSG(item, msg); 428 switch (msg->header.typecookie) { 429 case NGM_ETHER_COOKIE: 430 switch (msg->header.cmd) { 431 case NGM_ETHER_GET_IFNAME: 432 NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 433 if (resp == NULL) { 434 error = ENOMEM; 435 break; 436 } 437 snprintf(resp->data, IFNAMSIZ + 1, 438 "%s%d", priv->ifp->if_name, priv->ifp->if_unit); 439 break; 440 case NGM_ETHER_GET_IFINDEX: 441 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 442 if (resp == NULL) { 443 error = ENOMEM; 444 break; 445 } 446 *((u_int32_t *)resp->data) = priv->ifp->if_index; 447 break; 448 case NGM_ETHER_GET_ENADDR: 449 NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); 450 if (resp == NULL) { 451 error = ENOMEM; 452 break; 453 } 454 bcopy((IFP2AC(priv->ifp))->ac_enaddr, 455 resp->data, ETHER_ADDR_LEN); 456 break; 457 case NGM_ETHER_SET_ENADDR: 458 { 459 if (msg->header.arglen != ETHER_ADDR_LEN) { 460 error = EINVAL; 461 break; 462 } 463 error = if_setlladdr(priv->ifp, 464 (u_char *)msg->data, ETHER_ADDR_LEN); 465 break; 466 } 467 case NGM_ETHER_GET_PROMISC: 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->promisc; 474 break; 475 case NGM_ETHER_SET_PROMISC: 476 { 477 u_char want; 478 479 if (msg->header.arglen != sizeof(u_int32_t)) { 480 error = EINVAL; 481 break; 482 } 483 want = !!*((u_int32_t *)msg->data); 484 if (want ^ priv->promisc) { 485 if ((error = ifpromisc(priv->ifp, want)) != 0) 486 break; 487 priv->promisc = want; 488 } 489 break; 490 } 491 case NGM_ETHER_GET_AUTOSRC: 492 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); 493 if (resp == NULL) { 494 error = ENOMEM; 495 break; 496 } 497 *((u_int32_t *)resp->data) = priv->autoSrcAddr; 498 break; 499 case NGM_ETHER_SET_AUTOSRC: 500 if (msg->header.arglen != sizeof(u_int32_t)) { 501 error = EINVAL; 502 break; 503 } 504 priv->autoSrcAddr = !!*((u_int32_t *)msg->data); 505 break; 506 default: 507 error = EINVAL; 508 break; 509 } 510 break; 511 default: 512 error = EINVAL; 513 break; 514 } 515 NG_RESPOND_MSG(error, node, item, resp); 516 NG_FREE_MSG(msg); 517 return (error); 518 } 519 520 /* 521 * Receive data on a hook. 522 */ 523 static int 524 ng_ether_rcvdata(hook_p hook, item_p item) 525 { 526 const node_p node = NG_HOOK_NODE(hook); 527 const priv_p priv = NG_NODE_PRIVATE(node); 528 struct mbuf *m; 529 meta_p meta; 530 531 NGI_GET_M(item, m); 532 NGI_GET_META(item, meta); 533 NG_FREE_ITEM(item); 534 if (hook == priv->lower) 535 return ng_ether_rcv_lower(node, m, meta); 536 if (hook == priv->upper) 537 return ng_ether_rcv_upper(node, m, meta); 538 panic("%s: weird hook", __func__); 539 #ifdef RESTARTABLE_PANICS /* so we don;t get an error msg in LINT */ 540 return NULL; 541 #endif 542 } 543 544 /* 545 * Handle an mbuf received on the "lower" hook. 546 */ 547 static int 548 ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta) 549 { 550 const priv_p priv = NG_NODE_PRIVATE(node); 551 struct ifnet *const ifp = priv->ifp; 552 553 /* Discard meta info */ 554 NG_FREE_META(meta); 555 556 /* Check whether interface is ready for packets */ 557 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 558 NG_FREE_M(m); 559 return (ENETDOWN); 560 } 561 562 /* Make sure header is fully pulled up */ 563 if (m->m_pkthdr.len < sizeof(struct ether_header)) { 564 NG_FREE_M(m); 565 return (EINVAL); 566 } 567 if (m->m_len < sizeof(struct ether_header) 568 && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 569 return (ENOBUFS); 570 571 /* Drop in the MAC address if desired */ 572 if (priv->autoSrcAddr) { 573 574 /* Make the mbuf writable if it's not already */ 575 if (!M_WRITABLE(m) 576 && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) 577 return (ENOBUFS); 578 579 /* Overwrite source MAC address */ 580 bcopy((IFP2AC(ifp))->ac_enaddr, 581 mtod(m, struct ether_header *)->ether_shost, 582 ETHER_ADDR_LEN); 583 } 584 585 /* Send it on its way */ 586 return ether_output_frame(ifp, m); 587 } 588 589 /* 590 * Handle an mbuf received on the "upper" hook. 591 */ 592 static int 593 ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta) 594 { 595 const priv_p priv = NG_NODE_PRIVATE(node); 596 597 /* Discard meta info */ 598 NG_FREE_META(meta); 599 600 m->m_pkthdr.rcvif = priv->ifp; 601 602 /* Route packet back in */ 603 ether_demux(priv->ifp, m); 604 return (0); 605 } 606 607 /* 608 * Shutdown node. This resets the node but does not remove it 609 * unless the REALLY_DIE flag is set. 610 */ 611 static int 612 ng_ether_shutdown(node_p node) 613 { 614 const priv_p priv = NG_NODE_PRIVATE(node); 615 616 if (priv->promisc) { /* disable promiscuous mode */ 617 (void)ifpromisc(priv->ifp, 0); 618 priv->promisc = 0; 619 } 620 if (node->nd_flags & NG_REALLY_DIE) { 621 /* 622 * WE came here because the ethernet card is being unloaded, 623 * so stop being persistant. 624 * Actually undo all the things we did on creation. 625 * Assume the ifp has already been freed. 626 */ 627 NG_NODE_SET_PRIVATE(node, NULL); 628 FREE(priv, M_NETGRAPH); 629 NG_NODE_UNREF(node); /* free node itself */ 630 return (0); 631 } 632 priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ 633 node->nd_flags &= ~NG_INVALID; /* Signal ng_rmnode we are persisant */ 634 return (0); 635 } 636 637 /* 638 * Hook disconnection. 639 */ 640 static int 641 ng_ether_disconnect(hook_p hook) 642 { 643 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 644 645 if (hook == priv->upper) { 646 priv->upper = NULL; 647 priv->ifp->if_hwassist = priv->hwassist; /* restore h/w csum */ 648 } else if (hook == priv->lower) { 649 priv->lower = NULL; 650 priv->lowerOrphan = 0; 651 } else 652 panic("%s: weird hook", __func__); 653 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 654 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 655 ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ 656 return (0); 657 } 658 659 static int 660 ng_enaddr_parse(const struct ng_parse_type *type, 661 const char *s, int *const off, const u_char *const start, 662 u_char *const buf, int *const buflen) 663 { 664 char *eptr; 665 u_long val; 666 int i; 667 668 if (*buflen < ETHER_ADDR_LEN) 669 return (ERANGE); 670 for (i = 0; i < ETHER_ADDR_LEN; i++) { 671 val = strtoul(s + *off, &eptr, 16); 672 if (val > 0xff || eptr == s + *off) 673 return (EINVAL); 674 buf[i] = (u_char)val; 675 *off = (eptr - s); 676 if (i < ETHER_ADDR_LEN - 1) { 677 if (*eptr != ':') 678 return (EINVAL); 679 (*off)++; 680 } 681 } 682 *buflen = ETHER_ADDR_LEN; 683 return (0); 684 } 685 686 static int 687 ng_enaddr_unparse(const struct ng_parse_type *type, 688 const u_char *data, int *off, char *cbuf, int cbuflen) 689 { 690 int len; 691 692 len = snprintf(cbuf, cbuflen, "%02x:%02x:%02x:%02x:%02x:%02x", 693 data[*off], data[*off + 1], data[*off + 2], 694 data[*off + 3], data[*off + 4], data[*off + 5]); 695 if (len >= cbuflen) 696 return (ERANGE); 697 *off += ETHER_ADDR_LEN; 698 return (0); 699 } 700 701 /****************************************************************** 702 INITIALIZATION 703 ******************************************************************/ 704 705 /* 706 * Handle loading and unloading for this node type. 707 */ 708 static int 709 ng_ether_mod_event(module_t mod, int event, void *data) 710 { 711 struct ifnet *ifp; 712 int error = 0; 713 int s; 714 715 s = splnet(); 716 switch (event) { 717 case MOD_LOAD: 718 719 /* Register function hooks */ 720 if (ng_ether_attach_p != NULL) { 721 error = EEXIST; 722 break; 723 } 724 ng_ether_attach_p = ng_ether_attach; 725 ng_ether_detach_p = ng_ether_detach; 726 ng_ether_output_p = ng_ether_output; 727 ng_ether_input_p = ng_ether_input; 728 ng_ether_input_orphan_p = ng_ether_input_orphan; 729 730 /* Create nodes for any already-existing Ethernet interfaces */ 731 IFNET_RLOCK(); 732 TAILQ_FOREACH(ifp, &ifnet, if_link) { 733 if (ifp->if_type == IFT_ETHER 734 || ifp->if_type == IFT_L2VLAN) 735 ng_ether_attach(ifp); 736 } 737 IFNET_RUNLOCK(); 738 break; 739 740 case MOD_UNLOAD: 741 742 /* 743 * Note that the base code won't try to unload us until 744 * all nodes have been removed, and that can't happen 745 * until all Ethernet interfaces are removed. In any 746 * case, we know there are no nodes left if the action 747 * is MOD_UNLOAD, so there's no need to detach any nodes. 748 */ 749 750 /* Unregister function hooks */ 751 ng_ether_attach_p = NULL; 752 ng_ether_detach_p = NULL; 753 ng_ether_output_p = NULL; 754 ng_ether_input_p = NULL; 755 ng_ether_input_orphan_p = NULL; 756 break; 757 758 default: 759 error = EOPNOTSUPP; 760 break; 761 } 762 splx(s); 763 return (error); 764 } 765 766