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