netem.c

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