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