diff --git a/share/man/man4/ng_car.4 b/share/man/man4/ng_car.4 --- a/share/man/man4/ng_car.4 +++ b/share/man/man4/ng_car.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 13, 2012 +.Dd January 27, 2021 .Dt NG_CAR 4 .Os .Sh NAME @@ -108,6 +108,7 @@ links with bandwidth * delay product less than 6-8 TCP segments, but it consumes additional system resources for queue processing. .El +.Pp By default, all information rates are measured in bits per second and bursts are measured in bytes. But when NG_CAR_COUNT_PACKETS option is enabled, @@ -138,7 +139,8 @@ /* possible actions (..._action) */ enum { NG_CAR_ACTION_FORWARD = 1, - NG_CAR_ACTION_DROP + NG_CAR_ACTION_DROP, + NG_CAR_ACTION_MARK }; /* operation modes (mode) */ @@ -149,7 +151,8 @@ NG_CAR_SHAPE }; -/* mode options (opt) */ +/* mode options (bits for opt) */ +#define NG_CAR_COLOR_AWARE 1 #define NG_CAR_COUNT_PACKETS 2 struct ng_car_bulkconf { diff --git a/sys/netgraph/ng_car.h b/sys/netgraph/ng_car.h --- a/sys/netgraph/ng_car.h +++ b/sys/netgraph/ng_car.h @@ -103,8 +103,7 @@ enum { NG_CAR_ACTION_FORWARD = 1, NG_CAR_ACTION_DROP, - NG_CAR_ACTION_MARK, - NG_CAR_ACTION_SET_TOS + NG_CAR_ACTION_MARK }; /* operation modes (mode) */ @@ -115,7 +114,7 @@ NG_CAR_SHAPE }; -/* mode options (opt) */ +/* mode options (bits in opt) */ #define NG_CAR_COLOR_AWARE 1 #define NG_CAR_COUNT_PACKETS 2 diff --git a/sys/netgraph/ng_car.c b/sys/netgraph/ng_car.c --- a/sys/netgraph/ng_car.c +++ b/sys/netgraph/ng_car.c @@ -3,6 +3,7 @@ * * Copyright (c) 2005 Nuno Antunes * Copyright (c) 2007 Alexander Motin + * Copyright (c) 2019 Lutz Donnerhacke * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,9 +35,9 @@ * * TODO: * - Sanitize input config values (impose some limits) - * - Implement internal packet painting (possibly using mbuf tags) - * - Implement color-aware mode * - Implement DSCP marking for IPv4 + * - Decouple functionality into a simple classifier (g/y/r) + * and various action nodes (i.e. shape, dcsp, pcp) */ #include @@ -50,6 +51,8 @@ #include #include +#include "qos.h" + #define NG_CAR_QUEUE_SIZE 100 /* Maximum queue size for SHAPE mode */ #define NG_CAR_QUEUE_MIN_TH 8 /* Minimum RED threshold for SHAPE mode */ @@ -261,6 +264,8 @@ { struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); struct mbuf *m; + struct m_qos_color *colp; + enum qos_color col; int error = 0; u_int len; @@ -272,15 +277,22 @@ m = NGI_M(item); -#define NG_CAR_PERFORM_MATCH_ACTION(a) \ +#define NG_CAR_PERFORM_MATCH_ACTION(a,col) \ do { \ switch (a) { \ case NG_CAR_ACTION_FORWARD: \ /* Do nothing. */ \ break; \ case NG_CAR_ACTION_MARK: \ - /* XXX find a way to mark packets (mbuf tag?) */ \ - ++hinfo->stats.errors; \ + if (colp == NULL) { \ + colp = (void *)m_tag_alloc( \ + M_QOS_COOKIE, M_QOS_COLOR, \ + MTAG_SIZE(m_qos_color), M_NOWAIT); \ + if (colp != NULL) \ + m_tag_prepend(m, &colp->tag); \ + } \ + if (colp != NULL) \ + colp->color = col; \ break; \ case NG_CAR_ACTION_DROP: \ default: \ @@ -298,22 +310,33 @@ len = m->m_pkthdr.len; } + /* Determine current color of the packet (default green) */ + colp = (void *)m_tag_locate(m, M_QOS_COOKIE, M_QOS_COLOR, NULL); + if ((hinfo->conf.opt & NG_CAR_COLOR_AWARE) && (colp != NULL)) + col = colp->color; + else + col = QOS_COLOR_GREEN; + /* Check committed token bucket. */ - if (hinfo->tc - len >= 0) { + if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) { /* This packet is green. */ ++hinfo->stats.green_pkts; hinfo->tc -= len; - NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action); + NG_CAR_PERFORM_MATCH_ACTION( + hinfo->conf.green_action, + QOS_COLOR_GREEN); } else { /* Refill only if not green without it. */ ng_car_refillhook(hinfo); /* Check committed token bucket again after refill. */ - if (hinfo->tc - len >= 0) { + if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) { /* This packet is green */ ++hinfo->stats.green_pkts; hinfo->tc -= len; - NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.green_action); + NG_CAR_PERFORM_MATCH_ACTION( + hinfo->conf.green_action, + QOS_COLOR_GREEN); /* If not green and mode is SHAPE, enqueue packet. */ } else if (hinfo->conf.mode == NG_CAR_SHAPE) { @@ -323,40 +346,51 @@ /* If not green and mode is RED, calculate probability. */ } else if (hinfo->conf.mode == NG_CAR_RED) { /* Is packet is bigger then extended burst? */ - if (len - (hinfo->tc - len) > hinfo->conf.ebs) { + if (len - (hinfo->tc - len) > hinfo->conf.ebs || + col >= QOS_COLOR_RED) { /* This packet is definitely red. */ ++hinfo->stats.red_pkts; hinfo->te = 0; - NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); + NG_CAR_PERFORM_MATCH_ACTION( + hinfo->conf.red_action, + QOS_COLOR_RED); /* Use token bucket to simulate RED-like drop probability. */ - } else if (hinfo->te + (len - hinfo->tc) < - hinfo->conf.ebs) { + } else if (hinfo->te + (len - hinfo->tc) < hinfo->conf.ebs && + col <= QOS_COLOR_YELLOW) { /* This packet is yellow */ ++hinfo->stats.yellow_pkts; hinfo->te += len - hinfo->tc; /* Go to negative tokens. */ hinfo->tc -= len; - NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action); + NG_CAR_PERFORM_MATCH_ACTION( + hinfo->conf.yellow_action, + QOS_COLOR_YELLOW); } else { /* This packet is probably red. */ ++hinfo->stats.red_pkts; hinfo->te = 0; - NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); + NG_CAR_PERFORM_MATCH_ACTION( + hinfo->conf.red_action, + QOS_COLOR_RED); } /* If not green and mode is SINGLE/DOUBLE RATE. */ } else { /* Check extended token bucket. */ - if (hinfo->te - len >= 0) { + if (hinfo->te - len >= 0 && col <= QOS_COLOR_YELLOW) { /* This packet is yellow */ ++hinfo->stats.yellow_pkts; hinfo->te -= len; - NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.yellow_action); + NG_CAR_PERFORM_MATCH_ACTION( + hinfo->conf.yellow_action, + QOS_COLOR_YELLOW); } else { /* This packet is red */ ++hinfo->stats.red_pkts; - NG_CAR_PERFORM_MATCH_ACTION(hinfo->conf.red_action); + NG_CAR_PERFORM_MATCH_ACTION( + hinfo->conf.red_action, + QOS_COLOR_RED); } } } @@ -709,12 +743,21 @@ static void ng_car_enqueue(struct hookinfo *hinfo, item_p item) { - struct mbuf *m; - int len; + struct mbuf *m; + int len; + struct m_qos_color *colp; + enum qos_color col; NGI_GET_M(item, m); NG_FREE_ITEM(item); + /* Determine current color of the packet (default green) */ + colp = (void *)m_tag_locate(m, M_QOS_COOKIE, M_QOS_COLOR, NULL); + if ((hinfo->conf.opt & NG_CAR_COLOR_AWARE) && (colp != NULL)) + col = colp->color; + else + col = QOS_COLOR_GREEN; + /* Lock queue mutex. */ mtx_lock(&hinfo->q_mtx); @@ -725,7 +768,8 @@ /* If queue is overflowed or we have no RED tokens. */ if ((len >= (NG_CAR_QUEUE_SIZE - 1)) || - (hinfo->te + len >= NG_CAR_QUEUE_SIZE)) { + (hinfo->te + len >= NG_CAR_QUEUE_SIZE) || + (col >= QOS_COLOR_RED)) { /* Drop packet. */ ++hinfo->stats.red_pkts; ++hinfo->stats.dropped_pkts; diff --git a/sys/netgraph/qos.h b/sys/netgraph/qos.h new file mode 100644 --- /dev/null +++ b/sys/netgraph/qos.h @@ -0,0 +1,83 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Lutz Donnerhacke + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETGRAPH_QOS_H_ +#define _NETGRAPH_QOS_H_ + +#include + +/* ABI cookie */ +#define M_QOS_COOKIE 1571268051 +#define MTAG_SIZE(X) ( sizeof(struct X) - sizeof(struct m_tag) ) + +/* + * Definition of types within this ABI: + * - Choose a type (16bit) by i.e. "echo $((1000+$(date +%s)%64536))" + * - Retry if the type is already in use + * - Define the structure for the type according to mbuf_tags(9) + * struct m_qos_foo { + * struct m_tag tag; + * ... + * }; + * Preferred usage: + * struct m_qos_foo *p = (void *)m_tag_locate(m, + * M_QOS_COOKIE, M_QOS_FOO, ...); + * or + * p = (void *)m_tag_alloc( + * M_QOS_COOKIE, M_QOS_FOO, MTAG_SIZE(foo), ...); + m_tag_prepend(m, &p->tag); + */ + +/* Color marking type */ +#define M_QOS_COLOR 23568 +/* Keep colors ordered semantically in order to allow use of "<=" or ">=" */ +enum qos_color { + QOS_COLOR_GREEN, + QOS_COLOR_YELLOW, + QOS_COLOR_RED +}; +struct m_qos_color { + struct m_tag tag; + enum qos_color color; +}; + +/* + * Priority class + * + * Processing per priority requires an overhead, which should + * be static (i.e. for alloctating queues) and small (for memory) + * So keep your chosen range limited. + */ +#define M_QOS_PRIORITY 28858 +struct m_qos_priority { + struct m_tag tag; + uint8_t priority; /* 0 - lowest */ +}; + +#endif /* _NETGRAPH_QOS_H_ */