2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2006 Vadim Goncharov <vadimnuclight@tpu.ru>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * Portions Copyright (c) 1999 Whistle Communications, Inc.
30 * (ng_bpf by Archie Cobbs <archie@freebsd.org>)
34 * TAG NETGRAPH NODE TYPE
36 * This node type accepts an arbitrary number of hooks. Each hook can be
37 * configured for an mbuf_tags(9) definition and two hook names: a hook
38 * for matched packets, and a hook for packets, that didn't match. Incoming
39 * packets are examined for configured tag, matched packets are delivered
40 * out via first hook, and not matched out via second. If corresponding hook
41 * is not configured, packets are dropped.
43 * A hook can also have an outgoing tag definition configured, so that
44 * all packets leaving the hook will be unconditionally appended with newly
47 * Both hooks can be set to null tag definitions (that is, with zeroed
48 * fields), so that packet tags are unmodified on output or all packets
49 * are unconditionally forwarded to non-matching hook on input. There is
50 * also a possibility to replace tags by specifying strip flag on input
51 * and replacing tag on corresponding output tag (or simply remove tag if
52 * no tag specified on output).
54 * If compiled with NG_TAG_DEBUG, each hook also keeps statistics about
55 * how many packets have matched, etc.
58 #include <sys/param.h>
59 #include <sys/systm.h>
60 #include <sys/errno.h>
61 #include <sys/kernel.h>
62 #include <sys/malloc.h>
64 #include <sys/stddef.h>
66 #include <netgraph/ng_message.h>
67 #include <netgraph/netgraph.h>
68 #include <netgraph/ng_parse.h>
69 #include <netgraph/ng_tag.h>
71 #ifdef NG_SEPARATE_MALLOC
72 static MALLOC_DEFINE(M_NETGRAPH_TAG
, "netgraph_tag", "netgraph tag node");
74 #define M_NETGRAPH_TAG M_NETGRAPH
77 #define ERROUT(x) do { error = (x); goto done; } while (0)
80 * Per hook private info.
82 * We've separated API and ABI here, to make easier changes in this node,
83 * if needed. If you want to change representation, please do not break API.
84 * We still keep API structures in memory to simplify access to them for
85 * GET* messages, but most of data is accessed in internal representation
86 * only. The reason for this is to speed things up - if data will be
87 * accessed from API structures, there would be double pointer dereferencing
88 * in the code, which almost necessarily leads to CPU cache misses and
91 * We also do another optimization by using resolved pointers to
92 * destination hooks instead of expensive ng_findhook().
94 struct ng_tag_hookinfo
{
95 hook_p hi_match
; /* matching hook pointer */
96 hook_p hi_nonmatch
; /* non-matching hook pointer */
97 uint32_t in_tag_cookie
;
98 uint32_t out_tag_cookie
;
102 uint16_t out_tag_len
;
106 struct ng_tag_hookin
*in
;
107 struct ng_tag_hookout
*out
;
109 struct ng_tag_hookstat stats
;
112 typedef struct ng_tag_hookinfo
*hinfo_p
;
114 /* Netgraph methods. */
115 static ng_constructor_t ng_tag_constructor
;
116 static ng_rcvmsg_t ng_tag_rcvmsg
;
117 static ng_shutdown_t ng_tag_shutdown
;
118 static ng_newhook_t ng_tag_newhook
;
119 static ng_rcvdata_t ng_tag_rcvdata
;
120 static ng_disconnect_t ng_tag_disconnect
;
122 /* Internal helper functions. */
123 static int ng_tag_setdata_in(hook_p hook
, const struct ng_tag_hookin
*hp
);
124 static int ng_tag_setdata_out(hook_p hook
, const struct ng_tag_hookout
*hp
);
126 /* Parse types for the field 'tag_data' in structs ng_tag_hookin and out. */
128 ng_tag_hookinary_getLength(const struct ng_parse_type
*type
,
129 const u_char
*start
, const u_char
*buf
)
131 const struct ng_tag_hookin
*hp
;
133 hp
= (const struct ng_tag_hookin
*)
134 (buf
- offsetof(struct ng_tag_hookin
, tag_data
));
135 return (hp
->tag_len
);
139 ng_tag_hookoutary_getLength(const struct ng_parse_type
*type
,
140 const u_char
*start
, const u_char
*buf
)
142 const struct ng_tag_hookout
*hp
;
144 hp
= (const struct ng_tag_hookout
*)
145 (buf
- offsetof(struct ng_tag_hookout
, tag_data
));
146 return (hp
->tag_len
);
149 static const struct ng_parse_type ng_tag_hookinary_type
= {
150 &ng_parse_bytearray_type
,
151 &ng_tag_hookinary_getLength
154 static const struct ng_parse_type ng_tag_hookoutary_type
= {
155 &ng_parse_bytearray_type
,
156 &ng_tag_hookoutary_getLength
159 /* Parse type for struct ng_tag_hookin. */
160 static const struct ng_parse_struct_field ng_tag_hookin_type_fields
[]
161 = NG_TAG_HOOKIN_TYPE_INFO(&ng_tag_hookinary_type
);
162 static const struct ng_parse_type ng_tag_hookin_type
= {
163 &ng_parse_struct_type
,
164 &ng_tag_hookin_type_fields
167 /* Parse type for struct ng_tag_hookout. */
168 static const struct ng_parse_struct_field ng_tag_hookout_type_fields
[]
169 = NG_TAG_HOOKOUT_TYPE_INFO(&ng_tag_hookoutary_type
);
170 static const struct ng_parse_type ng_tag_hookout_type
= {
171 &ng_parse_struct_type
,
172 &ng_tag_hookout_type_fields
176 /* Parse type for struct ng_tag_hookstat. */
177 static const struct ng_parse_struct_field ng_tag_hookstat_type_fields
[]
178 = NG_TAG_HOOKSTAT_TYPE_INFO
;
179 static const struct ng_parse_type ng_tag_hookstat_type
= {
180 &ng_parse_struct_type
,
181 &ng_tag_hookstat_type_fields
185 /* List of commands and how to convert arguments to/from ASCII. */
186 static const struct ng_cmdlist ng_tag_cmdlist
[] = {
198 &ng_parse_hookbuf_type
,
205 &ng_tag_hookout_type
,
212 &ng_parse_hookbuf_type
,
220 &ng_parse_hookbuf_type
,
221 &ng_tag_hookstat_type
227 &ng_parse_hookbuf_type
,
232 NGM_TAG_GETCLR_STATS
,
234 &ng_parse_hookbuf_type
,
235 &ng_tag_hookstat_type
241 /* Netgraph type descriptor. */
242 static struct ng_type typestruct
= {
243 .version
= NG_ABI_VERSION
,
244 .name
= NG_TAG_NODE_TYPE
,
245 .constructor
= ng_tag_constructor
,
246 .rcvmsg
= ng_tag_rcvmsg
,
247 .shutdown
= ng_tag_shutdown
,
248 .newhook
= ng_tag_newhook
,
249 .rcvdata
= ng_tag_rcvdata
,
250 .disconnect
= ng_tag_disconnect
,
251 .cmdlist
= ng_tag_cmdlist
,
253 NETGRAPH_INIT(tag
, &typestruct
);
256 * This are default API structures (initialized to zeroes) which are
257 * returned in response to GET* messages when no configuration was made.
258 * One could ask why to have this structures at all when we have
259 * ng_tag_hookinfo initialized to zero and don't need in and out structures
260 * at all to operate. Unfortunatelly, we have to return thisHook field
261 * in response to messages so the fastest and simplest way is to have
262 * this default structures and initialize thisHook once at hook creation
263 * rather than to do it on every response.
266 /* Default tag values for a hook that matches nothing. */
267 static const struct ng_tag_hookin ng_tag_default_in
= {
268 { '\0' }, /* to be filled in at hook creation time */
277 /* Default tag values for a hook that adds nothing */
278 static const struct ng_tag_hookout ng_tag_default_out
= {
279 { '\0' }, /* to be filled in at hook creation time */
288 * We don't keep any per-node private data - we do it on per-hook basis.
291 ng_tag_constructor(node_p node
)
300 ng_tag_newhook(node_p node
, hook_p hook
, const char *name
)
305 /* Create hook private structure. */
306 hip
= malloc(sizeof(*hip
), M_NETGRAPH_TAG
, M_NOWAIT
| M_ZERO
);
309 NG_HOOK_SET_PRIVATE(hook
, hip
);
312 * After M_ZERO both in and out hook pointers are set to NULL,
313 * as well as all members and pointers to in and out API
314 * structures, so we need to set explicitly only thisHook field
315 * in that structures (after allocating them, of course).
318 /* Attach the default IN data. */
319 if ((error
= ng_tag_setdata_in(hook
, &ng_tag_default_in
)) != 0) {
320 free(hip
, M_NETGRAPH_TAG
);
324 /* Attach the default OUT data. */
325 if ((error
= ng_tag_setdata_out(hook
, &ng_tag_default_out
)) != 0) {
326 free(hip
, M_NETGRAPH_TAG
);
331 * Set hook name. This is done only once at hook creation time
332 * since hook name can't change, rather than to do it on every
333 * response to messages requesting API structures with data who
336 strncpy(hip
->in
->thisHook
, name
, sizeof(hip
->in
->thisHook
) - 1);
337 hip
->in
->thisHook
[sizeof(hip
->in
->thisHook
) - 1] = '\0';
338 strncpy(hip
->out
->thisHook
, name
, sizeof(hip
->out
->thisHook
) - 1);
339 hip
->out
->thisHook
[sizeof(hip
->out
->thisHook
) - 1] = '\0';
344 * Receive a control message.
347 ng_tag_rcvmsg(node_p node
, item_p item
, hook_p lasthook
)
350 struct ng_mesg
*resp
= NULL
;
353 NGI_GET_MSG(item
, msg
);
354 switch (msg
->header
.typecookie
) {
356 switch (msg
->header
.cmd
) {
357 case NGM_TAG_SET_HOOKIN
:
359 struct ng_tag_hookin
*const
360 hp
= (struct ng_tag_hookin
*)msg
->data
;
364 if (msg
->header
.arglen
< sizeof(*hp
) ||
365 msg
->header
.arglen
< NG_TAG_HOOKIN_SIZE(hp
->tag_len
))
369 if ((hook
= ng_findhook(node
, hp
->thisHook
)) == NULL
)
372 /* Set new tag values. */
373 if ((error
= ng_tag_setdata_in(hook
, hp
)) != 0)
378 case NGM_TAG_SET_HOOKOUT
:
380 struct ng_tag_hookout
*const
381 hp
= (struct ng_tag_hookout
*)msg
->data
;
385 if (msg
->header
.arglen
< sizeof(*hp
) ||
386 msg
->header
.arglen
< NG_TAG_HOOKOUT_SIZE(hp
->tag_len
))
390 if ((hook
= ng_findhook(node
, hp
->thisHook
)) == NULL
)
393 /* Set new tag values. */
394 if ((error
= ng_tag_setdata_out(hook
, hp
)) != 0)
399 case NGM_TAG_GET_HOOKIN
:
401 struct ng_tag_hookin
*hp
;
405 if (msg
->header
.arglen
== 0)
407 msg
->data
[msg
->header
.arglen
- 1] = '\0';
410 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
413 /* Build response. */
414 hp
= ((hinfo_p
)NG_HOOK_PRIVATE(hook
))->in
;
415 NG_MKRESPONSE(resp
, msg
,
416 NG_TAG_HOOKIN_SIZE(hp
->tag_len
), M_WAITOK
);
417 /* M_WAITOK can't return NULL. */
418 bcopy(hp
, resp
->data
,
419 NG_TAG_HOOKIN_SIZE(hp
->tag_len
));
423 case NGM_TAG_GET_HOOKOUT
:
425 struct ng_tag_hookout
*hp
;
429 if (msg
->header
.arglen
== 0)
431 msg
->data
[msg
->header
.arglen
- 1] = '\0';
434 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
437 /* Build response. */
438 hp
= ((hinfo_p
)NG_HOOK_PRIVATE(hook
))->out
;
439 NG_MKRESPONSE(resp
, msg
,
440 NG_TAG_HOOKOUT_SIZE(hp
->tag_len
), M_WAITOK
);
441 /* M_WAITOK can't return NULL. */
442 bcopy(hp
, resp
->data
,
443 NG_TAG_HOOKOUT_SIZE(hp
->tag_len
));
448 case NGM_TAG_GET_STATS
:
449 case NGM_TAG_CLR_STATS
:
450 case NGM_TAG_GETCLR_STATS
:
452 struct ng_tag_hookstat
*stats
;
456 if (msg
->header
.arglen
== 0)
458 msg
->data
[msg
->header
.arglen
- 1] = '\0';
461 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
463 stats
= &((hinfo_p
)NG_HOOK_PRIVATE(hook
))->stats
;
465 /* Build response (if desired). */
466 if (msg
->header
.cmd
!= NGM_TAG_CLR_STATS
) {
468 msg
, sizeof(*stats
), M_WAITOK
);
469 /* M_WAITOK can't return NULL. */
470 bcopy(stats
, resp
->data
, sizeof(*stats
));
473 /* Clear stats (if desired). */
474 if (msg
->header
.cmd
!= NGM_TAG_GET_STATS
)
475 bzero(stats
, sizeof(*stats
));
478 #endif /* NG_TAG_DEBUG */
490 NG_RESPOND_MSG(error
, node
, item
, resp
);
496 * Receive data on a hook.
498 * Apply the filter, and then drop or forward packet as appropriate.
501 ng_tag_rcvdata(hook_p hook
, item_p item
)
504 struct m_tag
*tag
= NULL
;
505 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
506 uint16_t type
, tag_len
;
513 int found
= 0, error
= 0;
515 m
= NGI_M(item
); /* 'item' still owns it.. we are peeking */
517 totlen
= m
->m_pkthdr
.len
;
519 hip
->stats
.recvFrames
++;
520 hip
->stats
.recvOctets
+= totlen
;
523 /* Looking up incoming tag. */
524 cookie
= hip
->in_tag_cookie
;
525 type
= hip
->in_tag_id
;
526 tag_len
= hip
->in_tag_len
;
529 * We treat case of all zeroes specially (that is, cookie and
530 * type are equal to zero), as we assume that such tag
531 * can never occur in the wild. So we don't waste time trying
532 * to find such tag (for example, these are zeroes after hook
533 * creation in default structures).
535 if ((cookie
!= 0) || (type
!= 0)) {
536 tag
= m_tag_locate(m
, cookie
, type
, NULL
);
537 while (tag
!= NULL
) {
538 if (memcmp((void *)(tag
+ 1),
539 hip
->in_tag_data
, tag_len
) == 0) {
543 tag
= m_tag_locate(m
, cookie
, type
, tag
);
547 /* See if we got a match and find destination hook. */
550 hip
->stats
.recvMatchFrames
++;
551 hip
->stats
.recvMatchOctets
+= totlen
;
554 m_tag_delete(m
, tag
);
555 dest
= hip
->hi_match
;
557 dest
= hip
->hi_nonmatch
;
563 /* Deliver frame out destination hook. */
564 dhip
= NG_HOOK_PRIVATE(dest
);
567 dhip
->stats
.xmitOctets
+= totlen
;
568 dhip
->stats
.xmitFrames
++;
571 cookie
= dhip
->out_tag_cookie
;
572 type
= dhip
->out_tag_id
;
573 tag_len
= dhip
->out_tag_len
;
575 if ((cookie
!= 0) || (type
!= 0)) {
576 tag
= m_tag_alloc(cookie
, type
, tag_len
, M_NOWAIT
);
577 /* XXX may be free the mbuf if tag allocation failed? */
580 /* copy tag data to its place */
581 memcpy((void *)(tag
+ 1),
582 dhip
->out_tag_data
, tag_len
);
584 m_tag_prepend(m
, tag
);
588 NG_FWD_ITEM_HOOK(error
, item
, dest
);
593 * Shutdown processing.
596 ng_tag_shutdown(node_p node
)
603 * Hook disconnection.
605 * We must check all hooks, since they may reference this one.
608 ng_tag_disconnect(hook_p hook
)
610 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
611 node_p node
= NG_HOOK_NODE(hook
);
614 KASSERT(hip
!= NULL
, ("%s: null info", __func__
));
616 LIST_FOREACH(hook2
, &node
->nd_hooks
, hk_hooks
) {
617 hinfo_p priv
= NG_HOOK_PRIVATE(hook2
);
619 if (priv
->hi_match
== hook
)
620 priv
->hi_match
= NULL
;
621 if (priv
->hi_nonmatch
== hook
)
622 priv
->hi_nonmatch
= NULL
;
625 free(hip
->in
, M_NETGRAPH_TAG
);
626 free(hip
->out
, M_NETGRAPH_TAG
);
627 free(hip
, M_NETGRAPH_TAG
);
628 NG_HOOK_SET_PRIVATE(hook
, NULL
); /* for good measure */
629 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook
)) == 0) &&
630 (NG_NODE_IS_VALID(NG_HOOK_NODE(hook
)))) {
631 ng_rmnode_self(NG_HOOK_NODE(hook
));
636 /************************************************************************
638 ************************************************************************/
641 * Set the IN tag values associated with a hook.
644 ng_tag_setdata_in(hook_p hook
, const struct ng_tag_hookin
*hp0
)
646 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
647 struct ng_tag_hookin
*hp
;
650 /* Make a copy of the tag values and data. */
651 size
= NG_TAG_HOOKIN_SIZE(hp0
->tag_len
);
652 hp
= malloc(size
, M_NETGRAPH_TAG
, M_WAITOK
);
653 /* M_WAITOK can't return NULL. */
654 bcopy(hp0
, hp
, size
);
656 /* Free previous tag, if any, and assign new one. */
658 free(hip
->in
, M_NETGRAPH_TAG
);
662 * Resolve hook names to pointers.
664 * As ng_findhook() is expensive operation to do it on every packet
665 * after tag matching check, we do it here and use resolved pointers
668 * XXX The drawback is that user can configure a hook to use
669 * ifMatch/ifNotMatch hooks that do not yet exist and will be added
670 * by user later, so that resolved pointers will be NULL even
671 * if the hook already exists, causing node to drop packets and
672 * user to report bugs. We could do check for this situation on
673 * every hook creation with pointers correction, but that involves
674 * re-resolving for all pointers in all hooks, up to O(n^2) operations,
675 * so we better document this in man page for user not to do
676 * configuration before creating all hooks.
678 hip
->hi_match
= ng_findhook(NG_HOOK_NODE(hook
), hip
->in
->ifMatch
);
679 hip
->hi_nonmatch
= ng_findhook(NG_HOOK_NODE(hook
), hip
->in
->ifNotMatch
);
681 /* Fill internal values from API structures. */
682 hip
->in_tag_cookie
= hip
->in
->tag_cookie
;
683 hip
->in_tag_id
= hip
->in
->tag_id
;
684 hip
->in_tag_len
= hip
->in
->tag_len
;
685 hip
->strip
= hip
->in
->strip
;
686 hip
->in_tag_data
= (void*)(hip
->in
->tag_data
);
691 * Set the OUT tag values associated with a hook.
694 ng_tag_setdata_out(hook_p hook
, const struct ng_tag_hookout
*hp0
)
696 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
697 struct ng_tag_hookout
*hp
;
700 /* Make a copy of the tag values and data. */
701 size
= NG_TAG_HOOKOUT_SIZE(hp0
->tag_len
);
702 hp
= malloc(size
, M_NETGRAPH_TAG
, M_WAITOK
);
703 /* M_WAITOK can't return NULL. */
704 bcopy(hp0
, hp
, size
);
706 /* Free previous tag, if any, and assign new one. */
707 if (hip
->out
!= NULL
)
708 free(hip
->out
, M_NETGRAPH_TAG
);
711 /* Fill internal values from API structures. */
712 hip
->out_tag_cookie
= hip
->out
->tag_cookie
;
713 hip
->out_tag_id
= hip
->out
->tag_id
;
714 hip
->out_tag_len
= hip
->out
->tag_len
;
715 hip
->out_tag_data
= (void*)(hip
->out
->tag_data
);