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