ng_fec.c revision 184214
1/* 2 * ng_fec.c 3 */ 4 5/*- 6 * Copyright (c) 2001 Berkeley Software Design, Inc. 7 * Copyright (c) 2000, 2001 8 * Bill Paul <wpaul@osd.bsdi.com>. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by Bill Paul. 21 * 4. Neither the name of the author nor the names of any co-contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 35 * THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * $FreeBSD: head/sys/netgraph/ng_fec.c 184214 2008-10-23 20:26:15Z des $ 38 */ 39/*- 40 * Copyright (c) 1996-1999 Whistle Communications, Inc. 41 * All rights reserved. 42 * 43 * Subject to the following obligations and disclaimer of warranty, use and 44 * redistribution of this software, in source or object code forms, with or 45 * without modifications are expressly permitted by Whistle Communications; 46 * provided, however, that: 47 * 1. Any and all reproductions of the source or object code must include the 48 * copyright notice above and the following disclaimer of warranties; and 49 * 2. No rights are granted, in any manner or form, to use Whistle 50 * Communications, Inc. trademarks, including the mark "WHISTLE 51 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 52 * such appears in the above copyright notice or in the software. 53 * 54 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 55 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 56 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 57 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 58 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 59 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 60 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 61 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 62 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 63 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 64 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 65 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 66 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 67 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 68 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 69 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 70 * OF SUCH DAMAGE. 71 * 72 * Author: Archie Cobbs <archie@freebsd.org> 73 * 74 * $Whistle: ng_fec.c,v 1.33 1999/11/01 09:24:51 julian Exp $ 75 */ 76 77/* 78 * This module implements ethernet channel bonding using the Cisco 79 * Fast EtherChannel mechanism. Two or four ports may be combined 80 * into a single aggregate interface. 81 * 82 * Interfaces are named fec0, fec1, etc. New nodes take the 83 * first available interface name. 84 * 85 * This node also includes Berkeley packet filter support. 86 * 87 * Note that this node doesn't need to connect to any other 88 * netgraph nodes in order to do its work. 89 */ 90 91#include <sys/param.h> 92#include <sys/systm.h> 93#include <sys/errno.h> 94#include <sys/kernel.h> 95#include <sys/malloc.h> 96#include <sys/mbuf.h> 97#include <sys/errno.h> 98#include <sys/sockio.h> 99#include <sys/socket.h> 100#include <sys/syslog.h> 101#include <sys/libkern.h> 102#include <sys/queue.h> 103 104#include <net/if.h> 105#include <net/if_dl.h> 106#include <net/if_types.h> 107#include <net/if_media.h> 108#include <net/bpf.h> 109#include <net/ethernet.h> 110 111#include "opt_inet.h" 112#include "opt_inet6.h" 113 114#include <netinet/in.h> 115#ifdef INET 116#include <netinet/in_systm.h> 117#include <netinet/ip.h> 118#endif 119 120#ifdef INET6 121#include <netinet/ip6.h> 122#endif 123 124#include <netgraph/ng_message.h> 125#include <netgraph/netgraph.h> 126#include <netgraph/ng_parse.h> 127#include <netgraph/ng_fec.h> 128 129/* 130 * We need a way to stash a pointer to our netgraph node in the 131 * ifnet structure so that receive handling works. As far as I can 132 * tell, although there is an AF_NETGRAPH address family, it's only 133 * used to identify sockaddr_ng structures: there is no netgraph address 134 * family domain. This means the AF_NETGRAPH entry in ifp->if_afdata 135 * should be unused, so we can use to hold our node context. 136 */ 137#define IFP2NG(ifp) ((ifp)->if_afdata[AF_NETGRAPH]) 138 139/* 140 * Current fast etherchannel implementations use either 2 or 4 141 * ports, so for now we limit the maximum bundle size to 4 interfaces. 142 */ 143#define FEC_BUNDLESIZ 4 144 145struct ng_fec_portlist { 146 struct ifnet *fec_if; 147 void (*fec_if_input) (struct ifnet *, 148 struct mbuf *); 149 int fec_idx; 150 int fec_ifstat; 151 struct ether_addr fec_mac; 152 SLIST_HEAD(__mclhd, ng_fec_mc) fec_mc_head; 153 TAILQ_ENTRY(ng_fec_portlist) fec_list; 154}; 155 156struct ng_fec_mc { 157 struct ifmultiaddr *mc_ifma; 158 SLIST_ENTRY(ng_fec_mc) mc_entries; 159}; 160 161struct ng_fec_bundle { 162 TAILQ_HEAD(,ng_fec_portlist) ng_fec_ports; 163 int fec_ifcnt; 164 int fec_btype; 165 int (*fec_if_output) (struct ifnet *, 166 struct mbuf *, 167 struct sockaddr *, 168 struct rtentry *); 169}; 170 171#define FEC_BTYPE_MAC 0x01 172#define FEC_BTYPE_INET 0x02 173#define FEC_BTYPE_INET6 0x03 174 175/* Node private data */ 176struct ng_fec_private { 177 struct ifnet *ifp; 178 struct ifmedia ifmedia; 179 int if_flags; 180 int if_error; /* XXX */ 181 int unit; /* Interface unit number */ 182 node_p node; /* Our netgraph node */ 183 struct ng_fec_bundle fec_bundle;/* Aggregate bundle */ 184 struct callout_handle fec_ch; /* callout handle for ticker */ 185}; 186typedef struct ng_fec_private *priv_p; 187 188/* Interface methods */ 189static void ng_fec_input(struct ifnet *, struct mbuf *); 190static void ng_fec_start(struct ifnet *ifp); 191static int ng_fec_choose_port(struct ng_fec_bundle *b, 192 struct mbuf *m, struct ifnet **ifp); 193static int ng_fec_setport(struct ifnet *ifp, u_long cmd, caddr_t data); 194static void ng_fec_init(void *arg); 195static void ng_fec_stop(struct ifnet *ifp); 196static int ng_fec_ifmedia_upd(struct ifnet *ifp); 197static void ng_fec_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr); 198static int ng_fec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); 199static int ng_fec_output(struct ifnet *ifp, struct mbuf *m0, 200 struct sockaddr *dst, struct rtentry *rt0); 201static void ng_fec_tick(void *arg); 202static int ng_fec_addport(struct ng_fec_private *priv, char *iface); 203static int ng_fec_delport(struct ng_fec_private *priv, char *iface); 204static int ng_fec_ether_cmdmulti(struct ifnet *trifp, struct ng_fec_portlist *p, int set); 205 206#ifdef DEBUG 207static void ng_fec_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); 208#endif 209 210/* Netgraph methods */ 211static int ng_fec_mod_event(module_t, int, void *); 212static ng_constructor_t ng_fec_constructor; 213static ng_rcvmsg_t ng_fec_rcvmsg; 214static ng_shutdown_t ng_fec_shutdown; 215 216/* List of commands and how to convert arguments to/from ASCII */ 217static const struct ng_cmdlist ng_fec_cmds[] = { 218 { 219 NGM_FEC_COOKIE, 220 NGM_FEC_ADD_IFACE, 221 "add_iface", 222 &ng_parse_string_type, 223 NULL, 224 }, 225 { 226 NGM_FEC_COOKIE, 227 NGM_FEC_DEL_IFACE, 228 "del_iface", 229 &ng_parse_string_type, 230 NULL, 231 }, 232 { 233 NGM_FEC_COOKIE, 234 NGM_FEC_SET_MODE_MAC, 235 "set_mode_mac", 236 NULL, 237 NULL, 238 }, 239 { 240 NGM_FEC_COOKIE, 241 NGM_FEC_SET_MODE_INET, 242 "set_mode_inet", 243 NULL, 244 NULL, 245 }, 246 { 0 } 247}; 248 249/* Node type descriptor */ 250static struct ng_type typestruct = { 251 .version = NG_ABI_VERSION, 252 .name = NG_FEC_NODE_TYPE, 253 .mod_event = ng_fec_mod_event, 254 .constructor = ng_fec_constructor, 255 .rcvmsg = ng_fec_rcvmsg, 256 .shutdown = ng_fec_shutdown, 257 .cmdlist = ng_fec_cmds, 258}; 259NETGRAPH_INIT(fec, &typestruct); 260 261/* We keep a bitmap indicating which unit numbers are free. 262 One means the unit number is free, zero means it's taken. */ 263static int *ng_fec_units = NULL; 264static int ng_fec_units_len = 0; 265static int ng_units_in_use = 0; 266 267#define UNITS_BITSPERWORD (sizeof(*ng_fec_units) * NBBY) 268 269static struct mtx ng_fec_mtx; 270 271/* 272 * Find the first free unit number for a new interface. 273 * Increase the size of the unit bitmap as necessary. 274 */ 275static __inline int 276ng_fec_get_unit(int *unit) 277{ 278 int index, bit; 279 280 mtx_lock(&ng_fec_mtx); 281 for (index = 0; index < ng_fec_units_len 282 && ng_fec_units[index] == 0; index++); 283 if (index == ng_fec_units_len) { /* extend array */ 284 int i, *newarray, newlen; 285 286 newlen = (2 * ng_fec_units_len) + 4; 287 newarray = malloc(newlen * sizeof(*ng_fec_units), 288 M_NETGRAPH, M_NOWAIT); 289 if (newarray == NULL) { 290 mtx_unlock(&ng_fec_mtx); 291 return (ENOMEM); 292 } 293 bcopy(ng_fec_units, newarray, 294 ng_fec_units_len * sizeof(*ng_fec_units)); 295 for (i = ng_fec_units_len; i < newlen; i++) 296 newarray[i] = ~0; 297 if (ng_fec_units != NULL) 298 free(ng_fec_units, M_NETGRAPH); 299 ng_fec_units = newarray; 300 ng_fec_units_len = newlen; 301 } 302 bit = ffs(ng_fec_units[index]) - 1; 303 KASSERT(bit >= 0 && bit <= UNITS_BITSPERWORD - 1, 304 ("%s: word=%d bit=%d", __func__, ng_fec_units[index], bit)); 305 ng_fec_units[index] &= ~(1 << bit); 306 *unit = (index * UNITS_BITSPERWORD) + bit; 307 ng_units_in_use++; 308 mtx_unlock(&ng_fec_mtx); 309 return (0); 310} 311 312/* 313 * Free a no longer needed unit number. 314 */ 315static __inline void 316ng_fec_free_unit(int unit) 317{ 318 int index, bit; 319 320 index = unit / UNITS_BITSPERWORD; 321 bit = unit % UNITS_BITSPERWORD; 322 mtx_lock(&ng_fec_mtx); 323 KASSERT(index < ng_fec_units_len, 324 ("%s: unit=%d len=%d", __func__, unit, ng_fec_units_len)); 325 KASSERT((ng_fec_units[index] & (1 << bit)) == 0, 326 ("%s: unit=%d is free", __func__, unit)); 327 ng_fec_units[index] |= (1 << bit); 328 /* 329 * XXX We could think about reducing the size of ng_fec_units[] 330 * XXX here if the last portion is all ones 331 * XXX At least free it if no more units 332 * Needed if we are to eventually be able to unload. 333 */ 334 ng_units_in_use--; 335 if (ng_units_in_use == 0) { /* XXX make SMP safe */ 336 free(ng_fec_units, M_NETGRAPH); 337 ng_fec_units_len = 0; 338 ng_fec_units = NULL; 339 } 340 mtx_unlock(&ng_fec_mtx); 341} 342 343/************************************************************************ 344 INTERFACE STUFF 345 ************************************************************************/ 346 347static int 348ng_fec_addport(struct ng_fec_private *priv, char *iface) 349{ 350 struct ng_fec_bundle *b; 351 struct ifnet *ifp, *bifp; 352 struct ng_fec_portlist *p, *new; 353 354 if (priv == NULL || iface == NULL) 355 return(EINVAL); 356 357 b = &priv->fec_bundle; 358 ifp = priv->ifp; 359 360 /* Only allow reconfiguration if not running. */ 361 if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 362 printf("fec%d: can't add new iface; bundle is running\n", 363 priv->unit); 364 return (EINVAL); 365 } 366 367 /* Find the interface */ 368 bifp = ifunit(iface); 369 if (bifp == NULL) { 370 printf("fec%d: tried to add iface %s, which " 371 "doesn't seem to exist\n", priv->unit, iface); 372 return(ENOENT); 373 } 374 375 /* See if we have room in the bundle */ 376 if (b->fec_ifcnt == FEC_BUNDLESIZ) { 377 printf("fec%d: can't add new iface; bundle is full\n", 378 priv->unit); 379 return(ENOSPC); 380 } 381 382 /* See if the interface is already in the bundle */ 383 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 384 if (p->fec_if == bifp) { 385 printf("fec%d: iface %s is already in this " 386 "bundle\n", priv->unit, iface); 387 return(EINVAL); 388 } 389 } 390 391 /* 392 * All interfaces must use the same output vector. Once the 393 * user attaches an interface of one type, make all subsequent 394 * interfaces have the same output vector. 395 */ 396 if (b->fec_if_output != NULL) { 397 if (b->fec_if_output != bifp->if_output) { 398 printf("fec%d: iface %s is not the same type " 399 "as the other interface(s) already in " 400 "the bundle\n", priv->unit, iface); 401 return(EINVAL); 402 } 403 } 404 405 /* Allocate new list entry. */ 406 new = malloc(sizeof(struct ng_fec_portlist), M_NETGRAPH, M_NOWAIT); 407 if (new == NULL) 408 return(ENOMEM); 409 410 IF_AFDATA_LOCK(bifp); 411 IFP2NG(bifp) = priv->node; 412 IF_AFDATA_UNLOCK(bifp); 413 414 /* 415 * If this is the first interface added to the bundle, 416 * use its MAC address for the virtual interface (and, 417 * by extension, all the other ports in the bundle). 418 */ 419 if (b->fec_ifcnt == 0) 420 if_setlladdr(ifp, IF_LLADDR(bifp), ETHER_ADDR_LEN); 421 422 b->fec_btype = FEC_BTYPE_MAC; 423 new->fec_idx = b->fec_ifcnt; 424 b->fec_ifcnt++; 425 426 /* Initialise the list of multicast addresses that we own. */ 427 SLIST_INIT(&new->fec_mc_head); 428 429 /* Save the real MAC address. */ 430 bcopy(IF_LLADDR(bifp), 431 (char *)&new->fec_mac, ETHER_ADDR_LEN); 432 433 /* Set up phony MAC address. */ 434 if_setlladdr(bifp, IF_LLADDR(ifp), ETHER_ADDR_LEN); 435 436 /* Save original input vector */ 437 new->fec_if_input = bifp->if_input; 438 439 /* Override it with our own */ 440 bifp->if_input = ng_fec_input; 441 442 /* Save output vector too. */ 443 if (b->fec_if_output == NULL) 444 b->fec_if_output = bifp->if_output; 445 446 /* Add to the queue */ 447 new->fec_if = bifp; 448 new->fec_ifstat = -1; 449 TAILQ_INSERT_TAIL(&b->ng_fec_ports, new, fec_list); 450 451 /* Add multicast addresses to this port. */ 452 ng_fec_ether_cmdmulti(ifp, new, 1); 453 454 return(0); 455} 456 457static int 458ng_fec_delport(struct ng_fec_private *priv, char *iface) 459{ 460 struct ng_fec_bundle *b; 461 struct ifnet *ifp, *bifp; 462 struct ng_fec_portlist *p; 463 464 if (priv == NULL || iface == NULL) 465 return(EINVAL); 466 467 b = &priv->fec_bundle; 468 ifp = priv->ifp; 469 470 /* Only allow reconfiguration if not running. */ 471 if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 472 printf("fec%d: can't remove iface; bundle is running\n", 473 priv->unit); 474 return (EINVAL); 475 } 476 477 /* Find the interface */ 478 bifp = ifunit(iface); 479 if (bifp == NULL) { 480 printf("fec%d: tried to remove iface %s, which " 481 "doesn't seem to exist\n", priv->unit, iface); 482 return(ENOENT); 483 } 484 485 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 486 if (p->fec_if == bifp) 487 break; 488 } 489 490 if (p == NULL) { 491 printf("fec%d: tried to remove iface %s which " 492 "is not in our bundle\n", priv->unit, iface); 493 return(EINVAL); 494 } 495 496 /* Stop interface */ 497 bifp->if_flags &= ~IFF_UP; 498 (*bifp->if_ioctl)(bifp, SIOCSIFFLAGS, NULL); 499 500 /* Restore MAC address. */ 501 if_setlladdr(bifp, (u_char *)&p->fec_mac, ETHER_ADDR_LEN); 502 503 /* Restore input vector */ 504 bifp->if_input = p->fec_if_input; 505 506 /* Remove our node context pointer. */ 507 IF_AFDATA_LOCK(bifp); 508 IFP2NG(bifp) = NULL; 509 IF_AFDATA_UNLOCK(bifp); 510 511 /* Delete port */ 512 TAILQ_REMOVE(&b->ng_fec_ports, p, fec_list); 513 free(p, M_NETGRAPH); 514 b->fec_ifcnt--; 515 516 if (b->fec_ifcnt == 0) 517 b->fec_if_output = NULL; 518 519 return(0); 520} 521 522static int 523ng_fec_ether_cmdmulti(struct ifnet *trifp, struct ng_fec_portlist *p, int set) 524{ 525 struct ifnet *ifp = p->fec_if; 526 struct ng_fec_mc *mc; 527 struct ifmultiaddr *ifma, *rifma = NULL; 528 struct sockaddr_dl sdl; 529 int error; 530 531 bzero((char *)&sdl, sizeof(sdl)); 532 sdl.sdl_len = sizeof(sdl); 533 sdl.sdl_family = AF_LINK; 534 sdl.sdl_type = IFT_ETHER; 535 sdl.sdl_alen = ETHER_ADDR_LEN; 536 sdl.sdl_index = ifp->if_index; 537 538 if (set) { 539 TAILQ_FOREACH(ifma, &trifp->if_multiaddrs, ifma_link) { 540 if (ifma->ifma_addr->sa_family != AF_LINK) 541 continue; 542 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 543 LLADDR(&sdl), ETHER_ADDR_LEN); 544 545 error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 546 if (error) 547 return (error); 548 mc = malloc(sizeof(struct ng_fec_mc), M_DEVBUF, M_NOWAIT); 549 if (mc == NULL) 550 return (ENOMEM); 551 mc->mc_ifma = rifma; 552 SLIST_INSERT_HEAD(&p->fec_mc_head, mc, mc_entries); 553 } 554 } else { 555 while ((mc = SLIST_FIRST(&p->fec_mc_head)) != NULL) { 556 SLIST_REMOVE(&p->fec_mc_head, mc, ng_fec_mc, mc_entries); 557 if_delmulti_ifma(mc->mc_ifma); 558 free(mc, M_DEVBUF); 559 } 560 } 561 return (0); 562} 563 564static int 565ng_fec_ether_setmulti(struct ifnet *ifp) 566{ 567 struct ng_fec_private *priv; 568 struct ng_fec_bundle *b; 569 struct ng_fec_portlist *p; 570 571 priv = ifp->if_softc; 572 b = &priv->fec_bundle; 573 574 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 575 /* First, remove any existing filter entries. */ 576 ng_fec_ether_cmdmulti(ifp, p, 0); 577 /* copy all addresses from the fec interface to the port */ 578 ng_fec_ether_cmdmulti(ifp, p, 1); 579 } 580 return (0); 581} 582 583/* 584 * Pass an ioctl command down to all the underyling interfaces in a 585 * bundle. Used for setting flags. 586 */ 587 588static int 589ng_fec_setport(struct ifnet *ifp, u_long command, caddr_t data) 590{ 591 struct ng_fec_private *priv; 592 struct ng_fec_bundle *b; 593 struct ifnet *oifp; 594 struct ng_fec_portlist *p; 595 596 priv = ifp->if_softc; 597 b = &priv->fec_bundle; 598 599 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 600 oifp = p->fec_if; 601 if (oifp != NULL) 602 (*oifp->if_ioctl)(oifp, command, data); 603 } 604 605 return(0); 606} 607 608static void 609ng_fec_init(void *arg) 610{ 611 struct ng_fec_private *priv; 612 struct ng_fec_bundle *b; 613 struct ifnet *ifp, *bifp; 614 struct ng_fec_portlist *p; 615 616 priv = arg; 617 ifp = priv->ifp; 618 b = &priv->fec_bundle; 619 620 if (b->fec_ifcnt != 2 && b->fec_ifcnt != FEC_BUNDLESIZ) { 621 printf("fec%d: invalid bundle " 622 "size: %d\n", priv->unit, 623 b->fec_ifcnt); 624 return; 625 } 626 627 ng_fec_stop(ifp); 628 629 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 630 bifp = p->fec_if; 631 bifp->if_flags |= IFF_UP; 632 (*bifp->if_ioctl)(bifp, SIOCSIFFLAGS, NULL); 633 /* mark iface as up and let the monitor check it */ 634 p->fec_ifstat = -1; 635 } 636 637 ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE); 638 ifp->if_drv_flags |= IFF_DRV_RUNNING; 639 640 priv->fec_ch = timeout(ng_fec_tick, priv, hz); 641 642 return; 643} 644 645static void 646ng_fec_stop(struct ifnet *ifp) 647{ 648 struct ng_fec_private *priv; 649 struct ng_fec_bundle *b; 650 struct ifnet *bifp; 651 struct ng_fec_portlist *p; 652 653 priv = ifp->if_softc; 654 b = &priv->fec_bundle; 655 656 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 657 bifp = p->fec_if; 658 bifp->if_flags &= ~IFF_UP; 659 (*bifp->if_ioctl)(bifp, SIOCSIFFLAGS, NULL); 660 } 661 662 untimeout(ng_fec_tick, priv, priv->fec_ch); 663 664 ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 665 666 return; 667} 668 669static void 670ng_fec_tick(void *arg) 671{ 672 struct ng_fec_private *priv; 673 struct ng_fec_bundle *b; 674 struct ifmediareq ifmr; 675 struct ifnet *ifp; 676 struct ng_fec_portlist *p; 677 int error = 0; 678 679 priv = arg; 680 b = &priv->fec_bundle; 681 682 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 683 bzero((char *)&ifmr, sizeof(ifmr)); 684 ifp = p->fec_if; 685 error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 686 if (error) { 687 printf("fec%d: failed to check status " 688 "of link %s\n", priv->unit, ifp->if_xname); 689 continue; 690 } 691 692 if (ifmr.ifm_status & IFM_AVALID) { 693 if (ifmr.ifm_status & IFM_ACTIVE) { 694 if (p->fec_ifstat == -1 || 695 p->fec_ifstat == 0) { 696 p->fec_ifstat = 1; 697 printf("fec%d: port %s in bundle " 698 "is up\n", priv->unit, 699 ifp->if_xname); 700 } 701 } else { 702 if (p->fec_ifstat == -1 || 703 p->fec_ifstat == 1) { 704 p->fec_ifstat = 0; 705 printf("fec%d: port %s in bundle " 706 "is down\n", priv->unit, 707 ifp->if_xname); 708 } 709 } 710 } 711 } 712 713 ifp = priv->ifp; 714 if (ifp->if_drv_flags & IFF_DRV_RUNNING) 715 priv->fec_ch = timeout(ng_fec_tick, priv, hz); 716 717 return; 718} 719 720static int 721ng_fec_ifmedia_upd(struct ifnet *ifp) 722{ 723 return(0); 724} 725 726static void ng_fec_ifmedia_sts(struct ifnet *ifp, 727 struct ifmediareq *ifmr) 728{ 729 struct ng_fec_private *priv; 730 struct ng_fec_bundle *b; 731 struct ng_fec_portlist *p; 732 733 priv = ifp->if_softc; 734 b = &priv->fec_bundle; 735 736 ifmr->ifm_status = IFM_AVALID; 737 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 738 if (p->fec_ifstat == 1) { 739 ifmr->ifm_status |= IFM_ACTIVE; 740 break; 741 } 742 } 743 744 return; 745} 746 747/* 748 * Process an ioctl for the virtual interface 749 */ 750static int 751ng_fec_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 752{ 753 struct ifreq *const ifr = (struct ifreq *) data; 754 int s, error = 0; 755 struct ng_fec_private *priv; 756 struct ng_fec_bundle *b; 757 758 priv = ifp->if_softc; 759 b = &priv->fec_bundle; 760 761#ifdef DEBUG 762 ng_fec_print_ioctl(ifp, command, data); 763#endif 764 s = splimp(); 765 switch (command) { 766 767 /* These two are mostly handled at a higher layer */ 768 case SIOCSIFADDR: 769 case SIOCGIFADDR: 770 error = ether_ioctl(ifp, command, data); 771 break; 772 773 case SIOCSIFMTU: 774 if (ifr->ifr_mtu >= NG_FEC_MTU_MIN && 775 ifr->ifr_mtu <= NG_FEC_MTU_MAX) { 776 struct ng_fec_portlist *p; 777 struct ifnet *bifp; 778 779 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 780 bifp = p->fec_if; 781 error = (*bifp->if_ioctl)(bifp, SIOCSIFMTU, 782 data); 783 if (error != 0) 784 break; 785 } 786 if (error == 0) 787 ifp->if_mtu = ifr->ifr_mtu; 788 } else 789 error = EINVAL; 790 break; 791 792 /* Set flags */ 793 case SIOCSIFFLAGS: 794 /* 795 * If the interface is marked up and stopped, then start it. 796 * If it is marked down and running, then stop it. 797 */ 798 if (ifr->ifr_flags & IFF_UP) { 799 if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 800 /* Sanity. */ 801 if (b->fec_ifcnt != 2 && 802 b->fec_ifcnt != FEC_BUNDLESIZ) { 803 printf("fec%d: invalid bundle " 804 "size: %d\n", priv->unit, 805 b->fec_ifcnt); 806 error = EINVAL; 807 break; 808 } 809 ng_fec_init(priv); 810 } 811 /* 812 * Bubble down changes in promisc mode to 813 * underlying interfaces. 814 */ 815 if ((ifp->if_flags & IFF_PROMISC) != 816 (priv->if_flags & IFF_PROMISC)) { 817 ng_fec_setport(ifp, command, data); 818 priv->if_flags = ifp->if_flags; 819 } 820 } else { 821 if (ifp->if_drv_flags & IFF_DRV_RUNNING) 822 ng_fec_stop(ifp); 823 } 824 break; 825 826 case SIOCADDMULTI: 827 case SIOCDELMULTI: 828 ng_fec_ether_setmulti(ifp); 829 error = 0; 830 break; 831 case SIOCGIFMEDIA: 832 case SIOCSIFMEDIA: 833 error = ifmedia_ioctl(ifp, ifr, &priv->ifmedia, command); 834 break; 835 /* Stuff that's not supported */ 836 case SIOCSIFPHYS: 837 error = EOPNOTSUPP; 838 break; 839 840 default: 841 error = EINVAL; 842 break; 843 } 844 (void) splx(s); 845 return (error); 846} 847 848/* 849 * This routine spies on mbufs received by underlying network device 850 * drivers. When we add an interface to our bundle, we override its 851 * if_input routine with a pointer to ng_fec_input(). This means we 852 * get to look at all the device's packets before sending them to the 853 * real ether_input() for processing by the stack. Once we verify the 854 * packet comes from an interface that's been aggregated into 855 * our bundle, we fix up the rcvif pointer and increment our 856 * packet counters so that it looks like the frames are actually 857 * coming from us. 858 */ 859static void 860ng_fec_input(struct ifnet *ifp, struct mbuf *m0) 861{ 862 struct ng_node *node; 863 struct ng_fec_private *priv; 864 struct ng_fec_bundle *b; 865 struct ifnet *bifp; 866 struct ng_fec_portlist *p; 867 868 /* Sanity check */ 869 if (ifp == NULL || m0 == NULL) 870 return; 871 872 node = IFP2NG(ifp); 873 874 /* Sanity check part II */ 875 if (node == NULL) 876 return; 877 878 priv = NG_NODE_PRIVATE(node); 879 b = &priv->fec_bundle; 880 bifp = priv->ifp; 881 882 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 883 if (p->fec_if == m0->m_pkthdr.rcvif) 884 break; 885 } 886 887 /* Wasn't meant for us; leave this frame alone. */ 888 if (p == NULL) 889 return; 890 891 /* 892 * Check for a BPF tap on the underlying interface. This 893 * is mainly a debugging aid: it allows tcpdump-ing of an 894 * individual interface in a bundle to work, which it 895 * otherwise would not. BPF tapping of our own aggregate 896 * interface will occur once we call ether_input(). 897 */ 898 BPF_MTAP(m0->m_pkthdr.rcvif, m0); 899 900 /* Convince the system that this is our frame. */ 901 m0->m_pkthdr.rcvif = bifp; 902 903 /* 904 * Count bytes on an individual interface in a bundle. 905 * The bytes will also be added to the aggregate interface 906 * once we call ether_input(). 907 */ 908 ifp->if_ibytes += m0->m_pkthdr.len; 909 910 bifp->if_ipackets++; 911 (*bifp->if_input)(bifp, m0); 912 913 return; 914} 915 916/* 917 * Take a quick peek at the packet and see if it's ok for us to use 918 * the inet or inet6 hash methods on it, if they're enabled. We do 919 * this by setting flags in the mbuf header. Once we've made up our 920 * mind what to do, we pass the frame to output vector for further 921 * processing. 922 */ 923 924static int 925ng_fec_output(struct ifnet *ifp, struct mbuf *m, 926 struct sockaddr *dst, struct rtentry *rt0) 927{ 928 const priv_p priv = (priv_p) ifp->if_softc; 929 struct ng_fec_bundle *b; 930 int error; 931 932 /* Check interface flags */ 933 if (!((ifp->if_flags & IFF_UP) && 934 (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 935 m_freem(m); 936 return (ENETDOWN); 937 } 938 939 b = &priv->fec_bundle; 940 941 switch (b->fec_btype) { 942 case FEC_BTYPE_MAC: 943 m->m_flags |= M_FEC_MAC; 944 break; 945#ifdef INET 946 case FEC_BTYPE_INET: 947 /* 948 * We can't use the INET address port selection 949 * scheme if this isn't an INET packet. 950 */ 951 if (dst->sa_family == AF_INET) 952 m->m_flags |= M_FEC_INET; 953#ifdef INET6 954 else if (dst->sa_family == AF_INET6) 955 m->m_flags |= M_FEC_INET6; 956#endif 957 else { 958#ifdef DEBUG 959 if_printf(ifp, "can't do inet aggregation of non " 960 "inet packet\n"); 961#endif 962 m->m_flags |= M_FEC_MAC; 963 } 964 break; 965#endif 966 default: 967 if_printf(ifp, "bogus hash type: %d\n", 968 b->fec_btype); 969 m_freem(m); 970 return(EINVAL); 971 break; 972 } 973 974 /* 975 * Pass the frame to the output vector for all the protocol 976 * handling. This will put the ethernet header on the packet 977 * for us. 978 */ 979 priv->if_error = 0; 980 error = (*b->fec_if_output)(ifp, m, dst, rt0); 981 if (priv->if_error && !error) 982 error = priv->if_error; 983 984 return(error); 985} 986 987/* 988 * Apply a hash to the source and destination addresses in the packet 989 * in order to select an interface. Also check link status and handle 990 * dead links accordingly. 991 */ 992 993static int 994ng_fec_choose_port(struct ng_fec_bundle *b, 995 struct mbuf *m, struct ifnet **ifp) 996{ 997 struct ether_header *eh; 998 struct mbuf *m0; 999#ifdef INET 1000 struct ip *ip; 1001#ifdef INET6 1002 struct ip6_hdr *ip6; 1003#endif 1004#endif 1005 1006 struct ng_fec_portlist *p; 1007 int port = 0, mask; 1008 1009 /* 1010 * If there are only two ports, mask off all but the 1011 * last bit for XORing. If there are 4, mask off all 1012 * but the last 2 bits. 1013 */ 1014 mask = b->fec_ifcnt == 2 ? 0x1 : 0x3; 1015 eh = mtod(m, struct ether_header *); 1016#ifdef INET 1017 ip = (struct ip *)(mtod(m, char *) + 1018 sizeof(struct ether_header)); 1019#ifdef INET6 1020 ip6 = (struct ip6_hdr *)(mtod(m, char *) + 1021 sizeof(struct ether_header)); 1022#endif 1023#endif 1024 1025 /* 1026 * The fg_fec_output() routine is supposed to leave a 1027 * flag for us in the mbuf that tells us what hash to 1028 * use, but sometimes a new mbuf is prepended to the 1029 * chain, so we have to search every mbuf in the chain 1030 * to find the flags. 1031 */ 1032 m0 = m; 1033 while (m0) { 1034 if (m0->m_flags & (M_FEC_MAC|M_FEC_INET|M_FEC_INET6)) 1035 break; 1036 m0 = m0->m_next; 1037 } 1038 if (m0 == NULL) 1039 return(EINVAL); 1040 1041 switch (m0->m_flags & (M_FEC_MAC|M_FEC_INET|M_FEC_INET6)) { 1042 case M_FEC_MAC: 1043 port = (eh->ether_dhost[5] ^ 1044 eh->ether_shost[5]) & mask; 1045 break; 1046#ifdef INET 1047 case M_FEC_INET: 1048 port = (ntohl(ip->ip_dst.s_addr) ^ 1049 ntohl(ip->ip_src.s_addr)) & mask; 1050 break; 1051#ifdef INET6 1052 case M_FEC_INET6: 1053 port = (ip6->ip6_dst.s6_addr[15] ^ 1054 ip6->ip6_dst.s6_addr[15]) & mask; 1055 break; 1056#endif 1057#endif 1058 default: 1059 return(EINVAL); 1060 break; 1061 } 1062 1063 TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) { 1064 if (port == p->fec_idx) 1065 break; 1066 } 1067 1068 /* 1069 * Now that we've chosen a port, make sure it's 1070 * alive. If it's not alive, cycle through the bundle 1071 * looking for a port that is alive. If we don't find 1072 * any, return an error. 1073 */ 1074 if (p->fec_ifstat != 1) { 1075 struct ng_fec_portlist *n = NULL; 1076 1077 n = TAILQ_NEXT(p, fec_list); 1078 if (n == NULL) 1079 n = TAILQ_FIRST(&b->ng_fec_ports); 1080 while (n != p) { 1081 if (n->fec_ifstat == 1) 1082 break; 1083 n = TAILQ_NEXT(n, fec_list); 1084 if (n == NULL) 1085 n = TAILQ_FIRST(&b->ng_fec_ports); 1086 } 1087 if (n == p) 1088 return(EAGAIN); 1089 p = n; 1090 } 1091 1092 *ifp = p->fec_if; 1093 1094 return(0); 1095} 1096 1097/* 1098 * Now that the packet has been run through ether_output(), yank it 1099 * off our own send queue and stick it on the queue for the appropriate 1100 * underlying physical interface. Note that if the interface's send 1101 * queue is full, we save an error status in our private netgraph 1102 * space which will eventually be handed up to ng_fec_output(), which 1103 * will return it to the rest of the IP stack. We need to do this 1104 * in order to duplicate the effect of ether_output() returning ENOBUFS 1105 * when it detects that an interface's send queue is full. There's no 1106 * other way to signal the error status from here since the if_start() 1107 * routine is spec'ed to return void. 1108 * 1109 * Once the frame is queued, we call ether_output_frame() to initiate 1110 * transmission. 1111 */ 1112static void 1113ng_fec_start(struct ifnet *ifp) 1114{ 1115 struct ng_fec_private *priv; 1116 struct ng_fec_bundle *b; 1117 struct ifnet *oifp = NULL; 1118 struct mbuf *m0; 1119 int error; 1120 1121 priv = ifp->if_softc; 1122 b = &priv->fec_bundle; 1123 1124 IF_DEQUEUE(&ifp->if_snd, m0); 1125 if (m0 == NULL) 1126 return; 1127 1128 BPF_MTAP(ifp, m0); 1129 1130 /* Queue up packet on the proper port. */ 1131 error = ng_fec_choose_port(b, m0, &oifp); 1132 if (error) { 1133 ifp->if_ierrors++; 1134 m_freem(m0); 1135 priv->if_error = ENOBUFS; 1136 return; 1137 } 1138 ifp->if_opackets++; 1139 1140 priv->if_error = IF_HANDOFF(&oifp->if_snd, m0, oifp) ? 0 : ENOBUFS; 1141 1142 return; 1143} 1144 1145#ifdef DEBUG 1146/* 1147 * Display an ioctl to the virtual interface 1148 */ 1149 1150static void 1151ng_fec_print_ioctl(struct ifnet *ifp, int command, caddr_t data) 1152{ 1153 char *str; 1154 1155 switch (command & IOC_DIRMASK) { 1156 case IOC_VOID: 1157 str = "IO"; 1158 break; 1159 case IOC_OUT: 1160 str = "IOR"; 1161 break; 1162 case IOC_IN: 1163 str = "IOW"; 1164 break; 1165 case IOC_INOUT: 1166 str = "IORW"; 1167 break; 1168 default: 1169 str = "IO??"; 1170 } 1171 log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n", 1172 ifp->if_xname, 1173 str, 1174 IOCGROUP(command), 1175 command & 0xff, 1176 IOCPARM_LEN(command)); 1177} 1178#endif /* DEBUG */ 1179 1180/************************************************************************ 1181 NETGRAPH NODE STUFF 1182 ************************************************************************/ 1183 1184/* 1185 * Constructor for a node 1186 */ 1187static int 1188ng_fec_constructor(node_p node) 1189{ 1190 char ifname[NG_FEC_FEC_NAME_MAX + 1]; 1191 struct ifnet *ifp; 1192 priv_p priv; 1193 const uint8_t eaddr[ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0}; 1194 struct ng_fec_bundle *b; 1195 int error = 0; 1196 1197 /* Allocate node and interface private structures */ 1198 priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 1199 if (priv == NULL) 1200 return (ENOMEM); 1201 1202 ifp = priv->ifp = if_alloc(IFT_ETHER); 1203 if (ifp == NULL) { 1204 free(priv, M_NETGRAPH); 1205 return (ENOSPC); 1206 } 1207 b = &priv->fec_bundle; 1208 1209 /* Link them together */ 1210 ifp->if_softc = priv; 1211 1212 /* Get an interface unit number */ 1213 if ((error = ng_fec_get_unit(&priv->unit)) != 0) { 1214 if_free(ifp); 1215 free(priv, M_NETGRAPH); 1216 return (error); 1217 } 1218 1219 /* Link together node and private info */ 1220 NG_NODE_SET_PRIVATE(node, priv); 1221 priv->node = node; 1222 1223 /* Initialize interface structure */ 1224 if_initname(ifp, NG_FEC_FEC_NAME, priv->unit); 1225 ifp->if_start = ng_fec_start; 1226 ifp->if_ioctl = ng_fec_ioctl; 1227 ifp->if_init = ng_fec_init; 1228 ifp->if_watchdog = NULL; 1229 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 1230 ifp->if_mtu = NG_FEC_MTU_DEFAULT; 1231 ifp->if_flags = (IFF_SIMPLEX|IFF_BROADCAST|IFF_MULTICAST); 1232 ifp->if_addrlen = 0; /* XXX */ 1233 ifp->if_hdrlen = 0; /* XXX */ 1234 ifp->if_baudrate = 100000000; /* XXX */ 1235 TAILQ_INIT(&ifp->if_addrhead); /* XXX useless - done in if_attach */ 1236 1237 /* Give this node the same name as the interface (if possible) */ 1238 bzero(ifname, sizeof(ifname)); 1239 strlcpy(ifname, ifp->if_xname, sizeof(ifname)); 1240 if (ng_name_node(node, ifname) != 0) 1241 log(LOG_WARNING, "%s: can't acquire netgraph name\n", ifname); 1242 1243 /* Attach the interface */ 1244 ether_ifattach(ifp, eaddr); 1245 callout_handle_init(&priv->fec_ch); 1246 1247 /* Override output method with our own */ 1248 ifp->if_output = ng_fec_output; 1249 1250 TAILQ_INIT(&b->ng_fec_ports); 1251 b->fec_ifcnt = 0; 1252 1253 ifmedia_init(&priv->ifmedia, 0, 1254 ng_fec_ifmedia_upd, ng_fec_ifmedia_sts); 1255 ifmedia_add(&priv->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL); 1256 ifmedia_set(&priv->ifmedia, IFM_ETHER|IFM_NONE); 1257 1258 /* Done */ 1259 return (0); 1260} 1261 1262/* 1263 * Receive a control message 1264 */ 1265static int 1266ng_fec_rcvmsg(node_p node, item_p item, hook_p lasthook) 1267{ 1268 const priv_p priv = NG_NODE_PRIVATE(node); 1269 struct ng_fec_bundle *b; 1270 struct ng_mesg *resp = NULL; 1271 struct ng_mesg *msg; 1272 char *ifname; 1273 int error = 0; 1274 1275 NGI_GET_MSG(item, msg); 1276 b = &priv->fec_bundle; 1277 1278 switch (msg->header.typecookie) { 1279 case NGM_FEC_COOKIE: 1280 switch (msg->header.cmd) { 1281 case NGM_FEC_ADD_IFACE: 1282 ifname = msg->data; 1283 error = ng_fec_addport(priv, ifname); 1284 break; 1285 case NGM_FEC_DEL_IFACE: 1286 ifname = msg->data; 1287 error = ng_fec_delport(priv, ifname); 1288 break; 1289 case NGM_FEC_SET_MODE_MAC: 1290 b->fec_btype = FEC_BTYPE_MAC; 1291 break; 1292#ifdef INET 1293 case NGM_FEC_SET_MODE_INET: 1294 b->fec_btype = FEC_BTYPE_INET; 1295 break; 1296#ifdef INET6 1297 case NGM_FEC_SET_MODE_INET6: 1298 b->fec_btype = FEC_BTYPE_INET6; 1299 break; 1300#endif 1301#endif 1302 default: 1303 error = EINVAL; 1304 break; 1305 } 1306 break; 1307 default: 1308 error = EINVAL; 1309 break; 1310 } 1311 NG_RESPOND_MSG(error, node, item, resp); 1312 NG_FREE_MSG(msg); 1313 return (error); 1314} 1315 1316/* 1317 * Shutdown and remove the node and its associated interface. 1318 */ 1319static int 1320ng_fec_shutdown(node_p node) 1321{ 1322 const priv_p priv = NG_NODE_PRIVATE(node); 1323 struct ng_fec_bundle *b; 1324 struct ng_fec_portlist *p; 1325 1326 b = &priv->fec_bundle; 1327 ng_fec_stop(priv->ifp); 1328 1329 while (!TAILQ_EMPTY(&b->ng_fec_ports)) { 1330 p = TAILQ_FIRST(&b->ng_fec_ports); 1331 ng_fec_ether_cmdmulti(priv->ifp, p, 0); 1332 ng_fec_delport(priv, p->fec_if->if_xname); 1333 } 1334 1335 ether_ifdetach(priv->ifp); 1336 if_free_type(priv->ifp, IFT_ETHER); 1337 ifmedia_removeall(&priv->ifmedia); 1338 ng_fec_free_unit(priv->unit); 1339 free(priv, M_NETGRAPH); 1340 NG_NODE_SET_PRIVATE(node, NULL); 1341 NG_NODE_UNREF(node); 1342 return (0); 1343} 1344 1345/* 1346 * Handle loading and unloading for this node type. 1347 */ 1348static int 1349ng_fec_mod_event(module_t mod, int event, void *data) 1350{ 1351 int error = 0; 1352 1353 switch (event) { 1354 case MOD_LOAD: 1355 mtx_init(&ng_fec_mtx, "ng_fec", NULL, MTX_DEF); 1356 break; 1357 case MOD_UNLOAD: 1358 mtx_destroy(&ng_fec_mtx); 1359 break; 1360 default: 1361 error = EOPNOTSUPP; 1362 break; 1363 } 1364 return (error); 1365} 1366