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