xref: /freebsd/sys/netgraph/ng_car.c (revision fe267a559009cbf34f9341666fe4d88a92c02d5e)
1c97bf8c3SAlexander Motin /*-
2*fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*fe267a55SPedro F. Giffuni  *
4c97bf8c3SAlexander Motin  * Copyright (c) 2005 Nuno Antunes <nuno.antunes@gmail.com>
5c97bf8c3SAlexander Motin  * Copyright (c) 2007 Alexander Motin <mav@freebsd.org>
6c97bf8c3SAlexander Motin  * All rights reserved.
7c97bf8c3SAlexander Motin  *
8c97bf8c3SAlexander Motin  * Redistribution and use in source and binary forms, with or without
9c97bf8c3SAlexander Motin  * modification, are permitted provided that the following conditions
10c97bf8c3SAlexander Motin  * are met:
11c97bf8c3SAlexander Motin  * 1. Redistributions of source code must retain the above copyright
12c97bf8c3SAlexander Motin  *    notice, this list of conditions and the following disclaimer.
13c97bf8c3SAlexander Motin  * 2. Redistributions in binary form must reproduce the above copyright
14c97bf8c3SAlexander Motin  *    notice, this list of conditions and the following disclaimer in the
15c97bf8c3SAlexander Motin  *    documentation and/or other materials provided with the distribution.
16c97bf8c3SAlexander Motin  *
17c97bf8c3SAlexander Motin  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18c97bf8c3SAlexander Motin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19c97bf8c3SAlexander Motin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20c97bf8c3SAlexander Motin  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21c97bf8c3SAlexander Motin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22c97bf8c3SAlexander Motin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23c97bf8c3SAlexander Motin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24c97bf8c3SAlexander Motin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25c97bf8c3SAlexander Motin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26c97bf8c3SAlexander Motin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27c97bf8c3SAlexander Motin  * SUCH DAMAGE.
28c97bf8c3SAlexander Motin  *
29c97bf8c3SAlexander Motin  * $FreeBSD$
30c97bf8c3SAlexander Motin  */
31c97bf8c3SAlexander Motin 
32c97bf8c3SAlexander Motin /*
33053359b7SPedro F. Giffuni  * ng_car - An implementation of committed access rate for netgraph
34c97bf8c3SAlexander Motin  *
35c97bf8c3SAlexander Motin  * TODO:
36c97bf8c3SAlexander Motin  *	- Sanitize input config values (impose some limits)
37c97bf8c3SAlexander Motin  *	- Implement internal packet painting (possibly using mbuf tags)
38c97bf8c3SAlexander Motin  *	- Implement color-aware mode
39c97bf8c3SAlexander Motin  *	- Implement DSCP marking for IPv4
40c97bf8c3SAlexander Motin  */
41c97bf8c3SAlexander Motin 
42c97bf8c3SAlexander Motin #include <sys/param.h>
43c97bf8c3SAlexander Motin #include <sys/errno.h>
44ae1be01fSAlexander Motin #include <sys/kernel.h>
45ae1be01fSAlexander Motin #include <sys/malloc.h>
46ae1be01fSAlexander Motin #include <sys/mbuf.h>
47c97bf8c3SAlexander Motin 
48c97bf8c3SAlexander Motin #include <netgraph/ng_message.h>
49c97bf8c3SAlexander Motin #include <netgraph/ng_parse.h>
50c97bf8c3SAlexander Motin #include <netgraph/netgraph.h>
51c97bf8c3SAlexander Motin #include <netgraph/ng_car.h>
52c97bf8c3SAlexander Motin 
53c97bf8c3SAlexander Motin #define NG_CAR_QUEUE_SIZE	100	/* Maximum queue size for SHAPE mode */
54053359b7SPedro F. Giffuni #define NG_CAR_QUEUE_MIN_TH	8	/* Minimum RED threshold for SHAPE mode */
55c97bf8c3SAlexander Motin 
56c97bf8c3SAlexander Motin /* Hook private info */
57c97bf8c3SAlexander Motin struct hookinfo {
58c97bf8c3SAlexander Motin 	hook_p		hook;		/* this (source) hook */
59c97bf8c3SAlexander Motin 	hook_p		dest;		/* destination hook */
60c97bf8c3SAlexander Motin 
61053359b7SPedro F. Giffuni 	int64_t 	tc;		/* committed token bucket counter */
62c97bf8c3SAlexander Motin 	int64_t 	te;		/* exceeded/peak token bucket counter */
63c86d865eSAlexander Motin 	struct bintime	lastRefill;	/* last token refill time */
64c97bf8c3SAlexander Motin 
65c97bf8c3SAlexander Motin 	struct ng_car_hookconf conf;	/* hook configuration */
66c97bf8c3SAlexander Motin 	struct ng_car_hookstats stats;	/* hook stats */
67c97bf8c3SAlexander Motin 
68c97bf8c3SAlexander Motin 	struct mbuf	*q[NG_CAR_QUEUE_SIZE];	/* circular packet queue */
69c77b232bSAlexander Motin 	u_int		q_first;	/* first queue element */
70c77b232bSAlexander Motin 	u_int		q_last;		/* last queue element */
71c97bf8c3SAlexander Motin 	struct callout	q_callout;	/* periodic queue processing routine */
72c97bf8c3SAlexander Motin 	struct mtx	q_mtx;		/* queue mutex */
73c97bf8c3SAlexander Motin };
74c97bf8c3SAlexander Motin 
75c97bf8c3SAlexander Motin /* Private information for each node instance */
76c97bf8c3SAlexander Motin struct privdata {
77c97bf8c3SAlexander Motin 	node_p node;				/* the node itself */
78c97bf8c3SAlexander Motin 	struct hookinfo upper;			/* hook to upper layers */
79c97bf8c3SAlexander Motin 	struct hookinfo lower;			/* hook to lower layers */
80c97bf8c3SAlexander Motin };
81c97bf8c3SAlexander Motin typedef struct privdata *priv_p;
82c97bf8c3SAlexander Motin 
83c97bf8c3SAlexander Motin static ng_constructor_t	ng_car_constructor;
84c97bf8c3SAlexander Motin static ng_rcvmsg_t	ng_car_rcvmsg;
85c97bf8c3SAlexander Motin static ng_shutdown_t	ng_car_shutdown;
86c97bf8c3SAlexander Motin static ng_newhook_t	ng_car_newhook;
87c97bf8c3SAlexander Motin static ng_rcvdata_t	ng_car_rcvdata;
88c97bf8c3SAlexander Motin static ng_disconnect_t	ng_car_disconnect;
89c97bf8c3SAlexander Motin 
90c97bf8c3SAlexander Motin static void	ng_car_refillhook(struct hookinfo *h);
91c97bf8c3SAlexander Motin static void	ng_car_schedule(struct hookinfo *h);
92c97bf8c3SAlexander Motin void		ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2);
93c97bf8c3SAlexander Motin static void	ng_car_enqueue(struct hookinfo *h, item_p item);
94c97bf8c3SAlexander Motin 
95c97bf8c3SAlexander Motin /* Parse type for struct ng_car_hookstats */
96c97bf8c3SAlexander Motin static const struct ng_parse_struct_field ng_car_hookstats_type_fields[]
97c97bf8c3SAlexander Motin 	= NG_CAR_HOOKSTATS;
98c97bf8c3SAlexander Motin static const struct ng_parse_type ng_car_hookstats_type = {
99c97bf8c3SAlexander Motin 	&ng_parse_struct_type,
100c97bf8c3SAlexander Motin 	&ng_car_hookstats_type_fields
101c97bf8c3SAlexander Motin };
102c97bf8c3SAlexander Motin 
103c97bf8c3SAlexander Motin /* Parse type for struct ng_car_bulkstats */
104c97bf8c3SAlexander Motin static const struct ng_parse_struct_field ng_car_bulkstats_type_fields[]
105c97bf8c3SAlexander Motin 	= NG_CAR_BULKSTATS(&ng_car_hookstats_type);
106c97bf8c3SAlexander Motin static const struct ng_parse_type ng_car_bulkstats_type = {
107c97bf8c3SAlexander Motin 	&ng_parse_struct_type,
108c97bf8c3SAlexander Motin 	&ng_car_bulkstats_type_fields
109c97bf8c3SAlexander Motin };
110c97bf8c3SAlexander Motin 
111c97bf8c3SAlexander Motin /* Parse type for struct ng_car_hookconf */
112c97bf8c3SAlexander Motin static const struct ng_parse_struct_field ng_car_hookconf_type_fields[]
113c97bf8c3SAlexander Motin 	= NG_CAR_HOOKCONF;
114c97bf8c3SAlexander Motin static const struct ng_parse_type ng_car_hookconf_type = {
115c97bf8c3SAlexander Motin 	&ng_parse_struct_type,
116c97bf8c3SAlexander Motin 	&ng_car_hookconf_type_fields
117c97bf8c3SAlexander Motin };
118c97bf8c3SAlexander Motin 
119c97bf8c3SAlexander Motin /* Parse type for struct ng_car_bulkconf */
120c97bf8c3SAlexander Motin static const struct ng_parse_struct_field ng_car_bulkconf_type_fields[]
121c97bf8c3SAlexander Motin 	= NG_CAR_BULKCONF(&ng_car_hookconf_type);
122c97bf8c3SAlexander Motin static const struct ng_parse_type ng_car_bulkconf_type = {
123c97bf8c3SAlexander Motin 	&ng_parse_struct_type,
124c97bf8c3SAlexander Motin 	&ng_car_bulkconf_type_fields
125c97bf8c3SAlexander Motin };
126c97bf8c3SAlexander Motin 
127c97bf8c3SAlexander Motin /* Command list */
128c97bf8c3SAlexander Motin static struct ng_cmdlist ng_car_cmdlist[] = {
129c97bf8c3SAlexander Motin 	{
130c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
131c97bf8c3SAlexander Motin 	  NGM_CAR_GET_STATS,
132c97bf8c3SAlexander Motin 	  "getstats",
133c97bf8c3SAlexander Motin 	  NULL,
134c97bf8c3SAlexander Motin 	  &ng_car_bulkstats_type,
135c97bf8c3SAlexander Motin 	},
136c97bf8c3SAlexander Motin 	{
137c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
138c97bf8c3SAlexander Motin 	  NGM_CAR_CLR_STATS,
139c97bf8c3SAlexander Motin 	  "clrstats",
140c97bf8c3SAlexander Motin 	  NULL,
141c97bf8c3SAlexander Motin 	  NULL,
142c97bf8c3SAlexander Motin 	},
143c97bf8c3SAlexander Motin 	{
144c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
145c97bf8c3SAlexander Motin 	  NGM_CAR_GETCLR_STATS,
146c97bf8c3SAlexander Motin 	  "getclrstats",
147c97bf8c3SAlexander Motin 	  NULL,
148c97bf8c3SAlexander Motin 	  &ng_car_bulkstats_type,
149c97bf8c3SAlexander Motin 	},
150c97bf8c3SAlexander Motin 
151c97bf8c3SAlexander Motin 	{
152c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
153c97bf8c3SAlexander Motin 	  NGM_CAR_GET_CONF,
154c97bf8c3SAlexander Motin 	  "getconf",
155c97bf8c3SAlexander Motin 	  NULL,
156c97bf8c3SAlexander Motin 	  &ng_car_bulkconf_type,
157c97bf8c3SAlexander Motin 	},
158c97bf8c3SAlexander Motin 	{
159c97bf8c3SAlexander Motin 	  NGM_CAR_COOKIE,
160c97bf8c3SAlexander Motin 	  NGM_CAR_SET_CONF,
161c97bf8c3SAlexander Motin 	  "setconf",
162c97bf8c3SAlexander Motin 	  &ng_car_bulkconf_type,
163c97bf8c3SAlexander Motin 	  NULL,
164c97bf8c3SAlexander Motin 	},
165c97bf8c3SAlexander Motin 	{ 0 }
166c97bf8c3SAlexander Motin };
167c97bf8c3SAlexander Motin 
168c97bf8c3SAlexander Motin /* Netgraph node type descriptor */
169c97bf8c3SAlexander Motin static struct ng_type ng_car_typestruct = {
170c97bf8c3SAlexander Motin 	.version =	NG_ABI_VERSION,
171c97bf8c3SAlexander Motin 	.name =		NG_CAR_NODE_TYPE,
172c97bf8c3SAlexander Motin 	.constructor =	ng_car_constructor,
173c97bf8c3SAlexander Motin 	.rcvmsg =	ng_car_rcvmsg,
174c97bf8c3SAlexander Motin 	.shutdown =	ng_car_shutdown,
175c97bf8c3SAlexander Motin 	.newhook =	ng_car_newhook,
176c97bf8c3SAlexander Motin 	.rcvdata =	ng_car_rcvdata,
177c97bf8c3SAlexander Motin 	.disconnect =	ng_car_disconnect,
178c97bf8c3SAlexander Motin 	.cmdlist =	ng_car_cmdlist,
179c97bf8c3SAlexander Motin };
180c97bf8c3SAlexander Motin NETGRAPH_INIT(car, &ng_car_typestruct);
181c97bf8c3SAlexander Motin 
182c97bf8c3SAlexander Motin /*
183c97bf8c3SAlexander Motin  * Node constructor
184c97bf8c3SAlexander Motin  */
185c97bf8c3SAlexander Motin static int
186c97bf8c3SAlexander Motin ng_car_constructor(node_p node)
187c97bf8c3SAlexander Motin {
188c97bf8c3SAlexander Motin 	priv_p priv;
189c97bf8c3SAlexander Motin 
190ae1be01fSAlexander Motin 	/* Initialize private descriptor. */
191674d86bfSGleb Smirnoff 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
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;
209c86d865eSAlexander Motin 	getbinuptime(&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);
263c77b232bSAlexander Motin 	struct mbuf *m;
264c97bf8c3SAlexander Motin 	int error = 0;
265c77b232bSAlexander Motin 	u_int len;
266c97bf8c3SAlexander Motin 
267c97bf8c3SAlexander Motin 	/* If queue is not empty now then enqueue packet. */
268c97bf8c3SAlexander Motin 	if (hinfo->q_first != hinfo->q_last) {
269c97bf8c3SAlexander Motin 		ng_car_enqueue(hinfo, item);
270c97bf8c3SAlexander Motin 		return (0);
271c97bf8c3SAlexander Motin 	}
272c97bf8c3SAlexander Motin 
273c97bf8c3SAlexander Motin 	m = NGI_M(item);
274c97bf8c3SAlexander Motin 
275c97bf8c3SAlexander Motin #define NG_CAR_PERFORM_MATCH_ACTION(a)			\
276c97bf8c3SAlexander Motin 	do {						\
277c97bf8c3SAlexander Motin 		switch (a) {				\
278c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_FORWARD:		\
279ae1be01fSAlexander Motin 			/* Do nothing. */		\
280c97bf8c3SAlexander Motin 			break;				\
281c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_MARK:		\
282c97bf8c3SAlexander Motin 			/* XXX find a way to mark packets (mbuf tag?) */ \
283c97bf8c3SAlexander Motin 			++hinfo->stats.errors;		\
284c97bf8c3SAlexander Motin 			break;				\
285c97bf8c3SAlexander Motin 		case NG_CAR_ACTION_DROP:		\
286c97bf8c3SAlexander Motin 		default:				\
287ae1be01fSAlexander Motin 			/* Drop packet and return. */	\
288c97bf8c3SAlexander Motin 			NG_FREE_ITEM(item);		\
289c97bf8c3SAlexander Motin 			++hinfo->stats.droped_pkts;	\
290ae1be01fSAlexander Motin 			return (0);			\
291c97bf8c3SAlexander Motin 		}					\
292ae1be01fSAlexander Motin 	} while (0)
293c97bf8c3SAlexander Motin 
294673f5a8bSAlexander Motin 	/* Packet is counted as 128 tokens for better resolution */
295673f5a8bSAlexander Motin 	if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
296673f5a8bSAlexander Motin 		len = 128;
297673f5a8bSAlexander Motin 	} else {
298673f5a8bSAlexander Motin 		len = m->m_pkthdr.len;
299673f5a8bSAlexander Motin 	}
300673f5a8bSAlexander Motin 
301053359b7SPedro F. Giffuni 	/* Check committed token bucket. */
302673f5a8bSAlexander Motin 	if (hinfo->tc - len >= 0) {
303ae1be01fSAlexander Motin 		/* This packet is green. */
304c97bf8c3SAlexander Motin 		++hinfo->stats.green_pkts;
305673f5a8bSAlexander Motin 		hinfo->tc -= 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 
312053359b7SPedro F. Giffuni 		 /* Check committed token bucket again after refill. */
313673f5a8bSAlexander Motin 		if (hinfo->tc - len >= 0) {
314c97bf8c3SAlexander Motin 			/* This packet is green */
315c97bf8c3SAlexander Motin 			++hinfo->stats.green_pkts;
316673f5a8bSAlexander Motin 			hinfo->tc -= 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? */
327673f5a8bSAlexander Motin 			if (len - (hinfo->tc - len) > hinfo->conf.ebs) {
328ae1be01fSAlexander Motin 				/* This packet is definitely red. */
329c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
330c97bf8c3SAlexander Motin 				hinfo->te = 0;
331c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
332c97bf8c3SAlexander Motin 
333ae1be01fSAlexander Motin 			/* Use token bucket to simulate RED-like drop
334ae1be01fSAlexander Motin 			   probability. */
335673f5a8bSAlexander Motin 			} else if (hinfo->te + (len - hinfo->tc) <
336ae1be01fSAlexander Motin 			    hinfo->conf.ebs) {
337c97bf8c3SAlexander Motin 				/* This packet is yellow */
338c97bf8c3SAlexander Motin 				++hinfo->stats.yellow_pkts;
339673f5a8bSAlexander Motin 				hinfo->te += len - hinfo->tc;
340ae1be01fSAlexander Motin 				/* Go to negative tokens. */
341673f5a8bSAlexander Motin 				hinfo->tc -= len;
342c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action);
343c97bf8c3SAlexander Motin 			} else {
344053359b7SPedro F. Giffuni 				/* This packet is probably red. */
345c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
346c97bf8c3SAlexander Motin 				hinfo->te = 0;
347c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
348c97bf8c3SAlexander Motin 			}
349c97bf8c3SAlexander Motin 		/* If not green and mode is SINGLE/DOUBLE RATE. */
350c97bf8c3SAlexander Motin 		} else {
351c97bf8c3SAlexander Motin 			/* Check extended token bucket. */
352673f5a8bSAlexander Motin 			if (hinfo->te - len >= 0) {
353c97bf8c3SAlexander Motin 				/* This packet is yellow */
354c97bf8c3SAlexander Motin 				++hinfo->stats.yellow_pkts;
355673f5a8bSAlexander Motin 				hinfo->te -= len;
356c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action);
357c97bf8c3SAlexander Motin 			} else {
358c97bf8c3SAlexander Motin 				/* This packet is red */
359c97bf8c3SAlexander Motin 				++hinfo->stats.red_pkts;
360c97bf8c3SAlexander Motin 				NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action);
361c97bf8c3SAlexander Motin 			}
362c97bf8c3SAlexander Motin 		}
363c97bf8c3SAlexander Motin 	}
364c97bf8c3SAlexander Motin 
365c97bf8c3SAlexander Motin #undef NG_CAR_PERFORM_MATCH_ACTION
366c97bf8c3SAlexander Motin 
367c77b232bSAlexander Motin 	NG_FWD_ITEM_HOOK(error, item, hinfo->dest);
368c97bf8c3SAlexander Motin 	if (error != 0)
369c97bf8c3SAlexander Motin 		++hinfo->stats.errors;
370c97bf8c3SAlexander Motin 	++hinfo->stats.passed_pkts;
371c97bf8c3SAlexander Motin 
372ae1be01fSAlexander Motin 	return (error);
373c97bf8c3SAlexander Motin }
374c97bf8c3SAlexander Motin 
375c97bf8c3SAlexander Motin /*
376ae1be01fSAlexander Motin  * Receive a control message.
377c97bf8c3SAlexander Motin  */
378c97bf8c3SAlexander Motin static int
379c97bf8c3SAlexander Motin ng_car_rcvmsg(node_p node, item_p item, hook_p lasthook)
380c97bf8c3SAlexander Motin {
381c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
382c97bf8c3SAlexander Motin 	struct ng_mesg *resp = NULL;
383c97bf8c3SAlexander Motin 	int error = 0;
384c97bf8c3SAlexander Motin 	struct ng_mesg *msg;
385c97bf8c3SAlexander Motin 
386c97bf8c3SAlexander Motin 	NGI_GET_MSG(item, msg);
387c97bf8c3SAlexander Motin 	switch (msg->header.typecookie) {
388c97bf8c3SAlexander Motin 	case NGM_CAR_COOKIE:
389c97bf8c3SAlexander Motin 		switch (msg->header.cmd) {
390c97bf8c3SAlexander Motin 		case NGM_CAR_GET_STATS:
391c97bf8c3SAlexander Motin 		case NGM_CAR_GETCLR_STATS:
392c97bf8c3SAlexander Motin 			{
393c97bf8c3SAlexander Motin 				struct ng_car_bulkstats *bstats;
394c97bf8c3SAlexander Motin 
395c97bf8c3SAlexander Motin 				NG_MKRESPONSE(resp, msg,
396c97bf8c3SAlexander Motin 					sizeof(*bstats), M_NOWAIT);
397c97bf8c3SAlexander Motin 				if (resp == NULL) {
398c97bf8c3SAlexander Motin 					error = ENOMEM;
399c97bf8c3SAlexander Motin 					break;
400c97bf8c3SAlexander Motin 				}
401c97bf8c3SAlexander Motin 				bstats = (struct ng_car_bulkstats *)resp->data;
402c97bf8c3SAlexander Motin 
403c97bf8c3SAlexander Motin 				bcopy(&priv->upper.stats, &bstats->downstream,
404c97bf8c3SAlexander Motin 				    sizeof(bstats->downstream));
405c97bf8c3SAlexander Motin 				bcopy(&priv->lower.stats, &bstats->upstream,
406c97bf8c3SAlexander Motin 				    sizeof(bstats->upstream));
407c97bf8c3SAlexander Motin 			}
408c97bf8c3SAlexander Motin 			if (msg->header.cmd == NGM_CAR_GET_STATS)
409c97bf8c3SAlexander Motin 				break;
410c97bf8c3SAlexander Motin 		case NGM_CAR_CLR_STATS:
411c97bf8c3SAlexander Motin 			bzero(&priv->upper.stats,
412c97bf8c3SAlexander Motin 				sizeof(priv->upper.stats));
413c97bf8c3SAlexander Motin 			bzero(&priv->lower.stats,
414c97bf8c3SAlexander Motin 				sizeof(priv->lower.stats));
415c97bf8c3SAlexander Motin 			break;
416c97bf8c3SAlexander Motin 		case NGM_CAR_GET_CONF:
417c97bf8c3SAlexander Motin 			{
418c97bf8c3SAlexander Motin 				struct ng_car_bulkconf *bconf;
419c97bf8c3SAlexander Motin 
420c97bf8c3SAlexander Motin 				NG_MKRESPONSE(resp, msg,
421c97bf8c3SAlexander Motin 					sizeof(*bconf), M_NOWAIT);
422c97bf8c3SAlexander Motin 				if (resp == NULL) {
423c97bf8c3SAlexander Motin 					error = ENOMEM;
424c97bf8c3SAlexander Motin 					break;
425c97bf8c3SAlexander Motin 				}
426c97bf8c3SAlexander Motin 				bconf = (struct ng_car_bulkconf *)resp->data;
427c97bf8c3SAlexander Motin 
428c97bf8c3SAlexander Motin 				bcopy(&priv->upper.conf, &bconf->downstream,
429c97bf8c3SAlexander Motin 				    sizeof(bconf->downstream));
430c97bf8c3SAlexander Motin 				bcopy(&priv->lower.conf, &bconf->upstream,
431c97bf8c3SAlexander Motin 				    sizeof(bconf->upstream));
432673f5a8bSAlexander Motin 				/* Convert internal 1/(8*128) of pps into pps */
433673f5a8bSAlexander Motin 				if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) {
434673f5a8bSAlexander Motin 				    bconf->downstream.cir /= 1024;
435673f5a8bSAlexander Motin 				    bconf->downstream.pir /= 1024;
436673f5a8bSAlexander Motin 				    bconf->downstream.cbs /= 128;
437673f5a8bSAlexander Motin 				    bconf->downstream.ebs /= 128;
438673f5a8bSAlexander Motin 				}
439673f5a8bSAlexander Motin 				if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) {
440673f5a8bSAlexander Motin 				    bconf->upstream.cir /= 1024;
441673f5a8bSAlexander Motin 				    bconf->upstream.pir /= 1024;
442673f5a8bSAlexander Motin 				    bconf->upstream.cbs /= 128;
443673f5a8bSAlexander Motin 				    bconf->upstream.ebs /= 128;
444673f5a8bSAlexander Motin 				}
445c97bf8c3SAlexander Motin 			}
446c97bf8c3SAlexander Motin 			break;
447c97bf8c3SAlexander Motin 		case NGM_CAR_SET_CONF:
448c97bf8c3SAlexander Motin 			{
449c97bf8c3SAlexander Motin 				struct ng_car_bulkconf *const bconf =
450c97bf8c3SAlexander Motin 				(struct ng_car_bulkconf *)msg->data;
451c97bf8c3SAlexander Motin 
452c97bf8c3SAlexander Motin 				/* Check for invalid or illegal config. */
453673f5a8bSAlexander Motin 				if (msg->header.arglen != sizeof(*bconf)) {
454673f5a8bSAlexander Motin 					error = EINVAL;
455673f5a8bSAlexander Motin 					break;
456673f5a8bSAlexander Motin 				}
457673f5a8bSAlexander Motin 				/* Convert pps into internal 1/(8*128) of pps */
458673f5a8bSAlexander Motin 				if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) {
459673f5a8bSAlexander Motin 				    bconf->downstream.cir *= 1024;
460673f5a8bSAlexander Motin 				    bconf->downstream.pir *= 1024;
461673f5a8bSAlexander Motin 				    bconf->downstream.cbs *= 125;
462673f5a8bSAlexander Motin 				    bconf->downstream.ebs *= 125;
463673f5a8bSAlexander Motin 				}
464673f5a8bSAlexander Motin 				if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) {
465673f5a8bSAlexander Motin 				    bconf->upstream.cir *= 1024;
466673f5a8bSAlexander Motin 				    bconf->upstream.pir *= 1024;
467673f5a8bSAlexander Motin 				    bconf->upstream.cbs *= 125;
468673f5a8bSAlexander Motin 				    bconf->upstream.ebs *= 125;
469673f5a8bSAlexander Motin 				}
470673f5a8bSAlexander Motin 				if ((bconf->downstream.cir > 1000000000) ||
471673f5a8bSAlexander Motin 				    (bconf->downstream.pir > 1000000000) ||
472673f5a8bSAlexander Motin 				    (bconf->upstream.cir > 1000000000) ||
473673f5a8bSAlexander Motin 				    (bconf->upstream.pir > 1000000000) ||
474673f5a8bSAlexander Motin 				    (bconf->downstream.cbs == 0 &&
475673f5a8bSAlexander Motin 					bconf->downstream.ebs == 0) ||
476673f5a8bSAlexander Motin 				    (bconf->upstream.cbs == 0 &&
477673f5a8bSAlexander Motin 					bconf->upstream.ebs == 0))
478c97bf8c3SAlexander Motin 				{
479c97bf8c3SAlexander Motin 					error = EINVAL;
480c97bf8c3SAlexander Motin 					break;
481c97bf8c3SAlexander Motin 				}
482673f5a8bSAlexander Motin 				if ((bconf->upstream.mode == NG_CAR_SHAPE) &&
483673f5a8bSAlexander Motin 				    (bconf->upstream.cir == 0)) {
484673f5a8bSAlexander Motin 					error = EINVAL;
485673f5a8bSAlexander Motin 					break;
486673f5a8bSAlexander Motin 				}
487673f5a8bSAlexander Motin 				if ((bconf->downstream.mode == NG_CAR_SHAPE) &&
488673f5a8bSAlexander Motin 				    (bconf->downstream.cir == 0)) {
489673f5a8bSAlexander Motin 					error = EINVAL;
490673f5a8bSAlexander Motin 					break;
491673f5a8bSAlexander Motin 				}
492c97bf8c3SAlexander Motin 
493c97bf8c3SAlexander Motin 				/* Copy downstream config. */
494c97bf8c3SAlexander Motin 				bcopy(&bconf->downstream, &priv->upper.conf,
495c97bf8c3SAlexander Motin 				    sizeof(priv->upper.conf));
496c97bf8c3SAlexander Motin     				priv->upper.tc = priv->upper.conf.cbs;
497c97bf8c3SAlexander Motin 				if (priv->upper.conf.mode == NG_CAR_RED ||
498b50ace73SAlexander Motin 				    priv->upper.conf.mode == NG_CAR_SHAPE) {
499c97bf8c3SAlexander Motin 					priv->upper.te = 0;
500c97bf8c3SAlexander Motin 				} else {
501c97bf8c3SAlexander Motin 					priv->upper.te = priv->upper.conf.ebs;
502c97bf8c3SAlexander Motin 				}
503c97bf8c3SAlexander Motin 
504c97bf8c3SAlexander Motin 				/* Copy upstream config. */
505c97bf8c3SAlexander Motin 				bcopy(&bconf->upstream, &priv->lower.conf,
506c97bf8c3SAlexander Motin 				    sizeof(priv->lower.conf));
507c97bf8c3SAlexander Motin     				priv->lower.tc = priv->lower.conf.cbs;
508c97bf8c3SAlexander Motin 				if (priv->lower.conf.mode == NG_CAR_RED ||
509c97bf8c3SAlexander Motin 				    priv->lower.conf.mode == NG_CAR_SHAPE) {
510c97bf8c3SAlexander Motin 					priv->lower.te = 0;
511c97bf8c3SAlexander Motin 				} else {
512c97bf8c3SAlexander Motin 					priv->lower.te = priv->lower.conf.ebs;
513c97bf8c3SAlexander Motin 				}
514c97bf8c3SAlexander Motin 			}
515c97bf8c3SAlexander Motin 			break;
516c97bf8c3SAlexander Motin 		default:
517c97bf8c3SAlexander Motin 			error = EINVAL;
518c97bf8c3SAlexander Motin 			break;
519c97bf8c3SAlexander Motin 		}
520c97bf8c3SAlexander Motin 		break;
521c97bf8c3SAlexander Motin 	default:
522c97bf8c3SAlexander Motin 		error = EINVAL;
523c97bf8c3SAlexander Motin 		break;
524c97bf8c3SAlexander Motin 	}
525c97bf8c3SAlexander Motin 	NG_RESPOND_MSG(error, node, item, resp);
526c97bf8c3SAlexander Motin 	NG_FREE_MSG(msg);
527c97bf8c3SAlexander Motin 	return (error);
528c97bf8c3SAlexander Motin }
529c97bf8c3SAlexander Motin 
530c97bf8c3SAlexander Motin /*
531c97bf8c3SAlexander Motin  * Do local shutdown processing.
532c97bf8c3SAlexander Motin  */
533c97bf8c3SAlexander Motin static int
534c97bf8c3SAlexander Motin ng_car_shutdown(node_p node)
535c97bf8c3SAlexander Motin {
536c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
537c97bf8c3SAlexander Motin 
538df01e689SAlexander Motin 	ng_uncallout(&priv->upper.q_callout, node);
539df01e689SAlexander Motin 	ng_uncallout(&priv->lower.q_callout, node);
540c97bf8c3SAlexander Motin 	mtx_destroy(&priv->upper.q_mtx);
541c97bf8c3SAlexander Motin 	mtx_destroy(&priv->lower.q_mtx);
542c97bf8c3SAlexander Motin 	NG_NODE_UNREF(priv->node);
543ae1be01fSAlexander Motin 	free(priv, M_NETGRAPH);
544c97bf8c3SAlexander Motin 	return (0);
545c97bf8c3SAlexander Motin }
546c97bf8c3SAlexander Motin 
547c97bf8c3SAlexander Motin /*
548c97bf8c3SAlexander Motin  * Hook disconnection.
549c97bf8c3SAlexander Motin  *
550c97bf8c3SAlexander Motin  * For this type, removal of the last link destroys the node.
551c97bf8c3SAlexander Motin  */
552c97bf8c3SAlexander Motin static int
553c97bf8c3SAlexander Motin ng_car_disconnect(hook_p hook)
554c97bf8c3SAlexander Motin {
555c97bf8c3SAlexander Motin 	struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
556c97bf8c3SAlexander Motin 	const node_p node = NG_HOOK_NODE(hook);
557c97bf8c3SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
558c97bf8c3SAlexander Motin 
559c97bf8c3SAlexander Motin 	if (hinfo) {
560c97bf8c3SAlexander Motin 		/* Purge queue if not empty. */
561c97bf8c3SAlexander Motin 		while (hinfo->q_first != hinfo->q_last) {
562c97bf8c3SAlexander Motin 			NG_FREE_M(hinfo->q[hinfo->q_first]);
563c97bf8c3SAlexander Motin 			hinfo->q_first++;
564c97bf8c3SAlexander Motin 			if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
565c97bf8c3SAlexander Motin 		    		hinfo->q_first = 0;
566c97bf8c3SAlexander Motin 		}
567c97bf8c3SAlexander Motin 		/* Remove hook refs. */
568c97bf8c3SAlexander Motin 		if (hinfo->hook == priv->upper.hook)
569c97bf8c3SAlexander Motin 			priv->lower.dest = NULL;
570c97bf8c3SAlexander Motin 		else
571c97bf8c3SAlexander Motin 			priv->upper.dest = NULL;
572c97bf8c3SAlexander Motin 		hinfo->hook = NULL;
573c97bf8c3SAlexander Motin 	}
574c97bf8c3SAlexander Motin 	/* Already shutting down? */
575c97bf8c3SAlexander Motin 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
576c97bf8c3SAlexander Motin 	    && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
577c97bf8c3SAlexander Motin 		ng_rmnode_self(NG_HOOK_NODE(hook));
578c97bf8c3SAlexander Motin 	return (0);
579c97bf8c3SAlexander Motin }
580c97bf8c3SAlexander Motin 
581c97bf8c3SAlexander Motin /*
582c97bf8c3SAlexander Motin  * Hook's token buckets refillment.
583c97bf8c3SAlexander Motin  */
584c97bf8c3SAlexander Motin static void
585c97bf8c3SAlexander Motin ng_car_refillhook(struct hookinfo *h)
586c97bf8c3SAlexander Motin {
587c86d865eSAlexander Motin 	struct bintime newt, deltat;
588c86d865eSAlexander Motin 	unsigned int deltat_us;
589c97bf8c3SAlexander Motin 
590c97bf8c3SAlexander Motin 	/* Get current time. */
591c86d865eSAlexander Motin 	getbinuptime(&newt);
592c86d865eSAlexander Motin 
593c86d865eSAlexander Motin 	/* Get time delta since last refill. */
594c86d865eSAlexander Motin 	deltat = newt;
595c86d865eSAlexander Motin 	bintime_sub(&deltat, &h->lastRefill);
596c97bf8c3SAlexander Motin 
597c97bf8c3SAlexander Motin 	/* Time must go forward. */
598c86d865eSAlexander Motin 	if (deltat.sec < 0) {
599c97bf8c3SAlexander Motin 	    h->lastRefill = newt;
600c97bf8c3SAlexander Motin 	    return;
601c97bf8c3SAlexander Motin 	}
602c97bf8c3SAlexander Motin 
603c86d865eSAlexander Motin 	/* But not too far forward. */
604c86d865eSAlexander Motin 	if (deltat.sec >= 1000) {
605c86d865eSAlexander Motin 	    deltat_us = (1000 << 20);
606c97bf8c3SAlexander Motin 	} else {
607c86d865eSAlexander Motin 	    /* convert bintime to the 1/(2^20) of sec */
608c86d865eSAlexander Motin 	    deltat_us = (deltat.sec << 20) + (deltat.frac >> 44);
609c97bf8c3SAlexander Motin 	}
610c97bf8c3SAlexander Motin 
611c97bf8c3SAlexander Motin 	if (h->conf.mode == NG_CAR_SINGLE_RATE) {
612c86d865eSAlexander Motin 		int64_t	delta;
613053359b7SPedro F. Giffuni 		/* Refill committed token bucket. */
614c86d865eSAlexander Motin 		h->tc += (h->conf.cir * deltat_us) >> 23;
615c97bf8c3SAlexander Motin 		delta = h->tc - h->conf.cbs;
616c97bf8c3SAlexander Motin 		if (delta > 0) {
617c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
618c97bf8c3SAlexander Motin 
619c97bf8c3SAlexander Motin 			/* Refill exceeded token bucket. */
620c97bf8c3SAlexander Motin 			h->te += delta;
621c86d865eSAlexander Motin 			if (h->te > ((int64_t)h->conf.ebs))
622c97bf8c3SAlexander Motin 				h->te = h->conf.ebs;
623c97bf8c3SAlexander Motin 		}
624c97bf8c3SAlexander Motin 
625c97bf8c3SAlexander Motin 	} else if (h->conf.mode == NG_CAR_DOUBLE_RATE) {
626053359b7SPedro F. Giffuni 		/* Refill committed token bucket. */
627c86d865eSAlexander Motin 		h->tc += (h->conf.cir * deltat_us) >> 23;
628c86d865eSAlexander Motin 		if (h->tc > ((int64_t)h->conf.cbs))
629c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
630c97bf8c3SAlexander Motin 
631c97bf8c3SAlexander Motin 		/* Refill peak token bucket. */
632c86d865eSAlexander Motin 		h->te += (h->conf.pir * deltat_us) >> 23;
633c86d865eSAlexander Motin 		if (h->te > ((int64_t)h->conf.ebs))
634c97bf8c3SAlexander Motin 			h->te = h->conf.ebs;
635c97bf8c3SAlexander Motin 
636c97bf8c3SAlexander Motin 	} else { /* RED or SHAPE mode. */
637053359b7SPedro F. Giffuni 		/* Refill committed token bucket. */
638c86d865eSAlexander Motin 		h->tc += (h->conf.cir * deltat_us) >> 23;
639c97bf8c3SAlexander Motin 		if (h->tc > ((int64_t)h->conf.cbs))
640c97bf8c3SAlexander Motin 			h->tc = h->conf.cbs;
641c97bf8c3SAlexander Motin 	}
642c97bf8c3SAlexander Motin 
643c97bf8c3SAlexander Motin 	/* Remember this moment. */
644c97bf8c3SAlexander Motin 	h->lastRefill = newt;
645c97bf8c3SAlexander Motin }
646c97bf8c3SAlexander Motin 
647c97bf8c3SAlexander Motin /*
648c97bf8c3SAlexander Motin  * Schedule callout when we will have required tokens.
649c97bf8c3SAlexander Motin  */
650c97bf8c3SAlexander Motin static void
651c97bf8c3SAlexander Motin ng_car_schedule(struct hookinfo *hinfo)
652c97bf8c3SAlexander Motin {
653c97bf8c3SAlexander Motin 	int 	delay;
654c97bf8c3SAlexander Motin 
655c97bf8c3SAlexander Motin 	delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1;
656c97bf8c3SAlexander Motin 
657c97bf8c3SAlexander Motin 	ng_callout(&hinfo->q_callout, NG_HOOK_NODE(hinfo->hook), hinfo->hook,
658c97bf8c3SAlexander Motin 	    delay, &ng_car_q_event, NULL, 0);
659c97bf8c3SAlexander Motin }
660c97bf8c3SAlexander Motin 
661c97bf8c3SAlexander Motin /*
662c97bf8c3SAlexander Motin  * Queue processing callout handler.
663c97bf8c3SAlexander Motin  */
664c97bf8c3SAlexander Motin void
665c97bf8c3SAlexander Motin ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2)
666c97bf8c3SAlexander Motin {
667c97bf8c3SAlexander Motin 	struct hookinfo	*hinfo = NG_HOOK_PRIVATE(hook);
668c97bf8c3SAlexander Motin 	struct mbuf 	*m;
669c97bf8c3SAlexander Motin 	int		error;
670c97bf8c3SAlexander Motin 
671c97bf8c3SAlexander Motin 	/* Refill tokens for time we have slept. */
672c97bf8c3SAlexander Motin 	ng_car_refillhook(hinfo);
673c97bf8c3SAlexander Motin 
674c97bf8c3SAlexander Motin 	/* If we have some tokens */
675c97bf8c3SAlexander Motin 	while (hinfo->tc >= 0) {
676c97bf8c3SAlexander Motin 
677c97bf8c3SAlexander Motin 		/* Send packet. */
678c97bf8c3SAlexander Motin 		m = hinfo->q[hinfo->q_first];
679c77b232bSAlexander Motin 		NG_SEND_DATA_ONLY(error, hinfo->dest, m);
680c77b232bSAlexander Motin 		if (error != 0)
681c77b232bSAlexander Motin 			++hinfo->stats.errors;
682c77b232bSAlexander Motin 		++hinfo->stats.passed_pkts;
683c97bf8c3SAlexander Motin 
684c97bf8c3SAlexander Motin 		/* Get next one. */
685c97bf8c3SAlexander Motin 		hinfo->q_first++;
686c97bf8c3SAlexander Motin 		if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
687c97bf8c3SAlexander Motin 			hinfo->q_first = 0;
688c97bf8c3SAlexander Motin 
689c97bf8c3SAlexander Motin 		/* Stop if none left. */
690c97bf8c3SAlexander Motin 		if (hinfo->q_first == hinfo->q_last)
691c97bf8c3SAlexander Motin 			break;
692c97bf8c3SAlexander Motin 
693c97bf8c3SAlexander Motin 		/* If we have more packet, try it. */
694c97bf8c3SAlexander Motin 		m = hinfo->q[hinfo->q_first];
695673f5a8bSAlexander Motin 		if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
696673f5a8bSAlexander Motin 			hinfo->tc -= 128;
697673f5a8bSAlexander Motin 		} else {
698c97bf8c3SAlexander Motin 			hinfo->tc -= m->m_pkthdr.len;
699c97bf8c3SAlexander Motin 		}
700c97bf8c3SAlexander Motin 	}
701c97bf8c3SAlexander Motin 
702c97bf8c3SAlexander Motin 	/* If something left */
703ae1be01fSAlexander Motin 	if (hinfo->q_first != hinfo->q_last)
704ae1be01fSAlexander Motin 		/* Schedule queue processing. */
705c97bf8c3SAlexander Motin 		ng_car_schedule(hinfo);
706c97bf8c3SAlexander Motin }
707c97bf8c3SAlexander Motin 
708c97bf8c3SAlexander Motin /*
709c97bf8c3SAlexander Motin  * Enqueue packet.
710c97bf8c3SAlexander Motin  */
711c97bf8c3SAlexander Motin static void
712c97bf8c3SAlexander Motin ng_car_enqueue(struct hookinfo *hinfo, item_p item)
713c97bf8c3SAlexander Motin {
714c97bf8c3SAlexander Motin 	struct mbuf 	*m;
715c97bf8c3SAlexander Motin 	int		len;
716c97bf8c3SAlexander Motin 
717c97bf8c3SAlexander Motin 	NGI_GET_M(item, m);
718c97bf8c3SAlexander Motin 	NG_FREE_ITEM(item);
719c97bf8c3SAlexander Motin 
720ae1be01fSAlexander Motin 	/* Lock queue mutex. */
721c97bf8c3SAlexander Motin 	mtx_lock(&hinfo->q_mtx);
722c97bf8c3SAlexander Motin 
723ae1be01fSAlexander Motin 	/* Calculate used queue length. */
724c97bf8c3SAlexander Motin 	len = hinfo->q_last - hinfo->q_first;
725c97bf8c3SAlexander Motin 	if (len < 0)
726c97bf8c3SAlexander Motin 		len += NG_CAR_QUEUE_SIZE;
727c97bf8c3SAlexander Motin 
728ae1be01fSAlexander Motin 	/* If queue is overflowed or we have no RED tokens. */
729c97bf8c3SAlexander Motin 	if ((len >= (NG_CAR_QUEUE_SIZE - 1)) ||
730c97bf8c3SAlexander Motin 	    (hinfo->te + len >= NG_CAR_QUEUE_SIZE)) {
731ae1be01fSAlexander Motin 		/* Drop packet. */
732c97bf8c3SAlexander Motin 		++hinfo->stats.red_pkts;
733c77b232bSAlexander Motin 		++hinfo->stats.droped_pkts;
734c97bf8c3SAlexander Motin 		NG_FREE_M(m);
735c97bf8c3SAlexander Motin 
736c97bf8c3SAlexander Motin 		hinfo->te = 0;
737c97bf8c3SAlexander Motin 	} else {
738c97bf8c3SAlexander Motin 		/* This packet is yellow. */
739c97bf8c3SAlexander Motin 		++hinfo->stats.yellow_pkts;
740c97bf8c3SAlexander Motin 
741c97bf8c3SAlexander Motin 		/* Enqueue packet. */
742c97bf8c3SAlexander Motin 		hinfo->q[hinfo->q_last] = m;
743c97bf8c3SAlexander Motin 		hinfo->q_last++;
744c97bf8c3SAlexander Motin 		if (hinfo->q_last >= NG_CAR_QUEUE_SIZE)
745c97bf8c3SAlexander Motin 			hinfo->q_last = 0;
746c97bf8c3SAlexander Motin 
747c97bf8c3SAlexander Motin 		/* Use RED tokens. */
748c97bf8c3SAlexander Motin 		if (len > NG_CAR_QUEUE_MIN_TH)
749c97bf8c3SAlexander Motin 			hinfo->te += len - NG_CAR_QUEUE_MIN_TH;
750c97bf8c3SAlexander Motin 
751ae1be01fSAlexander Motin 		/* If this is a first packet in the queue. */
752c97bf8c3SAlexander Motin 		if (len == 0) {
753673f5a8bSAlexander Motin 			if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
754673f5a8bSAlexander Motin 				hinfo->tc -= 128;
755673f5a8bSAlexander Motin 			} else {
756c97bf8c3SAlexander Motin 				hinfo->tc -= m->m_pkthdr.len;
757673f5a8bSAlexander Motin 			}
758c97bf8c3SAlexander Motin 
759ae1be01fSAlexander Motin 			/* Schedule queue processing. */
760c97bf8c3SAlexander Motin 			ng_car_schedule(hinfo);
761c97bf8c3SAlexander Motin 		}
762c97bf8c3SAlexander Motin 	}
763c97bf8c3SAlexander Motin 
764ae1be01fSAlexander Motin 	/* Unlock queue mutex. */
765c97bf8c3SAlexander Motin 	mtx_unlock(&hinfo->q_mtx);
766c97bf8c3SAlexander Motin }
767