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