libnl 3.0
|
00001 /* 00002 * lib/route/qdisc/netem.c Network Emulator Qdisc 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup qdisc 00014 * @defgroup qdisc_netem Network Emulator 00015 * @brief 00016 * 00017 * For further documentation see http://linux-net.osdl.org/index.php/Netem 00018 * @{ 00019 */ 00020 00021 #include <netlink-local.h> 00022 #include <netlink-tc.h> 00023 #include <netlink/netlink.h> 00024 #include <netlink/utils.h> 00025 #include <netlink/route/tc-api.h> 00026 #include <netlink/route/qdisc.h> 00027 #include <netlink/route/qdisc/netem.h> 00028 00029 /** @cond SKIP */ 00030 #define SCH_NETEM_ATTR_LATENCY 0x0001 00031 #define SCH_NETEM_ATTR_LIMIT 0x0002 00032 #define SCH_NETEM_ATTR_LOSS 0x0004 00033 #define SCH_NETEM_ATTR_GAP 0x0008 00034 #define SCH_NETEM_ATTR_DUPLICATE 0x0010 00035 #define SCH_NETEM_ATTR_JITTER 0x0020 00036 #define SCH_NETEM_ATTR_DELAY_CORR 0x0040 00037 #define SCH_NETEM_ATTR_LOSS_CORR 0x0080 00038 #define SCH_NETEM_ATTR_DUP_CORR 0x0100 00039 #define SCH_NETEM_ATTR_RO_PROB 0x0200 00040 #define SCH_NETEM_ATTR_RO_CORR 0x0400 00041 #define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800 00042 #define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000 00043 #define SCH_NETEM_ATTR_DIST 0x2000 00044 /** @endcond */ 00045 00046 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = { 00047 [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) }, 00048 [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) }, 00049 [TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) }, 00050 }; 00051 00052 static int netem_msg_parser(struct rtnl_tc *tc, void *data) 00053 { 00054 struct rtnl_netem *netem = data; 00055 struct tc_netem_qopt *opts; 00056 int len, err = 0; 00057 00058 if (tc->tc_opts->d_size < sizeof(*opts)) 00059 return -NLE_INVAL; 00060 00061 opts = (struct tc_netem_qopt *) tc->tc_opts->d_data; 00062 netem->qnm_latency = opts->latency; 00063 netem->qnm_limit = opts->limit; 00064 netem->qnm_loss = opts->loss; 00065 netem->qnm_gap = opts->gap; 00066 netem->qnm_duplicate = opts->duplicate; 00067 netem->qnm_jitter = opts->jitter; 00068 00069 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT | 00070 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP | 00071 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER); 00072 00073 len = tc->tc_opts->d_size - sizeof(*opts); 00074 00075 if (len > 0) { 00076 struct nlattr *tb[TCA_NETEM_MAX+1]; 00077 00078 err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *) 00079 (tc->tc_opts->d_data + sizeof(*opts)), 00080 len, netem_policy); 00081 if (err < 0) { 00082 free(netem); 00083 return err; 00084 } 00085 00086 if (tb[TCA_NETEM_CORR]) { 00087 struct tc_netem_corr cor; 00088 00089 nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor)); 00090 netem->qnm_corr.nmc_delay = cor.delay_corr; 00091 netem->qnm_corr.nmc_loss = cor.loss_corr; 00092 netem->qnm_corr.nmc_duplicate = cor.dup_corr; 00093 00094 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR | 00095 SCH_NETEM_ATTR_LOSS_CORR | 00096 SCH_NETEM_ATTR_DUP_CORR); 00097 } 00098 00099 if (tb[TCA_NETEM_REORDER]) { 00100 struct tc_netem_reorder ro; 00101 00102 nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro)); 00103 netem->qnm_ro.nmro_probability = ro.probability; 00104 netem->qnm_ro.nmro_correlation = ro.correlation; 00105 00106 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB | 00107 SCH_NETEM_ATTR_RO_CORR); 00108 } 00109 00110 if (tb[TCA_NETEM_CORRUPT]) { 00111 struct tc_netem_corrupt corrupt; 00112 00113 nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt)); 00114 netem->qnm_crpt.nmcr_probability = corrupt.probability; 00115 netem->qnm_crpt.nmcr_correlation = corrupt.correlation; 00116 00117 netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB | 00118 SCH_NETEM_ATTR_CORRUPT_CORR); 00119 } 00120 00121 /* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */ 00122 netem->qnm_dist.dist_data = NULL; 00123 netem->qnm_dist.dist_size = 0; 00124 } 00125 00126 return 0; 00127 } 00128 00129 static void netem_free_data(struct rtnl_tc *tc, void *data) 00130 { 00131 struct rtnl_netem *netem = data; 00132 00133 if (!netem) 00134 return; 00135 00136 free(netem->qnm_dist.dist_data); 00137 } 00138 00139 static void netem_dump_line(struct rtnl_tc *tc, void *data, 00140 struct nl_dump_params *p) 00141 { 00142 struct rtnl_netem *netem = data; 00143 00144 if (netem) 00145 nl_dump(p, "limit %d", netem->qnm_limit); 00146 } 00147 00148 int netem_msg_fill_raw(struct rtnl_tc *tc, void *data, struct nl_msg *msg) 00149 { 00150 int err = 0; 00151 struct tc_netem_qopt opts; 00152 struct tc_netem_corr cor; 00153 struct tc_netem_reorder reorder; 00154 struct tc_netem_corrupt corrupt; 00155 struct rtnl_netem *netem = data; 00156 00157 unsigned char set_correlation = 0, set_reorder = 0, 00158 set_corrupt = 0, set_dist = 0; 00159 00160 if (!netem) 00161 BUG(); 00162 00163 memset(&opts, 0, sizeof(opts)); 00164 memset(&cor, 0, sizeof(cor)); 00165 memset(&reorder, 0, sizeof(reorder)); 00166 memset(&corrupt, 0, sizeof(corrupt)); 00167 00168 msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST; 00169 00170 if ( netem->qnm_ro.nmro_probability != 0 ) { 00171 if (netem->qnm_latency == 0) { 00172 return -NLE_MISSING_ATTR; 00173 } 00174 if (netem->qnm_gap == 0) netem->qnm_gap = 1; 00175 } 00176 else if ( netem->qnm_gap ) { 00177 return -NLE_MISSING_ATTR; 00178 } 00179 00180 if ( netem->qnm_corr.nmc_delay != 0 ) { 00181 if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) { 00182 return -NLE_MISSING_ATTR; 00183 } 00184 set_correlation = 1; 00185 } 00186 00187 if ( netem->qnm_corr.nmc_loss != 0 ) { 00188 if ( netem->qnm_loss == 0 ) { 00189 return -NLE_MISSING_ATTR; 00190 } 00191 set_correlation = 1; 00192 } 00193 00194 if ( netem->qnm_corr.nmc_duplicate != 0 ) { 00195 if ( netem->qnm_duplicate == 0 ) { 00196 return -NLE_MISSING_ATTR; 00197 } 00198 set_correlation = 1; 00199 } 00200 00201 if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1; 00202 else if ( netem->qnm_ro.nmro_correlation != 0 ) { 00203 return -NLE_MISSING_ATTR; 00204 } 00205 00206 if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1; 00207 else if ( netem->qnm_crpt.nmcr_correlation != 0 ) { 00208 return -NLE_MISSING_ATTR; 00209 } 00210 00211 if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) { 00212 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) { 00213 return -NLE_MISSING_ATTR; 00214 } 00215 else { 00216 /* Resize to accomodate the large distribution table */ 00217 int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size * 00218 sizeof(netem->qnm_dist.dist_data[0]); 00219 00220 msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len); 00221 if ( msg->nm_nlh == NULL ) 00222 return -NLE_NOMEM; 00223 msg->nm_size = new_msg_len; 00224 set_dist = 1; 00225 } 00226 } 00227 00228 opts.latency = netem->qnm_latency; 00229 opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000; 00230 opts.loss = netem->qnm_loss; 00231 opts.gap = netem->qnm_gap; 00232 opts.duplicate = netem->qnm_duplicate; 00233 opts.jitter = netem->qnm_jitter; 00234 00235 NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts); 00236 00237 if ( set_correlation ) { 00238 cor.delay_corr = netem->qnm_corr.nmc_delay; 00239 cor.loss_corr = netem->qnm_corr.nmc_loss; 00240 cor.dup_corr = netem->qnm_corr.nmc_duplicate; 00241 00242 NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor); 00243 } 00244 00245 if ( set_reorder ) { 00246 reorder.probability = netem->qnm_ro.nmro_probability; 00247 reorder.correlation = netem->qnm_ro.nmro_correlation; 00248 00249 NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder); 00250 } 00251 00252 if ( set_corrupt ) { 00253 corrupt.probability = netem->qnm_crpt.nmcr_probability; 00254 corrupt.correlation = netem->qnm_crpt.nmcr_correlation; 00255 00256 NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); 00257 } 00258 00259 if ( set_dist ) { 00260 NLA_PUT(msg, TCA_NETEM_DELAY_DIST, 00261 netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]), 00262 netem->qnm_dist.dist_data); 00263 } 00264 00265 /* Length specified in the TCA_OPTIONS section must span the entire 00266 * remainder of the message. That's just the way that sch_netem expects it. 00267 * Maybe there's a more succinct way to do this at a higher level. 00268 */ 00269 struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) + 00270 NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO); 00271 00272 struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) + 00273 NLMSG_ALIGN(msg->nm_nlh->nlmsg_len)); 00274 00275 int old_len = head->nla_len; 00276 head->nla_len = (void *)tail - (void *)head; 00277 msg->nm_nlh->nlmsg_len += (head->nla_len - old_len); 00278 00279 return err; 00280 nla_put_failure: 00281 return -NLE_MSGSIZE; 00282 } 00283 00284 /** 00285 * @name Queue Limit 00286 * @{ 00287 */ 00288 00289 /** 00290 * Set limit of netem qdisc. 00291 * @arg qdisc Netem qdisc to be modified. 00292 * @arg limit New limit in bytes. 00293 * @return 0 on success or a negative error code. 00294 */ 00295 void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit) 00296 { 00297 struct rtnl_netem *netem; 00298 00299 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00300 BUG(); 00301 00302 netem->qnm_limit = limit; 00303 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT; 00304 } 00305 00306 /** 00307 * Get limit of netem qdisc. 00308 * @arg qdisc Netem qdisc. 00309 * @return Limit in bytes or a negative error code. 00310 */ 00311 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc) 00312 { 00313 struct rtnl_netem *netem; 00314 00315 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00316 return -NLE_NOMEM; 00317 00318 if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT) 00319 return netem->qnm_limit; 00320 else 00321 return -NLE_NOATTR; 00322 } 00323 00324 /** @} */ 00325 00326 /** 00327 * @name Packet Re-ordering 00328 * @{ 00329 */ 00330 00331 /** 00332 * Set re-ordering gap of netem qdisc. 00333 * @arg qdisc Netem qdisc to be modified. 00334 * @arg gap New gap in number of packets. 00335 * @return 0 on success or a negative error code. 00336 */ 00337 void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap) 00338 { 00339 struct rtnl_netem *netem; 00340 00341 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00342 BUG(); 00343 00344 netem->qnm_gap = gap; 00345 netem->qnm_mask |= SCH_NETEM_ATTR_GAP; 00346 } 00347 00348 /** 00349 * Get re-ordering gap of netem qdisc. 00350 * @arg qdisc Netem qdisc. 00351 * @return Re-ordering gap in packets or a negative error code. 00352 */ 00353 int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc) 00354 { 00355 struct rtnl_netem *netem; 00356 00357 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00358 return -NLE_NOMEM; 00359 00360 if (netem->qnm_mask & SCH_NETEM_ATTR_GAP) 00361 return netem->qnm_gap; 00362 else 00363 return -NLE_NOATTR; 00364 } 00365 00366 /** 00367 * Set re-ordering probability of netem qdisc. 00368 * @arg qdisc Netem qdisc to be modified. 00369 * @arg prob New re-ordering probability. 00370 * @return 0 on success or a negative error code. 00371 */ 00372 void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob) 00373 { 00374 struct rtnl_netem *netem; 00375 00376 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00377 BUG(); 00378 00379 netem->qnm_ro.nmro_probability = prob; 00380 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB; 00381 } 00382 00383 /** 00384 * Get re-ordering probability of netem qdisc. 00385 * @arg qdisc Netem qdisc. 00386 * @return Re-ordering probability or a negative error code. 00387 */ 00388 int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc) 00389 { 00390 struct rtnl_netem *netem; 00391 00392 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00393 return -NLE_NOMEM; 00394 00395 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB) 00396 return netem->qnm_ro.nmro_probability; 00397 else 00398 return -NLE_NOATTR; 00399 } 00400 00401 /** 00402 * Set re-order correlation probability of netem qdisc. 00403 * @arg qdisc Netem qdisc to be modified. 00404 * @arg prob New re-ordering correlation probability. 00405 * @return 0 on success or a negative error code. 00406 */ 00407 void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob) 00408 { 00409 struct rtnl_netem *netem; 00410 00411 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00412 BUG(); 00413 00414 netem->qnm_ro.nmro_correlation = prob; 00415 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR; 00416 } 00417 00418 /** 00419 * Get re-ordering correlation probability of netem qdisc. 00420 * @arg qdisc Netem qdisc. 00421 * @return Re-ordering correlation probability or a negative error code. 00422 */ 00423 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc) 00424 { 00425 struct rtnl_netem *netem; 00426 00427 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00428 return -NLE_NOMEM; 00429 00430 if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR) 00431 return netem->qnm_ro.nmro_correlation; 00432 else 00433 return -NLE_NOATTR; 00434 } 00435 00436 /** @} */ 00437 00438 /** 00439 * @name Corruption 00440 * @{ 00441 */ 00442 00443 /** 00444 * Set corruption probability of netem qdisc. 00445 * @arg qdisc Netem qdisc to be modified. 00446 * @arg prob New corruption probability. 00447 * @return 0 on success or a negative error code. 00448 */ 00449 void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob) 00450 { 00451 struct rtnl_netem *netem; 00452 00453 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00454 BUG(); 00455 00456 netem->qnm_crpt.nmcr_probability = prob; 00457 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB; 00458 } 00459 00460 /** 00461 * Get corruption probability of netem qdisc. 00462 * @arg qdisc Netem qdisc. 00463 * @return Corruption probability or a negative error code. 00464 */ 00465 int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc) 00466 { 00467 struct rtnl_netem *netem; 00468 00469 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00470 BUG(); 00471 00472 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB) 00473 return netem->qnm_crpt.nmcr_probability; 00474 else 00475 return -NLE_NOATTR; 00476 } 00477 00478 /** 00479 * Set corruption correlation probability of netem qdisc. 00480 * @arg qdisc Netem qdisc to be modified. 00481 * @arg prob New corruption correlation probability. 00482 * @return 0 on success or a negative error code. 00483 */ 00484 void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob) 00485 { 00486 struct rtnl_netem *netem; 00487 00488 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00489 BUG(); 00490 00491 netem->qnm_crpt.nmcr_correlation = prob; 00492 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR; 00493 } 00494 00495 /** 00496 * Get corruption correlation probability of netem qdisc. 00497 * @arg qdisc Netem qdisc. 00498 * @return Corruption correlation probability or a negative error code. 00499 */ 00500 int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc) 00501 { 00502 struct rtnl_netem *netem; 00503 00504 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00505 BUG(); 00506 00507 if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR) 00508 return netem->qnm_crpt.nmcr_correlation; 00509 else 00510 return -NLE_NOATTR; 00511 } 00512 00513 /** @} */ 00514 00515 /** 00516 * @name Packet Loss 00517 * @{ 00518 */ 00519 00520 /** 00521 * Set packet loss probability of netem qdisc. 00522 * @arg qdisc Netem qdisc to be modified. 00523 * @arg prob New packet loss probability. 00524 * @return 0 on success or a negative error code. 00525 */ 00526 void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob) 00527 { 00528 struct rtnl_netem *netem; 00529 00530 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00531 BUG(); 00532 00533 netem->qnm_loss = prob; 00534 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS; 00535 } 00536 00537 /** 00538 * Get packet loss probability of netem qdisc. 00539 * @arg qdisc Netem qdisc. 00540 * @return Packet loss probability or a negative error code. 00541 */ 00542 int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc) 00543 { 00544 struct rtnl_netem *netem; 00545 00546 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00547 BUG(); 00548 00549 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS) 00550 return netem->qnm_loss; 00551 else 00552 return -NLE_NOATTR; 00553 } 00554 00555 /** 00556 * Set packet loss correlation probability of netem qdisc. 00557 * @arg qdisc Netem qdisc to be modified. 00558 * @arg prob New packet loss correlation. 00559 * @return 0 on success or a negative error code. 00560 */ 00561 void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob) 00562 { 00563 struct rtnl_netem *netem; 00564 00565 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00566 BUG(); 00567 00568 netem->qnm_corr.nmc_loss = prob; 00569 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR; 00570 } 00571 00572 /** 00573 * Get packet loss correlation probability of netem qdisc. 00574 * @arg qdisc Netem qdisc. 00575 * @return Packet loss correlation probability or a negative error code. 00576 */ 00577 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc) 00578 { 00579 struct rtnl_netem *netem; 00580 00581 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00582 BUG(); 00583 00584 if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR) 00585 return netem->qnm_corr.nmc_loss; 00586 else 00587 return -NLE_NOATTR; 00588 } 00589 00590 /** @} */ 00591 00592 /** 00593 * @name Packet Duplication 00594 * @{ 00595 */ 00596 00597 /** 00598 * Set packet duplication probability of netem qdisc. 00599 * @arg qdisc Netem qdisc to be modified. 00600 * @arg prob New packet duplication probability. 00601 * @return 0 on success or a negative error code. 00602 */ 00603 void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob) 00604 { 00605 struct rtnl_netem *netem; 00606 00607 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00608 BUG(); 00609 00610 netem->qnm_duplicate = prob; 00611 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE; 00612 } 00613 00614 /** 00615 * Get packet duplication probability of netem qdisc. 00616 * @arg qdisc Netem qdisc. 00617 * @return Packet duplication probability or a negative error code. 00618 */ 00619 int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc) 00620 { 00621 struct rtnl_netem *netem; 00622 00623 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00624 BUG(); 00625 00626 if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE) 00627 return netem->qnm_duplicate; 00628 else 00629 return -NLE_NOATTR; 00630 } 00631 00632 /** 00633 * Set packet duplication correlation probability of netem qdisc. 00634 * @arg qdisc Netem qdisc to be modified. 00635 * @arg prob New packet duplication correlation probability. 00636 * @return 0 on sucess or a negative error code. 00637 */ 00638 void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob) 00639 { 00640 struct rtnl_netem *netem; 00641 00642 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00643 BUG(); 00644 00645 netem->qnm_corr.nmc_duplicate = prob; 00646 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR; 00647 } 00648 00649 /** 00650 * Get packet duplication correlation probability of netem qdisc. 00651 * @arg qdisc Netem qdisc. 00652 * @return Packet duplication correlation probability or a negative error code. 00653 */ 00654 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc) 00655 { 00656 struct rtnl_netem *netem; 00657 00658 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00659 BUG(); 00660 00661 if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR) 00662 return netem->qnm_corr.nmc_duplicate; 00663 else 00664 return -NLE_NOATTR; 00665 } 00666 00667 /** @} */ 00668 00669 /** 00670 * @name Packet Delay 00671 * @{ 00672 */ 00673 00674 /** 00675 * Set packet delay of netem qdisc. 00676 * @arg qdisc Netem qdisc to be modified. 00677 * @arg delay New packet delay in micro seconds. 00678 * @return 0 on success or a negative error code. 00679 */ 00680 void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay) 00681 { 00682 struct rtnl_netem *netem; 00683 00684 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00685 BUG(); 00686 00687 netem->qnm_latency = nl_us2ticks(delay); 00688 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY; 00689 } 00690 00691 /** 00692 * Get packet delay of netem qdisc. 00693 * @arg qdisc Netem qdisc. 00694 * @return Packet delay in micro seconds or a negative error code. 00695 */ 00696 int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc) 00697 { 00698 struct rtnl_netem *netem; 00699 00700 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00701 BUG(); 00702 00703 if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY) 00704 return nl_ticks2us(netem->qnm_latency); 00705 else 00706 return -NLE_NOATTR; 00707 } 00708 00709 /** 00710 * Set packet delay jitter of netem qdisc. 00711 * @arg qdisc Netem qdisc to be modified. 00712 * @arg jitter New packet delay jitter in micro seconds. 00713 * @return 0 on success or a negative error code. 00714 */ 00715 void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter) 00716 { 00717 struct rtnl_netem *netem; 00718 00719 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00720 BUG(); 00721 00722 netem->qnm_jitter = nl_us2ticks(jitter); 00723 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER; 00724 } 00725 00726 /** 00727 * Get packet delay jitter of netem qdisc. 00728 * @arg qdisc Netem qdisc. 00729 * @return Packet delay jitter in micro seconds or a negative error code. 00730 */ 00731 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc) 00732 { 00733 struct rtnl_netem *netem; 00734 00735 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00736 BUG(); 00737 00738 if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER) 00739 return nl_ticks2us(netem->qnm_jitter); 00740 else 00741 return -NLE_NOATTR; 00742 } 00743 00744 /** 00745 * Set packet delay correlation probability of netem qdisc. 00746 * @arg qdisc Netem qdisc to be modified. 00747 * @arg prob New packet delay correlation probability. 00748 */ 00749 void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob) 00750 { 00751 struct rtnl_netem *netem; 00752 00753 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00754 BUG(); 00755 00756 netem->qnm_corr.nmc_delay = prob; 00757 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR; 00758 } 00759 00760 /** 00761 * Get packet delay correlation probability of netem qdisc. 00762 * @arg qdisc Netem qdisc. 00763 * @return Packet delay correlation probability or a negative error code. 00764 */ 00765 int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc) 00766 { 00767 struct rtnl_netem *netem; 00768 00769 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00770 BUG(); 00771 00772 if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR) 00773 return netem->qnm_corr.nmc_delay; 00774 else 00775 return -NLE_NOATTR; 00776 } 00777 00778 /** 00779 * Get the size of the distribution table. 00780 * @arg qdisc Netem qdisc. 00781 * @return Distribution table size or a negative error code. 00782 */ 00783 int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc) 00784 { 00785 struct rtnl_netem *netem; 00786 00787 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00788 BUG(); 00789 00790 if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) 00791 return netem->qnm_dist.dist_size; 00792 else 00793 return -NLE_NOATTR; 00794 } 00795 00796 /** 00797 * Get a pointer to the distribution table. 00798 * @arg qdisc Netem qdisc. 00799 * @arg dist_ptr The pointer to set. 00800 * @return Negative error code on failure or 0 on success. 00801 */ 00802 int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr) 00803 { 00804 struct rtnl_netem *netem; 00805 00806 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00807 BUG(); 00808 00809 if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) { 00810 *dist_ptr = netem->qnm_dist.dist_data; 00811 return 0; 00812 } else 00813 return -NLE_NOATTR; 00814 } 00815 00816 /** 00817 * Set the delay distribution. Latency/jitter must be set before applying. 00818 * @arg qdisc Netem qdisc. 00819 * @arg dist_type The name of the distribution (type, file, path/file). 00820 * @return 0 on success, error code on failure. 00821 */ 00822 int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) { 00823 struct rtnl_netem *netem; 00824 00825 if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) 00826 BUG(); 00827 00828 FILE *f = NULL; 00829 int i, n = 0; 00830 size_t len = 2048; 00831 char *line; 00832 char name[NAME_MAX]; 00833 char dist_suffix[] = ".dist"; 00834 00835 /* If the given filename already ends in .dist, don't append it later */ 00836 char *test_suffix = strstr(dist_type, dist_suffix); 00837 if (test_suffix != NULL && strlen(test_suffix) == 5) 00838 strcpy(dist_suffix, ""); 00839 00840 /* Check several locations for the dist file */ 00841 char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" }; 00842 00843 for (i = 0; i < sizeof(test_path) && f == NULL; i++) { 00844 snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix); 00845 f = fopen(name, "r"); 00846 } 00847 00848 if ( f == NULL ) 00849 return -nl_syserr2nlerr(errno); 00850 00851 netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t)); 00852 00853 line = (char *) calloc (sizeof(char), len + 1); 00854 00855 while (getline(&line, &len, f) != -1) { 00856 char *p, *endp; 00857 00858 if (*line == '\n' || *line == '#') 00859 continue; 00860 00861 for (p = line; ; p = endp) { 00862 long x = strtol(p, &endp, 0); 00863 if (endp == p) break; 00864 00865 if (n >= MAXDIST) { 00866 free(line); 00867 fclose(f); 00868 return -NLE_INVAL; 00869 } 00870 netem->qnm_dist.dist_data[n++] = x; 00871 } 00872 } 00873 00874 free(line); 00875 00876 netem->qnm_dist.dist_size = n; 00877 netem->qnm_mask |= SCH_NETEM_ATTR_DIST; 00878 00879 fclose(f); 00880 return 0; 00881 } 00882 00883 /** @} */ 00884 00885 static struct rtnl_tc_ops netem_ops = { 00886 .to_kind = "netem", 00887 .to_type = RTNL_TC_TYPE_QDISC, 00888 .to_size = sizeof(struct rtnl_netem), 00889 .to_msg_parser = netem_msg_parser, 00890 .to_free_data = netem_free_data, 00891 .to_dump[NL_DUMP_LINE] = netem_dump_line, 00892 .to_msg_fill_raw = netem_msg_fill_raw, 00893 }; 00894 00895 static void __init netem_init(void) 00896 { 00897 rtnl_tc_register(&netem_ops); 00898 } 00899 00900 static void __exit netem_exit(void) 00901 { 00902 rtnl_tc_unregister(&netem_ops); 00903 } 00904 00905 /** @} */