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