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