xref: /freebsd/sys/netgraph/ng_car.c (revision 673f5a8b44fb98a78f6c4743b7177382ab5c5b07)
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;
266673f5a8bSAlexander Motin 	int len;
267c97bf8c3SAlexander Motin 
268c97bf8c3SAlexander Motin 	/* Node is useless without destination hook. */
269c97bf8c3SAlexander Motin 	if (dest == NULL) {
270c97bf8c3SAlexander Motin 		NG_FREE_ITEM(item);
271c97bf8c3SAlexander Motin 		++hinfo->stats.errors;
272c97bf8c3SAlexander Motin 		return(EINVAL);
273c97bf8c3SAlexander Motin 	}
274c97bf8c3SAlexander Motin 
275c97bf8c3SAlexander Motin 	/* If queue is not empty now then enqueue packet. */
276c97bf8c3SAlexander Motin 	if (hinfo->q_first != hinfo->q_last) {
277c97bf8c3SAlexander Motin 		ng_car_enqueue(hinfo, item);
278c97bf8c3SAlexander Motin 		return (0);
279c97bf8c3SAlexander Motin 	}
280c97bf8c3SAlexander Motin 
281c97bf8c3SAlexander Motin 	m = NGI_M(item);
282c97bf8c3SAlexander Motin 
283c97bf8c3SAlexander Motin #define NG_CAR_PERFORM_MATCH_ACTION(a)			\
284c97bf8c3SAlexander Motin 	do {						\
285c97bf8c3SAlexander Motin 		switch (a) {				\
286c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_FORWARD:		\
287ae1be01fSAlexander Motin 			/* Do nothing. */		\
288c97bf8c3SAlexander Motin 			break;				\
289c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_MARK:		\
290c97bf8c3SAlexander Motin 			/* XXX find a way to mark packets (mbuf tag?) */ \
291c97bf8c3SAlexander Motin 			++hinfo->stats.errors;		\
292c97bf8c3SAlexander Motin 			break;				\
293c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_DROP:		\
294c97bf8c3SAlexander Motin 		default:				\
295ae1be01fSAlexander Motin 			/* Drop packet and return. */	\
296c97bf8c3SAlexander Motin 			NG_FREE_ITEM(item);		\
297c97bf8c3SAlexander Motin 			++hinfo->stats.droped_pkts;	\
298ae1be01fSAlexander Motin 			return (0);			\
299c97bf8c3SAlexander Motin 		}					\
300ae1be01fSAlexander Motin 	} while (0)
301c97bf8c3SAlexander Motin 
302673f5a8bSAlexander Motin 	/* Packet is counted as 128 tokens for better resolution */
303673f5a8bSAlexander Motin 	if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
304673f5a8bSAlexander Motin 		len = 128;
305673f5a8bSAlexander Motin 	} else {
306673f5a8bSAlexander Motin 		len = m->m_pkthdr.len;
307673f5a8bSAlexander Motin 	}
308673f5a8bSAlexander Motin 
309c97bf8c3SAlexander Motin 	/* Check commited token bucket. */
310673f5a8bSAlexander Motin 	if (hinfo->tc - len >= 0) {
311ae1be01fSAlexander Motin 		/* This packet is green. */
312c97bf8c3SAlexander Motin 		++hinfo->stats.green_pkts;
313673f5a8bSAlexander Motin 		hinfo->tc -= len;
314c97bf8c3SAlexander Motin 		NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action);
315c97bf8c3SAlexander Motin 	} else {
316c97bf8c3SAlexander Motin 
317c97bf8c3SAlexander Motin 		/* Refill only if not green without it. */
318c97bf8c3SAlexander Motin 		ng_car_refillhook(hinfo);
319c97bf8c3SAlexander Motin 
320c97bf8c3SAlexander Motin 		 /* Check commited token bucket again after refill. */
321673f5a8bSAlexander Motin 		if (hinfo->tc - len >= 0) {
322c97bf8c3SAlexander Motin 			/* This packet is green */
323c97bf8c3SAlexander Motin 			++hinfo->stats.green_pkts;
324673f5a8bSAlexander Motin 			hinfo->tc -= len;
325c97bf8c3SAlexander Motin 			NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action);
326c97bf8c3SAlexander Motin 
327c97bf8c3SAlexander Motin 		/* If not green and mode is SHAPE, enqueue packet. */
328c97bf8c3SAlexander Motin 		} else if (hinfo->conf.mode == NG_CAR_SHAPE) {
329c97bf8c3SAlexander Motin 			ng_car_enqueue(hinfo, item);
330c97bf8c3SAlexander Motin 			return (0);
331c97bf8c3SAlexander Motin 
332c97bf8c3SAlexander Motin 		/* If not green and mode is RED, calculate probability. */
333c97bf8c3SAlexander Motin 		} else if (hinfo->conf.mode == NG_CAR_RED) {
334c97bf8c3SAlexander Motin 			/* Is packet is bigger then extended burst? */
335673f5a8bSAlexander Motin 			if (len - (hinfo->tc - len) > hinfo->conf.ebs) {
336ae1be01fSAlexander Motin 				/* This packet is definitely red. */
337c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
338c97bf8c3SAlexander Motin 				hinfo->te = 0;
339c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
340c97bf8c3SAlexander Motin 
341ae1be01fSAlexander Motin 			/* Use token bucket to simulate RED-like drop
342ae1be01fSAlexander Motin 			   probability. */
343673f5a8bSAlexander Motin 			} else if (hinfo->te + (len - hinfo->tc) <
344ae1be01fSAlexander Motin 			    hinfo->conf.ebs) {
345c97bf8c3SAlexander Motin 				/* This packet is yellow */
346c97bf8c3SAlexander Motin 				++hinfo->stats.yellow_pkts;
347673f5a8bSAlexander Motin 				hinfo->te += len - hinfo->tc;
348ae1be01fSAlexander Motin 				/* Go to negative tokens. */
349673f5a8bSAlexander Motin 				hinfo->tc -= len;
350c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action);
351c97bf8c3SAlexander Motin 			} else {
352ae1be01fSAlexander Motin 				/* This packet is probaly red. */
353c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
354c97bf8c3SAlexander Motin 				hinfo->te = 0;
355c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
356c97bf8c3SAlexander Motin 			}
357c97bf8c3SAlexander Motin 		/* If not green and mode is SINGLE/DOUBLE RATE. */
358c97bf8c3SAlexander Motin 		} else {
359c97bf8c3SAlexander Motin 			/* Check extended token bucket. */
360673f5a8bSAlexander Motin 			if (hinfo->te - len >= 0) {
361c97bf8c3SAlexander Motin 				/* This packet is yellow */
362c97bf8c3SAlexander Motin 				++hinfo->stats.yellow_pkts;
363673f5a8bSAlexander Motin 				hinfo->te -= len;
364c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action);
365c97bf8c3SAlexander Motin 			} else {
366c97bf8c3SAlexander Motin 				/* This packet is red */
367c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
368c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
369c97bf8c3SAlexander Motin 			}
370c97bf8c3SAlexander Motin 		}
371c97bf8c3SAlexander Motin 	}
372c97bf8c3SAlexander Motin 
373c97bf8c3SAlexander Motin #undef NG_CAR_PERFORM_MATCH_ACTION
374c97bf8c3SAlexander Motin 
375c97bf8c3SAlexander Motin 	NG_FWD_ITEM_HOOK(error, item, dest);
376c97bf8c3SAlexander Motin 	if (error != 0)
377c97bf8c3SAlexander Motin 		++hinfo->stats.errors;
378c97bf8c3SAlexander Motin 	++hinfo->stats.passed_pkts;
379c97bf8c3SAlexander Motin 
380ae1be01fSAlexander Motin 	return (error);
381c97bf8c3SAlexander Motin }
382c97bf8c3SAlexander Motin 
383c97bf8c3SAlexander Motin /*
384ae1be01fSAlexander Motin  * Receive a control message.
385c97bf8c3SAlexander Motin  */
386c97bf8c3SAlexander Motin static int
387c97bf8c3SAlexander Motin ng_car_rcvmsg(node_p node, item_p item, hook_p lasthook)
388c97bf8c3SAlexander Motin {
389c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
390c97bf8c3SAlexander Motin 	struct ng_mesg *resp = NULL;
391c97bf8c3SAlexander Motin 	int error = 0;
392c97bf8c3SAlexander Motin 	struct ng_mesg *msg;
393c97bf8c3SAlexander Motin 
394c97bf8c3SAlexander Motin 	NGI_GET_MSG(item, msg);
395c97bf8c3SAlexander Motin 	switch (msg->header.typecookie) {
396c97bf8c3SAlexander Motin 	case NGM_CAR_COOKIE:
397c97bf8c3SAlexander Motin 		switch (msg->header.cmd) {
398c97bf8c3SAlexander Motin 		case NGM_CAR_GET_STATS:
399c97bf8c3SAlexander Motin 		case NGM_CAR_GETCLR_STATS:
400c97bf8c3SAlexander Motin 			{
401c97bf8c3SAlexander Motin 				struct ng_car_bulkstats *bstats;
402c97bf8c3SAlexander Motin 
403c97bf8c3SAlexander Motin 				NG_MKRESPONSE(resp, msg,
404c97bf8c3SAlexander Motin 					sizeof(*bstats), M_NOWAIT);
405c97bf8c3SAlexander Motin 				if (resp == NULL) {
406c97bf8c3SAlexander Motin 					error = ENOMEM;
407c97bf8c3SAlexander Motin 					break;
408c97bf8c3SAlexander Motin 				}
409c97bf8c3SAlexander Motin 				bstats = (struct ng_car_bulkstats *)resp->data;
410c97bf8c3SAlexander Motin 
411c97bf8c3SAlexander Motin 				bcopy(&priv->upper.stats, &bstats->downstream,
412c97bf8c3SAlexander Motin 				    sizeof(bstats->downstream));
413c97bf8c3SAlexander Motin 				bcopy(&priv->lower.stats, &bstats->upstream,
414c97bf8c3SAlexander Motin 				    sizeof(bstats->upstream));
415c97bf8c3SAlexander Motin 			}
416c97bf8c3SAlexander Motin 			if (msg->header.cmd == NGM_CAR_GET_STATS)
417c97bf8c3SAlexander Motin 				break;
418c97bf8c3SAlexander Motin 		case NGM_CAR_CLR_STATS:
419c97bf8c3SAlexander Motin 			bzero(&priv->upper.stats,
420c97bf8c3SAlexander Motin 				sizeof(priv->upper.stats));
421c97bf8c3SAlexander Motin 			bzero(&priv->lower.stats,
422c97bf8c3SAlexander Motin 				sizeof(priv->lower.stats));
423c97bf8c3SAlexander Motin 			break;
424c97bf8c3SAlexander Motin 		case NGM_CAR_GET_CONF:
425c97bf8c3SAlexander Motin 			{
426c97bf8c3SAlexander Motin 				struct ng_car_bulkconf *bconf;
427c97bf8c3SAlexander Motin 
428c97bf8c3SAlexander Motin 				NG_MKRESPONSE(resp, msg,
429c97bf8c3SAlexander Motin 					sizeof(*bconf), M_NOWAIT);
430c97bf8c3SAlexander Motin 				if (resp == NULL) {
431c97bf8c3SAlexander Motin 					error = ENOMEM;
432c97bf8c3SAlexander Motin 					break;
433c97bf8c3SAlexander Motin 				}
434c97bf8c3SAlexander Motin 				bconf = (struct ng_car_bulkconf *)resp->data;
435c97bf8c3SAlexander Motin 
436c97bf8c3SAlexander Motin 				bcopy(&priv->upper.conf, &bconf->downstream,
437c97bf8c3SAlexander Motin 				    sizeof(bconf->downstream));
438c97bf8c3SAlexander Motin 				bcopy(&priv->lower.conf, &bconf->upstream,
439c97bf8c3SAlexander Motin 				    sizeof(bconf->upstream));
440673f5a8bSAlexander Motin 				/* Convert internal 1/(8*128) of pps into pps */
441673f5a8bSAlexander Motin 				if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) {
442673f5a8bSAlexander Motin 				    bconf->downstream.cir /= 1024;
443673f5a8bSAlexander Motin 				    bconf->downstream.pir /= 1024;
444673f5a8bSAlexander Motin 				    bconf->downstream.cbs /= 128;
445673f5a8bSAlexander Motin 				    bconf->downstream.ebs /= 128;
446673f5a8bSAlexander Motin 				}
447673f5a8bSAlexander Motin 				if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) {
448673f5a8bSAlexander Motin 				    bconf->upstream.cir /= 1024;
449673f5a8bSAlexander Motin 				    bconf->upstream.pir /= 1024;
450673f5a8bSAlexander Motin 				    bconf->upstream.cbs /= 128;
451673f5a8bSAlexander Motin 				    bconf->upstream.ebs /= 128;
452673f5a8bSAlexander Motin 				}
453c97bf8c3SAlexander Motin 			}
454c97bf8c3SAlexander Motin 			break;
455c97bf8c3SAlexander Motin 		case NGM_CAR_SET_CONF:
456c97bf8c3SAlexander Motin 			{
457c97bf8c3SAlexander Motin 				struct ng_car_bulkconf *const bconf =
458c97bf8c3SAlexander Motin 				(struct ng_car_bulkconf *)msg->data;
459c97bf8c3SAlexander Motin 
460c97bf8c3SAlexander Motin 				/* Check for invalid or illegal config. */
461673f5a8bSAlexander Motin 				if (msg->header.arglen != sizeof(*bconf)) {
462673f5a8bSAlexander Motin 					error = EINVAL;
463673f5a8bSAlexander Motin 					break;
464673f5a8bSAlexander Motin 				}
465673f5a8bSAlexander Motin 				/* Convert pps into internal 1/(8*128) of pps */
466673f5a8bSAlexander Motin 				if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) {
467673f5a8bSAlexander Motin 				    bconf->downstream.cir *= 1024;
468673f5a8bSAlexander Motin 				    bconf->downstream.pir *= 1024;
469673f5a8bSAlexander Motin 				    bconf->downstream.cbs *= 125;
470673f5a8bSAlexander Motin 				    bconf->downstream.ebs *= 125;
471673f5a8bSAlexander Motin 				}
472673f5a8bSAlexander Motin 				if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) {
473673f5a8bSAlexander Motin 				    bconf->upstream.cir *= 1024;
474673f5a8bSAlexander Motin 				    bconf->upstream.pir *= 1024;
475673f5a8bSAlexander Motin 				    bconf->upstream.cbs *= 125;
476673f5a8bSAlexander Motin 				    bconf->upstream.ebs *= 125;
477673f5a8bSAlexander Motin 				}
478673f5a8bSAlexander Motin 				if ((bconf->downstream.cir > 1000000000) ||
479673f5a8bSAlexander Motin 				    (bconf->downstream.pir > 1000000000) ||
480673f5a8bSAlexander Motin 				    (bconf->upstream.cir > 1000000000) ||
481673f5a8bSAlexander Motin 				    (bconf->upstream.pir > 1000000000) ||
482673f5a8bSAlexander Motin 				    (bconf->downstream.cbs == 0 &&
483673f5a8bSAlexander Motin 					bconf->downstream.ebs == 0) ||
484673f5a8bSAlexander Motin 				    (bconf->upstream.cbs == 0 &&
485673f5a8bSAlexander Motin 					bconf->upstream.ebs == 0))
486c97bf8c3SAlexander Motin 				{
487c97bf8c3SAlexander Motin 					error = EINVAL;
488c97bf8c3SAlexander Motin 					break;
489c97bf8c3SAlexander Motin 				}
490673f5a8bSAlexander Motin 				if ((bconf->upstream.mode == NG_CAR_SHAPE) &&
491673f5a8bSAlexander Motin 				    (bconf->upstream.cir == 0)) {
492673f5a8bSAlexander Motin 					error = EINVAL;
493673f5a8bSAlexander Motin 					break;
494673f5a8bSAlexander Motin 				}
495673f5a8bSAlexander Motin 				if ((bconf->downstream.mode == NG_CAR_SHAPE) &&
496673f5a8bSAlexander Motin 				    (bconf->downstream.cir == 0)) {
497673f5a8bSAlexander Motin 					error = EINVAL;
498673f5a8bSAlexander Motin 					break;
499673f5a8bSAlexander Motin 				}
500c97bf8c3SAlexander Motin 
501c97bf8c3SAlexander Motin 				/* Copy downstream config. */
502c97bf8c3SAlexander Motin 				bcopy(&bconf->downstream, &priv->upper.conf,
503c97bf8c3SAlexander Motin 				    sizeof(priv->upper.conf));
504c97bf8c3SAlexander Motin     				priv->upper.tc = priv->upper.conf.cbs;
505c97bf8c3SAlexander Motin 				if (priv->upper.conf.mode == NG_CAR_RED ||
506b50ace73SAlexander Motin 				    priv->upper.conf.mode == NG_CAR_SHAPE) {
507c97bf8c3SAlexander Motin 					priv->upper.te = 0;
508c97bf8c3SAlexander Motin 				} else {
509c97bf8c3SAlexander Motin 					priv->upper.te = priv->upper.conf.ebs;
510c97bf8c3SAlexander Motin 				}
511c97bf8c3SAlexander Motin 
512c97bf8c3SAlexander Motin 				/* Copy upstream config. */
513c97bf8c3SAlexander Motin 				bcopy(&bconf->upstream, &priv->lower.conf,
514c97bf8c3SAlexander Motin 				    sizeof(priv->lower.conf));
515c97bf8c3SAlexander Motin     				priv->lower.tc = priv->lower.conf.cbs;
516c97bf8c3SAlexander Motin 				if (priv->lower.conf.mode == NG_CAR_RED ||
517c97bf8c3SAlexander Motin 				    priv->lower.conf.mode == NG_CAR_SHAPE) {
518c97bf8c3SAlexander Motin 					priv->lower.te = 0;
519c97bf8c3SAlexander Motin 				} else {
520c97bf8c3SAlexander Motin 					priv->lower.te = priv->lower.conf.ebs;
521c97bf8c3SAlexander Motin 				}
522c97bf8c3SAlexander Motin 			}
523c97bf8c3SAlexander Motin 			break;
524c97bf8c3SAlexander Motin 		default:
525c97bf8c3SAlexander Motin 			error = EINVAL;
526c97bf8c3SAlexander Motin 			break;
527c97bf8c3SAlexander Motin 		}
528c97bf8c3SAlexander Motin 		break;
529c97bf8c3SAlexander Motin 	default:
530c97bf8c3SAlexander Motin 		error = EINVAL;
531c97bf8c3SAlexander Motin 		break;
532c97bf8c3SAlexander Motin 	}
533c97bf8c3SAlexander Motin 	NG_RESPOND_MSG(error, node, item, resp);
534c97bf8c3SAlexander Motin 	NG_FREE_MSG(msg);
535c97bf8c3SAlexander Motin 	return (error);
536c97bf8c3SAlexander Motin }
537c97bf8c3SAlexander Motin 
538c97bf8c3SAlexander Motin /*
539c97bf8c3SAlexander Motin  * Do local shutdown processing.
540c97bf8c3SAlexander Motin  */
541c97bf8c3SAlexander Motin static int
542c97bf8c3SAlexander Motin ng_car_shutdown(node_p node)
543c97bf8c3SAlexander Motin {
544c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
545c97bf8c3SAlexander Motin 
546df01e689SAlexander Motin 	ng_uncallout(&priv->upper.q_callout, node);
547df01e689SAlexander Motin 	ng_uncallout(&priv->lower.q_callout, node);
548c97bf8c3SAlexander Motin 	mtx_destroy(&priv->upper.q_mtx);
549c97bf8c3SAlexander Motin 	mtx_destroy(&priv->lower.q_mtx);
550c97bf8c3SAlexander Motin 	NG_NODE_UNREF(priv->node);
551ae1be01fSAlexander Motin 	free(priv, M_NETGRAPH);
552c97bf8c3SAlexander Motin 	return (0);
553c97bf8c3SAlexander Motin }
554c97bf8c3SAlexander Motin 
555c97bf8c3SAlexander Motin /*
556c97bf8c3SAlexander Motin  * Hook disconnection.
557c97bf8c3SAlexander Motin  *
558c97bf8c3SAlexander Motin  * For this type, removal of the last link destroys the node.
559c97bf8c3SAlexander Motin  */
560c97bf8c3SAlexander Motin static int
561c97bf8c3SAlexander Motin ng_car_disconnect(hook_p hook)
562c97bf8c3SAlexander Motin {
563c97bf8c3SAlexander Motin 	struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
564c97bf8c3SAlexander Motin 	const node_p node = NG_HOOK_NODE(hook);
565c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
566c97bf8c3SAlexander Motin 
567c97bf8c3SAlexander Motin 	if (hinfo) {
568c97bf8c3SAlexander Motin 		/* Purge queue if not empty. */
569c97bf8c3SAlexander Motin 		while (hinfo->q_first != hinfo->q_last) {
570c97bf8c3SAlexander Motin 			NG_FREE_M(hinfo->q[hinfo->q_first]);
571c97bf8c3SAlexander Motin 			hinfo->q_first++;
572c97bf8c3SAlexander Motin 			if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
573c97bf8c3SAlexander Motin 		    		hinfo->q_first = 0;
574c97bf8c3SAlexander Motin 		}
575c97bf8c3SAlexander Motin 		/* Remove hook refs. */
576c97bf8c3SAlexander Motin 		if (hinfo->hook == priv->upper.hook)
577c97bf8c3SAlexander Motin 			priv->lower.dest = NULL;
578c97bf8c3SAlexander Motin 		else
579c97bf8c3SAlexander Motin 			priv->upper.dest = NULL;
580c97bf8c3SAlexander Motin 		hinfo->hook = NULL;
581c97bf8c3SAlexander Motin 	}
582c97bf8c3SAlexander Motin 	/* Already shutting down? */
583c97bf8c3SAlexander Motin 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
584c97bf8c3SAlexander Motin 	    && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
585c97bf8c3SAlexander Motin 		ng_rmnode_self(NG_HOOK_NODE(hook));
586c97bf8c3SAlexander Motin 	return (0);
587c97bf8c3SAlexander Motin }
588c97bf8c3SAlexander Motin 
589c97bf8c3SAlexander Motin /*
590c97bf8c3SAlexander Motin  * Hook's token buckets refillment.
591c97bf8c3SAlexander Motin  */
592c97bf8c3SAlexander Motin static void
593c97bf8c3SAlexander Motin ng_car_refillhook(struct hookinfo *h)
594c97bf8c3SAlexander Motin {
595c97bf8c3SAlexander Motin 	struct timeval newt, deltat;
596c97bf8c3SAlexander Motin 	int64_t deltat_us;
597c97bf8c3SAlexander Motin 	int64_t	delta;
598c97bf8c3SAlexander Motin 
599c97bf8c3SAlexander Motin 	/* Get current time. */
600c97bf8c3SAlexander Motin 	getmicrotime(&newt);
601c97bf8c3SAlexander Motin 
602c97bf8c3SAlexander Motin 	/* Time must go forward. */
603c97bf8c3SAlexander Motin 	if (timevalcmp(&newt, &h->lastRefill, <= )) {
604c97bf8c3SAlexander Motin 	    h->lastRefill = newt;
605c97bf8c3SAlexander Motin 	    return;
606c97bf8c3SAlexander Motin 	}
607c97bf8c3SAlexander Motin 
608c97bf8c3SAlexander Motin 	/* Get time delta since last refill. */
609c97bf8c3SAlexander Motin 	deltat = newt;
610c97bf8c3SAlexander Motin 	timevalsub(&deltat, &h->lastRefill);
611c97bf8c3SAlexander Motin 
612c97bf8c3SAlexander Motin 	/* Sanity check */
613c97bf8c3SAlexander Motin 	if (deltat.tv_sec > 1000) {
614c97bf8c3SAlexander Motin 	    deltat_us = 1000000000;
615c97bf8c3SAlexander Motin 	} else {
616c97bf8c3SAlexander Motin 	    deltat_us = ((int64_t)deltat.tv_sec) * 1000000 + deltat.tv_usec;
617c97bf8c3SAlexander Motin 	}
618c97bf8c3SAlexander Motin 
619c97bf8c3SAlexander Motin 	if (h->conf.mode == NG_CAR_SINGLE_RATE) {
620c97bf8c3SAlexander Motin 		/* Refill commited token bucket. */
621c97bf8c3SAlexander Motin 		h->tc += h->conf.cir * deltat_us / 8000000;
622c97bf8c3SAlexander Motin 		delta = h->tc - h->conf.cbs;
623c97bf8c3SAlexander Motin 		if (delta > 0) {
624c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
625c97bf8c3SAlexander Motin 
626c97bf8c3SAlexander Motin 			/* Refill exceeded token bucket. */
627c97bf8c3SAlexander Motin 			h->te += delta;
628c97bf8c3SAlexander Motin 			if (h->te > h->conf.ebs)
629c97bf8c3SAlexander Motin 				h->te = h->conf.ebs;
630c97bf8c3SAlexander Motin 		}
631c97bf8c3SAlexander Motin 
632c97bf8c3SAlexander Motin 	} else if (h->conf.mode == NG_CAR_DOUBLE_RATE) {
633c97bf8c3SAlexander Motin 		/* Refill commited token bucket. */
634c97bf8c3SAlexander Motin 		h->tc += h->conf.cir * deltat_us / 8000000;
635c97bf8c3SAlexander Motin 		if (h->tc > h->conf.cbs)
636c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
637c97bf8c3SAlexander Motin 
638c97bf8c3SAlexander Motin 		/* Refill peak token bucket. */
639c97bf8c3SAlexander Motin 		h->te += h->conf.pir * deltat_us / 8000000;
640c97bf8c3SAlexander Motin 		if (h->te > h->conf.ebs)
641c97bf8c3SAlexander Motin 			h->te = h->conf.ebs;
642c97bf8c3SAlexander Motin 
643c97bf8c3SAlexander Motin 	} else { /* RED or SHAPE mode. */
644c97bf8c3SAlexander Motin 		/* Refill commited token bucket. */
645c97bf8c3SAlexander Motin 		h->tc += h->conf.cir * deltat_us / 8000000;
646c97bf8c3SAlexander Motin 		if (h->tc > ((int64_t)h->conf.cbs))
647c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
648c97bf8c3SAlexander Motin 	}
649c97bf8c3SAlexander Motin 
650c97bf8c3SAlexander Motin 	/* Remember this moment. */
651c97bf8c3SAlexander Motin 	h->lastRefill = newt;
652c97bf8c3SAlexander Motin }
653c97bf8c3SAlexander Motin 
654c97bf8c3SAlexander Motin /*
655c97bf8c3SAlexander Motin  * Schedule callout when we will have required tokens.
656c97bf8c3SAlexander Motin  */
657c97bf8c3SAlexander Motin static void
658c97bf8c3SAlexander Motin ng_car_schedule(struct hookinfo *hinfo)
659c97bf8c3SAlexander Motin {
660c97bf8c3SAlexander Motin 	int 	delay;
661c97bf8c3SAlexander Motin 
662c97bf8c3SAlexander Motin 	delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1;
663c97bf8c3SAlexander Motin 
664c97bf8c3SAlexander Motin 	ng_callout(&hinfo->q_callout, NG_HOOK_NODE(hinfo->hook), hinfo->hook,
665c97bf8c3SAlexander Motin 	    delay, &ng_car_q_event, NULL, 0);
666c97bf8c3SAlexander Motin }
667c97bf8c3SAlexander Motin 
668c97bf8c3SAlexander Motin /*
669c97bf8c3SAlexander Motin  * Queue processing callout handler.
670c97bf8c3SAlexander Motin  */
671c97bf8c3SAlexander Motin void
672c97bf8c3SAlexander Motin ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2)
673c97bf8c3SAlexander Motin {
674c97bf8c3SAlexander Motin 	struct hookinfo	*hinfo = NG_HOOK_PRIVATE(hook);
675c97bf8c3SAlexander Motin 	item_p 		item;
676c97bf8c3SAlexander Motin 	struct mbuf 	*m;
677c97bf8c3SAlexander Motin 	int		error;
678c97bf8c3SAlexander Motin 
679c97bf8c3SAlexander Motin 	/* Refill tokens for time we have slept. */
680c97bf8c3SAlexander Motin 	ng_car_refillhook(hinfo);
681c97bf8c3SAlexander Motin 
682c97bf8c3SAlexander Motin 	if (hinfo->dest != NULL) {
683c97bf8c3SAlexander Motin 		/* If we have some tokens */
684c97bf8c3SAlexander Motin 		while (hinfo->tc >= 0) {
685c97bf8c3SAlexander Motin 
686c97bf8c3SAlexander Motin 			/* Send packet. */
687c97bf8c3SAlexander Motin 			m = hinfo->q[hinfo->q_first];
688ae1be01fSAlexander Motin 			if ((item = ng_package_data(m, NG_NOFLAGS)) != NULL)
689c97bf8c3SAlexander Motin 		    		NG_FWD_ITEM_HOOK(error, item, hinfo->dest);
690c97bf8c3SAlexander Motin 
691c97bf8c3SAlexander Motin 			/* Get next one. */
692c97bf8c3SAlexander Motin 			hinfo->q_first++;
693c97bf8c3SAlexander Motin 			if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
694c97bf8c3SAlexander Motin 				hinfo->q_first = 0;
695c97bf8c3SAlexander Motin 
696c97bf8c3SAlexander Motin 			/* Stop if none left. */
697c97bf8c3SAlexander Motin 			if (hinfo->q_first == hinfo->q_last)
698c97bf8c3SAlexander Motin 				break;
699c97bf8c3SAlexander Motin 
700c97bf8c3SAlexander Motin 			/* If we have more packet, try it. */
701c97bf8c3SAlexander Motin 			m = hinfo->q[hinfo->q_first];
702673f5a8bSAlexander Motin 			if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
703673f5a8bSAlexander Motin 				hinfo->tc -= 128;
704673f5a8bSAlexander Motin 			} else {
705c97bf8c3SAlexander Motin 				hinfo->tc -= m->m_pkthdr.len;
706c97bf8c3SAlexander Motin 			}
707c97bf8c3SAlexander Motin 		}
708673f5a8bSAlexander Motin 	}
709c97bf8c3SAlexander Motin 
710c97bf8c3SAlexander Motin 	/* If something left */
711ae1be01fSAlexander Motin 	if (hinfo->q_first != hinfo->q_last)
712ae1be01fSAlexander Motin 		/* Schedule queue processing. */
713c97bf8c3SAlexander Motin 		ng_car_schedule(hinfo);
714c97bf8c3SAlexander Motin }
715c97bf8c3SAlexander Motin 
716c97bf8c3SAlexander Motin /*
717c97bf8c3SAlexander Motin  * Enqueue packet.
718c97bf8c3SAlexander Motin  */
719c97bf8c3SAlexander Motin static void
720c97bf8c3SAlexander Motin ng_car_enqueue(struct hookinfo *hinfo, item_p item)
721c97bf8c3SAlexander Motin {
722c97bf8c3SAlexander Motin 	struct mbuf 	*m;
723c97bf8c3SAlexander Motin 	int		len;
724c97bf8c3SAlexander Motin 
725c97bf8c3SAlexander Motin 	NGI_GET_M(item, m);
726c97bf8c3SAlexander Motin 	NG_FREE_ITEM(item);
727c97bf8c3SAlexander Motin 
728ae1be01fSAlexander Motin 	/* Lock queue mutex. */
729c97bf8c3SAlexander Motin 	mtx_lock(&hinfo->q_mtx);
730c97bf8c3SAlexander Motin 
731ae1be01fSAlexander Motin 	/* Calculate used queue length. */
732c97bf8c3SAlexander Motin 	len = hinfo->q_last - hinfo->q_first;
733c97bf8c3SAlexander Motin 	if (len < 0)
734c97bf8c3SAlexander Motin 		len += NG_CAR_QUEUE_SIZE;
735c97bf8c3SAlexander Motin 
736ae1be01fSAlexander Motin 	/* If queue is overflowed or we have no RED tokens. */
737c97bf8c3SAlexander Motin 	if ((len >= (NG_CAR_QUEUE_SIZE - 1)) ||
738c97bf8c3SAlexander Motin 	    (hinfo->te + len >= NG_CAR_QUEUE_SIZE)) {
739ae1be01fSAlexander Motin 		/* Drop packet. */
740c97bf8c3SAlexander Motin 		++hinfo->stats.red_pkts;
741c97bf8c3SAlexander Motin 		NG_FREE_M(m);
742c97bf8c3SAlexander Motin 
743c97bf8c3SAlexander Motin 		hinfo->te = 0;
744c97bf8c3SAlexander Motin 	} else {
745c97bf8c3SAlexander Motin 		/* This packet is yellow. */
746c97bf8c3SAlexander Motin 		++hinfo->stats.yellow_pkts;
747c97bf8c3SAlexander Motin 
748c97bf8c3SAlexander Motin 		/* Enqueue packet. */
749c97bf8c3SAlexander Motin 		hinfo->q[hinfo->q_last] = m;
750c97bf8c3SAlexander Motin 		hinfo->q_last++;
751c97bf8c3SAlexander Motin 		if (hinfo->q_last >= NG_CAR_QUEUE_SIZE)
752c97bf8c3SAlexander Motin 			hinfo->q_last = 0;
753c97bf8c3SAlexander Motin 
754c97bf8c3SAlexander Motin 		/* Use RED tokens. */
755c97bf8c3SAlexander Motin 		if (len > NG_CAR_QUEUE_MIN_TH)
756c97bf8c3SAlexander Motin 			hinfo->te += len - NG_CAR_QUEUE_MIN_TH;
757c97bf8c3SAlexander Motin 
758ae1be01fSAlexander Motin 		/* If this is a first packet in the queue. */
759c97bf8c3SAlexander Motin 		if (len == 0) {
760673f5a8bSAlexander Motin 			if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
761673f5a8bSAlexander Motin 				hinfo->tc -= 128;
762673f5a8bSAlexander Motin 			} else {
763c97bf8c3SAlexander Motin 				hinfo->tc -= m->m_pkthdr.len;
764673f5a8bSAlexander Motin 			}
765c97bf8c3SAlexander Motin 
766ae1be01fSAlexander Motin 			/* Schedule queue processing. */
767c97bf8c3SAlexander Motin 			ng_car_schedule(hinfo);
768c97bf8c3SAlexander Motin 		}
769c97bf8c3SAlexander Motin 	}
770c97bf8c3SAlexander Motin 
771ae1be01fSAlexander Motin 	/* Unlock queue mutex. */
772c97bf8c3SAlexander Motin 	mtx_unlock(&hinfo->q_mtx);
773c97bf8c3SAlexander Motin }
774