• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
42 static 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 
netem_msg_parser(struct rtnl_tc * tc,void * data)48 static 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 
netem_free_data(struct rtnl_tc * tc,void * data)125 static 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 
netem_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)135 static 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 
netem_dump_details(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)148 static 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 
netem_msg_fill_raw(struct rtnl_tc * tc,void * data,struct nl_msg * msg)201 static 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;
333 nla_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  */
rtnl_netem_set_limit(struct rtnl_qdisc * qdisc,int limit)348 void 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  */
rtnl_netem_get_limit(struct rtnl_qdisc * qdisc)364 int 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  */
rtnl_netem_set_gap(struct rtnl_qdisc * qdisc,int gap)390 void 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  */
rtnl_netem_get_gap(struct rtnl_qdisc * qdisc)406 int 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  */
rtnl_netem_set_reorder_probability(struct rtnl_qdisc * qdisc,int prob)425 void 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  */
rtnl_netem_get_reorder_probability(struct rtnl_qdisc * qdisc)441 int 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  */
rtnl_netem_set_reorder_correlation(struct rtnl_qdisc * qdisc,int prob)460 void 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  */
rtnl_netem_get_reorder_correlation(struct rtnl_qdisc * qdisc)476 int 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  */
rtnl_netem_set_corruption_probability(struct rtnl_qdisc * qdisc,int prob)502 void 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  */
rtnl_netem_get_corruption_probability(struct rtnl_qdisc * qdisc)518 int 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  */
rtnl_netem_set_corruption_correlation(struct rtnl_qdisc * qdisc,int prob)537 void 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  */
rtnl_netem_get_corruption_correlation(struct rtnl_qdisc * qdisc)553 int 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  */
rtnl_netem_set_loss(struct rtnl_qdisc * qdisc,int prob)579 void 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  */
rtnl_netem_get_loss(struct rtnl_qdisc * qdisc)595 int 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  */
rtnl_netem_set_loss_correlation(struct rtnl_qdisc * qdisc,int prob)614 void 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  */
rtnl_netem_get_loss_correlation(struct rtnl_qdisc * qdisc)630 int 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  */
rtnl_netem_set_duplicate(struct rtnl_qdisc * qdisc,int prob)656 void 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  */
rtnl_netem_get_duplicate(struct rtnl_qdisc * qdisc)672 int 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 sucess or a negative error code.
690  */
rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc * qdisc,int prob)691 void 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  */
rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc * qdisc)707 int 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  */
rtnl_netem_set_delay(struct rtnl_qdisc * qdisc,int delay)733 void 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  */
rtnl_netem_get_delay(struct rtnl_qdisc * qdisc)749 int 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  */
rtnl_netem_set_jitter(struct rtnl_qdisc * qdisc,int jitter)768 void 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  */
rtnl_netem_get_jitter(struct rtnl_qdisc * qdisc)784 int 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  */
rtnl_netem_set_delay_correlation(struct rtnl_qdisc * qdisc,int prob)802 void 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  */
rtnl_netem_get_delay_correlation(struct rtnl_qdisc * qdisc)818 int 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  */
rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc * qdisc)836 int 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  */
rtnl_netem_get_delay_distribution(struct rtnl_qdisc * qdisc,int16_t ** dist_ptr)855 int 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  */
rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc * qdisc,const int16_t * data,size_t len)874 int 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  */
rtnl_netem_set_delay_distribution(struct rtnl_qdisc * qdisc,const char * dist_type)905 int 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 
971 static 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 
netem_init(void)982 static void __init netem_init(void)
983 {
984 	rtnl_tc_register(&netem_ops);
985 }
986 
netem_exit(void)987 static void __exit netem_exit(void)
988 {
989 	rtnl_tc_unregister(&netem_ops);
990 }
991 
992 /** @} */
993