Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netgraph/ng_car.c
34372 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2005 Nuno Antunes <[email protected]>
5
* Copyright (c) 2007 Alexander Motin <[email protected]>
6
* Copyright (c) 2019 Lutz Donnerhacke <[email protected]>
7
* All rights reserved.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
* SUCH DAMAGE.
29
*/
30
31
/*
32
* ng_car - An implementation of committed access rate for netgraph
33
*
34
* TODO:
35
* - Sanitize input config values (impose some limits)
36
* - Implement DSCP marking for IPv4
37
* - Decouple functionality into a simple classifier (g/y/r)
38
* and various action nodes (i.e. shape, dcsp, pcp)
39
*/
40
41
#include <sys/param.h>
42
#include <sys/errno.h>
43
#include <sys/kernel.h>
44
#include <sys/malloc.h>
45
#include <sys/mbuf.h>
46
47
#include <netgraph/ng_message.h>
48
#include <netgraph/ng_parse.h>
49
#include <netgraph/netgraph.h>
50
#include <netgraph/ng_car.h>
51
52
#include "qos.h"
53
54
#ifdef NG_SEPARATE_MALLOC
55
static MALLOC_DEFINE(M_NETGRAPH_CAR, "netgraph_car", "netgraph car node");
56
#else
57
#define M_NETGRAPH_CAR M_NETGRAPH
58
#endif
59
60
#define NG_CAR_QUEUE_SIZE 100 /* Maximum queue size for SHAPE mode */
61
#define NG_CAR_QUEUE_MIN_TH 8 /* Minimum RED threshold for SHAPE mode */
62
63
/* Hook private info */
64
struct hookinfo {
65
hook_p hook; /* this (source) hook */
66
hook_p dest; /* destination hook */
67
68
int64_t tc; /* committed token bucket counter */
69
int64_t te; /* exceeded/peak token bucket counter */
70
struct bintime lastRefill; /* last token refill time */
71
72
struct ng_car_hookconf conf; /* hook configuration */
73
struct ng_car_hookstats stats; /* hook stats */
74
75
struct mbuf *q[NG_CAR_QUEUE_SIZE]; /* circular packet queue */
76
u_int q_first; /* first queue element */
77
u_int q_last; /* last queue element */
78
struct callout q_callout; /* periodic queue processing routine */
79
struct mtx q_mtx; /* queue mutex */
80
};
81
82
/* Private information for each node instance */
83
struct privdata {
84
node_p node; /* the node itself */
85
struct hookinfo upper; /* hook to upper layers */
86
struct hookinfo lower; /* hook to lower layers */
87
};
88
typedef struct privdata *priv_p;
89
90
static ng_constructor_t ng_car_constructor;
91
static ng_rcvmsg_t ng_car_rcvmsg;
92
static ng_shutdown_t ng_car_shutdown;
93
static ng_newhook_t ng_car_newhook;
94
static ng_rcvdata_t ng_car_rcvdata;
95
static ng_disconnect_t ng_car_disconnect;
96
97
static void ng_car_refillhook(struct hookinfo *h);
98
static void ng_car_schedule(struct hookinfo *h);
99
void ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2);
100
static void ng_car_enqueue(struct hookinfo *h, item_p item);
101
102
/* Parse type for struct ng_car_hookstats */
103
static const struct ng_parse_struct_field ng_car_hookstats_type_fields[]
104
= NG_CAR_HOOKSTATS;
105
static const struct ng_parse_type ng_car_hookstats_type = {
106
&ng_parse_struct_type,
107
&ng_car_hookstats_type_fields
108
};
109
110
/* Parse type for struct ng_car_bulkstats */
111
static const struct ng_parse_struct_field ng_car_bulkstats_type_fields[]
112
= NG_CAR_BULKSTATS(&ng_car_hookstats_type);
113
static const struct ng_parse_type ng_car_bulkstats_type = {
114
&ng_parse_struct_type,
115
&ng_car_bulkstats_type_fields
116
};
117
118
/* Parse type for struct ng_car_hookconf */
119
static const struct ng_parse_struct_field ng_car_hookconf_type_fields[]
120
= NG_CAR_HOOKCONF;
121
static const struct ng_parse_type ng_car_hookconf_type = {
122
&ng_parse_struct_type,
123
&ng_car_hookconf_type_fields
124
};
125
126
/* Parse type for struct ng_car_bulkconf */
127
static const struct ng_parse_struct_field ng_car_bulkconf_type_fields[]
128
= NG_CAR_BULKCONF(&ng_car_hookconf_type);
129
static const struct ng_parse_type ng_car_bulkconf_type = {
130
&ng_parse_struct_type,
131
&ng_car_bulkconf_type_fields
132
};
133
134
/* Command list */
135
static struct ng_cmdlist ng_car_cmdlist[] = {
136
{
137
NGM_CAR_COOKIE,
138
NGM_CAR_GET_STATS,
139
"getstats",
140
NULL,
141
&ng_car_bulkstats_type,
142
},
143
{
144
NGM_CAR_COOKIE,
145
NGM_CAR_CLR_STATS,
146
"clrstats",
147
NULL,
148
NULL,
149
},
150
{
151
NGM_CAR_COOKIE,
152
NGM_CAR_GETCLR_STATS,
153
"getclrstats",
154
NULL,
155
&ng_car_bulkstats_type,
156
},
157
158
{
159
NGM_CAR_COOKIE,
160
NGM_CAR_GET_CONF,
161
"getconf",
162
NULL,
163
&ng_car_bulkconf_type,
164
},
165
{
166
NGM_CAR_COOKIE,
167
NGM_CAR_SET_CONF,
168
"setconf",
169
&ng_car_bulkconf_type,
170
NULL,
171
},
172
{ 0 }
173
};
174
175
/* Netgraph node type descriptor */
176
static struct ng_type ng_car_typestruct = {
177
.version = NG_ABI_VERSION,
178
.name = NG_CAR_NODE_TYPE,
179
.constructor = ng_car_constructor,
180
.rcvmsg = ng_car_rcvmsg,
181
.shutdown = ng_car_shutdown,
182
.newhook = ng_car_newhook,
183
.rcvdata = ng_car_rcvdata,
184
.disconnect = ng_car_disconnect,
185
.cmdlist = ng_car_cmdlist,
186
};
187
NETGRAPH_INIT(car, &ng_car_typestruct);
188
189
/*
190
* Node constructor
191
*/
192
static int
193
ng_car_constructor(node_p node)
194
{
195
priv_p priv;
196
197
/* Initialize private descriptor. */
198
priv = malloc(sizeof(*priv), M_NETGRAPH_CAR, M_WAITOK | M_ZERO);
199
200
NG_NODE_SET_PRIVATE(node, priv);
201
priv->node = node;
202
203
/*
204
* Arbitrary default values
205
*/
206
207
priv->upper.hook = NULL;
208
priv->upper.dest = NULL;
209
priv->upper.tc = priv->upper.conf.cbs = NG_CAR_CBS_MIN;
210
priv->upper.te = priv->upper.conf.ebs = NG_CAR_EBS_MIN;
211
priv->upper.conf.cir = NG_CAR_CIR_DFLT;
212
priv->upper.conf.green_action = NG_CAR_ACTION_FORWARD;
213
priv->upper.conf.yellow_action = NG_CAR_ACTION_FORWARD;
214
priv->upper.conf.red_action = NG_CAR_ACTION_DROP;
215
priv->upper.conf.mode = 0;
216
getbinuptime(&priv->upper.lastRefill);
217
priv->upper.q_first = 0;
218
priv->upper.q_last = 0;
219
ng_callout_init(&priv->upper.q_callout);
220
mtx_init(&priv->upper.q_mtx, "ng_car_u", NULL, MTX_DEF);
221
222
priv->lower.hook = NULL;
223
priv->lower.dest = NULL;
224
priv->lower.tc = priv->lower.conf.cbs = NG_CAR_CBS_MIN;
225
priv->lower.te = priv->lower.conf.ebs = NG_CAR_EBS_MIN;
226
priv->lower.conf.cir = NG_CAR_CIR_DFLT;
227
priv->lower.conf.green_action = NG_CAR_ACTION_FORWARD;
228
priv->lower.conf.yellow_action = NG_CAR_ACTION_FORWARD;
229
priv->lower.conf.red_action = NG_CAR_ACTION_DROP;
230
priv->lower.conf.mode = 0;
231
priv->lower.lastRefill = priv->upper.lastRefill;
232
priv->lower.q_first = 0;
233
priv->lower.q_last = 0;
234
ng_callout_init(&priv->lower.q_callout);
235
mtx_init(&priv->lower.q_mtx, "ng_car_l", NULL, MTX_DEF);
236
237
return (0);
238
}
239
240
/*
241
* Add a hook.
242
*/
243
static int
244
ng_car_newhook(node_p node, hook_p hook, const char *name)
245
{
246
const priv_p priv = NG_NODE_PRIVATE(node);
247
248
if (strcmp(name, NG_CAR_HOOK_LOWER) == 0) {
249
priv->lower.hook = hook;
250
priv->upper.dest = hook;
251
bzero(&priv->lower.stats, sizeof(priv->lower.stats));
252
NG_HOOK_SET_PRIVATE(hook, &priv->lower);
253
} else if (strcmp(name, NG_CAR_HOOK_UPPER) == 0) {
254
priv->upper.hook = hook;
255
priv->lower.dest = hook;
256
bzero(&priv->upper.stats, sizeof(priv->upper.stats));
257
NG_HOOK_SET_PRIVATE(hook, &priv->upper);
258
} else
259
return (EINVAL);
260
return(0);
261
}
262
263
/*
264
* Data has arrived.
265
*/
266
static int
267
ng_car_rcvdata(hook_p hook, item_p item )
268
{
269
struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
270
struct mbuf *m;
271
struct m_qos_color *colp;
272
enum qos_color col;
273
int error = 0;
274
u_int len;
275
276
/* If queue is not empty now then enqueue packet. */
277
if (hinfo->q_first != hinfo->q_last) {
278
ng_car_enqueue(hinfo, item);
279
return (0);
280
}
281
282
m = NGI_M(item);
283
284
#define NG_CAR_PERFORM_MATCH_ACTION(a,col) \
285
do { \
286
switch (a) { \
287
case NG_CAR_ACTION_FORWARD: \
288
/* Do nothing. */ \
289
break; \
290
case NG_CAR_ACTION_MARK: \
291
if (colp == NULL) { \
292
colp = (void *)m_tag_alloc( \
293
M_QOS_COOKIE, M_QOS_COLOR, \
294
MTAG_SIZE(m_qos_color), M_NOWAIT); \
295
if (colp != NULL) \
296
m_tag_prepend(m, &colp->tag); \
297
} \
298
if (colp != NULL) \
299
colp->color = col; \
300
break; \
301
case NG_CAR_ACTION_DROP: \
302
default: \
303
/* Drop packet and return. */ \
304
NG_FREE_ITEM(item); \
305
++hinfo->stats.dropped_pkts; \
306
return (0); \
307
} \
308
} while (0)
309
310
/* Packet is counted as 128 tokens for better resolution */
311
if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
312
len = 128;
313
} else {
314
len = m->m_pkthdr.len;
315
}
316
317
/* Determine current color of the packet (default green) */
318
colp = (void *)m_tag_locate(m, M_QOS_COOKIE, M_QOS_COLOR, NULL);
319
if ((hinfo->conf.opt & NG_CAR_COLOR_AWARE) && (colp != NULL))
320
col = colp->color;
321
else
322
col = QOS_COLOR_GREEN;
323
324
/* Check committed token bucket. */
325
if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) {
326
/* This packet is green. */
327
++hinfo->stats.green_pkts;
328
hinfo->tc -= len;
329
NG_CAR_PERFORM_MATCH_ACTION(
330
hinfo->conf.green_action,
331
QOS_COLOR_GREEN);
332
} else {
333
/* Refill only if not green without it. */
334
ng_car_refillhook(hinfo);
335
336
/* Check committed token bucket again after refill. */
337
if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) {
338
/* This packet is green */
339
++hinfo->stats.green_pkts;
340
hinfo->tc -= len;
341
NG_CAR_PERFORM_MATCH_ACTION(
342
hinfo->conf.green_action,
343
QOS_COLOR_GREEN);
344
345
/* If not green and mode is SHAPE, enqueue packet. */
346
} else if (hinfo->conf.mode == NG_CAR_SHAPE) {
347
ng_car_enqueue(hinfo, item);
348
return (0);
349
350
/* If not green and mode is RED, calculate probability. */
351
} else if (hinfo->conf.mode == NG_CAR_RED) {
352
/* Is packet is bigger then extended burst? */
353
if (len - (hinfo->tc - len) > hinfo->conf.ebs ||
354
col >= QOS_COLOR_RED) {
355
/* This packet is definitely red. */
356
++hinfo->stats.red_pkts;
357
hinfo->te = 0;
358
NG_CAR_PERFORM_MATCH_ACTION(
359
hinfo->conf.red_action,
360
QOS_COLOR_RED);
361
362
/* Use token bucket to simulate RED-like drop
363
probability. */
364
} else if (hinfo->te + (len - hinfo->tc) < hinfo->conf.ebs &&
365
col <= QOS_COLOR_YELLOW) {
366
/* This packet is yellow */
367
++hinfo->stats.yellow_pkts;
368
hinfo->te += len - hinfo->tc;
369
/* Go to negative tokens. */
370
hinfo->tc -= len;
371
NG_CAR_PERFORM_MATCH_ACTION(
372
hinfo->conf.yellow_action,
373
QOS_COLOR_YELLOW);
374
} else {
375
/* This packet is probably red. */
376
++hinfo->stats.red_pkts;
377
hinfo->te = 0;
378
NG_CAR_PERFORM_MATCH_ACTION(
379
hinfo->conf.red_action,
380
QOS_COLOR_RED);
381
}
382
/* If not green and mode is SINGLE/DOUBLE RATE. */
383
} else {
384
/* Check extended token bucket. */
385
if (hinfo->te - len >= 0 && col <= QOS_COLOR_YELLOW) {
386
/* This packet is yellow */
387
++hinfo->stats.yellow_pkts;
388
hinfo->te -= len;
389
NG_CAR_PERFORM_MATCH_ACTION(
390
hinfo->conf.yellow_action,
391
QOS_COLOR_YELLOW);
392
} else {
393
/* This packet is red */
394
++hinfo->stats.red_pkts;
395
NG_CAR_PERFORM_MATCH_ACTION(
396
hinfo->conf.red_action,
397
QOS_COLOR_RED);
398
}
399
}
400
}
401
402
#undef NG_CAR_PERFORM_MATCH_ACTION
403
404
NG_FWD_ITEM_HOOK(error, item, hinfo->dest);
405
if (error != 0)
406
++hinfo->stats.errors;
407
++hinfo->stats.passed_pkts;
408
409
return (error);
410
}
411
412
/*
413
* Receive a control message.
414
*/
415
static int
416
ng_car_rcvmsg(node_p node, item_p item, hook_p lasthook)
417
{
418
const priv_p priv = NG_NODE_PRIVATE(node);
419
struct ng_mesg *resp = NULL;
420
int error = 0;
421
struct ng_mesg *msg;
422
423
NGI_GET_MSG(item, msg);
424
switch (msg->header.typecookie) {
425
case NGM_CAR_COOKIE:
426
switch (msg->header.cmd) {
427
case NGM_CAR_GET_STATS:
428
case NGM_CAR_GETCLR_STATS:
429
{
430
struct ng_car_bulkstats *bstats;
431
432
NG_MKRESPONSE(resp, msg,
433
sizeof(*bstats), M_NOWAIT);
434
if (resp == NULL) {
435
error = ENOMEM;
436
break;
437
}
438
bstats = (struct ng_car_bulkstats *)resp->data;
439
440
bcopy(&priv->upper.stats, &bstats->downstream,
441
sizeof(bstats->downstream));
442
bcopy(&priv->lower.stats, &bstats->upstream,
443
sizeof(bstats->upstream));
444
}
445
if (msg->header.cmd == NGM_CAR_GET_STATS)
446
break;
447
case NGM_CAR_CLR_STATS:
448
bzero(&priv->upper.stats,
449
sizeof(priv->upper.stats));
450
bzero(&priv->lower.stats,
451
sizeof(priv->lower.stats));
452
break;
453
case NGM_CAR_GET_CONF:
454
{
455
struct ng_car_bulkconf *bconf;
456
457
NG_MKRESPONSE(resp, msg,
458
sizeof(*bconf), M_NOWAIT);
459
if (resp == NULL) {
460
error = ENOMEM;
461
break;
462
}
463
bconf = (struct ng_car_bulkconf *)resp->data;
464
465
bcopy(&priv->upper.conf, &bconf->downstream,
466
sizeof(bconf->downstream));
467
bcopy(&priv->lower.conf, &bconf->upstream,
468
sizeof(bconf->upstream));
469
/* Convert internal 1/(8*128) of pps into pps */
470
if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) {
471
bconf->downstream.cir /= 1024;
472
bconf->downstream.pir /= 1024;
473
bconf->downstream.cbs /= 128;
474
bconf->downstream.ebs /= 128;
475
}
476
if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) {
477
bconf->upstream.cir /= 1024;
478
bconf->upstream.pir /= 1024;
479
bconf->upstream.cbs /= 128;
480
bconf->upstream.ebs /= 128;
481
}
482
}
483
break;
484
case NGM_CAR_SET_CONF:
485
{
486
struct ng_car_bulkconf *const bconf =
487
(struct ng_car_bulkconf *)msg->data;
488
489
/* Check for invalid or illegal config. */
490
if (msg->header.arglen != sizeof(*bconf)) {
491
error = EINVAL;
492
break;
493
}
494
/* Convert pps into internal 1/(8*128) of pps */
495
if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) {
496
bconf->downstream.cir *= 1024;
497
bconf->downstream.pir *= 1024;
498
bconf->downstream.cbs *= 128;
499
bconf->downstream.ebs *= 128;
500
}
501
if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) {
502
bconf->upstream.cir *= 1024;
503
bconf->upstream.pir *= 1024;
504
bconf->upstream.cbs *= 128;
505
bconf->upstream.ebs *= 128;
506
}
507
if ((bconf->downstream.cir > 1000000000) ||
508
(bconf->downstream.pir > 1000000000) ||
509
(bconf->upstream.cir > 1000000000) ||
510
(bconf->upstream.pir > 1000000000) ||
511
(bconf->downstream.cbs == 0 &&
512
bconf->downstream.ebs == 0) ||
513
(bconf->upstream.cbs == 0 &&
514
bconf->upstream.ebs == 0))
515
{
516
error = EINVAL;
517
break;
518
}
519
if ((bconf->upstream.mode == NG_CAR_SHAPE) &&
520
(bconf->upstream.cir == 0)) {
521
error = EINVAL;
522
break;
523
}
524
if ((bconf->downstream.mode == NG_CAR_SHAPE) &&
525
(bconf->downstream.cir == 0)) {
526
error = EINVAL;
527
break;
528
}
529
530
/* Copy downstream config. */
531
bcopy(&bconf->downstream, &priv->upper.conf,
532
sizeof(priv->upper.conf));
533
priv->upper.tc = priv->upper.conf.cbs;
534
if (priv->upper.conf.mode == NG_CAR_RED ||
535
priv->upper.conf.mode == NG_CAR_SHAPE) {
536
priv->upper.te = 0;
537
} else {
538
priv->upper.te = priv->upper.conf.ebs;
539
}
540
541
/* Copy upstream config. */
542
bcopy(&bconf->upstream, &priv->lower.conf,
543
sizeof(priv->lower.conf));
544
priv->lower.tc = priv->lower.conf.cbs;
545
if (priv->lower.conf.mode == NG_CAR_RED ||
546
priv->lower.conf.mode == NG_CAR_SHAPE) {
547
priv->lower.te = 0;
548
} else {
549
priv->lower.te = priv->lower.conf.ebs;
550
}
551
}
552
break;
553
default:
554
error = EINVAL;
555
break;
556
}
557
break;
558
default:
559
error = EINVAL;
560
break;
561
}
562
NG_RESPOND_MSG(error, node, item, resp);
563
NG_FREE_MSG(msg);
564
return (error);
565
}
566
567
/*
568
* Do local shutdown processing.
569
*/
570
static int
571
ng_car_shutdown(node_p node)
572
{
573
const priv_p priv = NG_NODE_PRIVATE(node);
574
575
ng_uncallout(&priv->upper.q_callout, node);
576
ng_uncallout(&priv->lower.q_callout, node);
577
mtx_destroy(&priv->upper.q_mtx);
578
mtx_destroy(&priv->lower.q_mtx);
579
NG_NODE_UNREF(priv->node);
580
free(priv, M_NETGRAPH_CAR);
581
return (0);
582
}
583
584
/*
585
* Hook disconnection.
586
*
587
* For this type, removal of the last link destroys the node.
588
*/
589
static int
590
ng_car_disconnect(hook_p hook)
591
{
592
struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
593
const node_p node = NG_HOOK_NODE(hook);
594
const priv_p priv = NG_NODE_PRIVATE(node);
595
596
if (hinfo) {
597
/* Purge queue if not empty. */
598
while (hinfo->q_first != hinfo->q_last) {
599
NG_FREE_M(hinfo->q[hinfo->q_first]);
600
hinfo->q_first++;
601
if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
602
hinfo->q_first = 0;
603
}
604
/* Remove hook refs. */
605
if (hinfo->hook == priv->upper.hook)
606
priv->lower.dest = NULL;
607
else
608
priv->upper.dest = NULL;
609
hinfo->hook = NULL;
610
}
611
/* Already shutting down? */
612
if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
613
&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
614
ng_rmnode_self(NG_HOOK_NODE(hook));
615
return (0);
616
}
617
618
/*
619
* Hook's token buckets refillment.
620
*/
621
static void
622
ng_car_refillhook(struct hookinfo *h)
623
{
624
struct bintime newt, deltat;
625
unsigned int deltat_us;
626
627
/* Get current time. */
628
getbinuptime(&newt);
629
630
/* Get time delta since last refill. */
631
deltat = newt;
632
bintime_sub(&deltat, &h->lastRefill);
633
634
/* Time must go forward. */
635
if (deltat.sec < 0) {
636
h->lastRefill = newt;
637
return;
638
}
639
640
/* But not too far forward. */
641
if (deltat.sec >= 1000) {
642
deltat_us = (1000 << 20);
643
} else {
644
/* convert bintime to the 1/(2^20) of sec */
645
deltat_us = (deltat.sec << 20) + (deltat.frac >> 44);
646
}
647
648
if (h->conf.mode == NG_CAR_SINGLE_RATE) {
649
int64_t delta;
650
/* Refill committed token bucket. */
651
h->tc += (h->conf.cir * deltat_us) >> 23;
652
delta = h->tc - h->conf.cbs;
653
if (delta > 0) {
654
h->tc = h->conf.cbs;
655
656
/* Refill exceeded token bucket. */
657
h->te += delta;
658
if (h->te > ((int64_t)h->conf.ebs))
659
h->te = h->conf.ebs;
660
}
661
662
} else if (h->conf.mode == NG_CAR_DOUBLE_RATE) {
663
/* Refill committed token bucket. */
664
h->tc += (h->conf.cir * deltat_us) >> 23;
665
if (h->tc > ((int64_t)h->conf.cbs))
666
h->tc = h->conf.cbs;
667
668
/* Refill peak token bucket. */
669
h->te += (h->conf.pir * deltat_us) >> 23;
670
if (h->te > ((int64_t)h->conf.ebs))
671
h->te = h->conf.ebs;
672
673
} else { /* RED or SHAPE mode. */
674
/* Refill committed token bucket. */
675
h->tc += (h->conf.cir * deltat_us) >> 23;
676
if (h->tc > ((int64_t)h->conf.cbs))
677
h->tc = h->conf.cbs;
678
}
679
680
/* Remember this moment. */
681
h->lastRefill = newt;
682
}
683
684
/*
685
* Schedule callout when we will have required tokens.
686
*/
687
static void
688
ng_car_schedule(struct hookinfo *hinfo)
689
{
690
int delay;
691
692
delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1;
693
694
ng_callout(&hinfo->q_callout, NG_HOOK_NODE(hinfo->hook), hinfo->hook,
695
delay, &ng_car_q_event, NULL, 0);
696
}
697
698
/*
699
* Queue processing callout handler.
700
*/
701
void
702
ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2)
703
{
704
struct hookinfo *hinfo = NG_HOOK_PRIVATE(hook);
705
struct mbuf *m;
706
int error;
707
708
/* Refill tokens for time we have slept. */
709
ng_car_refillhook(hinfo);
710
711
/* If we have some tokens */
712
while (hinfo->tc >= 0) {
713
/* Send packet. */
714
m = hinfo->q[hinfo->q_first];
715
NG_SEND_DATA_ONLY(error, hinfo->dest, m);
716
if (error != 0)
717
++hinfo->stats.errors;
718
++hinfo->stats.passed_pkts;
719
720
/* Get next one. */
721
hinfo->q_first++;
722
if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
723
hinfo->q_first = 0;
724
725
/* Stop if none left. */
726
if (hinfo->q_first == hinfo->q_last)
727
break;
728
729
/* If we have more packet, try it. */
730
m = hinfo->q[hinfo->q_first];
731
if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
732
hinfo->tc -= 128;
733
} else {
734
hinfo->tc -= m->m_pkthdr.len;
735
}
736
}
737
738
/* If something left */
739
if (hinfo->q_first != hinfo->q_last)
740
/* Schedule queue processing. */
741
ng_car_schedule(hinfo);
742
}
743
744
/*
745
* Enqueue packet.
746
*/
747
static void
748
ng_car_enqueue(struct hookinfo *hinfo, item_p item)
749
{
750
struct mbuf *m;
751
int len;
752
struct m_qos_color *colp;
753
enum qos_color col;
754
755
NGI_GET_M(item, m);
756
NG_FREE_ITEM(item);
757
758
/* Determine current color of the packet (default green) */
759
colp = (void *)m_tag_locate(m, M_QOS_COOKIE, M_QOS_COLOR, NULL);
760
if ((hinfo->conf.opt & NG_CAR_COLOR_AWARE) && (colp != NULL))
761
col = colp->color;
762
else
763
col = QOS_COLOR_GREEN;
764
765
/* Lock queue mutex. */
766
mtx_lock(&hinfo->q_mtx);
767
768
/* Calculate used queue length. */
769
len = hinfo->q_last - hinfo->q_first;
770
if (len < 0)
771
len += NG_CAR_QUEUE_SIZE;
772
773
/* If queue is overflowed or we have no RED tokens. */
774
if ((len >= (NG_CAR_QUEUE_SIZE - 1)) ||
775
(hinfo->te + len >= NG_CAR_QUEUE_SIZE) ||
776
(col >= QOS_COLOR_RED)) {
777
/* Drop packet. */
778
++hinfo->stats.red_pkts;
779
++hinfo->stats.dropped_pkts;
780
NG_FREE_M(m);
781
782
hinfo->te = 0;
783
} else {
784
/* This packet is yellow. */
785
++hinfo->stats.yellow_pkts;
786
787
/* Enqueue packet. */
788
hinfo->q[hinfo->q_last] = m;
789
hinfo->q_last++;
790
if (hinfo->q_last >= NG_CAR_QUEUE_SIZE)
791
hinfo->q_last = 0;
792
793
/* Use RED tokens. */
794
if (len > NG_CAR_QUEUE_MIN_TH)
795
hinfo->te += len - NG_CAR_QUEUE_MIN_TH;
796
797
/* If this is a first packet in the queue. */
798
if (len == 0) {
799
if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
800
hinfo->tc -= 128;
801
} else {
802
hinfo->tc -= m->m_pkthdr.len;
803
}
804
805
/* Schedule queue processing. */
806
ng_car_schedule(hinfo);
807
}
808
}
809
810
/* Unlock queue mutex. */
811
mtx_unlock(&hinfo->q_mtx);
812
}
813
814