xref: /freebsd/sys/netgraph/ng_pipe.c (revision 1ede983cc905643549d8cae56a9d0e28fc68375f)
19c238a0eSJulian Elischer /*
29c238a0eSJulian Elischer  * Copyright (c) 2004-2008 University of Zagreb
39c238a0eSJulian Elischer  * Copyright (c) 2007-2008 FreeBSD Foundation
49c238a0eSJulian Elischer  *
59c238a0eSJulian Elischer  * This software was developed by the University of Zagreb and the
69c238a0eSJulian Elischer  * FreeBSD Foundation under sponsorship by the Stichting NLnet and the
79c238a0eSJulian Elischer  * FreeBSD Foundation.
89c238a0eSJulian Elischer  *
99c238a0eSJulian Elischer  * Redistribution and use in source and binary forms, with or without
109c238a0eSJulian Elischer  * modification, are permitted provided that the following conditions
119c238a0eSJulian Elischer  * are met:
129c238a0eSJulian Elischer  * 1. Redistributions of source code must retain the above copyright
139c238a0eSJulian Elischer  *    notice, this list of conditions and the following disclaimer.
149c238a0eSJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
159c238a0eSJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
169c238a0eSJulian Elischer  *    documentation and/or other materials provided with the distribution.
179c238a0eSJulian Elischer  *
189c238a0eSJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199c238a0eSJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209c238a0eSJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219c238a0eSJulian Elischer  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229c238a0eSJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239c238a0eSJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249c238a0eSJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259c238a0eSJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269c238a0eSJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279c238a0eSJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289c238a0eSJulian Elischer  * SUCH DAMAGE.
299c238a0eSJulian Elischer  *
309c238a0eSJulian Elischer  * $FreeBSD$
319c238a0eSJulian Elischer  */
329c238a0eSJulian Elischer 
339c238a0eSJulian Elischer /*
349c238a0eSJulian Elischer  * This node permits simple traffic shaping by emulating bandwidth
359c238a0eSJulian Elischer  * and delay, as well as random packet losses.
369c238a0eSJulian Elischer  * The node has two hooks, upper and lower. Traffic flowing from upper to
379c238a0eSJulian Elischer  * lower hook is referenced as downstream, and vice versa. Parameters for
389c238a0eSJulian Elischer  * both directions can be set separately, except for delay.
399c238a0eSJulian Elischer  */
409c238a0eSJulian Elischer 
419c238a0eSJulian Elischer 
429c238a0eSJulian Elischer #include <sys/param.h>
439c238a0eSJulian Elischer #include <sys/errno.h>
449c238a0eSJulian Elischer #include <sys/systm.h>
459c238a0eSJulian Elischer #include <sys/kernel.h>
469c238a0eSJulian Elischer #include <sys/malloc.h>
479c238a0eSJulian Elischer #include <sys/mbuf.h>
489c238a0eSJulian Elischer #include <sys/time.h>
499c238a0eSJulian Elischer #include <sys/vimage.h>
509c238a0eSJulian Elischer 
519c238a0eSJulian Elischer #include <vm/uma.h>
529c238a0eSJulian Elischer 
539c238a0eSJulian Elischer #include <netinet/in.h>
549c238a0eSJulian Elischer #include <netinet/in_systm.h>
559c238a0eSJulian Elischer #include <netinet/ip.h>
569c238a0eSJulian Elischer 
579c238a0eSJulian Elischer #include <netgraph/ng_message.h>
589c238a0eSJulian Elischer #include <netgraph/netgraph.h>
599c238a0eSJulian Elischer #include <netgraph/ng_parse.h>
609c238a0eSJulian Elischer #include <netgraph/ng_pipe.h>
619c238a0eSJulian Elischer 
629c238a0eSJulian Elischer static MALLOC_DEFINE(M_NG_PIPE, "ng_pipe", "ng_pipe");
639c238a0eSJulian Elischer 
649c238a0eSJulian Elischer struct mtx ng_pipe_giant;
659c238a0eSJulian Elischer 
669c238a0eSJulian Elischer /* Packet header struct */
679c238a0eSJulian Elischer struct ngp_hdr {
689c238a0eSJulian Elischer 	TAILQ_ENTRY(ngp_hdr)	ngp_link;	/* next pkt in queue */
699c238a0eSJulian Elischer 	struct timeval		when;		/* this packet's due time */
709c238a0eSJulian Elischer 	struct mbuf		*m;		/* ptr to the packet data */
719c238a0eSJulian Elischer };
729c238a0eSJulian Elischer TAILQ_HEAD(p_head, ngp_hdr);
739c238a0eSJulian Elischer 
749c238a0eSJulian Elischer /* FIFO queue struct */
759c238a0eSJulian Elischer struct ngp_fifo {
769c238a0eSJulian Elischer 	TAILQ_ENTRY(ngp_fifo)	fifo_le;	/* list of active queues only */
779c238a0eSJulian Elischer 	struct p_head		packet_head;	/* FIFO queue head */
789c238a0eSJulian Elischer 	u_int32_t		hash;		/* flow signature */
799c238a0eSJulian Elischer 	struct timeval		vtime;		/* virtual time, for WFQ */
809c238a0eSJulian Elischer 	u_int32_t		rr_deficit;	/* for DRR */
819c238a0eSJulian Elischer 	u_int32_t		packets;	/* # of packets in this queue */
829c238a0eSJulian Elischer };
839c238a0eSJulian Elischer 
849c238a0eSJulian Elischer /* Per hook info */
859c238a0eSJulian Elischer struct hookinfo {
869c238a0eSJulian Elischer 	hook_p			hook;
879c238a0eSJulian Elischer 	int			noqueue;	/* bypass any processing */
889c238a0eSJulian Elischer 	TAILQ_HEAD(, ngp_fifo)	fifo_head;	/* FIFO queues */
899c238a0eSJulian Elischer 	TAILQ_HEAD(, ngp_hdr)	qout_head;	/* delay queue head */
909c238a0eSJulian Elischer 	LIST_ENTRY(hookinfo)	active_le;	/* active hooks */
919c238a0eSJulian Elischer 	struct timeval		qin_utime;
929c238a0eSJulian Elischer 	struct ng_pipe_hookcfg	cfg;
939c238a0eSJulian Elischer 	struct ng_pipe_hookrun	run;
949c238a0eSJulian Elischer 	struct ng_pipe_hookstat	stats;
959c238a0eSJulian Elischer 	uint64_t		*ber_p;		/* loss_p(BER,psize) map */
969c238a0eSJulian Elischer };
979c238a0eSJulian Elischer 
989c238a0eSJulian Elischer /* Per node info */
999c238a0eSJulian Elischer struct node_priv {
1009c238a0eSJulian Elischer 	u_int64_t		delay;
1019c238a0eSJulian Elischer 	u_int32_t		overhead;
1029c238a0eSJulian Elischer 	u_int32_t		header_offset;
1039c238a0eSJulian Elischer 	struct hookinfo		lower;
1049c238a0eSJulian Elischer 	struct hookinfo		upper;
1059c238a0eSJulian Elischer };
1069c238a0eSJulian Elischer typedef struct node_priv *priv_p;
1079c238a0eSJulian Elischer 
1089c238a0eSJulian Elischer /* Macro for calculating the virtual time for packet dequeueing in WFQ */
1099c238a0eSJulian Elischer #define FIFO_VTIME_SORT(plen)						\
1109c238a0eSJulian Elischer 	if (hinfo->cfg.wfq && hinfo->cfg.bandwidth) {			\
1119c238a0eSJulian Elischer 		ngp_f->vtime.tv_usec = now->tv_usec + ((uint64_t) (plen) \
1129c238a0eSJulian Elischer 			+ priv->overhead ) * hinfo->run.fifo_queues *	\
1139c238a0eSJulian Elischer 			8000000 / hinfo->cfg.bandwidth;			\
1149c238a0eSJulian Elischer 		ngp_f->vtime.tv_sec = now->tv_sec +			\
1159c238a0eSJulian Elischer 			ngp_f->vtime.tv_usec / 1000000;			\
1169c238a0eSJulian Elischer 		ngp_f->vtime.tv_usec = ngp_f->vtime.tv_usec % 1000000;	\
1179c238a0eSJulian Elischer 		TAILQ_FOREACH(ngp_f1, &hinfo->fifo_head, fifo_le)	\
1189c238a0eSJulian Elischer 			if (ngp_f1->vtime.tv_sec > ngp_f->vtime.tv_sec || \
1199c238a0eSJulian Elischer 			    (ngp_f1->vtime.tv_sec == ngp_f->vtime.tv_sec && \
1209c238a0eSJulian Elischer 			    ngp_f1->vtime.tv_usec > ngp_f->vtime.tv_usec)) \
1219c238a0eSJulian Elischer 				break;					\
1229c238a0eSJulian Elischer 		if (ngp_f1 == NULL)					\
1239c238a0eSJulian Elischer 			TAILQ_INSERT_TAIL(&hinfo->fifo_head, ngp_f, fifo_le); \
1249c238a0eSJulian Elischer 		else							\
1259c238a0eSJulian Elischer 			TAILQ_INSERT_BEFORE(ngp_f1, ngp_f, fifo_le);	\
1269c238a0eSJulian Elischer 	} else								\
1279c238a0eSJulian Elischer 		TAILQ_INSERT_TAIL(&hinfo->fifo_head, ngp_f, fifo_le);	\
1289c238a0eSJulian Elischer 
1299c238a0eSJulian Elischer 
1309c238a0eSJulian Elischer static void	parse_cfg(struct ng_pipe_hookcfg *, struct ng_pipe_hookcfg *,
1319c238a0eSJulian Elischer 			struct hookinfo *, priv_p);
1329c238a0eSJulian Elischer static void	pipe_dequeue(struct hookinfo *, struct timeval *);
1339c238a0eSJulian Elischer static void	pipe_scheduler(void *);
1349c238a0eSJulian Elischer static void	pipe_poll(void);
1359c238a0eSJulian Elischer static int	ngp_modevent(module_t, int, void *);
1369c238a0eSJulian Elischer 
1379c238a0eSJulian Elischer /* linked list of active "pipe" hooks */
1389c238a0eSJulian Elischer static LIST_HEAD(, hookinfo) active_head;
1399c238a0eSJulian Elischer static int active_gen_id = 0;
1409c238a0eSJulian Elischer 
1419c238a0eSJulian Elischer /* timeout handle for pipe_scheduler */
1429c238a0eSJulian Elischer static struct callout polling_timer;
1439c238a0eSJulian Elischer 
1449c238a0eSJulian Elischer /* zone for storing ngp_hdr-s */
1459c238a0eSJulian Elischer static uma_zone_t ngp_zone;
1469c238a0eSJulian Elischer 
1479c238a0eSJulian Elischer /* Netgraph methods */
1489c238a0eSJulian Elischer static ng_constructor_t	ngp_constructor;
1499c238a0eSJulian Elischer static ng_rcvmsg_t	ngp_rcvmsg;
1509c238a0eSJulian Elischer static ng_shutdown_t	ngp_shutdown;
1519c238a0eSJulian Elischer static ng_newhook_t	ngp_newhook;
1529c238a0eSJulian Elischer static ng_rcvdata_t	ngp_rcvdata;
1539c238a0eSJulian Elischer static ng_disconnect_t	ngp_disconnect;
1549c238a0eSJulian Elischer 
1559c238a0eSJulian Elischer /* Parse type for struct ng_pipe_hookstat */
1569c238a0eSJulian Elischer static const struct ng_parse_struct_field
1579c238a0eSJulian Elischer 	ng_pipe_hookstat_type_fields[] = NG_PIPE_HOOKSTAT_INFO;
1589c238a0eSJulian Elischer static const struct ng_parse_type ng_pipe_hookstat_type = {
1599c238a0eSJulian Elischer 	&ng_parse_struct_type,
1609c238a0eSJulian Elischer 	&ng_pipe_hookstat_type_fields
1619c238a0eSJulian Elischer };
1629c238a0eSJulian Elischer 
1639c238a0eSJulian Elischer /* Parse type for struct ng_pipe_stats */
1649c238a0eSJulian Elischer static const struct ng_parse_struct_field ng_pipe_stats_type_fields[] =
1659c238a0eSJulian Elischer 	NG_PIPE_STATS_INFO(&ng_pipe_hookstat_type);
1669c238a0eSJulian Elischer static const struct ng_parse_type ng_pipe_stats_type = {
1679c238a0eSJulian Elischer 	&ng_parse_struct_type,
1689c238a0eSJulian Elischer 	&ng_pipe_stats_type_fields
1699c238a0eSJulian Elischer };
1709c238a0eSJulian Elischer 
1719c238a0eSJulian Elischer /* Parse type for struct ng_pipe_hookrun */
1729c238a0eSJulian Elischer static const struct ng_parse_struct_field
1739c238a0eSJulian Elischer 	ng_pipe_hookrun_type_fields[] = NG_PIPE_HOOKRUN_INFO;
1749c238a0eSJulian Elischer static const struct ng_parse_type ng_pipe_hookrun_type = {
1759c238a0eSJulian Elischer 	&ng_parse_struct_type,
1769c238a0eSJulian Elischer 	&ng_pipe_hookrun_type_fields
1779c238a0eSJulian Elischer };
1789c238a0eSJulian Elischer 
1799c238a0eSJulian Elischer /* Parse type for struct ng_pipe_run */
1809c238a0eSJulian Elischer static const struct ng_parse_struct_field
1819c238a0eSJulian Elischer 	ng_pipe_run_type_fields[] = NG_PIPE_RUN_INFO(&ng_pipe_hookrun_type);
1829c238a0eSJulian Elischer static const struct ng_parse_type ng_pipe_run_type = {
1839c238a0eSJulian Elischer 	&ng_parse_struct_type,
1849c238a0eSJulian Elischer 	&ng_pipe_run_type_fields
1859c238a0eSJulian Elischer };
1869c238a0eSJulian Elischer 
1879c238a0eSJulian Elischer /* Parse type for struct ng_pipe_hookcfg */
1889c238a0eSJulian Elischer static const struct ng_parse_struct_field
1899c238a0eSJulian Elischer 	ng_pipe_hookcfg_type_fields[] = NG_PIPE_HOOKCFG_INFO;
1909c238a0eSJulian Elischer static const struct ng_parse_type ng_pipe_hookcfg_type = {
1919c238a0eSJulian Elischer 	&ng_parse_struct_type,
1929c238a0eSJulian Elischer 	&ng_pipe_hookcfg_type_fields
1939c238a0eSJulian Elischer };
1949c238a0eSJulian Elischer 
1959c238a0eSJulian Elischer /* Parse type for struct ng_pipe_cfg */
1969c238a0eSJulian Elischer static const struct ng_parse_struct_field
1979c238a0eSJulian Elischer 	ng_pipe_cfg_type_fields[] = NG_PIPE_CFG_INFO(&ng_pipe_hookcfg_type);
1989c238a0eSJulian Elischer static const struct ng_parse_type ng_pipe_cfg_type = {
1999c238a0eSJulian Elischer 	&ng_parse_struct_type,
2009c238a0eSJulian Elischer 	&ng_pipe_cfg_type_fields
2019c238a0eSJulian Elischer };
2029c238a0eSJulian Elischer 
2039c238a0eSJulian Elischer /* List of commands and how to convert arguments to/from ASCII */
2049c238a0eSJulian Elischer static const struct ng_cmdlist ngp_cmds[] = {
2059c238a0eSJulian Elischer 	{
2069c238a0eSJulian Elischer 		.cookie =	NGM_PIPE_COOKIE,
2079c238a0eSJulian Elischer 		.cmd =		NGM_PIPE_GET_STATS,
2089c238a0eSJulian Elischer 		.name = 	"getstats",
2099c238a0eSJulian Elischer 		.respType =	 &ng_pipe_stats_type
2109c238a0eSJulian Elischer 	},
2119c238a0eSJulian Elischer 	{
2129c238a0eSJulian Elischer 		.cookie =	NGM_PIPE_COOKIE,
2139c238a0eSJulian Elischer 		.cmd =		NGM_PIPE_CLR_STATS,
2149c238a0eSJulian Elischer 		.name =		"clrstats"
2159c238a0eSJulian Elischer 	},
2169c238a0eSJulian Elischer 	{
2179c238a0eSJulian Elischer 		.cookie =	NGM_PIPE_COOKIE,
2189c238a0eSJulian Elischer 		.cmd =		NGM_PIPE_GETCLR_STATS,
2199c238a0eSJulian Elischer 		.name =		"getclrstats",
2209c238a0eSJulian Elischer 		.respType =	&ng_pipe_stats_type
2219c238a0eSJulian Elischer 	},
2229c238a0eSJulian Elischer 	{
2239c238a0eSJulian Elischer 		.cookie =	NGM_PIPE_COOKIE,
2249c238a0eSJulian Elischer 		.cmd =		NGM_PIPE_GET_RUN,
2259c238a0eSJulian Elischer 		.name =		"getrun",
2269c238a0eSJulian Elischer 		.respType =	&ng_pipe_run_type
2279c238a0eSJulian Elischer 	},
2289c238a0eSJulian Elischer 	{
2299c238a0eSJulian Elischer 		.cookie =	NGM_PIPE_COOKIE,
2309c238a0eSJulian Elischer 		.cmd =		NGM_PIPE_GET_CFG,
2319c238a0eSJulian Elischer 		.name =		"getcfg",
2329c238a0eSJulian Elischer 		.respType =	&ng_pipe_cfg_type
2339c238a0eSJulian Elischer 	},
2349c238a0eSJulian Elischer 	{
2359c238a0eSJulian Elischer 		.cookie =	NGM_PIPE_COOKIE,
2369c238a0eSJulian Elischer 		.cmd =		NGM_PIPE_SET_CFG,
2379c238a0eSJulian Elischer 		.name =		"setcfg",
2389c238a0eSJulian Elischer 		.mesgType =	&ng_pipe_cfg_type,
2399c238a0eSJulian Elischer 	},
2409c238a0eSJulian Elischer 	{ 0 }
2419c238a0eSJulian Elischer };
2429c238a0eSJulian Elischer 
2439c238a0eSJulian Elischer /* Netgraph type descriptor */
2449c238a0eSJulian Elischer static struct ng_type ng_pipe_typestruct = {
2459c238a0eSJulian Elischer 	.version =	NG_ABI_VERSION,
2469c238a0eSJulian Elischer 	.name =		NG_PIPE_NODE_TYPE,
2479c238a0eSJulian Elischer 	.mod_event =	ngp_modevent,
2489c238a0eSJulian Elischer 	.constructor =	ngp_constructor,
2499c238a0eSJulian Elischer 	.shutdown =	ngp_shutdown,
2509c238a0eSJulian Elischer 	.rcvmsg =	ngp_rcvmsg,
2519c238a0eSJulian Elischer 	.newhook =	ngp_newhook,
2529c238a0eSJulian Elischer 	.rcvdata =	ngp_rcvdata,
2539c238a0eSJulian Elischer 	.disconnect =	ngp_disconnect,
2549c238a0eSJulian Elischer 	.cmdlist =	ngp_cmds
2559c238a0eSJulian Elischer };
2569c238a0eSJulian Elischer NETGRAPH_INIT(pipe, &ng_pipe_typestruct);
2579c238a0eSJulian Elischer 
2589c238a0eSJulian Elischer /* Node constructor */
2599c238a0eSJulian Elischer static int
2609c238a0eSJulian Elischer ngp_constructor(node_p node)
2619c238a0eSJulian Elischer {
2629c238a0eSJulian Elischer 	priv_p priv;
2639c238a0eSJulian Elischer 
2641ede983cSDag-Erling Smørgrav 	priv = malloc(sizeof(*priv), M_NG_PIPE, M_ZERO | M_NOWAIT);
2659c238a0eSJulian Elischer 	if (priv == NULL)
2669c238a0eSJulian Elischer 		return (ENOMEM);
2679c238a0eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
2689c238a0eSJulian Elischer 
2699c238a0eSJulian Elischer 	return (0);
2709c238a0eSJulian Elischer }
2719c238a0eSJulian Elischer 
2729c238a0eSJulian Elischer /* Add a hook */
2739c238a0eSJulian Elischer static int
2749c238a0eSJulian Elischer ngp_newhook(node_p node, hook_p hook, const char *name)
2759c238a0eSJulian Elischer {
2769c238a0eSJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
2779c238a0eSJulian Elischer 	struct hookinfo *hinfo;
2789c238a0eSJulian Elischer 
2799c238a0eSJulian Elischer 	if (strcmp(name, NG_PIPE_HOOK_UPPER) == 0) {
2809c238a0eSJulian Elischer 		bzero(&priv->upper, sizeof(priv->upper));
2819c238a0eSJulian Elischer 		priv->upper.hook = hook;
2829c238a0eSJulian Elischer 		NG_HOOK_SET_PRIVATE(hook, &priv->upper);
2839c238a0eSJulian Elischer 	} else if (strcmp(name, NG_PIPE_HOOK_LOWER) == 0) {
2849c238a0eSJulian Elischer 		bzero(&priv->lower, sizeof(priv->lower));
2859c238a0eSJulian Elischer 		priv->lower.hook = hook;
2869c238a0eSJulian Elischer 		NG_HOOK_SET_PRIVATE(hook, &priv->lower);
2879c238a0eSJulian Elischer 	} else
2889c238a0eSJulian Elischer 		return (EINVAL);
2899c238a0eSJulian Elischer 
2909c238a0eSJulian Elischer 	/* Load non-zero initial cfg values */
2919c238a0eSJulian Elischer 	hinfo = NG_HOOK_PRIVATE(hook);
2929c238a0eSJulian Elischer 	hinfo->cfg.qin_size_limit = 50;
2939c238a0eSJulian Elischer 	hinfo->cfg.fifo = 1;
2949c238a0eSJulian Elischer 	hinfo->cfg.droptail = 1;
2959c238a0eSJulian Elischer 	TAILQ_INIT(&hinfo->fifo_head);
2969c238a0eSJulian Elischer 	TAILQ_INIT(&hinfo->qout_head);
2979c238a0eSJulian Elischer 	return (0);
2989c238a0eSJulian Elischer }
2999c238a0eSJulian Elischer 
3009c238a0eSJulian Elischer /* Receive a control message */
3019c238a0eSJulian Elischer static int
3029c238a0eSJulian Elischer ngp_rcvmsg(node_p node, item_p item, hook_p lasthook)
3039c238a0eSJulian Elischer {
3049c238a0eSJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
3059c238a0eSJulian Elischer 	struct ng_mesg *resp = NULL;
3069c238a0eSJulian Elischer 	struct ng_mesg *msg;
3079c238a0eSJulian Elischer 	struct ng_pipe_stats *stats;
3089c238a0eSJulian Elischer 	struct ng_pipe_run *run;
3099c238a0eSJulian Elischer 	struct ng_pipe_cfg *cfg;
3109c238a0eSJulian Elischer 	int error = 0;
3119c238a0eSJulian Elischer 
3129c238a0eSJulian Elischer 	mtx_lock(&ng_pipe_giant);
3139c238a0eSJulian Elischer 
3149c238a0eSJulian Elischer 	NGI_GET_MSG(item, msg);
3159c238a0eSJulian Elischer 	switch (msg->header.typecookie) {
3169c238a0eSJulian Elischer 	case NGM_PIPE_COOKIE:
3179c238a0eSJulian Elischer 		switch (msg->header.cmd) {
3189c238a0eSJulian Elischer 		case NGM_PIPE_GET_STATS:
3199c238a0eSJulian Elischer 		case NGM_PIPE_CLR_STATS:
3209c238a0eSJulian Elischer 		case NGM_PIPE_GETCLR_STATS:
3219c238a0eSJulian Elischer 			if (msg->header.cmd != NGM_PIPE_CLR_STATS) {
3229c238a0eSJulian Elischer 				NG_MKRESPONSE(resp, msg,
3239c238a0eSJulian Elischer 				    sizeof(*stats), M_NOWAIT);
3249c238a0eSJulian Elischer 				if (resp == NULL) {
3259c238a0eSJulian Elischer 					error = ENOMEM;
3269c238a0eSJulian Elischer 					break;
3279c238a0eSJulian Elischer 				}
3289c238a0eSJulian Elischer 				stats = (struct ng_pipe_stats *)resp->data;
3299c238a0eSJulian Elischer 				bcopy(&priv->upper.stats, &stats->downstream,
3309c238a0eSJulian Elischer 				    sizeof(stats->downstream));
3319c238a0eSJulian Elischer 				bcopy(&priv->lower.stats, &stats->upstream,
3329c238a0eSJulian Elischer 				    sizeof(stats->upstream));
3339c238a0eSJulian Elischer 			}
3349c238a0eSJulian Elischer 			if (msg->header.cmd != NGM_PIPE_GET_STATS) {
3359c238a0eSJulian Elischer 				bzero(&priv->upper.stats,
3369c238a0eSJulian Elischer 				    sizeof(priv->upper.stats));
3379c238a0eSJulian Elischer 				bzero(&priv->lower.stats,
3389c238a0eSJulian Elischer 				    sizeof(priv->lower.stats));
3399c238a0eSJulian Elischer 			}
3409c238a0eSJulian Elischer 			break;
3419c238a0eSJulian Elischer 		case NGM_PIPE_GET_RUN:
3429c238a0eSJulian Elischer 			NG_MKRESPONSE(resp, msg, sizeof(*run), M_NOWAIT);
3439c238a0eSJulian Elischer 			if (resp == NULL) {
3449c238a0eSJulian Elischer 				error = ENOMEM;
3459c238a0eSJulian Elischer 				break;
3469c238a0eSJulian Elischer 			}
3479c238a0eSJulian Elischer 			run = (struct ng_pipe_run *)resp->data;
3489c238a0eSJulian Elischer 			bcopy(&priv->upper.run, &run->downstream,
3499c238a0eSJulian Elischer 				sizeof(run->downstream));
3509c238a0eSJulian Elischer 			bcopy(&priv->lower.run, &run->upstream,
3519c238a0eSJulian Elischer 				sizeof(run->upstream));
3529c238a0eSJulian Elischer 			break;
3539c238a0eSJulian Elischer 		case NGM_PIPE_GET_CFG:
3549c238a0eSJulian Elischer 			NG_MKRESPONSE(resp, msg, sizeof(*cfg), M_NOWAIT);
3559c238a0eSJulian Elischer 			if (resp == NULL) {
3569c238a0eSJulian Elischer 				error = ENOMEM;
3579c238a0eSJulian Elischer 				break;
3589c238a0eSJulian Elischer 			}
3599c238a0eSJulian Elischer 			cfg = (struct ng_pipe_cfg *)resp->data;
3609c238a0eSJulian Elischer 			bcopy(&priv->upper.cfg, &cfg->downstream,
3619c238a0eSJulian Elischer 				sizeof(cfg->downstream));
3629c238a0eSJulian Elischer 			bcopy(&priv->lower.cfg, &cfg->upstream,
3639c238a0eSJulian Elischer 				sizeof(cfg->upstream));
3649c238a0eSJulian Elischer 			cfg->delay = priv->delay;
3659c238a0eSJulian Elischer 			cfg->overhead = priv->overhead;
3669c238a0eSJulian Elischer 			cfg->header_offset = priv->header_offset;
3679c238a0eSJulian Elischer 			if (cfg->upstream.bandwidth ==
3689c238a0eSJulian Elischer 			    cfg->downstream.bandwidth) {
3699c238a0eSJulian Elischer 				cfg->bandwidth = cfg->upstream.bandwidth;
3709c238a0eSJulian Elischer 				cfg->upstream.bandwidth = 0;
3719c238a0eSJulian Elischer 				cfg->downstream.bandwidth = 0;
3729c238a0eSJulian Elischer 			} else
3739c238a0eSJulian Elischer 				cfg->bandwidth = 0;
3749c238a0eSJulian Elischer 			break;
3759c238a0eSJulian Elischer 		case NGM_PIPE_SET_CFG:
3769c238a0eSJulian Elischer 			cfg = (struct ng_pipe_cfg *)msg->data;
3779c238a0eSJulian Elischer 			if (msg->header.arglen != sizeof(*cfg)) {
3789c238a0eSJulian Elischer 				error = EINVAL;
3799c238a0eSJulian Elischer 				break;
3809c238a0eSJulian Elischer 			}
3819c238a0eSJulian Elischer 
3829c238a0eSJulian Elischer 			if (cfg->delay == -1)
3839c238a0eSJulian Elischer 				priv->delay = 0;
3849c238a0eSJulian Elischer 			else if (cfg->delay > 0 && cfg->delay < 10000000)
3859c238a0eSJulian Elischer 				priv->delay = cfg->delay;
3869c238a0eSJulian Elischer 
3879c238a0eSJulian Elischer 			if (cfg->bandwidth == -1) {
3889c238a0eSJulian Elischer 				priv->upper.cfg.bandwidth = 0;
3899c238a0eSJulian Elischer 				priv->lower.cfg.bandwidth = 0;
3909c238a0eSJulian Elischer 				priv->overhead = 0;
3919c238a0eSJulian Elischer 			} else if (cfg->bandwidth >= 100 &&
3929c238a0eSJulian Elischer 			    cfg->bandwidth <= 1000000000) {
3939c238a0eSJulian Elischer 				priv->upper.cfg.bandwidth = cfg->bandwidth;
3949c238a0eSJulian Elischer 				priv->lower.cfg.bandwidth = cfg->bandwidth;
3959c238a0eSJulian Elischer 				if (cfg->bandwidth >= 10000000)
3969c238a0eSJulian Elischer 					priv->overhead = 8+4+12; /* Ethernet */
3979c238a0eSJulian Elischer 				else
3989c238a0eSJulian Elischer 					priv->overhead = 10; /* HDLC */
3999c238a0eSJulian Elischer 			}
4009c238a0eSJulian Elischer 
4019c238a0eSJulian Elischer 			if (cfg->overhead == -1)
4029c238a0eSJulian Elischer 				priv->overhead = 0;
4039c238a0eSJulian Elischer 			else if (cfg->overhead > 0 && cfg->overhead < 256)
4049c238a0eSJulian Elischer 				priv->overhead = cfg->overhead;
4059c238a0eSJulian Elischer 
4069c238a0eSJulian Elischer 			if (cfg->header_offset == -1)
4079c238a0eSJulian Elischer 				priv->header_offset = 0;
4089c238a0eSJulian Elischer 			else if (cfg->header_offset > 0 &&
4099c238a0eSJulian Elischer 			    cfg->header_offset < 64)
4109c238a0eSJulian Elischer 				priv->header_offset = cfg->header_offset;
4119c238a0eSJulian Elischer 
4129c238a0eSJulian Elischer 			parse_cfg(&priv->upper.cfg, &cfg->downstream,
4139c238a0eSJulian Elischer 				  &priv->upper, priv);
4149c238a0eSJulian Elischer 			parse_cfg(&priv->lower.cfg, &cfg->upstream,
4159c238a0eSJulian Elischer 				  &priv->lower, priv);
4169c238a0eSJulian Elischer 			break;
4179c238a0eSJulian Elischer 		default:
4189c238a0eSJulian Elischer 			error = EINVAL;
4199c238a0eSJulian Elischer 			break;
4209c238a0eSJulian Elischer 		}
4219c238a0eSJulian Elischer 		break;
4229c238a0eSJulian Elischer 	default:
4239c238a0eSJulian Elischer 		error = EINVAL;
4249c238a0eSJulian Elischer 		break;
4259c238a0eSJulian Elischer 	}
4269c238a0eSJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
4279c238a0eSJulian Elischer 	NG_FREE_MSG(msg);
4289c238a0eSJulian Elischer 
4299c238a0eSJulian Elischer 	mtx_unlock(&ng_pipe_giant);
4309c238a0eSJulian Elischer 
4319c238a0eSJulian Elischer 	return (error);
4329c238a0eSJulian Elischer }
4339c238a0eSJulian Elischer 
4349c238a0eSJulian Elischer static void
4359c238a0eSJulian Elischer parse_cfg(struct ng_pipe_hookcfg *current, struct ng_pipe_hookcfg *new,
4369c238a0eSJulian Elischer 	struct hookinfo *hinfo, priv_p priv)
4379c238a0eSJulian Elischer {
4389c238a0eSJulian Elischer 
4399c238a0eSJulian Elischer 	if (new->ber == -1) {
4409c238a0eSJulian Elischer 		current->ber = 0;
4419c238a0eSJulian Elischer 		if (hinfo->ber_p) {
4421ede983cSDag-Erling Smørgrav 			free(hinfo->ber_p, M_NG_PIPE);
4439c238a0eSJulian Elischer 			hinfo->ber_p = NULL;
4449c238a0eSJulian Elischer 		}
4459c238a0eSJulian Elischer 	} else if (new->ber >= 1 && new->ber <= 1000000000000) {
4469c238a0eSJulian Elischer 		static const uint64_t one = 0x1000000000000; /* = 2^48 */
4479c238a0eSJulian Elischer 		uint64_t p0, p;
4489c238a0eSJulian Elischer 		uint32_t fsize, i;
4499c238a0eSJulian Elischer 
4509c238a0eSJulian Elischer 		if (hinfo->ber_p == NULL)
4511ede983cSDag-Erling Smørgrav 			hinfo->ber_p = malloc(\
4529c238a0eSJulian Elischer 				(MAX_FSIZE + MAX_OHSIZE)*sizeof(uint64_t), \
4539c238a0eSJulian Elischer 				M_NG_PIPE, M_NOWAIT);
4549c238a0eSJulian Elischer 		current->ber = new->ber;
4559c238a0eSJulian Elischer 
4569c238a0eSJulian Elischer 		/*
4579c238a0eSJulian Elischer 		 * For given BER and each frame size N (in bytes) calculate
4589c238a0eSJulian Elischer 		 * the probability P_OK that the frame is clean:
4599c238a0eSJulian Elischer 		 *
4609c238a0eSJulian Elischer 		 * P_OK(BER,N) = (1 - 1/BER)^(N*8)
4619c238a0eSJulian Elischer 		 *
4629c238a0eSJulian Elischer 		 * We use a 64-bit fixed-point format with decimal point
4639c238a0eSJulian Elischer 		 * positioned between bits 47 and 48.
4649c238a0eSJulian Elischer 		 */
4659c238a0eSJulian Elischer 		p0 = one - one / new->ber;
4669c238a0eSJulian Elischer 		p = one;
4679c238a0eSJulian Elischer 		for (fsize = 0; fsize < MAX_FSIZE + MAX_OHSIZE; fsize++) {
4689c238a0eSJulian Elischer 			hinfo->ber_p[fsize] = p;
4699c238a0eSJulian Elischer 			for (i=0; i<8; i++)
4709c238a0eSJulian Elischer 				p = (p*(p0&0xffff)>>48) + \
4719c238a0eSJulian Elischer 				    (p*((p0>>16)&0xffff)>>32) + \
4729c238a0eSJulian Elischer 				    (p*(p0>>32)>>16);
4739c238a0eSJulian Elischer 		}
4749c238a0eSJulian Elischer 	}
4759c238a0eSJulian Elischer 
4769c238a0eSJulian Elischer 	if (new->qin_size_limit == -1)
4779c238a0eSJulian Elischer 		current->qin_size_limit = 0;
4789c238a0eSJulian Elischer 	else if (new->qin_size_limit >= 5)
4799c238a0eSJulian Elischer 		current->qin_size_limit = new->qin_size_limit;
4809c238a0eSJulian Elischer 
4819c238a0eSJulian Elischer 	if (new->qout_size_limit == -1)
4829c238a0eSJulian Elischer 		current->qout_size_limit = 0;
4839c238a0eSJulian Elischer 	else if (new->qout_size_limit >= 5)
4849c238a0eSJulian Elischer 		current->qout_size_limit = new->qout_size_limit;
4859c238a0eSJulian Elischer 
4869c238a0eSJulian Elischer 	if (new->duplicate == -1)
4879c238a0eSJulian Elischer 		current->duplicate = 0;
4889c238a0eSJulian Elischer 	else if (new->duplicate > 0 && new->duplicate <= 50)
4899c238a0eSJulian Elischer 		current->duplicate = new->duplicate;
4909c238a0eSJulian Elischer 
4919c238a0eSJulian Elischer 	if (new->fifo) {
4929c238a0eSJulian Elischer 		current->fifo = 1;
4939c238a0eSJulian Elischer 		current->wfq = 0;
4949c238a0eSJulian Elischer 		current->drr = 0;
4959c238a0eSJulian Elischer 	}
4969c238a0eSJulian Elischer 
4979c238a0eSJulian Elischer 	if (new->wfq) {
4989c238a0eSJulian Elischer 		current->fifo = 0;
4999c238a0eSJulian Elischer 		current->wfq = 1;
5009c238a0eSJulian Elischer 		current->drr = 0;
5019c238a0eSJulian Elischer 	}
5029c238a0eSJulian Elischer 
5039c238a0eSJulian Elischer 	if (new->drr) {
5049c238a0eSJulian Elischer 		current->fifo = 0;
5059c238a0eSJulian Elischer 		current->wfq = 0;
5069c238a0eSJulian Elischer 		/* DRR quantum */
5079c238a0eSJulian Elischer 		if (new->drr >= 32)
5089c238a0eSJulian Elischer 			current->drr = new->drr;
5099c238a0eSJulian Elischer 		else
5109c238a0eSJulian Elischer 			current->drr = 2048;		/* default quantum */
5119c238a0eSJulian Elischer 	}
5129c238a0eSJulian Elischer 
5139c238a0eSJulian Elischer 	if (new->droptail) {
5149c238a0eSJulian Elischer 		current->droptail = 1;
5159c238a0eSJulian Elischer 		current->drophead = 0;
5169c238a0eSJulian Elischer 	}
5179c238a0eSJulian Elischer 
5189c238a0eSJulian Elischer 	if (new->drophead) {
5199c238a0eSJulian Elischer 		current->droptail = 0;
5209c238a0eSJulian Elischer 		current->drophead = 1;
5219c238a0eSJulian Elischer 	}
5229c238a0eSJulian Elischer 
5239c238a0eSJulian Elischer 	if (new->bandwidth == -1) {
5249c238a0eSJulian Elischer 		current->bandwidth = 0;
5259c238a0eSJulian Elischer 		current->fifo = 1;
5269c238a0eSJulian Elischer 		current->wfq = 0;
5279c238a0eSJulian Elischer 		current->drr = 0;
5289c238a0eSJulian Elischer 	} else if (new->bandwidth >= 100 && new->bandwidth <= 1000000000)
5299c238a0eSJulian Elischer 		current->bandwidth = new->bandwidth;
5309c238a0eSJulian Elischer 
5319c238a0eSJulian Elischer 	if (current->bandwidth | priv->delay |
5329c238a0eSJulian Elischer 	    current->duplicate | current->ber)
5339c238a0eSJulian Elischer 		hinfo->noqueue = 0;
5349c238a0eSJulian Elischer 	else
5359c238a0eSJulian Elischer 		hinfo->noqueue = 1;
5369c238a0eSJulian Elischer }
5379c238a0eSJulian Elischer 
5389c238a0eSJulian Elischer /*
5399c238a0eSJulian Elischer  * Compute a hash signature for a packet. This function suffers from the
5409c238a0eSJulian Elischer  * NIH sindrome, so probably it would be wise to look around what other
5419c238a0eSJulian Elischer  * folks have found out to be a good and efficient IP hash function...
5429c238a0eSJulian Elischer  */
5439c238a0eSJulian Elischer static int
5449c238a0eSJulian Elischer ip_hash(struct mbuf *m, int offset)
5459c238a0eSJulian Elischer {
5469c238a0eSJulian Elischer 	u_int64_t i;
5479c238a0eSJulian Elischer 	struct ip *ip = (struct ip *)(mtod(m, u_char *) + offset);
5489c238a0eSJulian Elischer 
5499c238a0eSJulian Elischer 	if (m->m_len < sizeof(struct ip) + offset ||
5509c238a0eSJulian Elischer 	    ip->ip_v != 4 || ip->ip_hl << 2 != sizeof(struct ip))
5519c238a0eSJulian Elischer 		return 0;
5529c238a0eSJulian Elischer 
5539c238a0eSJulian Elischer 	i = ((u_int64_t) ip->ip_src.s_addr ^
5549c238a0eSJulian Elischer 	    ((u_int64_t) ip->ip_src.s_addr << 13) ^
5559c238a0eSJulian Elischer 	    ((u_int64_t) ip->ip_dst.s_addr << 7) ^
5569c238a0eSJulian Elischer 	    ((u_int64_t) ip->ip_dst.s_addr << 19));
5579c238a0eSJulian Elischer 	return (i ^ (i >> 32));
5589c238a0eSJulian Elischer }
5599c238a0eSJulian Elischer 
5609c238a0eSJulian Elischer /*
5619c238a0eSJulian Elischer  * Receive data on a hook - both in upstream and downstream direction.
5629c238a0eSJulian Elischer  * We put the frame on the inbound queue, and try to initiate dequeuing
5639c238a0eSJulian Elischer  * sequence immediately. If inbound queue is full, discard one frame
5649c238a0eSJulian Elischer  * depending on dropping policy (from the head or from the tail of the
5659c238a0eSJulian Elischer  * queue).
5669c238a0eSJulian Elischer  */
5679c238a0eSJulian Elischer static int
5689c238a0eSJulian Elischer ngp_rcvdata(hook_p hook, item_p item)
5699c238a0eSJulian Elischer {
5709c238a0eSJulian Elischer 	struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
5719c238a0eSJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
5729c238a0eSJulian Elischer 	struct timeval uuptime;
5739c238a0eSJulian Elischer 	struct timeval *now = &uuptime;
5749c238a0eSJulian Elischer 	struct ngp_fifo *ngp_f = NULL, *ngp_f1;
5759c238a0eSJulian Elischer 	struct ngp_hdr *ngp_h = NULL;
5769c238a0eSJulian Elischer 	struct mbuf *m;
5779c238a0eSJulian Elischer 	int hash;
5789c238a0eSJulian Elischer 	int error = 0;
5799c238a0eSJulian Elischer 
5809c238a0eSJulian Elischer 	if (hinfo->noqueue) {
5819c238a0eSJulian Elischer 		struct hookinfo *dest;
5829c238a0eSJulian Elischer 		if (hinfo == &priv->lower)
5839c238a0eSJulian Elischer 			dest = &priv->upper;
5849c238a0eSJulian Elischer 		else
5859c238a0eSJulian Elischer 			dest = &priv->lower;
5869c238a0eSJulian Elischer 		NG_FWD_ITEM_HOOK(error, item, dest->hook);
5879c238a0eSJulian Elischer 		return error;
5889c238a0eSJulian Elischer 	}
5899c238a0eSJulian Elischer 
5909c238a0eSJulian Elischer 	mtx_lock(&ng_pipe_giant);
5919c238a0eSJulian Elischer 	microuptime(now);
5929c238a0eSJulian Elischer 
5939c238a0eSJulian Elischer 	/*
5949c238a0eSJulian Elischer 	 * Attach us to the list of active ng_pipes if this was an empty
5959c238a0eSJulian Elischer 	 * one before, and also update the queue service deadline time.
5969c238a0eSJulian Elischer 	 */
5979c238a0eSJulian Elischer 	if (hinfo->run.qin_frames == 0) {
5989c238a0eSJulian Elischer 		struct timeval *when = &hinfo->qin_utime;
5999c238a0eSJulian Elischer 		if (when->tv_sec < now->tv_sec || (when->tv_sec == now->tv_sec
6009c238a0eSJulian Elischer 		    && when->tv_usec < now->tv_usec)) {
6019c238a0eSJulian Elischer 			when->tv_sec = now->tv_sec;
6029c238a0eSJulian Elischer 			when->tv_usec = now->tv_usec;
6039c238a0eSJulian Elischer 		}
6049c238a0eSJulian Elischer 		if (hinfo->run.qout_frames == 0)
6059c238a0eSJulian Elischer 			LIST_INSERT_HEAD(&active_head, hinfo, active_le);
6069c238a0eSJulian Elischer 	}
6079c238a0eSJulian Elischer 
6089c238a0eSJulian Elischer 	/* Populate the packet header */
6099c238a0eSJulian Elischer 	ngp_h = uma_zalloc(ngp_zone, M_NOWAIT);
6109c238a0eSJulian Elischer 	KASSERT((ngp_h != NULL), ("ngp_h zalloc failed (1)"));
6119c238a0eSJulian Elischer 	NGI_GET_M(item, m);
6129c238a0eSJulian Elischer 	KASSERT(m != NULL, ("NGI_GET_M failed"));
6139c238a0eSJulian Elischer 	ngp_h->m = m;
6149c238a0eSJulian Elischer 	NG_FREE_ITEM(item);
6159c238a0eSJulian Elischer 
6169c238a0eSJulian Elischer 	if (hinfo->cfg.fifo)
6179c238a0eSJulian Elischer 		hash = 0;	/* all packets go into a single FIFO queue */
6189c238a0eSJulian Elischer 	else
6199c238a0eSJulian Elischer 		hash = ip_hash(m, priv->header_offset);
6209c238a0eSJulian Elischer 
6219c238a0eSJulian Elischer 	/* Find the appropriate FIFO queue for the packet and enqueue it*/
6229c238a0eSJulian Elischer 	TAILQ_FOREACH(ngp_f, &hinfo->fifo_head, fifo_le)
6239c238a0eSJulian Elischer 		if (hash == ngp_f->hash)
6249c238a0eSJulian Elischer 			break;
6259c238a0eSJulian Elischer 	if (ngp_f == NULL) {
6269c238a0eSJulian Elischer 		ngp_f = uma_zalloc(ngp_zone, M_NOWAIT);
6279c238a0eSJulian Elischer 		KASSERT(ngp_h != NULL, ("ngp_h zalloc failed (2)"));
6289c238a0eSJulian Elischer 		TAILQ_INIT(&ngp_f->packet_head);
6299c238a0eSJulian Elischer 		ngp_f->hash = hash;
6309c238a0eSJulian Elischer 		ngp_f->packets = 1;
6319c238a0eSJulian Elischer 		ngp_f->rr_deficit = hinfo->cfg.drr;	/* DRR quantum */
6329c238a0eSJulian Elischer 		hinfo->run.fifo_queues++;
6339c238a0eSJulian Elischer 		TAILQ_INSERT_TAIL(&ngp_f->packet_head, ngp_h, ngp_link);
6349c238a0eSJulian Elischer 		FIFO_VTIME_SORT(m->m_pkthdr.len);
6359c238a0eSJulian Elischer 	} else {
6369c238a0eSJulian Elischer 		TAILQ_INSERT_TAIL(&ngp_f->packet_head, ngp_h, ngp_link);
6379c238a0eSJulian Elischer 		ngp_f->packets++;
6389c238a0eSJulian Elischer 	}
6399c238a0eSJulian Elischer 	hinfo->run.qin_frames++;
6409c238a0eSJulian Elischer 	hinfo->run.qin_octets += m->m_pkthdr.len;
6419c238a0eSJulian Elischer 
6429c238a0eSJulian Elischer 	/* Discard a frame if inbound queue limit has been reached */
6439c238a0eSJulian Elischer 	if (hinfo->run.qin_frames > hinfo->cfg.qin_size_limit) {
6449c238a0eSJulian Elischer 		struct mbuf *m1;
6459c238a0eSJulian Elischer 		int longest = 0;
6469c238a0eSJulian Elischer 
6479c238a0eSJulian Elischer 		/* Find the longest queue */
6489c238a0eSJulian Elischer 		TAILQ_FOREACH(ngp_f1, &hinfo->fifo_head, fifo_le)
6499c238a0eSJulian Elischer 			if (ngp_f1->packets > longest) {
6509c238a0eSJulian Elischer 				longest = ngp_f1->packets;
6519c238a0eSJulian Elischer 				ngp_f = ngp_f1;
6529c238a0eSJulian Elischer 			}
6539c238a0eSJulian Elischer 
6549c238a0eSJulian Elischer 		/* Drop a frame from the queue head/tail, depending on cfg */
6559c238a0eSJulian Elischer 		if (hinfo->cfg.drophead)
6569c238a0eSJulian Elischer 			ngp_h = TAILQ_FIRST(&ngp_f->packet_head);
6579c238a0eSJulian Elischer 		else
6589c238a0eSJulian Elischer 			ngp_h = TAILQ_LAST(&ngp_f->packet_head, p_head);
6599c238a0eSJulian Elischer 		TAILQ_REMOVE(&ngp_f->packet_head, ngp_h, ngp_link);
6609c238a0eSJulian Elischer 		m1 = ngp_h->m;
6619c238a0eSJulian Elischer 		uma_zfree(ngp_zone, ngp_h);
6629c238a0eSJulian Elischer 		hinfo->run.qin_octets -= m1->m_pkthdr.len;
6639c238a0eSJulian Elischer 		hinfo->stats.in_disc_octets += m1->m_pkthdr.len;
6649c238a0eSJulian Elischer 		m_freem(m1);
6659c238a0eSJulian Elischer 		if (--(ngp_f->packets) == 0) {
6669c238a0eSJulian Elischer 			TAILQ_REMOVE(&hinfo->fifo_head, ngp_f, fifo_le);
6679c238a0eSJulian Elischer 			uma_zfree(ngp_zone, ngp_f);
6689c238a0eSJulian Elischer 			hinfo->run.fifo_queues--;
6699c238a0eSJulian Elischer 		}
6709c238a0eSJulian Elischer 		hinfo->run.qin_frames--;
6719c238a0eSJulian Elischer 		hinfo->stats.in_disc_frames++;
6729c238a0eSJulian Elischer 	} else if (hinfo->run.qin_frames > hinfo->cfg.qin_size_limit) {
6739c238a0eSJulian Elischer 		struct mbuf *m1;
6749c238a0eSJulian Elischer 		int longest = 0;
6759c238a0eSJulian Elischer 
6769c238a0eSJulian Elischer 		/* Find the longest queue */
6779c238a0eSJulian Elischer 		TAILQ_FOREACH(ngp_f1, &hinfo->fifo_head, fifo_le)
6789c238a0eSJulian Elischer 			if (ngp_f1->packets > longest) {
6799c238a0eSJulian Elischer 				longest = ngp_f1->packets;
6809c238a0eSJulian Elischer 				ngp_f = ngp_f1;
6819c238a0eSJulian Elischer 			}
6829c238a0eSJulian Elischer 
6839c238a0eSJulian Elischer 		/* Drop a frame from the queue head/tail, depending on cfg */
6849c238a0eSJulian Elischer 		if (hinfo->cfg.drophead)
6859c238a0eSJulian Elischer 			ngp_h = TAILQ_FIRST(&ngp_f->packet_head);
6869c238a0eSJulian Elischer 		else
6879c238a0eSJulian Elischer 			ngp_h = TAILQ_LAST(&ngp_f->packet_head, p_head);
6889c238a0eSJulian Elischer 		TAILQ_REMOVE(&ngp_f->packet_head, ngp_h, ngp_link);
6899c238a0eSJulian Elischer 		m1 = ngp_h->m;
6909c238a0eSJulian Elischer 		uma_zfree(ngp_zone, ngp_h);
6919c238a0eSJulian Elischer 		hinfo->run.qin_octets -= m1->m_pkthdr.len;
6929c238a0eSJulian Elischer 		hinfo->stats.in_disc_octets += m1->m_pkthdr.len;
6939c238a0eSJulian Elischer 		m_freem(m1);
6949c238a0eSJulian Elischer 		if (--(ngp_f->packets) == 0) {
6959c238a0eSJulian Elischer 			TAILQ_REMOVE(&hinfo->fifo_head, ngp_f, fifo_le);
6969c238a0eSJulian Elischer 			uma_zfree(ngp_zone, ngp_f);
6979c238a0eSJulian Elischer 			hinfo->run.fifo_queues--;
6989c238a0eSJulian Elischer 		}
6999c238a0eSJulian Elischer 		hinfo->run.qin_frames--;
7009c238a0eSJulian Elischer 		hinfo->stats.in_disc_frames++;
7019c238a0eSJulian Elischer 	}
7029c238a0eSJulian Elischer 
7039c238a0eSJulian Elischer 	/*
7049c238a0eSJulian Elischer 	 * Try to start the dequeuing process immediately.  We must
7059c238a0eSJulian Elischer 	 * hold the ng_pipe_giant lock here and pipe_dequeue() will
7069c238a0eSJulian Elischer 	 * release it
7079c238a0eSJulian Elischer 	 */
7089c238a0eSJulian Elischer 	pipe_dequeue(hinfo, now);
7099c238a0eSJulian Elischer 
7109c238a0eSJulian Elischer 	return (0);
7119c238a0eSJulian Elischer }
7129c238a0eSJulian Elischer 
7139c238a0eSJulian Elischer 
7149c238a0eSJulian Elischer /*
7159c238a0eSJulian Elischer  * Dequeueing sequence - we basically do the following:
7169c238a0eSJulian Elischer  *  1) Try to extract the frame from the inbound (bandwidth) queue;
7179c238a0eSJulian Elischer  *  2) In accordance to BER specified, discard the frame randomly;
7189c238a0eSJulian Elischer  *  3) If the frame survives BER, prepend it with delay info and move it
7199c238a0eSJulian Elischer  *     to outbound (delay) queue;
7209c238a0eSJulian Elischer  *  4) Loop to 2) until bandwidth quota for this timeslice is reached, or
7219c238a0eSJulian Elischer  *     inbound queue is flushed completely;
7229c238a0eSJulian Elischer  *  5) Extract the first frame from the outbound queue, if it's time has
7239c238a0eSJulian Elischer  *     come.  Queue the frame for transmission on the outbound hook;
7249c238a0eSJulian Elischer  *  6) Loop to 5) until outbound queue is flushed completely, or the next
7259c238a0eSJulian Elischer  *     frame in the queue is not scheduled to be dequeued yet;
7269c238a0eSJulian Elischer  *  7) Transimit all frames queued in 5)
7279c238a0eSJulian Elischer  *
7289c238a0eSJulian Elischer  * Note: the caller must hold the ng_pipe_giant lock; this function
7299c238a0eSJulian Elischer  * returns with the lock released.
7309c238a0eSJulian Elischer  */
7319c238a0eSJulian Elischer static void
7329c238a0eSJulian Elischer pipe_dequeue(struct hookinfo *hinfo, struct timeval *now) {
7339c238a0eSJulian Elischer 	static uint64_t rand, oldrand;
7349c238a0eSJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hinfo->hook));
7359c238a0eSJulian Elischer 	struct hookinfo *dest;
7369c238a0eSJulian Elischer 	struct ngp_fifo *ngp_f, *ngp_f1;
7379c238a0eSJulian Elischer 	struct ngp_hdr *ngp_h;
7389c238a0eSJulian Elischer 	struct timeval *when;
7399c238a0eSJulian Elischer 	struct mbuf *q_head = NULL;
7409c238a0eSJulian Elischer 	struct mbuf *q_tail = NULL;
7419c238a0eSJulian Elischer 	struct mbuf *m;
7429c238a0eSJulian Elischer 	int error = 0;
7439c238a0eSJulian Elischer 
7449c238a0eSJulian Elischer 	/* Which one is the destination hook? */
7459c238a0eSJulian Elischer 	if (hinfo == &priv->lower)
7469c238a0eSJulian Elischer 		dest = &priv->upper;
7479c238a0eSJulian Elischer 	else
7489c238a0eSJulian Elischer 		dest = &priv->lower;
7499c238a0eSJulian Elischer 
7509c238a0eSJulian Elischer 	/* Bandwidth queue processing */
7519c238a0eSJulian Elischer 	while ((ngp_f = TAILQ_FIRST(&hinfo->fifo_head))) {
7529c238a0eSJulian Elischer 		when = &hinfo->qin_utime;
7539c238a0eSJulian Elischer 		if (when->tv_sec > now->tv_sec || (when->tv_sec == now->tv_sec
7549c238a0eSJulian Elischer 		    && when->tv_usec > now->tv_usec))
7559c238a0eSJulian Elischer 			break;
7569c238a0eSJulian Elischer 
7579c238a0eSJulian Elischer 		ngp_h = TAILQ_FIRST(&ngp_f->packet_head);
7589c238a0eSJulian Elischer 		m = ngp_h->m;
7599c238a0eSJulian Elischer 
7609c238a0eSJulian Elischer 		/* Deficit Round Robin (DRR) processing */
7619c238a0eSJulian Elischer 		if (hinfo->cfg.drr) {
7629c238a0eSJulian Elischer 			if (ngp_f->rr_deficit >= m->m_pkthdr.len) {
7639c238a0eSJulian Elischer 				ngp_f->rr_deficit -= m->m_pkthdr.len;
7649c238a0eSJulian Elischer 			} else {
7659c238a0eSJulian Elischer 				ngp_f->rr_deficit += hinfo->cfg.drr;
7669c238a0eSJulian Elischer 				TAILQ_REMOVE(&hinfo->fifo_head, ngp_f, fifo_le);
7679c238a0eSJulian Elischer 				TAILQ_INSERT_TAIL(&hinfo->fifo_head,
7689c238a0eSJulian Elischer 				    ngp_f, fifo_le);
7699c238a0eSJulian Elischer 				continue;
7709c238a0eSJulian Elischer 			}
7719c238a0eSJulian Elischer 		}
7729c238a0eSJulian Elischer 
7739c238a0eSJulian Elischer 		/*
7749c238a0eSJulian Elischer 		 * Either create a duplicate and pass it on, or dequeue
7759c238a0eSJulian Elischer 		 * the original packet...
7769c238a0eSJulian Elischer 		 */
7779c238a0eSJulian Elischer 		if (hinfo->cfg.duplicate &&
7789c238a0eSJulian Elischer 		    random() % 100 <= hinfo->cfg.duplicate) {
7799c238a0eSJulian Elischer 			ngp_h = uma_zalloc(ngp_zone, M_NOWAIT);
7809c238a0eSJulian Elischer 			KASSERT(ngp_h != NULL, ("ngp_h zalloc failed (3)"));
7819c238a0eSJulian Elischer 			ngp_h->m = m_dup(m, M_NOWAIT);
7829c238a0eSJulian Elischer 			KASSERT(ngp_h->m != NULL, ("m_dup failed"));
7839c238a0eSJulian Elischer 		} else {
7849c238a0eSJulian Elischer 			TAILQ_REMOVE(&ngp_f->packet_head, ngp_h, ngp_link);
7859c238a0eSJulian Elischer 			hinfo->run.qin_frames--;
7869c238a0eSJulian Elischer 			hinfo->run.qin_octets -= m->m_pkthdr.len;
7879c238a0eSJulian Elischer 			ngp_f->packets--;
7889c238a0eSJulian Elischer 		}
7899c238a0eSJulian Elischer 
7909c238a0eSJulian Elischer 		/* Calculate the serialization delay */
7919c238a0eSJulian Elischer 		if (hinfo->cfg.bandwidth) {
7929c238a0eSJulian Elischer 			hinfo->qin_utime.tv_usec += ((uint64_t) m->m_pkthdr.len
7939c238a0eSJulian Elischer 				+ priv->overhead ) *
7949c238a0eSJulian Elischer 				8000000 / hinfo->cfg.bandwidth;
7959c238a0eSJulian Elischer 			hinfo->qin_utime.tv_sec +=
7969c238a0eSJulian Elischer 				hinfo->qin_utime.tv_usec / 1000000;
7979c238a0eSJulian Elischer 			hinfo->qin_utime.tv_usec =
7989c238a0eSJulian Elischer 				hinfo->qin_utime.tv_usec % 1000000;
7999c238a0eSJulian Elischer 		}
8009c238a0eSJulian Elischer 		when = &ngp_h->when;
8019c238a0eSJulian Elischer 		when->tv_sec = hinfo->qin_utime.tv_sec;
8029c238a0eSJulian Elischer 		when->tv_usec = hinfo->qin_utime.tv_usec;
8039c238a0eSJulian Elischer 
8049c238a0eSJulian Elischer 		/* Sort / rearrange inbound queues */
8059c238a0eSJulian Elischer 		if (ngp_f->packets) {
8069c238a0eSJulian Elischer 			if (hinfo->cfg.wfq) {
8079c238a0eSJulian Elischer 				TAILQ_REMOVE(&hinfo->fifo_head, ngp_f, fifo_le);
8089c238a0eSJulian Elischer 				FIFO_VTIME_SORT(TAILQ_FIRST(
8099c238a0eSJulian Elischer 				    &ngp_f->packet_head)->m->m_pkthdr.len)
8109c238a0eSJulian Elischer 			}
8119c238a0eSJulian Elischer 		} else {
8129c238a0eSJulian Elischer 			TAILQ_REMOVE(&hinfo->fifo_head, ngp_f, fifo_le);
8139c238a0eSJulian Elischer 			uma_zfree(ngp_zone, ngp_f);
8149c238a0eSJulian Elischer 			hinfo->run.fifo_queues--;
8159c238a0eSJulian Elischer 		}
8169c238a0eSJulian Elischer 
8179c238a0eSJulian Elischer 		/* Randomly discard the frame, according to BER setting */
8189c238a0eSJulian Elischer 		if (hinfo->cfg.ber &&
8199c238a0eSJulian Elischer 		    ((oldrand = rand) ^ (rand = random())<<17) >=
8209c238a0eSJulian Elischer 		    hinfo->ber_p[priv->overhead + m->m_pkthdr.len] ) {
8219c238a0eSJulian Elischer 			hinfo->stats.out_disc_frames++;
8229c238a0eSJulian Elischer 			hinfo->stats.out_disc_octets += m->m_pkthdr.len;
8239c238a0eSJulian Elischer 			uma_zfree(ngp_zone, ngp_h);
8249c238a0eSJulian Elischer 			m_freem(m);
8259c238a0eSJulian Elischer 			continue;
8269c238a0eSJulian Elischer 		}
8279c238a0eSJulian Elischer 
8289c238a0eSJulian Elischer 		/* Discard frame if outbound queue size limit exceeded */
8299c238a0eSJulian Elischer 		if (hinfo->cfg.qout_size_limit &&
8309c238a0eSJulian Elischer 		    hinfo->run.qout_frames>=hinfo->cfg.qout_size_limit) {
8319c238a0eSJulian Elischer 			hinfo->stats.out_disc_frames++;
8329c238a0eSJulian Elischer 			hinfo->stats.out_disc_octets += m->m_pkthdr.len;
8339c238a0eSJulian Elischer 			uma_zfree(ngp_zone, ngp_h);
8349c238a0eSJulian Elischer 			m_freem(m);
8359c238a0eSJulian Elischer 			continue;
8369c238a0eSJulian Elischer 		}
8379c238a0eSJulian Elischer 
8389c238a0eSJulian Elischer 		/* Calculate the propagation delay */
8399c238a0eSJulian Elischer 		when->tv_usec += priv->delay;
8409c238a0eSJulian Elischer 		when->tv_sec += when->tv_usec / 1000000;
8419c238a0eSJulian Elischer 		when->tv_usec = when->tv_usec % 1000000;
8429c238a0eSJulian Elischer 
8439c238a0eSJulian Elischer 		/* Put the frame into the delay queue */
8449c238a0eSJulian Elischer 		TAILQ_INSERT_TAIL(&hinfo->qout_head, ngp_h, ngp_link);
8459c238a0eSJulian Elischer 		hinfo->run.qout_frames++;
8469c238a0eSJulian Elischer 		hinfo->run.qout_octets += m->m_pkthdr.len;
8479c238a0eSJulian Elischer 	}
8489c238a0eSJulian Elischer 
8499c238a0eSJulian Elischer 	/* Delay queue processing */
8509c238a0eSJulian Elischer 	while ((ngp_h = TAILQ_FIRST(&hinfo->qout_head))) {
8519c238a0eSJulian Elischer 		struct mbuf *m = ngp_h->m;
8529c238a0eSJulian Elischer 
8539c238a0eSJulian Elischer 		when = &ngp_h->when;
8549c238a0eSJulian Elischer 		if (when->tv_sec > now->tv_sec ||
8559c238a0eSJulian Elischer 		    (when->tv_sec == now->tv_sec &&
8569c238a0eSJulian Elischer 		    when->tv_usec > now->tv_usec))
8579c238a0eSJulian Elischer 			break;
8589c238a0eSJulian Elischer 
8599c238a0eSJulian Elischer 		/* Update outbound queue stats */
8609c238a0eSJulian Elischer 		hinfo->stats.fwd_frames++;
8619c238a0eSJulian Elischer 		hinfo->stats.fwd_octets += m->m_pkthdr.len;
8629c238a0eSJulian Elischer 		hinfo->run.qout_frames--;
8639c238a0eSJulian Elischer 		hinfo->run.qout_octets -= m->m_pkthdr.len;
8649c238a0eSJulian Elischer 
8659c238a0eSJulian Elischer 		/* Dequeue the packet from qout */
8669c238a0eSJulian Elischer 		TAILQ_REMOVE(&hinfo->qout_head, ngp_h, ngp_link);
8679c238a0eSJulian Elischer 		uma_zfree(ngp_zone, ngp_h);
8689c238a0eSJulian Elischer 
8699c238a0eSJulian Elischer 		/* Enqueue locally for sending downstream */
8709c238a0eSJulian Elischer 		if (q_head == NULL)
8719c238a0eSJulian Elischer 			q_head = m;
8729c238a0eSJulian Elischer 		if (q_tail)
8739c238a0eSJulian Elischer 			q_tail->m_nextpkt = m;
8749c238a0eSJulian Elischer 		q_tail = m;
8759c238a0eSJulian Elischer 		m->m_nextpkt = NULL;
8769c238a0eSJulian Elischer 	}
8779c238a0eSJulian Elischer 
8789c238a0eSJulian Elischer 	/* If both queues are empty detach us from the list of active queues */
8799c238a0eSJulian Elischer 	if (hinfo->run.qin_frames + hinfo->run.qout_frames == 0) {
8809c238a0eSJulian Elischer 		LIST_REMOVE(hinfo, active_le);
8819c238a0eSJulian Elischer 		active_gen_id++;
8829c238a0eSJulian Elischer 	}
8839c238a0eSJulian Elischer 
8849c238a0eSJulian Elischer 	mtx_unlock(&ng_pipe_giant);
8859c238a0eSJulian Elischer 
8869c238a0eSJulian Elischer 	while ((m = q_head) != NULL) {
8879c238a0eSJulian Elischer 		q_head = m->m_nextpkt;
8889c238a0eSJulian Elischer 		m->m_nextpkt = NULL;
8899c238a0eSJulian Elischer 		NG_SEND_DATA(error, dest->hook, m, meta);
8909c238a0eSJulian Elischer 	}
8919c238a0eSJulian Elischer }
8929c238a0eSJulian Elischer 
8939c238a0eSJulian Elischer 
8949c238a0eSJulian Elischer /*
8959c238a0eSJulian Elischer  * This routine is called on every clock tick. We poll all nodes/hooks
8969c238a0eSJulian Elischer  * for queued frames by calling pipe_dequeue().
8979c238a0eSJulian Elischer  */
8989c238a0eSJulian Elischer static void
8999c238a0eSJulian Elischer pipe_scheduler(void *arg)
9009c238a0eSJulian Elischer {
9019c238a0eSJulian Elischer 	pipe_poll();
9029c238a0eSJulian Elischer 
9039c238a0eSJulian Elischer 	/* Reschedule  */
9049c238a0eSJulian Elischer 	callout_reset(&polling_timer, 1, &pipe_scheduler, NULL);
9059c238a0eSJulian Elischer }
9069c238a0eSJulian Elischer 
9079c238a0eSJulian Elischer 
9089c238a0eSJulian Elischer /*
9099c238a0eSJulian Elischer  * Traverse the list of all active hooks and attempt to dequeue
9109c238a0eSJulian Elischer  * some packets.  Hooks with empty queues are not traversed since
9119c238a0eSJulian Elischer  * they are not linked into this list.
9129c238a0eSJulian Elischer  */
9139c238a0eSJulian Elischer static void
9149c238a0eSJulian Elischer pipe_poll(void)
9159c238a0eSJulian Elischer {
9169c238a0eSJulian Elischer 	struct hookinfo *hinfo;
9179c238a0eSJulian Elischer 	struct timeval now;
9189c238a0eSJulian Elischer 	int old_gen_id = active_gen_id;
9199c238a0eSJulian Elischer 
9209c238a0eSJulian Elischer 	mtx_lock(&ng_pipe_giant);
9219c238a0eSJulian Elischer 	microuptime(&now);
9229c238a0eSJulian Elischer 	LIST_FOREACH(hinfo, &active_head, active_le) {
9239c238a0eSJulian Elischer 		CURVNET_SET(NG_HOOK_NODE(hinfo->hook)->nd_vnet);
9249c238a0eSJulian Elischer 		pipe_dequeue(hinfo, &now);
9259c238a0eSJulian Elischer 		CURVNET_RESTORE();
9269c238a0eSJulian Elischer 		mtx_lock(&ng_pipe_giant);
9279c238a0eSJulian Elischer 		if (old_gen_id != active_gen_id) {
9289c238a0eSJulian Elischer 			/* the list was updated; restart traversing */
9299c238a0eSJulian Elischer 			hinfo = LIST_FIRST(&active_head);
9309c238a0eSJulian Elischer 			if (hinfo == NULL)
9319c238a0eSJulian Elischer 				break;
9329c238a0eSJulian Elischer 			old_gen_id = active_gen_id;
9339c238a0eSJulian Elischer 			continue;
9349c238a0eSJulian Elischer 		}
9359c238a0eSJulian Elischer 	}
9369c238a0eSJulian Elischer 	mtx_unlock(&ng_pipe_giant);
9379c238a0eSJulian Elischer }
9389c238a0eSJulian Elischer 
9399c238a0eSJulian Elischer 
9409c238a0eSJulian Elischer /*
9419c238a0eSJulian Elischer  * Shutdown processing
9429c238a0eSJulian Elischer  *
9439c238a0eSJulian Elischer  * This is tricky. If we have both a lower and upper hook, then we
9449c238a0eSJulian Elischer  * probably want to extricate ourselves and leave the two peers
9459c238a0eSJulian Elischer  * still linked to each other. Otherwise we should just shut down as
9469c238a0eSJulian Elischer  * a normal node would.
9479c238a0eSJulian Elischer  */
9489c238a0eSJulian Elischer static int
9499c238a0eSJulian Elischer ngp_shutdown(node_p node)
9509c238a0eSJulian Elischer {
9519c238a0eSJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
9529c238a0eSJulian Elischer 
9539c238a0eSJulian Elischer 	if (priv->lower.hook && priv->upper.hook)
9549c238a0eSJulian Elischer 		ng_bypass(priv->lower.hook, priv->upper.hook);
9559c238a0eSJulian Elischer 	else {
9569c238a0eSJulian Elischer 		if (priv->upper.hook != NULL)
9579c238a0eSJulian Elischer 			ng_rmhook_self(priv->upper.hook);
9589c238a0eSJulian Elischer 		if (priv->lower.hook != NULL)
9599c238a0eSJulian Elischer 			ng_rmhook_self(priv->lower.hook);
9609c238a0eSJulian Elischer 	}
9619c238a0eSJulian Elischer 	NG_NODE_UNREF(node);
9621ede983cSDag-Erling Smørgrav 	free(priv, M_NG_PIPE);
9639c238a0eSJulian Elischer 	return (0);
9649c238a0eSJulian Elischer }
9659c238a0eSJulian Elischer 
9669c238a0eSJulian Elischer 
9679c238a0eSJulian Elischer /*
9689c238a0eSJulian Elischer  * Hook disconnection
9699c238a0eSJulian Elischer  */
9709c238a0eSJulian Elischer static int
9719c238a0eSJulian Elischer ngp_disconnect(hook_p hook)
9729c238a0eSJulian Elischer {
9739c238a0eSJulian Elischer 	struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
9749c238a0eSJulian Elischer 	struct ngp_fifo *ngp_f;
9759c238a0eSJulian Elischer 	struct ngp_hdr *ngp_h;
9769c238a0eSJulian Elischer 	int removed = 0;
9779c238a0eSJulian Elischer 
9789c238a0eSJulian Elischer 	mtx_lock(&ng_pipe_giant);
9799c238a0eSJulian Elischer 
9809c238a0eSJulian Elischer 	KASSERT(hinfo != NULL, ("%s: null info", __FUNCTION__));
9819c238a0eSJulian Elischer 	hinfo->hook = NULL;
9829c238a0eSJulian Elischer 
9839c238a0eSJulian Elischer 	/* Flush all fifo queues associated with the hook */
9849c238a0eSJulian Elischer 	while ((ngp_f = TAILQ_FIRST(&hinfo->fifo_head))) {
9859c238a0eSJulian Elischer 		while ((ngp_h = TAILQ_FIRST(&ngp_f->packet_head))) {
9869c238a0eSJulian Elischer 			TAILQ_REMOVE(&ngp_f->packet_head, ngp_h, ngp_link);
9879c238a0eSJulian Elischer 			m_freem(ngp_h->m);
9889c238a0eSJulian Elischer 			uma_zfree(ngp_zone, ngp_h);
9899c238a0eSJulian Elischer 			removed++;
9909c238a0eSJulian Elischer 		}
9919c238a0eSJulian Elischer 		TAILQ_REMOVE(&hinfo->fifo_head, ngp_f, fifo_le);
9929c238a0eSJulian Elischer 		uma_zfree(ngp_zone, ngp_f);
9939c238a0eSJulian Elischer 	}
9949c238a0eSJulian Elischer 
9959c238a0eSJulian Elischer 	/* Flush the delay queue */
9969c238a0eSJulian Elischer 	while ((ngp_h = TAILQ_FIRST(&hinfo->qout_head))) {
9979c238a0eSJulian Elischer 		TAILQ_REMOVE(&hinfo->qout_head, ngp_h, ngp_link);
9989c238a0eSJulian Elischer 		m_freem(ngp_h->m);
9999c238a0eSJulian Elischer 		uma_zfree(ngp_zone, ngp_h);
10009c238a0eSJulian Elischer 		removed++;
10019c238a0eSJulian Elischer 	}
10029c238a0eSJulian Elischer 
10039c238a0eSJulian Elischer 	/*
10049c238a0eSJulian Elischer 	 * Both queues should be empty by now, so detach us from
10059c238a0eSJulian Elischer 	 * the list of active queues
10069c238a0eSJulian Elischer 	 */
10079c238a0eSJulian Elischer 	if (removed) {
10089c238a0eSJulian Elischer 		LIST_REMOVE(hinfo, active_le);
10099c238a0eSJulian Elischer 		active_gen_id++;
10109c238a0eSJulian Elischer 	}
10119c238a0eSJulian Elischer 	if (hinfo->run.qin_frames + hinfo->run.qout_frames != removed)
10129c238a0eSJulian Elischer 		printf("Mismatch: queued=%d but removed=%d !?!",
10139c238a0eSJulian Elischer 		    hinfo->run.qin_frames + hinfo->run.qout_frames, removed);
10149c238a0eSJulian Elischer 
10159c238a0eSJulian Elischer 	/* Release the packet loss probability table (BER) */
10169c238a0eSJulian Elischer 	if (hinfo->ber_p)
10171ede983cSDag-Erling Smørgrav 		free(hinfo->ber_p, M_NG_PIPE);
10189c238a0eSJulian Elischer 
10199c238a0eSJulian Elischer 	mtx_unlock(&ng_pipe_giant);
10209c238a0eSJulian Elischer 
10219c238a0eSJulian Elischer 	return (0);
10229c238a0eSJulian Elischer }
10239c238a0eSJulian Elischer 
10249c238a0eSJulian Elischer static int
10259c238a0eSJulian Elischer ngp_modevent(module_t mod, int type, void *unused)
10269c238a0eSJulian Elischer {
10279c238a0eSJulian Elischer 	int error = 0;
10289c238a0eSJulian Elischer 
10299c238a0eSJulian Elischer 	switch (type) {
10309c238a0eSJulian Elischer 	case MOD_LOAD:
10319c238a0eSJulian Elischer 		ngp_zone = uma_zcreate("ng_pipe", max(sizeof(struct ngp_hdr),
10329c238a0eSJulian Elischer 		    sizeof (struct ngp_fifo)), NULL, NULL, NULL, NULL,
10339c238a0eSJulian Elischer 		    UMA_ALIGN_PTR, 0);
10349c238a0eSJulian Elischer 		if (ngp_zone == NULL)
10359c238a0eSJulian Elischer 			panic("ng_pipe: couldn't allocate descriptor zone");
10369c238a0eSJulian Elischer 
10379c238a0eSJulian Elischer 		mtx_init(&ng_pipe_giant, "ng_pipe_giant", NULL, MTX_DEF);
10389c238a0eSJulian Elischer 		LIST_INIT(&active_head);
10399c238a0eSJulian Elischer 		callout_init(&polling_timer, CALLOUT_MPSAFE);
10409c238a0eSJulian Elischer 		callout_reset(&polling_timer, 1, &pipe_scheduler, NULL);
10419c238a0eSJulian Elischer 		break;
10429c238a0eSJulian Elischer 	case MOD_UNLOAD:
10439c238a0eSJulian Elischer 		callout_drain(&polling_timer);
10449c238a0eSJulian Elischer 		uma_zdestroy(ngp_zone);
10459c238a0eSJulian Elischer 		mtx_destroy(&ng_pipe_giant);
10469c238a0eSJulian Elischer 		break;
10479c238a0eSJulian Elischer 	default:
10489c238a0eSJulian Elischer 		error = EOPNOTSUPP;
10499c238a0eSJulian Elischer 		break;
10509c238a0eSJulian Elischer 	}
10519c238a0eSJulian Elischer 
10529c238a0eSJulian Elischer 	return (error);
10539c238a0eSJulian Elischer }
1054