xref: /freebsd/sys/netgraph/ng_ether.c (revision 0bd0c3295ac09f759f2816b73cbd2d950e3bef7e)
1 
2 /*
3  * ng_ether.c
4  */
5 
6 /*-
7  * Copyright (c) 1996-2000 Whistle Communications, Inc.
8  * All rights reserved.
9  *
10  * Subject to the following obligations and disclaimer of warranty, use and
11  * redistribution of this software, in source or object code forms, with or
12  * without modifications are expressly permitted by Whistle Communications;
13  * provided, however, that:
14  * 1. Any and all reproductions of the source or object code must include the
15  *    copyright notice above and the following disclaimer of warranties; and
16  * 2. No rights are granted, in any manner or form, to use Whistle
17  *    Communications, Inc. trademarks, including the mark "WHISTLE
18  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
19  *    such appears in the above copyright notice or in the software.
20  *
21  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
22  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
23  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
24  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
26  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
27  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
28  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
29  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
30  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
31  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
32  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
33  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
34  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
37  * OF SUCH DAMAGE.
38  *
39  * Authors: Archie Cobbs <archie@freebsd.org>
40  *	    Julian Elischer <julian@freebsd.org>
41  */
42 
43 /*
44  * ng_ether(4) netgraph node type
45  */
46 
47 #include <sys/param.h>
48 #include <sys/eventhandler.h>
49 #include <sys/systm.h>
50 #include <sys/kernel.h>
51 #include <sys/malloc.h>
52 #include <sys/mbuf.h>
53 #include <sys/errno.h>
54 #include <sys/proc.h>
55 #include <sys/syslog.h>
56 #include <sys/socket.h>
57 #include <sys/taskqueue.h>
58 
59 #include <net/if.h>
60 #include <net/if_dl.h>
61 #include <net/if_types.h>
62 #include <net/if_arp.h>
63 #include <net/if_var.h>
64 #include <net/if_private.h>
65 #include <net/ethernet.h>
66 #include <net/if_bridgevar.h>
67 #include <net/vnet.h>
68 
69 #include <netgraph/ng_message.h>
70 #include <netgraph/netgraph.h>
71 #include <netgraph/ng_parse.h>
72 #include <netgraph/ng_ether.h>
73 
74 MODULE_VERSION(ng_ether, 1);
75 
76 #define IFP2NG(ifp)  ((ifp)->if_l2com)
77 
78 /* Per-node private data */
79 struct private {
80 	struct ifnet	*ifp;		/* associated interface */
81 	hook_p		upper;		/* upper hook connection */
82 	hook_p		lower;		/* lower hook connection */
83 	hook_p		orphan;		/* orphan hook connection */
84 	u_char		autoSrcAddr;	/* always overwrite source address */
85 	u_char		promisc;	/* promiscuous mode enabled */
86 	u_long		hwassist;	/* hardware checksum capabilities */
87 	u_int		flags;		/* flags e.g. really die */
88 };
89 typedef struct private *priv_p;
90 
91 /* Hook pointers used by if_ethersubr.c to callback to netgraph */
92 extern	void	(*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp);
93 extern	void	(*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m);
94 extern	int	(*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp);
95 
96 /* Functional hooks called from if_ethersubr.c */
97 static void	ng_ether_input(struct ifnet *ifp, struct mbuf **mp);
98 static void	ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m);
99 static int	ng_ether_output(struct ifnet *ifp, struct mbuf **mp);
100 
101 /* Other functions */
102 static int	ng_ether_rcv_lower(hook_p node, item_p item);
103 static int	ng_ether_rcv_upper(hook_p node, item_p item);
104 
105 /* Netgraph node methods */
106 static ng_constructor_t	ng_ether_constructor;
107 static ng_rcvmsg_t	ng_ether_rcvmsg;
108 static ng_shutdown_t	ng_ether_shutdown;
109 static ng_newhook_t	ng_ether_newhook;
110 static ng_rcvdata_t	ng_ether_rcvdata;
111 static ng_disconnect_t	ng_ether_disconnect;
112 static int		ng_ether_mod_event(module_t mod, int event, void *data);
113 
114 static eventhandler_tag	ifnet_arrival_tag, ifnet_departure_tag,
115     ifnet_rename_tag, ifnet_linkstate_tag;
116 
117 /* List of commands and how to convert arguments to/from ASCII */
118 static const struct ng_cmdlist ng_ether_cmdlist[] = {
119 	{
120 	  NGM_ETHER_COOKIE,
121 	  NGM_ETHER_GET_IFNAME,
122 	  "getifname",
123 	  NULL,
124 	  &ng_parse_string_type
125 	},
126 	{
127 	  NGM_ETHER_COOKIE,
128 	  NGM_ETHER_GET_IFINDEX,
129 	  "getifindex",
130 	  NULL,
131 	  &ng_parse_int32_type
132 	},
133 	{
134 	  NGM_ETHER_COOKIE,
135 	  NGM_ETHER_GET_ENADDR,
136 	  "getenaddr",
137 	  NULL,
138 	  &ng_parse_enaddr_type
139 	},
140 	{
141 	  NGM_ETHER_COOKIE,
142 	  NGM_ETHER_SET_ENADDR,
143 	  "setenaddr",
144 	  &ng_parse_enaddr_type,
145 	  NULL
146 	},
147 	{
148 	  NGM_ETHER_COOKIE,
149 	  NGM_ETHER_GET_PROMISC,
150 	  "getpromisc",
151 	  NULL,
152 	  &ng_parse_int32_type
153 	},
154 	{
155 	  NGM_ETHER_COOKIE,
156 	  NGM_ETHER_SET_PROMISC,
157 	  "setpromisc",
158 	  &ng_parse_int32_type,
159 	  NULL
160 	},
161 	{
162 	  NGM_ETHER_COOKIE,
163 	  NGM_ETHER_GET_AUTOSRC,
164 	  "getautosrc",
165 	  NULL,
166 	  &ng_parse_int32_type
167 	},
168 	{
169 	  NGM_ETHER_COOKIE,
170 	  NGM_ETHER_SET_AUTOSRC,
171 	  "setautosrc",
172 	  &ng_parse_int32_type,
173 	  NULL
174 	},
175 	{
176 	  NGM_ETHER_COOKIE,
177 	  NGM_ETHER_ADD_MULTI,
178 	  "addmulti",
179 	  &ng_parse_enaddr_type,
180 	  NULL
181 	},
182 	{
183 	  NGM_ETHER_COOKIE,
184 	  NGM_ETHER_DEL_MULTI,
185 	  "delmulti",
186 	  &ng_parse_enaddr_type,
187 	  NULL
188 	},
189 	{
190 	  NGM_ETHER_COOKIE,
191 	  NGM_ETHER_DETACH,
192 	  "detach",
193 	  NULL,
194 	  NULL
195 	},
196 	{ 0 }
197 };
198 
199 static struct ng_type ng_ether_typestruct = {
200 	.version =	NG_ABI_VERSION,
201 	.name =		NG_ETHER_NODE_TYPE,
202 	.mod_event =	ng_ether_mod_event,
203 	.constructor =	ng_ether_constructor,
204 	.rcvmsg =	ng_ether_rcvmsg,
205 	.shutdown =	ng_ether_shutdown,
206 	.newhook =	ng_ether_newhook,
207 	.rcvdata =	ng_ether_rcvdata,
208 	.disconnect =	ng_ether_disconnect,
209 	.cmdlist =	ng_ether_cmdlist,
210 };
211 NETGRAPH_INIT(ether, &ng_ether_typestruct);
212 
213 /******************************************************************
214 		    UTILITY FUNCTIONS
215 ******************************************************************/
216 static void
ng_ether_sanitize_ifname(const char * ifname,char * name)217 ng_ether_sanitize_ifname(const char *ifname, char *name)
218 {
219 	int i;
220 
221 	for (i = 0; i < IFNAMSIZ; i++) {
222 		if (ifname[i] == '.' || ifname[i] == ':')
223 			name[i] = '_';
224 		else
225 			name[i] = ifname[i];
226 		if (name[i] == '\0')
227 			break;
228 	}
229 }
230 
231 /******************************************************************
232 		    ETHERNET FUNCTION HOOKS
233 ******************************************************************/
234 
235 /*
236  * Handle a packet that has come in on an interface. We get to
237  * look at it here before any upper layer protocols do.
238  */
239 static void
ng_ether_input(struct ifnet * ifp,struct mbuf ** mp)240 ng_ether_input(struct ifnet *ifp, struct mbuf **mp)
241 {
242 	const node_p node = IFP2NG(ifp);
243 	const priv_p priv = NG_NODE_PRIVATE(node);
244 	int error;
245 
246 	/* If "lower" hook not connected, let packet continue */
247 	if (priv->lower == NULL)
248 		return;
249 	NG_SEND_DATA_ONLY(error, priv->lower, *mp);	/* sets *mp = NULL */
250 }
251 
252 /*
253  * Handle a packet that has come in on an interface, and which
254  * does not match any of our known protocols (an ``orphan'').
255  */
256 static void
ng_ether_input_orphan(struct ifnet * ifp,struct mbuf * m)257 ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m)
258 {
259 	const node_p node = IFP2NG(ifp);
260 	const priv_p priv = NG_NODE_PRIVATE(node);
261 	int error;
262 
263 	/* If "orphan" hook not connected, discard packet */
264 	if (priv->orphan == NULL) {
265 		m_freem(m);
266 		return;
267 	}
268 	NG_SEND_DATA_ONLY(error, priv->orphan, m);
269 }
270 
271 /*
272  * Handle a packet that is going out on an interface.
273  * The Ethernet header is already attached to the mbuf.
274  */
275 static int
ng_ether_output(struct ifnet * ifp,struct mbuf ** mp)276 ng_ether_output(struct ifnet *ifp, struct mbuf **mp)
277 {
278 	const node_p node = IFP2NG(ifp);
279 	const priv_p priv = NG_NODE_PRIVATE(node);
280 	int error = 0;
281 
282 	/* If "upper" hook not connected, let packet continue */
283 	if (priv->upper == NULL)
284 		return (0);
285 
286 	/* Send it out "upper" hook */
287 	NG_OUTBOUND_THREAD_REF();
288 	NG_SEND_DATA_ONLY(error, priv->upper, *mp);
289 	NG_OUTBOUND_THREAD_UNREF();
290 	return (error);
291 }
292 
293 /*
294  * A new Ethernet interface has been attached.
295  * Create a new node for it, etc.
296  */
297 static int
ng_ether_attach(struct ifnet * ifp,void * arg __unused)298 ng_ether_attach(struct ifnet *ifp, void *arg __unused)
299 {
300 	char name[IFNAMSIZ];
301 	priv_p priv;
302 	node_p node;
303 
304 	if ((ifp)->if_type != IFT_ETHER &&
305 	    (ifp)->if_type != IFT_L2VLAN &&
306 	    (ifp)->if_type != IFT_BRIDGE)
307 		return (0);
308 	MPASS(IFP2NG(ifp) == NULL);
309 
310 	/*
311 	 * Do not create / attach an ether node to this ifnet if
312 	 * a netgraph node with the same name already exists.
313 	 * This should prevent ether nodes to become attached to
314 	 * eiface nodes, which may be problematic due to naming
315 	 * clashes.
316 	 */
317 	ng_ether_sanitize_ifname(ifp->if_xname, name);
318 	if ((node = ng_name2noderef(NULL, name)) != NULL) {
319 		NG_NODE_UNREF(node);
320 		return (0);
321 	}
322 
323 	if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) {
324 		log(LOG_ERR, "%s: can't %s for %s\n",
325 		    __func__, "create node", ifp->if_xname);
326 		return (0);
327 	}
328 
329 	/* Allocate private data */
330 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
331 	NG_NODE_SET_PRIVATE(node, priv);
332 	priv->ifp = ifp;
333 	IFP2NG(ifp) = node;
334 	priv->hwassist = ifp->if_hwassist;
335 
336 	/* Try to give the node the same name as the interface */
337 	if (ng_name_node(node, name) != 0)
338 		log(LOG_WARNING, "%s: can't name node %s\n", __func__, name);
339 
340 	return (0);
341 }
342 
343 static void
ng_ether_arrival(void * arg __unused,struct ifnet * ifp)344 ng_ether_arrival(void *arg __unused, struct ifnet *ifp)
345 {
346 	(void)ng_ether_attach(ifp, NULL);
347 }
348 
349 #define	RETURN_IF_NOT_ETHERNET_OR_DETACHED(ifp)	do {			\
350 	if ((ifp)->if_type != IFT_ETHER &&				\
351 	    (ifp)->if_type != IFT_L2VLAN &&				\
352 	    (ifp)->if_type != IFT_BRIDGE)				\
353 		return;							\
354 	if (IFP2NG(ifp) == NULL)					\
355 		return;							\
356 } while (0)
357 
358 /*
359  * An Ethernet interface is being detached.
360  * REALLY Destroy its node.
361  */
362 static void
ng_ether_detach(void * arg __unused,struct ifnet * ifp)363 ng_ether_detach(void *arg __unused, struct ifnet *ifp)
364 {
365 	const node_p node = IFP2NG(ifp);
366 	priv_p priv;
367 
368 	RETURN_IF_NOT_ETHERNET_OR_DETACHED(ifp);
369 
370 	priv = NG_NODE_PRIVATE(node);
371 
372 	taskqueue_drain(taskqueue_swi, &ifp->if_linktask);
373 	NG_NODE_REALLY_DIE(node);	/* Force real removal of node */
374 	/*
375 	 * We can't assume the ifnet is still around when we run shutdown
376 	 * So zap it now. XXX We HOPE that anything running at this time
377 	 * handles it (as it should in the non netgraph case).
378 	 */
379 	IFP2NG(ifp) = NULL;
380 	priv->ifp = NULL;	/* XXX race if interrupted an output packet */
381 	ng_rmnode_self(node);		/* remove all netgraph parts */
382 }
383 
384 /*
385  * Notify graph about link event.
386  * if_link_state_change() has already checked that the state has changed.
387  */
388 static void
ng_ether_link_state(void * arg __unused,struct ifnet * ifp,int state)389 ng_ether_link_state(void *arg __unused, struct ifnet *ifp, int state)
390 {
391 	const node_p node = IFP2NG(ifp);
392 	priv_p priv;
393 	struct ng_mesg *msg;
394 	int cmd, dummy_error = 0;
395 
396 	RETURN_IF_NOT_ETHERNET_OR_DETACHED(ifp);
397 
398 	priv = NG_NODE_PRIVATE(node);
399 
400 	if (state == LINK_STATE_UP)
401 		cmd = NGM_LINK_IS_UP;
402 	else if (state == LINK_STATE_DOWN)
403 		cmd = NGM_LINK_IS_DOWN;
404 	else
405 		return;
406 
407 	if (priv->lower != NULL) {
408 		NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
409 		if (msg != NULL)
410 			NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0);
411 	}
412 	if (priv->orphan != NULL) {
413 		NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
414 		if (msg != NULL)
415 			NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->orphan, 0);
416 	}
417 }
418 
419 /*
420  * Interface has been renamed.
421  */
422 static void
ng_ether_rename(void * arg __unused,struct ifnet * ifp)423 ng_ether_rename(void *arg __unused, struct ifnet *ifp)
424 {
425 	char name[IFNAMSIZ];
426 	node_p node;
427 
428 	RETURN_IF_NOT_ETHERNET_OR_DETACHED(ifp);
429 
430 	node = IFP2NG(ifp);
431 
432 	/* Try to give the node the same name as the new interface name */
433 	ng_ether_sanitize_ifname(ifp->if_xname, name);
434 	if (ng_name_node(node, name) != 0)
435 		log(LOG_WARNING, "%s: can't re-name node %s\n", __func__, name);
436 }
437 
438 /******************************************************************
439 		    NETGRAPH NODE METHODS
440 ******************************************************************/
441 
442 /*
443  * It is not possible or allowable to create a node of this type.
444  * Nodes get created when the interface is attached (or, when
445  * this node type's KLD is loaded).
446  */
447 static int
ng_ether_constructor(node_p node)448 ng_ether_constructor(node_p node)
449 {
450 	return (EINVAL);
451 }
452 
453 /*
454  * Check for attaching a new hook.
455  */
456 static	int
ng_ether_newhook(node_p node,hook_p hook,const char * name)457 ng_ether_newhook(node_p node, hook_p hook, const char *name)
458 {
459 	const priv_p priv = NG_NODE_PRIVATE(node);
460 	hook_p *hookptr;
461 
462 	/* Divert hook is an alias for lower */
463 	if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0)
464 		name = NG_ETHER_HOOK_LOWER;
465 
466 	/* Which hook? */
467 	if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) {
468 		hookptr = &priv->upper;
469 		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_upper);
470 		NG_HOOK_SET_TO_INBOUND(hook);
471 	} else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) {
472 		hookptr = &priv->lower;
473 		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
474 	} else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) {
475 		hookptr = &priv->orphan;
476 		NG_HOOK_SET_RCVDATA(hook, ng_ether_rcv_lower);
477 	} else
478 		return (EINVAL);
479 
480 	/* Check if already connected (shouldn't be, but doesn't hurt) */
481 	if (*hookptr != NULL)
482 		return (EISCONN);
483 
484 	/* Disable hardware checksums while 'upper' hook is connected */
485 	if (hookptr == &priv->upper)
486 		priv->ifp->if_hwassist = 0;
487 	NG_HOOK_HI_STACK(hook);
488 	/* OK */
489 	*hookptr = hook;
490 	return (0);
491 }
492 
493 /*
494  * Receive an incoming control message.
495  */
496 static int
ng_ether_rcvmsg(node_p node,item_p item,hook_p lasthook)497 ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook)
498 {
499 	const priv_p priv = NG_NODE_PRIVATE(node);
500 	struct ng_mesg *resp = NULL;
501 	int error = 0;
502 	struct ng_mesg *msg;
503 
504 	NGI_GET_MSG(item, msg);
505 	switch (msg->header.typecookie) {
506 	case NGM_ETHER_COOKIE:
507 		switch (msg->header.cmd) {
508 		case NGM_ETHER_GET_IFNAME:
509 			NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT);
510 			if (resp == NULL) {
511 				error = ENOMEM;
512 				break;
513 			}
514 			strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ);
515 			break;
516 		case NGM_ETHER_GET_IFINDEX:
517 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
518 			if (resp == NULL) {
519 				error = ENOMEM;
520 				break;
521 			}
522 			*((u_int32_t *)resp->data) = priv->ifp->if_index;
523 			break;
524 		case NGM_ETHER_GET_ENADDR:
525 			NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT);
526 			if (resp == NULL) {
527 				error = ENOMEM;
528 				break;
529 			}
530 			bcopy(IF_LLADDR(priv->ifp),
531 			    resp->data, ETHER_ADDR_LEN);
532 			break;
533 		case NGM_ETHER_SET_ENADDR:
534 		    {
535 			if (msg->header.arglen != ETHER_ADDR_LEN) {
536 				error = EINVAL;
537 				break;
538 			}
539 			error = if_setlladdr(priv->ifp,
540 			    (u_char *)msg->data, ETHER_ADDR_LEN);
541 			break;
542 		    }
543 		case NGM_ETHER_GET_PROMISC:
544 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
545 			if (resp == NULL) {
546 				error = ENOMEM;
547 				break;
548 			}
549 			*((u_int32_t *)resp->data) = priv->promisc;
550 			break;
551 		case NGM_ETHER_SET_PROMISC:
552 		    {
553 			u_char want;
554 
555 			if (msg->header.arglen != sizeof(u_int32_t)) {
556 				error = EINVAL;
557 				break;
558 			}
559 			want = !!*((u_int32_t *)msg->data);
560 			if (want ^ priv->promisc) {
561 				if ((error = ifpromisc(priv->ifp, want)) != 0)
562 					break;
563 				priv->promisc = want;
564 			}
565 			break;
566 		    }
567 		case NGM_ETHER_GET_AUTOSRC:
568 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
569 			if (resp == NULL) {
570 				error = ENOMEM;
571 				break;
572 			}
573 			*((u_int32_t *)resp->data) = priv->autoSrcAddr;
574 			break;
575 		case NGM_ETHER_SET_AUTOSRC:
576 			if (msg->header.arglen != sizeof(u_int32_t)) {
577 				error = EINVAL;
578 				break;
579 			}
580 			priv->autoSrcAddr = !!*((u_int32_t *)msg->data);
581 			break;
582 		case NGM_ETHER_ADD_MULTI:
583 		    {
584 			struct sockaddr_dl sa_dl;
585 			struct epoch_tracker et;
586 			struct ifmultiaddr *ifma;
587 
588 			if (msg->header.arglen != ETHER_ADDR_LEN) {
589 				error = EINVAL;
590 				break;
591 			}
592 			bzero(&sa_dl, sizeof(struct sockaddr_dl));
593 			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
594 			sa_dl.sdl_family = AF_LINK;
595 			sa_dl.sdl_alen = ETHER_ADDR_LEN;
596 			bcopy((void *)msg->data, LLADDR(&sa_dl),
597 			    ETHER_ADDR_LEN);
598 			/*
599 			 * Netgraph is only permitted to join groups once
600 			 * via the if_addmulti() KPI, because it cannot hold
601 			 * struct ifmultiaddr * between calls. It may also
602 			 * lose a race while we check if the membership
603 			 * already exists.
604 			 */
605 			NET_EPOCH_ENTER(et);
606 			ifma = if_findmulti(priv->ifp,
607 			    (struct sockaddr *)&sa_dl);
608 			NET_EPOCH_EXIT(et);
609 			if (ifma != NULL) {
610 				error = EADDRINUSE;
611 			} else {
612 				error = if_addmulti(priv->ifp,
613 				    (struct sockaddr *)&sa_dl, &ifma);
614 			}
615 			break;
616 		    }
617 		case NGM_ETHER_DEL_MULTI:
618 		    {
619 			struct sockaddr_dl sa_dl;
620 
621 			if (msg->header.arglen != ETHER_ADDR_LEN) {
622 				error = EINVAL;
623 				break;
624 			}
625 			bzero(&sa_dl, sizeof(struct sockaddr_dl));
626 			sa_dl.sdl_len = sizeof(struct sockaddr_dl);
627 			sa_dl.sdl_family = AF_LINK;
628 			sa_dl.sdl_alen = ETHER_ADDR_LEN;
629 			bcopy((void *)msg->data, LLADDR(&sa_dl),
630 			    ETHER_ADDR_LEN);
631 			error = if_delmulti(priv->ifp,
632 			    (struct sockaddr *)&sa_dl);
633 			break;
634 		    }
635 		case NGM_ETHER_DETACH:
636 			ng_ether_detach(NULL, priv->ifp);
637 			break;
638 		default:
639 			error = EINVAL;
640 			break;
641 		}
642 		break;
643 	default:
644 		error = EINVAL;
645 		break;
646 	}
647 	NG_RESPOND_MSG(error, node, item, resp);
648 	NG_FREE_MSG(msg);
649 	return (error);
650 }
651 
652 /*
653  * Receive data on a hook.
654  * Since we use per-hook recveive methods this should never be called.
655  */
656 static int
ng_ether_rcvdata(hook_p hook,item_p item)657 ng_ether_rcvdata(hook_p hook, item_p item)
658 {
659 	NG_FREE_ITEM(item);
660 
661 	panic("%s: weird hook", __func__);
662 }
663 
664 /*
665  * Handle an mbuf received on the "lower" or "orphan" hook.
666  */
667 static int
ng_ether_rcv_lower(hook_p hook,item_p item)668 ng_ether_rcv_lower(hook_p hook, item_p item)
669 {
670 	struct mbuf *m;
671 	const node_p node = NG_HOOK_NODE(hook);
672 	const priv_p priv = NG_NODE_PRIVATE(node);
673  	struct ifnet *const ifp = priv->ifp;
674 
675 	NGI_GET_M(item, m);
676 	NG_FREE_ITEM(item);
677 
678 	/* Check whether interface is ready for packets */
679 
680 	if (!((ifp->if_flags & IFF_UP) &&
681 	    (ifp->if_drv_flags & IFF_DRV_RUNNING))) {
682 		NG_FREE_M(m);
683 		return (ENETDOWN);
684 	}
685 
686 	/* Make sure header is fully pulled up */
687 	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
688 		NG_FREE_M(m);
689 		return (EINVAL);
690 	}
691 	if (m->m_len < sizeof(struct ether_header)
692 	    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
693 		return (ENOBUFS);
694 
695 	/* Drop in the MAC address if desired */
696 	if (priv->autoSrcAddr) {
697 		/* Make the mbuf writable if it's not already */
698 		if (!M_WRITABLE(m)
699 		    && (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
700 			return (ENOBUFS);
701 
702 		/* Overwrite source MAC address */
703 		bcopy(IF_LLADDR(ifp),
704 		    mtod(m, struct ether_header *)->ether_shost,
705 		    ETHER_ADDR_LEN);
706 	}
707 
708 	/* Send it on its way */
709 	return ether_output_frame(ifp, m);
710 }
711 
712 /*
713  * Handle an mbuf received on the "upper" hook.
714  */
715 static int
ng_ether_rcv_upper(hook_p hook,item_p item)716 ng_ether_rcv_upper(hook_p hook, item_p item)
717 {
718 	struct mbuf *m;
719 	const node_p node = NG_HOOK_NODE(hook);
720 	const priv_p priv = NG_NODE_PRIVATE(node);
721 	struct ifnet *ifp = priv->ifp;
722 
723 	NGI_GET_M(item, m);
724 	NG_FREE_ITEM(item);
725 
726 	/* Check length and pull off header */
727 	if (m->m_pkthdr.len < sizeof(struct ether_header)) {
728 		NG_FREE_M(m);
729 		return (EINVAL);
730 	}
731 	if (m->m_len < sizeof(struct ether_header) &&
732 	    (m = m_pullup(m, sizeof(struct ether_header))) == NULL)
733 		return (ENOBUFS);
734 
735 	m->m_pkthdr.rcvif = ifp;
736 
737 	/* Pass the packet to the bridge, it may come back to us */
738 	if (ifp->if_bridge) {
739 		BRIDGE_INPUT(ifp, m);
740 		if (m == NULL)
741 			return (0);
742 	}
743 
744 	/* Route packet back in */
745 	ether_demux(ifp, m);
746 	return (0);
747 }
748 
749 /*
750  * Shutdown node. This resets the node but does not remove it
751  * unless the REALLY_DIE flag is set.
752  */
753 static int
ng_ether_shutdown(node_p node)754 ng_ether_shutdown(node_p node)
755 {
756 	const priv_p priv = NG_NODE_PRIVATE(node);
757 
758 	if (node->nd_flags & NGF_REALLY_DIE) {
759 		/*
760 		 * The ifnet is going away, perhaps because the driver was
761 		 * unloaded or its vnet is being torn down.
762 		 */
763 		NG_NODE_SET_PRIVATE(node, NULL);
764 		if (priv->ifp != NULL)
765 			IFP2NG(priv->ifp) = NULL;
766 		free(priv, M_NETGRAPH);
767 		NG_NODE_UNREF(node);	/* free node itself */
768 		return (0);
769 	}
770 	if (priv->promisc) {		/* disable promiscuous mode */
771 		(void)ifpromisc(priv->ifp, 0);
772 		priv->promisc = 0;
773 	}
774 	NG_NODE_REVIVE(node);		/* Signal ng_rmnode we are persisant */
775 
776 	return (0);
777 }
778 
779 /*
780  * Hook disconnection.
781  */
782 static int
ng_ether_disconnect(hook_p hook)783 ng_ether_disconnect(hook_p hook)
784 {
785 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
786 
787 	if (hook == priv->upper) {
788 		priv->upper = NULL;
789 		if (priv->ifp != NULL)		/* restore h/w csum */
790 			priv->ifp->if_hwassist = priv->hwassist;
791 	} else if (hook == priv->lower)
792 		priv->lower = NULL;
793 	else if (hook == priv->orphan)
794 		priv->orphan = NULL;
795 	else
796 		panic("%s: weird hook", __func__);
797 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
798 	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
799 		ng_rmnode_self(NG_HOOK_NODE(hook));	/* reset node */
800 	return (0);
801 }
802 
803 /******************************************************************
804 		    	INITIALIZATION
805 ******************************************************************/
806 
807 /*
808  * Handle loading and unloading for this node type.
809  */
810 static int
ng_ether_mod_event(module_t mod,int event,void * data)811 ng_ether_mod_event(module_t mod, int event, void *data)
812 {
813 	int error = 0;
814 
815 	switch (event) {
816 	case MOD_LOAD:
817 		ng_ether_output_p = ng_ether_output;
818 		ng_ether_input_p = ng_ether_input;
819 		ng_ether_input_orphan_p = ng_ether_input_orphan;
820 
821 		ifnet_arrival_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event,
822 		    ng_ether_arrival, NULL, EVENTHANDLER_PRI_ANY);
823 		ifnet_departure_tag =
824 		    EVENTHANDLER_REGISTER(ifnet_departure_event,
825 		    ng_ether_detach, NULL, EVENTHANDLER_PRI_ANY);
826 		ifnet_rename_tag = EVENTHANDLER_REGISTER(ifnet_rename_event,
827 		    ng_ether_rename, NULL, EVENTHANDLER_PRI_ANY);
828 		ifnet_linkstate_tag = EVENTHANDLER_REGISTER(ifnet_link_event,
829 		    ng_ether_link_state, NULL, EVENTHANDLER_PRI_ANY);
830 		break;
831 
832 	case MOD_UNLOAD:
833 
834 		/*
835 		 * Note that the base code won't try to unload us until
836 		 * all nodes have been removed, and that can't happen
837 		 * until all Ethernet interfaces are removed. In any
838 		 * case, we know there are no nodes left if the action
839 		 * is MOD_UNLOAD, so there's no need to detach any nodes.
840 		 */
841 
842 		EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ifnet_arrival_tag);
843 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
844 		    ifnet_departure_tag);
845 		EVENTHANDLER_DEREGISTER(ifnet_rename_event, ifnet_rename_tag);
846 		EVENTHANDLER_DEREGISTER(ifnet_link_event, ifnet_linkstate_tag);
847 
848 		/* Unregister function hooks */
849 		ng_ether_output_p = NULL;
850 		ng_ether_input_p = NULL;
851 		ng_ether_input_orphan_p = NULL;
852 		break;
853 
854 	default:
855 		error = EOPNOTSUPP;
856 		break;
857 	}
858 	return (error);
859 }
860 
861 static void
ng_ether_vnet_init(void * arg __unused)862 ng_ether_vnet_init(void *arg __unused)
863 {
864 	if_foreach_sleep(NULL, NULL, ng_ether_attach, NULL);
865 }
866 VNET_SYSINIT(ng_ether_vnet_init, SI_SUB_PROTO_IF, SI_ORDER_ANY,
867     ng_ether_vnet_init, NULL);
868