libnl 1.1
|
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-2006 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 0x001 00031 #define SCH_NETEM_ATTR_LIMIT 0x002 00032 #define SCH_NETEM_ATTR_LOSS 0x004 00033 #define SCH_NETEM_ATTR_GAP 0x008 00034 #define SCH_NETEM_ATTR_DUPLICATE 0x010 00035 #define SCH_NETEM_ATTR_JITTER 0x020 00036 #define SCH_NETEM_ATTR_DELAY_CORR 0x040 00037 #define SCH_NETEM_ATTR_LOSS_CORR 0x080 00038 #define SCH_NETEM_ATTR_DUP_CORR 0x100 00039 #define SCH_NETEM_ATTR_RO_PROB 0x200 00040 #define SCH_NETEM_ATTR_RO_CORR 0x400 00041 /** @endcond */ 00042 00043 static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc) 00044 { 00045 return (struct rtnl_netem *) qdisc->q_subdata; 00046 } 00047 00048 static inline struct rtnl_netem *netem_alloc(struct rtnl_qdisc *qdisc) 00049 { 00050 if (!qdisc->q_subdata) 00051 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_netem)); 00052 00053 return netem_qdisc(qdisc); 00054 } 00055 00056 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = { 00057 [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) }, 00058 [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) }, 00059 }; 00060 00061 static int netem_msg_parser(struct rtnl_qdisc *qdisc) 00062 { 00063 int len, err = 0; 00064 struct rtnl_netem *netem; 00065 struct tc_netem_qopt *opts; 00066 00067 if (qdisc->q_opts->d_size < sizeof(*opts)) 00068 return nl_error(EINVAL, "Netem specific options size mismatch"); 00069 00070 netem = netem_alloc(qdisc); 00071 if (!netem) 00072 return nl_errno(ENOMEM); 00073 00074 opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data; 00075 netem->qnm_latency = opts->latency; 00076 netem->qnm_limit = opts->limit; 00077 netem->qnm_loss = opts->loss; 00078 netem->qnm_gap = opts->gap; 00079 netem->qnm_duplicate = opts->duplicate; 00080 netem->qnm_jitter = opts->jitter; 00081 00082 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT | 00083 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP | 00084 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER); 00085 00086 len = qdisc->q_opts->d_size - sizeof(*opts); 00087 00088 if (len > 0) { 00089 struct nlattr *tb[TCA_NETEM_MAX+1]; 00090 00091 err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *) 00092 qdisc->q_opts->d_data + sizeof(*opts), 00093 len, netem_policy); 00094 if (err < 0) { 00095 free(netem); 00096 return err; 00097 } 00098 00099 if (tb[TCA_NETEM_CORR]) { 00100 struct tc_netem_corr cor; 00101 00102 nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor)); 00103 netem->qnm_corr.nmc_delay = cor.delay_corr; 00104 netem->qnm_corr.nmc_loss = cor.loss_corr; 00105 netem->qnm_corr.nmc_duplicate = cor.dup_corr; 00106 00107 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR | 00108 SCH_NETEM_ATTR_LOSS_CORR | 00109 SCH_NETEM_ATTR_DELAY_CORR); 00110 } 00111 00112 if (tb[TCA_NETEM_REORDER]) { 00113 struct tc_netem_reorder ro; 00114 00115 nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro)); 00116 netem->qnm_ro.nmro_probability = ro.probability; 00117 netem->qnm_ro.nmro_correlation = ro.correlation; 00118 00119 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB | 00120 SCH_NETEM_ATTR_RO_CORR); 00121 } 00122 } 00123 00124 return 0; 00125 } 00126 00127 static void netem_free_data(struct rtnl_qdisc *qdisc) 00128 { 00129 free(qdisc->q_subdata); 00130 } 00131 00132 static int netem_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, 00133 int line) 00134 { 00135 struct rtnl_netem *netem = netem_qdisc(qdisc); 00136 00137 if (netem) 00138 dp_dump(p, "limit %d", netem->qnm_limit); 00139 00140 return line; 00141 } 00142 00143 static int netem_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, 00144 int line) 00145 { 00146 return line; 00147 } 00148 00149 static struct nl_msg *netem_get_opts(struct rtnl_qdisc *qdisc) 00150 { 00151 return NULL; 00152 } 00153 00154 /** 00155 * @name Queue Limit 00156 * @{ 00157 */ 00158 00159 /** 00160 * Set limit of netem qdisc. 00161 * @arg qdisc Netem qdisc to be modified. 00162 * @arg limit New limit in bytes. 00163 * @return 0 on success or a negative error code. 00164 */ 00165 int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit) 00166 { 00167 struct rtnl_netem *netem; 00168 00169 netem = netem_alloc(qdisc); 00170 if (!netem) 00171 return nl_errno(ENOMEM); 00172 00173 netem->qnm_limit = limit; 00174 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT; 00175 00176 return 0; 00177 } 00178 00179 /** 00180 * Get limit of netem qdisc. 00181 * @arg qdisc Netem qdisc. 00182 * @return Limit in bytes or a negative error code. 00183 */ 00184 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc) 00185 { 00186 struct rtnl_netem *netem; 00187 00188 netem = netem_qdisc(qdisc); 00189 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)) 00190 return netem->qnm_limit; 00191 else 00192 return nl_errno(ENOENT); 00193 } 00194 00195 /** @} */ 00196 00197 /** 00198 * @name Packet Re-ordering 00199 * @{ 00200 */ 00201 00202 /** 00203 * Set re-ordering gap of netem qdisc. 00204 * @arg qdisc Netem qdisc to be modified. 00205 * @arg gap New gap in number of packets. 00206 * @return 0 on success or a negative error code. 00207 */ 00208 int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap) 00209 { 00210 struct rtnl_netem *netem; 00211 00212 netem = netem_alloc(qdisc); 00213 if (!netem) 00214 return nl_errno(ENOMEM); 00215 00216 netem->qnm_gap = gap; 00217 netem->qnm_mask |= SCH_NETEM_ATTR_GAP; 00218 00219 return 0; 00220 } 00221 00222 /** 00223 * Get re-ordering gap of netem qdisc. 00224 * @arg qdisc Netem qdisc. 00225 * @return Re-ordering gap in packets or a negative error code. 00226 */ 00227 int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc) 00228 { 00229 struct rtnl_netem *netem; 00230 00231 netem = netem_qdisc(qdisc); 00232 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP)) 00233 return netem->qnm_gap; 00234 else 00235 return nl_errno(ENOENT); 00236 } 00237 00238 /** 00239 * Set re-ordering probability of netem qdisc. 00240 * @arg qdisc Netem qdisc to be modified. 00241 * @arg prob New re-ordering probability. 00242 * @return 0 on success or a negative error code. 00243 */ 00244 int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob) 00245 { 00246 struct rtnl_netem *netem; 00247 00248 netem = netem_alloc(qdisc); 00249 if (!netem) 00250 return nl_errno(ENOMEM); 00251 00252 netem->qnm_ro.nmro_probability = prob; 00253 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB; 00254 00255 return 0; 00256 } 00257 00258 /** 00259 * Get re-ordering probability of netem qdisc. 00260 * @arg qdisc Netem qdisc. 00261 * @return Re-ordering probability or a negative error code. 00262 */ 00263 int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc) 00264 { 00265 struct rtnl_netem *netem; 00266 00267 netem = netem_qdisc(qdisc); 00268 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)) 00269 return netem->qnm_ro.nmro_probability; 00270 else 00271 return nl_errno(ENOENT); 00272 } 00273 00274 /** 00275 * Set re-order correlation probability of netem qdisc. 00276 * @arg qdisc Netem qdisc to be modified. 00277 * @arg prob New re-ordering correlation probability. 00278 * @return 0 on success or a negative error code. 00279 */ 00280 int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob) 00281 { 00282 struct rtnl_netem *netem; 00283 00284 netem = netem_alloc(qdisc); 00285 if (!netem) 00286 return nl_errno(ENOMEM); 00287 00288 netem->qnm_ro.nmro_correlation = prob; 00289 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR; 00290 00291 return 0; 00292 } 00293 00294 /** 00295 * Get re-ordering correlation probability of netem qdisc. 00296 * @arg qdisc Netem qdisc. 00297 * @return Re-ordering correlation probability or a negative error code. 00298 */ 00299 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc) 00300 { 00301 struct rtnl_netem *netem; 00302 00303 netem = netem_qdisc(qdisc); 00304 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)) 00305 return netem->qnm_ro.nmro_correlation; 00306 else 00307 return nl_errno(ENOENT); 00308 } 00309 00310 /** @} */ 00311 00312 /** 00313 * @name Packet Loss 00314 * @{ 00315 */ 00316 00317 /** 00318 * Set packet loss probability of netem qdisc. 00319 * @arg qdisc Netem qdisc to be modified. 00320 * @arg prob New packet loss probability. 00321 * @return 0 on success or a negative error code. 00322 */ 00323 int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob) 00324 { 00325 struct rtnl_netem *netem; 00326 00327 netem = netem_alloc(qdisc); 00328 if (!netem) 00329 return nl_errno(ENOMEM); 00330 00331 netem->qnm_loss = prob; 00332 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS; 00333 00334 return 0; 00335 } 00336 00337 /** 00338 * Get packet loss probability of netem qdisc. 00339 * @arg qdisc Netem qdisc. 00340 * @return Packet loss probability or a negative error code. 00341 */ 00342 int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc) 00343 { 00344 struct rtnl_netem *netem; 00345 00346 netem = netem_qdisc(qdisc); 00347 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)) 00348 return netem->qnm_loss; 00349 else 00350 return nl_errno(ENOENT); 00351 } 00352 00353 /** 00354 * Set packet loss correlation probability of netem qdisc. 00355 * @arg qdisc Netem qdisc to be modified. 00356 * @arg prob New packet loss correlation. 00357 * @return 0 on success or a negative error code. 00358 */ 00359 int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob) 00360 { 00361 struct rtnl_netem *netem; 00362 00363 netem = netem_alloc(qdisc); 00364 if (!netem) 00365 return nl_errno(ENOMEM); 00366 00367 netem->qnm_corr.nmc_loss = prob; 00368 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR; 00369 00370 return 0; 00371 } 00372 00373 /** 00374 * Get packet loss correlation probability of netem qdisc. 00375 * @arg qdisc Netem qdisc. 00376 * @return Packet loss correlation probability or a negative error code. 00377 */ 00378 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc) 00379 { 00380 struct rtnl_netem *netem; 00381 00382 netem = netem_qdisc(qdisc); 00383 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)) 00384 return netem->qnm_corr.nmc_loss; 00385 else 00386 return nl_errno(ENOENT); 00387 } 00388 00389 /** @} */ 00390 00391 /** 00392 * @name Packet Duplication 00393 * @{ 00394 */ 00395 00396 /** 00397 * Set packet duplication probability of netem qdisc. 00398 * @arg qdisc Netem qdisc to be modified. 00399 * @arg prob New packet duplication probability. 00400 * @return 0 on success or a negative error code. 00401 */ 00402 int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob) 00403 { 00404 struct rtnl_netem *netem; 00405 00406 netem = netem_alloc(qdisc); 00407 if (!netem) 00408 return nl_errno(ENOMEM); 00409 00410 netem->qnm_duplicate = prob; 00411 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE; 00412 00413 return 0; 00414 } 00415 00416 /** 00417 * Get packet duplication probability of netem qdisc. 00418 * @arg qdisc Netem qdisc. 00419 * @return Packet duplication probability or a negative error code. 00420 */ 00421 int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc) 00422 { 00423 struct rtnl_netem *netem; 00424 00425 netem = netem_qdisc(qdisc); 00426 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)) 00427 return netem->qnm_duplicate; 00428 else 00429 return nl_errno(ENOENT); 00430 } 00431 00432 /** 00433 * Set packet duplication correlation probability of netem qdisc. 00434 * @arg qdisc Netem qdisc to be modified. 00435 * @arg prob New packet duplication correlation probability. 00436 * @return 0 on sucess or a negative error code. 00437 */ 00438 int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob) 00439 { 00440 struct rtnl_netem *netem; 00441 00442 netem = netem_alloc(qdisc); 00443 if (!netem) 00444 return nl_errno(ENOMEM); 00445 00446 netem->qnm_corr.nmc_duplicate = prob; 00447 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR; 00448 00449 return 0; 00450 } 00451 00452 /** 00453 * Get packet duplication correlation probability of netem qdisc. 00454 * @arg qdisc Netem qdisc. 00455 * @return Packet duplication correlation probability or a negative error code. 00456 */ 00457 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc) 00458 { 00459 struct rtnl_netem *netem; 00460 00461 netem = netem_qdisc(qdisc); 00462 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)) 00463 return netem->qnm_corr.nmc_duplicate; 00464 else 00465 return nl_errno(ENOENT); 00466 } 00467 00468 /** @} */ 00469 00470 /** 00471 * @name Packet Delay 00472 * @{ 00473 */ 00474 00475 /** 00476 * Set packet delay of netem qdisc. 00477 * @arg qdisc Netem qdisc to be modified. 00478 * @arg delay New packet delay in micro seconds. 00479 * @return 0 on success or a negative error code. 00480 */ 00481 int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay) 00482 { 00483 struct rtnl_netem *netem; 00484 00485 netem = netem_alloc(qdisc); 00486 if (!netem) 00487 return nl_errno(ENOMEM); 00488 00489 netem->qnm_latency = nl_us2ticks(delay); 00490 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY; 00491 00492 return 0; 00493 } 00494 00495 /** 00496 * Get packet delay of netem qdisc. 00497 * @arg qdisc Netem qdisc. 00498 * @return Packet delay in micro seconds or a negative error code. 00499 */ 00500 int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc) 00501 { 00502 struct rtnl_netem *netem; 00503 00504 netem = netem_qdisc(qdisc); 00505 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)) 00506 return nl_ticks2us(netem->qnm_latency); 00507 else 00508 return nl_errno(ENOENT); 00509 } 00510 00511 /** 00512 * Set packet delay jitter of netem qdisc. 00513 * @arg qdisc Netem qdisc to be modified. 00514 * @arg jitter New packet delay jitter in micro seconds. 00515 * @return 0 on success or a negative error code. 00516 */ 00517 int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter) 00518 { 00519 struct rtnl_netem *netem; 00520 00521 netem = netem_alloc(qdisc); 00522 if (!netem) 00523 return nl_errno(ENOMEM); 00524 00525 netem->qnm_jitter = nl_us2ticks(jitter); 00526 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER; 00527 00528 return 0; 00529 } 00530 00531 /** 00532 * Get packet delay jitter of netem qdisc. 00533 * @arg qdisc Netem qdisc. 00534 * @return Packet delay jitter in micro seconds or a negative error code. 00535 */ 00536 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc) 00537 { 00538 struct rtnl_netem *netem; 00539 00540 netem = netem_qdisc(qdisc); 00541 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)) 00542 return nl_ticks2us(netem->qnm_jitter); 00543 else 00544 return nl_errno(ENOENT); 00545 } 00546 00547 /** 00548 * Set packet delay correlation probability of netem qdisc. 00549 * @arg qdisc Netem qdisc to be modified. 00550 * @arg prob New packet delay correlation probability. 00551 */ 00552 int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob) 00553 { 00554 struct rtnl_netem *netem; 00555 00556 netem = netem_alloc(qdisc); 00557 if (!netem) 00558 return nl_errno(ENOMEM); 00559 00560 netem->qnm_corr.nmc_delay = prob; 00561 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR; 00562 00563 return 0; 00564 } 00565 00566 /** 00567 * Get packet delay correlation probability of netem qdisc. 00568 * @arg qdisc Netem qdisc. 00569 * @return Packet delay correlation probability or a negative error code. 00570 */ 00571 int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc) 00572 { 00573 struct rtnl_netem *netem; 00574 00575 netem = netem_qdisc(qdisc); 00576 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)) 00577 return netem->qnm_corr.nmc_delay; 00578 else 00579 return nl_errno(ENOENT); 00580 } 00581 00582 /** @} */ 00583 00584 static struct rtnl_qdisc_ops netem_ops = { 00585 .qo_kind = "netem", 00586 .qo_msg_parser = netem_msg_parser, 00587 .qo_free_data = netem_free_data, 00588 .qo_dump[NL_DUMP_BRIEF] = netem_dump_brief, 00589 .qo_dump[NL_DUMP_FULL] = netem_dump_full, 00590 .qo_get_opts = netem_get_opts, 00591 }; 00592 00593 static void __init netem_init(void) 00594 { 00595 rtnl_qdisc_register(&netem_ops); 00596 } 00597 00598 static void __exit netem_exit(void) 00599 { 00600 rtnl_qdisc_unregister(&netem_ops); 00601 } 00602 00603 /** @} */