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