15#include "nl-default.h"
17#include <netlink/netlink.h>
18#include <netlink/utils.h>
19#include <netlink/route/qdisc.h>
20#include <netlink/route/qdisc/netem.h>
23#include "nl-priv-dynamic-core/nl-core.h"
26struct rtnl_netem_corr {
29 uint32_t nmc_duplicate;
32struct rtnl_netem_reo {
33 uint32_t nmro_probability;
34 uint32_t nmro_correlation;
37struct rtnl_netem_crpt {
38 uint32_t nmcr_probability;
39 uint32_t nmcr_correlation;
42struct rtnl_netem_dist {
52 uint32_t qnm_duplicate;
55 struct rtnl_netem_corr qnm_corr;
56 struct rtnl_netem_reo qnm_ro;
57 struct rtnl_netem_crpt qnm_crpt;
58 struct rtnl_netem_dist qnm_dist;
61#define SCH_NETEM_ATTR_LATENCY 0x0001
62#define SCH_NETEM_ATTR_LIMIT 0x0002
63#define SCH_NETEM_ATTR_LOSS 0x0004
64#define SCH_NETEM_ATTR_GAP 0x0008
65#define SCH_NETEM_ATTR_DUPLICATE 0x0010
66#define SCH_NETEM_ATTR_JITTER 0x0020
67#define SCH_NETEM_ATTR_DELAY_CORR 0x0040
68#define SCH_NETEM_ATTR_LOSS_CORR 0x0080
69#define SCH_NETEM_ATTR_DUP_CORR 0x0100
70#define SCH_NETEM_ATTR_RO_PROB 0x0200
71#define SCH_NETEM_ATTR_RO_CORR 0x0400
72#define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800
73#define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000
74#define SCH_NETEM_ATTR_DIST 0x2000
79 [TCA_NETEM_REORDER] = { .minlen =
sizeof(
struct tc_netem_reorder) },
80 [TCA_NETEM_CORRUPT] = { .minlen =
sizeof(
struct tc_netem_corrupt) },
83static int netem_msg_parser(
struct rtnl_tc *tc,
void *data)
85 struct rtnl_netem *netem = data;
86 struct tc_netem_qopt *opts;
89 if (tc->tc_opts->d_size <
sizeof(*opts))
92 opts = (
struct tc_netem_qopt *) tc->tc_opts->d_data;
93 netem->qnm_latency = opts->latency;
94 netem->qnm_limit = opts->limit;
95 netem->qnm_loss = opts->loss;
96 netem->qnm_gap = opts->gap;
97 netem->qnm_duplicate = opts->duplicate;
98 netem->qnm_jitter = opts->jitter;
100 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT |
101 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
102 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
104 len = tc->tc_opts->d_size -
sizeof(*opts);
107 struct nlattr *tb[TCA_NETEM_MAX+1];
109 err =
nla_parse(tb, TCA_NETEM_MAX, (
struct nlattr *)
110 ((
char *) tc->tc_opts->d_data +
sizeof(*opts)),
117 if (tb[TCA_NETEM_CORR]) {
118 struct tc_netem_corr cor;
120 nla_memcpy(&cor, tb[TCA_NETEM_CORR],
sizeof(cor));
121 netem->qnm_corr.nmc_delay = cor.delay_corr;
122 netem->qnm_corr.nmc_loss = cor.loss_corr;
123 netem->qnm_corr.nmc_duplicate = cor.dup_corr;
125 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
126 SCH_NETEM_ATTR_LOSS_CORR |
127 SCH_NETEM_ATTR_DUP_CORR);
130 if (tb[TCA_NETEM_REORDER]) {
131 struct tc_netem_reorder ro;
133 nla_memcpy(&ro, tb[TCA_NETEM_REORDER],
sizeof(ro));
134 netem->qnm_ro.nmro_probability = ro.probability;
135 netem->qnm_ro.nmro_correlation = ro.correlation;
137 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
138 SCH_NETEM_ATTR_RO_CORR);
141 if (tb[TCA_NETEM_CORRUPT]) {
142 struct tc_netem_corrupt corrupt;
144 nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT],
sizeof(corrupt));
145 netem->qnm_crpt.nmcr_probability = corrupt.probability;
146 netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
148 netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
149 SCH_NETEM_ATTR_CORRUPT_CORR);
153 netem->qnm_dist.dist_data = NULL;
154 netem->qnm_dist.dist_size = 0;
160static void netem_free_data(
struct rtnl_tc *tc,
void *data)
162 struct rtnl_netem *netem = data;
167 free(netem->qnm_dist.dist_data);
170static void netem_dump_line(
struct rtnl_tc *tc,
void *data,
173 struct rtnl_netem *netem = data;
176 if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0)
177 nl_dump(p,
" limit %dpkts", netem->qnm_limit);
183static void netem_dump_details(
struct rtnl_tc *tc,
void *data,
186 struct rtnl_netem *netem = data;
190 if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) {
192 nl_dump(p,
" latency %s", buf);
194 if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) {
198 if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR && netem->qnm_corr.nmc_delay > 0)
199 nl_dump(p,
" %d", netem->qnm_corr.nmc_delay);
203 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) {
204 nl_dump(p,
" loss %d", netem->qnm_loss);
206 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR && netem->qnm_corr.nmc_loss > 0)
207 nl_dump(p,
" %d", netem->qnm_corr.nmc_loss);
210 if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) {
211 nl_dump(p,
" duplicate %d", netem->qnm_duplicate);
213 if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR && netem->qnm_corr.nmc_duplicate > 0)
214 nl_dump(p,
" %d", netem->qnm_corr.nmc_duplicate);
217 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB && netem->qnm_ro.nmro_probability > 0) {
218 nl_dump(p,
" reorder %d", netem->qnm_ro.nmro_probability);
220 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR && netem->qnm_ro.nmro_correlation > 0)
221 nl_dump(p,
" %d", netem->qnm_ro.nmro_correlation);
223 if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0)
224 nl_dump(p,
" gap %d", netem->qnm_gap);
227 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB && netem->qnm_crpt.nmcr_probability > 0) {
228 nl_dump(p,
" reorder %d", netem->qnm_crpt.nmcr_probability);
230 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR && netem->qnm_crpt.nmcr_correlation > 0)
231 nl_dump(p,
" %d", netem->qnm_crpt.nmcr_correlation);
236static int netem_msg_fill_raw(
struct rtnl_tc *tc,
void *data,
240 struct tc_netem_qopt opts;
241 struct tc_netem_corr cor;
242 struct tc_netem_reorder reorder;
243 struct tc_netem_corrupt corrupt;
244 struct rtnl_netem *netem = data;
246 unsigned char set_correlation = 0, set_reorder = 0;
247 unsigned char set_corrupt = 0, set_dist = 0;
256 memset(&opts, 0,
sizeof(opts));
257 memset(&cor, 0,
sizeof(cor));
258 memset(&reorder, 0,
sizeof(reorder));
259 memset(&corrupt, 0,
sizeof(corrupt));
261 msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
263 if (netem->qnm_ro.nmro_probability != 0) {
264 if (netem->qnm_latency == 0)
265 return -NLE_MISSING_ATTR;
266 if (netem->qnm_gap == 0)
268 }
else if (netem->qnm_gap)
269 return -NLE_MISSING_ATTR;
271 if (netem->qnm_corr.nmc_delay != 0) {
272 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
273 return -NLE_MISSING_ATTR;
277 if (netem->qnm_corr.nmc_loss != 0) {
278 if (netem->qnm_loss == 0)
279 return -NLE_MISSING_ATTR;
283 if (netem->qnm_corr.nmc_duplicate != 0) {
284 if (netem->qnm_duplicate == 0)
285 return -NLE_MISSING_ATTR;
289 if (netem->qnm_ro.nmro_probability != 0)
291 else if (netem->qnm_ro.nmro_correlation != 0)
292 return -NLE_MISSING_ATTR;
294 if (netem->qnm_crpt.nmcr_probability != 0)
296 else if (netem->qnm_crpt.nmcr_correlation != 0)
297 return -NLE_MISSING_ATTR;
299 if (netem->qnm_dist.dist_data && netem->qnm_dist.dist_size) {
300 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
301 return -NLE_MISSING_ATTR;
304 int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
305 sizeof(netem->qnm_dist.dist_data[0]);
306 struct nlmsghdr *new_nlh = realloc(msg->nm_nlh, new_msg_len);
310 msg->nm_nlh = new_nlh;
311 msg->nm_size = new_msg_len;
316 opts.latency = netem->qnm_latency;
317 opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
318 opts.loss = netem->qnm_loss;
319 opts.gap = netem->qnm_gap;
320 opts.duplicate = netem->qnm_duplicate;
321 opts.jitter = netem->qnm_jitter;
323 NLA_PUT(msg, TCA_OPTIONS,
sizeof(opts), &opts);
325 if (set_correlation) {
326 cor.delay_corr = netem->qnm_corr.nmc_delay;
327 cor.loss_corr = netem->qnm_corr.nmc_loss;
328 cor.dup_corr = netem->qnm_corr.nmc_duplicate;
330 NLA_PUT(msg, TCA_NETEM_CORR,
sizeof(cor), &cor);
334 reorder.probability = netem->qnm_ro.nmro_probability;
335 reorder.correlation = netem->qnm_ro.nmro_correlation;
337 NLA_PUT(msg, TCA_NETEM_REORDER,
sizeof(reorder), &reorder);
341 corrupt.probability = netem->qnm_crpt.nmcr_probability;
342 corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
344 NLA_PUT(msg, TCA_NETEM_CORRUPT,
sizeof(corrupt), &corrupt);
348 NLA_PUT(msg, TCA_NETEM_DELAY_DIST,
349 netem->qnm_dist.dist_size *
sizeof(netem->qnm_dist.dist_data[0]),
350 netem->qnm_dist.dist_data);
357 head = (
struct nlattr *)(((
char *) NLMSG_DATA(msg->nm_nlh)) +
358 NLMSG_LENGTH(
sizeof(
struct tcmsg)) - NLMSG_ALIGNTO);
360 tail = (
struct nlattr *)(((
char *) (msg->nm_nlh)) +
361 NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
363 old_len = head->nla_len;
364 head->nla_len = (
char *)tail - (
char *)head;
365 msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
385 struct rtnl_netem *netem;
390 netem->qnm_limit = limit;
391 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
401 struct rtnl_netem *netem;
406 if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)
407 return netem->qnm_limit;
427 struct rtnl_netem *netem;
432 netem->qnm_gap = gap;
433 netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
443 struct rtnl_netem *netem;
448 if (netem->qnm_mask & SCH_NETEM_ATTR_GAP)
449 return netem->qnm_gap;
462 struct rtnl_netem *netem;
467 netem->qnm_ro.nmro_probability = prob;
468 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
478 struct rtnl_netem *netem;
483 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)
484 return netem->qnm_ro.nmro_probability;
497 struct rtnl_netem *netem;
502 netem->qnm_ro.nmro_correlation = prob;
503 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
513 struct rtnl_netem *netem;
518 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)
519 return netem->qnm_ro.nmro_correlation;
539 struct rtnl_netem *netem;
544 netem->qnm_crpt.nmcr_probability = prob;
545 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
555 struct rtnl_netem *netem;
560 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB)
561 return netem->qnm_crpt.nmcr_probability;
574 struct rtnl_netem *netem;
579 netem->qnm_crpt.nmcr_correlation = prob;
580 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
590 struct rtnl_netem *netem;
595 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)
596 return netem->qnm_crpt.nmcr_correlation;
616 struct rtnl_netem *netem;
621 netem->qnm_loss = prob;
622 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
632 struct rtnl_netem *netem;
637 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)
638 return netem->qnm_loss;
651 struct rtnl_netem *netem;
656 netem->qnm_corr.nmc_loss = prob;
657 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
667 struct rtnl_netem *netem;
672 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)
673 return netem->qnm_corr.nmc_loss;
693 struct rtnl_netem *netem;
698 netem->qnm_duplicate = prob;
699 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
709 struct rtnl_netem *netem;
714 if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)
715 return netem->qnm_duplicate;
728 struct rtnl_netem *netem;
733 netem->qnm_corr.nmc_duplicate = prob;
734 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
744 struct rtnl_netem *netem;
749 if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)
750 return netem->qnm_corr.nmc_duplicate;
770 struct rtnl_netem *netem;
776 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
786 struct rtnl_netem *netem;
791 if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)
805 struct rtnl_netem *netem;
811 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
821 struct rtnl_netem *netem;
826 if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)
839 struct rtnl_netem *netem;
844 netem->qnm_corr.nmc_delay = prob;
845 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
855 struct rtnl_netem *netem;
860 if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)
861 return netem->qnm_corr.nmc_delay;
873 struct rtnl_netem *netem;
878 if (netem->qnm_mask & SCH_NETEM_ATTR_DIST)
879 return netem->qnm_dist.dist_size;
892 struct rtnl_netem *netem;
897 if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) {
898 *dist_ptr = netem->qnm_dist.dist_data;
910 struct rtnl_netem *netem;
919 new_data = (int16_t *) calloc(len,
sizeof(int16_t));
923 free (netem->qnm_dist.dist_data);
924 netem->qnm_dist.dist_data = new_data;
926 memcpy(netem->qnm_dist.dist_data, data, len *
sizeof(int16_t));
928 netem->qnm_dist.dist_size = len;
929 netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
945 _nl_auto_free
char *line = NULL;
947 char dist_suffix[] =
".dist";
948 _nl_auto_free int16_t *data = NULL;
952 char *test_path[] = {
957 "/usr/local/lib/tc/",
961 test_suffix = strstr(dist_type, dist_suffix);
962 if (test_suffix != NULL && strlen(test_suffix) == 5)
963 strcpy(dist_suffix,
"");
965 for (i = 0; i < ARRAY_SIZE(test_path); i++) {
966 snprintf(name, NAME_MAX,
"%s%s%s", test_path[i], dist_type, dist_suffix);
967 if ((f = fopen(name,
"re")))
972 return -nl_syserr2nlerr(errno);
974 data = (int16_t *) calloc(MAXDIST,
sizeof(int16_t));
975 line = (
char *) calloc(len + 1,
sizeof(
char));
976 if (!data || !line) {
981 while (getline(&line, &len, f) != -1) {
984 if (*line ==
'\n' || *line ==
'#')
987 for (p = line; ; p = endp) {
988 long x = strtol(p, &endp, 0);
989 if (endp == p)
break;
1006static struct rtnl_tc_ops netem_ops = {
1008 .to_type = RTNL_TC_TYPE_QDISC,
1009 .to_size =
sizeof(
struct rtnl_netem),
1010 .to_msg_parser = netem_msg_parser,
1011 .to_free_data = netem_free_data,
1014 .to_msg_fill_raw = netem_msg_fill_raw,
1017static void _nl_init netem_init(
void)
1022static void _nl_exit netem_exit(
void)
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, const struct nla_policy *policy)
Create attribute index based on a stream of attributes.
#define NLA_PUT(msg, attrtype, attrlen, data)
Add unspecific attribute to netlink message.
int nla_memcpy(void *dest, const struct nlattr *src, int count)
Copy attribute payload to another memory area.
void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication correlation probability of netem qdisc.
int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
Get packet duplication probability of netem qdisc.
int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
Get a pointer to the distribution table.
int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
Get re-ordering correlation probability of netem qdisc.
void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet delay correlation probability of netem qdisc.
int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
Get packet delay correlation probability of netem qdisc.
int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
Get packet loss probability of netem qdisc.
int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
Get the size of the distribution table.
int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
Get re-ordering gap of netem qdisc.
int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
Get re-ordering probability of netem qdisc.
int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
Get packet duplication correlation probability of netem qdisc.
void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
Set packet delay jitter of netem qdisc.
int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
Get corruption correlation probability of netem qdisc.
void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
Set packet loss probability of netem qdisc.
void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
Set limit of netem qdisc.
void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
Set re-order correlation probability of netem qdisc.
int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
Get corruption probability of netem qdisc.
int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
Get packet delay jitter of netem qdisc.
void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
Set corruption correlation probability of netem qdisc.
int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
Get packet delay of netem qdisc.
void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
Set re-ordering probability of netem qdisc.
void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
Set re-ordering gap of netem qdisc.
void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
Set corruption probability of netem qdisc.
void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication probability of netem qdisc.
void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet loss correlation probability of netem qdisc.
int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type)
Load the delay distribution from a file.
void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
Set packet delay of netem qdisc.
int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
Get packet loss correlation probability of netem qdisc.
int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len)
Set the delay distribution data.
int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
Get limit of netem qdisc.
#define TC_CAST(ptr)
Macro to cast qdisc/class/classifier to tc object.
void * rtnl_tc_data(struct rtnl_tc *)
Return pointer to private data of traffic control object.
int rtnl_tc_register(struct rtnl_tc_ops *)
Register a traffic control module.
void rtnl_tc_unregister(struct rtnl_tc_ops *)
Unregister a traffic control module.
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
uint32_t nl_ticks2us(uint32_t ticks)
Convert ticks to micro seconds.
uint32_t nl_us2ticks(uint32_t us)
Convert micro seconds to ticks.
char * nl_msec2str(uint64_t msec, char *buf, size_t len)
Convert milliseconds to a character string.
@ NL_DUMP_LINE
Dump object briefly on one line.
@ NL_DUMP_DETAILS
Dump all attributes but no statistics.
Attribute validation policy.
uint16_t minlen
Minimal length of payload required.
uint16_t type
Type of attribute or NLA_UNSPEC.