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