xref: /freebsd/sys/netgraph/ng_macfilter.c (revision 95ee2897)
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