00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <netlink-local.h>
00020 #include <netlink-tc.h>
00021 #include <netlink/netlink.h>
00022 #include <netlink/utils.h>
00023 #include <netlink/route/rtnl.h>
00024 #include <netlink/route/link.h>
00025 #include <netlink/route/tc.h>
00026
00027
00028
00029 static struct nla_policy tc_policy[TCA_MAX+1] = {
00030 [TCA_KIND] = { .type = NLA_STRING,
00031 .maxlen = TCKINDSIZ },
00032 [TCA_STATS] = { .minlen = sizeof(struct tc_stats) },
00033 [TCA_STATS2] = { .type = NLA_NESTED },
00034 };
00035
00036 int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tca *g,
00037 struct nla_policy *policy)
00038 {
00039
00040 if (g->ce_mask & TCA_ATTR_OPTS)
00041 return nla_parse(tb, maxattr,
00042 (struct nlattr *) g->tc_opts->d_data,
00043 g->tc_opts->d_size, policy);
00044 else {
00045
00046
00047 memset(tb, 0, sizeof(struct nlattr *) * (maxattr + 1));
00048 return 0;
00049 }
00050 }
00051
00052 static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = {
00053 [TCA_STATS_BASIC] = { .minlen = sizeof(struct gnet_stats_basic) },
00054 [TCA_STATS_RATE_EST] = { .minlen = sizeof(struct gnet_stats_rate_est) },
00055 [TCA_STATS_QUEUE] = { .minlen = sizeof(struct gnet_stats_queue) },
00056 };
00057
00058 int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g)
00059 {
00060 struct nlattr *tb[TCA_MAX + 1];
00061 struct tcmsg *tm;
00062 int err;
00063
00064 err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy);
00065 if (err < 0)
00066 return err;
00067
00068 if (tb[TCA_KIND] == NULL)
00069 return nl_error(EINVAL, "Missing tca kind TLV");
00070
00071 nla_strlcpy(g->tc_kind, tb[TCA_KIND], TCKINDSIZ);
00072
00073 tm = nlmsg_data(n);
00074 g->tc_family = tm->tcm_family;
00075 g->tc_ifindex = tm->tcm_ifindex;
00076 g->tc_handle = tm->tcm_handle;
00077 g->tc_parent = tm->tcm_parent;
00078 g->tc_info = tm->tcm_info;
00079
00080 g->ce_mask = (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE |
00081 TCA_ATTR_PARENT | TCA_ATTR_INFO | TCA_ATTR_KIND);
00082
00083 if (tb[TCA_OPTIONS]) {
00084 g->tc_opts = nla_get_data(tb[TCA_OPTIONS]);
00085 if (!g->tc_opts)
00086 return nl_errno(ENOMEM);
00087 g->ce_mask |= TCA_ATTR_OPTS;
00088 }
00089
00090
00091 if (tb[TCA_STATS2]) {
00092 struct nlattr *tbs[TCA_STATS_MAX + 1];
00093
00094 err = nla_parse_nested(tbs, TCA_STATS_MAX, tb[TCA_STATS2],
00095 tc_stats2_policy);
00096 if (err < 0)
00097 return err;
00098
00099 if (tbs[TCA_STATS_BASIC]) {
00100 struct gnet_stats_basic *bs;
00101
00102 bs = nla_data(tbs[TCA_STATS_BASIC]);
00103 g->tc_stats[RTNL_TC_BYTES] = bs->bytes;
00104 g->tc_stats[RTNL_TC_PACKETS] = bs->packets;
00105 }
00106
00107 if (tbs[TCA_STATS_RATE_EST]) {
00108 struct gnet_stats_rate_est *re;
00109
00110 re = nla_data(tbs[TCA_STATS_RATE_EST]);
00111 g->tc_stats[RTNL_TC_RATE_BPS] = re->bps;
00112 g->tc_stats[RTNL_TC_RATE_PPS] = re->pps;
00113 }
00114
00115 if (tbs[TCA_STATS_QUEUE]) {
00116 struct gnet_stats_queue *q;
00117
00118 q = nla_data(tbs[TCA_STATS_QUEUE]);
00119 g->tc_stats[RTNL_TC_QLEN] = q->qlen;
00120 g->tc_stats[RTNL_TC_BACKLOG] = q->backlog;
00121 g->tc_stats[RTNL_TC_DROPS] = q->drops;
00122 g->tc_stats[RTNL_TC_REQUEUES] = q->requeues;
00123 g->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits;
00124 }
00125
00126 g->ce_mask |= TCA_ATTR_STATS;
00127
00128 if (tbs[TCA_STATS_APP]) {
00129 g->tc_xstats = nla_get_data(tbs[TCA_STATS_APP]);
00130 if (g->tc_xstats == NULL)
00131 return -ENOMEM;
00132 } else
00133 goto compat_xstats;
00134 } else {
00135 if (tb[TCA_STATS]) {
00136 struct tc_stats *st = nla_data(tb[TCA_STATS]);
00137
00138 g->tc_stats[RTNL_TC_BYTES] = st->bytes;
00139 g->tc_stats[RTNL_TC_PACKETS] = st->packets;
00140 g->tc_stats[RTNL_TC_RATE_BPS] = st->bps;
00141 g->tc_stats[RTNL_TC_RATE_PPS] = st->pps;
00142 g->tc_stats[RTNL_TC_QLEN] = st->qlen;
00143 g->tc_stats[RTNL_TC_BACKLOG] = st->backlog;
00144 g->tc_stats[RTNL_TC_DROPS] = st->drops;
00145 g->tc_stats[RTNL_TC_OVERLIMITS] = st->overlimits;
00146
00147 g->ce_mask |= TCA_ATTR_STATS;
00148 }
00149
00150 compat_xstats:
00151 if (tb[TCA_XSTATS]) {
00152 g->tc_xstats = nla_get_data(tb[TCA_XSTATS]);
00153 if (g->tc_xstats == NULL)
00154 return -ENOMEM;
00155 g->ce_mask |= TCA_ATTR_XSTATS;
00156 }
00157 }
00158
00159
00160 return 0;
00161 }
00162
00163 void tca_free_data(struct rtnl_tca *tca)
00164 {
00165 nl_data_free(tca->tc_opts);
00166 nl_data_free(tca->tc_xstats);
00167 }
00168
00169 int tca_clone(struct rtnl_tca *dst, struct rtnl_tca *src)
00170 {
00171 if (src->tc_opts) {
00172 dst->tc_opts = nl_data_clone(src->tc_opts);
00173 if (!dst->tc_opts)
00174 goto errout;
00175 }
00176
00177 if (src->tc_xstats) {
00178 dst->tc_xstats = nl_data_clone(src->tc_xstats);
00179 if (!dst->tc_xstats)
00180 goto errout;
00181 }
00182
00183 return 0;
00184 errout:
00185 return nl_get_errno();
00186 }
00187
00188 int tca_dump_brief(struct rtnl_tca *g, const char *type,
00189 struct nl_dump_params *p, int line)
00190 {
00191 char handle[32], parent[32];
00192 struct nl_cache *link_cache;
00193
00194 link_cache = nl_cache_mngt_require("route/link");
00195
00196 dp_dump(p, "%s %s ", g->tc_kind, type);
00197
00198 if (link_cache) {
00199 char buf[32];
00200 dp_dump(p, "dev %s ",
00201 rtnl_link_i2name(link_cache, g->tc_ifindex,
00202 buf, sizeof(buf)));
00203 } else
00204 dp_dump(p, "dev %u ", g->tc_ifindex);
00205
00206 dp_dump(p, "handle %s parent %s",
00207 rtnl_tc_handle2str(g->tc_handle, handle, sizeof(handle)),
00208 rtnl_tc_handle2str(g->tc_parent, parent, sizeof(parent)));
00209
00210 return 1;
00211 }
00212
00213 int tca_dump_full(struct rtnl_tca *g, struct nl_dump_params *p, int line)
00214 {
00215 dp_dump_line(p, line++, " ");
00216 return line;
00217 }
00218
00219 int tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p, int line)
00220 {
00221 char *unit, fmt[64];
00222 float res;
00223 strcpy(fmt, " %7.2f %s %10u %10u %10u %10u %10u\n");
00224
00225 dp_dump_line(p, line++,
00226 " Stats: bytes packets drops overlimits" \
00227 " qlen backlog\n");
00228
00229 res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_BYTES], &unit);
00230 if (*unit == 'B')
00231 fmt[11] = '9';
00232
00233 dp_dump_line(p, line++, fmt, res, unit,
00234 g->tc_stats[RTNL_TC_PACKETS],
00235 g->tc_stats[RTNL_TC_DROPS],
00236 g->tc_stats[RTNL_TC_OVERLIMITS],
00237 g->tc_stats[RTNL_TC_QLEN],
00238 g->tc_stats[RTNL_TC_BACKLOG]);
00239
00240 res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_RATE_BPS], &unit);
00241
00242 strcpy(fmt, " %7.2f %s/s%9u pps");
00243
00244 if (*unit == 'B')
00245 fmt[11] = '9';
00246
00247 dp_dump_line(p, line++, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]);
00248
00249 return line;
00250 }
00251
00252 int tca_compare(struct nl_object *_a, struct nl_object *_b,
00253 uint32_t attrs, int flags)
00254 {
00255 struct rtnl_tca *a = (struct rtnl_tca *) _a;
00256 struct rtnl_tca *b = (struct rtnl_tca *) _b;
00257 int diff = 0;
00258
00259 #define TC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TCA_ATTR_##ATTR, a, b, EXPR)
00260
00261 diff |= TC_DIFF(HANDLE, a->tc_handle != b->tc_handle);
00262 diff |= TC_DIFF(PARENT, a->tc_parent != b->tc_parent);
00263 diff |= TC_DIFF(IFINDEX, a->tc_ifindex != b->tc_ifindex);
00264 diff |= TC_DIFF(KIND, strcmp(a->tc_kind, b->tc_kind));
00265
00266 #undef TC_DIFF
00267
00268 return diff;
00269 }
00270
00271 void tca_set_ifindex(struct rtnl_tca *t, int ifindex)
00272 {
00273 t->tc_ifindex = ifindex;
00274 t->ce_mask |= TCA_ATTR_IFINDEX;
00275 }
00276
00277 int tca_get_ifindex(struct rtnl_tca *t)
00278 {
00279 if (t->ce_mask & TCA_ATTR_IFINDEX)
00280 return t->tc_ifindex;
00281 else
00282 return RTNL_LINK_NOT_FOUND;
00283 }
00284
00285 void tca_set_handle(struct rtnl_tca *t, uint32_t handle)
00286 {
00287 t->tc_handle = handle;
00288 t->ce_mask |= TCA_ATTR_HANDLE;
00289 }
00290
00291 uint32_t tca_get_handle(struct rtnl_tca *t)
00292 {
00293 if (t->ce_mask & TCA_ATTR_HANDLE)
00294 return t->tc_handle;
00295 else
00296 return 0;
00297 }
00298
00299 void tca_set_parent(struct rtnl_tca *t, uint32_t parent)
00300 {
00301 t->tc_parent = parent;
00302 t->ce_mask |= TCA_ATTR_PARENT;
00303 }
00304
00305 uint32_t tca_get_parent(struct rtnl_tca *t)
00306 {
00307 if (t->ce_mask & TCA_ATTR_PARENT)
00308 return t->tc_parent;
00309 else
00310 return 0;
00311 }
00312
00313 void tca_set_kind(struct rtnl_tca *t, const char *kind)
00314 {
00315 strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1);
00316 t->ce_mask |= TCA_ATTR_KIND;
00317 }
00318
00319 char *tca_get_kind(struct rtnl_tca *t)
00320 {
00321 if (t->ce_mask & TCA_ATTR_KIND)
00322 return t->tc_kind;
00323 else
00324 return NULL;
00325 }
00326
00327 uint64_t tca_get_stat(struct rtnl_tca *t, int id)
00328 {
00329 if (id < 0 || id > RTNL_TC_STATS_MAX)
00330 return 0;
00331
00332 return t->tc_stats[id];
00333 }
00334
00335 struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags)
00336 {
00337 struct nl_msg *msg;
00338 struct tcmsg tchdr = {
00339 .tcm_family = AF_UNSPEC,
00340 .tcm_ifindex = tca->tc_ifindex,
00341 .tcm_handle = tca->tc_handle,
00342 .tcm_parent = tca->tc_parent,
00343 };
00344
00345 msg = nlmsg_alloc_simple(type, flags);
00346 if (!msg)
00347 goto nla_put_failure;
00348
00349 if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
00350 goto nla_put_failure;
00351
00352 if (tca->ce_mask & TCA_ATTR_KIND)
00353 NLA_PUT_STRING(msg, TCA_KIND, tca->tc_kind);
00354
00355 return msg;
00356
00357 nla_put_failure:
00358 nlmsg_free(msg);
00359 return NULL;
00360 }
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383 int rtnl_tc_calc_txtime(int bufsize, int rate)
00384 {
00385 double tx_time_secs;
00386
00387 tx_time_secs = (double) bufsize / (double) rate;
00388
00389 return tx_time_secs * 1000000.;
00390 }
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406 int rtnl_tc_calc_bufsize(int txtime, int rate)
00407 {
00408 double bufsize;
00409
00410 bufsize = (double) txtime * (double) rate;
00411
00412 return bufsize / 1000000.;
00413 }
00414
00415
00416
00417
00418
00419
00420 int rtnl_tc_calc_cell_log(int cell_size)
00421 {
00422 int i;
00423
00424 for (i = 0; i < 32; i++)
00425 if ((1 << i) == cell_size)
00426 return i;
00427
00428 return nl_errno(EINVAL);
00429 }
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454 int rtnl_tc_build_rate_table(uint32_t *dst, uint8_t mpu, uint8_t overhead,
00455 int cell, int rate)
00456 {
00457 int i, size, cell_log;
00458
00459 cell_log = rtnl_tc_calc_cell_log(cell);
00460 if (cell_log < 0)
00461 return cell_log;
00462
00463 for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) {
00464 size = (i << cell_log) + overhead;
00465 if (size < mpu)
00466 size = mpu;
00467
00468 dst[i] = rtnl_tc_calc_txtime(size, rate);
00469 }
00470
00471 return 0;
00472 }
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493 char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
00494 {
00495 if (TC_H_ROOT == handle)
00496 snprintf(buf, len, "root");
00497 else if (TC_H_UNSPEC == handle)
00498 snprintf(buf, len, "none");
00499 else if (0 == TC_H_MAJ(handle))
00500 snprintf(buf, len, ":%02x", TC_H_MIN(handle));
00501 else if (0 == TC_H_MIN(handle))
00502 snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16);
00503 else
00504 snprintf(buf, len, "%02x:%02x",
00505 TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));
00506
00507 return buf;
00508 }
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528 int rtnl_tc_str2handle(const char *name, uint32_t *res)
00529 {
00530 char *colon, *end;
00531 uint32_t h;
00532
00533 if (!strcasecmp(name, "root")) {
00534 *res = TC_H_ROOT;
00535 return 0;
00536 }
00537
00538 if (!strcasecmp(name, "none")) {
00539 *res = TC_H_UNSPEC;
00540 return 0;
00541 }
00542
00543 h = strtoul(name, &colon, 16);
00544
00545 if (colon == name) {
00546
00547 h = 0;
00548 if (':' != *colon)
00549 return -EINVAL;
00550 }
00551
00552 if (':' == *colon) {
00553
00554 if (TC_H_MAJ(h))
00555 return -ERANGE;
00556 h <<= 16;
00557
00558 if ('\0' == colon[1]) {
00559
00560 *res = h;
00561 } else {
00562
00563 uint32_t l = strtoul(colon+1, &end, 16);
00564
00565
00566 if (TC_H_MAJ(l))
00567 return -ERANGE;
00568
00569 if ('\0' != *end)
00570 return -EINVAL;
00571
00572 *res = (h | l);
00573 }
00574 } else if ('\0' == *colon) {
00575
00576 *res = h;
00577 } else
00578 return -EINVAL;
00579
00580 return 0;
00581 }
00582
00583
00584
00585