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