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