2 * Copyright (c) 2006 Vadim Goncharov <vadimnuclight@tpu.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * Portions Copyright (c) 1999 Whistle Communications, Inc.
28 * (ng_bpf by Archie Cobbs <archie@freebsd.org>)
30 * $FreeBSD: src/sys/netgraph/ng_tag.c,v 1.1 2006/06/27 12:45:28 glebius Exp $
31 * $DragonFly: src/sys/netgraph7/ng_tag.c,v 1.2 2008/06/26 23:05:35 dillon Exp $
35 * TAG NETGRAPH NODE TYPE
37 * This node type accepts an arbitrary number of hooks. Each hook can be
38 * configured for an mbuf_tags(9) definition and two hook names: a hook
39 * for matched packets, and a hook for packets, that didn't match. Incoming
40 * packets are examined for configured tag, matched packets are delivered
41 * out via first hook, and not matched out via second. If corresponding hook
42 * is not configured, packets are dropped.
44 * A hook can also have an outgoing tag definition configured, so that
45 * all packets leaving the hook will be unconditionally appended with newly
48 * Both hooks can be set to null tag definitions (that is, with zeroed
49 * fields), so that packet tags are unmodified on output or all packets
50 * are unconditionally forwarded to non-matching hook on input. There is
51 * also a possibility to replace tags by specifying strip flag on input
52 * and replacing tag on corresponding output tag (or simply remove tag if
53 * no tag specified on output).
55 * If compiled with NG_TAG_DEBUG, each hook also keeps statistics about
56 * how many packets have matched, etc.
59 #include <sys/param.h>
60 #include <sys/systm.h>
61 #include <sys/errno.h>
62 #include <sys/kernel.h>
63 #include <sys/malloc.h>
65 #include <sys/stddef.h>
67 #include "ng_message.h"
72 #ifdef NG_SEPARATE_MALLOC
73 MALLOC_DEFINE(M_NETGRAPH_TAG
, "netgraph_tag", "netgraph tag node ");
75 #define M_NETGRAPH_TAG M_NETGRAPH
78 #define ERROUT(x) do { error = (x); goto done; } while (0)
81 * Per hook private info.
83 * We've separated API and ABI here, to make easier changes in this node,
84 * if needed. If you want to change representation, please do not break API.
85 * We still keep API structures in memory to simplify access to them for
86 * GET* messages, but most of data is accessed in internal representation
87 * only. The reason for this is to speed things up - if data will be
88 * accessed from API structures, there would be double pointer dereferencing
89 * in the code, which almost necessarily leads to CPU cache misses and
92 * We also do another optimization by using resolved pointers to
93 * destination hooks instead of expensive ng_findhook().
95 struct ng_tag_hookinfo
{
96 hook_p hi_match
; /* matching hook pointer */
97 hook_p hi_nonmatch
; /* non-matching hook pointer */
98 uint32_t in_tag_cookie
;
99 uint32_t out_tag_cookie
;
103 uint16_t out_tag_len
;
107 struct ng_tag_hookin
*in
;
108 struct ng_tag_hookout
*out
;
110 struct ng_tag_hookstat stats
;
113 typedef struct ng_tag_hookinfo
*hinfo_p
;
115 /* Netgraph methods. */
116 static ng_constructor_t ng_tag_constructor
;
117 static ng_rcvmsg_t ng_tag_rcvmsg
;
118 static ng_shutdown_t ng_tag_shutdown
;
119 static ng_newhook_t ng_tag_newhook
;
120 static ng_rcvdata_t ng_tag_rcvdata
;
121 static ng_disconnect_t ng_tag_disconnect
;
123 /* Internal helper functions. */
124 static int ng_tag_setdata_in(hook_p hook
, const struct ng_tag_hookin
*hp
);
125 static int ng_tag_setdata_out(hook_p hook
, const struct ng_tag_hookout
*hp
);
127 /* Parse types for the field 'tag_data' in structs ng_tag_hookin and out. */
129 ng_tag_hookinary_getLength(const struct ng_parse_type
*type
,
130 const u_char
*start
, const u_char
*buf
)
132 const struct ng_tag_hookin
*hp
;
134 hp
= (const struct ng_tag_hookin
*)
135 (buf
- offsetof(struct ng_tag_hookin
, tag_data
));
136 return (hp
->tag_len
);
140 ng_tag_hookoutary_getLength(const struct ng_parse_type
*type
,
141 const u_char
*start
, const u_char
*buf
)
143 const struct ng_tag_hookout
*hp
;
145 hp
= (const struct ng_tag_hookout
*)
146 (buf
- offsetof(struct ng_tag_hookout
, tag_data
));
147 return (hp
->tag_len
);
150 static const struct ng_parse_type ng_tag_hookinary_type
= {
151 &ng_parse_bytearray_type
,
152 &ng_tag_hookinary_getLength
155 static const struct ng_parse_type ng_tag_hookoutary_type
= {
156 &ng_parse_bytearray_type
,
157 &ng_tag_hookoutary_getLength
160 /* Parse type for struct ng_tag_hookin. */
161 static const struct ng_parse_struct_field ng_tag_hookin_type_fields
[]
162 = NG_TAG_HOOKIN_TYPE_INFO(&ng_tag_hookinary_type
);
163 static const struct ng_parse_type ng_tag_hookin_type
= {
164 &ng_parse_struct_type
,
165 &ng_tag_hookin_type_fields
168 /* Parse type for struct ng_tag_hookout. */
169 static const struct ng_parse_struct_field ng_tag_hookout_type_fields
[]
170 = NG_TAG_HOOKOUT_TYPE_INFO(&ng_tag_hookoutary_type
);
171 static const struct ng_parse_type ng_tag_hookout_type
= {
172 &ng_parse_struct_type
,
173 &ng_tag_hookout_type_fields
177 /* Parse type for struct ng_tag_hookstat. */
178 static const struct ng_parse_struct_field ng_tag_hookstat_type_fields
[]
179 = NG_TAG_HOOKSTAT_TYPE_INFO
;
180 static const struct ng_parse_type ng_tag_hookstat_type
= {
181 &ng_parse_struct_type
,
182 &ng_tag_hookstat_type_fields
186 /* List of commands and how to convert arguments to/from ASCII. */
187 static const struct ng_cmdlist ng_tag_cmdlist
[] = {
199 &ng_parse_hookbuf_type
,
206 &ng_tag_hookout_type
,
213 &ng_parse_hookbuf_type
,
221 &ng_parse_hookbuf_type
,
222 &ng_tag_hookstat_type
228 &ng_parse_hookbuf_type
,
233 NGM_TAG_GETCLR_STATS
,
235 &ng_parse_hookbuf_type
,
236 &ng_tag_hookstat_type
242 /* Netgraph type descriptor. */
243 static struct ng_type typestruct
= {
244 .version
= NG_ABI_VERSION
,
245 .name
= NG_TAG_NODE_TYPE
,
246 .constructor
= ng_tag_constructor
,
247 .rcvmsg
= ng_tag_rcvmsg
,
248 .shutdown
= ng_tag_shutdown
,
249 .newhook
= ng_tag_newhook
,
250 .rcvdata
= ng_tag_rcvdata
,
251 .disconnect
= ng_tag_disconnect
,
252 .cmdlist
= ng_tag_cmdlist
,
254 NETGRAPH_INIT(tag
, &typestruct
);
257 * This are default API structures (initialized to zeroes) which are
258 * returned in response to GET* messages when no configuration was made.
259 * One could ask why to have this structures at all when we have
260 * ng_tag_hookinfo initialized to zero and don't need in and out structures
261 * at all to operate. Unfortunatelly, we have to return thisHook field
262 * in response to messages so the fastest and simpliest way is to have
263 * this default structures and initialize thisHook once at hook creation
264 * rather than to do it on every response.
267 /* Default tag values for a hook that matches nothing. */
268 static const struct ng_tag_hookin ng_tag_default_in
= {
269 { '\0' }, /* to be filled in at hook creation time */
278 /* Default tag values for a hook that adds nothing */
279 static const struct ng_tag_hookout ng_tag_default_out
= {
280 { '\0' }, /* to be filled in at hook creation time */
289 * We don't keep any per-node private data - we do it on per-hook basis.
292 ng_tag_constructor(node_p node
)
301 ng_tag_newhook(node_p node
, hook_p hook
, const char *name
)
306 /* Create hook private structure. */
307 hip
= kmalloc(sizeof(*hip
), M_NETGRAPH_TAG
, M_WAITOK
| M_ZERO
);
308 /* M_WAITOK can't return NULL. */
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 kfree(hip
, M_NETGRAPH_TAG
);
324 /* Attach the default OUT data. */
325 if ((error
= ng_tag_setdata_out(hook
, &ng_tag_default_out
)) != 0) {
326 kfree(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
!=
366 NG_TAG_HOOKIN_SIZE(hp
->tag_len
))
370 if ((hook
= ng_findhook(node
, hp
->thisHook
)) == NULL
)
373 /* Set new tag values. */
374 if ((error
= ng_tag_setdata_in(hook
, hp
)) != 0)
379 case NGM_TAG_SET_HOOKOUT
:
381 struct ng_tag_hookout
*const
382 hp
= (struct ng_tag_hookout
*)msg
->data
;
386 if (msg
->header
.arglen
< sizeof(*hp
)
387 || msg
->header
.arglen
!=
388 NG_TAG_HOOKOUT_SIZE(hp
->tag_len
))
392 if ((hook
= ng_findhook(node
, hp
->thisHook
)) == NULL
)
395 /* Set new tag values. */
396 if ((error
= ng_tag_setdata_out(hook
, hp
)) != 0)
401 case NGM_TAG_GET_HOOKIN
:
403 struct ng_tag_hookin
*hp
;
407 if (msg
->header
.arglen
== 0)
409 msg
->data
[msg
->header
.arglen
- 1] = '\0';
412 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
415 /* Build response. */
416 hp
= ((hinfo_p
)NG_HOOK_PRIVATE(hook
))->in
;
417 NG_MKRESPONSE(resp
, msg
,
418 NG_TAG_HOOKIN_SIZE(hp
->tag_len
), M_WAITOK
);
419 /* M_WAITOK can't return NULL. */
420 bcopy(hp
, resp
->data
,
421 NG_TAG_HOOKIN_SIZE(hp
->tag_len
));
425 case NGM_TAG_GET_HOOKOUT
:
427 struct ng_tag_hookout
*hp
;
431 if (msg
->header
.arglen
== 0)
433 msg
->data
[msg
->header
.arglen
- 1] = '\0';
436 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
439 /* Build response. */
440 hp
= ((hinfo_p
)NG_HOOK_PRIVATE(hook
))->out
;
441 NG_MKRESPONSE(resp
, msg
,
442 NG_TAG_HOOKOUT_SIZE(hp
->tag_len
), M_WAITOK
);
443 /* M_WAITOK can't return NULL. */
444 bcopy(hp
, resp
->data
,
445 NG_TAG_HOOKOUT_SIZE(hp
->tag_len
));
450 case NGM_TAG_GET_STATS
:
451 case NGM_TAG_CLR_STATS
:
452 case NGM_TAG_GETCLR_STATS
:
454 struct ng_tag_hookstat
*stats
;
458 if (msg
->header
.arglen
== 0)
460 msg
->data
[msg
->header
.arglen
- 1] = '\0';
463 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
465 stats
= &((hinfo_p
)NG_HOOK_PRIVATE(hook
))->stats
;
467 /* Build response (if desired). */
468 if (msg
->header
.cmd
!= NGM_TAG_CLR_STATS
) {
470 msg
, sizeof(*stats
), M_WAITOK
);
471 /* M_WAITOK can't return NULL. */
472 bcopy(stats
, resp
->data
, sizeof(*stats
));
475 /* Clear stats (if desired). */
476 if (msg
->header
.cmd
!= NGM_TAG_GET_STATS
)
477 bzero(stats
, sizeof(*stats
));
480 #endif /* NG_TAG_DEBUG */
492 NG_RESPOND_MSG(error
, node
, item
, resp
);
498 * Receive data on a hook.
500 * Apply the filter, and then drop or forward packet as appropriate.
503 ng_tag_rcvdata(hook_p hook
, item_p item
)
506 struct m_tag
*tag
= NULL
;
507 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
508 uint16_t type
, tag_len
;
513 int found
= 0, error
= 0;
515 m
= NGI_M(item
); /* 'item' still owns it.. we are peeking */
516 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
, MB_DONTWAIT
);
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 kfree(hip
->in
, M_NETGRAPH_TAG
);
626 kfree(hip
->out
, M_NETGRAPH_TAG
);
627 kfree(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
= kmalloc(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 kfree(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
= kmalloc(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 kfree(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
);