netgraph/ng_car: Add color marking code

Chained policing should be able to reuse the classification of
traffic.  A new mbuf_tag type is defined to handle gereral QoS
marking.  A new subtype is defined to track the color marking.

Reviewed by:	manpages (bcr), melifaro, kp
Approved by:	kp (mentor)
Sponsored by:	IKS Service GmbH
MFC after:	1 month
Differential Revision: https://reviews.freebsd.org/D22110
This commit is contained in:
Lutz Donnerhacke 2021-01-27 21:19:14 +01:00
parent 65efb73fbd
commit d0d2e523ba
4 changed files with 156 additions and 27 deletions

View File

@ -25,7 +25,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd November 13, 2012 .Dd January 27, 2021
.Dt NG_CAR 4 .Dt NG_CAR 4
.Os .Os
.Sh NAME .Sh NAME
@ -108,6 +108,7 @@ Traffic shaping is much more polite to the TCP traffic than rate limit on
links with bandwidth * delay product less than 6-8 TCP segments, but it links with bandwidth * delay product less than 6-8 TCP segments, but it
consumes additional system resources for queue processing. consumes additional system resources for queue processing.
.El .El
.Pp
By default, all information rates are measured in bits per second and bursts By default, all information rates are measured in bits per second and bursts
are measured in bytes. are measured in bytes.
But when NG_CAR_COUNT_PACKETS option is enabled, But when NG_CAR_COUNT_PACKETS option is enabled,
@ -138,7 +139,8 @@ struct ng_car_hookconf {
/* possible actions (..._action) */ /* possible actions (..._action) */
enum { enum {
NG_CAR_ACTION_FORWARD = 1, NG_CAR_ACTION_FORWARD = 1,
NG_CAR_ACTION_DROP NG_CAR_ACTION_DROP,
NG_CAR_ACTION_MARK
}; };
/* operation modes (mode) */ /* operation modes (mode) */
@ -149,7 +151,8 @@ enum {
NG_CAR_SHAPE NG_CAR_SHAPE
}; };
/* mode options (opt) */ /* mode options (bits for opt) */
#define NG_CAR_COLOR_AWARE 1
#define NG_CAR_COUNT_PACKETS 2 #define NG_CAR_COUNT_PACKETS 2
struct ng_car_bulkconf { struct ng_car_bulkconf {

View File

@ -3,6 +3,7 @@
* *
* Copyright (c) 2005 Nuno Antunes <nuno.antunes@gmail.com> * Copyright (c) 2005 Nuno Antunes <nuno.antunes@gmail.com>
* Copyright (c) 2007 Alexander Motin <mav@freebsd.org> * Copyright (c) 2007 Alexander Motin <mav@freebsd.org>
* Copyright (c) 2019 Lutz Donnerhacke <lutz@donnerhacke.de>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -34,9 +35,9 @@
* *
* TODO: * TODO:
* - Sanitize input config values (impose some limits) * - Sanitize input config values (impose some limits)
* - Implement internal packet painting (possibly using mbuf tags)
* - Implement color-aware mode
* - Implement DSCP marking for IPv4 * - Implement DSCP marking for IPv4
* - Decouple functionality into a simple classifier (g/y/r)
* and various action nodes (i.e. shape, dcsp, pcp)
*/ */
#include <sys/param.h> #include <sys/param.h>
@ -50,6 +51,8 @@
#include <netgraph/netgraph.h> #include <netgraph/netgraph.h>
#include <netgraph/ng_car.h> #include <netgraph/ng_car.h>
#include "qos.h"
#define NG_CAR_QUEUE_SIZE 100 /* Maximum queue size for SHAPE mode */ #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 */ #define NG_CAR_QUEUE_MIN_TH 8 /* Minimum RED threshold for SHAPE mode */
@ -261,6 +264,8 @@ ng_car_rcvdata(hook_p hook, item_p item )
{ {
struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
struct mbuf *m; struct mbuf *m;
struct m_qos_color *colp;
enum qos_color col;
int error = 0; int error = 0;
u_int len; u_int len;
@ -272,15 +277,22 @@ ng_car_rcvdata(hook_p hook, item_p item )
m = NGI_M(item); m = NGI_M(item);
#define NG_CAR_PERFORM_MATCH_ACTION(a) \ #define NG_CAR_PERFORM_MATCH_ACTION(a,col) \
do { \ do { \
switch (a) { \ switch (a) { \
case NG_CAR_ACTION_FORWARD: \ case NG_CAR_ACTION_FORWARD: \
/* Do nothing. */ \ /* Do nothing. */ \
break; \ break; \
case NG_CAR_ACTION_MARK: \ case NG_CAR_ACTION_MARK: \
/* XXX find a way to mark packets (mbuf tag?) */ \ if (colp == NULL) { \
++hinfo->stats.errors; \ 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; \ break; \
case NG_CAR_ACTION_DROP: \ case NG_CAR_ACTION_DROP: \
default: \ default: \
@ -298,22 +310,33 @@ ng_car_rcvdata(hook_p hook, item_p item )
len = m->m_pkthdr.len; 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. */ /* Check committed token bucket. */
if (hinfo->tc - len >= 0) { if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) {
/* This packet is green. */ /* This packet is green. */
++hinfo->stats.green_pkts; ++hinfo->stats.green_pkts;
hinfo->tc -= len; 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 { } else {
/* Refill only if not green without it. */ /* Refill only if not green without it. */
ng_car_refillhook(hinfo); ng_car_refillhook(hinfo);
/* Check committed token bucket again after refill. */ /* 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 */ /* This packet is green */
++hinfo->stats.green_pkts; ++hinfo->stats.green_pkts;
hinfo->tc -= len; 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. */ /* If not green and mode is SHAPE, enqueue packet. */
} else if (hinfo->conf.mode == NG_CAR_SHAPE) { } else if (hinfo->conf.mode == NG_CAR_SHAPE) {
@ -323,40 +346,51 @@ ng_car_rcvdata(hook_p hook, item_p item )
/* If not green and mode is RED, calculate probability. */ /* If not green and mode is RED, calculate probability. */
} else if (hinfo->conf.mode == NG_CAR_RED) { } else if (hinfo->conf.mode == NG_CAR_RED) {
/* Is packet is bigger then extended burst? */ /* 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. */ /* This packet is definitely red. */
++hinfo->stats.red_pkts; ++hinfo->stats.red_pkts;
hinfo->te = 0; 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 /* Use token bucket to simulate RED-like drop
probability. */ probability. */
} else if (hinfo->te + (len - hinfo->tc) < } else if (hinfo->te + (len - hinfo->tc) < hinfo->conf.ebs &&
hinfo->conf.ebs) { col <= QOS_COLOR_YELLOW) {
/* This packet is yellow */ /* This packet is yellow */
++hinfo->stats.yellow_pkts; ++hinfo->stats.yellow_pkts;
hinfo->te += len - hinfo->tc; hinfo->te += len - hinfo->tc;
/* Go to negative tokens. */ /* Go to negative tokens. */
hinfo->tc -= len; 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 { } else {
/* This packet is probably red. */ /* This packet is probably red. */
++hinfo->stats.red_pkts; ++hinfo->stats.red_pkts;
hinfo->te = 0; 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. */ /* If not green and mode is SINGLE/DOUBLE RATE. */
} else { } else {
/* Check extended token bucket. */ /* Check extended token bucket. */
if (hinfo->te - len >= 0) { if (hinfo->te - len >= 0 && col <= QOS_COLOR_YELLOW) {
/* This packet is yellow */ /* This packet is yellow */
++hinfo->stats.yellow_pkts; ++hinfo->stats.yellow_pkts;
hinfo->te -= len; 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 { } else {
/* This packet is red */ /* This packet is red */
++hinfo->stats.red_pkts; ++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);
} }
} }
} }
@ -711,10 +745,19 @@ ng_car_enqueue(struct hookinfo *hinfo, item_p item)
{ {
struct mbuf *m; struct mbuf *m;
int len; int len;
struct m_qos_color *colp;
enum qos_color col;
NGI_GET_M(item, m); NGI_GET_M(item, m);
NG_FREE_ITEM(item); 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. */ /* Lock queue mutex. */
mtx_lock(&hinfo->q_mtx); mtx_lock(&hinfo->q_mtx);
@ -725,7 +768,8 @@ ng_car_enqueue(struct hookinfo *hinfo, item_p item)
/* If queue is overflowed or we have no RED tokens. */ /* If queue is overflowed or we have no RED tokens. */
if ((len >= (NG_CAR_QUEUE_SIZE - 1)) || 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. */ /* Drop packet. */
++hinfo->stats.red_pkts; ++hinfo->stats.red_pkts;
++hinfo->stats.dropped_pkts; ++hinfo->stats.dropped_pkts;

View File

@ -103,8 +103,7 @@ struct ng_car_hookconf {
enum { enum {
NG_CAR_ACTION_FORWARD = 1, NG_CAR_ACTION_FORWARD = 1,
NG_CAR_ACTION_DROP, NG_CAR_ACTION_DROP,
NG_CAR_ACTION_MARK, NG_CAR_ACTION_MARK
NG_CAR_ACTION_SET_TOS
}; };
/* operation modes (mode) */ /* operation modes (mode) */
@ -115,7 +114,7 @@ enum {
NG_CAR_SHAPE NG_CAR_SHAPE
}; };
/* mode options (opt) */ /* mode options (bits in opt) */
#define NG_CAR_COLOR_AWARE 1 #define NG_CAR_COLOR_AWARE 1
#define NG_CAR_COUNT_PACKETS 2 #define NG_CAR_COUNT_PACKETS 2

83
sys/netgraph/qos.h Normal file
View File

@ -0,0 +1,83 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2019 Lutz Donnerhacke <lutz@donnerhacke.de>
*
* 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 <sys/mbuf.h>
/* 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_ */