libnl  3.5.0
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  ((char *) 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  unsigned char set_corrupt = 0, set_dist = 0;
217 
218  struct nlattr* head;
219  struct nlattr* tail;
220  int old_len;
221 
222  if (!netem)
223  BUG();
224 
225  memset(&opts, 0, sizeof(opts));
226  memset(&cor, 0, sizeof(cor));
227  memset(&reorder, 0, sizeof(reorder));
228  memset(&corrupt, 0, sizeof(corrupt));
229 
230  msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
231 
232  if (netem->qnm_ro.nmro_probability != 0) {
233  if (netem->qnm_latency == 0)
234  return -NLE_MISSING_ATTR;
235  if (netem->qnm_gap == 0)
236  netem->qnm_gap = 1;
237  } else if (netem->qnm_gap)
238  return -NLE_MISSING_ATTR;
239 
240  if (netem->qnm_corr.nmc_delay != 0) {
241  if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
242  return -NLE_MISSING_ATTR;
243  set_correlation = 1;
244  }
245 
246  if (netem->qnm_corr.nmc_loss != 0) {
247  if (netem->qnm_loss == 0)
248  return -NLE_MISSING_ATTR;
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  set_correlation = 1;
256  }
257 
258  if (netem->qnm_ro.nmro_probability != 0)
259  set_reorder = 1;
260  else if (netem->qnm_ro.nmro_correlation != 0)
261  return -NLE_MISSING_ATTR;
262 
263  if (netem->qnm_crpt.nmcr_probability != 0)
264  set_corrupt = 1;
265  else if (netem->qnm_crpt.nmcr_correlation != 0)
266  return -NLE_MISSING_ATTR;
267 
268  if (netem->qnm_dist.dist_data && netem->qnm_dist.dist_size) {
269  if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
270  return -NLE_MISSING_ATTR;
271  else {
272  /* Resize to accomodate the large distribution table */
273  int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
274  sizeof(netem->qnm_dist.dist_data[0]);
275  struct nlmsghdr *new_nlh = realloc(msg->nm_nlh, new_msg_len);
276 
277  if (new_nlh == NULL)
278  return -NLE_NOMEM;
279  msg->nm_nlh = new_nlh;
280  msg->nm_size = new_msg_len;
281  set_dist = 1;
282  }
283  }
284 
285  opts.latency = netem->qnm_latency;
286  opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
287  opts.loss = netem->qnm_loss;
288  opts.gap = netem->qnm_gap;
289  opts.duplicate = netem->qnm_duplicate;
290  opts.jitter = netem->qnm_jitter;
291 
292  NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts);
293 
294  if (set_correlation) {
295  cor.delay_corr = netem->qnm_corr.nmc_delay;
296  cor.loss_corr = netem->qnm_corr.nmc_loss;
297  cor.dup_corr = netem->qnm_corr.nmc_duplicate;
298 
299  NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor);
300  }
301 
302  if (set_reorder) {
303  reorder.probability = netem->qnm_ro.nmro_probability;
304  reorder.correlation = netem->qnm_ro.nmro_correlation;
305 
306  NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
307  }
308 
309  if (set_corrupt) {
310  corrupt.probability = netem->qnm_crpt.nmcr_probability;
311  corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
312 
313  NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
314  }
315 
316  if (set_dist) {
317  NLA_PUT(msg, TCA_NETEM_DELAY_DIST,
318  netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
319  netem->qnm_dist.dist_data);
320  }
321 
322  /* Length specified in the TCA_OPTIONS section must span the entire
323  * remainder of the message. That's just the way that sch_netem expects it.
324  * Maybe there's a more succinct way to do this at a higher level.
325  */
326  head = (struct nlattr *)(((char *) NLMSG_DATA(msg->nm_nlh)) +
327  NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
328 
329  tail = (struct nlattr *)(((char *) (msg->nm_nlh)) +
330  NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
331 
332  old_len = head->nla_len;
333  head->nla_len = (char *)tail - (char *)head;
334  msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
335 
336  return err;
337 nla_put_failure:
338  return -NLE_MSGSIZE;
339 }
340 
341 /**
342  * @name Queue Limit
343  * @{
344  */
345 
346 /**
347  * Set limit of netem qdisc.
348  * @arg qdisc Netem qdisc to be modified.
349  * @arg limit New limit in bytes.
350  * @return 0 on success or a negative error code.
351  */
352 void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
353 {
354  struct rtnl_netem *netem;
355 
356  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
357  BUG();
358 
359  netem->qnm_limit = limit;
360  netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
361 }
362 
363 /**
364  * Get limit of netem qdisc.
365  * @arg qdisc Netem qdisc.
366  * @return Limit in bytes or a negative error code.
367  */
368 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
369 {
370  struct rtnl_netem *netem;
371 
372  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
373  return -NLE_NOMEM;
374 
375  if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)
376  return netem->qnm_limit;
377  else
378  return -NLE_NOATTR;
379 }
380 
381 /** @} */
382 
383 /**
384  * @name Packet Re-ordering
385  * @{
386  */
387 
388 /**
389  * Set re-ordering gap of netem qdisc.
390  * @arg qdisc Netem qdisc to be modified.
391  * @arg gap New gap in number of packets.
392  * @return 0 on success or a negative error code.
393  */
394 void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
395 {
396  struct rtnl_netem *netem;
397 
398  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
399  BUG();
400 
401  netem->qnm_gap = gap;
402  netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
403 }
404 
405 /**
406  * Get re-ordering gap of netem qdisc.
407  * @arg qdisc Netem qdisc.
408  * @return Re-ordering gap in packets or a negative error code.
409  */
410 int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
411 {
412  struct rtnl_netem *netem;
413 
414  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
415  return -NLE_NOMEM;
416 
417  if (netem->qnm_mask & SCH_NETEM_ATTR_GAP)
418  return netem->qnm_gap;
419  else
420  return -NLE_NOATTR;
421 }
422 
423 /**
424  * Set re-ordering probability of netem qdisc.
425  * @arg qdisc Netem qdisc to be modified.
426  * @arg prob New re-ordering probability.
427  * @return 0 on success or a negative error code.
428  */
429 void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
430 {
431  struct rtnl_netem *netem;
432 
433  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
434  BUG();
435 
436  netem->qnm_ro.nmro_probability = prob;
437  netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
438 }
439 
440 /**
441  * Get re-ordering probability of netem qdisc.
442  * @arg qdisc Netem qdisc.
443  * @return Re-ordering probability or a negative error code.
444  */
445 int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
446 {
447  struct rtnl_netem *netem;
448 
449  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
450  return -NLE_NOMEM;
451 
452  if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)
453  return netem->qnm_ro.nmro_probability;
454  else
455  return -NLE_NOATTR;
456 }
457 
458 /**
459  * Set re-order correlation probability of netem qdisc.
460  * @arg qdisc Netem qdisc to be modified.
461  * @arg prob New re-ordering correlation probability.
462  * @return 0 on success or a negative error code.
463  */
464 void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
465 {
466  struct rtnl_netem *netem;
467 
468  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
469  BUG();
470 
471  netem->qnm_ro.nmro_correlation = prob;
472  netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
473 }
474 
475 /**
476  * Get re-ordering correlation probability of netem qdisc.
477  * @arg qdisc Netem qdisc.
478  * @return Re-ordering correlation probability or a negative error code.
479  */
480 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
481 {
482  struct rtnl_netem *netem;
483 
484  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
485  return -NLE_NOMEM;
486 
487  if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)
488  return netem->qnm_ro.nmro_correlation;
489  else
490  return -NLE_NOATTR;
491 }
492 
493 /** @} */
494 
495 /**
496  * @name Corruption
497  * @{
498  */
499 
500 /**
501  * Set corruption probability of netem qdisc.
502  * @arg qdisc Netem qdisc to be modified.
503  * @arg prob New corruption probability.
504  * @return 0 on success or a negative error code.
505  */
506 void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
507 {
508  struct rtnl_netem *netem;
509 
510  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
511  BUG();
512 
513  netem->qnm_crpt.nmcr_probability = prob;
514  netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
515 }
516 
517 /**
518  * Get corruption probability of netem qdisc.
519  * @arg qdisc Netem qdisc.
520  * @return Corruption probability or a negative error code.
521  */
522 int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
523 {
524  struct rtnl_netem *netem;
525 
526  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
527  BUG();
528 
529  if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB)
530  return netem->qnm_crpt.nmcr_probability;
531  else
532  return -NLE_NOATTR;
533 }
534 
535 /**
536  * Set corruption correlation probability of netem qdisc.
537  * @arg qdisc Netem qdisc to be modified.
538  * @arg prob New corruption correlation probability.
539  * @return 0 on success or a negative error code.
540  */
541 void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
542 {
543  struct rtnl_netem *netem;
544 
545  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
546  BUG();
547 
548  netem->qnm_crpt.nmcr_correlation = prob;
549  netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
550 }
551 
552 /**
553  * Get corruption correlation probability of netem qdisc.
554  * @arg qdisc Netem qdisc.
555  * @return Corruption correlation probability or a negative error code.
556  */
557 int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
558 {
559  struct rtnl_netem *netem;
560 
561  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
562  BUG();
563 
564  if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)
565  return netem->qnm_crpt.nmcr_correlation;
566  else
567  return -NLE_NOATTR;
568 }
569 
570 /** @} */
571 
572 /**
573  * @name Packet Loss
574  * @{
575  */
576 
577 /**
578  * Set packet loss probability of netem qdisc.
579  * @arg qdisc Netem qdisc to be modified.
580  * @arg prob New packet loss probability.
581  * @return 0 on success or a negative error code.
582  */
583 void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
584 {
585  struct rtnl_netem *netem;
586 
587  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
588  BUG();
589 
590  netem->qnm_loss = prob;
591  netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
592 }
593 
594 /**
595  * Get packet loss probability of netem qdisc.
596  * @arg qdisc Netem qdisc.
597  * @return Packet loss probability or a negative error code.
598  */
599 int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
600 {
601  struct rtnl_netem *netem;
602 
603  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
604  BUG();
605 
606  if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)
607  return netem->qnm_loss;
608  else
609  return -NLE_NOATTR;
610 }
611 
612 /**
613  * Set packet loss correlation probability of netem qdisc.
614  * @arg qdisc Netem qdisc to be modified.
615  * @arg prob New packet loss correlation.
616  * @return 0 on success or a negative error code.
617  */
618 void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
619 {
620  struct rtnl_netem *netem;
621 
622  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
623  BUG();
624 
625  netem->qnm_corr.nmc_loss = prob;
626  netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
627 }
628 
629 /**
630  * Get packet loss correlation probability of netem qdisc.
631  * @arg qdisc Netem qdisc.
632  * @return Packet loss correlation probability or a negative error code.
633  */
634 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
635 {
636  struct rtnl_netem *netem;
637 
638  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
639  BUG();
640 
641  if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)
642  return netem->qnm_corr.nmc_loss;
643  else
644  return -NLE_NOATTR;
645 }
646 
647 /** @} */
648 
649 /**
650  * @name Packet Duplication
651  * @{
652  */
653 
654 /**
655  * Set packet duplication probability of netem qdisc.
656  * @arg qdisc Netem qdisc to be modified.
657  * @arg prob New packet duplication probability.
658  * @return 0 on success or a negative error code.
659  */
660 void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
661 {
662  struct rtnl_netem *netem;
663 
664  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
665  BUG();
666 
667  netem->qnm_duplicate = prob;
668  netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
669 }
670 
671 /**
672  * Get packet duplication probability of netem qdisc.
673  * @arg qdisc Netem qdisc.
674  * @return Packet duplication probability or a negative error code.
675  */
676 int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
677 {
678  struct rtnl_netem *netem;
679 
680  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
681  BUG();
682 
683  if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)
684  return netem->qnm_duplicate;
685  else
686  return -NLE_NOATTR;
687 }
688 
689 /**
690  * Set packet duplication correlation probability of netem qdisc.
691  * @arg qdisc Netem qdisc to be modified.
692  * @arg prob New packet duplication correlation probability.
693  * @return 0 on sucess or a negative error code.
694  */
695 void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
696 {
697  struct rtnl_netem *netem;
698 
699  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
700  BUG();
701 
702  netem->qnm_corr.nmc_duplicate = prob;
703  netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
704 }
705 
706 /**
707  * Get packet duplication correlation probability of netem qdisc.
708  * @arg qdisc Netem qdisc.
709  * @return Packet duplication correlation probability or a negative error code.
710  */
711 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
712 {
713  struct rtnl_netem *netem;
714 
715  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
716  BUG();
717 
718  if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)
719  return netem->qnm_corr.nmc_duplicate;
720  else
721  return -NLE_NOATTR;
722 }
723 
724 /** @} */
725 
726 /**
727  * @name Packet Delay
728  * @{
729  */
730 
731 /**
732  * Set packet delay of netem qdisc.
733  * @arg qdisc Netem qdisc to be modified.
734  * @arg delay New packet delay in micro seconds.
735  * @return 0 on success or a negative error code.
736  */
737 void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
738 {
739  struct rtnl_netem *netem;
740 
741  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
742  BUG();
743 
744  netem->qnm_latency = nl_us2ticks(delay);
745  netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
746 }
747 
748 /**
749  * Get packet delay of netem qdisc.
750  * @arg qdisc Netem qdisc.
751  * @return Packet delay in micro seconds or a negative error code.
752  */
753 int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
754 {
755  struct rtnl_netem *netem;
756 
757  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
758  BUG();
759 
760  if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)
761  return nl_ticks2us(netem->qnm_latency);
762  else
763  return -NLE_NOATTR;
764 }
765 
766 /**
767  * Set packet delay jitter of netem qdisc.
768  * @arg qdisc Netem qdisc to be modified.
769  * @arg jitter New packet delay jitter in micro seconds.
770  * @return 0 on success or a negative error code.
771  */
772 void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
773 {
774  struct rtnl_netem *netem;
775 
776  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
777  BUG();
778 
779  netem->qnm_jitter = nl_us2ticks(jitter);
780  netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
781 }
782 
783 /**
784  * Get packet delay jitter of netem qdisc.
785  * @arg qdisc Netem qdisc.
786  * @return Packet delay jitter in micro seconds or a negative error code.
787  */
788 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
789 {
790  struct rtnl_netem *netem;
791 
792  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
793  BUG();
794 
795  if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)
796  return nl_ticks2us(netem->qnm_jitter);
797  else
798  return -NLE_NOATTR;
799 }
800 
801 /**
802  * Set packet delay correlation probability of netem qdisc.
803  * @arg qdisc Netem qdisc to be modified.
804  * @arg prob New packet delay correlation probability.
805  */
806 void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
807 {
808  struct rtnl_netem *netem;
809 
810  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
811  BUG();
812 
813  netem->qnm_corr.nmc_delay = prob;
814  netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
815 }
816 
817 /**
818  * Get packet delay correlation probability of netem qdisc.
819  * @arg qdisc Netem qdisc.
820  * @return Packet delay correlation probability or a negative error code.
821  */
822 int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
823 {
824  struct rtnl_netem *netem;
825 
826  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
827  BUG();
828 
829  if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)
830  return netem->qnm_corr.nmc_delay;
831  else
832  return -NLE_NOATTR;
833 }
834 
835 /**
836  * Get the size of the distribution table.
837  * @arg qdisc Netem qdisc.
838  * @return Distribution table size or a negative error code.
839  */
840 int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
841 {
842  struct rtnl_netem *netem;
843 
844  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
845  BUG();
846 
847  if (netem->qnm_mask & SCH_NETEM_ATTR_DIST)
848  return netem->qnm_dist.dist_size;
849  else
850  return -NLE_NOATTR;
851 }
852 
853 /**
854  * Get a pointer to the distribution table.
855  * @arg qdisc Netem qdisc.
856  * @arg dist_ptr The pointer to set.
857  * @return Negative error code on failure or 0 on success.
858  */
859 int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
860 {
861  struct rtnl_netem *netem;
862 
863  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
864  BUG();
865 
866  if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) {
867  *dist_ptr = netem->qnm_dist.dist_data;
868  return 0;
869  } else
870  return -NLE_NOATTR;
871 }
872 
873 /**
874  * Set the delay distribution data. Latency/jitter must be set before applying.
875  * @arg qdisc Netem qdisc.
876  * @return 0 on success, error code on failure.
877  */
878 int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len) {
879  struct rtnl_netem *netem;
880  int16_t *new_data;
881 
882  if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
883  BUG();
884 
885  if (len > MAXDIST)
886  return -NLE_INVAL;
887 
888  new_data = (int16_t *) calloc(len, sizeof(int16_t));
889  if (!new_data)
890  return -NLE_NOMEM;
891 
892  free (netem->qnm_dist.dist_data);
893  netem->qnm_dist.dist_data = new_data;
894 
895  memcpy(netem->qnm_dist.dist_data, data, len * sizeof(int16_t));
896 
897  netem->qnm_dist.dist_size = len;
898  netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
899 
900  return 0;
901 }
902 
903 /**
904  * Load the delay distribution from a file. Latency/jitter must be set before applying.
905  * @arg qdisc Netem qdisc.
906  * @arg dist_type The name of the distribution (type, file, path/file).
907  * @return 0 on success, error code on failure.
908  */
909 int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
910  FILE *f;
911  int n = 0;
912  size_t i;
913  size_t len = 2048;
914  char *line;
915  char name[NAME_MAX];
916  char dist_suffix[] = ".dist";
917  int16_t *data;
918  char *test_suffix;
919 
920  /* Check several locations for the dist file */
921  char *test_path[] = {
922  "",
923  "./",
924  "/usr/lib/tc/",
925  "/usr/lib64/tc/",
926  "/usr/local/lib/tc/",
927  };
928 
929  /* If the given filename already ends in .dist, don't append it later */
930  test_suffix = strstr(dist_type, dist_suffix);
931  if (test_suffix != NULL && strlen(test_suffix) == 5)
932  strcpy(dist_suffix, "");
933 
934  for (i = 0; i < ARRAY_SIZE(test_path); i++) {
935  snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
936  if ((f = fopen(name, "re")))
937  break;
938  }
939 
940  if (f == NULL)
941  return -nl_syserr2nlerr(errno);
942 
943  data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
944 
945  line = (char *) calloc (sizeof(char), len + 1);
946 
947  while (getline(&line, &len, f) != -1) {
948  char *p, *endp;
949 
950  if (*line == '\n' || *line == '#')
951  continue;
952 
953  for (p = line; ; p = endp) {
954  long x = strtol(p, &endp, 0);
955  if (endp == p) break;
956 
957  if (n >= MAXDIST) {
958  free(line);
959  fclose(f);
960  return -NLE_INVAL;
961  }
962  data[n++] = x;
963  }
964  }
965 
966  free(line);
967  fclose(f);
968 
969  i = rtnl_netem_set_delay_distribution_data(qdisc, data, n);
970  free(data);
971  return i;
972 }
973 
974 /** @} */
975 
976 static struct rtnl_tc_ops netem_ops = {
977  .to_kind = "netem",
978  .to_type = RTNL_TC_TYPE_QDISC,
979  .to_size = sizeof(struct rtnl_netem),
980  .to_msg_parser = netem_msg_parser,
981  .to_free_data = netem_free_data,
982  .to_dump[NL_DUMP_LINE] = netem_dump_line,
983  .to_dump[NL_DUMP_DETAILS] = netem_dump_details,
984  .to_msg_fill_raw = netem_msg_fill_raw,
985 };
986 
987 static void __init netem_init(void)
988 {
989  rtnl_tc_register(&netem_ops);
990 }
991 
992 static void __exit netem_exit(void)
993 {
994  rtnl_tc_unregister(&netem_ops);
995 }
996 
997 /** @} */
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:1021
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.
Definition: attr.c:243
int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
Get re-ordering gap of netem qdisc.
Definition: netem.c:410
void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
Set packet delay of netem qdisc.
Definition: netem.c:737
Attribute validation policy.
Definition: attr.h:69
void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
Set limit of netem qdisc.
Definition: netem.c:352
int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
Get packet loss probability of netem qdisc.
Definition: netem.c:599
int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
Get corruption probability of netem qdisc.
Definition: netem.c:522
int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
Get packet delay of netem qdisc.
Definition: netem.c:753
char * nl_msec2str(uint64_t msec, char *buf, size_t len)
Convert milliseconds to a character string.
Definition: utils.c:595
int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
Get re-ordering correlation probability of netem qdisc.
Definition: netem.c:480
void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
Set re-ordering probability of netem qdisc.
Definition: netem.c:429
int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len)
Set the delay distribution data.
Definition: netem.c:878
int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
Get packet duplication correlation probability of netem qdisc.
Definition: netem.c:711
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:541
int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
Get re-ordering probability of netem qdisc.
Definition: netem.c:445
int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
Get packet delay jitter of netem qdisc.
Definition: netem.c:788
int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
Get packet duplication probability of netem qdisc.
Definition: netem.c:676
void rtnl_tc_unregister(struct rtnl_tc_ops *ops)
Unregister a traffic control module.
Definition: tc.c:1055
void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication correlation probability of netem qdisc.
Definition: netem.c:695
int nla_memcpy(void *dest, const struct nlattr *src, int count)
Copy attribute payload to another memory area.
Definition: attr.c:354
void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
Set re-order correlation probability of netem qdisc.
Definition: netem.c:464
#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:165
void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication probability of netem qdisc.
Definition: netem.c:660
void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet loss correlation probability of netem qdisc.
Definition: netem.c:618
int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
Get the size of the distribution table.
Definition: netem.c:840
int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
Get packet loss correlation probability of netem qdisc.
Definition: netem.c:634
uint32_t nl_ticks2us(uint32_t ticks)
Convert ticks to micro seconds.
Definition: utils.c:541
int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
Get limit of netem qdisc.
Definition: netem.c:368
uint16_t minlen
Minimal length of payload required.
Definition: attr.h:74
void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
Set re-ordering gap of netem qdisc.
Definition: netem.c:394
void * rtnl_tc_data(struct rtnl_tc *tc)
Return pointer to private data of traffic control object.
Definition: tc.c:1082
void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet delay correlation probability of netem qdisc.
Definition: netem.c:806
uint32_t nl_us2ticks(uint32_t us)
Convert micro seconds to ticks.
Definition: utils.c:529
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:822
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition: utils.c:962
int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
Get corruption correlation probability of netem qdisc.
Definition: netem.c:557
void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
Set corruption probability of netem qdisc.
Definition: netem.c:506
int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type)
Load the delay distribution from a file.
Definition: netem.c:909
int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
Get a pointer to the distribution table.
Definition: netem.c:859
void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
Set packet delay jitter of netem qdisc.
Definition: netem.c:772
void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
Set packet loss probability of netem qdisc.
Definition: netem.c:583