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: releng/10.2/sys/netgraph/ng_tag.c 230272 2012-01-17 18:10:25Z glebius $
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
72227293Sedstatic 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. */
306230272Sglebius	hip = malloc(sizeof(*hip), M_NETGRAPH_TAG, M_NOWAIT | M_ZERO);
307230272Sglebius	if (hip == NULL)
308230272Sglebius		return (ENOMEM);
309159979Sglebius	NG_HOOK_SET_PRIVATE(hook, hip);
310159979Sglebius
311159979Sglebius	/*
312159979Sglebius	 * After M_ZERO both in and out hook pointers are set to NULL,
313159979Sglebius	 * as well as all members and pointers to in and out API
314159979Sglebius	 * structures, so we need to set explicitly only thisHook field
315159979Sglebius	 * in that structures (after allocating them, of course).
316159979Sglebius	 */
317159979Sglebius
318159979Sglebius	/* Attach the default IN data. */
319159979Sglebius	if ((error = ng_tag_setdata_in(hook, &ng_tag_default_in)) != 0) {
320184205Sdes		free(hip, M_NETGRAPH_TAG);
321159979Sglebius		return (error);
322159979Sglebius	}
323159979Sglebius
324159979Sglebius	/* Attach the default OUT data. */
325159979Sglebius	if ((error = ng_tag_setdata_out(hook, &ng_tag_default_out)) != 0) {
326184205Sdes		free(hip, M_NETGRAPH_TAG);
327159979Sglebius		return (error);
328159979Sglebius	}
329159979Sglebius
330159979Sglebius	/*
331159979Sglebius	 * Set hook name.  This is done only once at hook creation time
332159979Sglebius	 * since hook name can't change, rather than to do it on every
333159979Sglebius	 * response to messages requesting API structures with data who
334159979Sglebius	 * we are etc.
335159979Sglebius	 */
336159979Sglebius	strncpy(hip->in->thisHook, name, sizeof(hip->in->thisHook) - 1);
337159979Sglebius	hip->in->thisHook[sizeof(hip->in->thisHook) - 1] = '\0';
338159979Sglebius	strncpy(hip->out->thisHook, name, sizeof(hip->out->thisHook) - 1);
339159979Sglebius	hip->out->thisHook[sizeof(hip->out->thisHook) - 1] = '\0';
340159979Sglebius	return (0);
341159979Sglebius}
342159979Sglebius
343159979Sglebius/*
344159979Sglebius * Receive a control message.
345159979Sglebius */
346159979Sglebiusstatic int
347159979Sglebiusng_tag_rcvmsg(node_p node, item_p item, hook_p lasthook)
348159979Sglebius{
349159979Sglebius	struct ng_mesg *msg;
350159979Sglebius	struct ng_mesg *resp = NULL;
351159979Sglebius	int error = 0;
352159979Sglebius
353159979Sglebius	NGI_GET_MSG(item, msg);
354159979Sglebius	switch (msg->header.typecookie) {
355159979Sglebius	case NGM_TAG_COOKIE:
356159979Sglebius		switch (msg->header.cmd) {
357159979Sglebius		case NGM_TAG_SET_HOOKIN:
358159979Sglebius		    {
359159979Sglebius			struct ng_tag_hookin *const
360159979Sglebius			    hp = (struct ng_tag_hookin *)msg->data;
361159979Sglebius			hook_p hook;
362159979Sglebius
363159979Sglebius			/* Sanity check. */
364159979Sglebius			if (msg->header.arglen < sizeof(*hp)
365159979Sglebius			    || msg->header.arglen !=
366159979Sglebius			    NG_TAG_HOOKIN_SIZE(hp->tag_len))
367159979Sglebius				ERROUT(EINVAL);
368159979Sglebius
369159979Sglebius			/* Find hook. */
370159979Sglebius			if ((hook = ng_findhook(node, hp->thisHook)) == NULL)
371159979Sglebius				ERROUT(ENOENT);
372159979Sglebius
373159979Sglebius			/* Set new tag values. */
374159979Sglebius			if ((error = ng_tag_setdata_in(hook, hp)) != 0)
375159979Sglebius				ERROUT(error);
376159979Sglebius			break;
377159979Sglebius		    }
378159979Sglebius
379159979Sglebius		case NGM_TAG_SET_HOOKOUT:
380159979Sglebius		    {
381159979Sglebius			struct ng_tag_hookout *const
382159979Sglebius			    hp = (struct ng_tag_hookout *)msg->data;
383159979Sglebius			hook_p hook;
384159979Sglebius
385159979Sglebius			/* Sanity check. */
386159979Sglebius			if (msg->header.arglen < sizeof(*hp)
387159979Sglebius			    || msg->header.arglen !=
388159979Sglebius			    NG_TAG_HOOKOUT_SIZE(hp->tag_len))
389159979Sglebius				ERROUT(EINVAL);
390159979Sglebius
391159979Sglebius			/* Find hook. */
392159979Sglebius			if ((hook = ng_findhook(node, hp->thisHook)) == NULL)
393159979Sglebius				ERROUT(ENOENT);
394159979Sglebius
395159979Sglebius			/* Set new tag values. */
396159979Sglebius			if ((error = ng_tag_setdata_out(hook, hp)) != 0)
397159979Sglebius				ERROUT(error);
398159979Sglebius			break;
399159979Sglebius		    }
400159979Sglebius
401159979Sglebius		case NGM_TAG_GET_HOOKIN:
402159979Sglebius		    {
403159979Sglebius			struct ng_tag_hookin *hp;
404159979Sglebius			hook_p hook;
405159979Sglebius
406159979Sglebius			/* Sanity check. */
407159979Sglebius			if (msg->header.arglen == 0)
408159979Sglebius				ERROUT(EINVAL);
409159979Sglebius			msg->data[msg->header.arglen - 1] = '\0';
410159979Sglebius
411159979Sglebius			/* Find hook. */
412159979Sglebius			if ((hook = ng_findhook(node, msg->data)) == NULL)
413159979Sglebius				ERROUT(ENOENT);
414159979Sglebius
415159979Sglebius			/* Build response. */
416159979Sglebius			hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->in;
417159979Sglebius			NG_MKRESPONSE(resp, msg,
418159979Sglebius			    NG_TAG_HOOKIN_SIZE(hp->tag_len), M_WAITOK);
419159979Sglebius			/* M_WAITOK can't return NULL. */
420159979Sglebius			bcopy(hp, resp->data,
421159979Sglebius			   NG_TAG_HOOKIN_SIZE(hp->tag_len));
422159979Sglebius			break;
423159979Sglebius		    }
424159979Sglebius
425159979Sglebius		case NGM_TAG_GET_HOOKOUT:
426159979Sglebius		    {
427159979Sglebius			struct ng_tag_hookout *hp;
428159979Sglebius			hook_p hook;
429159979Sglebius
430159979Sglebius			/* Sanity check. */
431159979Sglebius			if (msg->header.arglen == 0)
432159979Sglebius				ERROUT(EINVAL);
433159979Sglebius			msg->data[msg->header.arglen - 1] = '\0';
434159979Sglebius
435159979Sglebius			/* Find hook. */
436159979Sglebius			if ((hook = ng_findhook(node, msg->data)) == NULL)
437159979Sglebius				ERROUT(ENOENT);
438159979Sglebius
439159979Sglebius			/* Build response. */
440159979Sglebius			hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->out;
441159979Sglebius			NG_MKRESPONSE(resp, msg,
442159979Sglebius			    NG_TAG_HOOKOUT_SIZE(hp->tag_len), M_WAITOK);
443159979Sglebius			/* M_WAITOK can't return NULL. */
444159979Sglebius			bcopy(hp, resp->data,
445159979Sglebius			   NG_TAG_HOOKOUT_SIZE(hp->tag_len));
446159979Sglebius			break;
447159979Sglebius		    }
448159979Sglebius
449159979Sglebius#ifdef NG_TAG_DEBUG
450159979Sglebius		case NGM_TAG_GET_STATS:
451159979Sglebius		case NGM_TAG_CLR_STATS:
452159979Sglebius		case NGM_TAG_GETCLR_STATS:
453159979Sglebius		    {
454159979Sglebius			struct ng_tag_hookstat *stats;
455159979Sglebius			hook_p hook;
456159979Sglebius
457159979Sglebius			/* Sanity check. */
458159979Sglebius			if (msg->header.arglen == 0)
459159979Sglebius				ERROUT(EINVAL);
460159979Sglebius			msg->data[msg->header.arglen - 1] = '\0';
461159979Sglebius
462159979Sglebius			/* Find hook. */
463159979Sglebius			if ((hook = ng_findhook(node, msg->data)) == NULL)
464159979Sglebius				ERROUT(ENOENT);
465159979Sglebius			stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats;
466159979Sglebius
467159979Sglebius			/* Build response (if desired). */
468159979Sglebius			if (msg->header.cmd != NGM_TAG_CLR_STATS) {
469159979Sglebius				NG_MKRESPONSE(resp,
470159979Sglebius				    msg, sizeof(*stats), M_WAITOK);
471159979Sglebius				/* M_WAITOK can't return NULL. */
472159979Sglebius				bcopy(stats, resp->data, sizeof(*stats));
473159979Sglebius			}
474159979Sglebius
475159979Sglebius			/* Clear stats (if desired). */
476159979Sglebius			if (msg->header.cmd != NGM_TAG_GET_STATS)
477159979Sglebius				bzero(stats, sizeof(*stats));
478159979Sglebius			break;
479159979Sglebius		    }
480159979Sglebius#endif /* NG_TAG_DEBUG */
481159979Sglebius
482159979Sglebius		default:
483159979Sglebius			error = EINVAL;
484159979Sglebius			break;
485159979Sglebius		}
486159979Sglebius		break;
487159979Sglebius	default:
488159979Sglebius		error = EINVAL;
489159979Sglebius		break;
490159979Sglebius	}
491159979Sglebiusdone:
492159979Sglebius	NG_RESPOND_MSG(error, node, item, resp);
493159979Sglebius	NG_FREE_MSG(msg);
494159979Sglebius	return (error);
495159979Sglebius}
496159979Sglebius
497159979Sglebius/*
498159979Sglebius * Receive data on a hook.
499159979Sglebius *
500159979Sglebius * Apply the filter, and then drop or forward packet as appropriate.
501159979Sglebius */
502159979Sglebiusstatic int
503159979Sglebiusng_tag_rcvdata(hook_p hook, item_p item)
504159979Sglebius{
505159979Sglebius	struct mbuf *m;
506159979Sglebius	struct m_tag *tag = NULL;
507159979Sglebius	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
508159979Sglebius	uint16_t type, tag_len;
509159979Sglebius	uint32_t cookie;
510159979Sglebius	hinfo_p dhip;
511159979Sglebius	hook_p dest;
512159979Sglebius	int totlen;
513159979Sglebius	int found = 0, error = 0;
514159979Sglebius
515159979Sglebius	m = NGI_M(item);	/* 'item' still owns it.. we are peeking */
516159979Sglebius	totlen = m->m_pkthdr.len;
517159979Sglebius
518159979Sglebius#ifdef NG_TAG_DEBUG
519159979Sglebius	hip->stats.recvFrames++;
520159979Sglebius	hip->stats.recvOctets += totlen;
521159979Sglebius#endif
522159979Sglebius
523159979Sglebius	/* Looking up incoming tag. */
524159979Sglebius	cookie = hip->in_tag_cookie;
525159979Sglebius	type = hip->in_tag_id;
526159979Sglebius	tag_len = hip->in_tag_len;
527159979Sglebius
528159979Sglebius	/*
529159979Sglebius	 * We treat case of all zeroes specially (that is, cookie and
530159979Sglebius	 * type are equal to zero), as we assume that such tag
531159979Sglebius	 * can never occur in the wild.  So we don't waste time trying
532159979Sglebius	 * to find such tag (for example, these are zeroes after hook
533159979Sglebius	 * creation in default structures).
534159979Sglebius	 */
535159979Sglebius	if ((cookie != 0) || (type != 0)) {
536159979Sglebius		tag = m_tag_locate(m, cookie, type, NULL);
537159979Sglebius		while (tag != NULL) {
538159979Sglebius			if (memcmp((void *)(tag + 1),
539159979Sglebius			    hip->in_tag_data, tag_len) == 0) {
540159979Sglebius				found = 1;
541159979Sglebius				break;
542159979Sglebius			}
543159979Sglebius			tag = m_tag_locate(m, cookie, type, tag);
544159979Sglebius		}
545159979Sglebius	}
546159979Sglebius
547159979Sglebius	/* See if we got a match and find destination hook. */
548159979Sglebius	if (found) {
549159979Sglebius#ifdef NG_TAG_DEBUG
550159979Sglebius		hip->stats.recvMatchFrames++;
551159979Sglebius		hip->stats.recvMatchOctets += totlen;
552159979Sglebius#endif
553159979Sglebius		if (hip->strip)
554159979Sglebius			m_tag_delete(m, tag);
555159979Sglebius		dest = hip->hi_match;
556159979Sglebius	} else
557159979Sglebius		dest = hip->hi_nonmatch;
558159979Sglebius	if (dest == NULL) {
559159979Sglebius		NG_FREE_ITEM(item);
560159979Sglebius		return (0);
561159979Sglebius	}
562159979Sglebius
563159979Sglebius	/* Deliver frame out destination hook. */
564159979Sglebius	dhip = NG_HOOK_PRIVATE(dest);
565159979Sglebius
566159979Sglebius#ifdef NG_TAG_DEBUG
567159979Sglebius	dhip->stats.xmitOctets += totlen;
568159979Sglebius	dhip->stats.xmitFrames++;
569159979Sglebius#endif
570159979Sglebius
571159979Sglebius	cookie = dhip->out_tag_cookie;
572159979Sglebius	type = dhip->out_tag_id;
573159979Sglebius	tag_len = dhip->out_tag_len;
574159979Sglebius
575159979Sglebius	if ((cookie != 0) || (type != 0)) {
576159979Sglebius		tag = m_tag_alloc(cookie, type, tag_len, M_NOWAIT);
577159979Sglebius		/* XXX may be free the mbuf if tag allocation failed? */
578159979Sglebius		if (tag != NULL) {
579159979Sglebius			if (tag_len != 0) {
580159979Sglebius				/* copy tag data to its place */
581159979Sglebius				memcpy((void *)(tag + 1),
582159979Sglebius				    dhip->out_tag_data, tag_len);
583159979Sglebius			}
584159979Sglebius			m_tag_prepend(m, tag);
585159979Sglebius		}
586159979Sglebius	}
587159979Sglebius
588159979Sglebius	NG_FWD_ITEM_HOOK(error, item, dest);
589159979Sglebius	return (error);
590159979Sglebius}
591159979Sglebius
592159979Sglebius/*
593159979Sglebius * Shutdown processing.
594159979Sglebius */
595159979Sglebiusstatic int
596159979Sglebiusng_tag_shutdown(node_p node)
597159979Sglebius{
598159979Sglebius	NG_NODE_UNREF(node);
599159979Sglebius	return (0);
600159979Sglebius}
601159979Sglebius
602159979Sglebius/*
603159979Sglebius * Hook disconnection.
604159979Sglebius *
605159979Sglebius * We must check all hooks, since they may reference this one.
606159979Sglebius */
607159979Sglebiusstatic int
608159979Sglebiusng_tag_disconnect(hook_p hook)
609159979Sglebius{
610159979Sglebius	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
611159979Sglebius	node_p node = NG_HOOK_NODE(hook);
612159979Sglebius	hook_p hook2;
613159979Sglebius
614159979Sglebius	KASSERT(hip != NULL, ("%s: null info", __func__));
615159979Sglebius
616159979Sglebius	LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks) {
617159979Sglebius		hinfo_p priv = NG_HOOK_PRIVATE(hook2);
618159979Sglebius
619159979Sglebius		if (priv->hi_match == hook)
620159979Sglebius			priv->hi_match = NULL;
621159979Sglebius		if (priv->hi_nonmatch == hook)
622159979Sglebius			priv->hi_nonmatch = NULL;
623159979Sglebius	}
624159979Sglebius
625184205Sdes	free(hip->in, M_NETGRAPH_TAG);
626184205Sdes	free(hip->out, M_NETGRAPH_TAG);
627184205Sdes	free(hip, M_NETGRAPH_TAG);
628159979Sglebius	NG_HOOK_SET_PRIVATE(hook, NULL);			/* for good measure */
629159979Sglebius	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
630159979Sglebius	    (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) {
631159979Sglebius		ng_rmnode_self(NG_HOOK_NODE(hook));
632159979Sglebius	}
633159979Sglebius	return (0);
634159979Sglebius}
635159979Sglebius
636159979Sglebius/************************************************************************
637159979Sglebius			HELPER STUFF
638159979Sglebius ************************************************************************/
639159979Sglebius
640159979Sglebius/*
641159979Sglebius * Set the IN tag values associated with a hook.
642159979Sglebius */
643159979Sglebiusstatic int
644159979Sglebiusng_tag_setdata_in(hook_p hook, const struct ng_tag_hookin *hp0)
645159979Sglebius{
646159979Sglebius	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
647159979Sglebius	struct ng_tag_hookin *hp;
648159979Sglebius	int size;
649159979Sglebius
650159979Sglebius	/* Make a copy of the tag values and data. */
651159979Sglebius	size = NG_TAG_HOOKIN_SIZE(hp0->tag_len);
652184205Sdes	hp = malloc(size, M_NETGRAPH_TAG, M_WAITOK);
653159979Sglebius	/* M_WAITOK can't return NULL. */
654159979Sglebius	bcopy(hp0, hp, size);
655159979Sglebius
656159979Sglebius	/* Free previous tag, if any, and assign new one. */
657159979Sglebius	if (hip->in != NULL)
658184205Sdes		free(hip->in, M_NETGRAPH_TAG);
659159979Sglebius	hip->in = hp;
660159979Sglebius
661159979Sglebius	/*
662159979Sglebius	 * Resolve hook names to pointers.
663159979Sglebius	 *
664159979Sglebius	 * As ng_findhook() is expensive operation to do it on every packet
665159979Sglebius	 * after tag matching check, we do it here and use resolved pointers
666159979Sglebius	 * where appropriate.
667159979Sglebius	 *
668159979Sglebius	 * XXX The drawback is that user can configure a hook to use
669159979Sglebius	 * ifMatch/ifNotMatch hooks that do not yet exist and will be added
670159979Sglebius	 * by user later, so that resolved pointers will be NULL even
671159979Sglebius	 * if the hook already exists, causing node to drop packets and
672159979Sglebius	 * user to report bugs.  We could do check for this situation on
673159979Sglebius	 * every hook creation with pointers correction, but that involves
674159979Sglebius	 * re-resolving for all pointers in all hooks, up to O(n^2) operations,
675159979Sglebius	 * so we better document this in man page for user not to do
676159979Sglebius	 * configuration before creating all hooks.
677159979Sglebius	 */
678159979Sglebius	hip->hi_match = ng_findhook(NG_HOOK_NODE(hook), hip->in->ifMatch);
679159979Sglebius	hip->hi_nonmatch = ng_findhook(NG_HOOK_NODE(hook), hip->in->ifNotMatch);
680159979Sglebius
681159979Sglebius	/* Fill internal values from API structures. */
682159979Sglebius	hip->in_tag_cookie = hip->in->tag_cookie;
683159979Sglebius	hip->in_tag_id = hip->in->tag_id;
684159979Sglebius	hip->in_tag_len = hip->in->tag_len;
685159979Sglebius	hip->strip = hip->in->strip;
686159979Sglebius	hip->in_tag_data = (void*)(hip->in->tag_data);
687159979Sglebius	return (0);
688159979Sglebius}
689159979Sglebius
690159979Sglebius/*
691159979Sglebius * Set the OUT tag values associated with a hook.
692159979Sglebius */
693159979Sglebiusstatic int
694159979Sglebiusng_tag_setdata_out(hook_p hook, const struct ng_tag_hookout *hp0)
695159979Sglebius{
696159979Sglebius	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
697159979Sglebius	struct ng_tag_hookout *hp;
698159979Sglebius	int size;
699159979Sglebius
700159979Sglebius	/* Make a copy of the tag values and data. */
701159979Sglebius	size = NG_TAG_HOOKOUT_SIZE(hp0->tag_len);
702184205Sdes	hp = malloc(size, M_NETGRAPH_TAG, M_WAITOK);
703159979Sglebius	/* M_WAITOK can't return NULL. */
704159979Sglebius	bcopy(hp0, hp, size);
705159979Sglebius
706159979Sglebius	/* Free previous tag, if any, and assign new one. */
707159979Sglebius	if (hip->out != NULL)
708184205Sdes		free(hip->out, M_NETGRAPH_TAG);
709159979Sglebius	hip->out = hp;
710159979Sglebius
711159979Sglebius	/* Fill internal values from API structures. */
712159979Sglebius	hip->out_tag_cookie = hip->out->tag_cookie;
713159979Sglebius	hip->out_tag_id = hip->out->tag_id;
714159979Sglebius	hip->out_tag_len = hip->out->tag_len;
715159979Sglebius	hip->out_tag_data = (void*)(hip->out->tag_data);
716159979Sglebius	return (0);
717159979Sglebius}
718159979Sglebius
719