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