1169577Smav/*- 2169577Smav * Copyright (c) 2005 Nuno Antunes <nuno.antunes@gmail.com> 3169577Smav * Copyright (c) 2007 Alexander Motin <mav@freebsd.org> 4169577Smav * All rights reserved. 5169577Smav * 6169577Smav * Redistribution and use in source and binary forms, with or without 7169577Smav * modification, are permitted provided that the following conditions 8169577Smav * are met: 9169577Smav * 1. Redistributions of source code must retain the above copyright 10169577Smav * notice, this list of conditions and the following disclaimer. 11169577Smav * 2. Redistributions in binary form must reproduce the above copyright 12169577Smav * notice, this list of conditions and the following disclaimer in the 13169577Smav * documentation and/or other materials provided with the distribution. 14169577Smav * 15169577Smav * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16169577Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17169577Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18169577Smav * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19169577Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20169577Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21169577Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22169577Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23169577Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24169577Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25169577Smav * SUCH DAMAGE. 26169577Smav * 27169577Smav * $FreeBSD$ 28169577Smav */ 29169577Smav 30169577Smav/* 31169577Smav * ng_car - An implementation of commited access rate for netgraph 32169577Smav * 33169577Smav * TODO: 34169577Smav * - Sanitize input config values (impose some limits) 35169577Smav * - Implement internal packet painting (possibly using mbuf tags) 36169577Smav * - Implement color-aware mode 37169577Smav * - Implement DSCP marking for IPv4 38169577Smav */ 39169577Smav 40169577Smav#include <sys/param.h> 41169602Smav#include <sys/errno.h> 42169577Smav#include <sys/kernel.h> 43169602Smav#include <sys/malloc.h> 44169577Smav#include <sys/mbuf.h> 45169577Smav 46169577Smav#include <netgraph/ng_message.h> 47169577Smav#include <netgraph/ng_parse.h> 48169577Smav#include <netgraph/netgraph.h> 49169577Smav#include <netgraph/ng_car.h> 50169577Smav 51169577Smav#define NG_CAR_QUEUE_SIZE 100 /* Maximum queue size for SHAPE mode */ 52169577Smav#define NG_CAR_QUEUE_MIN_TH 8 /* Minimum RED threshhold for SHAPE mode */ 53169577Smav 54169577Smav/* Hook private info */ 55169577Smavstruct hookinfo { 56169577Smav hook_p hook; /* this (source) hook */ 57169577Smav hook_p dest; /* destination hook */ 58169577Smav 59169577Smav int64_t tc; /* commited token bucket counter */ 60169602Smav int64_t te; /* exceeded/peak token bucket counter */ 61177670Smav struct bintime lastRefill; /* last token refill time */ 62169577Smav 63169577Smav struct ng_car_hookconf conf; /* hook configuration */ 64169577Smav struct ng_car_hookstats stats; /* hook stats */ 65169577Smav 66169602Smav struct mbuf *q[NG_CAR_QUEUE_SIZE]; /* circular packet queue */ 67177732Smav u_int q_first; /* first queue element */ 68177732Smav u_int q_last; /* last queue element */ 69169577Smav struct callout q_callout; /* periodic queue processing routine */ 70169577Smav struct mtx q_mtx; /* queue mutex */ 71169577Smav}; 72169577Smav 73169577Smav/* Private information for each node instance */ 74169577Smavstruct privdata { 75169577Smav node_p node; /* the node itself */ 76169577Smav struct hookinfo upper; /* hook to upper layers */ 77169577Smav struct hookinfo lower; /* hook to lower layers */ 78169577Smav}; 79169577Smavtypedef struct privdata *priv_p; 80169577Smav 81169577Smavstatic ng_constructor_t ng_car_constructor; 82169577Smavstatic ng_rcvmsg_t ng_car_rcvmsg; 83169577Smavstatic ng_shutdown_t ng_car_shutdown; 84169577Smavstatic ng_newhook_t ng_car_newhook; 85169577Smavstatic ng_rcvdata_t ng_car_rcvdata; 86169577Smavstatic ng_disconnect_t ng_car_disconnect; 87169577Smav 88169577Smavstatic void ng_car_refillhook(struct hookinfo *h); 89169577Smavstatic void ng_car_schedule(struct hookinfo *h); 90169577Smavvoid ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2); 91169577Smavstatic void ng_car_enqueue(struct hookinfo *h, item_p item); 92169577Smav 93169577Smav/* Parse type for struct ng_car_hookstats */ 94169577Smavstatic const struct ng_parse_struct_field ng_car_hookstats_type_fields[] 95169577Smav = NG_CAR_HOOKSTATS; 96169577Smavstatic const struct ng_parse_type ng_car_hookstats_type = { 97169577Smav &ng_parse_struct_type, 98169577Smav &ng_car_hookstats_type_fields 99169577Smav}; 100169577Smav 101169577Smav/* Parse type for struct ng_car_bulkstats */ 102169577Smavstatic const struct ng_parse_struct_field ng_car_bulkstats_type_fields[] 103169577Smav = NG_CAR_BULKSTATS(&ng_car_hookstats_type); 104169577Smavstatic const struct ng_parse_type ng_car_bulkstats_type = { 105169577Smav &ng_parse_struct_type, 106169577Smav &ng_car_bulkstats_type_fields 107169577Smav}; 108169577Smav 109169577Smav/* Parse type for struct ng_car_hookconf */ 110169577Smavstatic const struct ng_parse_struct_field ng_car_hookconf_type_fields[] 111169577Smav = NG_CAR_HOOKCONF; 112169577Smavstatic const struct ng_parse_type ng_car_hookconf_type = { 113169577Smav &ng_parse_struct_type, 114169577Smav &ng_car_hookconf_type_fields 115169577Smav}; 116169577Smav 117169577Smav/* Parse type for struct ng_car_bulkconf */ 118169577Smavstatic const struct ng_parse_struct_field ng_car_bulkconf_type_fields[] 119169577Smav = NG_CAR_BULKCONF(&ng_car_hookconf_type); 120169577Smavstatic const struct ng_parse_type ng_car_bulkconf_type = { 121169577Smav &ng_parse_struct_type, 122169577Smav &ng_car_bulkconf_type_fields 123169577Smav}; 124169577Smav 125169577Smav/* Command list */ 126169577Smavstatic struct ng_cmdlist ng_car_cmdlist[] = { 127169577Smav { 128169577Smav NGM_CAR_COOKIE, 129169577Smav NGM_CAR_GET_STATS, 130169577Smav "getstats", 131169577Smav NULL, 132169577Smav &ng_car_bulkstats_type, 133169577Smav }, 134169577Smav { 135169577Smav NGM_CAR_COOKIE, 136169577Smav NGM_CAR_CLR_STATS, 137169577Smav "clrstats", 138169577Smav NULL, 139169577Smav NULL, 140169577Smav }, 141169577Smav { 142169577Smav NGM_CAR_COOKIE, 143169577Smav NGM_CAR_GETCLR_STATS, 144169577Smav "getclrstats", 145169577Smav NULL, 146169577Smav &ng_car_bulkstats_type, 147169577Smav }, 148169577Smav 149169577Smav { 150169577Smav NGM_CAR_COOKIE, 151169577Smav NGM_CAR_GET_CONF, 152169577Smav "getconf", 153169577Smav NULL, 154169577Smav &ng_car_bulkconf_type, 155169577Smav }, 156169577Smav { 157169577Smav NGM_CAR_COOKIE, 158169577Smav NGM_CAR_SET_CONF, 159169577Smav "setconf", 160169577Smav &ng_car_bulkconf_type, 161169577Smav NULL, 162169577Smav }, 163169577Smav { 0 } 164169577Smav}; 165169577Smav 166169577Smav/* Netgraph node type descriptor */ 167169577Smavstatic struct ng_type ng_car_typestruct = { 168169577Smav .version = NG_ABI_VERSION, 169169577Smav .name = NG_CAR_NODE_TYPE, 170169577Smav .constructor = ng_car_constructor, 171169577Smav .rcvmsg = ng_car_rcvmsg, 172169577Smav .shutdown = ng_car_shutdown, 173169577Smav .newhook = ng_car_newhook, 174169577Smav .rcvdata = ng_car_rcvdata, 175169577Smav .disconnect = ng_car_disconnect, 176169577Smav .cmdlist = ng_car_cmdlist, 177169577Smav}; 178169577SmavNETGRAPH_INIT(car, &ng_car_typestruct); 179169577Smav 180169577Smav/* 181169577Smav * Node constructor 182169577Smav */ 183169577Smavstatic int 184169577Smavng_car_constructor(node_p node) 185169577Smav{ 186169577Smav priv_p priv; 187169577Smav 188169602Smav /* Initialize private descriptor. */ 189220768Sglebius priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 190169577Smav 191169577Smav NG_NODE_SET_PRIVATE(node, priv); 192169577Smav priv->node = node; 193169577Smav 194169577Smav /* 195169577Smav * Arbitrary default values 196169577Smav */ 197169577Smav 198169577Smav priv->upper.hook = NULL; 199169577Smav priv->upper.dest = NULL; 200169577Smav priv->upper.tc = priv->upper.conf.cbs = NG_CAR_CBS_MIN; 201169577Smav priv->upper.te = priv->upper.conf.ebs = NG_CAR_EBS_MIN; 202169577Smav priv->upper.conf.cir = NG_CAR_CIR_DFLT; 203169577Smav priv->upper.conf.green_action = NG_CAR_ACTION_FORWARD; 204169577Smav priv->upper.conf.yellow_action = NG_CAR_ACTION_FORWARD; 205169577Smav priv->upper.conf.red_action = NG_CAR_ACTION_DROP; 206169577Smav priv->upper.conf.mode = 0; 207177670Smav getbinuptime(&priv->upper.lastRefill); 208169577Smav priv->upper.q_first = 0; 209169577Smav priv->upper.q_last = 0; 210169577Smav ng_callout_init(&priv->upper.q_callout); 211169577Smav mtx_init(&priv->upper.q_mtx, "ng_car_u", NULL, MTX_DEF); 212169577Smav 213169577Smav priv->lower.hook = NULL; 214169577Smav priv->lower.dest = NULL; 215169577Smav priv->lower.tc = priv->lower.conf.cbs = NG_CAR_CBS_MIN; 216169577Smav priv->lower.te = priv->lower.conf.ebs = NG_CAR_EBS_MIN; 217169577Smav priv->lower.conf.cir = NG_CAR_CIR_DFLT; 218169577Smav priv->lower.conf.green_action = NG_CAR_ACTION_FORWARD; 219169577Smav priv->lower.conf.yellow_action = NG_CAR_ACTION_FORWARD; 220169577Smav priv->lower.conf.red_action = NG_CAR_ACTION_DROP; 221169577Smav priv->lower.conf.mode = 0; 222169577Smav priv->lower.lastRefill = priv->upper.lastRefill; 223169577Smav priv->lower.q_first = 0; 224169577Smav priv->lower.q_last = 0; 225169577Smav ng_callout_init(&priv->lower.q_callout); 226169577Smav mtx_init(&priv->lower.q_mtx, "ng_car_l", NULL, MTX_DEF); 227169577Smav 228169577Smav return (0); 229169577Smav} 230169577Smav 231169577Smav/* 232169602Smav * Add a hook. 233169577Smav */ 234169577Smavstatic int 235169577Smavng_car_newhook(node_p node, hook_p hook, const char *name) 236169577Smav{ 237169577Smav const priv_p priv = NG_NODE_PRIVATE(node); 238169577Smav 239169577Smav if (strcmp(name, NG_CAR_HOOK_LOWER) == 0) { 240169577Smav priv->lower.hook = hook; 241169577Smav priv->upper.dest = hook; 242169577Smav bzero(&priv->lower.stats, sizeof(priv->lower.stats)); 243169577Smav NG_HOOK_SET_PRIVATE(hook, &priv->lower); 244169577Smav } else if (strcmp(name, NG_CAR_HOOK_UPPER) == 0) { 245169577Smav priv->upper.hook = hook; 246169577Smav priv->lower.dest = hook; 247169577Smav bzero(&priv->upper.stats, sizeof(priv->upper.stats)); 248169577Smav NG_HOOK_SET_PRIVATE(hook, &priv->upper); 249169577Smav } else 250169577Smav return (EINVAL); 251169577Smav return(0); 252169577Smav} 253169577Smav 254169577Smav/* 255169602Smav * Data has arrived. 256169577Smav */ 257169577Smavstatic int 258169577Smavng_car_rcvdata(hook_p hook, item_p item ) 259169577Smav{ 260169577Smav struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); 261177732Smav struct mbuf *m; 262169577Smav int error = 0; 263177732Smav u_int len; 264169577Smav 265169577Smav /* If queue is not empty now then enqueue packet. */ 266169577Smav if (hinfo->q_first != hinfo->q_last) { 267169577Smav ng_car_enqueue(hinfo, item); 268169577Smav return (0); 269169577Smav } 270169577Smav 271169577Smav m = NGI_M(item); 272169577Smav 273169577Smav#define NG_CAR_PERFORM_MATCH_ACTION(a) \ 274169577Smav do { \ 275169577Smav switch (a) { \ 276169577Smav case NG_CAR_ACTION_FORWARD: \ 277169602Smav /* Do nothing. */ \ 278169577Smav break; \ 279169577Smav case NG_CAR_ACTION_MARK: \ 280169577Smav /* XXX find a way to mark packets (mbuf tag?) */ \ 281169577Smav ++hinfo->stats.errors; \ 282169577Smav break; \ 283169577Smav case NG_CAR_ACTION_DROP: \ 284169577Smav default: \ 285169602Smav /* Drop packet and return. */ \ 286169577Smav NG_FREE_ITEM(item); \ 287169577Smav ++hinfo->stats.droped_pkts; \ 288169602Smav return (0); \ 289169577Smav } \ 290169602Smav } while (0) 291169577Smav 292174795Smav /* Packet is counted as 128 tokens for better resolution */ 293174795Smav if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) { 294174795Smav len = 128; 295174795Smav } else { 296174795Smav len = m->m_pkthdr.len; 297174795Smav } 298174795Smav 299169577Smav /* Check commited token bucket. */ 300174795Smav if (hinfo->tc - len >= 0) { 301169602Smav /* This packet is green. */ 302169577Smav ++hinfo->stats.green_pkts; 303174795Smav hinfo->tc -= len; 304169577Smav NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action); 305169602Smav } else { 306169577Smav 307169602Smav /* Refill only if not green without it. */ 308169602Smav ng_car_refillhook(hinfo); 309169577Smav 310169602Smav /* Check commited token bucket again after refill. */ 311174795Smav if (hinfo->tc - len >= 0) { 312169602Smav /* This packet is green */ 313169602Smav ++hinfo->stats.green_pkts; 314174795Smav hinfo->tc -= len; 315169602Smav NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action); 316169577Smav 317169602Smav /* If not green and mode is SHAPE, enqueue packet. */ 318169602Smav } else if (hinfo->conf.mode == NG_CAR_SHAPE) { 319169602Smav ng_car_enqueue(hinfo, item); 320169602Smav return (0); 321169577Smav 322169602Smav /* If not green and mode is RED, calculate probability. */ 323169602Smav } else if (hinfo->conf.mode == NG_CAR_RED) { 324169602Smav /* Is packet is bigger then extended burst? */ 325174795Smav if (len - (hinfo->tc - len) > hinfo->conf.ebs) { 326169602Smav /* This packet is definitely red. */ 327169602Smav ++hinfo->stats.red_pkts; 328169602Smav hinfo->te = 0; 329169602Smav NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); 330169577Smav 331169602Smav /* Use token bucket to simulate RED-like drop 332169602Smav probability. */ 333174795Smav } else if (hinfo->te + (len - hinfo->tc) < 334169602Smav hinfo->conf.ebs) { 335169602Smav /* This packet is yellow */ 336169602Smav ++hinfo->stats.yellow_pkts; 337174795Smav hinfo->te += len - hinfo->tc; 338169602Smav /* Go to negative tokens. */ 339174795Smav hinfo->tc -= len; 340169602Smav NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action); 341169602Smav } else { 342169602Smav /* This packet is probaly red. */ 343169602Smav ++hinfo->stats.red_pkts; 344169602Smav hinfo->te = 0; 345169602Smav NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); 346169602Smav } 347169602Smav /* If not green and mode is SINGLE/DOUBLE RATE. */ 348169577Smav } else { 349169602Smav /* Check extended token bucket. */ 350174795Smav if (hinfo->te - len >= 0) { 351169602Smav /* This packet is yellow */ 352169602Smav ++hinfo->stats.yellow_pkts; 353174795Smav hinfo->te -= len; 354169602Smav NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action); 355169602Smav } else { 356169602Smav /* This packet is red */ 357169602Smav ++hinfo->stats.red_pkts; 358169602Smav NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); 359169602Smav } 360169577Smav } 361169577Smav } 362169577Smav 363169577Smav#undef NG_CAR_PERFORM_MATCH_ACTION 364169577Smav 365177732Smav NG_FWD_ITEM_HOOK(error, item, hinfo->dest); 366169577Smav if (error != 0) 367169577Smav ++hinfo->stats.errors; 368169577Smav ++hinfo->stats.passed_pkts; 369169577Smav 370169602Smav return (error); 371169577Smav} 372169577Smav 373169577Smav/* 374169602Smav * Receive a control message. 375169577Smav */ 376169577Smavstatic int 377169577Smavng_car_rcvmsg(node_p node, item_p item, hook_p lasthook) 378169577Smav{ 379169577Smav const priv_p priv = NG_NODE_PRIVATE(node); 380169577Smav struct ng_mesg *resp = NULL; 381169577Smav int error = 0; 382169577Smav struct ng_mesg *msg; 383169577Smav 384169577Smav NGI_GET_MSG(item, msg); 385169577Smav switch (msg->header.typecookie) { 386169577Smav case NGM_CAR_COOKIE: 387169577Smav switch (msg->header.cmd) { 388169577Smav case NGM_CAR_GET_STATS: 389169577Smav case NGM_CAR_GETCLR_STATS: 390169577Smav { 391169577Smav struct ng_car_bulkstats *bstats; 392169602Smav 393169577Smav NG_MKRESPONSE(resp, msg, 394169577Smav sizeof(*bstats), M_NOWAIT); 395169577Smav if (resp == NULL) { 396169577Smav error = ENOMEM; 397169577Smav break; 398169577Smav } 399169577Smav bstats = (struct ng_car_bulkstats *)resp->data; 400169577Smav 401169577Smav bcopy(&priv->upper.stats, &bstats->downstream, 402169577Smav sizeof(bstats->downstream)); 403169577Smav bcopy(&priv->lower.stats, &bstats->upstream, 404169577Smav sizeof(bstats->upstream)); 405169577Smav } 406169577Smav if (msg->header.cmd == NGM_CAR_GET_STATS) 407169577Smav break; 408169577Smav case NGM_CAR_CLR_STATS: 409169577Smav bzero(&priv->upper.stats, 410169577Smav sizeof(priv->upper.stats)); 411169577Smav bzero(&priv->lower.stats, 412169577Smav sizeof(priv->lower.stats)); 413169577Smav break; 414169577Smav case NGM_CAR_GET_CONF: 415169577Smav { 416169577Smav struct ng_car_bulkconf *bconf; 417169602Smav 418169577Smav NG_MKRESPONSE(resp, msg, 419169577Smav sizeof(*bconf), M_NOWAIT); 420169577Smav if (resp == NULL) { 421169577Smav error = ENOMEM; 422169577Smav break; 423169577Smav } 424169577Smav bconf = (struct ng_car_bulkconf *)resp->data; 425169577Smav 426169577Smav bcopy(&priv->upper.conf, &bconf->downstream, 427169577Smav sizeof(bconf->downstream)); 428169577Smav bcopy(&priv->lower.conf, &bconf->upstream, 429169577Smav sizeof(bconf->upstream)); 430174795Smav /* Convert internal 1/(8*128) of pps into pps */ 431174795Smav if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) { 432174795Smav bconf->downstream.cir /= 1024; 433174795Smav bconf->downstream.pir /= 1024; 434174795Smav bconf->downstream.cbs /= 128; 435174795Smav bconf->downstream.ebs /= 128; 436174795Smav } 437174795Smav if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) { 438174795Smav bconf->upstream.cir /= 1024; 439174795Smav bconf->upstream.pir /= 1024; 440174795Smav bconf->upstream.cbs /= 128; 441174795Smav bconf->upstream.ebs /= 128; 442174795Smav } 443169577Smav } 444169577Smav break; 445169577Smav case NGM_CAR_SET_CONF: 446169577Smav { 447169577Smav struct ng_car_bulkconf *const bconf = 448169577Smav (struct ng_car_bulkconf *)msg->data; 449169602Smav 450169577Smav /* Check for invalid or illegal config. */ 451174795Smav if (msg->header.arglen != sizeof(*bconf)) { 452174795Smav error = EINVAL; 453174795Smav break; 454174795Smav } 455174795Smav /* Convert pps into internal 1/(8*128) of pps */ 456174795Smav if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) { 457174795Smav bconf->downstream.cir *= 1024; 458174795Smav bconf->downstream.pir *= 1024; 459174795Smav bconf->downstream.cbs *= 125; 460174795Smav bconf->downstream.ebs *= 125; 461174795Smav } 462174795Smav if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) { 463174795Smav bconf->upstream.cir *= 1024; 464174795Smav bconf->upstream.pir *= 1024; 465174795Smav bconf->upstream.cbs *= 125; 466174795Smav bconf->upstream.ebs *= 125; 467174795Smav } 468174795Smav if ((bconf->downstream.cir > 1000000000) || 469174795Smav (bconf->downstream.pir > 1000000000) || 470174795Smav (bconf->upstream.cir > 1000000000) || 471174795Smav (bconf->upstream.pir > 1000000000) || 472174795Smav (bconf->downstream.cbs == 0 && 473174795Smav bconf->downstream.ebs == 0) || 474174795Smav (bconf->upstream.cbs == 0 && 475174795Smav bconf->upstream.ebs == 0)) 476169577Smav { 477169577Smav error = EINVAL; 478169577Smav break; 479169577Smav } 480174795Smav if ((bconf->upstream.mode == NG_CAR_SHAPE) && 481174795Smav (bconf->upstream.cir == 0)) { 482174795Smav error = EINVAL; 483174795Smav break; 484174795Smav } 485174795Smav if ((bconf->downstream.mode == NG_CAR_SHAPE) && 486174795Smav (bconf->downstream.cir == 0)) { 487174795Smav error = EINVAL; 488174795Smav break; 489174795Smav } 490169577Smav 491169577Smav /* Copy downstream config. */ 492169577Smav bcopy(&bconf->downstream, &priv->upper.conf, 493169577Smav sizeof(priv->upper.conf)); 494169577Smav priv->upper.tc = priv->upper.conf.cbs; 495169577Smav if (priv->upper.conf.mode == NG_CAR_RED || 496169656Smav priv->upper.conf.mode == NG_CAR_SHAPE) { 497169577Smav priv->upper.te = 0; 498169577Smav } else { 499169577Smav priv->upper.te = priv->upper.conf.ebs; 500169577Smav } 501169577Smav 502169577Smav /* Copy upstream config. */ 503169577Smav bcopy(&bconf->upstream, &priv->lower.conf, 504169577Smav sizeof(priv->lower.conf)); 505169577Smav priv->lower.tc = priv->lower.conf.cbs; 506169577Smav if (priv->lower.conf.mode == NG_CAR_RED || 507169577Smav priv->lower.conf.mode == NG_CAR_SHAPE) { 508169577Smav priv->lower.te = 0; 509169577Smav } else { 510169577Smav priv->lower.te = priv->lower.conf.ebs; 511169577Smav } 512169577Smav } 513169577Smav break; 514169577Smav default: 515169577Smav error = EINVAL; 516169577Smav break; 517169577Smav } 518169577Smav break; 519169577Smav default: 520169577Smav error = EINVAL; 521169577Smav break; 522169577Smav } 523169577Smav NG_RESPOND_MSG(error, node, item, resp); 524169577Smav NG_FREE_MSG(msg); 525169577Smav return (error); 526169577Smav} 527169577Smav 528169577Smav/* 529169577Smav * Do local shutdown processing. 530169577Smav */ 531169577Smavstatic int 532169577Smavng_car_shutdown(node_p node) 533169577Smav{ 534169577Smav const priv_p priv = NG_NODE_PRIVATE(node); 535169577Smav 536170661Smav ng_uncallout(&priv->upper.q_callout, node); 537170661Smav ng_uncallout(&priv->lower.q_callout, node); 538169577Smav mtx_destroy(&priv->upper.q_mtx); 539169577Smav mtx_destroy(&priv->lower.q_mtx); 540169577Smav NG_NODE_UNREF(priv->node); 541169602Smav free(priv, M_NETGRAPH); 542169577Smav return (0); 543169577Smav} 544169577Smav 545169577Smav/* 546169577Smav * Hook disconnection. 547169577Smav * 548169577Smav * For this type, removal of the last link destroys the node. 549169577Smav */ 550169577Smavstatic int 551169577Smavng_car_disconnect(hook_p hook) 552169577Smav{ 553169577Smav struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); 554169577Smav const node_p node = NG_HOOK_NODE(hook); 555169577Smav const priv_p priv = NG_NODE_PRIVATE(node); 556169577Smav 557169577Smav if (hinfo) { 558169577Smav /* Purge queue if not empty. */ 559169577Smav while (hinfo->q_first != hinfo->q_last) { 560169577Smav NG_FREE_M(hinfo->q[hinfo->q_first]); 561169577Smav hinfo->q_first++; 562169577Smav if (hinfo->q_first >= NG_CAR_QUEUE_SIZE) 563169577Smav hinfo->q_first = 0; 564169577Smav } 565169577Smav /* Remove hook refs. */ 566169602Smav if (hinfo->hook == priv->upper.hook) 567169577Smav priv->lower.dest = NULL; 568169602Smav else 569169577Smav priv->upper.dest = NULL; 570169577Smav hinfo->hook = NULL; 571169577Smav } 572169577Smav /* Already shutting down? */ 573169577Smav if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 574169577Smav && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 575169577Smav ng_rmnode_self(NG_HOOK_NODE(hook)); 576169577Smav return (0); 577169577Smav} 578169577Smav 579169577Smav/* 580169577Smav * Hook's token buckets refillment. 581169577Smav */ 582169577Smavstatic void 583169577Smavng_car_refillhook(struct hookinfo *h) 584169577Smav{ 585177670Smav struct bintime newt, deltat; 586177670Smav unsigned int deltat_us; 587169577Smav 588169577Smav /* Get current time. */ 589177670Smav getbinuptime(&newt); 590169577Smav 591177670Smav /* Get time delta since last refill. */ 592177670Smav deltat = newt; 593177670Smav bintime_sub(&deltat, &h->lastRefill); 594177670Smav 595169577Smav /* Time must go forward. */ 596177670Smav if (deltat.sec < 0) { 597169577Smav h->lastRefill = newt; 598169577Smav return; 599169577Smav } 600169577Smav 601177670Smav /* But not too far forward. */ 602177670Smav if (deltat.sec >= 1000) { 603177670Smav deltat_us = (1000 << 20); 604169577Smav } else { 605177670Smav /* convert bintime to the 1/(2^20) of sec */ 606177670Smav deltat_us = (deltat.sec << 20) + (deltat.frac >> 44); 607169577Smav } 608169577Smav 609169577Smav if (h->conf.mode == NG_CAR_SINGLE_RATE) { 610177670Smav int64_t delta; 611169577Smav /* Refill commited token bucket. */ 612177670Smav h->tc += (h->conf.cir * deltat_us) >> 23; 613169577Smav delta = h->tc - h->conf.cbs; 614169577Smav if (delta > 0) { 615169577Smav h->tc = h->conf.cbs; 616169577Smav 617169577Smav /* Refill exceeded token bucket. */ 618169577Smav h->te += delta; 619177670Smav if (h->te > ((int64_t)h->conf.ebs)) 620169577Smav h->te = h->conf.ebs; 621169577Smav } 622169577Smav 623169577Smav } else if (h->conf.mode == NG_CAR_DOUBLE_RATE) { 624169577Smav /* Refill commited token bucket. */ 625177670Smav h->tc += (h->conf.cir * deltat_us) >> 23; 626177670Smav if (h->tc > ((int64_t)h->conf.cbs)) 627169577Smav h->tc = h->conf.cbs; 628169577Smav 629169577Smav /* Refill peak token bucket. */ 630177670Smav h->te += (h->conf.pir * deltat_us) >> 23; 631177670Smav if (h->te > ((int64_t)h->conf.ebs)) 632169577Smav h->te = h->conf.ebs; 633169577Smav 634169577Smav } else { /* RED or SHAPE mode. */ 635169577Smav /* Refill commited token bucket. */ 636177670Smav h->tc += (h->conf.cir * deltat_us) >> 23; 637169577Smav if (h->tc > ((int64_t)h->conf.cbs)) 638169577Smav h->tc = h->conf.cbs; 639169577Smav } 640169577Smav 641169577Smav /* Remember this moment. */ 642169577Smav h->lastRefill = newt; 643169577Smav} 644169577Smav 645169577Smav/* 646169577Smav * Schedule callout when we will have required tokens. 647169577Smav */ 648169577Smavstatic void 649169577Smavng_car_schedule(struct hookinfo *hinfo) 650169577Smav{ 651169577Smav int delay; 652169602Smav 653169577Smav delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1; 654169577Smav 655169602Smav ng_callout(&hinfo->q_callout, NG_HOOK_NODE(hinfo->hook), hinfo->hook, 656169577Smav delay, &ng_car_q_event, NULL, 0); 657169577Smav} 658169577Smav 659169577Smav/* 660169577Smav * Queue processing callout handler. 661169577Smav */ 662169577Smavvoid 663169577Smavng_car_q_event(node_p node, hook_p hook, void *arg, int arg2) 664169577Smav{ 665169577Smav struct hookinfo *hinfo = NG_HOOK_PRIVATE(hook); 666169577Smav struct mbuf *m; 667169577Smav int error; 668169577Smav 669169577Smav /* Refill tokens for time we have slept. */ 670169577Smav ng_car_refillhook(hinfo); 671169577Smav 672177732Smav /* If we have some tokens */ 673177732Smav while (hinfo->tc >= 0) { 674169577Smav 675177732Smav /* Send packet. */ 676177732Smav m = hinfo->q[hinfo->q_first]; 677177732Smav NG_SEND_DATA_ONLY(error, hinfo->dest, m); 678177732Smav if (error != 0) 679177732Smav ++hinfo->stats.errors; 680177732Smav ++hinfo->stats.passed_pkts; 681169577Smav 682177732Smav /* Get next one. */ 683177732Smav hinfo->q_first++; 684177732Smav if (hinfo->q_first >= NG_CAR_QUEUE_SIZE) 685177732Smav hinfo->q_first = 0; 686169577Smav 687177732Smav /* Stop if none left. */ 688177732Smav if (hinfo->q_first == hinfo->q_last) 689177732Smav break; 690169577Smav 691177732Smav /* If we have more packet, try it. */ 692177732Smav m = hinfo->q[hinfo->q_first]; 693177732Smav if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) { 694177732Smav hinfo->tc -= 128; 695177732Smav } else { 696177732Smav hinfo->tc -= m->m_pkthdr.len; 697169577Smav } 698169577Smav } 699169577Smav 700169577Smav /* If something left */ 701169602Smav if (hinfo->q_first != hinfo->q_last) 702169602Smav /* Schedule queue processing. */ 703169577Smav ng_car_schedule(hinfo); 704169577Smav} 705169577Smav 706169577Smav/* 707169577Smav * Enqueue packet. 708169577Smav */ 709169577Smavstatic void 710169577Smavng_car_enqueue(struct hookinfo *hinfo, item_p item) 711169577Smav{ 712169577Smav struct mbuf *m; 713169577Smav int len; 714169577Smav 715169577Smav NGI_GET_M(item, m); 716169577Smav NG_FREE_ITEM(item); 717169577Smav 718169602Smav /* Lock queue mutex. */ 719169577Smav mtx_lock(&hinfo->q_mtx); 720169577Smav 721169602Smav /* Calculate used queue length. */ 722169577Smav len = hinfo->q_last - hinfo->q_first; 723169577Smav if (len < 0) 724169577Smav len += NG_CAR_QUEUE_SIZE; 725169577Smav 726169602Smav /* If queue is overflowed or we have no RED tokens. */ 727169602Smav if ((len >= (NG_CAR_QUEUE_SIZE - 1)) || 728169577Smav (hinfo->te + len >= NG_CAR_QUEUE_SIZE)) { 729169602Smav /* Drop packet. */ 730169577Smav ++hinfo->stats.red_pkts; 731177732Smav ++hinfo->stats.droped_pkts; 732169577Smav NG_FREE_M(m); 733169602Smav 734169577Smav hinfo->te = 0; 735169577Smav } else { 736169577Smav /* This packet is yellow. */ 737169577Smav ++hinfo->stats.yellow_pkts; 738169577Smav 739169577Smav /* Enqueue packet. */ 740169577Smav hinfo->q[hinfo->q_last] = m; 741169577Smav hinfo->q_last++; 742169577Smav if (hinfo->q_last >= NG_CAR_QUEUE_SIZE) 743169577Smav hinfo->q_last = 0; 744169577Smav 745169577Smav /* Use RED tokens. */ 746169577Smav if (len > NG_CAR_QUEUE_MIN_TH) 747169577Smav hinfo->te += len - NG_CAR_QUEUE_MIN_TH; 748169577Smav 749169602Smav /* If this is a first packet in the queue. */ 750169577Smav if (len == 0) { 751174795Smav if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) { 752174795Smav hinfo->tc -= 128; 753174795Smav } else { 754174795Smav hinfo->tc -= m->m_pkthdr.len; 755174795Smav } 756169577Smav 757169602Smav /* Schedule queue processing. */ 758169577Smav ng_car_schedule(hinfo); 759169577Smav } 760169577Smav } 761169577Smav 762169602Smav /* Unlock queue mutex. */ 763169577Smav mtx_unlock(&hinfo->q_mtx); 764169577Smav} 765