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