libnl 1.1
|
00001 /* 00002 * lib/route/tc.c Traffic Control 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup rtnl 00014 * @defgroup tc Traffic Control 00015 * @brief 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 /** @cond SKIP */ 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 /* Ugly but tb[] must be in a defined state even if no 00046 * attributes can be found. */ 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 /** @endcond */ 00363 00364 /** 00365 * @name Utilities 00366 * @{ 00367 */ 00368 00369 /** 00370 * Calculate time required to transmit buffer at a specific rate 00371 * @arg bufsize Size of buffer to be transmited in bytes. 00372 * @arg rate Transmit rate in bytes per second. 00373 * 00374 * Calculates the number of micro seconds required to transmit a 00375 * specific buffer at a specific transmit rate. 00376 * 00377 * @f[ 00378 * txtime=\frac{bufsize}{rate}10^6 00379 * @f] 00380 * 00381 * @return Required transmit time in micro seconds. 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 * Calculate buffer size able to transmit in a specific time and rate. 00394 * @arg txtime Available transmit time in micro seconds. 00395 * @arg rate Transmit rate in bytes per second. 00396 * 00397 * Calculates the size of the buffer that can be transmitted in a 00398 * specific time period at a specific transmit rate. 00399 * 00400 * @f[ 00401 * bufsize=\frac{{txtime} \times {rate}}{10^6} 00402 * @f] 00403 * 00404 * @return Size of buffer in bytes. 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 * Calculate the binary logarithm for a specific cell size 00417 * @arg cell_size Size of cell, must be a power of two. 00418 * @return Binary logirhtm of cell size or a negative error code. 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 * @name Rate Tables 00436 * @{ 00437 */ 00438 00439 /** 00440 * Compute a transmission time lookup table 00441 * @arg dst Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[]. 00442 * @arg mpu Minimal size of a packet at all times. 00443 * @arg overhead Overhead to be added to each packet. 00444 * @arg cell Size of cell, i.e. size of step between entries in bytes. 00445 * @arg rate Rate in bytes per second. 00446 * 00447 * Computes a table of RTNL_TC_RTABLE_SIZE entries specyfing the 00448 * transmission times for various packet sizes, e.g. the transmission 00449 * time for a packet of size \c pktsize could be looked up: 00450 * @code 00451 * txtime = table[pktsize >> log2(cell)]; 00452 * @endcode 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 * @name Traffic Control Handle Translations 00478 * @{ 00479 */ 00480 00481 /** 00482 * Convert a traffic control handle to a character string (Reentrant). 00483 * @arg handle traffic control handle 00484 * @arg buf destination buffer 00485 * @arg len buffer length 00486 * 00487 * Converts a tarffic control handle to a character string in the 00488 * form of \c MAJ:MIN and stores it in the specified destination buffer. 00489 * 00490 * @return The destination buffer or the type encoded in hexidecimal 00491 * form if no match was found. 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 * Convert a charactering strint to a traffic control handle 00512 * @arg name traffic control handle as character string 00513 * @arg res destination buffer 00514 * 00515 * Converts the provided character string specifying a traffic 00516 * control handle to the corresponding numeric value. 00517 * 00518 * The handle must be provided in one of the following formats: 00519 * - root 00520 * - none 00521 * - XXXX: 00522 * - :YYYY 00523 * - XXXX:YYYY 00524 * - XXXXYYYY 00525 * 00526 * @return 0 on success or a negative error code 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 /* :YYYY */ 00547 h = 0; 00548 if (':' != *colon) 00549 return -EINVAL; 00550 } 00551 00552 if (':' == *colon) { 00553 /* check if we would lose bits */ 00554 if (TC_H_MAJ(h)) 00555 return -ERANGE; 00556 h <<= 16; 00557 00558 if ('\0' == colon[1]) { 00559 /* XXXX: */ 00560 *res = h; 00561 } else { 00562 /* XXXX:YYYY */ 00563 uint32_t l = strtoul(colon+1, &end, 16); 00564 00565 /* check if we overlap with major part */ 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 /* XXXXYYYY */ 00576 *res = h; 00577 } else 00578 return -EINVAL; 00579 00580 return 0; 00581 } 00582 00583 /** @} */ 00584 00585 /** @} */