libnl  3.2.27
netem.c
1 /*
2  * lib/route/qdisc/netem.c Network Emulator Qdisc
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation version 2.1
7  * of the License.
8  *
9  * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
10  */
11 
12 /**
13  * @ingroup qdisc
14  * @defgroup qdisc_netem Network Emulator
15  * @brief
16  *
17  * For further documentation see http://linux-net.osdl.org/index.php/Netem
18  * @{
19  */
20 
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>
28 
29 /** @cond SKIP */
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
44 /** @endcond */
45 
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) },
50 };
51 
52 static int netem_msg_parser(struct rtnl_tc *tc, void *data)
53 {
54  struct rtnl_netem *netem = data;
55  struct tc_netem_qopt *opts;
56  int len, err = 0;
57 
58  if (tc->tc_opts->d_size < sizeof(*opts))
59  return -NLE_INVAL;
60 
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;
68 
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);
72 
73  len = tc->tc_opts->d_size - sizeof(*opts);
74 
75  if (len > 0) {
76  struct nlattr *tb[TCA_NETEM_MAX+1];
77 
78  err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
79  (tc->tc_opts->d_data + sizeof(*opts)),
80  len, netem_policy);
81  if (err < 0) {
82  free(netem);
83  return err;
84  }
85 
86  if (tb[TCA_NETEM_CORR]) {
87  struct tc_netem_corr cor;
88 
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;
93 
94  netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
95  SCH_NETEM_ATTR_LOSS_CORR |
96  SCH_NETEM_ATTR_DUP_CORR);
97  }
98 
99  if (tb[TCA_NETEM_REORDER]) {
100  struct tc_netem_reorder ro;
101 
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;
105 
106  netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
107  SCH_NETEM_ATTR_RO_CORR);
108  }
109 
110  if (tb[TCA_NETEM_CORRUPT]) {
111  struct tc_netem_corrupt corrupt;
112 
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;
116 
117  netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
118  SCH_NETEM_ATTR_CORRUPT_CORR);
119  }
120 
121  /* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */
122  netem->qnm_dist.dist_data = NULL;
123  netem->qnm_dist.dist_size = 0;
124  }
125 
126  return 0;
127 }
128 
129 static void netem_free_data(struct rtnl_tc *tc, void *data)
130 {
131  struct rtnl_netem *netem = data;
132 
133  if (!netem)
134  return;
135 
136  free(netem->qnm_dist.dist_data);
137 }
138 
139 static void netem_dump_line(struct rtnl_tc *tc, void *data,
140  struct nl_dump_params *p)
141 {
142  struct rtnl_netem *netem = data;
143 
144  if (netem) {
145  if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0)
146  nl_dump(p, " limit %dpkts", netem->qnm_limit);
147  else
148  nl_dump(p, " no limit");
149  }
150 }
151 
152 static void netem_dump_details(struct rtnl_tc *tc, void *data,
153  struct nl_dump_params *p)
154 {
155  struct rtnl_netem *netem = data;
156  char buf[32];
157 
158  if (netem) {
159  if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) {
160  nl_msec2str(nl_ticks2us(netem->qnm_latency) / 1000, buf, sizeof(buf));
161  nl_dump(p, " latency %s", buf);
162 
163  if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) {
164  nl_msec2str(nl_ticks2us(netem->qnm_jitter) / 1000, buf, sizeof(buf));
165  nl_dump(p, " jitter %s", buf);
166 
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);
169  }
170  }
171 
172  if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) {
173  nl_dump(p, " loss %d%", netem->qnm_loss);
174 
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);
177  }
178 
179  if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) {
180  nl_dump(p, " duplicate %d%", netem->qnm_duplicate);
181 
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);
184  }
185 
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);
188 
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);
191 
192  if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0)
193  nl_dump(p, " gap %d", netem->qnm_gap);
194  }
195 
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);
198 
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);
201  }
202  }
203 }
204 
205 static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data,
206  struct nl_msg *msg)
207 {
208  int err = 0;
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;
214 
215  unsigned char set_correlation = 0, set_reorder = 0,
216  set_corrupt = 0, set_dist = 0;
217 
218  if (!netem)
219  BUG();
220 
221  memset(&opts, 0, sizeof(opts));
222  memset(&cor, 0, sizeof(cor));
223  memset(&reorder, 0, sizeof(reorder));
224  memset(&corrupt, 0, sizeof(corrupt));
225 
226  msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
227 
228  if ( netem->qnm_ro.nmro_probability != 0 ) {
229  if (netem->qnm_latency == 0) {
230  return -NLE_MISSING_ATTR;
231  }
232  if (netem->qnm_gap == 0) netem->qnm_gap = 1;
233  }
234  else if ( netem->qnm_gap ) {
235  return -NLE_MISSING_ATTR;
236  }
237 
238  if ( netem->qnm_corr.nmc_delay != 0 ) {
239  if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
240  return -NLE_MISSING_ATTR;
241  }
242  set_correlation = 1;
243  }
244 
245  if ( netem->qnm_corr.nmc_loss != 0 ) {
246  if ( netem->qnm_loss == 0 ) {
247  return -NLE_MISSING_ATTR;
248  }
249  set_correlation = 1;
250  }
251 
252  if ( netem->qnm_corr.nmc_duplicate != 0 ) {
253  if ( netem->qnm_duplicate == 0 ) {
254  return -NLE_MISSING_ATTR;
255  }
256  set_correlation = 1;
257  }
258 
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;
262  }
263 
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;
267  }
268 
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;
272  }
273  else {
274  /* Resize to accomodate the large distribution table */
275  int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
276  sizeof(netem->qnm_dist.dist_data[0]);
277 
278  msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len);
279  if ( msg->nm_nlh == NULL )
280  return -NLE_NOMEM;
281  msg->nm_size = new_msg_len;
282  set_dist = 1;
283  }
284  }
285 
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;
292 
293  NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts);
294 
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;
299 
300  NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor);
301  }
302 
303  if ( set_reorder ) {
304  reorder.probability = netem->qnm_ro.nmro_probability;
305  reorder.correlation = netem->qnm_ro.nmro_correlation;
306 
307  NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
308  }
309 
310  if ( set_corrupt ) {
311  corrupt.probability = netem->qnm_crpt.nmcr_probability;
312  corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
313 
314  NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
315  }
316 
317  if ( set_dist ) {
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);
321  }
322 
323  /* Length specified in the TCA_OPTIONS section must span the entire
324  * remainder of the message. That's just the way that sch_netem expects it.
325  * Maybe there's a more succinct way to do this at a higher level.
326  */
327  struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) +
328  NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
329 
330  struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) +
331  NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
332 
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);
336 
337  return err;
338 nla_put_failure:
339  return -NLE_MSGSIZE;
340 }
341 
342 /**
343  * @name Queue Limit
344  * @{
345  */
346 
347 /**
348  * Set limit of netem qdisc.
349  * @arg qdisc Netem qdisc to be modified.
350  * @arg limit New limit in bytes.
351  * @return 0 on success or a negative error code.
352  */
353 void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
354 {
355  struct rtnl_netem *netem;
356 
357  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
358  BUG();
359 
360  netem->qnm_limit = limit;
361  netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
362 }
363 
364 /**
365  * Get limit of netem qdisc.
366  * @arg qdisc Netem qdisc.
367  * @return Limit in bytes or a negative error code.
368  */
369 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
370 {
371  struct rtnl_netem *netem;
372 
373  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
374  return -NLE_NOMEM;
375 
376  if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)
377  return netem->qnm_limit;
378  else
379  return -NLE_NOATTR;
380 }
381 
382 /** @} */
383 
384 /**
385  * @name Packet Re-ordering
386  * @{
387  */
388 
389 /**
390  * Set re-ordering gap of netem qdisc.
391  * @arg qdisc Netem qdisc to be modified.
392  * @arg gap New gap in number of packets.
393  * @return 0 on success or a negative error code.
394  */
395 void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
396 {
397  struct rtnl_netem *netem;
398 
399  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
400  BUG();
401 
402  netem->qnm_gap = gap;
403  netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
404 }
405 
406 /**
407  * Get re-ordering gap of netem qdisc.
408  * @arg qdisc Netem qdisc.
409  * @return Re-ordering gap in packets or a negative error code.
410  */
411 int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
412 {
413  struct rtnl_netem *netem;
414 
415  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
416  return -NLE_NOMEM;
417 
418  if (netem->qnm_mask & SCH_NETEM_ATTR_GAP)
419  return netem->qnm_gap;
420  else
421  return -NLE_NOATTR;
422 }
423 
424 /**
425  * Set re-ordering probability of netem qdisc.
426  * @arg qdisc Netem qdisc to be modified.
427  * @arg prob New re-ordering probability.
428  * @return 0 on success or a negative error code.
429  */
430 void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
431 {
432  struct rtnl_netem *netem;
433 
434  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
435  BUG();
436 
437  netem->qnm_ro.nmro_probability = prob;
438  netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
439 }
440 
441 /**
442  * Get re-ordering probability of netem qdisc.
443  * @arg qdisc Netem qdisc.
444  * @return Re-ordering probability or a negative error code.
445  */
446 int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
447 {
448  struct rtnl_netem *netem;
449 
450  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
451  return -NLE_NOMEM;
452 
453  if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)
454  return netem->qnm_ro.nmro_probability;
455  else
456  return -NLE_NOATTR;
457 }
458 
459 /**
460  * Set re-order correlation probability of netem qdisc.
461  * @arg qdisc Netem qdisc to be modified.
462  * @arg prob New re-ordering correlation probability.
463  * @return 0 on success or a negative error code.
464  */
465 void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
466 {
467  struct rtnl_netem *netem;
468 
469  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
470  BUG();
471 
472  netem->qnm_ro.nmro_correlation = prob;
473  netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
474 }
475 
476 /**
477  * Get re-ordering correlation probability of netem qdisc.
478  * @arg qdisc Netem qdisc.
479  * @return Re-ordering correlation probability or a negative error code.
480  */
481 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
482 {
483  struct rtnl_netem *netem;
484 
485  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
486  return -NLE_NOMEM;
487 
488  if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)
489  return netem->qnm_ro.nmro_correlation;
490  else
491  return -NLE_NOATTR;
492 }
493 
494 /** @} */
495 
496 /**
497  * @name Corruption
498  * @{
499  */
500 
501 /**
502  * Set corruption probability of netem qdisc.
503  * @arg qdisc Netem qdisc to be modified.
504  * @arg prob New corruption probability.
505  * @return 0 on success or a negative error code.
506  */
507 void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
508 {
509  struct rtnl_netem *netem;
510 
511  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
512  BUG();
513 
514  netem->qnm_crpt.nmcr_probability = prob;
515  netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
516 }
517 
518 /**
519  * Get corruption probability of netem qdisc.
520  * @arg qdisc Netem qdisc.
521  * @return Corruption probability or a negative error code.
522  */
523 int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
524 {
525  struct rtnl_netem *netem;
526 
527  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
528  BUG();
529 
530  if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB)
531  return netem->qnm_crpt.nmcr_probability;
532  else
533  return -NLE_NOATTR;
534 }
535 
536 /**
537  * Set corruption correlation probability of netem qdisc.
538  * @arg qdisc Netem qdisc to be modified.
539  * @arg prob New corruption correlation probability.
540  * @return 0 on success or a negative error code.
541  */
542 void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
543 {
544  struct rtnl_netem *netem;
545 
546  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
547  BUG();
548 
549  netem->qnm_crpt.nmcr_correlation = prob;
550  netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
551 }
552 
553 /**
554  * Get corruption correlation probability of netem qdisc.
555  * @arg qdisc Netem qdisc.
556  * @return Corruption correlation probability or a negative error code.
557  */
558 int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
559 {
560  struct rtnl_netem *netem;
561 
562  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
563  BUG();
564 
565  if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)
566  return netem->qnm_crpt.nmcr_correlation;
567  else
568  return -NLE_NOATTR;
569 }
570 
571 /** @} */
572 
573 /**
574  * @name Packet Loss
575  * @{
576  */
577 
578 /**
579  * Set packet loss probability of netem qdisc.
580  * @arg qdisc Netem qdisc to be modified.
581  * @arg prob New packet loss probability.
582  * @return 0 on success or a negative error code.
583  */
584 void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
585 {
586  struct rtnl_netem *netem;
587 
588  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
589  BUG();
590 
591  netem->qnm_loss = prob;
592  netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
593 }
594 
595 /**
596  * Get packet loss probability of netem qdisc.
597  * @arg qdisc Netem qdisc.
598  * @return Packet loss probability or a negative error code.
599  */
600 int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
601 {
602  struct rtnl_netem *netem;
603 
604  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
605  BUG();
606 
607  if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)
608  return netem->qnm_loss;
609  else
610  return -NLE_NOATTR;
611 }
612 
613 /**
614  * Set packet loss correlation probability of netem qdisc.
615  * @arg qdisc Netem qdisc to be modified.
616  * @arg prob New packet loss correlation.
617  * @return 0 on success or a negative error code.
618  */
619 void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
620 {
621  struct rtnl_netem *netem;
622 
623  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
624  BUG();
625 
626  netem->qnm_corr.nmc_loss = prob;
627  netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
628 }
629 
630 /**
631  * Get packet loss correlation probability of netem qdisc.
632  * @arg qdisc Netem qdisc.
633  * @return Packet loss correlation probability or a negative error code.
634  */
635 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
636 {
637  struct rtnl_netem *netem;
638 
639  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
640  BUG();
641 
642  if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)
643  return netem->qnm_corr.nmc_loss;
644  else
645  return -NLE_NOATTR;
646 }
647 
648 /** @} */
649 
650 /**
651  * @name Packet Duplication
652  * @{
653  */
654 
655 /**
656  * Set packet duplication probability of netem qdisc.
657  * @arg qdisc Netem qdisc to be modified.
658  * @arg prob New packet duplication probability.
659  * @return 0 on success or a negative error code.
660  */
661 void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
662 {
663  struct rtnl_netem *netem;
664 
665  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
666  BUG();
667 
668  netem->qnm_duplicate = prob;
669  netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
670 }
671 
672 /**
673  * Get packet duplication probability of netem qdisc.
674  * @arg qdisc Netem qdisc.
675  * @return Packet duplication probability or a negative error code.
676  */
677 int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
678 {
679  struct rtnl_netem *netem;
680 
681  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
682  BUG();
683 
684  if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)
685  return netem->qnm_duplicate;
686  else
687  return -NLE_NOATTR;
688 }
689 
690 /**
691  * Set packet duplication correlation probability of netem qdisc.
692  * @arg qdisc Netem qdisc to be modified.
693  * @arg prob New packet duplication correlation probability.
694  * @return 0 on sucess or a negative error code.
695  */
696 void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
697 {
698  struct rtnl_netem *netem;
699 
700  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
701  BUG();
702 
703  netem->qnm_corr.nmc_duplicate = prob;
704  netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
705 }
706 
707 /**
708  * Get packet duplication correlation probability of netem qdisc.
709  * @arg qdisc Netem qdisc.
710  * @return Packet duplication correlation probability or a negative error code.
711  */
712 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
713 {
714  struct rtnl_netem *netem;
715 
716  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
717  BUG();
718 
719  if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)
720  return netem->qnm_corr.nmc_duplicate;
721  else
722  return -NLE_NOATTR;
723 }
724 
725 /** @} */
726 
727 /**
728  * @name Packet Delay
729  * @{
730  */
731 
732 /**
733  * Set packet delay of netem qdisc.
734  * @arg qdisc Netem qdisc to be modified.
735  * @arg delay New packet delay in micro seconds.
736  * @return 0 on success or a negative error code.
737  */
738 void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
739 {
740  struct rtnl_netem *netem;
741 
742  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
743  BUG();
744 
745  netem->qnm_latency = nl_us2ticks(delay);
746  netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
747 }
748 
749 /**
750  * Get packet delay of netem qdisc.
751  * @arg qdisc Netem qdisc.
752  * @return Packet delay in micro seconds or a negative error code.
753  */
754 int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
755 {
756  struct rtnl_netem *netem;
757 
758  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
759  BUG();
760 
761  if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)
762  return nl_ticks2us(netem->qnm_latency);
763  else
764  return -NLE_NOATTR;
765 }
766 
767 /**
768  * Set packet delay jitter of netem qdisc.
769  * @arg qdisc Netem qdisc to be modified.
770  * @arg jitter New packet delay jitter in micro seconds.
771  * @return 0 on success or a negative error code.
772  */
773 void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
774 {
775  struct rtnl_netem *netem;
776 
777  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
778  BUG();
779 
780  netem->qnm_jitter = nl_us2ticks(jitter);
781  netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
782 }
783 
784 /**
785  * Get packet delay jitter of netem qdisc.
786  * @arg qdisc Netem qdisc.
787  * @return Packet delay jitter in micro seconds or a negative error code.
788  */
789 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
790 {
791  struct rtnl_netem *netem;
792 
793  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
794  BUG();
795 
796  if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)
797  return nl_ticks2us(netem->qnm_jitter);
798  else
799  return -NLE_NOATTR;
800 }
801 
802 /**
803  * Set packet delay correlation probability of netem qdisc.
804  * @arg qdisc Netem qdisc to be modified.
805  * @arg prob New packet delay correlation probability.
806  */
807 void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
808 {
809  struct rtnl_netem *netem;
810 
811  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
812  BUG();
813 
814  netem->qnm_corr.nmc_delay = prob;
815  netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
816 }
817 
818 /**
819  * Get packet delay correlation probability of netem qdisc.
820  * @arg qdisc Netem qdisc.
821  * @return Packet delay correlation probability or a negative error code.
822  */
823 int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
824 {
825  struct rtnl_netem *netem;
826 
827  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
828  BUG();
829 
830  if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)
831  return netem->qnm_corr.nmc_delay;
832  else
833  return -NLE_NOATTR;
834 }
835 
836 /**
837  * Get the size of the distribution table.
838  * @arg qdisc Netem qdisc.
839  * @return Distribution table size or a negative error code.
840  */
841 int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
842 {
843  struct rtnl_netem *netem;
844 
845  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
846  BUG();
847 
848  if (netem->qnm_mask & SCH_NETEM_ATTR_DIST)
849  return netem->qnm_dist.dist_size;
850  else
851  return -NLE_NOATTR;
852 }
853 
854 /**
855  * Get a pointer to the distribution table.
856  * @arg qdisc Netem qdisc.
857  * @arg dist_ptr The pointer to set.
858  * @return Negative error code on failure or 0 on success.
859  */
860 int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
861 {
862  struct rtnl_netem *netem;
863 
864  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
865  BUG();
866 
867  if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) {
868  *dist_ptr = netem->qnm_dist.dist_data;
869  return 0;
870  } else
871  return -NLE_NOATTR;
872 }
873 
874 /**
875  * Set the delay distribution. Latency/jitter must be set before applying.
876  * @arg qdisc Netem qdisc.
877  * @arg dist_type The name of the distribution (type, file, path/file).
878  * @return 0 on success, error code on failure.
879  */
880 int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
881  struct rtnl_netem *netem;
882 
883  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
884  BUG();
885 
886  FILE *f;
887  int n = 0;
888  size_t i;
889  size_t len = 2048;
890  char *line;
891  char name[NAME_MAX];
892  char dist_suffix[] = ".dist";
893 
894  /* If the given filename already ends in .dist, don't append it later */
895  char *test_suffix = strstr(dist_type, dist_suffix);
896  if (test_suffix != NULL && strlen(test_suffix) == 5)
897  strcpy(dist_suffix, "");
898 
899  /* Check several locations for the dist file */
900  char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" };
901 
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, "r")))
905  break;
906  }
907 
908  if ( f == NULL )
909  return -nl_syserr2nlerr(errno);
910 
911  netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
912 
913  line = (char *) calloc (sizeof(char), len + 1);
914 
915  while (getline(&line, &len, f) != -1) {
916  char *p, *endp;
917 
918  if (*line == '\n' || *line == '#')
919  continue;
920 
921  for (p = line; ; p = endp) {
922  long x = strtol(p, &endp, 0);
923  if (endp == p) break;
924 
925  if (n >= MAXDIST) {
926  free(line);
927  fclose(f);
928  return -NLE_INVAL;
929  }
930  netem->qnm_dist.dist_data[n++] = x;
931  }
932  }
933 
934  free(line);
935 
936  netem->qnm_dist.dist_size = n;
937  netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
938 
939  fclose(f);
940  return 0;
941 }
942 
943 /** @} */
944 
945 static struct rtnl_tc_ops netem_ops = {
946  .to_kind = "netem",
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,
951  .to_dump[NL_DUMP_LINE] = netem_dump_line,
952  .to_dump[NL_DUMP_DETAILS] = netem_dump_details,
953  .to_msg_fill_raw = netem_msg_fill_raw,
954 };
955 
956 static void __init netem_init(void)
957 {
958  rtnl_tc_register(&netem_ops);
959 }
960 
961 static void __exit netem_exit(void)
962 {
963  rtnl_tc_unregister(&netem_ops);
964 }
965 
966 /** @} */
Dump object briefly on one line.
Definition: types.h:22
int rtnl_tc_register(struct rtnl_tc_ops *ops)
Register a traffic control module.
Definition: tc.c:983
int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
Get re-ordering gap of netem qdisc.
Definition: netem.c:411
void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
Set packet delay of netem qdisc.
Definition: netem.c:738
Attribute validation policy.
Definition: attr.h:67
void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
Set limit of netem qdisc.
Definition: netem.c:353
int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
Get packet loss probability of netem qdisc.
Definition: netem.c:600
int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
Get corruption probability of netem qdisc.
Definition: netem.c:523
int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
Get packet delay of netem qdisc.
Definition: netem.c:754
char * nl_msec2str(uint64_t msec, char *buf, size_t len)
Convert milliseconds to a character string.
Definition: utils.c:547
int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
Get re-ordering correlation probability of netem qdisc.
Definition: netem.c:481
void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
Set re-ordering probability of netem qdisc.
Definition: netem.c:430
int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
Get packet duplication correlation probability of netem qdisc.
Definition: netem.c:712
Dump all attributes but no statistics.
Definition: types.h:23
void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
Set corruption correlation probability of netem qdisc.
Definition: netem.c:542
int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
Get re-ordering probability of netem qdisc.
Definition: netem.c:446
int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
Get packet delay jitter of netem qdisc.
Definition: netem.c:789
int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
Get packet duplication probability of netem qdisc.
Definition: netem.c:677
void rtnl_tc_unregister(struct rtnl_tc_ops *ops)
Unregister a traffic control module.
Definition: tc.c:1017
void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication correlation probability of netem qdisc.
Definition: netem.c:696
int nla_memcpy(void *dest, const struct nlattr *src, int count)
Copy attribute payload to another memory area.
Definition: attr.c:353
void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
Set re-order correlation probability of netem qdisc.
Definition: netem.c:465
#define TC_CAST(ptr)
Macro to cast qdisc/class/classifier to tc object.
Definition: tc.h:56
#define NLA_PUT(msg, attrtype, attrlen, data)
Add unspecific attribute to netlink message.
Definition: attr.h:162
void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication probability of netem qdisc.
Definition: netem.c:661
void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet loss correlation probability of netem qdisc.
Definition: netem.c:619
int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
Get the size of the distribution table.
Definition: netem.c:841
int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
Get packet loss correlation probability of netem qdisc.
Definition: netem.c:635
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.
Definition: attr.c:242
uint32_t nl_ticks2us(uint32_t ticks)
Convert ticks to micro seconds.
Definition: utils.c:494
int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
Get limit of netem qdisc.
Definition: netem.c:369
uint16_t minlen
Minimal length of payload required.
Definition: attr.h:72
void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
Set re-ordering gap of netem qdisc.
Definition: netem.c:395
void * rtnl_tc_data(struct rtnl_tc *tc)
Return pointer to private data of traffic control object.
Definition: tc.c:1044
void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet delay correlation probability of netem qdisc.
Definition: netem.c:807
uint32_t nl_us2ticks(uint32_t us)
Convert micro seconds to ticks.
Definition: utils.c:483
Dumping parameters.
Definition: types.h:33
int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
Get packet delay correlation probability of netem qdisc.
Definition: netem.c:823
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition: utils.c:914
int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
Get corruption correlation probability of netem qdisc.
Definition: netem.c:558
void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
Set corruption probability of netem qdisc.
Definition: netem.c:507
int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type)
Set the delay distribution.
Definition: netem.c:880
int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
Get a pointer to the distribution table.
Definition: netem.c:860
void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
Set packet delay jitter of netem qdisc.
Definition: netem.c:773
void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
Set packet loss probability of netem qdisc.
Definition: netem.c:584