xref: /freebsd/sys/netgraph/ng_car.c (revision df01e689224d4f3c600da7d4010fdf279ef8c885)
1c97bf8c3SAlexander Motin /*-
2c97bf8c3SAlexander Motin  * Copyright (c) 2005 Nuno Antunes <nuno.antunes@gmail.com>
3c97bf8c3SAlexander Motin  * Copyright (c) 2007 Alexander Motin <mav@freebsd.org>
4c97bf8c3SAlexander Motin  * All rights reserved.
5c97bf8c3SAlexander Motin  *
6c97bf8c3SAlexander Motin  * Redistribution and use in source and binary forms, with or without
7c97bf8c3SAlexander Motin  * modification, are permitted provided that the following conditions
8c97bf8c3SAlexander Motin  * are met:
9c97bf8c3SAlexander Motin  * 1. Redistributions of source code must retain the above copyright
10c97bf8c3SAlexander Motin  *    notice, this list of conditions and the following disclaimer.
11c97bf8c3SAlexander Motin  * 2. Redistributions in binary form must reproduce the above copyright
12c97bf8c3SAlexander Motin  *    notice, this list of conditions and the following disclaimer in the
13c97bf8c3SAlexander Motin  *    documentation and/or other materials provided with the distribution.
14c97bf8c3SAlexander Motin  *
15c97bf8c3SAlexander Motin  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16c97bf8c3SAlexander Motin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17c97bf8c3SAlexander Motin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18c97bf8c3SAlexander Motin  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19c97bf8c3SAlexander Motin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c97bf8c3SAlexander Motin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21c97bf8c3SAlexander Motin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c97bf8c3SAlexander Motin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c97bf8c3SAlexander Motin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c97bf8c3SAlexander Motin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c97bf8c3SAlexander Motin  * SUCH DAMAGE.
26c97bf8c3SAlexander Motin  *
27c97bf8c3SAlexander Motin  * $FreeBSD$
28c97bf8c3SAlexander Motin  */
29c97bf8c3SAlexander Motin 
30c97bf8c3SAlexander Motin /*
31c97bf8c3SAlexander Motin  * ng_car - An implementation of commited access rate for netgraph
32c97bf8c3SAlexander Motin  *
33c97bf8c3SAlexander Motin  * TODO:
34c97bf8c3SAlexander Motin  *	- Sanitize input config values (impose some limits)
35c97bf8c3SAlexander Motin  *	- Implement internal packet painting (possibly using mbuf tags)
36c97bf8c3SAlexander Motin  *	- Implement color-aware mode
37c97bf8c3SAlexander Motin  *	- Implement DSCP marking for IPv4
38c97bf8c3SAlexander Motin  */
39c97bf8c3SAlexander Motin 
40c97bf8c3SAlexander Motin #include <sys/param.h>
41c97bf8c3SAlexander Motin #include <sys/errno.h>
42ae1be01fSAlexander Motin #include <sys/kernel.h>
43ae1be01fSAlexander Motin #include <sys/malloc.h>
44ae1be01fSAlexander Motin #include <sys/mbuf.h>
45c97bf8c3SAlexander Motin 
46c97bf8c3SAlexander Motin #include <netgraph/ng_message.h>
47c97bf8c3SAlexander Motin #include <netgraph/ng_parse.h>
48c97bf8c3SAlexander Motin #include <netgraph/netgraph.h>
49c97bf8c3SAlexander Motin #include <netgraph/ng_car.h>
50c97bf8c3SAlexander Motin 
51c97bf8c3SAlexander Motin #define NG_CAR_QUEUE_SIZE	100	/* Maximum queue size for SHAPE mode */
52c97bf8c3SAlexander Motin #define NG_CAR_QUEUE_MIN_TH	8	/* Minimum RED threshhold for SHAPE mode */
53c97bf8c3SAlexander Motin 
54c97bf8c3SAlexander Motin /* Hook private info */
55c97bf8c3SAlexander Motin struct hookinfo {
56c97bf8c3SAlexander Motin 	hook_p		hook;		/* this (source) hook */
57c97bf8c3SAlexander Motin 	hook_p		dest;		/* destination hook */
58c97bf8c3SAlexander Motin 
59c97bf8c3SAlexander Motin 	int64_t 	tc;		/* commited token bucket counter */
60c97bf8c3SAlexander Motin 	int64_t 	te;		/* exceeded/peak token bucket counter */
61c97bf8c3SAlexander Motin 	struct timeval	lastRefill;	/* last token refill time */
62c97bf8c3SAlexander Motin 
63c97bf8c3SAlexander Motin 	struct ng_car_hookconf conf;	/* hook configuration */
64c97bf8c3SAlexander Motin 	struct ng_car_hookstats stats;	/* hook stats */
65c97bf8c3SAlexander Motin 
66c97bf8c3SAlexander Motin 	struct mbuf	*q[NG_CAR_QUEUE_SIZE];	/* circular packet queue */
67c97bf8c3SAlexander Motin 	int		q_first;	/* first queue element */
68c97bf8c3SAlexander Motin 	int		q_last;		/* last queue element */
69c97bf8c3SAlexander Motin 	struct callout	q_callout;	/* periodic queue processing routine */
70c97bf8c3SAlexander Motin 	struct mtx	q_mtx;		/* queue mutex */
71c97bf8c3SAlexander Motin };
72c97bf8c3SAlexander Motin 
73c97bf8c3SAlexander Motin /* Private information for each node instance */
74c97bf8c3SAlexander Motin struct privdata {
75c97bf8c3SAlexander Motin 	node_p node;				/* the node itself */
76c97bf8c3SAlexander Motin 	struct hookinfo upper;			/* hook to upper layers */
77c97bf8c3SAlexander Motin 	struct hookinfo lower;			/* hook to lower layers */
78c97bf8c3SAlexander Motin };
79c97bf8c3SAlexander Motin typedef struct privdata *priv_p;
80c97bf8c3SAlexander Motin 
81c97bf8c3SAlexander Motin static ng_constructor_t	ng_car_constructor;
82c97bf8c3SAlexander Motin static ng_rcvmsg_t	ng_car_rcvmsg;
83c97bf8c3SAlexander Motin static ng_shutdown_t	ng_car_shutdown;
84c97bf8c3SAlexander Motin static ng_newhook_t	ng_car_newhook;
85c97bf8c3SAlexander Motin static ng_rcvdata_t	ng_car_rcvdata;
86c97bf8c3SAlexander Motin static ng_disconnect_t	ng_car_disconnect;
87c97bf8c3SAlexander Motin 
88c97bf8c3SAlexander Motin static void	ng_car_refillhook(struct hookinfo *h);
89c97bf8c3SAlexander Motin static void	ng_car_schedule(struct hookinfo *h);
90c97bf8c3SAlexander Motin void		ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2);
91c97bf8c3SAlexander Motin static void	ng_car_enqueue(struct hookinfo *h, item_p item);
92c97bf8c3SAlexander Motin 
93c97bf8c3SAlexander Motin /* Parse type for struct ng_car_hookstats */
94c97bf8c3SAlexander Motin static const struct ng_parse_struct_field ng_car_hookstats_type_fields[]
95c97bf8c3SAlexander Motin 	= NG_CAR_HOOKSTATS;
96c97bf8c3SAlexander Motin static const struct ng_parse_type ng_car_hookstats_type = {
97c97bf8c3SAlexander Motin 	&ng_parse_struct_type,
98c97bf8c3SAlexander Motin 	&ng_car_hookstats_type_fields
99c97bf8c3SAlexander Motin };
100c97bf8c3SAlexander Motin 
101c97bf8c3SAlexander Motin /* Parse type for struct ng_car_bulkstats */
102c97bf8c3SAlexander Motin static const struct ng_parse_struct_field ng_car_bulkstats_type_fields[]
103c97bf8c3SAlexander Motin 	= NG_CAR_BULKSTATS(&ng_car_hookstats_type);
104c97bf8c3SAlexander Motin static const struct ng_parse_type ng_car_bulkstats_type = {
105c97bf8c3SAlexander Motin 	&ng_parse_struct_type,
106c97bf8c3SAlexander Motin 	&ng_car_bulkstats_type_fields
107c97bf8c3SAlexander Motin };
108c97bf8c3SAlexander Motin 
109c97bf8c3SAlexander Motin /* Parse type for struct ng_car_hookconf */
110c97bf8c3SAlexander Motin static const struct ng_parse_struct_field ng_car_hookconf_type_fields[]
111c97bf8c3SAlexander Motin 	= NG_CAR_HOOKCONF;
112c97bf8c3SAlexander Motin static const struct ng_parse_type ng_car_hookconf_type = {
113c97bf8c3SAlexander Motin 	&ng_parse_struct_type,
114c97bf8c3SAlexander Motin 	&ng_car_hookconf_type_fields
115c97bf8c3SAlexander Motin };
116c97bf8c3SAlexander Motin 
117c97bf8c3SAlexander Motin /* Parse type for struct ng_car_bulkconf */
118c97bf8c3SAlexander Motin static const struct ng_parse_struct_field ng_car_bulkconf_type_fields[]
119c97bf8c3SAlexander Motin 	= NG_CAR_BULKCONF(&ng_car_hookconf_type);
120c97bf8c3SAlexander Motin static const struct ng_parse_type ng_car_bulkconf_type = {
121c97bf8c3SAlexander Motin 	&ng_parse_struct_type,
122c97bf8c3SAlexander Motin 	&ng_car_bulkconf_type_fields
123c97bf8c3SAlexander Motin };
124c97bf8c3SAlexander Motin 
125c97bf8c3SAlexander Motin /* Command list */
126c97bf8c3SAlexander Motin static struct ng_cmdlist ng_car_cmdlist[] = {
127c97bf8c3SAlexander Motin 	{
128c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
129c97bf8c3SAlexander Motin 	  NGM_CAR_GET_STATS,
130c97bf8c3SAlexander Motin 	  "getstats",
131c97bf8c3SAlexander Motin 	  NULL,
132c97bf8c3SAlexander Motin 	  &ng_car_bulkstats_type,
133c97bf8c3SAlexander Motin 	},
134c97bf8c3SAlexander Motin 	{
135c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
136c97bf8c3SAlexander Motin 	  NGM_CAR_CLR_STATS,
137c97bf8c3SAlexander Motin 	  "clrstats",
138c97bf8c3SAlexander Motin 	  NULL,
139c97bf8c3SAlexander Motin 	  NULL,
140c97bf8c3SAlexander Motin 	},
141c97bf8c3SAlexander Motin 	{
142c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
143c97bf8c3SAlexander Motin 	  NGM_CAR_GETCLR_STATS,
144c97bf8c3SAlexander Motin 	  "getclrstats",
145c97bf8c3SAlexander Motin 	  NULL,
146c97bf8c3SAlexander Motin 	  &ng_car_bulkstats_type,
147c97bf8c3SAlexander Motin 	},
148c97bf8c3SAlexander Motin 
149c97bf8c3SAlexander Motin 	{
150c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
151c97bf8c3SAlexander Motin 	  NGM_CAR_GET_CONF,
152c97bf8c3SAlexander Motin 	  "getconf",
153c97bf8c3SAlexander Motin 	  NULL,
154c97bf8c3SAlexander Motin 	  &ng_car_bulkconf_type,
155c97bf8c3SAlexander Motin 	},
156c97bf8c3SAlexander Motin 	{
157c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
158c97bf8c3SAlexander Motin 	  NGM_CAR_SET_CONF,
159c97bf8c3SAlexander Motin 	  "setconf",
160c97bf8c3SAlexander Motin 	  &ng_car_bulkconf_type,
161c97bf8c3SAlexander Motin 	  NULL,
162c97bf8c3SAlexander Motin 	},
163c97bf8c3SAlexander Motin 	{ 0 }
164c97bf8c3SAlexander Motin };
165c97bf8c3SAlexander Motin 
166c97bf8c3SAlexander Motin /* Netgraph node type descriptor */
167c97bf8c3SAlexander Motin static struct ng_type ng_car_typestruct = {
168c97bf8c3SAlexander Motin 	.version =	NG_ABI_VERSION,
169c97bf8c3SAlexander Motin 	.name =		NG_CAR_NODE_TYPE,
170c97bf8c3SAlexander Motin 	.constructor =	ng_car_constructor,
171c97bf8c3SAlexander Motin 	.rcvmsg =	ng_car_rcvmsg,
172c97bf8c3SAlexander Motin 	.shutdown =	ng_car_shutdown,
173c97bf8c3SAlexander Motin 	.newhook =	ng_car_newhook,
174c97bf8c3SAlexander Motin 	.rcvdata =	ng_car_rcvdata,
175c97bf8c3SAlexander Motin 	.disconnect =	ng_car_disconnect,
176c97bf8c3SAlexander Motin 	.cmdlist =	ng_car_cmdlist,
177c97bf8c3SAlexander Motin };
178c97bf8c3SAlexander Motin NETGRAPH_INIT(car, &ng_car_typestruct);
179c97bf8c3SAlexander Motin 
180c97bf8c3SAlexander Motin /*
181c97bf8c3SAlexander Motin  * Node constructor
182c97bf8c3SAlexander Motin  */
183c97bf8c3SAlexander Motin static int
184c97bf8c3SAlexander Motin ng_car_constructor(node_p node)
185c97bf8c3SAlexander Motin {
186c97bf8c3SAlexander Motin 	priv_p priv;
187c97bf8c3SAlexander Motin 
188ae1be01fSAlexander Motin 	/* Initialize private descriptor. */
189ae1be01fSAlexander Motin 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
190c97bf8c3SAlexander Motin 	if (priv == NULL)
191c97bf8c3SAlexander Motin 		return (ENOMEM);
192c97bf8c3SAlexander Motin 
193c97bf8c3SAlexander Motin 	NG_NODE_SET_PRIVATE(node, priv);
194c97bf8c3SAlexander Motin 	priv->node = node;
195c97bf8c3SAlexander Motin 
196c97bf8c3SAlexander Motin 	/*
197c97bf8c3SAlexander Motin 	 * Arbitrary default values
198c97bf8c3SAlexander Motin 	 */
199c97bf8c3SAlexander Motin 
200c97bf8c3SAlexander Motin 	priv->upper.hook = NULL;
201c97bf8c3SAlexander Motin 	priv->upper.dest = NULL;
202c97bf8c3SAlexander Motin 	priv->upper.tc = priv->upper.conf.cbs = NG_CAR_CBS_MIN;
203c97bf8c3SAlexander Motin 	priv->upper.te = priv->upper.conf.ebs = NG_CAR_EBS_MIN;
204c97bf8c3SAlexander Motin 	priv->upper.conf.cir = NG_CAR_CIR_DFLT;
205c97bf8c3SAlexander Motin 	priv->upper.conf.green_action = NG_CAR_ACTION_FORWARD;
206c97bf8c3SAlexander Motin 	priv->upper.conf.yellow_action = NG_CAR_ACTION_FORWARD;
207c97bf8c3SAlexander Motin 	priv->upper.conf.red_action = NG_CAR_ACTION_DROP;
208c97bf8c3SAlexander Motin 	priv->upper.conf.mode = 0;
209c97bf8c3SAlexander Motin 	getmicrotime(&priv->upper.lastRefill);
210c97bf8c3SAlexander Motin 	priv->upper.q_first = 0;
211c97bf8c3SAlexander Motin 	priv->upper.q_last = 0;
212c97bf8c3SAlexander Motin 	ng_callout_init(&priv->upper.q_callout);
213c97bf8c3SAlexander Motin 	mtx_init(&priv->upper.q_mtx, "ng_car_u", NULL, MTX_DEF);
214c97bf8c3SAlexander Motin 
215c97bf8c3SAlexander Motin 	priv->lower.hook = NULL;
216c97bf8c3SAlexander Motin 	priv->lower.dest = NULL;
217c97bf8c3SAlexander Motin 	priv->lower.tc = priv->lower.conf.cbs = NG_CAR_CBS_MIN;
218c97bf8c3SAlexander Motin 	priv->lower.te = priv->lower.conf.ebs = NG_CAR_EBS_MIN;
219c97bf8c3SAlexander Motin 	priv->lower.conf.cir = NG_CAR_CIR_DFLT;
220c97bf8c3SAlexander Motin 	priv->lower.conf.green_action = NG_CAR_ACTION_FORWARD;
221c97bf8c3SAlexander Motin 	priv->lower.conf.yellow_action = NG_CAR_ACTION_FORWARD;
222c97bf8c3SAlexander Motin 	priv->lower.conf.red_action = NG_CAR_ACTION_DROP;
223c97bf8c3SAlexander Motin 	priv->lower.conf.mode = 0;
224c97bf8c3SAlexander Motin 	priv->lower.lastRefill = priv->upper.lastRefill;
225c97bf8c3SAlexander Motin 	priv->lower.q_first = 0;
226c97bf8c3SAlexander Motin 	priv->lower.q_last = 0;
227c97bf8c3SAlexander Motin 	ng_callout_init(&priv->lower.q_callout);
228c97bf8c3SAlexander Motin 	mtx_init(&priv->lower.q_mtx, "ng_car_l", NULL, MTX_DEF);
229c97bf8c3SAlexander Motin 
230c97bf8c3SAlexander Motin 	return (0);
231c97bf8c3SAlexander Motin }
232c97bf8c3SAlexander Motin 
233c97bf8c3SAlexander Motin /*
234ae1be01fSAlexander Motin  * Add a hook.
235c97bf8c3SAlexander Motin  */
236c97bf8c3SAlexander Motin static int
237c97bf8c3SAlexander Motin ng_car_newhook(node_p node, hook_p hook, const char *name)
238c97bf8c3SAlexander Motin {
239c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
240c97bf8c3SAlexander Motin 
241c97bf8c3SAlexander Motin 	if (strcmp(name, NG_CAR_HOOK_LOWER) == 0) {
242c97bf8c3SAlexander Motin 		priv->lower.hook = hook;
243c97bf8c3SAlexander Motin 		priv->upper.dest = hook;
244c97bf8c3SAlexander Motin 		bzero(&priv->lower.stats, sizeof(priv->lower.stats));
245c97bf8c3SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, &priv->lower);
246c97bf8c3SAlexander Motin 	} else if (strcmp(name, NG_CAR_HOOK_UPPER) == 0) {
247c97bf8c3SAlexander Motin 		priv->upper.hook = hook;
248c97bf8c3SAlexander Motin 		priv->lower.dest = hook;
249c97bf8c3SAlexander Motin 		bzero(&priv->upper.stats, sizeof(priv->upper.stats));
250c97bf8c3SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, &priv->upper);
251c97bf8c3SAlexander Motin 	} else
252c97bf8c3SAlexander Motin 		return (EINVAL);
253c97bf8c3SAlexander Motin 	return(0);
254c97bf8c3SAlexander Motin }
255c97bf8c3SAlexander Motin 
256c97bf8c3SAlexander Motin /*
257ae1be01fSAlexander Motin  * Data has arrived.
258c97bf8c3SAlexander Motin  */
259c97bf8c3SAlexander Motin static int
260c97bf8c3SAlexander Motin ng_car_rcvdata(hook_p hook, item_p item )
261c97bf8c3SAlexander Motin {
262c97bf8c3SAlexander Motin 	struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
263c97bf8c3SAlexander Motin 	hook_p dest = hinfo->dest;
264c97bf8c3SAlexander Motin 	struct mbuf *m = NULL;
265c97bf8c3SAlexander Motin 	int error = 0;
266c97bf8c3SAlexander Motin 
267c97bf8c3SAlexander Motin 	/* Node is useless without destination hook. */
268c97bf8c3SAlexander Motin 	if (dest == NULL) {
269c97bf8c3SAlexander Motin 		NG_FREE_ITEM(item);
270c97bf8c3SAlexander Motin 		++hinfo->stats.errors;
271c97bf8c3SAlexander Motin 		return(EINVAL);
272c97bf8c3SAlexander Motin 	}
273c97bf8c3SAlexander Motin 
274c97bf8c3SAlexander Motin 	/* If queue is not empty now then enqueue packet. */
275c97bf8c3SAlexander Motin 	if (hinfo->q_first != hinfo->q_last) {
276c97bf8c3SAlexander Motin 		ng_car_enqueue(hinfo, item);
277c97bf8c3SAlexander Motin 		return (0);
278c97bf8c3SAlexander Motin 	}
279c97bf8c3SAlexander Motin 
280c97bf8c3SAlexander Motin 	m = NGI_M(item);
281c97bf8c3SAlexander Motin 
282c97bf8c3SAlexander Motin #define NG_CAR_PERFORM_MATCH_ACTION(a)			\
283c97bf8c3SAlexander Motin 	do {						\
284c97bf8c3SAlexander Motin 		switch (a) {				\
285c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_FORWARD:		\
286ae1be01fSAlexander Motin 			/* Do nothing. */		\
287c97bf8c3SAlexander Motin 			break;				\
288c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_MARK:		\
289c97bf8c3SAlexander Motin 			/* XXX find a way to mark packets (mbuf tag?) */ \
290c97bf8c3SAlexander Motin 			++hinfo->stats.errors;		\
291c97bf8c3SAlexander Motin 			break;				\
292c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_DROP:		\
293c97bf8c3SAlexander Motin 		default:				\
294ae1be01fSAlexander Motin 			/* Drop packet and return. */	\
295c97bf8c3SAlexander Motin 			NG_FREE_ITEM(item);		\
296c97bf8c3SAlexander Motin 			++hinfo->stats.droped_pkts;	\
297ae1be01fSAlexander Motin 			return (0);			\
298c97bf8c3SAlexander Motin 		}					\
299ae1be01fSAlexander Motin 	} while (0)
300c97bf8c3SAlexander Motin 
301c97bf8c3SAlexander Motin 	/* Check commited token bucket. */
302c97bf8c3SAlexander Motin 	if (hinfo->tc - m->m_pkthdr.len >= 0) {
303ae1be01fSAlexander Motin 		/* This packet is green. */
304c97bf8c3SAlexander Motin 		++hinfo->stats.green_pkts;
305c97bf8c3SAlexander Motin 		hinfo->tc -= m->m_pkthdr.len;
306c97bf8c3SAlexander Motin 		NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action);
307c97bf8c3SAlexander Motin 	} else {
308c97bf8c3SAlexander Motin 
309c97bf8c3SAlexander Motin 		/* Refill only if not green without it. */
310c97bf8c3SAlexander Motin 		ng_car_refillhook(hinfo);
311c97bf8c3SAlexander Motin 
312c97bf8c3SAlexander Motin 		 /* Check commited token bucket again after refill. */
313c97bf8c3SAlexander Motin 		if (hinfo->tc - m->m_pkthdr.len >= 0) {
314c97bf8c3SAlexander Motin 			/* This packet is green */
315c97bf8c3SAlexander Motin 			++hinfo->stats.green_pkts;
316c97bf8c3SAlexander Motin 			hinfo->tc -= m->m_pkthdr.len;
317c97bf8c3SAlexander Motin 			NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action);
318c97bf8c3SAlexander Motin 
319c97bf8c3SAlexander Motin 		/* If not green and mode is SHAPE, enqueue packet. */
320c97bf8c3SAlexander Motin 		} else if (hinfo->conf.mode == NG_CAR_SHAPE) {
321c97bf8c3SAlexander Motin 			ng_car_enqueue(hinfo, item);
322c97bf8c3SAlexander Motin 			return (0);
323c97bf8c3SAlexander Motin 
324c97bf8c3SAlexander Motin 		/* If not green and mode is RED, calculate probability. */
325c97bf8c3SAlexander Motin 		} else if (hinfo->conf.mode == NG_CAR_RED) {
326c97bf8c3SAlexander Motin 			/* Is packet is bigger then extended burst? */
327ae1be01fSAlexander Motin 			if (m->m_pkthdr.len - (hinfo->tc - m->m_pkthdr.len) >
328ae1be01fSAlexander Motin 			    hinfo->conf.ebs) {
329ae1be01fSAlexander Motin 				/* This packet is definitely red. */
330c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
331c97bf8c3SAlexander Motin 				hinfo->te = 0;
332c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
333c97bf8c3SAlexander Motin 
334ae1be01fSAlexander Motin 			/* Use token bucket to simulate RED-like drop
335ae1be01fSAlexander Motin 			   probability. */
336ae1be01fSAlexander Motin 			} else if (hinfo->te + (m->m_pkthdr.len - hinfo->tc) <
337ae1be01fSAlexander Motin 			    hinfo->conf.ebs) {
338c97bf8c3SAlexander Motin 				/* This packet is yellow */
339c97bf8c3SAlexander Motin 				++hinfo->stats.yellow_pkts;
340c97bf8c3SAlexander Motin 				hinfo->te += m->m_pkthdr.len - hinfo->tc;
341ae1be01fSAlexander Motin 				/* Go to negative tokens. */
342ae1be01fSAlexander Motin 				hinfo->tc -= m->m_pkthdr.len;
343c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action);
344c97bf8c3SAlexander Motin 			} else {
345ae1be01fSAlexander Motin 				/* This packet is probaly red. */
346c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
347c97bf8c3SAlexander Motin 				hinfo->te = 0;
348c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
349c97bf8c3SAlexander Motin 			}
350c97bf8c3SAlexander Motin 		/* If not green and mode is SINGLE/DOUBLE RATE. */
351c97bf8c3SAlexander Motin 		} else {
352c97bf8c3SAlexander Motin 			/* Check extended token bucket. */
353c97bf8c3SAlexander Motin 			if (hinfo->te - m->m_pkthdr.len >= 0) {
354c97bf8c3SAlexander Motin 				/* This packet is yellow */
355c97bf8c3SAlexander Motin 				++hinfo->stats.yellow_pkts;
356c97bf8c3SAlexander Motin 				hinfo->te -= m->m_pkthdr.len;
357c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action);
358c97bf8c3SAlexander Motin 			} else {
359c97bf8c3SAlexander Motin 				/* This packet is red */
360c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
361c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
362c97bf8c3SAlexander Motin 			}
363c97bf8c3SAlexander Motin 		}
364c97bf8c3SAlexander Motin 	}
365c97bf8c3SAlexander Motin 
366c97bf8c3SAlexander Motin #undef NG_CAR_PERFORM_MATCH_ACTION
367c97bf8c3SAlexander Motin 
368c97bf8c3SAlexander Motin 	NG_FWD_ITEM_HOOK(error, item, dest);
369c97bf8c3SAlexander Motin 	if (error != 0)
370c97bf8c3SAlexander Motin 		++hinfo->stats.errors;
371c97bf8c3SAlexander Motin 	++hinfo->stats.passed_pkts;
372c97bf8c3SAlexander Motin 
373ae1be01fSAlexander Motin 	return (error);
374c97bf8c3SAlexander Motin }
375c97bf8c3SAlexander Motin 
376c97bf8c3SAlexander Motin /*
377ae1be01fSAlexander Motin  * Receive a control message.
378c97bf8c3SAlexander Motin  */
379c97bf8c3SAlexander Motin static int
380c97bf8c3SAlexander Motin ng_car_rcvmsg(node_p node, item_p item, hook_p lasthook)
381c97bf8c3SAlexander Motin {
382c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
383c97bf8c3SAlexander Motin 	struct ng_mesg *resp = NULL;
384c97bf8c3SAlexander Motin 	int error = 0;
385c97bf8c3SAlexander Motin 	struct ng_mesg *msg;
386c97bf8c3SAlexander Motin 
387c97bf8c3SAlexander Motin 	NGI_GET_MSG(item, msg);
388c97bf8c3SAlexander Motin 	switch (msg->header.typecookie) {
389c97bf8c3SAlexander Motin 	case NGM_CAR_COOKIE:
390c97bf8c3SAlexander Motin 		switch (msg->header.cmd) {
391c97bf8c3SAlexander Motin 		case NGM_CAR_GET_STATS:
392c97bf8c3SAlexander Motin 		case NGM_CAR_GETCLR_STATS:
393c97bf8c3SAlexander Motin 			{
394c97bf8c3SAlexander Motin 				struct ng_car_bulkstats *bstats;
395c97bf8c3SAlexander Motin 
396c97bf8c3SAlexander Motin 				NG_MKRESPONSE(resp, msg,
397c97bf8c3SAlexander Motin 					sizeof(*bstats), M_NOWAIT);
398c97bf8c3SAlexander Motin 				if (resp == NULL) {
399c97bf8c3SAlexander Motin 					error = ENOMEM;
400c97bf8c3SAlexander Motin 					break;
401c97bf8c3SAlexander Motin 				}
402c97bf8c3SAlexander Motin 				bstats = (struct ng_car_bulkstats *)resp->data;
403c97bf8c3SAlexander Motin 
404c97bf8c3SAlexander Motin 				bcopy(&priv->upper.stats, &bstats->downstream,
405c97bf8c3SAlexander Motin 				    sizeof(bstats->downstream));
406c97bf8c3SAlexander Motin 				bcopy(&priv->lower.stats, &bstats->upstream,
407c97bf8c3SAlexander Motin 				    sizeof(bstats->upstream));
408c97bf8c3SAlexander Motin 			}
409c97bf8c3SAlexander Motin 			if (msg->header.cmd == NGM_CAR_GET_STATS)
410c97bf8c3SAlexander Motin 				break;
411c97bf8c3SAlexander Motin 		case NGM_CAR_CLR_STATS:
412c97bf8c3SAlexander Motin 			bzero(&priv->upper.stats,
413c97bf8c3SAlexander Motin 				sizeof(priv->upper.stats));
414c97bf8c3SAlexander Motin 			bzero(&priv->lower.stats,
415c97bf8c3SAlexander Motin 				sizeof(priv->lower.stats));
416c97bf8c3SAlexander Motin 			break;
417c97bf8c3SAlexander Motin 		case NGM_CAR_GET_CONF:
418c97bf8c3SAlexander Motin 			{
419c97bf8c3SAlexander Motin 				struct ng_car_bulkconf *bconf;
420c97bf8c3SAlexander Motin 
421c97bf8c3SAlexander Motin 				NG_MKRESPONSE(resp, msg,
422c97bf8c3SAlexander Motin 					sizeof(*bconf), M_NOWAIT);
423c97bf8c3SAlexander Motin 				if (resp == NULL) {
424c97bf8c3SAlexander Motin 					error = ENOMEM;
425c97bf8c3SAlexander Motin 					break;
426c97bf8c3SAlexander Motin 				}
427c97bf8c3SAlexander Motin 				bconf = (struct ng_car_bulkconf *)resp->data;
428c97bf8c3SAlexander Motin 
429c97bf8c3SAlexander Motin 				bcopy(&priv->upper.conf, &bconf->downstream,
430c97bf8c3SAlexander Motin 				    sizeof(bconf->downstream));
431c97bf8c3SAlexander Motin 				bcopy(&priv->lower.conf, &bconf->upstream,
432c97bf8c3SAlexander Motin 				    sizeof(bconf->upstream));
433c97bf8c3SAlexander Motin 			}
434c97bf8c3SAlexander Motin 			break;
435c97bf8c3SAlexander Motin 		case NGM_CAR_SET_CONF:
436c97bf8c3SAlexander Motin 			{
437c97bf8c3SAlexander Motin 				struct ng_car_bulkconf *const bconf =
438c97bf8c3SAlexander Motin 				(struct ng_car_bulkconf *)msg->data;
439c97bf8c3SAlexander Motin 
440c97bf8c3SAlexander Motin 				/* Check for invalid or illegal config. */
441c97bf8c3SAlexander Motin 				if ((msg->header.arglen != sizeof(*bconf))
442c97bf8c3SAlexander Motin 				    || (bconf->downstream.cir > 1000000000)
443c97bf8c3SAlexander Motin 				    || (bconf->downstream.pir > 1000000000)
444c97bf8c3SAlexander Motin 				    || (bconf->upstream.cir > 1000000000)
445c97bf8c3SAlexander Motin 				    || (bconf->upstream.pir > 1000000000)
446c97bf8c3SAlexander Motin 				    || (bconf->downstream.cbs == 0
447c97bf8c3SAlexander Motin 					&& bconf->downstream.ebs == 0)
448c97bf8c3SAlexander Motin 				    || (bconf->upstream.cbs == 0
449c97bf8c3SAlexander Motin 					&& bconf->upstream.ebs == 0))
450c97bf8c3SAlexander Motin 				{
451c97bf8c3SAlexander Motin 					error = EINVAL;
452c97bf8c3SAlexander Motin 					break;
453c97bf8c3SAlexander Motin 				}
454c97bf8c3SAlexander Motin 
455c97bf8c3SAlexander Motin 				/* Copy downstream config. */
456c97bf8c3SAlexander Motin 				bcopy(&bconf->downstream, &priv->upper.conf,
457c97bf8c3SAlexander Motin 				    sizeof(priv->upper.conf));
458c97bf8c3SAlexander Motin     				priv->upper.tc = priv->upper.conf.cbs;
459c97bf8c3SAlexander Motin 				if (priv->upper.conf.mode == NG_CAR_RED ||
460b50ace73SAlexander Motin 				    priv->upper.conf.mode == NG_CAR_SHAPE) {
461c97bf8c3SAlexander Motin 					priv->upper.te = 0;
462c97bf8c3SAlexander Motin 				} else {
463c97bf8c3SAlexander Motin 					priv->upper.te = priv->upper.conf.ebs;
464c97bf8c3SAlexander Motin 				}
465c97bf8c3SAlexander Motin 
466c97bf8c3SAlexander Motin 				/* Copy upstream config. */
467c97bf8c3SAlexander Motin 				bcopy(&bconf->upstream, &priv->lower.conf,
468c97bf8c3SAlexander Motin 				    sizeof(priv->lower.conf));
469c97bf8c3SAlexander Motin     				priv->lower.tc = priv->lower.conf.cbs;
470c97bf8c3SAlexander Motin 				if (priv->lower.conf.mode == NG_CAR_RED ||
471c97bf8c3SAlexander Motin 				    priv->lower.conf.mode == NG_CAR_SHAPE) {
472c97bf8c3SAlexander Motin 					priv->lower.te = 0;
473c97bf8c3SAlexander Motin 				} else {
474c97bf8c3SAlexander Motin 					priv->lower.te = priv->lower.conf.ebs;
475c97bf8c3SAlexander Motin 				}
476c97bf8c3SAlexander Motin 			}
477c97bf8c3SAlexander Motin 			break;
478c97bf8c3SAlexander Motin 		default:
479c97bf8c3SAlexander Motin 			error = EINVAL;
480c97bf8c3SAlexander Motin 			break;
481c97bf8c3SAlexander Motin 		}
482c97bf8c3SAlexander Motin 		break;
483c97bf8c3SAlexander Motin 	default:
484c97bf8c3SAlexander Motin 		error = EINVAL;
485c97bf8c3SAlexander Motin 		break;
486c97bf8c3SAlexander Motin 	}
487c97bf8c3SAlexander Motin 	NG_RESPOND_MSG(error, node, item, resp);
488c97bf8c3SAlexander Motin 	NG_FREE_MSG(msg);
489c97bf8c3SAlexander Motin 	return (error);
490c97bf8c3SAlexander Motin }
491c97bf8c3SAlexander Motin 
492c97bf8c3SAlexander Motin /*
493c97bf8c3SAlexander Motin  * Do local shutdown processing.
494c97bf8c3SAlexander Motin  */
495c97bf8c3SAlexander Motin static int
496c97bf8c3SAlexander Motin ng_car_shutdown(node_p node)
497c97bf8c3SAlexander Motin {
498c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
499c97bf8c3SAlexander Motin 
500df01e689SAlexander Motin 	ng_uncallout(&priv->upper.q_callout, node);
501df01e689SAlexander Motin 	ng_uncallout(&priv->lower.q_callout, node);
502c97bf8c3SAlexander Motin 	mtx_destroy(&priv->upper.q_mtx);
503c97bf8c3SAlexander Motin 	mtx_destroy(&priv->lower.q_mtx);
504c97bf8c3SAlexander Motin 	NG_NODE_UNREF(priv->node);
505ae1be01fSAlexander Motin 	free(priv, M_NETGRAPH);
506c97bf8c3SAlexander Motin 	return (0);
507c97bf8c3SAlexander Motin }
508c97bf8c3SAlexander Motin 
509c97bf8c3SAlexander Motin /*
510c97bf8c3SAlexander Motin  * Hook disconnection.
511c97bf8c3SAlexander Motin  *
512c97bf8c3SAlexander Motin  * For this type, removal of the last link destroys the node.
513c97bf8c3SAlexander Motin  */
514c97bf8c3SAlexander Motin static int
515c97bf8c3SAlexander Motin ng_car_disconnect(hook_p hook)
516c97bf8c3SAlexander Motin {
517c97bf8c3SAlexander Motin 	struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
518c97bf8c3SAlexander Motin 	const node_p node = NG_HOOK_NODE(hook);
519c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
520c97bf8c3SAlexander Motin 
521c97bf8c3SAlexander Motin 	if (hinfo) {
522c97bf8c3SAlexander Motin 		/* Purge queue if not empty. */
523c97bf8c3SAlexander Motin 		while (hinfo->q_first != hinfo->q_last) {
524c97bf8c3SAlexander Motin 			NG_FREE_M(hinfo->q[hinfo->q_first]);
525c97bf8c3SAlexander Motin 			hinfo->q_first++;
526c97bf8c3SAlexander Motin 			if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
527c97bf8c3SAlexander Motin 		    		hinfo->q_first = 0;
528c97bf8c3SAlexander Motin 		}
529c97bf8c3SAlexander Motin 		/* Remove hook refs. */
530c97bf8c3SAlexander Motin 		if (hinfo->hook == priv->upper.hook)
531c97bf8c3SAlexander Motin 			priv->lower.dest = NULL;
532c97bf8c3SAlexander Motin 		else
533c97bf8c3SAlexander Motin 			priv->upper.dest = NULL;
534c97bf8c3SAlexander Motin 		hinfo->hook = NULL;
535c97bf8c3SAlexander Motin 	}
536c97bf8c3SAlexander Motin 	/* Already shutting down? */
537c97bf8c3SAlexander Motin 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
538c97bf8c3SAlexander Motin 	    && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
539c97bf8c3SAlexander Motin 		ng_rmnode_self(NG_HOOK_NODE(hook));
540c97bf8c3SAlexander Motin 	return (0);
541c97bf8c3SAlexander Motin }
542c97bf8c3SAlexander Motin 
543c97bf8c3SAlexander Motin /*
544c97bf8c3SAlexander Motin  * Hook's token buckets refillment.
545c97bf8c3SAlexander Motin  */
546c97bf8c3SAlexander Motin static void
547c97bf8c3SAlexander Motin ng_car_refillhook(struct hookinfo *h)
548c97bf8c3SAlexander Motin {
549c97bf8c3SAlexander Motin 	struct timeval newt, deltat;
550c97bf8c3SAlexander Motin 	int64_t deltat_us;
551c97bf8c3SAlexander Motin 	int64_t	delta;
552c97bf8c3SAlexander Motin 
553c97bf8c3SAlexander Motin 	/* Get current time. */
554c97bf8c3SAlexander Motin 	getmicrotime(&newt);
555c97bf8c3SAlexander Motin 
556c97bf8c3SAlexander Motin 	/* Time must go forward. */
557c97bf8c3SAlexander Motin 	if (timevalcmp(&newt, &h->lastRefill, <= )) {
558c97bf8c3SAlexander Motin 	    h->lastRefill = newt;
559c97bf8c3SAlexander Motin 	    return;
560c97bf8c3SAlexander Motin 	}
561c97bf8c3SAlexander Motin 
562c97bf8c3SAlexander Motin 	/* Get time delta since last refill. */
563c97bf8c3SAlexander Motin 	deltat = newt;
564c97bf8c3SAlexander Motin 	timevalsub(&deltat, &h->lastRefill);
565c97bf8c3SAlexander Motin 
566c97bf8c3SAlexander Motin 	/* Sanity check */
567c97bf8c3SAlexander Motin 	if (deltat.tv_sec > 1000) {
568c97bf8c3SAlexander Motin 	    deltat_us = 1000000000;
569c97bf8c3SAlexander Motin 	} else {
570c97bf8c3SAlexander Motin 	    deltat_us = ((int64_t)deltat.tv_sec) * 1000000 + deltat.tv_usec;
571c97bf8c3SAlexander Motin 	}
572c97bf8c3SAlexander Motin 
573c97bf8c3SAlexander Motin 	if (h->conf.mode == NG_CAR_SINGLE_RATE) {
574c97bf8c3SAlexander Motin 		/* Refill commited token bucket. */
575c97bf8c3SAlexander Motin 		h->tc += h->conf.cir * deltat_us / 8000000;
576c97bf8c3SAlexander Motin 		delta = h->tc - h->conf.cbs;
577c97bf8c3SAlexander Motin 		if (delta > 0) {
578c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
579c97bf8c3SAlexander Motin 
580c97bf8c3SAlexander Motin 			/* Refill exceeded token bucket. */
581c97bf8c3SAlexander Motin 			h->te += delta;
582c97bf8c3SAlexander Motin 			if (h->te > h->conf.ebs)
583c97bf8c3SAlexander Motin 				h->te = h->conf.ebs;
584c97bf8c3SAlexander Motin 		}
585c97bf8c3SAlexander Motin 
586c97bf8c3SAlexander Motin 	} else if (h->conf.mode == NG_CAR_DOUBLE_RATE) {
587c97bf8c3SAlexander Motin 		/* Refill commited token bucket. */
588c97bf8c3SAlexander Motin 		h->tc += h->conf.cir * deltat_us / 8000000;
589c97bf8c3SAlexander Motin 		if (h->tc > h->conf.cbs)
590c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
591c97bf8c3SAlexander Motin 
592c97bf8c3SAlexander Motin 		/* Refill peak token bucket. */
593c97bf8c3SAlexander Motin 		h->te += h->conf.pir * deltat_us / 8000000;
594c97bf8c3SAlexander Motin 		if (h->te > h->conf.ebs)
595c97bf8c3SAlexander Motin 			h->te = h->conf.ebs;
596c97bf8c3SAlexander Motin 
597c97bf8c3SAlexander Motin 	} else { /* RED or SHAPE mode. */
598c97bf8c3SAlexander Motin 		/* Refill commited token bucket. */
599c97bf8c3SAlexander Motin 		h->tc += h->conf.cir * deltat_us / 8000000;
600c97bf8c3SAlexander Motin 		if (h->tc > ((int64_t)h->conf.cbs))
601c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
602c97bf8c3SAlexander Motin 	}
603c97bf8c3SAlexander Motin 
604c97bf8c3SAlexander Motin 	/* Remember this moment. */
605c97bf8c3SAlexander Motin 	h->lastRefill = newt;
606c97bf8c3SAlexander Motin }
607c97bf8c3SAlexander Motin 
608c97bf8c3SAlexander Motin /*
609c97bf8c3SAlexander Motin  * Schedule callout when we will have required tokens.
610c97bf8c3SAlexander Motin  */
611c97bf8c3SAlexander Motin static void
612c97bf8c3SAlexander Motin ng_car_schedule(struct hookinfo *hinfo)
613c97bf8c3SAlexander Motin {
614c97bf8c3SAlexander Motin 	int 	delay;
615c97bf8c3SAlexander Motin 
616c97bf8c3SAlexander Motin 	delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1;
617c97bf8c3SAlexander Motin 
618c97bf8c3SAlexander Motin 	ng_callout(&hinfo->q_callout, NG_HOOK_NODE(hinfo->hook), hinfo->hook,
619c97bf8c3SAlexander Motin 	    delay, &ng_car_q_event, NULL, 0);
620c97bf8c3SAlexander Motin }
621c97bf8c3SAlexander Motin 
622c97bf8c3SAlexander Motin /*
623c97bf8c3SAlexander Motin  * Queue processing callout handler.
624c97bf8c3SAlexander Motin  */
625c97bf8c3SAlexander Motin void
626c97bf8c3SAlexander Motin ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2)
627c97bf8c3SAlexander Motin {
628c97bf8c3SAlexander Motin 	struct hookinfo	*hinfo = NG_HOOK_PRIVATE(hook);
629c97bf8c3SAlexander Motin 	item_p 		item;
630c97bf8c3SAlexander Motin 	struct mbuf 	*m;
631c97bf8c3SAlexander Motin 	int		error;
632c97bf8c3SAlexander Motin 
633c97bf8c3SAlexander Motin 	/* Refill tokens for time we have slept. */
634c97bf8c3SAlexander Motin 	ng_car_refillhook(hinfo);
635c97bf8c3SAlexander Motin 
636c97bf8c3SAlexander Motin 	if (hinfo->dest != NULL) {
637c97bf8c3SAlexander Motin 		/* If we have some tokens */
638c97bf8c3SAlexander Motin 		while (hinfo->tc >= 0) {
639c97bf8c3SAlexander Motin 
640c97bf8c3SAlexander Motin 			/* Send packet. */
641c97bf8c3SAlexander Motin 			m = hinfo->q[hinfo->q_first];
642ae1be01fSAlexander Motin 			if ((item = ng_package_data(m, NG_NOFLAGS)) != NULL)
643c97bf8c3SAlexander Motin 		    		NG_FWD_ITEM_HOOK(error, item, hinfo->dest);
644c97bf8c3SAlexander Motin 
645c97bf8c3SAlexander Motin 			/* Get next one. */
646c97bf8c3SAlexander Motin 			hinfo->q_first++;
647c97bf8c3SAlexander Motin 			if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
648c97bf8c3SAlexander Motin 				hinfo->q_first = 0;
649c97bf8c3SAlexander Motin 
650c97bf8c3SAlexander Motin 			/* Stop if none left. */
651c97bf8c3SAlexander Motin 			if (hinfo->q_first == hinfo->q_last)
652c97bf8c3SAlexander Motin 				break;
653c97bf8c3SAlexander Motin 
654c97bf8c3SAlexander Motin 			/* If we have more packet, try it. */
655c97bf8c3SAlexander Motin 			m = hinfo->q[hinfo->q_first];
656c97bf8c3SAlexander Motin 			hinfo->tc -= m->m_pkthdr.len;
657c97bf8c3SAlexander Motin 		}
658c97bf8c3SAlexander Motin 	}
659c97bf8c3SAlexander Motin 
660c97bf8c3SAlexander Motin 	/* If something left */
661ae1be01fSAlexander Motin 	if (hinfo->q_first != hinfo->q_last)
662ae1be01fSAlexander Motin 		/* Schedule queue processing. */
663c97bf8c3SAlexander Motin 		ng_car_schedule(hinfo);
664c97bf8c3SAlexander Motin }
665c97bf8c3SAlexander Motin 
666c97bf8c3SAlexander Motin /*
667c97bf8c3SAlexander Motin  * Enqueue packet.
668c97bf8c3SAlexander Motin  */
669c97bf8c3SAlexander Motin static void
670c97bf8c3SAlexander Motin ng_car_enqueue(struct hookinfo *hinfo, item_p item)
671c97bf8c3SAlexander Motin {
672c97bf8c3SAlexander Motin 	struct mbuf 	*m;
673c97bf8c3SAlexander Motin 	int		len;
674c97bf8c3SAlexander Motin 
675c97bf8c3SAlexander Motin 	NGI_GET_M(item, m);
676c97bf8c3SAlexander Motin 	NG_FREE_ITEM(item);
677c97bf8c3SAlexander Motin 
678ae1be01fSAlexander Motin 	/* Lock queue mutex. */
679c97bf8c3SAlexander Motin 	mtx_lock(&hinfo->q_mtx);
680c97bf8c3SAlexander Motin 
681ae1be01fSAlexander Motin 	/* Calculate used queue length. */
682c97bf8c3SAlexander Motin 	len = hinfo->q_last - hinfo->q_first;
683c97bf8c3SAlexander Motin 	if (len < 0)
684c97bf8c3SAlexander Motin 		len += NG_CAR_QUEUE_SIZE;
685c97bf8c3SAlexander Motin 
686ae1be01fSAlexander Motin 	/* If queue is overflowed or we have no RED tokens. */
687c97bf8c3SAlexander Motin 	if ((len >= (NG_CAR_QUEUE_SIZE - 1)) ||
688c97bf8c3SAlexander Motin 	    (hinfo->te + len >= NG_CAR_QUEUE_SIZE)) {
689ae1be01fSAlexander Motin 		/* Drop packet. */
690c97bf8c3SAlexander Motin 		++hinfo->stats.red_pkts;
691c97bf8c3SAlexander Motin 		NG_FREE_M(m);
692c97bf8c3SAlexander Motin 
693c97bf8c3SAlexander Motin 		hinfo->te = 0;
694c97bf8c3SAlexander Motin 	} else {
695c97bf8c3SAlexander Motin 		/* This packet is yellow. */
696c97bf8c3SAlexander Motin 		++hinfo->stats.yellow_pkts;
697c97bf8c3SAlexander Motin 
698c97bf8c3SAlexander Motin 		/* Enqueue packet. */
699c97bf8c3SAlexander Motin 		hinfo->q[hinfo->q_last] = m;
700c97bf8c3SAlexander Motin 		hinfo->q_last++;
701c97bf8c3SAlexander Motin 		if (hinfo->q_last >= NG_CAR_QUEUE_SIZE)
702c97bf8c3SAlexander Motin 			hinfo->q_last = 0;
703c97bf8c3SAlexander Motin 
704c97bf8c3SAlexander Motin 		/* Use RED tokens. */
705c97bf8c3SAlexander Motin 		if (len > NG_CAR_QUEUE_MIN_TH)
706c97bf8c3SAlexander Motin 			hinfo->te += len - NG_CAR_QUEUE_MIN_TH;
707c97bf8c3SAlexander Motin 
708ae1be01fSAlexander Motin 		/* If this is a first packet in the queue. */
709c97bf8c3SAlexander Motin 		if (len == 0) {
710c97bf8c3SAlexander Motin 			hinfo->tc -= m->m_pkthdr.len;
711c97bf8c3SAlexander Motin 
712ae1be01fSAlexander Motin 			/* Schedule queue processing. */
713c97bf8c3SAlexander Motin 			ng_car_schedule(hinfo);
714c97bf8c3SAlexander Motin 		}
715c97bf8c3SAlexander Motin 	}
716c97bf8c3SAlexander Motin 
717ae1be01fSAlexander Motin 	/* Unlock queue mutex. */
718c97bf8c3SAlexander Motin 	mtx_unlock(&hinfo->q_mtx);
719c97bf8c3SAlexander Motin }
720