libnl 3.7.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 <netlink-private/netlink.h>
16#include <netlink-private/tc.h>
17#include <netlink/netlink.h>
18#include <netlink/utils.h>
19#include <netlink-private/route/tc-api.h>
20#include <netlink/route/qdisc.h>
21#include <netlink/route/qdisc/netem.h>
22
23#include "netlink-private/utils.h"
24
25/** @cond SKIP */
26#define SCH_NETEM_ATTR_LATENCY 0x0001
27#define SCH_NETEM_ATTR_LIMIT 0x0002
28#define SCH_NETEM_ATTR_LOSS 0x0004
29#define SCH_NETEM_ATTR_GAP 0x0008
30#define SCH_NETEM_ATTR_DUPLICATE 0x0010
31#define SCH_NETEM_ATTR_JITTER 0x0020
32#define SCH_NETEM_ATTR_DELAY_CORR 0x0040
33#define SCH_NETEM_ATTR_LOSS_CORR 0x0080
34#define SCH_NETEM_ATTR_DUP_CORR 0x0100
35#define SCH_NETEM_ATTR_RO_PROB 0x0200
36#define SCH_NETEM_ATTR_RO_CORR 0x0400
37#define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800
38#define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000
39#define SCH_NETEM_ATTR_DIST 0x2000
40/** @endcond */
41
42static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
43 [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) },
44 [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) },
45 [TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) },
46};
47
48static int netem_msg_parser(struct rtnl_tc *tc, void *data)
49{
50 struct rtnl_netem *netem = data;
51 struct tc_netem_qopt *opts;
52 int len, err = 0;
53
54 if (tc->tc_opts->d_size < sizeof(*opts))
55 return -NLE_INVAL;
56
57 opts = (struct tc_netem_qopt *) tc->tc_opts->d_data;
58 netem->qnm_latency = opts->latency;
59 netem->qnm_limit = opts->limit;
60 netem->qnm_loss = opts->loss;
61 netem->qnm_gap = opts->gap;
62 netem->qnm_duplicate = opts->duplicate;
63 netem->qnm_jitter = opts->jitter;
64
65 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT |
66 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
67 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
68
69 len = tc->tc_opts->d_size - sizeof(*opts);
70
71 if (len > 0) {
72 struct nlattr *tb[TCA_NETEM_MAX+1];
73
74 err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
75 ((char *) tc->tc_opts->d_data + sizeof(*opts)),
76 len, netem_policy);
77 if (err < 0) {
78 free(netem);
79 return err;
80 }
81
82 if (tb[TCA_NETEM_CORR]) {
83 struct tc_netem_corr cor;
84
85 nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor));
86 netem->qnm_corr.nmc_delay = cor.delay_corr;
87 netem->qnm_corr.nmc_loss = cor.loss_corr;
88 netem->qnm_corr.nmc_duplicate = cor.dup_corr;
89
90 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
91 SCH_NETEM_ATTR_LOSS_CORR |
92 SCH_NETEM_ATTR_DUP_CORR);
93 }
94
95 if (tb[TCA_NETEM_REORDER]) {
96 struct tc_netem_reorder ro;
97
98 nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro));
99 netem->qnm_ro.nmro_probability = ro.probability;
100 netem->qnm_ro.nmro_correlation = ro.correlation;
101
102 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
103 SCH_NETEM_ATTR_RO_CORR);
104 }
105
106 if (tb[TCA_NETEM_CORRUPT]) {
107 struct tc_netem_corrupt corrupt;
108
109 nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt));
110 netem->qnm_crpt.nmcr_probability = corrupt.probability;
111 netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
112
113 netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
114 SCH_NETEM_ATTR_CORRUPT_CORR);
115 }
116
117 /* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */
118 netem->qnm_dist.dist_data = NULL;
119 netem->qnm_dist.dist_size = 0;
120 }
121
122 return 0;
123}
124
125static void netem_free_data(struct rtnl_tc *tc, void *data)
126{
127 struct rtnl_netem *netem = data;
128
129 if (!netem)
130 return;
131
132 free(netem->qnm_dist.dist_data);
133}
134
135static void netem_dump_line(struct rtnl_tc *tc, void *data,
136 struct nl_dump_params *p)
137{
138 struct rtnl_netem *netem = data;
139
140 if (netem) {
141 if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0)
142 nl_dump(p, " limit %dpkts", netem->qnm_limit);
143 else
144 nl_dump(p, " no limit");
145 }
146}
147
148static void netem_dump_details(struct rtnl_tc *tc, void *data,
149 struct nl_dump_params *p)
150{
151 struct rtnl_netem *netem = data;
152 char buf[32];
153
154 if (netem) {
155 if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) {
156 nl_msec2str(nl_ticks2us(netem->qnm_latency) / 1000, buf, sizeof(buf));
157 nl_dump(p, " latency %s", buf);
158
159 if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) {
160 nl_msec2str(nl_ticks2us(netem->qnm_jitter) / 1000, buf, sizeof(buf));
161 nl_dump(p, " jitter %s", buf);
162
163 if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR && netem->qnm_corr.nmc_delay > 0)
164 nl_dump(p, " %d", netem->qnm_corr.nmc_delay);
165 }
166 }
167
168 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) {
169 nl_dump(p, " loss %d", netem->qnm_loss);
170
171 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR && netem->qnm_corr.nmc_loss > 0)
172 nl_dump(p, " %d", netem->qnm_corr.nmc_loss);
173 }
174
175 if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) {
176 nl_dump(p, " duplicate %d", netem->qnm_duplicate);
177
178 if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR && netem->qnm_corr.nmc_duplicate > 0)
179 nl_dump(p, " %d", netem->qnm_corr.nmc_duplicate);
180 }
181
182 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB && netem->qnm_ro.nmro_probability > 0) {
183 nl_dump(p, " reorder %d", netem->qnm_ro.nmro_probability);
184
185 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR && netem->qnm_ro.nmro_correlation > 0)
186 nl_dump(p, " %d", netem->qnm_ro.nmro_correlation);
187
188 if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0)
189 nl_dump(p, " gap %d", netem->qnm_gap);
190 }
191
192 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB && netem->qnm_crpt.nmcr_probability > 0) {
193 nl_dump(p, " reorder %d", netem->qnm_crpt.nmcr_probability);
194
195 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR && netem->qnm_crpt.nmcr_correlation > 0)
196 nl_dump(p, " %d", netem->qnm_crpt.nmcr_correlation);
197 }
198 }
199}
200
201static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data,
202 struct nl_msg *msg)
203{
204 int err = 0;
205 struct tc_netem_qopt opts;
206 struct tc_netem_corr cor;
207 struct tc_netem_reorder reorder;
208 struct tc_netem_corrupt corrupt;
209 struct rtnl_netem *netem = data;
210
211 unsigned char set_correlation = 0, set_reorder = 0;
212 unsigned char set_corrupt = 0, set_dist = 0;
213
214 struct nlattr* head;
215 struct nlattr* tail;
216 int old_len;
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 head = (struct nlattr *)(((char *) NLMSG_DATA(msg->nm_nlh)) +
323 NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
324
325 tail = (struct nlattr *)(((char *) (msg->nm_nlh)) +
326 NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
327
328 old_len = head->nla_len;
329 head->nla_len = (char *)tail - (char *)head;
330 msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
331
332 return err;
333nla_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 */
348void 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 */
364int 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 */
390void 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 */
406int 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 */
425void 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 */
441int 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 */
460void 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 */
476int 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 */
502void 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 */
518int 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 */
537void 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 */
553int 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 */
579void 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 */
595int 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 */
614void 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 */
630int 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 */
656void 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 */
672int 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 success or a negative error code.
690 */
691void 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 */
707int 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 */
733void 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 */
749int 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 */
768void 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 */
784int 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 */
802void 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 */
818int 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 */
836int 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 */
855int 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 data. Latency/jitter must be set before applying.
871 * @arg qdisc Netem qdisc.
872 * @return 0 on success, error code on failure.
873 */
874int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len) {
875 struct rtnl_netem *netem;
876 int16_t *new_data;
877
878 if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
879 BUG();
880
881 if (len > MAXDIST)
882 return -NLE_INVAL;
883
884 new_data = (int16_t *) calloc(len, sizeof(int16_t));
885 if (!new_data)
886 return -NLE_NOMEM;
887
888 free (netem->qnm_dist.dist_data);
889 netem->qnm_dist.dist_data = new_data;
890
891 memcpy(netem->qnm_dist.dist_data, data, len * sizeof(int16_t));
892
893 netem->qnm_dist.dist_size = len;
894 netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
895
896 return 0;
897}
898
899/**
900 * Load the delay distribution from a file. Latency/jitter must be set before applying.
901 * @arg qdisc Netem qdisc.
902 * @arg dist_type The name of the distribution (type, file, path/file).
903 * @return 0 on success, error code on failure.
904 */
905int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
906 FILE *f;
907 int n = 0;
908 size_t i;
909 size_t len = 2048;
910 _nl_auto_free char *line = NULL;
911 char name[NAME_MAX];
912 char dist_suffix[] = ".dist";
913 _nl_auto_free int16_t *data = NULL;
914 char *test_suffix;
915
916 /* Check several locations for the dist file */
917 char *test_path[] = {
918 "",
919 "./",
920 "/usr/lib/tc/",
921 "/usr/lib64/tc/",
922 "/usr/local/lib/tc/",
923 };
924
925 /* If the given filename already ends in .dist, don't append it later */
926 test_suffix = strstr(dist_type, dist_suffix);
927 if (test_suffix != NULL && strlen(test_suffix) == 5)
928 strcpy(dist_suffix, "");
929
930 for (i = 0; i < ARRAY_SIZE(test_path); i++) {
931 snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
932 if ((f = fopen(name, "re")))
933 break;
934 }
935
936 if (f == NULL)
937 return -nl_syserr2nlerr(errno);
938
939 data = (int16_t *) calloc(MAXDIST, sizeof(int16_t));
940 line = (char *) calloc(sizeof(char), len + 1);
941 if (!data || !line) {
942 fclose(f);
943 return -NLE_NOMEM;
944 }
945
946 while (getline(&line, &len, f) != -1) {
947 char *p, *endp;
948
949 if (*line == '\n' || *line == '#')
950 continue;
951
952 for (p = line; ; p = endp) {
953 long x = strtol(p, &endp, 0);
954 if (endp == p) break;
955
956 if (n >= MAXDIST) {
957 fclose(f);
958 return -NLE_INVAL;
959 }
960 data[n++] = x;
961 }
962 }
963
964 fclose(f);
965 i = rtnl_netem_set_delay_distribution_data(qdisc, data, n);
966 return i;
967}
968
969/** @} */
970
971static struct rtnl_tc_ops netem_ops = {
972 .to_kind = "netem",
973 .to_type = RTNL_TC_TYPE_QDISC,
974 .to_size = sizeof(struct rtnl_netem),
975 .to_msg_parser = netem_msg_parser,
976 .to_free_data = netem_free_data,
977 .to_dump[NL_DUMP_LINE] = netem_dump_line,
978 .to_dump[NL_DUMP_DETAILS] = netem_dump_details,
979 .to_msg_fill_raw = netem_msg_fill_raw,
980};
981
982static void __init netem_init(void)
983{
984 rtnl_tc_register(&netem_ops);
985}
986
987static void __exit netem_exit(void)
988{
989 rtnl_tc_unregister(&netem_ops);
990}
991
992/** @} */
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:236
#define NLA_PUT(msg, attrtype, attrlen, data)
Add unspecific attribute to netlink message.
Definition: attr.h:159
int nla_memcpy(void *dest, const struct nlattr *src, int count)
Copy attribute payload to another memory area.
Definition: attr.c:346
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 rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
Get packet duplication probability of netem qdisc.
Definition: netem.c:672
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
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_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
Set packet delay correlation probability of netem qdisc.
Definition: netem.c:802
int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
Get packet delay correlation probability of netem qdisc.
Definition: netem.c:818
int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
Get packet loss probability of netem qdisc.
Definition: netem.c:595
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_gap(struct rtnl_qdisc *qdisc)
Get re-ordering gap of netem qdisc.
Definition: netem.c:406
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_duplicate_correlation(struct rtnl_qdisc *qdisc)
Get packet duplication correlation probability of netem qdisc.
Definition: netem.c:707
void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
Set packet delay jitter of netem qdisc.
Definition: netem.c:768
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_loss(struct rtnl_qdisc *qdisc, int prob)
Set packet loss probability of netem qdisc.
Definition: netem.c:579
void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
Set limit of netem qdisc.
Definition: netem.c:348
void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
Set re-order correlation probability of netem qdisc.
Definition: netem.c:460
int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
Get corruption probability of netem qdisc.
Definition: netem.c:518
int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
Get packet delay jitter of netem qdisc.
Definition: netem.c:784
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_delay(struct rtnl_qdisc *qdisc)
Get packet delay of netem qdisc.
Definition: netem.c:749
void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
Set re-ordering probability of netem qdisc.
Definition: netem.c:425
void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
Set re-ordering gap of netem qdisc.
Definition: netem.c:390
void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
Set corruption probability of netem qdisc.
Definition: netem.c:502
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_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type)
Load the delay distribution from a file.
Definition: netem.c:905
void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
Set packet delay of netem qdisc.
Definition: netem.c:733
int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
Get packet loss correlation probability of netem qdisc.
Definition: netem.c:630
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:874
int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
Get limit of netem qdisc.
Definition: netem.c:364
#define TC_CAST(ptr)
Macro to cast qdisc/class/classifier to tc object.
Definition: tc.h:50
void * rtnl_tc_data(struct rtnl_tc *tc)
Return pointer to private data of traffic control object.
Definition: tc.c:1076
int rtnl_tc_register(struct rtnl_tc_ops *ops)
Register a traffic control module.
Definition: tc.c:1015
void rtnl_tc_unregister(struct rtnl_tc_ops *ops)
Unregister a traffic control module.
Definition: tc.c:1049
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition: utils.c:955
uint32_t nl_ticks2us(uint32_t ticks)
Convert ticks to micro seconds.
Definition: utils.c:534
uint32_t nl_us2ticks(uint32_t us)
Convert micro seconds to ticks.
Definition: utils.c:522
char * nl_msec2str(uint64_t msec, char *buf, size_t len)
Convert milliseconds to a character string.
Definition: utils.c:588
@ NL_DUMP_LINE
Dump object briefly on one line.
Definition: types.h:16
@ NL_DUMP_DETAILS
Dump all attributes but no statistics.
Definition: types.h:17
Dumping parameters.
Definition: types.h:28
Attribute validation policy.
Definition: attr.h:63
uint16_t minlen
Minimal length of payload required.
Definition: attr.h:68