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