ng_ether.c revision 62678
11553Srgrimes
21553Srgrimes/*
31553Srgrimes * ng_ether.c
41553Srgrimes *
51553Srgrimes * Copyright (c) 1996-2000 Whistle Communications, Inc.
61553Srgrimes * All rights reserved.
71553Srgrimes *
81553Srgrimes * Subject to the following obligations and disclaimer of warranty, use and
91553Srgrimes * redistribution of this software, in source or object code forms, with or
101553Srgrimes * without modifications are expressly permitted by Whistle Communications;
111553Srgrimes * provided, however, that:
121553Srgrimes * 1. Any and all reproductions of the source or object code must include the
131553Srgrimes *    copyright notice above and the following disclaimer of warranties; and
141553Srgrimes * 2. No rights are granted, in any manner or form, to use Whistle
151553Srgrimes *    Communications, Inc. trademarks, including the mark "WHISTLE
161553Srgrimes *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
171553Srgrimes *    such appears in the above copyright notice or in the software.
181553Srgrimes *
191553Srgrimes * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
201553Srgrimes * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
211553Srgrimes * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
221553Srgrimes * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
231553Srgrimes * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
241553Srgrimes * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
251553Srgrimes * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
261553Srgrimes * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
271553Srgrimes * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
281553Srgrimes * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2931492Swollman * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3050479Speter * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
311553Srgrimes * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
321553Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33117554Sgad * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
341553Srgrimes * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3515703Sjoerg * OF SUCH DAMAGE.
3668467Sgad *
3715703Sjoerg * Authors: Archie Cobbs <archie@freebsd.org>
3831492Swollman *	    Julian Elischer <julian@freebsd.org>
3939084Swollman *
4031492Swollman * $FreeBSD: head/sys/netgraph/ng_ether.c 62678 2000-07-06 15:35:59Z julian $
4131492Swollman */
4278146Sgad
4378146Sgad/*
4478146Sgad * ng_ether(4) netgraph node type
4578146Sgad */
4631492Swollman
47#include <sys/param.h>
48#include <sys/systm.h>
49#include <sys/kernel.h>
50#include <sys/malloc.h>
51#include <sys/mbuf.h>
52#include <sys/errno.h>
53#include <sys/syslog.h>
54#include <sys/socket.h>
55
56#include <net/if.h>
57#include <net/if_types.h>
58#include <net/if_arp.h>
59#include <net/if_var.h>
60#include <net/ethernet.h>
61
62#include <netgraph/ng_message.h>
63#include <netgraph/netgraph.h>
64#include <netgraph/ng_parse.h>
65#include <netgraph/ng_ether.h>
66
67#define IFP2AC(IFP)  ((struct arpcom *)IFP)
68#define IFP2NG(ifp)  ((struct ng_node *)((struct arpcom *)(ifp))->ac_netgraph)
69
70/* Per-node private data */
71struct private {
72	struct ifnet	*ifp;		/* associated interface */
73	hook_p		upper;		/* upper hook connection */
74	hook_p		lower;		/* lower OR orphan hook connection */
75	u_char		lowerOrphan;	/* whether lower is lower or orphan */
76};
77typedef struct private *priv_p;
78
79/* Functional hooks called from if_ethersubr.c */
80static void	ng_ether_input(struct ifnet *ifp,
81		    struct mbuf **mp, struct ether_header *eh);
82static void	ng_ether_input_orphan(struct ifnet *ifp,
83		    struct mbuf *m, struct ether_header *eh);
84static int	ng_ether_output(struct ifnet *ifp, struct mbuf **mp);
85static void	ng_ether_attach(struct ifnet *ifp);
86static void	ng_ether_detach(struct ifnet *ifp);
87
88/* Other functions */
89static void	ng_ether_input2(node_p node,
90		    struct mbuf **mp, struct ether_header *eh);
91static int	ng_ether_glueback_header(struct mbuf **mp,
92			struct ether_header *eh);
93static int	ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta);
94static int	ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta);
95
96/* Netgraph node methods */
97static ng_constructor_t	ng_ether_constructor;
98static ng_rcvmsg_t	ng_ether_rcvmsg;
99static ng_shutdown_t	ng_ether_rmnode;
100static ng_newhook_t	ng_ether_newhook;
101static ng_rcvdata_t	ng_ether_rcvdata;
102static ng_disconnect_t	ng_ether_disconnect;
103static int		ng_ether_mod_event(module_t mod, int event, void *data);
104
105/* List of commands and how to convert arguments to/from ASCII */
106static const struct ng_cmdlist ng_ether_cmdlist[] = {
107	{
108	  NGM_ETHER_COOKIE,
109	  NGM_ETHER_GET_IFNAME,
110	  "getifname",
111	  NULL,
112	  &ng_parse_string_type
113	},
114	{
115	  NGM_ETHER_COOKIE,
116	  NGM_ETHER_GET_IFINDEX,
117	  "getifindex",
118	  NULL,
119	  &ng_parse_int32_type
120	},
121	{ 0 }
122};
123
124static struct ng_type ng_ether_typestruct = {
125	NG_VERSION,
126	NG_ETHER_NODE_TYPE,
127	ng_ether_mod_event,
128	ng_ether_constructor,
129	ng_ether_rcvmsg,
130	ng_ether_rmnode,
131	ng_ether_newhook,
132	NULL,
133	NULL,
134	ng_ether_rcvdata,
135	ng_ether_rcvdata,
136	ng_ether_disconnect,
137	ng_ether_cmdlist,
138};
139NETGRAPH_INIT(ether, &ng_ether_typestruct);
140
141/******************************************************************
142		    ETHERNET FUNCTION HOOKS
143******************************************************************/
144
145/*
146 * Handle a packet that has come in on an interface. We get to
147 * look at it here before any upper layer protocols do.
148 *
149 * NOTE: this function will get called at splimp()
150 */
151static void
152ng_ether_input(struct ifnet *ifp,
153	struct mbuf **mp, struct ether_header *eh)
154{
155	const node_p node = IFP2NG(ifp);
156	const priv_p priv = node->private;
157
158	/* If "lower" hook not connected, let packet continue */
159	if (priv->lower == NULL || priv->lowerOrphan)
160		return;
161	ng_ether_input2(node, mp, eh);
162}
163
164/*
165 * Handle a packet that has come in on an interface, and which
166 * does not match any of our known protocols (an ``orphan'').
167 *
168 * NOTE: this function will get called at splimp()
169 */
170static void
171ng_ether_input_orphan(struct ifnet *ifp,
172	struct mbuf *m, struct ether_header *eh)
173{
174	const node_p node = IFP2NG(ifp);
175	const priv_p priv = node->private;
176
177	/* If "orphan" hook not connected, let packet continue */
178	if (priv->lower == NULL || !priv->lowerOrphan) {
179		m_freem(m);
180		return;
181	}
182	ng_ether_input2(node, &m, eh);
183	if (m != NULL)
184		m_freem(m);
185}
186
187/*
188 * Handle a packet that has come in on an interface.
189 * The Ethernet header has already been detached from the mbuf,
190 * so we have to put it back.
191 *
192 * NOTE: this function will get called at splimp()
193 */
194static void
195ng_ether_input2(node_p node, struct mbuf **mp, struct ether_header *eh)
196{
197	const priv_p priv = node->private;
198	meta_p meta = NULL;
199	int error;
200
201	/* Glue Ethernet header back on */
202	if ((error = ng_ether_glueback_header(mp, eh)) != 0)
203		return;
204
205	/* Send out lower/orphan hook */
206	NG_SEND_DATAQ(error, priv->lower, *mp, meta);
207
208	/* Any reflected packet must come later due to queuing */
209	*mp = NULL;
210}
211
212/*
213 * Handle a packet that is going out on an interface.
214 * The Ethernet header is already attached to the mbuf.
215 */
216static int
217ng_ether_output(struct ifnet *ifp, struct mbuf **mp)
218{
219	const node_p node = IFP2NG(ifp);
220	const priv_p priv = node->private;
221	meta_p meta = NULL;
222	int error = 0;
223
224	/* If "upper" hook not connected, let packet continue */
225	if (priv->upper == NULL)
226		return (0);
227
228	/* Send it out "upper" hook */
229	NG_SEND_DATA_RET(error, priv->upper, *mp, meta);
230
231	/* If we got a reflected packet back, handle it */
232	if (error == 0 && *mp != NULL) {
233		error = ng_ether_rcv_upper(node, *mp, meta);
234		*mp = NULL;
235	}
236	return (error);
237}
238
239/*
240 * A new Ethernet interface has been attached.
241 * Create a new node for it, etc.
242 */
243static void
244ng_ether_attach(struct ifnet *ifp)
245{
246	char name[IFNAMSIZ + 1];
247	priv_p priv;
248	node_p node;
249
250	/* Create node */
251	KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __FUNCTION__));
252	snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit);
253	if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) {
254		log(LOG_ERR, "%s: can't %s for %s\n",
255		    __FUNCTION__, "create node", name);
256		return;
257	}
258
259	/* Allocate private data */
260	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT);
261	if (priv == NULL) {
262		log(LOG_ERR, "%s: can't %s for %s\n",
263		    __FUNCTION__, "allocate memory", name);
264		ng_unref(node);
265		return;
266	}
267	bzero(priv, sizeof(*priv));
268	node->private = priv;
269	priv->ifp = ifp;
270	IFP2NG(ifp) = node;
271
272	/* Try to give the node the same name as the interface */
273	if (ng_name_node(node, name) != 0) {
274		log(LOG_WARNING, "%s: can't name node %s\n",
275		    __FUNCTION__, name);
276	}
277}
278
279/*
280 * An Ethernet interface is being detached.
281 * Destroy its node.
282 */
283static void
284ng_ether_detach(struct ifnet *ifp)
285{
286	const node_p node = IFP2NG(ifp);
287	priv_p priv;
288
289	if (node == NULL)		/* no node (why not?), ignore */
290		return;
291	ng_rmnode(node);		/* break all links to other nodes */
292	IFP2NG(ifp) = NULL;		/* detach node from interface */
293	priv = node->private;		/* free node private info */
294	bzero(priv, sizeof(*priv));
295	FREE(priv, M_NETGRAPH);
296	node->private = NULL;
297	ng_unref(node);			/* free node itself */
298}
299
300/*
301 * Optimization for gluing the Ethernet header back onto
302 * the front of an incoming packet.
303 */
304static int
305ng_ether_glueback_header(struct mbuf **mp, struct ether_header *eh)
306{
307	struct mbuf *m = *mp;
308	uintfptr_t room;
309	int error = 0;
310
311	/*
312	 * Possibly the header is already on the front.
313	 * If this is the case so just move the markers back
314	 * to re-include it. We lucked out.
315	 * This allows us to avoid a yucky m_pullup
316	 * in later nodes if it works.
317	 */
318	if (eh == mtod(m, struct ether_header *) - 1) {
319		m->m_len += sizeof(*eh);
320		m->m_data -= sizeof(*eh);
321		m->m_pkthdr.len += sizeof(*eh);
322		goto done;
323	}
324
325	/*
326	 * Alternatively there may be room even though
327	 * it is stored somewhere else. If so, copy it in.
328	 * This only safe because we KNOW that this packet has
329	 * just been generated by an ethernet card, so there are
330	 * no aliases to the buffer (not so for outgoing packets).
331	 * Nearly all ethernet cards will end up producing mbufs
332	 * that fall into these cases. So we are not optimizing
333	 * contorted cases.
334	 */
335	if ((m->m_flags & M_EXT) != 0) {
336		room = mtod(m, caddr_t) - m->m_ext.ext_buf;
337		if (room > m->m_ext.ext_size)	/* garbage, fail immediately */
338			room = 0;
339	} else
340		room = mtod(m, caddr_t) - m->m_pktdat;
341
342	/*
343	 * If we have room, just copy it and adjust
344	 */
345	if (room >= sizeof(*eh)) {
346		m->m_len += sizeof(*eh);
347		m->m_data -= sizeof(*eh);
348		m->m_pkthdr.len += sizeof(*eh);
349		goto copy;
350	}
351
352	/*
353	 * Doing anything more is likely to get more
354	 * expensive than it's worth..
355	 * it's probable that everything else is in one
356	 * big lump. The next node will do an m_pullup()
357	 * for exactly the amount of data it needs and
358	 * hopefully everything after that will not
359	 * need one. So let's just use M_PREPEND.
360	 */
361	M_PREPEND(m, sizeof (*eh), M_DONTWAIT);
362	if (m == NULL) {
363		error = ENOBUFS;
364		goto done;
365	}
366
367copy:
368	/* Copy header and return (possibly new) mbuf */
369	bcopy((caddr_t)eh, mtod(m, struct ether_header *), sizeof(*eh));
370done:
371	*mp = m;
372	return error;
373}
374
375/******************************************************************
376		    NETGRAPH NODE METHODS
377******************************************************************/
378
379/*
380 * It is not possible or allowable to create a node of this type.
381 * Nodes get created when the interface is attached (or, when
382 * this node type's KLD is loaded).
383 */
384static int
385ng_ether_constructor(node_p *nodep)
386{
387	return (EINVAL);
388}
389
390/*
391 * Check for attaching a new hook.
392 */
393static	int
394ng_ether_newhook(node_p node, hook_p hook, const char *name)
395{
396	const priv_p priv = node->private;
397	u_char orphan = priv->lowerOrphan;
398	hook_p *hookptr;
399
400	/* Divert hook is an alias for lower */
401	if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0)
402		name = NG_ETHER_HOOK_LOWER;
403
404	/* Which hook? */
405	if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0)
406		hookptr = &priv->upper;
407	else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) {
408		hookptr = &priv->lower;
409		orphan = 0;
410	} else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) {
411		hookptr = &priv->lower;
412		orphan = 1;
413	} else
414		return (EINVAL);
415
416	/* Check if already connected (shouldn't be, but doesn't hurt) */
417	if (*hookptr != NULL)
418		return (EISCONN);
419
420	/* OK */
421	*hookptr = hook;
422	priv->lowerOrphan = orphan;
423	return (0);
424}
425
426/*
427 * Receive an incoming control message.
428 */
429static int
430ng_ether_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
431		struct ng_mesg **rptr, hook_p lasthook)
432{
433	const priv_p priv = node->private;
434	struct ng_mesg *resp = NULL;
435	int error = 0;
436
437	switch (msg->header.typecookie) {
438	case NGM_ETHER_COOKIE:
439		switch (msg->header.cmd) {
440		case NGM_ETHER_GET_IFNAME:
441			NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT);
442			if (resp == NULL) {
443				error = ENOMEM;
444				break;
445			}
446			snprintf(resp->data, IFNAMSIZ + 1,
447			    "%s%d", priv->ifp->if_name, priv->ifp->if_unit);
448			break;
449		case NGM_ETHER_GET_IFINDEX:
450			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
451			if (resp == NULL) {
452				error = ENOMEM;
453				break;
454			}
455			*((u_int32_t *)resp->data) = priv->ifp->if_index;
456			break;
457		default:
458			error = EINVAL;
459			break;
460		}
461		break;
462	default:
463		error = EINVAL;
464		break;
465	}
466	if (rptr)
467		*rptr = resp;
468	else if (resp != NULL)
469		FREE(resp, M_NETGRAPH);
470	FREE(msg, M_NETGRAPH);
471	return (error);
472}
473
474/*
475 * Receive data on a hook.
476 */
477static int
478ng_ether_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
479		struct mbuf **ret_m, meta_p *ret_meta)
480{
481	const node_p node = hook->node;
482	const priv_p priv = node->private;
483
484	if (hook == priv->lower)
485		return ng_ether_rcv_lower(node, m, meta);
486	if (hook == priv->upper)
487		return ng_ether_rcv_upper(node, m, meta);
488	panic("%s: weird hook", __FUNCTION__);
489}
490
491/*
492 * Handle an mbuf received on the "lower" hook.
493 */
494static int
495ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta)
496{
497	const priv_p priv = node->private;
498	struct ether_header *eh;
499
500	/* Make sure header is fully pulled up */
501	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
502		NG_FREE_DATA(m, meta);
503		return (EINVAL);
504	}
505	if (m->m_len < sizeof(struct ether_header)
506	    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
507		NG_FREE_META(meta);
508		return (ENOBUFS);
509	}
510
511        /* drop in the MAC address */
512	eh = mtod(m, struct ether_header *);
513	bcopy((IFP2AC(priv->ifp))->ac_enaddr, eh->ether_shost, 6);
514
515	/* Send it on its way */
516	NG_FREE_META(meta);
517	return ether_output_frame(priv->ifp, m);
518}
519
520/*
521 * Handle an mbuf received on the "upper" hook.
522 */
523static int
524ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta)
525{
526	const priv_p priv = node->private;
527	struct ether_header *eh;
528
529	/* Check length and pull off header */
530	if (m->m_pkthdr.len < sizeof(*eh)) {
531		NG_FREE_DATA(m, meta);
532		return (EINVAL);
533	}
534	if (m->m_len < sizeof(*eh) && (m = m_pullup(m, sizeof(*eh))) == NULL) {
535		NG_FREE_META(meta);
536		return (ENOBUFS);
537	}
538	eh = mtod(m, struct ether_header *);
539	m->m_data += sizeof(*eh);
540	m->m_len -= sizeof(*eh);
541	m->m_pkthdr.len -= sizeof(*eh);
542
543	/* Route packet back in */
544	NG_FREE_META(meta);
545	ether_demux(priv->ifp, eh, m);
546	return (0);
547}
548
549/*
550 * Shutdown node. This resets the node but does not remove it.
551 */
552static int
553ng_ether_rmnode(node_p node)
554{
555	ng_cutlinks(node);
556	node->flags &= ~NG_INVALID;	/* bounce back to life */
557	return (0);
558}
559
560/*
561 * Hook disconnection.
562 */
563static int
564ng_ether_disconnect(hook_p hook)
565{
566	const priv_p priv = hook->node->private;
567
568	if (hook == priv->upper)
569		priv->upper = NULL;
570	else if (hook == priv->lower) {
571		priv->lower = NULL;
572		priv->lowerOrphan = 0;
573	} else
574		panic("%s: weird hook", __FUNCTION__);
575	return (0);
576}
577
578/******************************************************************
579		    	INITIALIZATION
580******************************************************************/
581
582/*
583 * Handle loading and unloading for this node type.
584 */
585static int
586ng_ether_mod_event(module_t mod, int event, void *data)
587{
588	struct ifnet *ifp;
589	int error = 0;
590	int s;
591
592	s = splnet();
593	switch (event) {
594	case MOD_LOAD:
595
596		/* Register function hooks */
597		if (ng_ether_attach_p != NULL) {
598			error = EEXIST;
599			break;
600		}
601		ng_ether_attach_p = ng_ether_attach;
602		ng_ether_detach_p = ng_ether_detach;
603		ng_ether_output_p = ng_ether_output;
604		ng_ether_input_p = ng_ether_input;
605		ng_ether_input_orphan_p = ng_ether_input_orphan;
606
607		/* Create nodes for any already-existing Ethernet interfaces */
608		TAILQ_FOREACH(ifp, &ifnet, if_link) {
609			if (ifp->if_type == IFT_ETHER)
610				ng_ether_attach(ifp);
611		}
612		break;
613
614	case MOD_UNLOAD:
615
616		/*
617		 * Note that the base code won't try to unload us until
618		 * all nodes have been removed, and that can't happen
619		 * until all Ethernet interfaces are removed. In any
620		 * case, we know there are no nodes left if the action
621		 * is MOD_UNLOAD, so there's no need to detach any nodes.
622		 */
623
624		/* Unregister function hooks */
625		ng_ether_attach_p = NULL;
626		ng_ether_detach_p = NULL;
627		ng_ether_output_p = NULL;
628		ng_ether_input_p = NULL;
629		ng_ether_input_orphan_p = NULL;
630		break;
631
632	default:
633		error = EOPNOTSUPP;
634		break;
635	}
636	splx(s);
637	return (error);
638}
639
640