netem.c revision 861901c55bd9e2f84e7c8de0da5ea6179867907d
1/*
2 * lib/route/sch/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-2006 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup qdisc_api
14 * @defgroup 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-local.h>
22#include <netlink-tc.h>
23#include <netlink/netlink.h>
24#include <netlink/utils.h>
25#include <netlink/route/qdisc.h>
26#include <netlink/route/qdisc-modules.h>
27#include <netlink/route/sch/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
46static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc)
47{
48	return (struct rtnl_netem *) qdisc->q_subdata;
49}
50
51static inline struct rtnl_netem *netem_alloc(struct rtnl_qdisc *qdisc)
52{
53	if (!qdisc->q_subdata)
54		qdisc->q_subdata = calloc(1, sizeof(struct rtnl_netem));
55
56	return netem_qdisc(qdisc);
57}
58
59static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
60	[TCA_NETEM_CORR]	= { .minlen = sizeof(struct tc_netem_corr) },
61	[TCA_NETEM_REORDER]	= { .minlen = sizeof(struct tc_netem_reorder) },
62	[TCA_NETEM_CORRUPT]	= { .minlen = sizeof(struct tc_netem_corrupt) },
63};
64
65static int netem_msg_parser(struct rtnl_qdisc *qdisc)
66{
67	int len, err = 0;
68	struct rtnl_netem *netem;
69	struct tc_netem_qopt *opts;
70
71	if (qdisc->q_opts->d_size < sizeof(*opts))
72		return nl_error(EINVAL, "Netem specific options size mismatch");
73
74	netem = netem_alloc(qdisc);
75	if (!netem)
76		return nl_errno(ENOMEM);
77
78	opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data;
79	netem->qnm_latency = opts->latency;
80	netem->qnm_limit = opts->limit;
81	netem->qnm_loss = opts->loss;
82	netem->qnm_gap = opts->gap;
83	netem->qnm_duplicate = opts->duplicate;
84	netem->qnm_jitter = opts->jitter;
85
86	netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT |
87			   SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
88			   SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
89
90	len = qdisc->q_opts->d_size - sizeof(*opts);
91
92	if (len > 0) {
93		struct nlattr *tb[TCA_NETEM_MAX+1];
94
95		err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
96				(qdisc->q_opts->d_data + sizeof(*opts)),
97				len, netem_policy);
98		if (err < 0) {
99			free(netem);
100			return err;
101		}
102
103		if (tb[TCA_NETEM_CORR]) {
104			struct tc_netem_corr cor;
105
106			nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor));
107			netem->qnm_corr.nmc_delay = cor.delay_corr;
108			netem->qnm_corr.nmc_loss = cor.loss_corr;
109			netem->qnm_corr.nmc_duplicate = cor.dup_corr;
110
111			netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
112					    SCH_NETEM_ATTR_LOSS_CORR |
113					SCH_NETEM_ATTR_DUP_CORR);
114		}
115
116		if (tb[TCA_NETEM_REORDER]) {
117			struct tc_netem_reorder ro;
118
119			nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro));
120			netem->qnm_ro.nmro_probability = ro.probability;
121			netem->qnm_ro.nmro_correlation = ro.correlation;
122
123			netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
124					    SCH_NETEM_ATTR_RO_CORR);
125		}
126
127		if (tb[TCA_NETEM_CORRUPT]) {
128			struct tc_netem_corrupt corrupt;
129
130			nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt));
131			netem->qnm_crpt.nmcr_probability = corrupt.probability;
132			netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
133
134			netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
135						SCH_NETEM_ATTR_CORRUPT_CORR);
136		}
137
138		/* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */
139		netem->qnm_dist.dist_data = NULL;
140		netem->qnm_dist.dist_size = 0;
141	}
142
143	return 0;
144}
145
146static void netem_free_data(struct rtnl_qdisc *qdisc)
147{
148	struct rtnl_netem *netem;
149
150	if ( ! qdisc ) return;
151
152	netem = netem_qdisc(qdisc);
153	if ( ! netem ) return;
154
155	if ( netem->qnm_dist.dist_data )
156		free(netem->qnm_dist.dist_data);
157
158	netem = NULL;
159
160	free (qdisc->q_subdata);
161}
162
163static int netem_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
164			    int line)
165{
166	struct rtnl_netem *netem = netem_qdisc(qdisc);
167
168	if (netem)
169		dp_dump(p, "limit %d", netem->qnm_limit);
170
171	return line;
172}
173
174static int netem_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
175			   int line)
176{
177	return line;
178}
179
180int netem_build_msg(struct rtnl_qdisc *qdisc, struct nl_msg *msg)
181{
182	int err = 0;
183	struct tc_netem_qopt opts;
184	struct tc_netem_corr cor;
185	struct tc_netem_reorder reorder;
186	struct tc_netem_corrupt corrupt;
187	struct rtnl_netem *netem;
188
189	unsigned char set_correlation = 0, set_reorder = 0,
190		set_corrupt = 0, set_dist = 0;
191
192	memset(&opts, 0, sizeof(opts));
193	memset(&cor, 0, sizeof(cor));
194	memset(&reorder, 0, sizeof(reorder));
195	memset(&corrupt, 0, sizeof(corrupt));
196
197	netem = netem_qdisc(qdisc);
198	if (!netem || !msg)
199		return EFAULT;
200
201	msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
202
203	if ( netem->qnm_ro.nmro_probability != 0 ) {
204		if (netem->qnm_latency == 0) {
205			return nl_error(EINVAL,
206				"netem: Specified reorder gap without latency.");
207		}
208		if (netem->qnm_gap == 0) netem->qnm_gap = 1;
209	}
210	else if ( netem->qnm_gap ) {
211		return nl_error(EINVAL,
212			"netem: Specified reorder gap without reorder probability.");
213	}
214
215	if ( netem->qnm_corr.nmc_delay != 0 ) {
216		if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
217			return nl_error(EINVAL,
218				"netem: Specified delay correlation without delay size / jitter.");
219		}
220		set_correlation = 1;
221	}
222
223	if ( netem->qnm_corr.nmc_loss != 0 ) {
224		if ( netem->qnm_loss == 0 ) {
225			return nl_error(EINVAL,
226				"netem: Specified loss correlation without loss probability.");
227		}
228		set_correlation = 1;
229	}
230
231	if ( netem->qnm_corr.nmc_duplicate != 0 ) {
232		if ( netem->qnm_duplicate == 0 ) {
233			return nl_error(EINVAL,
234				"netem: Specified dup correlation without duplication probability.");
235		}
236		set_correlation = 1;
237	}
238
239	if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1;
240	else if ( netem->qnm_ro.nmro_correlation != 0 ) {
241			return nl_error(EINVAL,
242				"netem: Specified reorder correlation without reorder probability.");
243	}
244
245	if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1;
246	else if ( netem->qnm_crpt.nmcr_correlation != 0 ) {
247			return nl_error(EINVAL,
248				"netem: Specified corrupt correlation without corrupt probability.");
249	}
250
251	if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) {
252		if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
253		return nl_error(EINVAL,
254			"netem: Distribution specified with empty latency and jitter values.");
255	}
256	else {
257		/* Resize to accomodate the large distribution table */
258		int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
259			sizeof(netem->qnm_dist.dist_data[0]);
260
261		msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len);
262		if ( msg->nm_nlh == NULL )
263			return nl_error(ENOMEM,
264				"netem: Unable to reallocate message size to contain delay distribution data.");
265		msg->nm_size = new_msg_len;
266			set_dist = 1;
267		}
268	}
269
270	opts.latency = netem->qnm_latency;
271	opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
272	opts.loss = netem->qnm_loss;
273	opts.gap = netem->qnm_gap;
274	opts.duplicate = netem->qnm_duplicate;
275	opts.jitter = netem->qnm_jitter;
276
277	err = nla_put(msg, TCA_OPTIONS, sizeof(opts), &opts);
278	if (err)
279		return nl_error(err, "netem: Unable to add TCA_OPTIONS to nl_msg.");
280
281	if ( set_correlation ) {
282		cor.delay_corr = netem->qnm_corr.nmc_delay;
283		cor.loss_corr = netem->qnm_corr.nmc_loss;
284		cor.dup_corr = netem->qnm_corr.nmc_duplicate;
285
286		err = nla_put(msg, TCA_NETEM_CORR, sizeof(cor), &cor);
287		if (err)
288			return nl_error(err, "netem: Unable to add TCA_NETEM_CORR to nl_msg.");
289	}
290
291	if ( set_reorder ) {
292		reorder.probability = netem->qnm_ro.nmro_probability;
293		reorder.correlation = netem->qnm_ro.nmro_correlation;
294
295		err = nla_put(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
296		if (err)
297			return nl_error(err, "netem: Unable to add TCA_NETEM_REORDER to nl_msg.");
298	}
299
300	if ( set_corrupt ) {
301		corrupt.probability = netem->qnm_crpt.nmcr_probability;
302		corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
303
304		err = nla_put(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
305		if (err)
306			return nl_error(err, "netem: Unable to add TCA_NETEM_CORRUPT to nl_msg.");
307	}
308
309	if ( set_dist ) {
310		err = nla_put(msg, TCA_NETEM_DELAY_DIST,
311			netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
312			netem->qnm_dist.dist_data);
313		if (err)
314			return nl_error(err, "netem: Unable to add TCA_NETEM_DELAY_DIST to nl_msg.");
315	}
316
317	/* Length specified in the TCA_OPTIONS section must span the entire
318	 * remainder of the message. That's just the way that sch_netem expects it.
319	 * Maybe there's a more succinct way to do this at a higher level.
320	 */
321	struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) +
322		NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
323
324	struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) +
325		NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
326
327	int old_len = head->nla_len;
328	head->nla_len = (void *)tail - (void *)head;
329	msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
330
331	return err;
332}
333
334/**
335 * @name Queue Limit
336 * @{
337 */
338
339/**
340 * Set limit of netem qdisc.
341 * @arg qdisc		Netem qdisc to be modified.
342 * @arg limit		New limit in bytes.
343 * @return 0 on success or a negative error code.
344 */
345int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
346{
347	struct rtnl_netem *netem;
348
349	netem = netem_alloc(qdisc);
350	if (!netem)
351		return nl_errno(ENOMEM);
352
353	netem->qnm_limit = limit;
354	netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
355
356	return 0;
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	netem = netem_qdisc(qdisc);
369	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT))
370		return netem->qnm_limit;
371	else
372		return nl_errno(ENOENT);
373}
374
375/** @} */
376
377/**
378 * @name Packet Re-ordering
379 * @{
380 */
381
382/**
383 * Set re-ordering gap of netem qdisc.
384 * @arg qdisc		Netem qdisc to be modified.
385 * @arg gap		New gap in number of packets.
386 * @return 0 on success or a negative error code.
387 */
388int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
389{
390	struct rtnl_netem *netem;
391
392	netem = netem_alloc(qdisc);
393	if (!netem)
394		return nl_errno(ENOMEM);
395
396	netem->qnm_gap = gap;
397	netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
398
399	return 0;
400}
401
402/**
403 * Get re-ordering gap of netem qdisc.
404 * @arg qdisc		Netem qdisc.
405 * @return Re-ordering gap in packets or a negative error code.
406 */
407int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
408{
409	struct rtnl_netem *netem;
410
411	netem = netem_qdisc(qdisc);
412	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP))
413		return netem->qnm_gap;
414	else
415		return nl_errno(ENOENT);
416}
417
418/**
419 * Set re-ordering probability of netem qdisc.
420 * @arg qdisc		Netem qdisc to be modified.
421 * @arg prob		New re-ordering probability.
422 * @return 0 on success or a negative error code.
423 */
424int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
425{
426	struct rtnl_netem *netem;
427
428	netem = netem_alloc(qdisc);
429	if (!netem)
430		return nl_errno(ENOMEM);
431
432	netem->qnm_ro.nmro_probability = prob;
433	netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
434
435	return 0;
436}
437
438/**
439 * Get re-ordering probability of netem qdisc.
440 * @arg qdisc		Netem qdisc.
441 * @return Re-ordering probability or a negative error code.
442 */
443int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
444{
445	struct rtnl_netem *netem;
446
447	netem = netem_qdisc(qdisc);
448	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB))
449		return netem->qnm_ro.nmro_probability;
450	else
451		return nl_errno(ENOENT);
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 */
460int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
461{
462	struct rtnl_netem *netem;
463
464	netem = netem_alloc(qdisc);
465	if (!netem)
466		return nl_errno(ENOMEM);
467
468	netem->qnm_ro.nmro_correlation = prob;
469	netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
470
471	return 0;
472}
473
474/**
475 * Get re-ordering correlation probability of netem qdisc.
476 * @arg qdisc		Netem qdisc.
477 * @return Re-ordering correlation probability or a negative error code.
478 */
479int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
480{
481	struct rtnl_netem *netem;
482
483	netem = netem_qdisc(qdisc);
484	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR))
485		return netem->qnm_ro.nmro_correlation;
486	else
487		return nl_errno(ENOENT);
488}
489
490/** @} */
491
492/**
493 * @name Corruption
494 * @{
495 */
496
497/**
498 * Set corruption probability of netem qdisc.
499 * @arg qdisc		Netem qdisc to be modified.
500 * @arg prob		New corruption probability.
501 * @return 0 on success or a negative error code.
502 */
503int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
504{
505	struct rtnl_netem *netem;
506
507	netem = netem_alloc(qdisc);
508	if (!netem)
509		return nl_errno(ENOMEM);
510
511	netem->qnm_crpt.nmcr_probability = prob;
512	netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
513
514	return 0;
515}
516
517/**
518 * Get corruption probability of netem qdisc.
519 * @arg qdisc		Netem qdisc.
520 * @return Corruption probability or a negative error code.
521 */
522int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
523{
524	struct rtnl_netem *netem;
525
526	netem = netem_qdisc(qdisc);
527	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB))
528		return netem->qnm_crpt.nmcr_probability;
529	else
530		return nl_errno(ENOENT);
531}
532
533/**
534 * Set corruption correlation probability of netem qdisc.
535 * @arg qdisc		Netem qdisc to be modified.
536 * @arg prob		New corruption correlation probability.
537 * @return 0 on success or a negative error code.
538 */
539int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
540{
541	struct rtnl_netem *netem;
542
543	netem = netem_alloc(qdisc);
544	if (!netem)
545		return nl_errno(ENOMEM);
546
547	netem->qnm_crpt.nmcr_correlation = prob;
548	netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
549
550	return 0;
551}
552
553/**
554 * Get corruption correlation probability of netem qdisc.
555 * @arg qdisc		Netem qdisc.
556 * @return Corruption correlation probability or a negative error code.
557 */
558int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
559{
560	struct rtnl_netem *netem;
561
562	netem = netem_qdisc(qdisc);
563	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR))
564		return netem->qnm_crpt.nmcr_correlation;
565	else
566		return nl_errno(ENOENT);
567}
568
569/** @} */
570
571/**
572 * @name Packet Loss
573 * @{
574 */
575
576/**
577 * Set packet loss probability of netem qdisc.
578 * @arg qdisc		Netem qdisc to be modified.
579 * @arg prob		New packet loss probability.
580 * @return 0 on success or a negative error code.
581 */
582int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
583{
584	struct rtnl_netem *netem;
585
586	netem = netem_alloc(qdisc);
587	if (!netem)
588		return nl_errno(ENOMEM);
589
590	netem->qnm_loss = prob;
591	netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
592
593	return 0;
594}
595
596/**
597 * Get packet loss probability of netem qdisc.
598 * @arg qdisc		Netem qdisc.
599 * @return Packet loss probability or a negative error code.
600 */
601int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
602{
603	struct rtnl_netem *netem;
604
605	netem = netem_qdisc(qdisc);
606	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS))
607		return netem->qnm_loss;
608	else
609		return nl_errno(ENOENT);
610}
611
612/**
613 * Set packet loss correlation probability of netem qdisc.
614 * @arg qdisc		Netem qdisc to be modified.
615 * @arg prob	New packet loss correlation.
616 * @return 0 on success or a negative error code.
617 */
618int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
619{
620	struct rtnl_netem *netem;
621
622	netem = netem_alloc(qdisc);
623	if (!netem)
624		return nl_errno(ENOMEM);
625
626	netem->qnm_corr.nmc_loss = prob;
627	netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
628
629	return 0;
630}
631
632/**
633 * Get packet loss correlation probability of netem qdisc.
634 * @arg qdisc		Netem qdisc.
635 * @return Packet loss correlation probability or a negative error code.
636 */
637int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
638{
639	struct rtnl_netem *netem;
640
641	netem = netem_qdisc(qdisc);
642	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR))
643		return netem->qnm_corr.nmc_loss;
644	else
645		return nl_errno(ENOENT);
646}
647
648/** @} */
649
650/**
651 * @name Packet Duplication
652 * @{
653 */
654
655/**
656 * Set packet duplication probability of netem qdisc.
657 * @arg qdisc		Netem qdisc to be modified.
658 * @arg prob	New packet duplication probability.
659 * @return 0 on success or a negative error code.
660 */
661int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
662{
663	struct rtnl_netem *netem;
664
665	netem = netem_alloc(qdisc);
666	if (!netem)
667		return nl_errno(ENOMEM);
668
669	netem->qnm_duplicate = prob;
670	netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
671
672	return 0;
673}
674
675/**
676 * Get packet duplication probability of netem qdisc.
677 * @arg qdisc		Netem qdisc.
678 * @return Packet duplication probability or a negative error code.
679 */
680int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
681{
682	struct rtnl_netem *netem;
683
684	netem = netem_qdisc(qdisc);
685	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE))
686		return netem->qnm_duplicate;
687	else
688		return nl_errno(ENOENT);
689}
690
691/**
692 * Set packet duplication correlation probability of netem qdisc.
693 * @arg qdisc		Netem qdisc to be modified.
694 * @arg prob		New packet duplication correlation probability.
695 * @return 0 on sucess or a negative error code.
696 */
697int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
698{
699	struct rtnl_netem *netem;
700
701	netem = netem_alloc(qdisc);
702	if (!netem)
703		return nl_errno(ENOMEM);
704
705	netem->qnm_corr.nmc_duplicate = prob;
706	netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
707
708	return 0;
709}
710
711/**
712 * Get packet duplication correlation probability of netem qdisc.
713 * @arg qdisc		Netem qdisc.
714 * @return Packet duplication correlation probability or a negative error code.
715 */
716int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
717{
718	struct rtnl_netem *netem;
719
720	netem = netem_qdisc(qdisc);
721	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR))
722		return netem->qnm_corr.nmc_duplicate;
723	else
724		return nl_errno(ENOENT);
725}
726
727/** @} */
728
729/**
730 * @name Packet Delay
731 * @{
732 */
733
734/**
735 * Set packet delay of netem qdisc.
736 * @arg qdisc		Netem qdisc to be modified.
737 * @arg delay		New packet delay in micro seconds.
738 * @return 0 on success or a negative error code.
739 */
740int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
741{
742	struct rtnl_netem *netem;
743
744	netem = netem_alloc(qdisc);
745	if (!netem)
746		return nl_errno(ENOMEM);
747
748	netem->qnm_latency = nl_us2ticks(delay);
749	netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
750
751	return 0;
752}
753
754/**
755 * Get packet delay of netem qdisc.
756 * @arg qdisc		Netem qdisc.
757 * @return Packet delay in micro seconds or a negative error code.
758 */
759int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
760{
761	struct rtnl_netem *netem;
762
763	netem = netem_qdisc(qdisc);
764	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY))
765		return nl_ticks2us(netem->qnm_latency);
766	else
767		return nl_errno(ENOENT);
768}
769
770/**
771 * Set packet delay jitter of netem qdisc.
772 * @arg qdisc		Netem qdisc to be modified.
773 * @arg jitter		New packet delay jitter in micro seconds.
774 * @return 0 on success or a negative error code.
775 */
776int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
777{
778	struct rtnl_netem *netem;
779
780	netem = netem_alloc(qdisc);
781	if (!netem)
782		return nl_errno(ENOMEM);
783
784	netem->qnm_jitter = nl_us2ticks(jitter);
785	netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
786
787	return 0;
788}
789
790/**
791 * Get packet delay jitter of netem qdisc.
792 * @arg qdisc		Netem qdisc.
793 * @return Packet delay jitter in micro seconds or a negative error code.
794 */
795int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
796{
797	struct rtnl_netem *netem;
798
799	netem = netem_qdisc(qdisc);
800	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER))
801		return nl_ticks2us(netem->qnm_jitter);
802	else
803		return nl_errno(ENOENT);
804}
805
806/**
807 * Set packet delay correlation probability of netem qdisc.
808 * @arg qdisc		Netem qdisc to be modified.
809 * @arg prob		New packet delay correlation probability.
810 */
811int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
812{
813	struct rtnl_netem *netem;
814
815	netem = netem_alloc(qdisc);
816	if (!netem)
817		return nl_errno(ENOMEM);
818
819	netem->qnm_corr.nmc_delay = prob;
820	netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
821
822	return 0;
823}
824
825/**
826 * Get packet delay correlation probability of netem qdisc.
827 * @arg qdisc		Netem qdisc.
828 * @return Packet delay correlation probability or a negative error code.
829 */
830int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
831{
832	struct rtnl_netem *netem;
833
834	netem = netem_qdisc(qdisc);
835	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR))
836		return netem->qnm_corr.nmc_delay;
837	else
838		return nl_errno(ENOENT);
839}
840
841/**
842 * Get the size of the distribution table.
843 * @arg qdisc		Netem qdisc.
844 * @return Distribution table size or a negative error code.
845 */
846int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
847{
848	struct rtnl_netem *netem;
849
850	netem = netem_qdisc(qdisc);
851	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST))
852		return netem->qnm_dist.dist_size;
853	else
854		return nl_errno(ENOENT);
855}
856
857/**
858 * Get a pointer to the distribution table.
859 * @arg qdisc		Netem qdisc.
860 * @arg dist_ptr	The pointer to set.
861 * @return Negative error code on failure or 0 on success.
862 */
863int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
864{
865	struct rtnl_netem *netem;
866
867	netem = netem_qdisc(qdisc);
868	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) {
869		*dist_ptr = netem->qnm_dist.dist_data;
870		return 0;
871	}
872	else
873		return nl_errno(ENOENT);
874}
875
876/**
877 * Set the delay distribution. Latency/jitter must be set before applying.
878 * @arg qdisc Netem qdisc.
879 * @arg dist_type The name of the distribution (type, file, path/file).
880 * @return 0 on success, error code on failure.
881 */
882int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
883	struct rtnl_netem *netem;
884
885	netem = netem_alloc(qdisc);
886	if (!netem)
887		return nl_errno(ENOMEM);
888
889	FILE *f = NULL;
890	int i, n = 0;
891	size_t len = 2048;
892	char *line;
893	char name[NAME_MAX];
894	char dist_suffix[] = ".dist";
895
896	/* If the given filename already ends in .dist, don't append it later */
897	char *test_suffix = strstr(dist_type, dist_suffix);
898	if (test_suffix != NULL && strlen(test_suffix) == 5)
899		strcpy(dist_suffix, "");
900
901	/* Check several locations for the dist file */
902	char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" };
903
904	for (i = 0; i < sizeof(test_path) && f == NULL; i++) {
905		snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
906		f = fopen(name, "r");
907	}
908
909	if ( f == NULL ) return nl_error(errno, "netem: Unable to open distribution file.");
910
911	netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
912
913	line = (char *) calloc (sizeof(char), len + 1);
914
915	while (getline(&line, &len, f) != -1) {
916		char *p, *endp;
917
918		if (*line == '\n' || *line == '#')
919			continue;
920
921		for (p = line; ; p = endp) {
922			long x = strtol(p, &endp, 0);
923			if (endp == p) break;
924
925			if (n >= MAXDIST) {
926				free(line);
927				fclose(f);
928				return nl_error(EINVAL,	"netem: Distribution file too long.");
929			}
930			netem->qnm_dist.dist_data[n++] = x;
931		}
932	}
933
934	free(line);
935
936	netem->qnm_dist.dist_size = n;
937	netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
938
939	fclose(f);
940	return 0;
941}
942
943/** @} */
944
945static struct rtnl_qdisc_ops netem_ops = {
946	.qo_kind		= "netem",
947	.qo_msg_parser		= netem_msg_parser,
948	.qo_free_data		= netem_free_data,
949	.qo_dump[NL_DUMP_BRIEF]	= netem_dump_brief,
950	.qo_dump[NL_DUMP_FULL]	= netem_dump_full,
951	.qo_get_opts		= 0,
952	.qo_build_msg	= netem_build_msg
953};
954
955static void __init netem_init(void)
956{
957	rtnl_qdisc_register(&netem_ops);
958}
959
960static void __exit netem_exit(void)
961{
962	rtnl_qdisc_unregister(&netem_ops);
963}
964
965/** @} */
966