libnl: /data/src/libnl/lib/route/qdisc/netem.c Source File

libnl 3.12.0

netem.c
1/* SPDX-License-Identifier: LGPL-2.1-only */
2/*
3 * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
4 */
5
6/**
7 * @ingroup qdisc
8 * @defgroup qdisc_netem Network Emulator
9 * @brief
10 *
11 * For further documentation see http://linux-net.osdl.org/index.php/Netem
12 * @{
13 */
14
15#include "nl-default.h"
16
17#include <netlink/netlink.h>
18#include <netlink/utils.h>
19#include <netlink/route/qdisc.h>
20#include <netlink/route/qdisc/netem.h>
21
22#include "tc-api.h"
23#include "nl-priv-dynamic-core/nl-core.h"
24
25/** @cond SKIP */
26struct rtnl_netem_corr {
27 uint32_t nmc_delay;
28 uint32_t nmc_loss;
29 uint32_t nmc_duplicate;
30};
31
32struct rtnl_netem_reo {
33 uint32_t nmro_probability;
34 uint32_t nmro_correlation;
35};
36
37struct rtnl_netem_crpt {
38 uint32_t nmcr_probability;
39 uint32_t nmcr_correlation;
40};
41
42struct rtnl_netem_dist {
43 int16_t *dist_data;
44 size_t dist_size;
45};
46
47struct rtnl_netem {
48 uint32_t qnm_latency;
49 uint32_t qnm_limit;
50 uint32_t qnm_loss;
51 uint32_t qnm_gap;
52 uint32_t qnm_duplicate;
53 uint32_t qnm_jitter;
54 uint32_t qnm_mask;
55 struct rtnl_netem_corr qnm_corr;
56 struct rtnl_netem_reo qnm_ro;
57 struct rtnl_netem_crpt qnm_crpt;
58 struct rtnl_netem_dist qnm_dist;
59};
60
61#define SCH_NETEM_ATTR_LATENCY 0x0001
62#define SCH_NETEM_ATTR_LIMIT 0x0002
63#define SCH_NETEM_ATTR_LOSS 0x0004
64#define SCH_NETEM_ATTR_GAP 0x0008
65#define SCH_NETEM_ATTR_DUPLICATE 0x0010
66#define SCH_NETEM_ATTR_JITTER 0x0020
67#define SCH_NETEM_ATTR_DELAY_CORR 0x0040
68#define SCH_NETEM_ATTR_LOSS_CORR 0x0080
69#define SCH_NETEM_ATTR_DUP_CORR 0x0100
70#define SCH_NETEM_ATTR_RO_PROB 0x0200
71#define SCH_NETEM_ATTR_RO_CORR 0x0400
72#define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800
73#define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000
74#define SCH_NETEM_ATTR_DIST 0x2000
75/** @endcond */
76
77static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
78 [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) },
79 [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) },
80 [TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) },
81};
82
83static int netem_msg_parser(struct rtnl_tc *tc, void *data)
84{
85 struct rtnl_netem *netem = data;
86 struct tc_netem_qopt *opts;
87 int len, err = 0;
88
89 if (tc->tc_opts->d_size < sizeof(*opts))
90 return -NLE_INVAL;
91
92 opts = (struct tc_netem_qopt *) tc->tc_opts->d_data;
93 netem->qnm_latency = opts->latency;
94 netem->qnm_limit = opts->limit;
95 netem->qnm_loss = opts->loss;
96 netem->qnm_gap = opts->gap;
97 netem->qnm_duplicate = opts->duplicate;
98 netem->qnm_jitter = opts->jitter;
99
100 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT |
101 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
102 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
103
104 len = tc->tc_opts->d_size - sizeof(*opts);
105
106 if (len > 0) {
107 struct nlattr *tb[TCA_NETEM_MAX+1];
108
109 err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
110 ((char *) tc->tc_opts->d_data + sizeof(*opts)),
111 len, netem_policy);
112 if (err < 0) {
113 free(netem);
114 return err;
115 }
116
117 if (tb[TCA_NETEM_CORR]) {
118 struct tc_netem_corr cor;
119
120 nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor));
121 netem->qnm_corr.nmc_delay = cor.delay_corr;
122 netem->qnm_corr.nmc_loss = cor.loss_corr;
123 netem->qnm_corr.nmc_duplicate = cor.dup_corr;
124
125 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
126 SCH_NETEM_ATTR_LOSS_CORR |
127 SCH_NETEM_ATTR_DUP_CORR);
128 }
129
130 if (tb[TCA_NETEM_REORDER]) {
131 struct tc_netem_reorder ro;
132
133 nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro));
134 netem->qnm_ro.nmro_probability = ro.probability;
135 netem->qnm_ro.nmro_correlation = ro.correlation;
136
137 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
138 SCH_NETEM_ATTR_RO_CORR);
139 }
140
141 if (tb[TCA_NETEM_CORRUPT]) {
142 struct tc_netem_corrupt corrupt;
143
144 nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt));
145 netem->qnm_crpt.nmcr_probability = corrupt.probability;
146 netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
147
148 netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
149 SCH_NETEM_ATTR_CORRUPT_CORR);
150 }
151
152 /* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */
153 netem->qnm_dist.dist_data = NULL;
154 netem->qnm_dist.dist_size = 0;
155 }
156
157 return 0;
158}
159
160static void netem_free_data(struct rtnl_tc *tc, void *data)
161{
162 struct rtnl_netem *netem = data;
163
164 if (!netem)
165 return;
166
167 free(netem->qnm_dist.dist_data);
168}
169
170static void netem_dump_line(struct rtnl_tc *tc, void *data,
171 struct nl_dump_params *p)
172{
173 struct rtnl_netem *netem = data;
174
175 if (netem) {
176 if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0)
177 nl_dump(p, " limit %dpkts", netem->qnm_limit);
178 else
179 nl_dump(p, " no limit");
180 }
181}
182
183static void netem_dump_details(struct rtnl_tc *tc, void *data,
184 struct nl_dump_params *p)
185{
186 struct rtnl_netem *netem = data;
187 char buf[32];
188
189 if (netem) {
190 if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) {
191 nl_msec2str(nl_ticks2us(netem->qnm_latency) / 1000, buf, sizeof(buf));
192 nl_dump(p, " latency %s", buf);
193
194 if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) {
195 nl_msec2str(nl_ticks2us(netem->qnm_jitter) / 1000, buf, sizeof(buf));
196 nl_dump(p, " jitter %s", buf);
197
198 if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR && netem->qnm_corr.nmc_delay > 0)
199 nl_dump(p, " %d", netem->qnm_corr.nmc_delay);
200 }
201 }
202
203 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) {
204 nl_dump(p, " loss %d", netem->qnm_loss);
205
206 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR && netem->qnm_corr.nmc_loss > 0)
207 nl_dump(p, " %d", netem->qnm_corr.nmc_loss);
208 }
209
210 if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) {
211 nl_dump(p, " duplicate %d", netem->qnm_duplicate);
212
213 if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR && netem->qnm_corr.nmc_duplicate > 0)
214 nl_dump(p, " %d", netem->qnm_corr.nmc_duplicate);
215 }
216
217 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB && netem->qnm_ro.nmro_probability > 0) {
218 nl_dump(p, " reorder %d", netem->qnm_ro.nmro_probability);
219
220 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR && netem->qnm_ro.nmro_correlation > 0)
221 nl_dump(p, " %d", netem->qnm_ro.nmro_correlation);
222
223 if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0)
224 nl_dump(p, " gap %d", netem->qnm_gap);
225 }
226
227 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB && netem->qnm_crpt.nmcr_probability > 0) {
228 nl_dump(p, " reorder %d", netem->qnm_crpt.nmcr_probability);
229
230 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR && netem->qnm_crpt.nmcr_correlation > 0)
231 nl_dump(p, " %d", netem->qnm_crpt.nmcr_correlation);
232 }
233 }
234}
235
236static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data,
237 struct nl_msg *msg)
238{
239 int err = 0;
240 struct tc_netem_qopt opts;
241 struct tc_netem_corr cor;
242 struct tc_netem_reorder reorder;
243 struct tc_netem_corrupt corrupt;
244 struct rtnl_netem *netem = data;
245
246 unsigned char set_correlation = 0, set_reorder = 0;
247 unsigned char set_corrupt = 0, set_dist = 0;
248
249 struct nlattr* head;
250 struct nlattr* tail;
251 int old_len;
252
253 if (!netem)
254 BUG();
255
256 memset(&opts, 0, sizeof(opts));
257 memset(&cor, 0, sizeof(cor));
258 memset(&reorder, 0, sizeof(reorder));
259 memset(&corrupt, 0, sizeof(corrupt));
260
261 msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
262
263 if (netem->qnm_ro.nmro_probability != 0) {
264 if (netem->qnm_latency == 0)
265 return -NLE_MISSING_ATTR;
266 if (netem->qnm_gap == 0)
267 netem->qnm_gap = 1;
268 } else if (netem->qnm_gap)
269 return -NLE_MISSING_ATTR;
270
271 if (netem->qnm_corr.nmc_delay != 0) {
272 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
273 return -NLE_MISSING_ATTR;
274 set_correlation = 1;
275 }
276
277 if (netem->qnm_corr.nmc_loss != 0) {
278 if (netem->qnm_loss == 0)
279 return -NLE_MISSING_ATTR;
280 set_correlation = 1;
281 }
282
283 if (netem->qnm_corr.nmc_duplicate != 0) {
284 if (netem->qnm_duplicate == 0)
285 return -NLE_MISSING_ATTR;
286 set_correlation = 1;
287 }
288
289 if (netem->qnm_ro.nmro_probability != 0)
290 set_reorder = 1;
291 else if (netem->qnm_ro.nmro_correlation != 0)
292 return -NLE_MISSING_ATTR;
293
294 if (netem->qnm_crpt.nmcr_probability != 0)
295 set_corrupt = 1;
296 else if (netem->qnm_crpt.nmcr_correlation != 0)
297 return -NLE_MISSING_ATTR;
298
299 if (netem->qnm_dist.dist_data && netem->qnm_dist.dist_size) {
300 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
301 return -NLE_MISSING_ATTR;
302 else {
303 /* Resize to accomodate the large distribution table */
304 int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
305 sizeof(netem->qnm_dist.dist_data[0]);
306 struct nlmsghdr *new_nlh = realloc(msg->nm_nlh, new_msg_len);
307
308 if (new_nlh == NULL)
309 return -NLE_NOMEM;
310 msg->nm_nlh = new_nlh;
311 msg->nm_size = new_msg_len;
312 set_dist = 1;
313 }
314 }
315
316 opts.latency = netem->qnm_latency;
317 opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
318 opts.loss = netem->qnm_loss;
319 opts.gap = netem->qnm_gap;
320 opts.duplicate = netem->qnm_duplicate;
321 opts.jitter = netem->qnm_jitter;
322
323 NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts);
324
325 if (set_correlation) {
326 cor.delay_corr = netem->qnm_corr.nmc_delay;
327 cor.loss_corr = netem->qnm_corr.nmc_loss;
328 cor.dup_corr = netem->qnm_corr.nmc_duplicate;
329
330 NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor);
331 }
332
333 if (set_reorder) {
334 reorder.probability = netem->qnm_ro.nmro_probability;
335 reorder.correlation = netem->qnm_ro.nmro_correlation;
336
337 NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
338 }
339
340 if (set_corrupt) {
341 corrupt.probability = netem->qnm_crpt.nmcr_probability;
342 corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
343
344 NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
345 }
346
347 if (set_dist) {
348 NLA_PUT(msg, TCA_NETEM_DELAY_DIST,
349 netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
350 netem->qnm_dist.dist_data);
351 }
352
353 /* Length specified in the TCA_OPTIONS section must span the entire
354 * remainder of the message. That's just the way that sch_netem expects it.
355 * Maybe there's a more succinct way to do this at a higher level.
356 */
357 head = (struct nlattr *)(((char *) NLMSG_DATA(msg->nm_nlh)) +
358 NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
359
360 tail = (struct nlattr *)(((char *) (msg->nm_nlh)) +
361 NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
362
363 old_len = head->nla_len;
364 head->nla_len = (char *)tail - (char *)head;
365 msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
366
367 return err;
368nla_put_failure:
369 return -NLE_MSGSIZE;
370}
371
372/**
373 * @name Queue Limit
374 * @{
375 */
376
377/**
378 * Set limit of netem qdisc.
379 * @arg qdisc Netem qdisc to be modified.
380 * @arg limit New limit in bytes.
381 * @return 0 on success or a negative error code.
382 */
383 void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
384{
385 struct rtnl_netem *netem;
386
387 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
388 BUG();
389
390 netem->qnm_limit = limit;
391 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
392}
393
394/**
395 * Get limit of netem qdisc.
396 * @arg qdisc Netem qdisc.
397 * @return Limit in bytes or a negative error code.
398 */
399 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
400{
401 struct rtnl_netem *netem;
402
403 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
404 return -NLE_NOMEM;
405
406 if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)
407 return netem->qnm_limit;
408 else
409 return -NLE_NOATTR;
410}
411
412/** @} */
413
414/**
415 * @name Packet Re-ordering
416 * @{
417 */
418
419/**
420 * Set re-ordering gap of netem qdisc.
421 * @arg qdisc Netem qdisc to be modified.
422 * @arg gap New gap in number of packets.
423 * @return 0 on success or a negative error code.
424 */
425 void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
426{
427 struct rtnl_netem *netem;
428
429 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
430 BUG();
431
432 netem->qnm_gap = gap;
433 netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
434}
435
436/**
437 * Get re-ordering gap of netem qdisc.
438 * @arg qdisc Netem qdisc.
439 * @return Re-ordering gap in packets or a negative error code.
440 */
441 int rtnl_netem_get_gap(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_GAP)
449 return netem->qnm_gap;
450 else
451 return -NLE_NOATTR;
452}
453
454/**
455 * Set re-ordering probability of netem qdisc.
456 * @arg qdisc Netem qdisc to be modified.
457 * @arg prob New re-ordering probability.
458 * @return 0 on success or a negative error code.
459 */
460 void rtnl_netem_set_reorder_probability(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_probability = prob;
468 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
469}
470
471/**
472 * Get re-ordering probability of netem qdisc.
473 * @arg qdisc Netem qdisc.
474 * @return Re-ordering probability or a negative error code.
475 */
476 int rtnl_netem_get_reorder_probability(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_PROB)
484 return netem->qnm_ro.nmro_probability;
485 else
486 return -NLE_NOATTR;
487}
488
489/**
490 * Set re-order correlation probability of netem qdisc.
491 * @arg qdisc Netem qdisc to be modified.
492 * @arg prob New re-ordering correlation probability.
493 * @return 0 on success or a negative error code.
494 */
495 void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
496{
497 struct rtnl_netem *netem;
498
499 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
500 BUG();
501
502 netem->qnm_ro.nmro_correlation = prob;
503 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
504}
505
506/**
507 * Get re-ordering correlation probability of netem qdisc.
508 * @arg qdisc Netem qdisc.
509 * @return Re-ordering correlation probability or a negative error code.
510 */
511 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
512{
513 struct rtnl_netem *netem;
514
515 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
516 return -NLE_NOMEM;
517
518 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)
519 return netem->qnm_ro.nmro_correlation;
520 else
521 return -NLE_NOATTR;
522}
523
524/** @} */
525
526/**
527 * @name Corruption
528 * @{
529 */
530
531/**
532 * Set corruption probability of netem qdisc.
533 * @arg qdisc Netem qdisc to be modified.
534 * @arg prob New corruption probability.
535 * @return 0 on success or a negative error code.
536 */
537 void rtnl_netem_set_corruption_probability(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_probability = prob;
545 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
546}
547
548/**
549 * Get corruption probability of netem qdisc.
550 * @arg qdisc Netem qdisc.
551 * @return Corruption probability or a negative error code.
552 */
553 int rtnl_netem_get_corruption_probability(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_PROB)
561 return netem->qnm_crpt.nmcr_probability;
562 else
563 return -NLE_NOATTR;
564}
565
566/**
567 * Set corruption correlation probability of netem qdisc.
568 * @arg qdisc Netem qdisc to be modified.
569 * @arg prob New corruption correlation probability.
570 * @return 0 on success or a negative error code.
571 */
572 void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
573{
574 struct rtnl_netem *netem;
575
576 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
577 BUG();
578
579 netem->qnm_crpt.nmcr_correlation = prob;
580 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
581}
582
583/**
584 * Get corruption correlation probability of netem qdisc.
585 * @arg qdisc Netem qdisc.
586 * @return Corruption correlation probability or a negative error code.
587 */
588 int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
589{
590 struct rtnl_netem *netem;
591
592 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
593 BUG();
594
595 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)
596 return netem->qnm_crpt.nmcr_correlation;
597 else
598 return -NLE_NOATTR;
599}
600
601/** @} */
602
603/**
604 * @name Packet Loss
605 * @{
606 */
607
608/**
609 * Set packet loss probability of netem qdisc.
610 * @arg qdisc Netem qdisc to be modified.
611 * @arg prob New packet loss probability.
612 * @return 0 on success or a negative error code.
613 */
614 void rtnl_netem_set_loss(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_loss = prob;
622 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
623}
624
625/**
626 * Get packet loss probability of netem qdisc.
627 * @arg qdisc Netem qdisc.
628 * @return Packet loss probability or a negative error code.
629 */
630 int rtnl_netem_get_loss(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)
638 return netem->qnm_loss;
639 else
640 return -NLE_NOATTR;
641}
642
643/**
644 * Set packet loss correlation probability of netem qdisc.
645 * @arg qdisc Netem qdisc to be modified.
646 * @arg prob New packet loss correlation.
647 * @return 0 on success or a negative error code.
648 */
649 void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
650{
651 struct rtnl_netem *netem;
652
653 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
654 BUG();
655
656 netem->qnm_corr.nmc_loss = prob;
657 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
658}
659
660/**
661 * Get packet loss correlation probability of netem qdisc.
662 * @arg qdisc Netem qdisc.
663 * @return Packet loss correlation probability or a negative error code.
664 */
665 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
666{
667 struct rtnl_netem *netem;
668
669 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
670 BUG();
671
672 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)
673 return netem->qnm_corr.nmc_loss;
674 else
675 return -NLE_NOATTR;
676}
677
678/** @} */
679
680/**
681 * @name Packet Duplication
682 * @{
683 */
684
685/**
686 * Set packet duplication probability of netem qdisc.
687 * @arg qdisc Netem qdisc to be modified.
688 * @arg prob New packet duplication probability.
689 * @return 0 on success or a negative error code.
690 */
691 void rtnl_netem_set_duplicate(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_duplicate = prob;
699 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
700}
701
702/**
703 * Get packet duplication probability of netem qdisc.
704 * @arg qdisc Netem qdisc.
705 * @return Packet duplication probability or a negative error code.
706 */
707 int rtnl_netem_get_duplicate(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_DUPLICATE)
715 return netem->qnm_duplicate;
716 else
717 return -NLE_NOATTR;
718}
719
720/**
721 * Set packet duplication correlation probability of netem qdisc.
722 * @arg qdisc Netem qdisc to be modified.
723 * @arg prob New packet duplication correlation probability.
724 * @return 0 on success or a negative error code.
725 */
726 void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
727{
728 struct rtnl_netem *netem;
729
730 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
731 BUG();
732
733 netem->qnm_corr.nmc_duplicate = prob;
734 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
735}
736
737/**
738 * Get packet duplication correlation probability of netem qdisc.
739 * @arg qdisc Netem qdisc.
740 * @return Packet duplication correlation probability or a negative error code.
741 */
742 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
743{
744 struct rtnl_netem *netem;
745
746 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
747 BUG();
748
749 if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)
750 return netem->qnm_corr.nmc_duplicate;
751 else
752 return -NLE_NOATTR;
753}
754
755/** @} */
756
757/**
758 * @name Packet Delay
759 * @{
760 */
761
762/**
763 * Set packet delay of netem qdisc.
764 * @arg qdisc Netem qdisc to be modified.
765 * @arg delay New packet delay in micro seconds.
766 * @return 0 on success or a negative error code.
767 */
768 void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
769{
770 struct rtnl_netem *netem;
771
772 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
773 BUG();
774
775 netem->qnm_latency = nl_us2ticks(delay);
776 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
777}
778
779/**
780 * Get packet delay of netem qdisc.
781 * @arg qdisc Netem qdisc.
782 * @return Packet delay in micro seconds or a negative error code.
783 */
784 int rtnl_netem_get_delay(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_LATENCY)
792 return nl_ticks2us(netem->qnm_latency);
793 else
794 return -NLE_NOATTR;
795}
796
797/**
798 * Set packet delay jitter of netem qdisc.
799 * @arg qdisc Netem qdisc to be modified.
800 * @arg jitter New packet delay jitter in micro seconds.
801 * @return 0 on success or a negative error code.
802 */
803 void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
804{
805 struct rtnl_netem *netem;
806
807 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
808 BUG();
809
810 netem->qnm_jitter = nl_us2ticks(jitter);
811 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
812}
813
814/**
815 * Get packet delay jitter of netem qdisc.
816 * @arg qdisc Netem qdisc.
817 * @return Packet delay jitter in micro seconds or a negative error code.
818 */
819 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
820{
821 struct rtnl_netem *netem;
822
823 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
824 BUG();
825
826 if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)
827 return nl_ticks2us(netem->qnm_jitter);
828 else
829 return -NLE_NOATTR;
830}
831
832/**
833 * Set packet delay correlation probability of netem qdisc.
834 * @arg qdisc Netem qdisc to be modified.
835 * @arg prob New packet delay correlation probability.
836 */
837 void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
838{
839 struct rtnl_netem *netem;
840
841 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
842 BUG();
843
844 netem->qnm_corr.nmc_delay = prob;
845 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
846}
847
848/**
849 * Get packet delay correlation probability of netem qdisc.
850 * @arg qdisc Netem qdisc.
851 * @return Packet delay correlation probability or a negative error code.
852 */
853 int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
854{
855 struct rtnl_netem *netem;
856
857 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
858 BUG();
859
860 if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)
861 return netem->qnm_corr.nmc_delay;
862 else
863 return -NLE_NOATTR;
864}
865
866/**
867 * Get the size of the distribution table.
868 * @arg qdisc Netem qdisc.
869 * @return Distribution table size or a negative error code.
870 */
871 int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
872{
873 struct rtnl_netem *netem;
874
875 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
876 BUG();
877
878 if (netem->qnm_mask & SCH_NETEM_ATTR_DIST)
879 return netem->qnm_dist.dist_size;
880 else
881 return -NLE_NOATTR;
882}
883
884/**
885 * Get a pointer to the distribution table.
886 * @arg qdisc Netem qdisc.
887 * @arg dist_ptr The pointer to set.
888 * @return Negative error code on failure or 0 on success.
889 */
890 int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
891{
892 struct rtnl_netem *netem;
893
894 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
895 BUG();
896
897 if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) {
898 *dist_ptr = netem->qnm_dist.dist_data;
899 return 0;
900 } else
901 return -NLE_NOATTR;
902}
903
904/**
905 * Set the delay distribution data. Latency/jitter must be set before applying.
906 * @arg qdisc Netem qdisc.
907 * @return 0 on success, error code on failure.
908 */
909 int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len) {
910 struct rtnl_netem *netem;
911 int16_t *new_data;
912
913 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
914 BUG();
915
916 if (len > MAXDIST)
917 return -NLE_INVAL;
918
919 new_data = (int16_t *) calloc(len, sizeof(int16_t));
920 if (!new_data)
921 return -NLE_NOMEM;
922
923 free (netem->qnm_dist.dist_data);
924 netem->qnm_dist.dist_data = new_data;
925
926 memcpy(netem->qnm_dist.dist_data, data, len * sizeof(int16_t));
927
928 netem->qnm_dist.dist_size = len;
929 netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
930
931 return 0;
932}
933
934/**
935 * Load the delay distribution from a file. Latency/jitter must be set before applying.
936 * @arg qdisc Netem qdisc.
937 * @arg dist_type The name of the distribution (type, file, path/file).
938 * @return 0 on success, error code on failure.
939 */
940 int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
941 FILE *f;
942 int n = 0;
943 size_t i;
944 size_t len = 2048;
945 _nl_auto_free char *line = NULL;
946 char name[NAME_MAX];
947 char dist_suffix[] = ".dist";
948 _nl_auto_free int16_t *data = NULL;
949 char *test_suffix;
950
951 /* Check several locations for the dist file */
952 char *test_path[] = {
953 "",
954 "./",
955 "/usr/lib/tc/",
956 "/usr/lib64/tc/",
957 "/usr/local/lib/tc/",
958 };
959
960 /* If the given filename already ends in .dist, don't append it later */
961 test_suffix = strstr(dist_type, dist_suffix);
962 if (test_suffix != NULL && strlen(test_suffix) == 5)
963 strcpy(dist_suffix, "");
964
965 for (i = 0; i < ARRAY_SIZE(test_path); i++) {
966 snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
967 if ((f = fopen(name, "re")))
968 break;
969 }
970
971 if (f == NULL)
972 return -nl_syserr2nlerr(errno);
973
974 data = (int16_t *) calloc(MAXDIST, sizeof(int16_t));
975 line = (char *) calloc(len + 1, sizeof(char));
976 if (!data || !line) {
977 fclose(f);
978 return -NLE_NOMEM;
979 }
980
981 while (getline(&line, &len, f) != -1) {
982 char *p, *endp;
983
984 if (*line == '\n' || *line == '#')
985 continue;
986
987 for (p = line; ; p = endp) {
988 long x = strtol(p, &endp, 0);
989 if (endp == p) break;
990
991 if (n >= MAXDIST) {
992 fclose(f);
993 return -NLE_INVAL;
994 }
995 data[n++] = x;
996 }
997 }
998
999 fclose(f);
1000 i = rtnl_netem_set_delay_distribution_data(qdisc, data, n);
1001 return i;
1002}
1003
1004/** @} */
1005
1006static struct rtnl_tc_ops netem_ops = {
1007 .to_kind = "netem",
1008 .to_type = RTNL_TC_TYPE_QDISC,
1009 .to_size = sizeof(struct rtnl_netem),
1010 .to_msg_parser = netem_msg_parser,
1011 .to_free_data = netem_free_data,
1012 .to_dump[NL_DUMP_LINE] = netem_dump_line,
1013 .to_dump[NL_DUMP_DETAILS] = netem_dump_details,
1014 .to_msg_fill_raw = netem_msg_fill_raw,
1015};
1016
1017static void _nl_init netem_init(void)
1018{
1019 rtnl_tc_register(&netem_ops);
1020}
1021
1022static void _nl_exit netem_exit(void)
1023{
1024 rtnl_tc_unregister(&netem_ops);
1025}
1026
1027/** @} */
nla_parse
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:245
NLA_PUT
#define NLA_PUT(msg, attrtype, attrlen, data)
Add unspecific attribute to netlink message.
Definition attr.h:166
nla_memcpy
int nla_memcpy(void *dest, const struct nlattr *src, int count)
Copy attribute payload to another memory area.
Definition attr.c:355
rtnl_netem_set_duplicate_correlation
void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication correlation probability of netem qdisc.
Definition netem.c:726
rtnl_netem_get_duplicate
int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
Get packet duplication probability of netem qdisc.
Definition netem.c:707
rtnl_netem_get_delay_distribution
int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
Get a pointer to the distribution table.
Definition netem.c:890
rtnl_netem_get_reorder_correlation
int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
Get re-ordering correlation probability of netem qdisc.
Definition netem.c:511
rtnl_netem_set_delay_correlation
void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet delay correlation probability of netem qdisc.
Definition netem.c:837
rtnl_netem_get_delay_correlation
int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
Get packet delay correlation probability of netem qdisc.
Definition netem.c:853
rtnl_netem_get_loss
int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
Get packet loss probability of netem qdisc.
Definition netem.c:630
rtnl_netem_get_delay_distribution_size
int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
Get the size of the distribution table.
Definition netem.c:871
rtnl_netem_get_gap
int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
Get re-ordering gap of netem qdisc.
Definition netem.c:441
rtnl_netem_get_reorder_probability
int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
Get re-ordering probability of netem qdisc.
Definition netem.c:476
rtnl_netem_get_duplicate_correlation
int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
Get packet duplication correlation probability of netem qdisc.
Definition netem.c:742
rtnl_netem_set_jitter
void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
Set packet delay jitter of netem qdisc.
Definition netem.c:803
rtnl_netem_get_corruption_correlation
int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
Get corruption correlation probability of netem qdisc.
Definition netem.c:588
rtnl_netem_set_loss
void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
Set packet loss probability of netem qdisc.
Definition netem.c:614
rtnl_netem_set_limit
void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
Set limit of netem qdisc.
Definition netem.c:383
rtnl_netem_set_reorder_correlation
void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
Set re-order correlation probability of netem qdisc.
Definition netem.c:495
rtnl_netem_get_corruption_probability
int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
Get corruption probability of netem qdisc.
Definition netem.c:553
rtnl_netem_get_jitter
int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
Get packet delay jitter of netem qdisc.
Definition netem.c:819
rtnl_netem_set_corruption_correlation
void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
Set corruption correlation probability of netem qdisc.
Definition netem.c:572
rtnl_netem_get_delay
int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
Get packet delay of netem qdisc.
Definition netem.c:784
rtnl_netem_set_reorder_probability
void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
Set re-ordering probability of netem qdisc.
Definition netem.c:460
rtnl_netem_set_gap
void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
Set re-ordering gap of netem qdisc.
Definition netem.c:425
rtnl_netem_set_corruption_probability
void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
Set corruption probability of netem qdisc.
Definition netem.c:537
rtnl_netem_set_duplicate
void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
Set packet duplication probability of netem qdisc.
Definition netem.c:691
rtnl_netem_set_loss_correlation
void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet loss correlation probability of netem qdisc.
Definition netem.c:649
rtnl_netem_set_delay_distribution
int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type)
Load the delay distribution from a file.
Definition netem.c:940
rtnl_netem_set_delay
void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
Set packet delay of netem qdisc.
Definition netem.c:768
rtnl_netem_get_loss_correlation
int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
Get packet loss correlation probability of netem qdisc.
Definition netem.c:665
rtnl_netem_set_delay_distribution_data
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:909
rtnl_netem_get_limit
int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
Get limit of netem qdisc.
Definition netem.c:399
TC_CAST
#define TC_CAST(ptr)
Macro to cast qdisc/class/classifier to tc object.
Definition tc.h:50
rtnl_tc_data
void * rtnl_tc_data(struct rtnl_tc *)
Return pointer to private data of traffic control object.
Definition tc.c:1079
rtnl_tc_register
int rtnl_tc_register(struct rtnl_tc_ops *)
Register a traffic control module.
Definition tc.c:1018
rtnl_tc_unregister
void rtnl_tc_unregister(struct rtnl_tc_ops *)
Unregister a traffic control module.
Definition tc.c:1052
nl_dump
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition utils.c:1015
nl_ticks2us
uint32_t nl_ticks2us(uint32_t ticks)
Convert ticks to micro seconds.
Definition utils.c:594
nl_us2ticks
uint32_t nl_us2ticks(uint32_t us)
Convert micro seconds to ticks.
Definition utils.c:582
nl_msec2str
char * nl_msec2str(uint64_t msec, char *buf, size_t len)
Convert milliseconds to a character string.
Definition utils.c:648
NL_DUMP_LINE
@ NL_DUMP_LINE
Dump object briefly on one line.
Definition types.h:20
NL_DUMP_DETAILS
@ NL_DUMP_DETAILS
Dump all attributes but no statistics.
Definition types.h:21
nl_dump_params
Dumping parameters.
Definition types.h:32
nla_policy
Attribute validation policy.
Definition attr.h:66
rtnl_qdisc
Definition nl-route.h:119
rtnl_tc
Definition nl-route.h:115