1159979Sglebius/*-
2159979Sglebius * Copyright (c) 2006 Vadim Goncharov <vadimnuclight@tpu.ru>
3159979Sglebius * All rights reserved.
4159979Sglebius *
5159979Sglebius * Redistribution and use in source and binary forms, with or without
6159979Sglebius * modification, are permitted provided that the following conditions
7159979Sglebius * are met:
8159979Sglebius * 1. Redistributions of source code must retain the above copyright
9159979Sglebius *    notice unmodified, this list of conditions, and the following
10159979Sglebius *    disclaimer.
11159979Sglebius * 2. Redistributions in binary form must reproduce the above copyright
12159979Sglebius *    notice, this list of conditions and the following disclaimer in the
13159979Sglebius *    documentation and/or other materials provided with the distribution.
14159979Sglebius *
15159979Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16159979Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17159979Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18159979Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19159979Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20159979Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21159979Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22159979Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23159979Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24159979Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25159979Sglebius * SUCH DAMAGE.
26159979Sglebius *
27159979Sglebius * Portions Copyright (c) 1999 Whistle Communications, Inc.
28159979Sglebius * (ng_bpf by Archie Cobbs <archie@freebsd.org>)
29159979Sglebius *
30159979Sglebius * $FreeBSD$
31159979Sglebius */
32159979Sglebius
33159979Sglebius/*
34159979Sglebius * TAG NETGRAPH NODE TYPE
35159979Sglebius *
36159979Sglebius * This node type accepts an arbitrary number of hooks. Each hook can be
37159979Sglebius * configured for an mbuf_tags(9) definition and two hook names: a hook
38159979Sglebius * for matched packets, and a hook for packets, that didn't match. Incoming
39159979Sglebius * packets are examined for configured tag, matched packets are delivered
40159979Sglebius * out via first hook, and not matched out via second. If corresponding hook
41159979Sglebius * is not configured, packets are dropped.
42159979Sglebius *
43159979Sglebius * A hook can also have an outgoing tag definition configured, so that
44159979Sglebius * all packets leaving the hook will be unconditionally appended with newly
45159979Sglebius * allocated tag.
46159979Sglebius *
47159979Sglebius * Both hooks can be set to null tag definitions (that is, with zeroed
48159979Sglebius * fields), so that packet tags are unmodified on output or all packets
49159979Sglebius * are unconditionally forwarded to non-matching hook on input.  There is
50159979Sglebius * also a possibility to replace tags by specifying strip flag on input
51159979Sglebius * and replacing tag on corresponding output tag (or simply remove tag if
52159979Sglebius * no tag specified on output).
53159979Sglebius *
54159979Sglebius * If compiled with NG_TAG_DEBUG, each hook also keeps statistics about
55159979Sglebius * how many packets have matched, etc.
56159979Sglebius */
57159979Sglebius
58159979Sglebius#include <sys/param.h>
59159979Sglebius#include <sys/systm.h>
60159979Sglebius#include <sys/errno.h>
61159979Sglebius#include <sys/kernel.h>
62159979Sglebius#include <sys/malloc.h>
63159979Sglebius#include <sys/mbuf.h>
64159979Sglebius#include <sys/stddef.h>
65159979Sglebius
66159979Sglebius#include <netgraph/ng_message.h>
67159979Sglebius#include <netgraph/netgraph.h>
68159979Sglebius#include <netgraph/ng_parse.h>
69159979Sglebius#include <netgraph/ng_tag.h>
70159979Sglebius
71159979Sglebius#ifdef NG_SEPARATE_MALLOC
72249132Smavstatic MALLOC_DEFINE(M_NETGRAPH_TAG, "netgraph_tag", "netgraph tag node");
73159979Sglebius#else
74159979Sglebius#define M_NETGRAPH_TAG M_NETGRAPH
75159979Sglebius#endif
76159979Sglebius
77159979Sglebius#define ERROUT(x)	do { error = (x); goto done; } while (0)
78159979Sglebius
79159979Sglebius/*
80159979Sglebius * Per hook private info.
81159979Sglebius *
82159979Sglebius * We've separated API and ABI here, to make easier changes in this node,
83159979Sglebius * if needed. If you want to change representation, please do not break API.
84159979Sglebius * We still keep API structures in memory to simplify access to them for
85159979Sglebius * GET* messages, but most of data is accessed in internal representation
86159979Sglebius * only.  The reason for this is to speed things up - if data will be
87159979Sglebius * accessed from API structures, there would be double pointer dereferencing
88159979Sglebius * in the code, which almost necessarily leads to CPU cache misses and
89159979Sglebius * reloads.
90159979Sglebius *
91159979Sglebius * We also do another optimization by using resolved pointers to
92159979Sglebius * destination hooks instead of expensive ng_findhook().
93159979Sglebius */
94159979Sglebiusstruct ng_tag_hookinfo {
95159979Sglebius	hook_p			hi_match;	/* matching hook pointer */
96159979Sglebius	hook_p			hi_nonmatch;	/* non-matching hook pointer */
97159979Sglebius	uint32_t		in_tag_cookie;
98159979Sglebius	uint32_t		out_tag_cookie;
99159979Sglebius	uint16_t		in_tag_id;
100159979Sglebius	uint16_t		in_tag_len;
101159979Sglebius	uint16_t		out_tag_id;
102159979Sglebius	uint16_t		out_tag_len;
103159979Sglebius	uint8_t			strip;
104159979Sglebius	void			*in_tag_data;
105159979Sglebius	void			*out_tag_data;
106159979Sglebius	struct ng_tag_hookin	*in;
107159979Sglebius	struct ng_tag_hookout	*out;
108159979Sglebius#ifdef NG_TAG_DEBUG
109159979Sglebius	struct ng_tag_hookstat	stats;
110159979Sglebius#endif
111159979Sglebius};
112159979Sglebiustypedef struct ng_tag_hookinfo *hinfo_p;
113159979Sglebius
114159979Sglebius/* Netgraph methods. */
115159979Sglebiusstatic ng_constructor_t	ng_tag_constructor;
116159979Sglebiusstatic ng_rcvmsg_t	ng_tag_rcvmsg;
117159979Sglebiusstatic ng_shutdown_t	ng_tag_shutdown;
118159979Sglebiusstatic ng_newhook_t	ng_tag_newhook;
119159979Sglebiusstatic ng_rcvdata_t	ng_tag_rcvdata;
120159979Sglebiusstatic ng_disconnect_t	ng_tag_disconnect;
121159979Sglebius
122159979Sglebius/* Internal helper functions. */
123159979Sglebiusstatic int	ng_tag_setdata_in(hook_p hook, const struct ng_tag_hookin *hp);
124159979Sglebiusstatic int	ng_tag_setdata_out(hook_p hook, const struct ng_tag_hookout *hp);
125159979Sglebius
126159979Sglebius/* Parse types for the field 'tag_data' in structs ng_tag_hookin and out. */
127159979Sglebiusstatic int
128159979Sglebiusng_tag_hookinary_getLength(const struct ng_parse_type *type,
129159979Sglebius	const u_char *start, const u_char *buf)
130159979Sglebius{
131159979Sglebius	const struct ng_tag_hookin *hp;
132159979Sglebius
133159979Sglebius	hp = (const struct ng_tag_hookin *)
134159979Sglebius	    (buf - offsetof(struct ng_tag_hookin, tag_data));
135159979Sglebius	return (hp->tag_len);
136159979Sglebius}
137159979Sglebius
138159979Sglebiusstatic int
139159979Sglebiusng_tag_hookoutary_getLength(const struct ng_parse_type *type,
140159979Sglebius	const u_char *start, const u_char *buf)
141159979Sglebius{
142159979Sglebius	const struct ng_tag_hookout *hp;
143159979Sglebius
144159979Sglebius	hp = (const struct ng_tag_hookout *)
145159979Sglebius	    (buf - offsetof(struct ng_tag_hookout, tag_data));
146159979Sglebius	return (hp->tag_len);
147159979Sglebius}
148159979Sglebius
149159979Sglebiusstatic const struct ng_parse_type ng_tag_hookinary_type = {
150159979Sglebius	&ng_parse_bytearray_type,
151159979Sglebius	&ng_tag_hookinary_getLength
152159979Sglebius};
153159979Sglebius
154159979Sglebiusstatic const struct ng_parse_type ng_tag_hookoutary_type = {
155159979Sglebius	&ng_parse_bytearray_type,
156159979Sglebius	&ng_tag_hookoutary_getLength
157159979Sglebius};
158159979Sglebius
159159979Sglebius/* Parse type for struct ng_tag_hookin. */
160159979Sglebiusstatic const struct ng_parse_struct_field ng_tag_hookin_type_fields[]
161159979Sglebius	= NG_TAG_HOOKIN_TYPE_INFO(&ng_tag_hookinary_type);
162159979Sglebiusstatic const struct ng_parse_type ng_tag_hookin_type = {
163159979Sglebius	&ng_parse_struct_type,
164159979Sglebius	&ng_tag_hookin_type_fields
165159979Sglebius};
166159979Sglebius
167159979Sglebius/* Parse type for struct ng_tag_hookout. */
168159979Sglebiusstatic const struct ng_parse_struct_field ng_tag_hookout_type_fields[]
169159979Sglebius	= NG_TAG_HOOKOUT_TYPE_INFO(&ng_tag_hookoutary_type);
170159979Sglebiusstatic const struct ng_parse_type ng_tag_hookout_type = {
171159979Sglebius	&ng_parse_struct_type,
172159979Sglebius	&ng_tag_hookout_type_fields
173159979Sglebius};
174159979Sglebius
175159979Sglebius#ifdef NG_TAG_DEBUG
176159979Sglebius/* Parse type for struct ng_tag_hookstat. */
177159979Sglebiusstatic const struct ng_parse_struct_field ng_tag_hookstat_type_fields[]
178159979Sglebius	= NG_TAG_HOOKSTAT_TYPE_INFO;
179159979Sglebiusstatic const struct ng_parse_type ng_tag_hookstat_type = {
180159979Sglebius	&ng_parse_struct_type,
181159979Sglebius	&ng_tag_hookstat_type_fields
182159979Sglebius};
183159979Sglebius#endif
184159979Sglebius
185159979Sglebius/* List of commands and how to convert arguments to/from ASCII. */
186159979Sglebiusstatic const struct ng_cmdlist ng_tag_cmdlist[] = {
187159979Sglebius	{
188159979Sglebius	  NGM_TAG_COOKIE,
189159979Sglebius	  NGM_TAG_SET_HOOKIN,
190159979Sglebius	  "sethookin",
191159979Sglebius	  &ng_tag_hookin_type,
192159979Sglebius	  NULL
193159979Sglebius	},
194159979Sglebius	{
195159979Sglebius	  NGM_TAG_COOKIE,
196159979Sglebius	  NGM_TAG_GET_HOOKIN,
197159979Sglebius	  "gethookin",
198159979Sglebius	  &ng_parse_hookbuf_type,
199159979Sglebius	  &ng_tag_hookin_type
200159979Sglebius	},
201159979Sglebius	{
202159979Sglebius	  NGM_TAG_COOKIE,
203159979Sglebius	  NGM_TAG_SET_HOOKOUT,
204159979Sglebius	  "sethookout",
205159979Sglebius	  &ng_tag_hookout_type,
206159979Sglebius	  NULL
207159979Sglebius	},
208159979Sglebius	{
209159979Sglebius	  NGM_TAG_COOKIE,
210159979Sglebius	  NGM_TAG_GET_HOOKOUT,
211159979Sglebius	  "gethookout",
212159979Sglebius	  &ng_parse_hookbuf_type,
213159979Sglebius	  &ng_tag_hookout_type
214159979Sglebius	},
215159979Sglebius#ifdef NG_TAG_DEBUG
216159979Sglebius	{
217159979Sglebius	  NGM_TAG_COOKIE,
218159979Sglebius	  NGM_TAG_GET_STATS,
219159979Sglebius	  "getstats",
220159979Sglebius	  &ng_parse_hookbuf_type,
221159979Sglebius	  &ng_tag_hookstat_type
222159979Sglebius	},
223159979Sglebius	{
224159979Sglebius	  NGM_TAG_COOKIE,
225159979Sglebius	  NGM_TAG_CLR_STATS,
226159979Sglebius	  "clrstats",
227159979Sglebius	  &ng_parse_hookbuf_type,
228159979Sglebius	  NULL
229159979Sglebius	},
230159979Sglebius	{
231159979Sglebius	  NGM_TAG_COOKIE,
232159979Sglebius	  NGM_TAG_GETCLR_STATS,
233159979Sglebius	  "getclrstats",
234159979Sglebius	  &ng_parse_hookbuf_type,
235159979Sglebius	  &ng_tag_hookstat_type
236159979Sglebius	},
237159979Sglebius#endif
238159979Sglebius	{ 0 }
239159979Sglebius};
240159979Sglebius
241159979Sglebius/* Netgraph type descriptor. */
242159979Sglebiusstatic struct ng_type typestruct = {
243159979Sglebius	.version =	NG_ABI_VERSION,
244159979Sglebius	.name =		NG_TAG_NODE_TYPE,
245159979Sglebius	.constructor =	ng_tag_constructor,
246159979Sglebius	.rcvmsg =	ng_tag_rcvmsg,
247159979Sglebius	.shutdown =	ng_tag_shutdown,
248159979Sglebius	.newhook =	ng_tag_newhook,
249159979Sglebius	.rcvdata =	ng_tag_rcvdata,
250159979Sglebius	.disconnect =	ng_tag_disconnect,
251159979Sglebius	.cmdlist =	ng_tag_cmdlist,
252159979Sglebius};
253159979SglebiusNETGRAPH_INIT(tag, &typestruct);
254159979Sglebius
255159979Sglebius/*
256159979Sglebius * This are default API structures (initialized to zeroes) which are
257159979Sglebius * returned in response to GET* messages when no configuration was made.
258159979Sglebius * One could ask why to have this structures at all when we have
259159979Sglebius * ng_tag_hookinfo initialized to zero and don't need in and out structures
260159979Sglebius * at all to operate.  Unfortunatelly, we have to return thisHook field
261159979Sglebius * in response to messages so the fastest and simpliest way is to have
262159979Sglebius * this default structures and initialize thisHook once at hook creation
263159979Sglebius * rather than to do it on every response.
264159979Sglebius */
265159979Sglebius
266159979Sglebius/* Default tag values for a hook that matches nothing. */
267159979Sglebiusstatic const struct ng_tag_hookin ng_tag_default_in = {
268159979Sglebius	{ '\0' },		/* to be filled in at hook creation time */
269159979Sglebius	{ '\0' },
270159979Sglebius	{ '\0' },
271159979Sglebius	0,
272159979Sglebius	0,
273159979Sglebius	0,
274159979Sglebius	0
275159979Sglebius};
276159979Sglebius
277159979Sglebius/* Default tag values for a hook that adds nothing */
278159979Sglebiusstatic const struct ng_tag_hookout ng_tag_default_out = {
279159979Sglebius	{ '\0' },		/* to be filled in at hook creation time */
280159979Sglebius	0,
281159979Sglebius	0,
282159979Sglebius	0
283159979Sglebius};
284159979Sglebius
285159979Sglebius/*
286159979Sglebius * Node constructor.
287159979Sglebius *
288159979Sglebius * We don't keep any per-node private data - we do it on per-hook basis.
289159979Sglebius */
290159979Sglebiusstatic int
291159979Sglebiusng_tag_constructor(node_p node)
292159979Sglebius{
293159979Sglebius	return (0);
294159979Sglebius}
295159979Sglebius
296159979Sglebius/*
297159979Sglebius * Add a hook.
298159979Sglebius */
299159979Sglebiusstatic int
300159979Sglebiusng_tag_newhook(node_p node, hook_p hook, const char *name)
301159979Sglebius{
302159979Sglebius	hinfo_p hip;
303159979Sglebius	int error;
304159979Sglebius
305159979Sglebius	/* Create hook private structure. */
306184205Sdes	hip = malloc(sizeof(*hip), M_NETGRAPH_TAG, M_WAITOK | M_ZERO);
307159979Sglebius	/* M_WAITOK can't return NULL. */
308159979Sglebius	NG_HOOK_SET_PRIVATE(hook, hip);
309159979Sglebius
310159979Sglebius	/*
311159979Sglebius	 * After M_ZERO both in and out hook pointers are set to NULL,
312159979Sglebius	 * as well as all members and pointers to in and out API
313159979Sglebius	 * structures, so we need to set explicitly only thisHook field
314159979Sglebius	 * in that structures (after allocating them, of course).
315159979Sglebius	 */
316159979Sglebius
317159979Sglebius	/* Attach the default IN data. */
318159979Sglebius	if ((error = ng_tag_setdata_in(hook, &ng_tag_default_in)) != 0) {
319184205Sdes		free(hip, M_NETGRAPH_TAG);
320159979Sglebius		return (error);
321159979Sglebius	}
322159979Sglebius
323159979Sglebius	/* Attach the default OUT data. */
324159979Sglebius	if ((error = ng_tag_setdata_out(hook, &ng_tag_default_out)) != 0) {
325184205Sdes		free(hip, M_NETGRAPH_TAG);
326159979Sglebius		return (error);
327159979Sglebius	}
328159979Sglebius
329159979Sglebius	/*
330159979Sglebius	 * Set hook name.  This is done only once at hook creation time
331159979Sglebius	 * since hook name can't change, rather than to do it on every
332159979Sglebius	 * response to messages requesting API structures with data who
333159979Sglebius	 * we are etc.
334159979Sglebius	 */
335159979Sglebius	strncpy(hip->in->thisHook, name, sizeof(hip->in->thisHook) - 1);
336159979Sglebius	hip->in->thisHook[sizeof(hip->in->thisHook) - 1] = '\0';
337159979Sglebius	strncpy(hip->out->thisHook, name, sizeof(hip->out->thisHook) - 1);
338159979Sglebius	hip->out->thisHook[sizeof(hip->out->thisHook) - 1] = '\0';
339159979Sglebius	return (0);
340159979Sglebius}
341159979Sglebius
342159979Sglebius/*
343159979Sglebius * Receive a control message.
344159979Sglebius */
345159979Sglebiusstatic int
346159979Sglebiusng_tag_rcvmsg(node_p node, item_p item, hook_p lasthook)
347159979Sglebius{
348159979Sglebius	struct ng_mesg *msg;
349159979Sglebius	struct ng_mesg *resp = NULL;
350159979Sglebius	int error = 0;
351159979Sglebius
352159979Sglebius	NGI_GET_MSG(item, msg);
353159979Sglebius	switch (msg->header.typecookie) {
354159979Sglebius	case NGM_TAG_COOKIE:
355159979Sglebius		switch (msg->header.cmd) {
356159979Sglebius		case NGM_TAG_SET_HOOKIN:
357159979Sglebius		    {
358159979Sglebius			struct ng_tag_hookin *const
359159979Sglebius			    hp = (struct ng_tag_hookin *)msg->data;
360159979Sglebius			hook_p hook;
361159979Sglebius
362159979Sglebius			/* Sanity check. */
363159979Sglebius			if (msg->header.arglen < sizeof(*hp)
364159979Sglebius			    || msg->header.arglen !=
365159979Sglebius			    NG_TAG_HOOKIN_SIZE(hp->tag_len))
366159979Sglebius				ERROUT(EINVAL);
367159979Sglebius
368159979Sglebius			/* Find hook. */
369159979Sglebius			if ((hook = ng_findhook(node, hp->thisHook)) == NULL)
370159979Sglebius				ERROUT(ENOENT);
371159979Sglebius
372159979Sglebius			/* Set new tag values. */
373159979Sglebius			if ((error = ng_tag_setdata_in(hook, hp)) != 0)
374159979Sglebius				ERROUT(error);
375159979Sglebius			break;
376159979Sglebius		    }
377159979Sglebius
378159979Sglebius		case NGM_TAG_SET_HOOKOUT:
379159979Sglebius		    {
380159979Sglebius			struct ng_tag_hookout *const
381159979Sglebius			    hp = (struct ng_tag_hookout *)msg->data;
382159979Sglebius			hook_p hook;
383159979Sglebius
384159979Sglebius			/* Sanity check. */
385159979Sglebius			if (msg->header.arglen < sizeof(*hp)
386159979Sglebius			    || msg->header.arglen !=
387159979Sglebius			    NG_TAG_HOOKOUT_SIZE(hp->tag_len))
388159979Sglebius				ERROUT(EINVAL);
389159979Sglebius
390159979Sglebius			/* Find hook. */
391159979Sglebius			if ((hook = ng_findhook(node, hp->thisHook)) == NULL)
392159979Sglebius				ERROUT(ENOENT);
393159979Sglebius
394159979Sglebius			/* Set new tag values. */
395159979Sglebius			if ((error = ng_tag_setdata_out(hook, hp)) != 0)
396159979Sglebius				ERROUT(error);
397159979Sglebius			break;
398159979Sglebius		    }
399159979Sglebius
400159979Sglebius		case NGM_TAG_GET_HOOKIN:
401159979Sglebius		    {
402159979Sglebius			struct ng_tag_hookin *hp;
403159979Sglebius			hook_p hook;
404159979Sglebius
405159979Sglebius			/* Sanity check. */
406159979Sglebius			if (msg->header.arglen == 0)
407159979Sglebius				ERROUT(EINVAL);
408159979Sglebius			msg->data[msg->header.arglen - 1] = '\0';
409159979Sglebius
410159979Sglebius			/* Find hook. */
411159979Sglebius			if ((hook = ng_findhook(node, msg->data)) == NULL)
412159979Sglebius				ERROUT(ENOENT);
413159979Sglebius
414159979Sglebius			/* Build response. */
415159979Sglebius			hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->in;
416159979Sglebius			NG_MKRESPONSE(resp, msg,
417159979Sglebius			    NG_TAG_HOOKIN_SIZE(hp->tag_len), M_WAITOK);
418159979Sglebius			/* M_WAITOK can't return NULL. */
419159979Sglebius			bcopy(hp, resp->data,
420159979Sglebius			   NG_TAG_HOOKIN_SIZE(hp->tag_len));
421159979Sglebius			break;
422159979Sglebius		    }
423159979Sglebius
424159979Sglebius		case NGM_TAG_GET_HOOKOUT:
425159979Sglebius		    {
426159979Sglebius			struct ng_tag_hookout *hp;
427159979Sglebius			hook_p hook;
428159979Sglebius
429159979Sglebius			/* Sanity check. */
430159979Sglebius			if (msg->header.arglen == 0)
431159979Sglebius				ERROUT(EINVAL);
432159979Sglebius			msg->data[msg->header.arglen - 1] = '\0';
433159979Sglebius
434159979Sglebius			/* Find hook. */
435159979Sglebius			if ((hook = ng_findhook(node, msg->data)) == NULL)
436159979Sglebius				ERROUT(ENOENT);
437159979Sglebius
438159979Sglebius			/* Build response. */
439159979Sglebius			hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->out;
440159979Sglebius			NG_MKRESPONSE(resp, msg,
441159979Sglebius			    NG_TAG_HOOKOUT_SIZE(hp->tag_len), M_WAITOK);
442159979Sglebius			/* M_WAITOK can't return NULL. */
443159979Sglebius			bcopy(hp, resp->data,
444159979Sglebius			   NG_TAG_HOOKOUT_SIZE(hp->tag_len));
445159979Sglebius			break;
446159979Sglebius		    }
447159979Sglebius
448159979Sglebius#ifdef NG_TAG_DEBUG
449159979Sglebius		case NGM_TAG_GET_STATS:
450159979Sglebius		case NGM_TAG_CLR_STATS:
451159979Sglebius		case NGM_TAG_GETCLR_STATS:
452159979Sglebius		    {
453159979Sglebius			struct ng_tag_hookstat *stats;
454159979Sglebius			hook_p hook;
455159979Sglebius
456159979Sglebius			/* Sanity check. */
457159979Sglebius			if (msg->header.arglen == 0)
458159979Sglebius				ERROUT(EINVAL);
459159979Sglebius			msg->data[msg->header.arglen - 1] = '\0';
460159979Sglebius
461159979Sglebius			/* Find hook. */
462159979Sglebius			if ((hook = ng_findhook(node, msg->data)) == NULL)
463159979Sglebius				ERROUT(ENOENT);
464159979Sglebius			stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats;
465159979Sglebius
466159979Sglebius			/* Build response (if desired). */
467159979Sglebius			if (msg->header.cmd != NGM_TAG_CLR_STATS) {
468159979Sglebius				NG_MKRESPONSE(resp,
469159979Sglebius				    msg, sizeof(*stats), M_WAITOK);
470159979Sglebius				/* M_WAITOK can't return NULL. */
471159979Sglebius				bcopy(stats, resp->data, sizeof(*stats));
472159979Sglebius			}
473159979Sglebius
474159979Sglebius			/* Clear stats (if desired). */
475159979Sglebius			if (msg->header.cmd != NGM_TAG_GET_STATS)
476159979Sglebius				bzero(stats, sizeof(*stats));
477159979Sglebius			break;
478159979Sglebius		    }
479159979Sglebius#endif /* NG_TAG_DEBUG */
480159979Sglebius
481159979Sglebius		default:
482159979Sglebius			error = EINVAL;
483159979Sglebius			break;
484159979Sglebius		}
485159979Sglebius		break;
486159979Sglebius	default:
487159979Sglebius		error = EINVAL;
488159979Sglebius		break;
489159979Sglebius	}
490159979Sglebiusdone:
491159979Sglebius	NG_RESPOND_MSG(error, node, item, resp);
492159979Sglebius	NG_FREE_MSG(msg);
493159979Sglebius	return (error);
494159979Sglebius}
495159979Sglebius
496159979Sglebius/*
497159979Sglebius * Receive data on a hook.
498159979Sglebius *
499159979Sglebius * Apply the filter, and then drop or forward packet as appropriate.
500159979Sglebius */
501159979Sglebiusstatic int
502159979Sglebiusng_tag_rcvdata(hook_p hook, item_p item)
503159979Sglebius{
504159979Sglebius	struct mbuf *m;
505159979Sglebius	struct m_tag *tag = NULL;
506159979Sglebius	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
507159979Sglebius	uint16_t type, tag_len;
508159979Sglebius	uint32_t cookie;
509159979Sglebius	hinfo_p dhip;
510159979Sglebius	hook_p dest;
511159979Sglebius	int totlen;
512159979Sglebius	int found = 0, error = 0;
513159979Sglebius
514159979Sglebius	m = NGI_M(item);	/* 'item' still owns it.. we are peeking */
515159979Sglebius	totlen = m->m_pkthdr.len;
516159979Sglebius
517159979Sglebius#ifdef NG_TAG_DEBUG
518159979Sglebius	hip->stats.recvFrames++;
519159979Sglebius	hip->stats.recvOctets += totlen;
520159979Sglebius#endif
521159979Sglebius
522159979Sglebius	/* Looking up incoming tag. */
523159979Sglebius	cookie = hip->in_tag_cookie;
524159979Sglebius	type = hip->in_tag_id;
525159979Sglebius	tag_len = hip->in_tag_len;
526159979Sglebius
527159979Sglebius	/*
528159979Sglebius	 * We treat case of all zeroes specially (that is, cookie and
529159979Sglebius	 * type are equal to zero), as we assume that such tag
530159979Sglebius	 * can never occur in the wild.  So we don't waste time trying
531159979Sglebius	 * to find such tag (for example, these are zeroes after hook
532159979Sglebius	 * creation in default structures).
533159979Sglebius	 */
534159979Sglebius	if ((cookie != 0) || (type != 0)) {
535159979Sglebius		tag = m_tag_locate(m, cookie, type, NULL);
536159979Sglebius		while (tag != NULL) {
537159979Sglebius			if (memcmp((void *)(tag + 1),
538159979Sglebius			    hip->in_tag_data, tag_len) == 0) {
539159979Sglebius				found = 1;
540159979Sglebius				break;
541159979Sglebius			}
542159979Sglebius			tag = m_tag_locate(m, cookie, type, tag);
543159979Sglebius		}
544159979Sglebius	}
545159979Sglebius
546159979Sglebius	/* See if we got a match and find destination hook. */
547159979Sglebius	if (found) {
548159979Sglebius#ifdef NG_TAG_DEBUG
549159979Sglebius		hip->stats.recvMatchFrames++;
550159979Sglebius		hip->stats.recvMatchOctets += totlen;
551159979Sglebius#endif
552159979Sglebius		if (hip->strip)
553159979Sglebius			m_tag_delete(m, tag);
554159979Sglebius		dest = hip->hi_match;
555159979Sglebius	} else
556159979Sglebius		dest = hip->hi_nonmatch;
557159979Sglebius	if (dest == NULL) {
558159979Sglebius		NG_FREE_ITEM(item);
559159979Sglebius		return (0);
560159979Sglebius	}
561159979Sglebius
562159979Sglebius	/* Deliver frame out destination hook. */
563159979Sglebius	dhip = NG_HOOK_PRIVATE(dest);
564159979Sglebius
565159979Sglebius#ifdef NG_TAG_DEBUG
566159979Sglebius	dhip->stats.xmitOctets += totlen;
567159979Sglebius	dhip->stats.xmitFrames++;
568159979Sglebius#endif
569159979Sglebius
570159979Sglebius	cookie = dhip->out_tag_cookie;
571159979Sglebius	type = dhip->out_tag_id;
572159979Sglebius	tag_len = dhip->out_tag_len;
573159979Sglebius
574159979Sglebius	if ((cookie != 0) || (type != 0)) {
575159979Sglebius		tag = m_tag_alloc(cookie, type, tag_len, M_NOWAIT);
576159979Sglebius		/* XXX may be free the mbuf if tag allocation failed? */
577159979Sglebius		if (tag != NULL) {
578159979Sglebius			if (tag_len != 0) {
579159979Sglebius				/* copy tag data to its place */
580159979Sglebius				memcpy((void *)(tag + 1),
581159979Sglebius				    dhip->out_tag_data, tag_len);
582159979Sglebius			}
583159979Sglebius			m_tag_prepend(m, tag);
584159979Sglebius		}
585159979Sglebius	}
586159979Sglebius
587159979Sglebius	NG_FWD_ITEM_HOOK(error, item, dest);
588159979Sglebius	return (error);
589159979Sglebius}
590159979Sglebius
591159979Sglebius/*
592159979Sglebius * Shutdown processing.
593159979Sglebius */
594159979Sglebiusstatic int
595159979Sglebiusng_tag_shutdown(node_p node)
596159979Sglebius{
597159979Sglebius	NG_NODE_UNREF(node);
598159979Sglebius	return (0);
599159979Sglebius}
600159979Sglebius
601159979Sglebius/*
602159979Sglebius * Hook disconnection.
603159979Sglebius *
604159979Sglebius * We must check all hooks, since they may reference this one.
605159979Sglebius */
606159979Sglebiusstatic int
607159979Sglebiusng_tag_disconnect(hook_p hook)
608159979Sglebius{
609159979Sglebius	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
610159979Sglebius	node_p node = NG_HOOK_NODE(hook);
611159979Sglebius	hook_p hook2;
612159979Sglebius
613159979Sglebius	KASSERT(hip != NULL, ("%s: null info", __func__));
614159979Sglebius
615159979Sglebius	LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks) {
616159979Sglebius		hinfo_p priv = NG_HOOK_PRIVATE(hook2);
617159979Sglebius
618159979Sglebius		if (priv->hi_match == hook)
619159979Sglebius			priv->hi_match = NULL;
620159979Sglebius		if (priv->hi_nonmatch == hook)
621159979Sglebius			priv->hi_nonmatch = NULL;
622159979Sglebius	}
623159979Sglebius
624184205Sdes	free(hip->in, M_NETGRAPH_TAG);
625184205Sdes	free(hip->out, M_NETGRAPH_TAG);
626184205Sdes	free(hip, M_NETGRAPH_TAG);
627159979Sglebius	NG_HOOK_SET_PRIVATE(hook, NULL);			/* for good measure */
628159979Sglebius	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
629159979Sglebius	    (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) {
630159979Sglebius		ng_rmnode_self(NG_HOOK_NODE(hook));
631159979Sglebius	}
632159979Sglebius	return (0);
633159979Sglebius}
634159979Sglebius
635159979Sglebius/************************************************************************
636159979Sglebius			HELPER STUFF
637159979Sglebius ************************************************************************/
638159979Sglebius
639159979Sglebius/*
640159979Sglebius * Set the IN tag values associated with a hook.
641159979Sglebius */
642159979Sglebiusstatic int
643159979Sglebiusng_tag_setdata_in(hook_p hook, const struct ng_tag_hookin *hp0)
644159979Sglebius{
645159979Sglebius	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
646159979Sglebius	struct ng_tag_hookin *hp;
647159979Sglebius	int size;
648159979Sglebius
649159979Sglebius	/* Make a copy of the tag values and data. */
650159979Sglebius	size = NG_TAG_HOOKIN_SIZE(hp0->tag_len);
651184205Sdes	hp = malloc(size, M_NETGRAPH_TAG, M_WAITOK);
652159979Sglebius	/* M_WAITOK can't return NULL. */
653159979Sglebius	bcopy(hp0, hp, size);
654159979Sglebius
655159979Sglebius	/* Free previous tag, if any, and assign new one. */
656159979Sglebius	if (hip->in != NULL)
657184205Sdes		free(hip->in, M_NETGRAPH_TAG);
658159979Sglebius	hip->in = hp;
659159979Sglebius
660159979Sglebius	/*
661159979Sglebius	 * Resolve hook names to pointers.
662159979Sglebius	 *
663159979Sglebius	 * As ng_findhook() is expensive operation to do it on every packet
664159979Sglebius	 * after tag matching check, we do it here and use resolved pointers
665159979Sglebius	 * where appropriate.
666159979Sglebius	 *
667159979Sglebius	 * XXX The drawback is that user can configure a hook to use
668159979Sglebius	 * ifMatch/ifNotMatch hooks that do not yet exist and will be added
669159979Sglebius	 * by user later, so that resolved pointers will be NULL even
670159979Sglebius	 * if the hook already exists, causing node to drop packets and
671159979Sglebius	 * user to report bugs.  We could do check for this situation on
672159979Sglebius	 * every hook creation with pointers correction, but that involves
673159979Sglebius	 * re-resolving for all pointers in all hooks, up to O(n^2) operations,
674159979Sglebius	 * so we better document this in man page for user not to do
675159979Sglebius	 * configuration before creating all hooks.
676159979Sglebius	 */
677159979Sglebius	hip->hi_match = ng_findhook(NG_HOOK_NODE(hook), hip->in->ifMatch);
678159979Sglebius	hip->hi_nonmatch = ng_findhook(NG_HOOK_NODE(hook), hip->in->ifNotMatch);
679159979Sglebius
680159979Sglebius	/* Fill internal values from API structures. */
681159979Sglebius	hip->in_tag_cookie = hip->in->tag_cookie;
682159979Sglebius	hip->in_tag_id = hip->in->tag_id;
683159979Sglebius	hip->in_tag_len = hip->in->tag_len;
684159979Sglebius	hip->strip = hip->in->strip;
685159979Sglebius	hip->in_tag_data = (void*)(hip->in->tag_data);
686159979Sglebius	return (0);
687159979Sglebius}
688159979Sglebius
689159979Sglebius/*
690159979Sglebius * Set the OUT tag values associated with a hook.
691159979Sglebius */
692159979Sglebiusstatic int
693159979Sglebiusng_tag_setdata_out(hook_p hook, const struct ng_tag_hookout *hp0)
694159979Sglebius{
695159979Sglebius	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
696159979Sglebius	struct ng_tag_hookout *hp;
697159979Sglebius	int size;
698159979Sglebius
699159979Sglebius	/* Make a copy of the tag values and data. */
700159979Sglebius	size = NG_TAG_HOOKOUT_SIZE(hp0->tag_len);
701184205Sdes	hp = malloc(size, M_NETGRAPH_TAG, M_WAITOK);
702159979Sglebius	/* M_WAITOK can't return NULL. */
703159979Sglebius	bcopy(hp0, hp, size);
704159979Sglebius
705159979Sglebius	/* Free previous tag, if any, and assign new one. */
706159979Sglebius	if (hip->out != NULL)
707184205Sdes		free(hip->out, M_NETGRAPH_TAG);
708159979Sglebius	hip->out = hp;
709159979Sglebius
710159979Sglebius	/* Fill internal values from API structures. */
711159979Sglebius	hip->out_tag_cookie = hip->out->tag_cookie;
712159979Sglebius	hip->out_tag_id = hip->out->tag_id;
713159979Sglebius	hip->out_tag_len = hip->out->tag_len;
714159979Sglebius	hip->out_tag_data = (void*)(hip->out->tag_data);
715159979Sglebius	return (0);
716159979Sglebius}
717159979Sglebius
718