summaryrefslogtreecommitdiff
path: root/lib/route/qdisc/netem.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/route/qdisc/netem.c')
-rw-r--r--lib/route/qdisc/netem.c301
1 files changed, 195 insertions, 106 deletions
diff --git a/lib/route/qdisc/netem.c b/lib/route/qdisc/netem.c
index 06d9fe8c..17dee3b7 100644
--- a/lib/route/qdisc/netem.c
+++ b/lib/route/qdisc/netem.c
@@ -29,8 +29,8 @@
/** @cond SKIP */
#define SCH_NETEM_ATTR_LATENCY 0x0001
#define SCH_NETEM_ATTR_LIMIT 0x0002
-#define SCH_NETEM_ATTR_LOSS 0x0004
-#define SCH_NETEM_ATTR_GAP 0x0008
+#define SCH_NETEM_ATTR_LOSS 0x0004
+#define SCH_NETEM_ATTR_GAP 0x0008
#define SCH_NETEM_ATTR_DUPLICATE 0x0010
#define SCH_NETEM_ATTR_JITTER 0x0020
#define SCH_NETEM_ATTR_DELAY_CORR 0x0040
@@ -40,7 +40,7 @@
#define SCH_NETEM_ATTR_RO_CORR 0x0400
#define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800
#define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000
-#define SCH_NETEM_ATTR_DIST 0x2000
+#define SCH_NETEM_ATTR_DIST 0x2000
/** @endcond */
static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
@@ -76,7 +76,7 @@ static int netem_msg_parser(struct rtnl_tc *tc, void *data)
struct nlattr *tb[TCA_NETEM_MAX+1];
err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
- (tc->tc_opts->d_data + sizeof(*opts)),
+ ((char *) tc->tc_opts->d_data + sizeof(*opts)),
len, netem_policy);
if (err < 0) {
free(netem);
@@ -106,18 +106,18 @@ static int netem_msg_parser(struct rtnl_tc *tc, void *data)
netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
SCH_NETEM_ATTR_RO_CORR);
}
-
+
if (tb[TCA_NETEM_CORRUPT]) {
struct tc_netem_corrupt corrupt;
-
+
nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt));
netem->qnm_crpt.nmcr_probability = corrupt.probability;
netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
-
+
netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
SCH_NETEM_ATTR_CORRUPT_CORR);
}
-
+
/* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */
netem->qnm_dist.dist_data = NULL;
netem->qnm_dist.dist_size = 0;
@@ -129,10 +129,10 @@ static int netem_msg_parser(struct rtnl_tc *tc, void *data)
static void netem_free_data(struct rtnl_tc *tc, void *data)
{
struct rtnl_netem *netem = data;
-
+
if (!netem)
return;
-
+
free(netem->qnm_dist.dist_data);
}
@@ -141,12 +141,69 @@ static void netem_dump_line(struct rtnl_tc *tc, void *data,
{
struct rtnl_netem *netem = data;
- if (netem)
- nl_dump(p, "limit %d", netem->qnm_limit);
+ if (netem) {
+ if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0)
+ nl_dump(p, " limit %dpkts", netem->qnm_limit);
+ else
+ nl_dump(p, " no limit");
+ }
+}
+
+static void netem_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_netem *netem = data;
+ char buf[32];
+
+ if (netem) {
+ if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) {
+ nl_msec2str(nl_ticks2us(netem->qnm_latency) / 1000, buf, sizeof(buf));
+ nl_dump(p, " latency %s", buf);
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) {
+ nl_msec2str(nl_ticks2us(netem->qnm_jitter) / 1000, buf, sizeof(buf));
+ nl_dump(p, " jitter %s", buf);
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR && netem->qnm_corr.nmc_delay > 0)
+ nl_dump(p, " %d%", netem->qnm_corr.nmc_delay);
+ }
+ }
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) {
+ nl_dump(p, " loss %d%", netem->qnm_loss);
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR && netem->qnm_corr.nmc_loss > 0)
+ nl_dump(p, " %d%", netem->qnm_corr.nmc_loss);
+ }
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) {
+ nl_dump(p, " duplicate %d%", netem->qnm_duplicate);
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR && netem->qnm_corr.nmc_duplicate > 0)
+ nl_dump(p, " %d%", netem->qnm_corr.nmc_duplicate);
+ }
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB && netem->qnm_ro.nmro_probability > 0) {
+ nl_dump(p, " reorder %d%", netem->qnm_ro.nmro_probability);
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR && netem->qnm_ro.nmro_correlation > 0)
+ nl_dump(p, " %d%", netem->qnm_ro.nmro_correlation);
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0)
+ nl_dump(p, " gap %d", netem->qnm_gap);
+ }
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB && netem->qnm_crpt.nmcr_probability > 0) {
+ nl_dump(p, " reorder %d%", netem->qnm_crpt.nmcr_probability);
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR && netem->qnm_crpt.nmcr_correlation > 0)
+ nl_dump(p, " %d%", netem->qnm_crpt.nmcr_correlation);
+ }
+ }
}
static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data,
- struct nl_msg *msg)
+ struct nl_msg *msg)
{
int err = 0;
struct tc_netem_qopt opts;
@@ -154,9 +211,13 @@ static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data,
struct tc_netem_reorder reorder;
struct tc_netem_corrupt corrupt;
struct rtnl_netem *netem = data;
-
- unsigned char set_correlation = 0, set_reorder = 0,
- set_corrupt = 0, set_dist = 0;
+
+ unsigned char set_correlation = 0, set_reorder = 0;
+ unsigned char set_corrupt = 0, set_dist = 0;
+
+ struct nlattr* head;
+ struct nlattr* tail;
+ int old_len;
if (!netem)
BUG();
@@ -167,116 +228,111 @@ static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data,
memset(&corrupt, 0, sizeof(corrupt));
msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
-
- if ( netem->qnm_ro.nmro_probability != 0 ) {
- if (netem->qnm_latency == 0) {
+
+ if (netem->qnm_ro.nmro_probability != 0) {
+ if (netem->qnm_latency == 0)
return -NLE_MISSING_ATTR;
- }
- if (netem->qnm_gap == 0) netem->qnm_gap = 1;
- }
- else if ( netem->qnm_gap ) {
+ if (netem->qnm_gap == 0)
+ netem->qnm_gap = 1;
+ } else if (netem->qnm_gap)
return -NLE_MISSING_ATTR;
- }
- if ( netem->qnm_corr.nmc_delay != 0 ) {
- if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
+ if (netem->qnm_corr.nmc_delay != 0) {
+ if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
return -NLE_MISSING_ATTR;
- }
set_correlation = 1;
}
-
- if ( netem->qnm_corr.nmc_loss != 0 ) {
- if ( netem->qnm_loss == 0 ) {
+
+ if (netem->qnm_corr.nmc_loss != 0) {
+ if (netem->qnm_loss == 0)
return -NLE_MISSING_ATTR;
- }
set_correlation = 1;
}
- if ( netem->qnm_corr.nmc_duplicate != 0 ) {
- if ( netem->qnm_duplicate == 0 ) {
+ if (netem->qnm_corr.nmc_duplicate != 0) {
+ if (netem->qnm_duplicate == 0)
return -NLE_MISSING_ATTR;
- }
set_correlation = 1;
}
-
- if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1;
- else if ( netem->qnm_ro.nmro_correlation != 0 ) {
- return -NLE_MISSING_ATTR;
- }
-
- if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1;
- else if ( netem->qnm_crpt.nmcr_correlation != 0 ) {
- return -NLE_MISSING_ATTR;
- }
-
- if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) {
- if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
+
+ if (netem->qnm_ro.nmro_probability != 0)
+ set_reorder = 1;
+ else if (netem->qnm_ro.nmro_correlation != 0)
+ return -NLE_MISSING_ATTR;
+
+ if (netem->qnm_crpt.nmcr_probability != 0)
+ set_corrupt = 1;
+ else if (netem->qnm_crpt.nmcr_correlation != 0)
+ return -NLE_MISSING_ATTR;
+
+ if (netem->qnm_dist.dist_data && netem->qnm_dist.dist_size) {
+ if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
return -NLE_MISSING_ATTR;
- }
- else {
- /* Resize to accomodate the large distribution table */
- int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
- sizeof(netem->qnm_dist.dist_data[0]);
-
- msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len);
- if ( msg->nm_nlh == NULL )
- return -NLE_NOMEM;
- msg->nm_size = new_msg_len;
+ else {
+ /* Resize to accomodate the large distribution table */
+ int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
+ sizeof(netem->qnm_dist.dist_data[0]);
+ struct nlmsghdr *new_nlh = realloc(msg->nm_nlh, new_msg_len);
+
+ if (new_nlh == NULL)
+ return -NLE_NOMEM;
+ msg->nm_nlh = new_nlh;
+ msg->nm_size = new_msg_len;
set_dist = 1;
}
}
-
+
opts.latency = netem->qnm_latency;
opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
opts.loss = netem->qnm_loss;
opts.gap = netem->qnm_gap;
opts.duplicate = netem->qnm_duplicate;
opts.jitter = netem->qnm_jitter;
-
+
NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts);
-
- if ( set_correlation ) {
+
+ if (set_correlation) {
cor.delay_corr = netem->qnm_corr.nmc_delay;
cor.loss_corr = netem->qnm_corr.nmc_loss;
cor.dup_corr = netem->qnm_corr.nmc_duplicate;
NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor);
}
-
- if ( set_reorder ) {
+
+ if (set_reorder) {
reorder.probability = netem->qnm_ro.nmro_probability;
reorder.correlation = netem->qnm_ro.nmro_correlation;
NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
}
-
- if ( set_corrupt ) {
+
+ if (set_corrupt) {
corrupt.probability = netem->qnm_crpt.nmcr_probability;
corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
}
-
- if ( set_dist ) {
+
+ if (set_dist) {
NLA_PUT(msg, TCA_NETEM_DELAY_DIST,
- netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
- netem->qnm_dist.dist_data);
+ netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
+ netem->qnm_dist.dist_data);
}
/* Length specified in the TCA_OPTIONS section must span the entire
* remainder of the message. That's just the way that sch_netem expects it.
* Maybe there's a more succinct way to do this at a higher level.
*/
- struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) +
- NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
-
- struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) +
- NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
-
- int old_len = head->nla_len;
- head->nla_len = (void *)tail - (void *)head;
+ head = (struct nlattr *)(((char *) NLMSG_DATA(msg->nm_nlh)) +
+ NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
+
+ tail = (struct nlattr *)(((char *) (msg->nm_nlh)) +
+ NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
+
+ old_len = head->nla_len;
+ head->nla_len = (char *)tail - (char *)head;
msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
-
+
return err;
nla_put_failure:
return -NLE_MSGSIZE;
@@ -299,7 +355,7 @@ void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
BUG();
-
+
netem->qnm_limit = limit;
netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
}
@@ -440,7 +496,7 @@ int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
* @name Corruption
* @{
*/
-
+
/**
* Set corruption probability of netem qdisc.
* @arg qdisc Netem qdisc to be modified.
@@ -815,17 +871,42 @@ int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_p
}
/**
- * Set the delay distribution. Latency/jitter must be set before applying.
+ * Set the delay distribution data. Latency/jitter must be set before applying.
* @arg qdisc Netem qdisc.
- * @arg dist_type The name of the distribution (type, file, path/file).
* @return 0 on success, error code on failure.
*/
-int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
+int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len) {
struct rtnl_netem *netem;
+ int16_t *new_data;
if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
BUG();
-
+
+ if (len > MAXDIST)
+ return -NLE_INVAL;
+
+ new_data = (int16_t *) calloc(len, sizeof(int16_t));
+ if (!new_data)
+ return -NLE_NOMEM;
+
+ free (netem->qnm_dist.dist_data);
+ netem->qnm_dist.dist_data = new_data;
+
+ memcpy(netem->qnm_dist.dist_data, data, len * sizeof(int16_t));
+
+ netem->qnm_dist.dist_size = len;
+ netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
+
+ return 0;
+}
+
+/**
+ * Load the delay distribution from a file. Latency/jitter must be set before applying.
+ * @arg qdisc Netem qdisc.
+ * @arg dist_type The name of the distribution (type, file, path/file).
+ * @return 0 on success, error code on failure.
+ */
+int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
FILE *f;
int n = 0;
size_t i;
@@ -833,31 +914,39 @@ int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist
char *line;
char name[NAME_MAX];
char dist_suffix[] = ".dist";
-
+ int16_t *data;
+ char *test_suffix;
+
+ /* Check several locations for the dist file */
+ char *test_path[] = {
+ "",
+ "./",
+ "/usr/lib/tc/",
+ "/usr/lib64/tc/",
+ "/usr/local/lib/tc/",
+ };
+
/* If the given filename already ends in .dist, don't append it later */
- char *test_suffix = strstr(dist_type, dist_suffix);
+ test_suffix = strstr(dist_type, dist_suffix);
if (test_suffix != NULL && strlen(test_suffix) == 5)
strcpy(dist_suffix, "");
-
- /* Check several locations for the dist file */
- char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" };
-
+
for (i = 0; i < ARRAY_SIZE(test_path); i++) {
snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
- if ((f = fopen(name, "r")))
+ if ((f = fopen(name, "re")))
break;
}
-
- if ( f == NULL )
+
+ if (f == NULL)
return -nl_syserr2nlerr(errno);
-
- netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
-
+
+ data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
+
line = (char *) calloc (sizeof(char), len + 1);
-
+
while (getline(&line, &len, f) != -1) {
char *p, *endp;
-
+
if (*line == '\n' || *line == '#')
continue;
@@ -870,17 +959,16 @@ int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist
fclose(f);
return -NLE_INVAL;
}
- netem->qnm_dist.dist_data[n++] = x;
- }
+ data[n++] = x;
+ }
}
-
+
free(line);
-
- netem->qnm_dist.dist_size = n;
- netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
-
fclose(f);
- return 0;
+
+ i = rtnl_netem_set_delay_distribution_data(qdisc, data, n);
+ free(data);
+ return i;
}
/** @} */
@@ -892,6 +980,7 @@ static struct rtnl_tc_ops netem_ops = {
.to_msg_parser = netem_msg_parser,
.to_free_data = netem_free_data,
.to_dump[NL_DUMP_LINE] = netem_dump_line,
+ .to_dump[NL_DUMP_DETAILS] = netem_dump_details,
.to_msg_fill_raw = netem_msg_fill_raw,
};