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