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:
parent
65efb73fbd
commit
d0d2e523ba
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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
83
sys/netgraph/qos.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user