Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netgraph/ng_ether.c
102404 views
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 <[email protected]>
40
* Julian Elischer <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
869