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