21 #include <netlink-private/netlink.h> 22 #include <netlink-private/tc.h> 23 #include <netlink/netlink.h> 24 #include <netlink/utils.h> 25 #include <netlink-private/route/tc-api.h> 26 #include <netlink/route/qdisc.h> 27 #include <netlink/route/qdisc/netem.h> 30 #define SCH_NETEM_ATTR_LATENCY 0x0001 31 #define SCH_NETEM_ATTR_LIMIT 0x0002 32 #define SCH_NETEM_ATTR_LOSS 0x0004 33 #define SCH_NETEM_ATTR_GAP 0x0008 34 #define SCH_NETEM_ATTR_DUPLICATE 0x0010 35 #define SCH_NETEM_ATTR_JITTER 0x0020 36 #define SCH_NETEM_ATTR_DELAY_CORR 0x0040 37 #define SCH_NETEM_ATTR_LOSS_CORR 0x0080 38 #define SCH_NETEM_ATTR_DUP_CORR 0x0100 39 #define SCH_NETEM_ATTR_RO_PROB 0x0200 40 #define SCH_NETEM_ATTR_RO_CORR 0x0400 41 #define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800 42 #define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000 43 #define SCH_NETEM_ATTR_DIST 0x2000 46 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
47 [TCA_NETEM_CORR] = { .
minlen =
sizeof(
struct tc_netem_corr) },
48 [TCA_NETEM_REORDER] = { .minlen =
sizeof(
struct tc_netem_reorder) },
49 [TCA_NETEM_CORRUPT] = { .minlen =
sizeof(
struct tc_netem_corrupt) },
52 static int netem_msg_parser(
struct rtnl_tc *tc,
void *data)
54 struct rtnl_netem *netem = data;
55 struct tc_netem_qopt *opts;
58 if (tc->tc_opts->d_size <
sizeof(*opts))
61 opts = (
struct tc_netem_qopt *) tc->tc_opts->d_data;
62 netem->qnm_latency = opts->latency;
63 netem->qnm_limit = opts->limit;
64 netem->qnm_loss = opts->loss;
65 netem->qnm_gap = opts->gap;
66 netem->qnm_duplicate = opts->duplicate;
67 netem->qnm_jitter = opts->jitter;
69 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT |
70 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
71 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
73 len = tc->tc_opts->d_size -
sizeof(*opts);
76 struct nlattr *tb[TCA_NETEM_MAX+1];
78 err =
nla_parse(tb, TCA_NETEM_MAX, (
struct nlattr *)
79 (tc->tc_opts->d_data +
sizeof(*opts)),
86 if (tb[TCA_NETEM_CORR]) {
87 struct tc_netem_corr cor;
89 nla_memcpy(&cor, tb[TCA_NETEM_CORR],
sizeof(cor));
90 netem->qnm_corr.nmc_delay = cor.delay_corr;
91 netem->qnm_corr.nmc_loss = cor.loss_corr;
92 netem->qnm_corr.nmc_duplicate = cor.dup_corr;
94 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
95 SCH_NETEM_ATTR_LOSS_CORR |
96 SCH_NETEM_ATTR_DUP_CORR);
99 if (tb[TCA_NETEM_REORDER]) {
100 struct tc_netem_reorder ro;
102 nla_memcpy(&ro, tb[TCA_NETEM_REORDER],
sizeof(ro));
103 netem->qnm_ro.nmro_probability = ro.probability;
104 netem->qnm_ro.nmro_correlation = ro.correlation;
106 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
107 SCH_NETEM_ATTR_RO_CORR);
110 if (tb[TCA_NETEM_CORRUPT]) {
111 struct tc_netem_corrupt corrupt;
113 nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT],
sizeof(corrupt));
114 netem->qnm_crpt.nmcr_probability = corrupt.probability;
115 netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
117 netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
118 SCH_NETEM_ATTR_CORRUPT_CORR);
122 netem->qnm_dist.dist_data = NULL;
123 netem->qnm_dist.dist_size = 0;
129 static void netem_free_data(
struct rtnl_tc *tc,
void *data)
131 struct rtnl_netem *netem = data;
136 free(netem->qnm_dist.dist_data);
139 static void netem_dump_line(
struct rtnl_tc *tc,
void *data,
142 struct rtnl_netem *netem = data;
145 if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0)
146 nl_dump(p,
" limit %dpkts", netem->qnm_limit);
152 static void netem_dump_details(
struct rtnl_tc *tc,
void *data,
155 struct rtnl_netem *netem = data;
159 if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) {
161 nl_dump(p,
" latency %s", buf);
163 if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) {
167 if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR && netem->qnm_corr.nmc_delay > 0)
168 nl_dump(p,
" %d%", netem->qnm_corr.nmc_delay);
172 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) {
173 nl_dump(p,
" loss %d%", netem->qnm_loss);
175 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR && netem->qnm_corr.nmc_loss > 0)
176 nl_dump(p,
" %d%", netem->qnm_corr.nmc_loss);
179 if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) {
180 nl_dump(p,
" duplicate %d%", netem->qnm_duplicate);
182 if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR && netem->qnm_corr.nmc_duplicate > 0)
183 nl_dump(p,
" %d%", netem->qnm_corr.nmc_duplicate);
186 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB && netem->qnm_ro.nmro_probability > 0) {
187 nl_dump(p,
" reorder %d%", netem->qnm_ro.nmro_probability);
189 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR && netem->qnm_ro.nmro_correlation > 0)
190 nl_dump(p,
" %d%", netem->qnm_ro.nmro_correlation);
192 if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0)
193 nl_dump(p,
" gap %d", netem->qnm_gap);
196 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB && netem->qnm_crpt.nmcr_probability > 0) {
197 nl_dump(p,
" reorder %d%", netem->qnm_crpt.nmcr_probability);
199 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR && netem->qnm_crpt.nmcr_correlation > 0)
200 nl_dump(p,
" %d%", netem->qnm_crpt.nmcr_correlation);
205 static int netem_msg_fill_raw(
struct rtnl_tc *tc,
void *data,
209 struct tc_netem_qopt opts;
210 struct tc_netem_corr cor;
211 struct tc_netem_reorder reorder;
212 struct tc_netem_corrupt corrupt;
213 struct rtnl_netem *netem = data;
215 unsigned char set_correlation = 0, set_reorder = 0,
216 set_corrupt = 0, set_dist = 0;
221 memset(&opts, 0,
sizeof(opts));
222 memset(&cor, 0,
sizeof(cor));
223 memset(&reorder, 0,
sizeof(reorder));
224 memset(&corrupt, 0,
sizeof(corrupt));
226 msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
228 if ( netem->qnm_ro.nmro_probability != 0 ) {
229 if (netem->qnm_latency == 0) {
230 return -NLE_MISSING_ATTR;
232 if (netem->qnm_gap == 0) netem->qnm_gap = 1;
234 else if ( netem->qnm_gap ) {
235 return -NLE_MISSING_ATTR;
238 if ( netem->qnm_corr.nmc_delay != 0 ) {
239 if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
240 return -NLE_MISSING_ATTR;
245 if ( netem->qnm_corr.nmc_loss != 0 ) {
246 if ( netem->qnm_loss == 0 ) {
247 return -NLE_MISSING_ATTR;
252 if ( netem->qnm_corr.nmc_duplicate != 0 ) {
253 if ( netem->qnm_duplicate == 0 ) {
254 return -NLE_MISSING_ATTR;
259 if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1;
260 else if ( netem->qnm_ro.nmro_correlation != 0 ) {
261 return -NLE_MISSING_ATTR;
264 if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1;
265 else if ( netem->qnm_crpt.nmcr_correlation != 0 ) {
266 return -NLE_MISSING_ATTR;
269 if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) {
270 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
271 return -NLE_MISSING_ATTR;
275 int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
276 sizeof(netem->qnm_dist.dist_data[0]);
278 msg->nm_nlh = (
struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len);
279 if ( msg->nm_nlh == NULL )
281 msg->nm_size = new_msg_len;
286 opts.latency = netem->qnm_latency;
287 opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
288 opts.loss = netem->qnm_loss;
289 opts.gap = netem->qnm_gap;
290 opts.duplicate = netem->qnm_duplicate;
291 opts.jitter = netem->qnm_jitter;
293 NLA_PUT(msg, TCA_OPTIONS,
sizeof(opts), &opts);
295 if ( set_correlation ) {
296 cor.delay_corr = netem->qnm_corr.nmc_delay;
297 cor.loss_corr = netem->qnm_corr.nmc_loss;
298 cor.dup_corr = netem->qnm_corr.nmc_duplicate;
300 NLA_PUT(msg, TCA_NETEM_CORR,
sizeof(cor), &cor);
304 reorder.probability = netem->qnm_ro.nmro_probability;
305 reorder.correlation = netem->qnm_ro.nmro_correlation;
307 NLA_PUT(msg, TCA_NETEM_REORDER,
sizeof(reorder), &reorder);
311 corrupt.probability = netem->qnm_crpt.nmcr_probability;
312 corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
314 NLA_PUT(msg, TCA_NETEM_CORRUPT,
sizeof(corrupt), &corrupt);
318 NLA_PUT(msg, TCA_NETEM_DELAY_DIST,
319 netem->qnm_dist.dist_size *
sizeof(netem->qnm_dist.dist_data[0]),
320 netem->qnm_dist.dist_data);
327 struct nlattr* head = (
struct nlattr *)(NLMSG_DATA(msg->nm_nlh) +
328 NLMSG_LENGTH(
sizeof(
struct tcmsg)) - NLMSG_ALIGNTO);
330 struct nlattr* tail = (
struct nlattr *)(((
void *) (msg->nm_nlh)) +
331 NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
333 int old_len = head->nla_len;
334 head->nla_len = (
void *)tail - (
void *)head;
335 msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
355 struct rtnl_netem *netem;
360 netem->qnm_limit = limit;
361 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
371 struct rtnl_netem *netem;
376 if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)
377 return netem->qnm_limit;
397 struct rtnl_netem *netem;
402 netem->qnm_gap = gap;
403 netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
413 struct rtnl_netem *netem;
418 if (netem->qnm_mask & SCH_NETEM_ATTR_GAP)
419 return netem->qnm_gap;
432 struct rtnl_netem *netem;
437 netem->qnm_ro.nmro_probability = prob;
438 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
448 struct rtnl_netem *netem;
453 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)
454 return netem->qnm_ro.nmro_probability;
467 struct rtnl_netem *netem;
472 netem->qnm_ro.nmro_correlation = prob;
473 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
483 struct rtnl_netem *netem;
488 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)
489 return netem->qnm_ro.nmro_correlation;
509 struct rtnl_netem *netem;
514 netem->qnm_crpt.nmcr_probability = prob;
515 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
525 struct rtnl_netem *netem;
530 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB)
531 return netem->qnm_crpt.nmcr_probability;
544 struct rtnl_netem *netem;
549 netem->qnm_crpt.nmcr_correlation = prob;
550 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
560 struct rtnl_netem *netem;
565 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)
566 return netem->qnm_crpt.nmcr_correlation;
586 struct rtnl_netem *netem;
591 netem->qnm_loss = prob;
592 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
602 struct rtnl_netem *netem;
607 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)
608 return netem->qnm_loss;
621 struct rtnl_netem *netem;
626 netem->qnm_corr.nmc_loss = prob;
627 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
637 struct rtnl_netem *netem;
642 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)
643 return netem->qnm_corr.nmc_loss;
663 struct rtnl_netem *netem;
668 netem->qnm_duplicate = prob;
669 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
679 struct rtnl_netem *netem;
684 if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)
685 return netem->qnm_duplicate;
698 struct rtnl_netem *netem;
703 netem->qnm_corr.nmc_duplicate = prob;
704 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
714 struct rtnl_netem *netem;
719 if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)
720 return netem->qnm_corr.nmc_duplicate;
740 struct rtnl_netem *netem;
746 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
756 struct rtnl_netem *netem;
761 if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)
775 struct rtnl_netem *netem;
781 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
791 struct rtnl_netem *netem;
796 if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)
809 struct rtnl_netem *netem;
814 netem->qnm_corr.nmc_delay = prob;
815 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
825 struct rtnl_netem *netem;
830 if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)
831 return netem->qnm_corr.nmc_delay;
843 struct rtnl_netem *netem;
848 if (netem->qnm_mask & SCH_NETEM_ATTR_DIST)
849 return netem->qnm_dist.dist_size;
862 struct rtnl_netem *netem;
867 if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) {
868 *dist_ptr = netem->qnm_dist.dist_data;
881 struct rtnl_netem *netem;
892 char dist_suffix[] =
".dist";
895 char *test_suffix = strstr(dist_type, dist_suffix);
896 if (test_suffix != NULL && strlen(test_suffix) == 5)
897 strcpy(dist_suffix,
"");
900 char *test_path[] = {
"",
"./",
"/usr/lib/tc/",
"/usr/local/lib/tc/" };
902 for (i = 0; i < ARRAY_SIZE(test_path); i++) {
903 snprintf(name, NAME_MAX,
"%s%s%s", test_path[i], dist_type, dist_suffix);
904 if ((f = fopen(name,
"re")))
909 return -nl_syserr2nlerr(errno);
911 netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST,
sizeof(int16_t));
913 line = (
char *) calloc (
sizeof(
char), len + 1);
915 while (getline(&line, &len, f) != -1) {
918 if (*line ==
'\n' || *line ==
'#')
921 for (p = line; ; p = endp) {
922 long x = strtol(p, &endp, 0);
923 if (endp == p)
break;
930 netem->qnm_dist.dist_data[n++] = x;
936 netem->qnm_dist.dist_size = n;
937 netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
945 static struct rtnl_tc_ops netem_ops = {
947 .to_type = RTNL_TC_TYPE_QDISC,
948 .to_size =
sizeof(
struct rtnl_netem),
949 .to_msg_parser = netem_msg_parser,
950 .to_free_data = netem_free_data,
953 .to_msg_fill_raw = netem_msg_fill_raw,
956 static void __init netem_init(
void)
961 static void __exit netem_exit(
void)
Dump object briefly on one line.
int rtnl_tc_register(struct rtnl_tc_ops *ops)
Register a traffic control module.
int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
Get re-ordering gap of netem qdisc.
void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
Set packet delay of netem qdisc.
Attribute validation policy.
void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
Set limit of netem qdisc.
int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
Get packet loss probability of netem qdisc.
int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
Get corruption probability of netem qdisc.
int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
Get packet delay of netem qdisc.
char * nl_msec2str(uint64_t msec, char *buf, size_t len)
Convert milliseconds to a character string.
int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
Get re-ordering correlation probability of netem qdisc.
void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
Set re-ordering probability of netem qdisc.
int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
Get packet duplication correlation probability of netem qdisc.
Dump all attributes but no statistics.
void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
Set corruption correlation probability of netem qdisc.
int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
Get re-ordering probability of netem qdisc.
int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
Get packet delay jitter of netem qdisc.
int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
Get packet duplication probability of netem qdisc.
void rtnl_tc_unregister(struct rtnl_tc_ops *ops)
Unregister a traffic control module.
void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication correlation probability of netem qdisc.
int nla_memcpy(void *dest, const struct nlattr *src, int count)
Copy attribute payload to another memory area.
void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
Set re-order correlation probability of netem qdisc.
#define TC_CAST(ptr)
Macro to cast qdisc/class/classifier to tc object.
#define NLA_PUT(msg, attrtype, attrlen, data)
Add unspecific attribute to netlink message.
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_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
Get the size of the distribution table.
int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
Get packet loss correlation probability of netem qdisc.
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, struct nla_policy *policy)
Create attribute index based on a stream of attributes.
uint32_t nl_ticks2us(uint32_t ticks)
Convert ticks to micro seconds.
int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
Get limit of netem qdisc.
uint16_t minlen
Minimal length of payload required.
void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
Set re-ordering gap of netem qdisc.
void * rtnl_tc_data(struct rtnl_tc *tc)
Return pointer to private data of traffic control object.
void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet delay correlation probability of netem qdisc.
uint32_t nl_us2ticks(uint32_t us)
Convert micro seconds to ticks.
int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
Get packet delay correlation probability of netem qdisc.
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
Get corruption correlation probability of netem qdisc.
void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
Set corruption probability of netem qdisc.
int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type)
Set the delay distribution.
int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
Get a pointer to the distribution table.
void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
Set packet delay jitter of netem qdisc.
void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
Set packet loss probability of netem qdisc.