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 $
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 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 simpliest 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 MALLOC(hip
, hinfo_p
, sizeof(*hip
), M_NETGRAPH_TAG
, M_WAITOK
| M_ZERO
);
307 /* M_WAITOK can't return NULL. */
308 NG_HOOK_SET_PRIVATE(hook
, hip
);
311 * After M_ZERO both in and out hook pointers are set to NULL,
312 * as well as all members and pointers to in and out API
313 * structures, so we need to set explicitly only thisHook field
314 * in that structures (after allocating them, of course).
317 /* Attach the default IN data. */
318 if ((error
= ng_tag_setdata_in(hook
, &ng_tag_default_in
)) != 0) {
319 FREE(hip
, M_NETGRAPH_TAG
);
323 /* Attach the default OUT data. */
324 if ((error
= ng_tag_setdata_out(hook
, &ng_tag_default_out
)) != 0) {
325 FREE(hip
, M_NETGRAPH_TAG
);
330 * Set hook name. This is done only once at hook creation time
331 * since hook name can't change, rather than to do it on every
332 * response to messages requesting API structures with data who
335 strncpy(hip
->in
->thisHook
, name
, sizeof(hip
->in
->thisHook
) - 1);
336 hip
->in
->thisHook
[sizeof(hip
->in
->thisHook
) - 1] = '\0';
337 strncpy(hip
->out
->thisHook
, name
, sizeof(hip
->out
->thisHook
) - 1);
338 hip
->out
->thisHook
[sizeof(hip
->out
->thisHook
) - 1] = '\0';
343 * Receive a control message.
346 ng_tag_rcvmsg(node_p node
, item_p item
, hook_p lasthook
)
349 struct ng_mesg
*resp
= NULL
;
352 NGI_GET_MSG(item
, msg
);
353 switch (msg
->header
.typecookie
) {
355 switch (msg
->header
.cmd
) {
356 case NGM_TAG_SET_HOOKIN
:
358 struct ng_tag_hookin
*const
359 hp
= (struct ng_tag_hookin
*)msg
->data
;
363 if (msg
->header
.arglen
< sizeof(*hp
)
364 || msg
->header
.arglen
!=
365 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
!=
387 NG_TAG_HOOKOUT_SIZE(hp
->tag_len
))
391 if ((hook
= ng_findhook(node
, hp
->thisHook
)) == NULL
)
394 /* Set new tag values. */
395 if ((error
= ng_tag_setdata_out(hook
, hp
)) != 0)
400 case NGM_TAG_GET_HOOKIN
:
402 struct ng_tag_hookin
*hp
;
406 if (msg
->header
.arglen
== 0)
408 msg
->data
[msg
->header
.arglen
- 1] = '\0';
411 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
414 /* Build response. */
415 hp
= ((hinfo_p
)NG_HOOK_PRIVATE(hook
))->in
;
416 NG_MKRESPONSE(resp
, msg
,
417 NG_TAG_HOOKIN_SIZE(hp
->tag_len
), M_WAITOK
);
418 /* M_WAITOK can't return NULL. */
419 bcopy(hp
, resp
->data
,
420 NG_TAG_HOOKIN_SIZE(hp
->tag_len
));
424 case NGM_TAG_GET_HOOKOUT
:
426 struct ng_tag_hookout
*hp
;
430 if (msg
->header
.arglen
== 0)
432 msg
->data
[msg
->header
.arglen
- 1] = '\0';
435 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
438 /* Build response. */
439 hp
= ((hinfo_p
)NG_HOOK_PRIVATE(hook
))->out
;
440 NG_MKRESPONSE(resp
, msg
,
441 NG_TAG_HOOKOUT_SIZE(hp
->tag_len
), M_WAITOK
);
442 /* M_WAITOK can't return NULL. */
443 bcopy(hp
, resp
->data
,
444 NG_TAG_HOOKOUT_SIZE(hp
->tag_len
));
449 case NGM_TAG_GET_STATS
:
450 case NGM_TAG_CLR_STATS
:
451 case NGM_TAG_GETCLR_STATS
:
453 struct ng_tag_hookstat
*stats
;
457 if (msg
->header
.arglen
== 0)
459 msg
->data
[msg
->header
.arglen
- 1] = '\0';
462 if ((hook
= ng_findhook(node
, msg
->data
)) == NULL
)
464 stats
= &((hinfo_p
)NG_HOOK_PRIVATE(hook
))->stats
;
466 /* Build response (if desired). */
467 if (msg
->header
.cmd
!= NGM_TAG_CLR_STATS
) {
469 msg
, sizeof(*stats
), M_WAITOK
);
470 /* M_WAITOK can't return NULL. */
471 bcopy(stats
, resp
->data
, sizeof(*stats
));
474 /* Clear stats (if desired). */
475 if (msg
->header
.cmd
!= NGM_TAG_GET_STATS
)
476 bzero(stats
, sizeof(*stats
));
479 #endif /* NG_TAG_DEBUG */
491 NG_RESPOND_MSG(error
, node
, item
, resp
);
497 * Receive data on a hook.
499 * Apply the filter, and then drop or forward packet as appropriate.
502 ng_tag_rcvdata(hook_p hook
, item_p item
)
505 struct m_tag
*tag
= NULL
;
506 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
507 uint16_t type
, tag_len
;
512 int found
= 0, error
= 0;
514 m
= NGI_M(item
); /* 'item' still owns it.. we are peeking */
515 totlen
= m
->m_pkthdr
.len
;
518 hip
->stats
.recvFrames
++;
519 hip
->stats
.recvOctets
+= totlen
;
522 /* Looking up incoming tag. */
523 cookie
= hip
->in_tag_cookie
;
524 type
= hip
->in_tag_id
;
525 tag_len
= hip
->in_tag_len
;
528 * We treat case of all zeroes specially (that is, cookie and
529 * type are equal to zero), as we assume that such tag
530 * can never occur in the wild. So we don't waste time trying
531 * to find such tag (for example, these are zeroes after hook
532 * creation in default structures).
534 if ((cookie
!= 0) || (type
!= 0)) {
535 tag
= m_tag_locate(m
, cookie
, type
, NULL
);
536 while (tag
!= NULL
) {
537 if (memcmp((void *)(tag
+ 1),
538 hip
->in_tag_data
, tag_len
) == 0) {
542 tag
= m_tag_locate(m
, cookie
, type
, tag
);
546 /* See if we got a match and find destination hook. */
549 hip
->stats
.recvMatchFrames
++;
550 hip
->stats
.recvMatchOctets
+= totlen
;
553 m_tag_delete(m
, tag
);
554 dest
= hip
->hi_match
;
556 dest
= hip
->hi_nonmatch
;
562 /* Deliver frame out destination hook. */
563 dhip
= NG_HOOK_PRIVATE(dest
);
566 dhip
->stats
.xmitOctets
+= totlen
;
567 dhip
->stats
.xmitFrames
++;
570 cookie
= dhip
->out_tag_cookie
;
571 type
= dhip
->out_tag_id
;
572 tag_len
= dhip
->out_tag_len
;
574 if ((cookie
!= 0) || (type
!= 0)) {
575 tag
= m_tag_alloc(cookie
, type
, tag_len
, M_NOWAIT
);
576 /* XXX may be free the mbuf if tag allocation failed? */
579 /* copy tag data to its place */
580 memcpy((void *)(tag
+ 1),
581 dhip
->out_tag_data
, tag_len
);
583 m_tag_prepend(m
, tag
);
587 NG_FWD_ITEM_HOOK(error
, item
, dest
);
592 * Shutdown processing.
595 ng_tag_shutdown(node_p node
)
602 * Hook disconnection.
604 * We must check all hooks, since they may reference this one.
607 ng_tag_disconnect(hook_p hook
)
609 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
610 node_p node
= NG_HOOK_NODE(hook
);
613 KASSERT(hip
!= NULL
, ("%s: null info", __func__
));
615 LIST_FOREACH(hook2
, &node
->nd_hooks
, hk_hooks
) {
616 hinfo_p priv
= NG_HOOK_PRIVATE(hook2
);
618 if (priv
->hi_match
== hook
)
619 priv
->hi_match
= NULL
;
620 if (priv
->hi_nonmatch
== hook
)
621 priv
->hi_nonmatch
= NULL
;
624 FREE(hip
->in
, M_NETGRAPH_TAG
);
625 FREE(hip
->out
, M_NETGRAPH_TAG
);
626 FREE(hip
, M_NETGRAPH_TAG
);
627 NG_HOOK_SET_PRIVATE(hook
, NULL
); /* for good measure */
628 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook
)) == 0) &&
629 (NG_NODE_IS_VALID(NG_HOOK_NODE(hook
)))) {
630 ng_rmnode_self(NG_HOOK_NODE(hook
));
635 /************************************************************************
637 ************************************************************************/
640 * Set the IN tag values associated with a hook.
643 ng_tag_setdata_in(hook_p hook
, const struct ng_tag_hookin
*hp0
)
645 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
646 struct ng_tag_hookin
*hp
;
649 /* Make a copy of the tag values and data. */
650 size
= NG_TAG_HOOKIN_SIZE(hp0
->tag_len
);
651 MALLOC(hp
, struct ng_tag_hookin
*, size
, M_NETGRAPH_TAG
, M_WAITOK
);
652 /* M_WAITOK can't return NULL. */
653 bcopy(hp0
, hp
, size
);
655 /* Free previous tag, if any, and assign new one. */
657 FREE(hip
->in
, M_NETGRAPH_TAG
);
661 * Resolve hook names to pointers.
663 * As ng_findhook() is expensive operation to do it on every packet
664 * after tag matching check, we do it here and use resolved pointers
667 * XXX The drawback is that user can configure a hook to use
668 * ifMatch/ifNotMatch hooks that do not yet exist and will be added
669 * by user later, so that resolved pointers will be NULL even
670 * if the hook already exists, causing node to drop packets and
671 * user to report bugs. We could do check for this situation on
672 * every hook creation with pointers correction, but that involves
673 * re-resolving for all pointers in all hooks, up to O(n^2) operations,
674 * so we better document this in man page for user not to do
675 * configuration before creating all hooks.
677 hip
->hi_match
= ng_findhook(NG_HOOK_NODE(hook
), hip
->in
->ifMatch
);
678 hip
->hi_nonmatch
= ng_findhook(NG_HOOK_NODE(hook
), hip
->in
->ifNotMatch
);
680 /* Fill internal values from API structures. */
681 hip
->in_tag_cookie
= hip
->in
->tag_cookie
;
682 hip
->in_tag_id
= hip
->in
->tag_id
;
683 hip
->in_tag_len
= hip
->in
->tag_len
;
684 hip
->strip
= hip
->in
->strip
;
685 hip
->in_tag_data
= (void*)(hip
->in
->tag_data
);
690 * Set the OUT tag values associated with a hook.
693 ng_tag_setdata_out(hook_p hook
, const struct ng_tag_hookout
*hp0
)
695 const hinfo_p hip
= NG_HOOK_PRIVATE(hook
);
696 struct ng_tag_hookout
*hp
;
699 /* Make a copy of the tag values and data. */
700 size
= NG_TAG_HOOKOUT_SIZE(hp0
->tag_len
);
701 MALLOC(hp
, struct ng_tag_hookout
*, size
, M_NETGRAPH_TAG
, M_WAITOK
);
702 /* M_WAITOK can't return NULL. */
703 bcopy(hp0
, hp
, size
);
705 /* Free previous tag, if any, and assign new one. */
706 if (hip
->out
!= NULL
)
707 FREE(hip
->out
, M_NETGRAPH_TAG
);
710 /* Fill internal values from API structures. */
711 hip
->out_tag_cookie
= hip
->out
->tag_cookie
;
712 hip
->out_tag_id
= hip
->out
->tag_id
;
713 hip
->out_tag_len
= hip
->out
->tag_len
;
714 hip
->out_tag_data
= (void*)(hip
->out
->tag_data
);