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