libnl 1.1

lib/route/sch/netem.c

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 /** @} */