libnl 2.0

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