mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-10-06 12:26:02 +00:00
netem: add support for 4 state and GE loss model
Incorporate support for new loss models.
This commit is contained in:
parent
fd6fac34e9
commit
3c7950af59
214
tc/q_netem.c
214
tc/q_netem.c
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -31,9 +33,11 @@ static void explain(void)
|
|||||||
"Usage: ... netem [ limit PACKETS ] \n" \
|
"Usage: ... netem [ limit PACKETS ] \n" \
|
||||||
" [ delay TIME [ JITTER [CORRELATION]]]\n" \
|
" [ delay TIME [ JITTER [CORRELATION]]]\n" \
|
||||||
" [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
|
" [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
|
||||||
" [ drop PERCENT [CORRELATION]] \n" \
|
|
||||||
" [ corrupt PERCENT [CORRELATION]] \n" \
|
" [ corrupt PERCENT [CORRELATION]] \n" \
|
||||||
" [ duplicate PERCENT [CORRELATION]]\n" \
|
" [ duplicate PERCENT [CORRELATION]]\n" \
|
||||||
|
" [ loss random PERCENT [CORRELATION]]\n" \
|
||||||
|
" [ loss state P13 [P31 [P32 [P23 P14]]]\n" \
|
||||||
|
" [ loss gemodel PERCENT [R [1-H [1-K]]]\n" \
|
||||||
" [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n");
|
" [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +51,51 @@ static void explain1(const char *arg)
|
|||||||
*/
|
*/
|
||||||
#define MAX_DIST (16*1024)
|
#define MAX_DIST (16*1024)
|
||||||
|
|
||||||
|
static const double max_percent_value = 0xffffffff;
|
||||||
|
|
||||||
|
/* scaled value used to percent of maximum. */
|
||||||
|
static void set_percent(__u32 *percent, double per)
|
||||||
|
{
|
||||||
|
*percent = (unsigned) rint(per * max_percent_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse either a fraction '.3' or percent '30%
|
||||||
|
* return: 0 = ok, -1 = error, 1 = out of range
|
||||||
|
*/
|
||||||
|
static int parse_percent(double *val, const char *str)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
*val = strtod(str, &p) / 100.;
|
||||||
|
if (*p && strcmp(p, "%") )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_percent(__u32 *percent, const char *str)
|
||||||
|
{
|
||||||
|
double per;
|
||||||
|
|
||||||
|
if (parse_percent(&per, str))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
set_percent(percent, per);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_percent(char *buf, int len, __u32 per)
|
||||||
|
{
|
||||||
|
snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
char * sprint_percent(__u32 per, char *buf)
|
||||||
|
{
|
||||||
|
print_percent(buf, SPRINT_BSIZE-1, per);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simplistic file parser for distrbution data.
|
* Simplistic file parser for distrbution data.
|
||||||
* Format is:
|
* Format is:
|
||||||
@ -95,14 +144,7 @@ static int get_distribution(const char *type, __s16 *data, int maxdata)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int isnumber(const char *arg)
|
#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
return strtod(arg, &p) != 0 || p != arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isnumber(argv[1]))
|
|
||||||
|
|
||||||
/* Adjust for the fact that psched_ticks aren't always usecs
|
/* Adjust for the fact that psched_ticks aren't always usecs
|
||||||
(based on kernel PSCHED_CLOCK configuration */
|
(based on kernel PSCHED_CLOCK configuration */
|
||||||
@ -127,21 +169,22 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
|
|||||||
{
|
{
|
||||||
int dist_size = 0;
|
int dist_size = 0;
|
||||||
struct rtattr *tail;
|
struct rtattr *tail;
|
||||||
struct tc_netem_qopt opt;
|
struct tc_netem_qopt opt = { .limit = 1000 };
|
||||||
struct tc_netem_corr cor;
|
struct tc_netem_corr cor;
|
||||||
struct tc_netem_reorder reorder;
|
struct tc_netem_reorder reorder;
|
||||||
struct tc_netem_corrupt corrupt;
|
struct tc_netem_corrupt corrupt;
|
||||||
|
struct tc_netem_gimodel gimodel;
|
||||||
|
struct tc_netem_gemodel gemodel;
|
||||||
__s16 *dist_data = NULL;
|
__s16 *dist_data = NULL;
|
||||||
|
__u16 loss_type = NETEM_LOSS_UNSPEC;
|
||||||
int present[__TCA_NETEM_MAX];
|
int present[__TCA_NETEM_MAX];
|
||||||
|
|
||||||
memset(&opt, 0, sizeof(opt));
|
|
||||||
opt.limit = 1000;
|
|
||||||
memset(&cor, 0, sizeof(cor));
|
memset(&cor, 0, sizeof(cor));
|
||||||
memset(&reorder, 0, sizeof(reorder));
|
memset(&reorder, 0, sizeof(reorder));
|
||||||
memset(&corrupt, 0, sizeof(corrupt));
|
memset(&corrupt, 0, sizeof(corrupt));
|
||||||
memset(present, 0, sizeof(present));
|
memset(present, 0, sizeof(present));
|
||||||
|
|
||||||
while (argc > 0) {
|
for( ; argc > 0; --argc, ++argv) {
|
||||||
if (matches(*argv, "limit") == 0) {
|
if (matches(*argv, "limit") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (get_size(&opt.limit, *argv)) {
|
if (get_size(&opt.limit, *argv)) {
|
||||||
@ -174,19 +217,112 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
|
|||||||
}
|
}
|
||||||
} else if (matches(*argv, "loss") == 0 ||
|
} else if (matches(*argv, "loss") == 0 ||
|
||||||
matches(*argv, "drop") == 0) {
|
matches(*argv, "drop") == 0) {
|
||||||
|
if (opt.loss > 0 || loss_type != NETEM_LOSS_UNSPEC) {
|
||||||
|
explain1("duplicate loss argument\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
|
/* Old (deprecated) random loss model syntax */
|
||||||
|
if (isdigit(argv[0][0]))
|
||||||
|
goto random_loss_model;
|
||||||
|
|
||||||
|
if (!strcmp(*argv, "random")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
random_loss_model:
|
||||||
if (get_percent(&opt.loss, *argv)) {
|
if (get_percent(&opt.loss, *argv)) {
|
||||||
explain1("loss");
|
explain1("loss percent");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (NEXT_IS_NUMBER()) {
|
if (NEXT_IS_NUMBER()) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
++present[TCA_NETEM_CORR];
|
++present[TCA_NETEM_CORR];
|
||||||
if (get_percent(&cor.loss_corr, *argv)) {
|
if (get_percent(&cor.loss_corr, *argv)) {
|
||||||
explain1("loss");
|
explain1("loss correllation");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (!strcmp(*argv, "state")) {
|
||||||
|
double p13;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
if (parse_percent(&p13, *argv)) {
|
||||||
|
explain1("loss p13");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set defaults */
|
||||||
|
set_percent(&gimodel.p13, p13);
|
||||||
|
set_percent(&gimodel.p31, 1. - p13);
|
||||||
|
set_percent(&gimodel.p32, 0);
|
||||||
|
set_percent(&gimodel.p23, 1.);
|
||||||
|
loss_type = NETEM_LOSS_GI;
|
||||||
|
|
||||||
|
if (!NEXT_IS_NUMBER())
|
||||||
|
continue;
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_percent(&gimodel.p31, *argv)) {
|
||||||
|
explain1("loss p31");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NEXT_IS_NUMBER())
|
||||||
|
continue;
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_percent(&gimodel.p32, *argv)) {
|
||||||
|
explain1("loss p32");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NEXT_IS_NUMBER())
|
||||||
|
continue;
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_percent(&gimodel.p23, *argv)) {
|
||||||
|
explain1("loss p23");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!strcmp(*argv, "gemodel")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_percent(&gemodel.p, *argv)) {
|
||||||
|
explain1("loss gemodel p");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set defaults */
|
||||||
|
set_percent(&gemodel.r, 1.);
|
||||||
|
set_percent(&gemodel.h, 0);
|
||||||
|
set_percent(&gemodel.k1, 1.);
|
||||||
|
loss_type = NETEM_LOSS_GE;
|
||||||
|
|
||||||
|
if (!NEXT_IS_NUMBER())
|
||||||
|
continue;
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_percent(&gemodel.r, *argv)) {
|
||||||
|
explain1("loss gemodel r");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NEXT_IS_NUMBER())
|
||||||
|
continue;
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_percent(&gemodel.h, *argv)) {
|
||||||
|
explain1("loss gemodel h");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NEXT_IS_NUMBER())
|
||||||
|
continue;
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_percent(&gemodel.k1, *argv)) {
|
||||||
|
explain1("loss gemodel k");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unknown loss parameter: %s\n",
|
||||||
|
*argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
} else if (matches(*argv, "reorder") == 0) {
|
} else if (matches(*argv, "reorder") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
present[TCA_NETEM_REORDER] = 1;
|
present[TCA_NETEM_REORDER] = 1;
|
||||||
@ -252,7 +388,6 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
|
|||||||
explain();
|
explain();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
argc--; argv++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tail = NLMSG_TAIL(n);
|
tail = NLMSG_TAIL(n);
|
||||||
@ -290,6 +425,26 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
|
|||||||
addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
|
addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (loss_type != NETEM_LOSS_UNSPEC) {
|
||||||
|
struct rtattr *start;
|
||||||
|
|
||||||
|
start = addattr_nest(n, 1024, TCA_NETEM_LOSS | NLA_F_NESTED);
|
||||||
|
if (loss_type == NETEM_LOSS_GI) {
|
||||||
|
if (addattr_l(n, 1024, NETEM_LOSS_GI,
|
||||||
|
&gimodel, sizeof(gimodel)) < 0)
|
||||||
|
return -1;
|
||||||
|
} else if (loss_type == NETEM_LOSS_GE) {
|
||||||
|
if (addattr_l(n, 1024, NETEM_LOSS_GE,
|
||||||
|
&gemodel, sizeof(gemodel)) < 0)
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "loss in the weeds!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
addattr_nest_end(n, start);
|
||||||
|
}
|
||||||
|
|
||||||
if (dist_data) {
|
if (dist_data) {
|
||||||
if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
|
if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
|
||||||
TCA_NETEM_DELAY_DIST,
|
TCA_NETEM_DELAY_DIST,
|
||||||
@ -306,6 +461,8 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
|
|||||||
const struct tc_netem_corr *cor = NULL;
|
const struct tc_netem_corr *cor = NULL;
|
||||||
const struct tc_netem_reorder *reorder = NULL;
|
const struct tc_netem_reorder *reorder = NULL;
|
||||||
const struct tc_netem_corrupt *corrupt = NULL;
|
const struct tc_netem_corrupt *corrupt = NULL;
|
||||||
|
const struct tc_netem_gimodel *gimodel = NULL;
|
||||||
|
const struct tc_netem_gemodel *gemodel = NULL;
|
||||||
struct tc_netem_qopt qopt;
|
struct tc_netem_qopt qopt;
|
||||||
int len = RTA_PAYLOAD(opt) - sizeof(qopt);
|
int len = RTA_PAYLOAD(opt) - sizeof(qopt);
|
||||||
SPRINT_BUF(b1);
|
SPRINT_BUF(b1);
|
||||||
@ -339,6 +496,15 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
|
|||||||
return -1;
|
return -1;
|
||||||
corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
|
corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
|
||||||
}
|
}
|
||||||
|
if (tb[TCA_NETEM_LOSS]) {
|
||||||
|
struct rtattr *lb[NETEM_LOSS_MAX + 1];
|
||||||
|
|
||||||
|
parse_rtattr_nested(lb, NETEM_LOSS_MAX, tb[TCA_NETEM_LOSS]);
|
||||||
|
if (lb[NETEM_LOSS_GI])
|
||||||
|
gemodel = RTA_DATA(lb[NETEM_LOSS_GI]);
|
||||||
|
if (lb[NETEM_LOSS_GE])
|
||||||
|
gemodel = RTA_DATA(lb[NETEM_LOSS_GE]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(f, "limit %d", qopt.limit);
|
fprintf(f, "limit %d", qopt.limit);
|
||||||
@ -359,6 +525,22 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
|
|||||||
fprintf(f, " %s", sprint_percent(cor->loss_corr, b1));
|
fprintf(f, " %s", sprint_percent(cor->loss_corr, b1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gimodel) {
|
||||||
|
fprintf(f, " loss state p13 %s", sprint_percent(gimodel->p13, b1));
|
||||||
|
fprintf(f, " p31 %s", sprint_percent(gimodel->p31, b1));
|
||||||
|
fprintf(f, " p32 %s", sprint_percent(gimodel->p32, b1));
|
||||||
|
fprintf(f, " p23 %s", sprint_percent(gimodel->p23, b1));
|
||||||
|
fprintf(f, " p14 %s", sprint_percent(gimodel->p14, b1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gemodel) {
|
||||||
|
fprintf(f, "loss gemodel p %s",
|
||||||
|
sprint_percent(gemodel->p, b1));
|
||||||
|
fprintf(f, " r %s", sprint_percent(gemodel->r, b1));
|
||||||
|
fprintf(f, " 1-h %s", sprint_percent(gemodel->h, b1));
|
||||||
|
fprintf(f, " 1-k %s", sprint_percent(gemodel->k1, b1));
|
||||||
|
}
|
||||||
|
|
||||||
if (qopt.duplicate) {
|
if (qopt.duplicate) {
|
||||||
fprintf(f, " duplicate %s",
|
fprintf(f, " duplicate %s",
|
||||||
sprint_percent(qopt.duplicate, b1));
|
sprint_percent(qopt.duplicate, b1));
|
||||||
|
27
tc/tc_util.c
27
tc/tc_util.c
@ -352,33 +352,6 @@ char * sprint_size(__u32 size, char *buf)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const double max_percent_value = 0xffffffff;
|
|
||||||
|
|
||||||
int get_percent(__u32 *percent, const char *str)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
double per = strtod(str, &p) / 100.;
|
|
||||||
|
|
||||||
if (per > 1. || per < 0)
|
|
||||||
return -1;
|
|
||||||
if (*p && strcmp(p, "%"))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
*percent = (unsigned) rint(per * max_percent_value);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_percent(char *buf, int len, __u32 per)
|
|
||||||
{
|
|
||||||
snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
char * sprint_percent(__u32 per, char *buf)
|
|
||||||
{
|
|
||||||
print_percent(buf, SPRINT_BSIZE-1, per);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_qdisc_handle(char *buf, int len, __u32 h)
|
void print_qdisc_handle(char *buf, int len, __u32 h)
|
||||||
{
|
{
|
||||||
snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16);
|
snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16);
|
||||||
|
@ -58,7 +58,6 @@ extern struct filter_util *get_filter_kind(const char *str);
|
|||||||
|
|
||||||
extern int get_qdisc_handle(__u32 *h, const char *str);
|
extern int get_qdisc_handle(__u32 *h, const char *str);
|
||||||
extern int get_rate(unsigned *rate, const char *str);
|
extern int get_rate(unsigned *rate, const char *str);
|
||||||
extern int get_percent(unsigned *percent, const char *str);
|
|
||||||
extern int get_size(unsigned *size, const char *str);
|
extern int get_size(unsigned *size, const char *str);
|
||||||
extern int get_size_and_cell(unsigned *size, int *cell_log, char *str);
|
extern int get_size_and_cell(unsigned *size, int *cell_log, char *str);
|
||||||
extern int get_time(unsigned *time, const char *str);
|
extern int get_time(unsigned *time, const char *str);
|
||||||
@ -66,7 +65,6 @@ extern int get_linklayer(unsigned *val, const char *arg);
|
|||||||
|
|
||||||
extern void print_rate(char *buf, int len, __u32 rate);
|
extern void print_rate(char *buf, int len, __u32 rate);
|
||||||
extern void print_size(char *buf, int len, __u32 size);
|
extern void print_size(char *buf, int len, __u32 size);
|
||||||
extern void print_percent(char *buf, int len, __u32 percent);
|
|
||||||
extern void print_qdisc_handle(char *buf, int len, __u32 h);
|
extern void print_qdisc_handle(char *buf, int len, __u32 h);
|
||||||
extern void print_time(char *buf, int len, __u32 time);
|
extern void print_time(char *buf, int len, __u32 time);
|
||||||
extern void print_linklayer(char *buf, int len, unsigned linklayer);
|
extern void print_linklayer(char *buf, int len, unsigned linklayer);
|
||||||
@ -76,7 +74,6 @@ extern char * sprint_qdisc_handle(__u32 h, char *buf);
|
|||||||
extern char * sprint_tc_classid(__u32 h, char *buf);
|
extern char * sprint_tc_classid(__u32 h, char *buf);
|
||||||
extern char * sprint_time(__u32 time, char *buf);
|
extern char * sprint_time(__u32 time, char *buf);
|
||||||
extern char * sprint_ticks(__u32 ticks, char *buf);
|
extern char * sprint_ticks(__u32 ticks, char *buf);
|
||||||
extern char * sprint_percent(__u32 percent, char *buf);
|
|
||||||
extern char * sprint_linklayer(unsigned linklayer, char *buf);
|
extern char * sprint_linklayer(unsigned linklayer, char *buf);
|
||||||
|
|
||||||
extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats);
|
extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats);
|
||||||
|
Loading…
Reference in New Issue
Block a user