1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2002 Ericsson Research & Pekka Nikander
5 * Copyright (c) 2020 Nick Hibma <n_hibma@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice unmodified, this list of conditions, and the following
13 * disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /*
32 * MACFILTER NETGRAPH NODE TYPE
33 *
34 * This node type routes packets from the ether hook to either the default hook
35 * if sender MAC address is not in the MAC table, or out over the specified
36 * hook if it is.
37 *
38 * Other node types can then be used to apply specific processing to the
39 * packets on each hook.
40 *
41 * If compiled with NG_MACFILTER_DEBUG the flow and resizing of the MAC table
42 * are logged to the console.
43 *
44 * If compiled with NG_MACFILTER_DEBUG_RECVDATA every packet handled is logged
45 * on the console.
46 */
47
48 #include <sys/param.h>
49 #include <sys/ctype.h>
50 #include <sys/systm.h>
51
52 #include <sys/lock.h>
53 #include <sys/mbuf.h>
54 #include <sys/mutex.h>
55
56 #include <sys/kernel.h>
57 #include <sys/malloc.h>
58
59 #include <sys/socket.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
66 #include "ng_macfilter.h"
67
68 #ifdef NG_SEPARATE_MALLOC
69 MALLOC_DEFINE(M_NETGRAPH_MACFILTER, "netgraph_macfilter", "netgraph macfilter node ");
70 #else
71 #define M_NETGRAPH_MACFILTER M_NETGRAPH
72 #endif
73
74 #define MACTABLE_BLOCKSIZE 128 /* block size for incrementing table */
75
76 #ifdef NG_MACFILTER_DEBUG
77 #define MACFILTER_DEBUG(fmt, ...) printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, __VA_ARGS__)
78 #else
79 #define MACFILTER_DEBUG(fmt, ...)
80 #endif
81 #define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
82 #define MAC_S_ARGS(v) (v)[0], (v)[1], (v)[2], (v)[3], (v)[4], (v)[5]
83
84 /*
85 * Parse type for struct ngm_macfilter_direct
86 */
87
88 static const struct ng_parse_struct_field macfilter_direct_fields[]
89 = NGM_MACFILTER_DIRECT_FIELDS;
90 static const struct ng_parse_type ng_macfilter_direct_type = {
91 &ng_parse_struct_type,
92 &macfilter_direct_fields
93 };
94
95 /*
96 * Parse type for struct ngm_macfilter_direct_hookid.
97 */
98
99 static const struct ng_parse_struct_field macfilter_direct_ndx_fields[]
100 = NGM_MACFILTER_DIRECT_NDX_FIELDS;
101 static const struct ng_parse_type ng_macfilter_direct_hookid_type = {
102 &ng_parse_struct_type,
103 &macfilter_direct_ndx_fields
104 };
105
106 /*
107 * Parse types for struct ngm_macfilter_get_macs.
108 */
109 static int
macfilter_get_macs_count(const struct ng_parse_type * type,const u_char * start,const u_char * buf)110 macfilter_get_macs_count(const struct ng_parse_type *type,
111 const u_char *start, const u_char *buf)
112 {
113 const struct ngm_macfilter_macs *const ngm_macs =
114 (const struct ngm_macfilter_macs *)(buf - OFFSETOF(struct ngm_macfilter_macs, macs));
115
116 return ngm_macs->n;
117 }
118 static const struct ng_parse_struct_field ng_macfilter_mac_fields[]
119 = NGM_MACFILTER_MAC_FIELDS;
120 static const struct ng_parse_type ng_macfilter_mac_type = {
121 &ng_parse_struct_type,
122 ng_macfilter_mac_fields,
123 };
124 static const struct ng_parse_array_info ng_macfilter_macs_array_info = {
125 &ng_macfilter_mac_type,
126 macfilter_get_macs_count
127 };
128 static const struct ng_parse_type ng_macfilter_macs_array_type = {
129 &ng_parse_array_type,
130 &ng_macfilter_macs_array_info
131 };
132 static const struct ng_parse_struct_field ng_macfilter_macs_fields[]
133 = NGM_MACFILTER_MACS_FIELDS;
134 static const struct ng_parse_type ng_macfilter_macs_type = {
135 &ng_parse_struct_type,
136 &ng_macfilter_macs_fields
137 };
138
139 /*
140 * Parse types for struct ngm_macfilter_get_hooks.
141 */
142 static int
macfilter_get_upper_hook_count(const struct ng_parse_type * type,const u_char * start,const u_char * buf)143 macfilter_get_upper_hook_count(const struct ng_parse_type *type,
144 const u_char *start, const u_char *buf)
145 {
146 const struct ngm_macfilter_hooks *const ngm_hooks =
147 (const struct ngm_macfilter_hooks *)(buf - OFFSETOF(struct ngm_macfilter_hooks, hooks));
148
149 MACFILTER_DEBUG("buf %p, ngm_hooks %p, n %d", buf, ngm_hooks, ngm_hooks->n);
150
151 return ngm_hooks->n;
152 }
153
154 static const struct ng_parse_struct_field ng_macfilter_hook_fields[]
155 = NGM_MACFILTER_HOOK_FIELDS;
156 static const struct ng_parse_type ng_macfilter_hook_type = {
157 &ng_parse_struct_type,
158 ng_macfilter_hook_fields,
159 };
160 static const struct ng_parse_array_info ng_macfilter_hooks_array_info = {
161 &ng_macfilter_hook_type,
162 macfilter_get_upper_hook_count
163 };
164 static const struct ng_parse_type ng_macfilter_hooks_array_type = {
165 &ng_parse_array_type,
166 &ng_macfilter_hooks_array_info
167 };
168 static const struct ng_parse_struct_field ng_macfilter_hooks_fields[]
169 = NGM_MACFILTER_HOOKS_FIELDS;
170 static const struct ng_parse_type ng_macfilter_hooks_type = {
171 &ng_parse_struct_type,
172 &ng_macfilter_hooks_fields
173 };
174
175 /*
176 * List of commands and how to convert arguments to/from ASCII
177 */
178 static const struct ng_cmdlist ng_macfilter_cmdlist[] = {
179 {
180 NGM_MACFILTER_COOKIE,
181 NGM_MACFILTER_RESET,
182 "reset",
183 NULL,
184 NULL
185 },
186 {
187 NGM_MACFILTER_COOKIE,
188 NGM_MACFILTER_DIRECT,
189 "direct",
190 &ng_macfilter_direct_type,
191 NULL
192 },
193 {
194 NGM_MACFILTER_COOKIE,
195 NGM_MACFILTER_DIRECT_HOOKID,
196 "directi",
197 &ng_macfilter_direct_hookid_type,
198 NULL
199 },
200 {
201 NGM_MACFILTER_COOKIE,
202 NGM_MACFILTER_GET_MACS,
203 "getmacs",
204 NULL,
205 &ng_macfilter_macs_type
206 },
207 {
208 NGM_MACFILTER_COOKIE,
209 NGM_MACFILTER_GETCLR_MACS,
210 "getclrmacs",
211 NULL,
212 &ng_macfilter_macs_type
213 },
214 {
215 NGM_MACFILTER_COOKIE,
216 NGM_MACFILTER_CLR_MACS,
217 "clrmacs",
218 NULL,
219 NULL,
220 },
221 {
222 NGM_MACFILTER_COOKIE,
223 NGM_MACFILTER_GET_HOOKS,
224 "gethooks",
225 NULL,
226 &ng_macfilter_hooks_type
227 },
228 { 0 }
229 };
230
231 /*
232 * Netgraph node type descriptor
233 */
234 static ng_constructor_t ng_macfilter_constructor;
235 static ng_rcvmsg_t ng_macfilter_rcvmsg;
236 static ng_shutdown_t ng_macfilter_shutdown;
237 static ng_newhook_t ng_macfilter_newhook;
238 static ng_rcvdata_t ng_macfilter_rcvdata;
239 static ng_disconnect_t ng_macfilter_disconnect;
240
241 static struct ng_type typestruct = {
242 .version = NG_ABI_VERSION,
243 .name = NG_MACFILTER_NODE_TYPE,
244 .constructor = ng_macfilter_constructor,
245 .rcvmsg = ng_macfilter_rcvmsg,
246 .shutdown = ng_macfilter_shutdown,
247 .newhook = ng_macfilter_newhook,
248 .rcvdata = ng_macfilter_rcvdata,
249 .disconnect = ng_macfilter_disconnect,
250 .cmdlist = ng_macfilter_cmdlist
251 };
252 NETGRAPH_INIT(macfilter, &typestruct);
253
254 /*
255 * Per MAC address info: the hook where to send to, the address
256 * Note: We use the same struct as in the netgraph message, so we can bcopy the
257 * array.
258 */
259 typedef struct ngm_macfilter_mac *mf_mac_p;
260
261 /*
262 * Node info
263 */
264 typedef struct {
265 hook_p mf_ether_hook; /* Ethernet hook */
266
267 hook_p *mf_upper; /* Upper hooks */
268 u_int mf_upper_cnt; /* Allocated # of upper slots */
269
270 struct mtx mtx; /* Mutex for MACs table */
271 mf_mac_p mf_macs; /* MAC info: dynamically allocated */
272 u_int mf_mac_allocated;/* Allocated # of MAC slots */
273 u_int mf_mac_used; /* Used # of MAC slots */
274 } *macfilter_p;
275
276 /*
277 * Resize the MAC table to accommodate at least mfp->mf_mac_used + 1 entries.
278 *
279 * Note: mtx already held
280 */
281 static int
macfilter_mactable_resize(macfilter_p mfp)282 macfilter_mactable_resize(macfilter_p mfp)
283 {
284 int error = 0;
285
286 int n = mfp->mf_mac_allocated;
287 if (mfp->mf_mac_used < 2*MACTABLE_BLOCKSIZE-1) /* minimum size */
288 n = 2*MACTABLE_BLOCKSIZE-1;
289 else if (mfp->mf_mac_used + 2*MACTABLE_BLOCKSIZE < mfp->mf_mac_allocated) /* reduce size */
290 n = mfp->mf_mac_allocated - MACTABLE_BLOCKSIZE;
291 else if (mfp->mf_mac_used == mfp->mf_mac_allocated) /* increase size */
292 n = mfp->mf_mac_allocated + MACTABLE_BLOCKSIZE;
293
294 if (n != mfp->mf_mac_allocated) {
295 MACFILTER_DEBUG("used=%d allocated=%d->%d",
296 mfp->mf_mac_used, mfp->mf_mac_allocated, n);
297
298 mf_mac_p mfp_new = realloc(mfp->mf_macs,
299 sizeof(mfp->mf_macs[0])*n,
300 M_NETGRAPH, M_NOWAIT | M_ZERO);
301 if (mfp_new == NULL) {
302 error = -1;
303 } else {
304 mfp->mf_macs = mfp_new;
305 mfp->mf_mac_allocated = n;
306 }
307 }
308
309 return error;
310 }
311
312 /*
313 * Resets the macfilter to pass all received packets
314 * to the default hook.
315 *
316 * Note: mtx already held
317 */
318 static void
macfilter_reset(macfilter_p mfp)319 macfilter_reset(macfilter_p mfp)
320 {
321 mfp->mf_mac_used = 0;
322
323 macfilter_mactable_resize(mfp);
324 }
325
326 /*
327 * Resets the counts for each MAC address.
328 *
329 * Note: mtx already held
330 */
331 static void
macfilter_reset_stats(macfilter_p mfp)332 macfilter_reset_stats(macfilter_p mfp)
333 {
334 int i;
335
336 for (i = 0; i < mfp->mf_mac_used; i++) {
337 mf_mac_p p = &mfp->mf_macs[i];
338 p->packets_in = p->packets_out = 0;
339 p->bytes_in = p->bytes_out = 0;
340 }
341 }
342
343 /*
344 * Count the number of matching macs routed to this hook.
345 *
346 * Note: mtx already held
347 */
348 static int
macfilter_mac_count(macfilter_p mfp,int hookid)349 macfilter_mac_count(macfilter_p mfp, int hookid)
350 {
351 int i;
352 int cnt = 0;
353
354 for (i = 0; i < mfp->mf_mac_used; i++)
355 if (mfp->mf_macs[i].hookid == hookid)
356 cnt++;
357
358 return cnt;
359 }
360
361 /*
362 * Find a MAC address in the mac table.
363 *
364 * Returns 0 on failure with *ri set to index before which to insert a new
365 * element. Or returns 1 on success with *ri set to the index of the element
366 * that matches.
367 *
368 * Note: mtx already held.
369 */
370 static u_int
macfilter_find_mac(macfilter_p mfp,const u_char * ether,u_int * ri)371 macfilter_find_mac(macfilter_p mfp, const u_char *ether, u_int *ri)
372 {
373 mf_mac_p mf_macs = mfp->mf_macs;
374
375 u_int base = 0;
376 u_int range = mfp->mf_mac_used;
377 while (range > 0) {
378 u_int middle = base + (range >> 1); /* middle */
379 int d = bcmp(ether, mf_macs[middle].ether, ETHER_ADDR_LEN);
380 if (d == 0) { /* match */
381 *ri = middle;
382 return 1;
383 } else if (d > 0) { /* move right */
384 range -= middle - base + 1;
385 base = middle + 1;
386 } else { /* move left */
387 range = middle - base;
388 }
389 }
390
391 *ri = base;
392 return 0;
393 }
394
395 /*
396 * Change the upper hook for the given MAC address. If the hook id is zero (the
397 * default hook), the MAC address is removed from the table. Otherwise it is
398 * inserted to the table at a proper location, and the id of the hook is
399 * marked.
400 *
401 * Note: mtx already held.
402 */
403 static int
macfilter_mactable_change(macfilter_p mfp,u_char * ether,int hookid)404 macfilter_mactable_change(macfilter_p mfp, u_char *ether, int hookid)
405 {
406 u_int i;
407 int found = macfilter_find_mac(mfp, ether, &i);
408
409 mf_mac_p mf_macs = mfp->mf_macs;
410
411 MACFILTER_DEBUG("ether=" MAC_FMT " found=%d i=%d ether=" MAC_FMT " hookid=%d->%d used=%d allocated=%d",
412 MAC_S_ARGS(ether), found, i, MAC_S_ARGS(mf_macs[i].ether),
413 (found? mf_macs[i].hookid:NG_MACFILTER_HOOK_DEFAULT_ID), hookid,
414 mfp->mf_mac_used, mfp->mf_mac_allocated);
415
416 if (found) {
417 if (hookid == NG_MACFILTER_HOOK_DEFAULT_ID) { /* drop */
418 /* Compress table */
419 mfp->mf_mac_used--;
420 size_t len = (mfp->mf_mac_used - i) * sizeof(mf_macs[0]);
421 if (len > 0)
422 bcopy(&mf_macs[i+1], &mf_macs[i], len);
423
424 macfilter_mactable_resize(mfp);
425 } else { /* modify */
426 mf_macs[i].hookid = hookid;
427 }
428 } else {
429 if (hookid == NG_MACFILTER_HOOK_DEFAULT_ID) { /* not found */
430 /* not present and not inserted */
431 return 0;
432 } else { /* add */
433 if (macfilter_mactable_resize(mfp) == -1) {
434 return ENOMEM;
435 } else {
436 mf_macs = mfp->mf_macs; /* reassign; might have moved during resize */
437
438 /* make room for new entry, unless appending */
439 size_t len = (mfp->mf_mac_used - i) * sizeof(mf_macs[0]);
440 if (len > 0)
441 bcopy(&mf_macs[i], &mf_macs[i+1], len);
442
443 mf_macs[i].hookid = hookid;
444 bcopy(ether, mf_macs[i].ether, ETHER_ADDR_LEN);
445
446 mfp->mf_mac_used++;
447 }
448 }
449 }
450
451 return 0;
452 }
453
454 static int
macfilter_mactable_remove_by_hookid(macfilter_p mfp,int hookid)455 macfilter_mactable_remove_by_hookid(macfilter_p mfp, int hookid)
456 {
457 int i, j;
458
459 for (i = 0, j = 0; i < mfp->mf_mac_used; i++) {
460 if (mfp->mf_macs[i].hookid != hookid) {
461 if (i != j)
462 bcopy(&mfp->mf_macs[i], &mfp->mf_macs[j], sizeof(mfp->mf_macs[0]));
463 j++;
464 }
465 }
466
467 int removed = i - j;
468 mfp->mf_mac_used = j;
469 macfilter_mactable_resize(mfp);
470
471 return removed;
472 }
473
474 static int
macfilter_find_hook(macfilter_p mfp,const char * hookname)475 macfilter_find_hook(macfilter_p mfp, const char *hookname)
476 {
477 int hookid;
478
479 for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) {
480 if (mfp->mf_upper[hookid]) {
481 if (strncmp(NG_HOOK_NAME(mfp->mf_upper[hookid]),
482 hookname, NG_HOOKSIZ) == 0) {
483 return hookid;
484 }
485 }
486 }
487
488 return 0;
489 }
490
491 static int
macfilter_direct(macfilter_p mfp,struct ngm_macfilter_direct * md)492 macfilter_direct(macfilter_p mfp, struct ngm_macfilter_direct *md)
493 {
494 MACFILTER_DEBUG("ether=" MAC_FMT " hook=%s",
495 MAC_S_ARGS(md->ether), md->hookname);
496
497 int hookid = macfilter_find_hook(mfp, md->hookname);
498 if (hookid < 0)
499 return ENOENT;
500
501 return macfilter_mactable_change(mfp, md->ether, hookid);
502 }
503
504 static int
macfilter_direct_hookid(macfilter_p mfp,struct ngm_macfilter_direct_hookid * mdi)505 macfilter_direct_hookid(macfilter_p mfp, struct ngm_macfilter_direct_hookid *mdi)
506 {
507 MACFILTER_DEBUG("ether=" MAC_FMT " hookid=%d",
508 MAC_S_ARGS(mdi->ether), mdi->hookid);
509
510 if (mdi->hookid >= mfp->mf_upper_cnt)
511 return EINVAL;
512 else if (mfp->mf_upper[mdi->hookid] == NULL)
513 return EINVAL;
514
515 return macfilter_mactable_change(mfp, mdi->ether, mdi->hookid);
516 }
517
518 /*
519 * Packet handling
520 */
521
522 /*
523 * Pass packets received from any upper hook to
524 * a lower hook
525 */
526 static int
macfilter_ether_output(hook_p hook,macfilter_p mfp,struct mbuf * m,hook_p * next_hook)527 macfilter_ether_output(hook_p hook, macfilter_p mfp, struct mbuf *m, hook_p *next_hook)
528 {
529 struct ether_header *ether_header = mtod(m, struct ether_header *);
530 u_char *ether = ether_header->ether_dhost;
531
532 *next_hook = mfp->mf_ether_hook;
533
534 mtx_lock(&mfp->mtx);
535
536 u_int i;
537 int found = macfilter_find_mac(mfp, ether, &i);
538 if (found) {
539 mf_mac_p mf_macs = mfp->mf_macs;
540
541 mf_macs[i].packets_out++;
542 if (m->m_len > ETHER_HDR_LEN)
543 mf_macs[i].bytes_out += m->m_len - ETHER_HDR_LEN;
544
545 #ifdef NG_MACFILTER_DEBUG_RECVDATA
546 MACFILTER_DEBUG("ether=" MAC_FMT " len=%db->%lldb: bytes: %s -> %s",
547 MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, mf_macs[i].bytes_out,
548 NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook));
549 #endif
550 } else {
551 #ifdef NG_MACFILTER_DEBUG_RECVDATA
552 MACFILTER_DEBUG("ether=" MAC_FMT " len=%db->?b: bytes: %s->%s",
553 MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN,
554 NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook));
555 #endif
556 }
557
558 mtx_unlock(&mfp->mtx);
559
560 return 0;
561 }
562
563 /*
564 * Search for the right upper hook, based on the source ethernet
565 * address. If not found, pass to the default upper hook.
566 */
567 static int
macfilter_ether_input(hook_p hook,macfilter_p mfp,struct mbuf * m,hook_p * next_hook)568 macfilter_ether_input(hook_p hook, macfilter_p mfp, struct mbuf *m, hook_p *next_hook)
569 {
570 struct ether_header *ether_header = mtod(m, struct ether_header *);
571 u_char *ether = ether_header->ether_shost;
572 int hookid = NG_MACFILTER_HOOK_DEFAULT_ID;
573
574 mtx_lock(&mfp->mtx);
575
576 u_int i;
577 int found = macfilter_find_mac(mfp, ether, &i);
578 if (found) {
579 mf_mac_p mf_macs = mfp->mf_macs;
580
581 mf_macs[i].packets_in++;
582 if (m->m_len > ETHER_HDR_LEN)
583 mf_macs[i].bytes_in += m->m_len - ETHER_HDR_LEN;
584
585 hookid = mf_macs[i].hookid;
586
587 #ifdef NG_MACFILTER_DEBUG_RECVDATA
588 MACFILTER_DEBUG("ether=" MAC_FMT " len=%db->%lldb: bytes: %s->%s",
589 MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, mf_macs[i].bytes_in,
590 NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook));
591 #endif
592 } else {
593 #ifdef NG_MACFILTER_DEBUG_RECVDATA
594 MACFILTER_DEBUG("ether=" MAC_FMT " len=%db->?b: bytes: %s->%s",
595 MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN,
596 NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook));
597 #endif
598 }
599
600 if (hookid >= mfp->mf_upper_cnt)
601 *next_hook = NULL;
602 else
603 *next_hook = mfp->mf_upper[hookid];
604
605 mtx_unlock(&mfp->mtx);
606
607 return 0;
608 }
609
610 /*
611 * ======================================================================
612 * Netgraph hooks
613 * ======================================================================
614 */
615
616 /*
617 * See basic netgraph code for comments on the individual functions.
618 */
619
620 static int
ng_macfilter_constructor(node_p node)621 ng_macfilter_constructor(node_p node)
622 {
623 macfilter_p mfp = malloc(sizeof(*mfp), M_NETGRAPH, M_NOWAIT | M_ZERO);
624 if (mfp == NULL)
625 return ENOMEM;
626
627 int error = macfilter_mactable_resize(mfp);
628 if (error)
629 return error;
630
631 NG_NODE_SET_PRIVATE(node, mfp);
632
633 mtx_init(&mfp->mtx, "Macfilter table", NULL, MTX_DEF);
634
635 return (0);
636 }
637
638 static int
ng_macfilter_newhook(node_p node,hook_p hook,const char * hookname)639 ng_macfilter_newhook(node_p node, hook_p hook, const char *hookname)
640 {
641 const macfilter_p mfp = NG_NODE_PRIVATE(node);
642
643 MACFILTER_DEBUG("%s", hookname);
644
645 if (strcmp(hookname, NG_MACFILTER_HOOK_ETHER) == 0) {
646 mfp->mf_ether_hook = hook;
647 } else {
648 int hookid;
649 if (strcmp(hookname, NG_MACFILTER_HOOK_DEFAULT) == 0) {
650 hookid = NG_MACFILTER_HOOK_DEFAULT_ID;
651 } else {
652 for (hookid = 1; hookid < mfp->mf_upper_cnt; hookid++)
653 if (mfp->mf_upper[hookid] == NULL)
654 break;
655 }
656
657 if (hookid >= mfp->mf_upper_cnt) {
658 MACFILTER_DEBUG("upper cnt %d -> %d", mfp->mf_upper_cnt, hookid + 1);
659
660 mfp->mf_upper_cnt = hookid + 1;
661 mfp->mf_upper = realloc(mfp->mf_upper,
662 sizeof(mfp->mf_upper[0])*mfp->mf_upper_cnt,
663 M_NETGRAPH, M_NOWAIT | M_ZERO);
664 }
665
666 mfp->mf_upper[hookid] = hook;
667 }
668
669 return(0);
670 }
671
672 static int
ng_macfilter_rcvmsg(node_p node,item_p item,hook_p lasthook)673 ng_macfilter_rcvmsg(node_p node, item_p item, hook_p lasthook)
674 {
675 const macfilter_p mfp = NG_NODE_PRIVATE(node);
676 struct ng_mesg *resp = NULL;
677 struct ng_mesg *msg;
678 int error = 0;
679 struct ngm_macfilter_macs *ngm_macs;
680 struct ngm_macfilter_hooks *ngm_hooks;
681 struct ngm_macfilter_direct *md;
682 struct ngm_macfilter_direct_hookid *mdi;
683 int n = 0, i = 0;
684 int hookid = 0;
685 int resplen;
686
687 NGI_GET_MSG(item, msg);
688
689 mtx_lock(&mfp->mtx);
690
691 switch (msg->header.typecookie) {
692 case NGM_MACFILTER_COOKIE:
693 switch (msg->header.cmd) {
694
695 case NGM_MACFILTER_RESET:
696 macfilter_reset(mfp);
697 break;
698
699 case NGM_MACFILTER_DIRECT:
700 if (msg->header.arglen != sizeof(struct ngm_macfilter_direct)) {
701 MACFILTER_DEBUG("direct: wrong type length (%d, expected %zu)",
702 msg->header.arglen, sizeof(struct ngm_macfilter_direct));
703 error = EINVAL;
704 break;
705 }
706 md = (struct ngm_macfilter_direct *)msg->data;
707 error = macfilter_direct(mfp, md);
708 break;
709 case NGM_MACFILTER_DIRECT_HOOKID:
710 if (msg->header.arglen != sizeof(struct ngm_macfilter_direct_hookid)) {
711 MACFILTER_DEBUG("direct hookid: wrong type length (%d, expected %zu)",
712 msg->header.arglen, sizeof(struct ngm_macfilter_direct));
713 error = EINVAL;
714 break;
715 }
716 mdi = (struct ngm_macfilter_direct_hookid *)msg->data;
717 error = macfilter_direct_hookid(mfp, mdi);
718 break;
719
720 case NGM_MACFILTER_GET_MACS:
721 case NGM_MACFILTER_GETCLR_MACS:
722 n = mfp->mf_mac_used;
723 resplen = sizeof(struct ngm_macfilter_macs) + n * sizeof(struct ngm_macfilter_mac);
724 NG_MKRESPONSE(resp, msg, resplen, M_NOWAIT);
725 if (resp == NULL) {
726 error = ENOMEM;
727 break;
728 }
729 ngm_macs = (struct ngm_macfilter_macs *)resp->data;
730 ngm_macs->n = n;
731 bcopy(mfp->mf_macs, &ngm_macs->macs[0], n * sizeof(struct ngm_macfilter_mac));
732
733 if (msg->header.cmd == NGM_MACFILTER_GETCLR_MACS)
734 macfilter_reset_stats(mfp);
735 break;
736
737 case NGM_MACFILTER_CLR_MACS:
738 macfilter_reset_stats(mfp);
739 break;
740
741 case NGM_MACFILTER_GET_HOOKS:
742 for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++)
743 if (mfp->mf_upper[hookid] != NULL)
744 n++;
745 resplen = sizeof(struct ngm_macfilter_hooks) + n * sizeof(struct ngm_macfilter_hook);
746 NG_MKRESPONSE(resp, msg, resplen, M_NOWAIT | M_ZERO);
747 if (resp == NULL) {
748 error = ENOMEM;
749 break;
750 }
751
752 ngm_hooks = (struct ngm_macfilter_hooks *)resp->data;
753 ngm_hooks->n = n;
754 for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) {
755 if (mfp->mf_upper[hookid] != NULL) {
756 struct ngm_macfilter_hook *ngm_hook = &ngm_hooks->hooks[i++];
757 strlcpy(ngm_hook->hookname,
758 NG_HOOK_NAME(mfp->mf_upper[hookid]),
759 NG_HOOKSIZ);
760 ngm_hook->hookid = hookid;
761 ngm_hook->maccnt = macfilter_mac_count(mfp, hookid);
762 }
763 }
764 break;
765
766 default:
767 error = EINVAL; /* unknown command */
768 break;
769 }
770 break;
771
772 default:
773 error = EINVAL; /* unknown cookie type */
774 break;
775 }
776
777 mtx_unlock(&mfp->mtx);
778
779 NG_RESPOND_MSG(error, node, item, resp);
780 NG_FREE_MSG(msg);
781
782 return error;
783 }
784
785 static int
ng_macfilter_rcvdata(hook_p hook,item_p item)786 ng_macfilter_rcvdata(hook_p hook, item_p item)
787 {
788 const macfilter_p mfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
789 int error;
790 hook_p next_hook = NULL;
791 struct mbuf *m;
792
793 m = NGI_M(item); /* 'item' still owns it. We are peeking */
794 MACFILTER_DEBUG("%s", NG_HOOK_NAME(hook));
795
796 if (hook == mfp->mf_ether_hook)
797 error = macfilter_ether_input(hook, mfp, m, &next_hook);
798 else if (mfp->mf_ether_hook != NULL)
799 error = macfilter_ether_output(hook, mfp, m, &next_hook);
800
801 if (next_hook == NULL) {
802 NG_FREE_ITEM(item);
803 return (0);
804 }
805
806 NG_FWD_ITEM_HOOK(error, item, next_hook);
807
808 return error;
809 }
810
811 static int
ng_macfilter_disconnect(hook_p hook)812 ng_macfilter_disconnect(hook_p hook)
813 {
814 const macfilter_p mfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
815
816 mtx_lock(&mfp->mtx);
817
818 if (mfp->mf_ether_hook == hook) {
819 mfp->mf_ether_hook = NULL;
820
821 MACFILTER_DEBUG("%s", NG_HOOK_NAME(hook));
822 } else {
823 int hookid;
824
825 for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) {
826 if (mfp->mf_upper[hookid] == hook) {
827 mfp->mf_upper[hookid] = NULL;
828
829 #ifndef NG_MACFILTER_DEBUG
830 macfilter_mactable_remove_by_hookid(mfp, hookid);
831 #else
832 int cnt = macfilter_mactable_remove_by_hookid(mfp, hookid);
833
834 MACFILTER_DEBUG("%s: removed %d MACs", NG_HOOK_NAME(hook), cnt);
835 #endif
836 break;
837 }
838 }
839
840 if (hookid == mfp->mf_upper_cnt - 1) {
841 /* Reduce the size of the array when the last element was removed */
842 for (--hookid; hookid >= 0 && mfp->mf_upper[hookid] == NULL; hookid--)
843 ;
844
845 MACFILTER_DEBUG("upper cnt %d -> %d", mfp->mf_upper_cnt, hookid + 1);
846 mfp->mf_upper_cnt = hookid + 1;
847 mfp->mf_upper = realloc(mfp->mf_upper,
848 sizeof(mfp->mf_upper[0])*mfp->mf_upper_cnt,
849 M_NETGRAPH, M_NOWAIT | M_ZERO);
850 }
851 }
852
853 mtx_unlock(&mfp->mtx);
854
855 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
856 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) {
857 ng_rmnode_self(NG_HOOK_NODE(hook));
858 }
859
860 return (0);
861 }
862
863 static int
ng_macfilter_shutdown(node_p node)864 ng_macfilter_shutdown(node_p node)
865 {
866 const macfilter_p mfp = NG_NODE_PRIVATE(node);
867
868 mtx_destroy(&mfp->mtx);
869 free(mfp->mf_upper, M_NETGRAPH);
870 free(mfp->mf_macs, M_NETGRAPH);
871 free(mfp, M_NETGRAPH);
872
873 NG_NODE_UNREF(node);
874
875 return (0);
876 }
877