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; 266c97bf8c3SAlexander Motin 267c97bf8c3SAlexander Motin /* Node is useless without destination hook. */ 268c97bf8c3SAlexander Motin if (dest == NULL) { 269c97bf8c3SAlexander Motin NG_FREE_ITEM(item); 270c97bf8c3SAlexander Motin ++hinfo->stats.errors; 271c97bf8c3SAlexander Motin return(EINVAL); 272c97bf8c3SAlexander Motin } 273c97bf8c3SAlexander Motin 274c97bf8c3SAlexander Motin /* If queue is not empty now then enqueue packet. */ 275c97bf8c3SAlexander Motin if (hinfo->q_first != hinfo->q_last) { 276c97bf8c3SAlexander Motin ng_car_enqueue(hinfo, item); 277c97bf8c3SAlexander Motin return (0); 278c97bf8c3SAlexander Motin } 279c97bf8c3SAlexander Motin 280c97bf8c3SAlexander Motin m = NGI_M(item); 281c97bf8c3SAlexander Motin 282c97bf8c3SAlexander Motin #define NG_CAR_PERFORM_MATCH_ACTION(a) \ 283c97bf8c3SAlexander Motin do { \ 284c97bf8c3SAlexander Motin switch (a) { \ 285c97bf8c3SAlexander Motin case NG_CAR_ACTION_FORWARD: \ 286ae1be01fSAlexander Motin /* Do nothing. */ \ 287c97bf8c3SAlexander Motin break; \ 288c97bf8c3SAlexander Motin case NG_CAR_ACTION_MARK: \ 289c97bf8c3SAlexander Motin /* XXX find a way to mark packets (mbuf tag?) */ \ 290c97bf8c3SAlexander Motin ++hinfo->stats.errors; \ 291c97bf8c3SAlexander Motin break; \ 292c97bf8c3SAlexander Motin case NG_CAR_ACTION_DROP: \ 293c97bf8c3SAlexander Motin default: \ 294ae1be01fSAlexander Motin /* Drop packet and return. */ \ 295c97bf8c3SAlexander Motin NG_FREE_ITEM(item); \ 296c97bf8c3SAlexander Motin ++hinfo->stats.droped_pkts; \ 297ae1be01fSAlexander Motin return (0); \ 298c97bf8c3SAlexander Motin } \ 299ae1be01fSAlexander Motin } while (0) 300c97bf8c3SAlexander Motin 301c97bf8c3SAlexander Motin /* Check commited token bucket. */ 302c97bf8c3SAlexander Motin if (hinfo->tc - m->m_pkthdr.len >= 0) { 303ae1be01fSAlexander Motin /* This packet is green. */ 304c97bf8c3SAlexander Motin ++hinfo->stats.green_pkts; 305c97bf8c3SAlexander Motin hinfo->tc -= m->m_pkthdr.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 312c97bf8c3SAlexander Motin /* Check commited token bucket again after refill. */ 313c97bf8c3SAlexander Motin if (hinfo->tc - m->m_pkthdr.len >= 0) { 314c97bf8c3SAlexander Motin /* This packet is green */ 315c97bf8c3SAlexander Motin ++hinfo->stats.green_pkts; 316c97bf8c3SAlexander Motin hinfo->tc -= m->m_pkthdr.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? */ 327ae1be01fSAlexander Motin if (m->m_pkthdr.len - (hinfo->tc - m->m_pkthdr.len) > 328ae1be01fSAlexander Motin hinfo->conf.ebs) { 329ae1be01fSAlexander Motin /* This packet is definitely red. */ 330c97bf8c3SAlexander Motin ++hinfo->stats.red_pkts; 331c97bf8c3SAlexander Motin hinfo->te = 0; 332c97bf8c3SAlexander Motin NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); 333c97bf8c3SAlexander Motin 334ae1be01fSAlexander Motin /* Use token bucket to simulate RED-like drop 335ae1be01fSAlexander Motin probability. */ 336ae1be01fSAlexander Motin } else if (hinfo->te + (m->m_pkthdr.len - hinfo->tc) < 337ae1be01fSAlexander Motin hinfo->conf.ebs) { 338c97bf8c3SAlexander Motin /* This packet is yellow */ 339c97bf8c3SAlexander Motin ++hinfo->stats.yellow_pkts; 340c97bf8c3SAlexander Motin hinfo->te += m->m_pkthdr.len - hinfo->tc; 341ae1be01fSAlexander Motin /* Go to negative tokens. */ 342ae1be01fSAlexander Motin hinfo->tc -= m->m_pkthdr.len; 343c97bf8c3SAlexander Motin NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action); 344c97bf8c3SAlexander Motin } else { 345ae1be01fSAlexander Motin /* This packet is probaly red. */ 346c97bf8c3SAlexander Motin ++hinfo->stats.red_pkts; 347c97bf8c3SAlexander Motin hinfo->te = 0; 348c97bf8c3SAlexander Motin NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); 349c97bf8c3SAlexander Motin } 350c97bf8c3SAlexander Motin /* If not green and mode is SINGLE/DOUBLE RATE. */ 351c97bf8c3SAlexander Motin } else { 352c97bf8c3SAlexander Motin /* Check extended token bucket. */ 353c97bf8c3SAlexander Motin if (hinfo->te - m->m_pkthdr.len >= 0) { 354c97bf8c3SAlexander Motin /* This packet is yellow */ 355c97bf8c3SAlexander Motin ++hinfo->stats.yellow_pkts; 356c97bf8c3SAlexander Motin hinfo->te -= m->m_pkthdr.len; 357c97bf8c3SAlexander Motin NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action); 358c97bf8c3SAlexander Motin } else { 359c97bf8c3SAlexander Motin /* This packet is red */ 360c97bf8c3SAlexander Motin ++hinfo->stats.red_pkts; 361c97bf8c3SAlexander Motin NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); 362c97bf8c3SAlexander Motin } 363c97bf8c3SAlexander Motin } 364c97bf8c3SAlexander Motin } 365c97bf8c3SAlexander Motin 366c97bf8c3SAlexander Motin #undef NG_CAR_PERFORM_MATCH_ACTION 367c97bf8c3SAlexander Motin 368c97bf8c3SAlexander Motin NG_FWD_ITEM_HOOK(error, item, dest); 369c97bf8c3SAlexander Motin if (error != 0) 370c97bf8c3SAlexander Motin ++hinfo->stats.errors; 371c97bf8c3SAlexander Motin ++hinfo->stats.passed_pkts; 372c97bf8c3SAlexander Motin 373ae1be01fSAlexander Motin return (error); 374c97bf8c3SAlexander Motin } 375c97bf8c3SAlexander Motin 376c97bf8c3SAlexander Motin /* 377ae1be01fSAlexander Motin * Receive a control message. 378c97bf8c3SAlexander Motin */ 379c97bf8c3SAlexander Motin static int 380c97bf8c3SAlexander Motin ng_car_rcvmsg(node_p node, item_p item, hook_p lasthook) 381c97bf8c3SAlexander Motin { 382c97bf8c3SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(node); 383c97bf8c3SAlexander Motin struct ng_mesg *resp = NULL; 384c97bf8c3SAlexander Motin int error = 0; 385c97bf8c3SAlexander Motin struct ng_mesg *msg; 386c97bf8c3SAlexander Motin 387c97bf8c3SAlexander Motin NGI_GET_MSG(item, msg); 388c97bf8c3SAlexander Motin switch (msg->header.typecookie) { 389c97bf8c3SAlexander Motin case NGM_CAR_COOKIE: 390c97bf8c3SAlexander Motin switch (msg->header.cmd) { 391c97bf8c3SAlexander Motin case NGM_CAR_GET_STATS: 392c97bf8c3SAlexander Motin case NGM_CAR_GETCLR_STATS: 393c97bf8c3SAlexander Motin { 394c97bf8c3SAlexander Motin struct ng_car_bulkstats *bstats; 395c97bf8c3SAlexander Motin 396c97bf8c3SAlexander Motin NG_MKRESPONSE(resp, msg, 397c97bf8c3SAlexander Motin sizeof(*bstats), M_NOWAIT); 398c97bf8c3SAlexander Motin if (resp == NULL) { 399c97bf8c3SAlexander Motin error = ENOMEM; 400c97bf8c3SAlexander Motin break; 401c97bf8c3SAlexander Motin } 402c97bf8c3SAlexander Motin bstats = (struct ng_car_bulkstats *)resp->data; 403c97bf8c3SAlexander Motin 404c97bf8c3SAlexander Motin bcopy(&priv->upper.stats, &bstats->downstream, 405c97bf8c3SAlexander Motin sizeof(bstats->downstream)); 406c97bf8c3SAlexander Motin bcopy(&priv->lower.stats, &bstats->upstream, 407c97bf8c3SAlexander Motin sizeof(bstats->upstream)); 408c97bf8c3SAlexander Motin } 409c97bf8c3SAlexander Motin if (msg->header.cmd == NGM_CAR_GET_STATS) 410c97bf8c3SAlexander Motin break; 411c97bf8c3SAlexander Motin case NGM_CAR_CLR_STATS: 412c97bf8c3SAlexander Motin bzero(&priv->upper.stats, 413c97bf8c3SAlexander Motin sizeof(priv->upper.stats)); 414c97bf8c3SAlexander Motin bzero(&priv->lower.stats, 415c97bf8c3SAlexander Motin sizeof(priv->lower.stats)); 416c97bf8c3SAlexander Motin break; 417c97bf8c3SAlexander Motin case NGM_CAR_GET_CONF: 418c97bf8c3SAlexander Motin { 419c97bf8c3SAlexander Motin struct ng_car_bulkconf *bconf; 420c97bf8c3SAlexander Motin 421c97bf8c3SAlexander Motin NG_MKRESPONSE(resp, msg, 422c97bf8c3SAlexander Motin sizeof(*bconf), M_NOWAIT); 423c97bf8c3SAlexander Motin if (resp == NULL) { 424c97bf8c3SAlexander Motin error = ENOMEM; 425c97bf8c3SAlexander Motin break; 426c97bf8c3SAlexander Motin } 427c97bf8c3SAlexander Motin bconf = (struct ng_car_bulkconf *)resp->data; 428c97bf8c3SAlexander Motin 429c97bf8c3SAlexander Motin bcopy(&priv->upper.conf, &bconf->downstream, 430c97bf8c3SAlexander Motin sizeof(bconf->downstream)); 431c97bf8c3SAlexander Motin bcopy(&priv->lower.conf, &bconf->upstream, 432c97bf8c3SAlexander Motin sizeof(bconf->upstream)); 433c97bf8c3SAlexander Motin } 434c97bf8c3SAlexander Motin break; 435c97bf8c3SAlexander Motin case NGM_CAR_SET_CONF: 436c97bf8c3SAlexander Motin { 437c97bf8c3SAlexander Motin struct ng_car_bulkconf *const bconf = 438c97bf8c3SAlexander Motin (struct ng_car_bulkconf *)msg->data; 439c97bf8c3SAlexander Motin 440c97bf8c3SAlexander Motin /* Check for invalid or illegal config. */ 441c97bf8c3SAlexander Motin if ((msg->header.arglen != sizeof(*bconf)) 442c97bf8c3SAlexander Motin || (bconf->downstream.cir > 1000000000) 443c97bf8c3SAlexander Motin || (bconf->downstream.pir > 1000000000) 444c97bf8c3SAlexander Motin || (bconf->upstream.cir > 1000000000) 445c97bf8c3SAlexander Motin || (bconf->upstream.pir > 1000000000) 446c97bf8c3SAlexander Motin || (bconf->downstream.cbs == 0 447c97bf8c3SAlexander Motin && bconf->downstream.ebs == 0) 448c97bf8c3SAlexander Motin || (bconf->upstream.cbs == 0 449c97bf8c3SAlexander Motin && bconf->upstream.ebs == 0)) 450c97bf8c3SAlexander Motin { 451c97bf8c3SAlexander Motin error = EINVAL; 452c97bf8c3SAlexander Motin break; 453c97bf8c3SAlexander Motin } 454c97bf8c3SAlexander Motin 455c97bf8c3SAlexander Motin /* Copy downstream config. */ 456c97bf8c3SAlexander Motin bcopy(&bconf->downstream, &priv->upper.conf, 457c97bf8c3SAlexander Motin sizeof(priv->upper.conf)); 458c97bf8c3SAlexander Motin priv->upper.tc = priv->upper.conf.cbs; 459c97bf8c3SAlexander Motin if (priv->upper.conf.mode == NG_CAR_RED || 460b50ace73SAlexander Motin priv->upper.conf.mode == NG_CAR_SHAPE) { 461c97bf8c3SAlexander Motin priv->upper.te = 0; 462c97bf8c3SAlexander Motin } else { 463c97bf8c3SAlexander Motin priv->upper.te = priv->upper.conf.ebs; 464c97bf8c3SAlexander Motin } 465c97bf8c3SAlexander Motin 466c97bf8c3SAlexander Motin /* Copy upstream config. */ 467c97bf8c3SAlexander Motin bcopy(&bconf->upstream, &priv->lower.conf, 468c97bf8c3SAlexander Motin sizeof(priv->lower.conf)); 469c97bf8c3SAlexander Motin priv->lower.tc = priv->lower.conf.cbs; 470c97bf8c3SAlexander Motin if (priv->lower.conf.mode == NG_CAR_RED || 471c97bf8c3SAlexander Motin priv->lower.conf.mode == NG_CAR_SHAPE) { 472c97bf8c3SAlexander Motin priv->lower.te = 0; 473c97bf8c3SAlexander Motin } else { 474c97bf8c3SAlexander Motin priv->lower.te = priv->lower.conf.ebs; 475c97bf8c3SAlexander Motin } 476c97bf8c3SAlexander Motin } 477c97bf8c3SAlexander Motin break; 478c97bf8c3SAlexander Motin default: 479c97bf8c3SAlexander Motin error = EINVAL; 480c97bf8c3SAlexander Motin break; 481c97bf8c3SAlexander Motin } 482c97bf8c3SAlexander Motin break; 483c97bf8c3SAlexander Motin default: 484c97bf8c3SAlexander Motin error = EINVAL; 485c97bf8c3SAlexander Motin break; 486c97bf8c3SAlexander Motin } 487c97bf8c3SAlexander Motin NG_RESPOND_MSG(error, node, item, resp); 488c97bf8c3SAlexander Motin NG_FREE_MSG(msg); 489c97bf8c3SAlexander Motin return (error); 490c97bf8c3SAlexander Motin } 491c97bf8c3SAlexander Motin 492c97bf8c3SAlexander Motin /* 493c97bf8c3SAlexander Motin * Do local shutdown processing. 494c97bf8c3SAlexander Motin */ 495c97bf8c3SAlexander Motin static int 496c97bf8c3SAlexander Motin ng_car_shutdown(node_p node) 497c97bf8c3SAlexander Motin { 498c97bf8c3SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(node); 499c97bf8c3SAlexander Motin 500df01e689SAlexander Motin ng_uncallout(&priv->upper.q_callout, node); 501df01e689SAlexander Motin ng_uncallout(&priv->lower.q_callout, node); 502c97bf8c3SAlexander Motin mtx_destroy(&priv->upper.q_mtx); 503c97bf8c3SAlexander Motin mtx_destroy(&priv->lower.q_mtx); 504c97bf8c3SAlexander Motin NG_NODE_UNREF(priv->node); 505ae1be01fSAlexander Motin free(priv, M_NETGRAPH); 506c97bf8c3SAlexander Motin return (0); 507c97bf8c3SAlexander Motin } 508c97bf8c3SAlexander Motin 509c97bf8c3SAlexander Motin /* 510c97bf8c3SAlexander Motin * Hook disconnection. 511c97bf8c3SAlexander Motin * 512c97bf8c3SAlexander Motin * For this type, removal of the last link destroys the node. 513c97bf8c3SAlexander Motin */ 514c97bf8c3SAlexander Motin static int 515c97bf8c3SAlexander Motin ng_car_disconnect(hook_p hook) 516c97bf8c3SAlexander Motin { 517c97bf8c3SAlexander Motin struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); 518c97bf8c3SAlexander Motin const node_p node = NG_HOOK_NODE(hook); 519c97bf8c3SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(node); 520c97bf8c3SAlexander Motin 521c97bf8c3SAlexander Motin if (hinfo) { 522c97bf8c3SAlexander Motin /* Purge queue if not empty. */ 523c97bf8c3SAlexander Motin while (hinfo->q_first != hinfo->q_last) { 524c97bf8c3SAlexander Motin NG_FREE_M(hinfo->q[hinfo->q_first]); 525c97bf8c3SAlexander Motin hinfo->q_first++; 526c97bf8c3SAlexander Motin if (hinfo->q_first >= NG_CAR_QUEUE_SIZE) 527c97bf8c3SAlexander Motin hinfo->q_first = 0; 528c97bf8c3SAlexander Motin } 529c97bf8c3SAlexander Motin /* Remove hook refs. */ 530c97bf8c3SAlexander Motin if (hinfo->hook == priv->upper.hook) 531c97bf8c3SAlexander Motin priv->lower.dest = NULL; 532c97bf8c3SAlexander Motin else 533c97bf8c3SAlexander Motin priv->upper.dest = NULL; 534c97bf8c3SAlexander Motin hinfo->hook = NULL; 535c97bf8c3SAlexander Motin } 536c97bf8c3SAlexander Motin /* Already shutting down? */ 537c97bf8c3SAlexander Motin if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 538c97bf8c3SAlexander Motin && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 539c97bf8c3SAlexander Motin ng_rmnode_self(NG_HOOK_NODE(hook)); 540c97bf8c3SAlexander Motin return (0); 541c97bf8c3SAlexander Motin } 542c97bf8c3SAlexander Motin 543c97bf8c3SAlexander Motin /* 544c97bf8c3SAlexander Motin * Hook's token buckets refillment. 545c97bf8c3SAlexander Motin */ 546c97bf8c3SAlexander Motin static void 547c97bf8c3SAlexander Motin ng_car_refillhook(struct hookinfo *h) 548c97bf8c3SAlexander Motin { 549c97bf8c3SAlexander Motin struct timeval newt, deltat; 550c97bf8c3SAlexander Motin int64_t deltat_us; 551c97bf8c3SAlexander Motin int64_t delta; 552c97bf8c3SAlexander Motin 553c97bf8c3SAlexander Motin /* Get current time. */ 554c97bf8c3SAlexander Motin getmicrotime(&newt); 555c97bf8c3SAlexander Motin 556c97bf8c3SAlexander Motin /* Time must go forward. */ 557c97bf8c3SAlexander Motin if (timevalcmp(&newt, &h->lastRefill, <= )) { 558c97bf8c3SAlexander Motin h->lastRefill = newt; 559c97bf8c3SAlexander Motin return; 560c97bf8c3SAlexander Motin } 561c97bf8c3SAlexander Motin 562c97bf8c3SAlexander Motin /* Get time delta since last refill. */ 563c97bf8c3SAlexander Motin deltat = newt; 564c97bf8c3SAlexander Motin timevalsub(&deltat, &h->lastRefill); 565c97bf8c3SAlexander Motin 566c97bf8c3SAlexander Motin /* Sanity check */ 567c97bf8c3SAlexander Motin if (deltat.tv_sec > 1000) { 568c97bf8c3SAlexander Motin deltat_us = 1000000000; 569c97bf8c3SAlexander Motin } else { 570c97bf8c3SAlexander Motin deltat_us = ((int64_t)deltat.tv_sec) * 1000000 + deltat.tv_usec; 571c97bf8c3SAlexander Motin } 572c97bf8c3SAlexander Motin 573c97bf8c3SAlexander Motin if (h->conf.mode == NG_CAR_SINGLE_RATE) { 574c97bf8c3SAlexander Motin /* Refill commited token bucket. */ 575c97bf8c3SAlexander Motin h->tc += h->conf.cir * deltat_us / 8000000; 576c97bf8c3SAlexander Motin delta = h->tc - h->conf.cbs; 577c97bf8c3SAlexander Motin if (delta > 0) { 578c97bf8c3SAlexander Motin h->tc = h->conf.cbs; 579c97bf8c3SAlexander Motin 580c97bf8c3SAlexander Motin /* Refill exceeded token bucket. */ 581c97bf8c3SAlexander Motin h->te += delta; 582c97bf8c3SAlexander Motin if (h->te > h->conf.ebs) 583c97bf8c3SAlexander Motin h->te = h->conf.ebs; 584c97bf8c3SAlexander Motin } 585c97bf8c3SAlexander Motin 586c97bf8c3SAlexander Motin } else if (h->conf.mode == NG_CAR_DOUBLE_RATE) { 587c97bf8c3SAlexander Motin /* Refill commited token bucket. */ 588c97bf8c3SAlexander Motin h->tc += h->conf.cir * deltat_us / 8000000; 589c97bf8c3SAlexander Motin if (h->tc > h->conf.cbs) 590c97bf8c3SAlexander Motin h->tc = h->conf.cbs; 591c97bf8c3SAlexander Motin 592c97bf8c3SAlexander Motin /* Refill peak token bucket. */ 593c97bf8c3SAlexander Motin h->te += h->conf.pir * deltat_us / 8000000; 594c97bf8c3SAlexander Motin if (h->te > h->conf.ebs) 595c97bf8c3SAlexander Motin h->te = h->conf.ebs; 596c97bf8c3SAlexander Motin 597c97bf8c3SAlexander Motin } else { /* RED or SHAPE mode. */ 598c97bf8c3SAlexander Motin /* Refill commited token bucket. */ 599c97bf8c3SAlexander Motin h->tc += h->conf.cir * deltat_us / 8000000; 600c97bf8c3SAlexander Motin if (h->tc > ((int64_t)h->conf.cbs)) 601c97bf8c3SAlexander Motin h->tc = h->conf.cbs; 602c97bf8c3SAlexander Motin } 603c97bf8c3SAlexander Motin 604c97bf8c3SAlexander Motin /* Remember this moment. */ 605c97bf8c3SAlexander Motin h->lastRefill = newt; 606c97bf8c3SAlexander Motin } 607c97bf8c3SAlexander Motin 608c97bf8c3SAlexander Motin /* 609c97bf8c3SAlexander Motin * Schedule callout when we will have required tokens. 610c97bf8c3SAlexander Motin */ 611c97bf8c3SAlexander Motin static void 612c97bf8c3SAlexander Motin ng_car_schedule(struct hookinfo *hinfo) 613c97bf8c3SAlexander Motin { 614c97bf8c3SAlexander Motin int delay; 615c97bf8c3SAlexander Motin 616c97bf8c3SAlexander Motin delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1; 617c97bf8c3SAlexander Motin 618c97bf8c3SAlexander Motin ng_callout(&hinfo->q_callout, NG_HOOK_NODE(hinfo->hook), hinfo->hook, 619c97bf8c3SAlexander Motin delay, &ng_car_q_event, NULL, 0); 620c97bf8c3SAlexander Motin } 621c97bf8c3SAlexander Motin 622c97bf8c3SAlexander Motin /* 623c97bf8c3SAlexander Motin * Queue processing callout handler. 624c97bf8c3SAlexander Motin */ 625c97bf8c3SAlexander Motin void 626c97bf8c3SAlexander Motin ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2) 627c97bf8c3SAlexander Motin { 628c97bf8c3SAlexander Motin struct hookinfo *hinfo = NG_HOOK_PRIVATE(hook); 629c97bf8c3SAlexander Motin item_p item; 630c97bf8c3SAlexander Motin struct mbuf *m; 631c97bf8c3SAlexander Motin int error; 632c97bf8c3SAlexander Motin 633c97bf8c3SAlexander Motin /* Refill tokens for time we have slept. */ 634c97bf8c3SAlexander Motin ng_car_refillhook(hinfo); 635c97bf8c3SAlexander Motin 636c97bf8c3SAlexander Motin if (hinfo->dest != NULL) { 637c97bf8c3SAlexander Motin /* If we have some tokens */ 638c97bf8c3SAlexander Motin while (hinfo->tc >= 0) { 639c97bf8c3SAlexander Motin 640c97bf8c3SAlexander Motin /* Send packet. */ 641c97bf8c3SAlexander Motin m = hinfo->q[hinfo->q_first]; 642ae1be01fSAlexander Motin if ((item = ng_package_data(m, NG_NOFLAGS)) != NULL) 643c97bf8c3SAlexander Motin NG_FWD_ITEM_HOOK(error, item, hinfo->dest); 644c97bf8c3SAlexander Motin 645c97bf8c3SAlexander Motin /* Get next one. */ 646c97bf8c3SAlexander Motin hinfo->q_first++; 647c97bf8c3SAlexander Motin if (hinfo->q_first >= NG_CAR_QUEUE_SIZE) 648c97bf8c3SAlexander Motin hinfo->q_first = 0; 649c97bf8c3SAlexander Motin 650c97bf8c3SAlexander Motin /* Stop if none left. */ 651c97bf8c3SAlexander Motin if (hinfo->q_first == hinfo->q_last) 652c97bf8c3SAlexander Motin break; 653c97bf8c3SAlexander Motin 654c97bf8c3SAlexander Motin /* If we have more packet, try it. */ 655c97bf8c3SAlexander Motin m = hinfo->q[hinfo->q_first]; 656c97bf8c3SAlexander Motin hinfo->tc -= m->m_pkthdr.len; 657c97bf8c3SAlexander Motin } 658c97bf8c3SAlexander Motin } 659c97bf8c3SAlexander Motin 660c97bf8c3SAlexander Motin /* If something left */ 661ae1be01fSAlexander Motin if (hinfo->q_first != hinfo->q_last) 662ae1be01fSAlexander Motin /* Schedule queue processing. */ 663c97bf8c3SAlexander Motin ng_car_schedule(hinfo); 664c97bf8c3SAlexander Motin } 665c97bf8c3SAlexander Motin 666c97bf8c3SAlexander Motin /* 667c97bf8c3SAlexander Motin * Enqueue packet. 668c97bf8c3SAlexander Motin */ 669c97bf8c3SAlexander Motin static void 670c97bf8c3SAlexander Motin ng_car_enqueue(struct hookinfo *hinfo, item_p item) 671c97bf8c3SAlexander Motin { 672c97bf8c3SAlexander Motin struct mbuf *m; 673c97bf8c3SAlexander Motin int len; 674c97bf8c3SAlexander Motin 675c97bf8c3SAlexander Motin NGI_GET_M(item, m); 676c97bf8c3SAlexander Motin NG_FREE_ITEM(item); 677c97bf8c3SAlexander Motin 678ae1be01fSAlexander Motin /* Lock queue mutex. */ 679c97bf8c3SAlexander Motin mtx_lock(&hinfo->q_mtx); 680c97bf8c3SAlexander Motin 681ae1be01fSAlexander Motin /* Calculate used queue length. */ 682c97bf8c3SAlexander Motin len = hinfo->q_last - hinfo->q_first; 683c97bf8c3SAlexander Motin if (len < 0) 684c97bf8c3SAlexander Motin len += NG_CAR_QUEUE_SIZE; 685c97bf8c3SAlexander Motin 686ae1be01fSAlexander Motin /* If queue is overflowed or we have no RED tokens. */ 687c97bf8c3SAlexander Motin if ((len >= (NG_CAR_QUEUE_SIZE - 1)) || 688c97bf8c3SAlexander Motin (hinfo->te + len >= NG_CAR_QUEUE_SIZE)) { 689ae1be01fSAlexander Motin /* Drop packet. */ 690c97bf8c3SAlexander Motin ++hinfo->stats.red_pkts; 691c97bf8c3SAlexander Motin NG_FREE_M(m); 692c97bf8c3SAlexander Motin 693c97bf8c3SAlexander Motin hinfo->te = 0; 694c97bf8c3SAlexander Motin } else { 695c97bf8c3SAlexander Motin /* This packet is yellow. */ 696c97bf8c3SAlexander Motin ++hinfo->stats.yellow_pkts; 697c97bf8c3SAlexander Motin 698c97bf8c3SAlexander Motin /* Enqueue packet. */ 699c97bf8c3SAlexander Motin hinfo->q[hinfo->q_last] = m; 700c97bf8c3SAlexander Motin hinfo->q_last++; 701c97bf8c3SAlexander Motin if (hinfo->q_last >= NG_CAR_QUEUE_SIZE) 702c97bf8c3SAlexander Motin hinfo->q_last = 0; 703c97bf8c3SAlexander Motin 704c97bf8c3SAlexander Motin /* Use RED tokens. */ 705c97bf8c3SAlexander Motin if (len > NG_CAR_QUEUE_MIN_TH) 706c97bf8c3SAlexander Motin hinfo->te += len - NG_CAR_QUEUE_MIN_TH; 707c97bf8c3SAlexander Motin 708ae1be01fSAlexander Motin /* If this is a first packet in the queue. */ 709c97bf8c3SAlexander Motin if (len == 0) { 710c97bf8c3SAlexander Motin hinfo->tc -= m->m_pkthdr.len; 711c97bf8c3SAlexander Motin 712ae1be01fSAlexander Motin /* Schedule queue processing. */ 713c97bf8c3SAlexander Motin ng_car_schedule(hinfo); 714c97bf8c3SAlexander Motin } 715c97bf8c3SAlexander Motin } 716c97bf8c3SAlexander Motin 717ae1be01fSAlexander Motin /* Unlock queue mutex. */ 718c97bf8c3SAlexander Motin mtx_unlock(&hinfo->q_mtx); 719c97bf8c3SAlexander Motin } 720