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