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