1 /* 2 * lib/route/tc.c Traffic Control 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2008 Thomas Graf <tgraf (at) suug.ch> 10 */ 11 12 /** 13 * @ingroup rtnl 14 * @defgroup tc Traffic Control 15 * @brief 16 * @{ 17 */ 18 19 #include <netlink-local.h> 20 #include <netlink-tc.h> 21 #include <netlink/netlink.h> 22 #include <netlink/utils.h> 23 #include <netlink/route/rtnl.h> 24 #include <netlink/route/link.h> 25 #include <netlink/route/tc.h> 26 27 /** @cond SKIP */ 28 29 static struct nla_policy tc_policy[TCA_MAX+1] = { 30 [TCA_KIND] = { .type = NLA_STRING, 31 .maxlen = TCKINDSIZ }, 32 [TCA_STATS] = { .minlen = sizeof(struct tc_stats) }, 33 [TCA_STATS2] = { .type = NLA_NESTED }, 34 }; 35 36 int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tca *g, 37 struct nla_policy *policy) 38 { 39 40 if (g->ce_mask & TCA_ATTR_OPTS) 41 return nla_parse(tb, maxattr, 42 (struct nlattr *) g->tc_opts->d_data, 43 g->tc_opts->d_size, policy); 44 else { 45 /* Ugly but tb[] must be in a defined state even if no 46 * attributes can be found. */ 47 memset(tb, 0, sizeof(struct nlattr *) * (maxattr + 1)); 48 return 0; 49 } 50 } 51 52 static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = { 53 [TCA_STATS_BASIC] = { .minlen = sizeof(struct gnet_stats_basic) }, 54 [TCA_STATS_RATE_EST] = { .minlen = sizeof(struct gnet_stats_rate_est) }, 55 [TCA_STATS_QUEUE] = { .minlen = sizeof(struct gnet_stats_queue) }, 56 }; 57 58 int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g) 59 { 60 struct nlattr *tb[TCA_MAX + 1]; 61 struct tcmsg *tm; 62 int err; 63 64 err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy); 65 if (err < 0) 66 return err; 67 68 if (tb[TCA_KIND] == NULL) 69 return -NLE_MISSING_ATTR; 70 71 nla_strlcpy(g->tc_kind, tb[TCA_KIND], TCKINDSIZ); 72 73 tm = nlmsg_data(n); 74 g->tc_family = tm->tcm_family; 75 g->tc_ifindex = tm->tcm_ifindex; 76 g->tc_handle = tm->tcm_handle; 77 g->tc_parent = tm->tcm_parent; 78 g->tc_info = tm->tcm_info; 79 80 g->ce_mask = (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE | 81 TCA_ATTR_PARENT | TCA_ATTR_INFO | TCA_ATTR_KIND); 82 83 if (tb[TCA_OPTIONS]) { 84 g->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]); 85 if (!g->tc_opts) 86 return -NLE_NOMEM; 87 g->ce_mask |= TCA_ATTR_OPTS; 88 } 89 90 91 if (tb[TCA_STATS2]) { 92 struct nlattr *tbs[TCA_STATS_MAX + 1]; 93 94 err = nla_parse_nested(tbs, TCA_STATS_MAX, tb[TCA_STATS2], 95 tc_stats2_policy); 96 if (err < 0) 97 return err; 98 99 if (tbs[TCA_STATS_BASIC]) { 100 struct gnet_stats_basic *bs; 101 102 bs = nla_data(tbs[TCA_STATS_BASIC]); 103 g->tc_stats[RTNL_TC_BYTES] = bs->bytes; 104 g->tc_stats[RTNL_TC_PACKETS] = bs->packets; 105 } 106 107 if (tbs[TCA_STATS_RATE_EST]) { 108 struct gnet_stats_rate_est *re; 109 110 re = nla_data(tbs[TCA_STATS_RATE_EST]); 111 g->tc_stats[RTNL_TC_RATE_BPS] = re->bps; 112 g->tc_stats[RTNL_TC_RATE_PPS] = re->pps; 113 } 114 115 if (tbs[TCA_STATS_QUEUE]) { 116 struct gnet_stats_queue *q; 117 118 q = nla_data(tbs[TCA_STATS_QUEUE]); 119 g->tc_stats[RTNL_TC_QLEN] = q->qlen; 120 g->tc_stats[RTNL_TC_BACKLOG] = q->backlog; 121 g->tc_stats[RTNL_TC_DROPS] = q->drops; 122 g->tc_stats[RTNL_TC_REQUEUES] = q->requeues; 123 g->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits; 124 } 125 126 g->ce_mask |= TCA_ATTR_STATS; 127 128 if (tbs[TCA_STATS_APP]) { 129 g->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]); 130 if (g->tc_xstats == NULL) 131 return -NLE_NOMEM; 132 } else 133 goto compat_xstats; 134 } else { 135 if (tb[TCA_STATS]) { 136 struct tc_stats *st = nla_data(tb[TCA_STATS]); 137 138 g->tc_stats[RTNL_TC_BYTES] = st->bytes; 139 g->tc_stats[RTNL_TC_PACKETS] = st->packets; 140 g->tc_stats[RTNL_TC_RATE_BPS] = st->bps; 141 g->tc_stats[RTNL_TC_RATE_PPS] = st->pps; 142 g->tc_stats[RTNL_TC_QLEN] = st->qlen; 143 g->tc_stats[RTNL_TC_BACKLOG] = st->backlog; 144 g->tc_stats[RTNL_TC_DROPS] = st->drops; 145 g->tc_stats[RTNL_TC_OVERLIMITS] = st->overlimits; 146 147 g->ce_mask |= TCA_ATTR_STATS; 148 } 149 150 compat_xstats: 151 if (tb[TCA_XSTATS]) { 152 g->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]); 153 if (g->tc_xstats == NULL) 154 return -NLE_NOMEM; 155 g->ce_mask |= TCA_ATTR_XSTATS; 156 } 157 } 158 159 160 return 0; 161 } 162 163 void tca_free_data(struct rtnl_tca *tca) 164 { 165 nl_data_free(tca->tc_opts); 166 nl_data_free(tca->tc_xstats); 167 } 168 169 int tca_clone(struct rtnl_tca *dst, struct rtnl_tca *src) 170 { 171 if (src->tc_opts) { 172 dst->tc_opts = nl_data_clone(src->tc_opts); 173 if (!dst->tc_opts) 174 return -NLE_NOMEM; 175 } 176 177 if (src->tc_xstats) { 178 dst->tc_xstats = nl_data_clone(src->tc_xstats); 179 if (!dst->tc_xstats) 180 return -NLE_NOMEM; 181 } 182 183 return 0; 184 } 185 186 void tca_dump_line(struct rtnl_tca *g, const char *type, 187 struct nl_dump_params *p) 188 { 189 char handle[32], parent[32]; 190 struct nl_cache *link_cache; 191 192 link_cache = nl_cache_mngt_require("route/link"); 193 194 nl_dump_line(p, "%s %s ", g->tc_kind, type); 195 196 if (link_cache) { 197 char buf[32]; 198 nl_dump(p, "dev %s ", 199 rtnl_link_i2name(link_cache, g->tc_ifindex, 200 buf, sizeof(buf))); 201 } else 202 nl_dump(p, "dev %u ", g->tc_ifindex); 203 204 nl_dump(p, "handle %s parent %s", 205 rtnl_tc_handle2str(g->tc_handle, handle, sizeof(handle)), 206 rtnl_tc_handle2str(g->tc_parent, parent, sizeof(parent))); 207 } 208 209 void tca_dump_details(struct rtnl_tca *g, struct nl_dump_params *p) 210 { 211 nl_dump_line(p, " "); 212 } 213 214 void tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p) 215 { 216 char *unit, fmt[64]; 217 float res; 218 strcpy(fmt, " %7.2f %s %10u %10u %10u %10u %10u\n"); 219 220 nl_dump_line(p, 221 " Stats: bytes packets drops overlimits" \ 222 " qlen backlog\n"); 223 224 res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_BYTES], &unit); 225 if (*unit == 'B') 226 fmt[11] = '9'; 227 228 nl_dump_line(p, fmt, res, unit, 229 g->tc_stats[RTNL_TC_PACKETS], 230 g->tc_stats[RTNL_TC_DROPS], 231 g->tc_stats[RTNL_TC_OVERLIMITS], 232 g->tc_stats[RTNL_TC_QLEN], 233 g->tc_stats[RTNL_TC_BACKLOG]); 234 235 res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_RATE_BPS], &unit); 236 237 strcpy(fmt, " %7.2f %s/s%9u pps"); 238 239 if (*unit == 'B') 240 fmt[11] = '9'; 241 242 nl_dump_line(p, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]); 243 } 244 245 int tca_compare(struct nl_object *_a, struct nl_object *_b, 246 uint32_t attrs, int flags) 247 { 248 struct rtnl_tca *a = (struct rtnl_tca *) _a; 249 struct rtnl_tca *b = (struct rtnl_tca *) _b; 250 int diff = 0; 251 252 #define TC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TCA_ATTR_##ATTR, a, b, EXPR) 253 254 diff |= TC_DIFF(HANDLE, a->tc_handle != b->tc_handle); 255 diff |= TC_DIFF(PARENT, a->tc_parent != b->tc_parent); 256 diff |= TC_DIFF(IFINDEX, a->tc_ifindex != b->tc_ifindex); 257 diff |= TC_DIFF(KIND, strcmp(a->tc_kind, b->tc_kind)); 258 259 #undef TC_DIFF 260 261 return diff; 262 } 263 264 void tca_set_ifindex(struct rtnl_tca *t, int ifindex) 265 { 266 t->tc_ifindex = ifindex; 267 t->ce_mask |= TCA_ATTR_IFINDEX; 268 } 269 270 int tca_get_ifindex(struct rtnl_tca *t) 271 { 272 return t->tc_ifindex; 273 } 274 275 void tca_set_handle(struct rtnl_tca *t, uint32_t handle) 276 { 277 t->tc_handle = handle; 278 t->ce_mask |= TCA_ATTR_HANDLE; 279 } 280 281 uint32_t tca_get_handle(struct rtnl_tca *t) 282 { 283 if (t->ce_mask & TCA_ATTR_HANDLE) 284 return t->tc_handle; 285 else 286 return 0; 287 } 288 289 void tca_set_parent(struct rtnl_tca *t, uint32_t parent) 290 { 291 t->tc_parent = parent; 292 t->ce_mask |= TCA_ATTR_PARENT; 293 } 294 295 uint32_t tca_get_parent(struct rtnl_tca *t) 296 { 297 if (t->ce_mask & TCA_ATTR_PARENT) 298 return t->tc_parent; 299 else 300 return 0; 301 } 302 303 void tca_set_kind(struct rtnl_tca *t, const char *kind) 304 { 305 strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1); 306 t->ce_mask |= TCA_ATTR_KIND; 307 } 308 309 char *tca_get_kind(struct rtnl_tca *t) 310 { 311 if (t->ce_mask & TCA_ATTR_KIND) 312 return t->tc_kind; 313 else 314 return NULL; 315 } 316 317 uint64_t tca_get_stat(struct rtnl_tca *t, int id) 318 { 319 if (id < 0 || id > RTNL_TC_STATS_MAX) 320 return 0; 321 322 return t->tc_stats[id]; 323 } 324 325 int tca_build_msg(struct rtnl_tca *tca, int type, int flags, 326 struct nl_msg **result) 327 { 328 struct nl_msg *msg; 329 struct tcmsg tchdr = { 330 .tcm_family = AF_UNSPEC, 331 .tcm_ifindex = tca->tc_ifindex, 332 .tcm_handle = tca->tc_handle, 333 .tcm_parent = tca->tc_parent, 334 }; 335 336 msg = nlmsg_alloc_simple(type, flags); 337 if (!msg) 338 return -NLE_NOMEM; 339 340 if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) 341 goto nla_put_failure; 342 343 if (tca->ce_mask & TCA_ATTR_KIND) 344 NLA_PUT_STRING(msg, TCA_KIND, tca->tc_kind); 345 346 *result = msg; 347 return 0; 348 349 nla_put_failure: 350 nlmsg_free(msg); 351 return -NLE_MSGSIZE; 352 } 353 354 /** @endcond */ 355 356 /** 357 * @name Utilities 358 * @{ 359 */ 360 361 /** 362 * Calculate time required to transmit buffer at a specific rate 363 * @arg bufsize Size of buffer to be transmited in bytes. 364 * @arg rate Transmit rate in bytes per second. 365 * 366 * Calculates the number of micro seconds required to transmit a 367 * specific buffer at a specific transmit rate. 368 * 369 * @f[ 370 * txtime=\frac{bufsize}{rate}10^6 371 * @f] 372 * 373 * @return Required transmit time in micro seconds. 374 */ 375 int rtnl_tc_calc_txtime(int bufsize, int rate) 376 { 377 double tx_time_secs; 378 379 tx_time_secs = (double) bufsize / (double) rate; 380 381 return tx_time_secs * 1000000.; 382 } 383 384 /** 385 * Calculate buffer size able to transmit in a specific time and rate. 386 * @arg txtime Available transmit time in micro seconds. 387 * @arg rate Transmit rate in bytes per second. 388 * 389 * Calculates the size of the buffer that can be transmitted in a 390 * specific time period at a specific transmit rate. 391 * 392 * @f[ 393 * bufsize=\frac{{txtime} \times {rate}}{10^6} 394 * @f] 395 * 396 * @return Size of buffer in bytes. 397 */ 398 int rtnl_tc_calc_bufsize(int txtime, int rate) 399 { 400 double bufsize; 401 402 bufsize = (double) txtime * (double) rate; 403 404 return bufsize / 1000000.; 405 } 406 407 /** 408 * Calculate the binary logarithm for a specific cell size 409 * @arg cell_size Size of cell, must be a power of two. 410 * @return Binary logirhtm of cell size or a negative error code. 411 */ 412 int rtnl_tc_calc_cell_log(int cell_size) 413 { 414 int i; 415 416 for (i = 0; i < 32; i++) 417 if ((1 << i) == cell_size) 418 return i; 419 420 return -NLE_INVAL; 421 } 422 423 424 /** @} */ 425 426 /** 427 * @name Rate Tables 428 * @{ 429 */ 430 431 /** 432 * Compute a transmission time lookup table 433 * @arg dst Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[]. 434 * @arg mpu Minimal size of a packet at all times. 435 * @arg overhead Overhead to be added to each packet. 436 * @arg cell Size of cell, i.e. size of step between entries in bytes. 437 * @arg rate Rate in bytes per second. 438 * 439 * Computes a table of RTNL_TC_RTABLE_SIZE entries specyfing the 440 * transmission times for various packet sizes, e.g. the transmission 441 * time for a packet of size \c pktsize could be looked up: 442 * @code 443 * txtime = table[pktsize >> log2(cell)]; 444 * @endcode 445 */ 446 int rtnl_tc_build_rate_table(uint32_t *dst, uint8_t mpu, uint8_t overhead, 447 int cell, int rate) 448 { 449 int i, size, cell_log; 450 451 cell_log = rtnl_tc_calc_cell_log(cell); 452 if (cell_log < 0) 453 return cell_log; 454 455 for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) { 456 size = (i << cell_log) + overhead; 457 if (size < mpu) 458 size = mpu; 459 460 dst[i] = rtnl_tc_calc_txtime(size, rate); 461 } 462 463 return 0; 464 } 465 466 /** @} */ 467 468 /** 469 * @name Traffic Control Handle Translations 470 * @{ 471 */ 472 473 /** 474 * Convert a traffic control handle to a character string (Reentrant). 475 * @arg handle traffic control handle 476 * @arg buf destination buffer 477 * @arg len buffer length 478 * 479 * Converts a tarffic control handle to a character string in the 480 * form of \c MAJ:MIN and stores it in the specified destination buffer. 481 * 482 * @return The destination buffer or the type encoded in hexidecimal 483 * form if no match was found. 484 */ 485 char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) 486 { 487 if (TC_H_ROOT == handle) 488 snprintf(buf, len, "root"); 489 else if (TC_H_UNSPEC == handle) 490 snprintf(buf, len, "none"); 491 else if (0 == TC_H_MAJ(handle)) 492 snprintf(buf, len, ":%02x", TC_H_MIN(handle)); 493 else if (0 == TC_H_MIN(handle)) 494 snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16); 495 else 496 snprintf(buf, len, "%02x:%02x", 497 TC_H_MAJ(handle) >> 16, TC_H_MIN(handle)); 498 499 return buf; 500 } 501 502 /** 503 * Convert a charactering strint to a traffic control handle 504 * @arg name traffic control handle as character string 505 * @arg res destination buffer 506 * 507 * Converts the provided character string specifying a traffic 508 * control handle to the corresponding numeric value. 509 * 510 * The handle must be provided in one of the following formats: 511 * - root 512 * - none 513 * - XXXX: 514 * - :YYYY 515 * - XXXX:YYYY 516 * - XXXXYYYY 517 * 518 * @return 0 on success or a negative error code 519 */ 520 int rtnl_tc_str2handle(const char *name, uint32_t *res) 521 { 522 char *colon, *end; 523 uint32_t h; 524 525 if (!strcasecmp(name, "root")) { 526 *res = TC_H_ROOT; 527 return 0; 528 } 529 530 if (!strcasecmp(name, "none")) { 531 *res = TC_H_UNSPEC; 532 return 0; 533 } 534 535 h = strtoul(name, &colon, 16); 536 537 if (colon == name) { 538 /* :YYYY */ 539 h = 0; 540 if (':' != *colon) 541 return -NLE_INVAL; 542 } 543 544 if (':' == *colon) { 545 /* check if we would lose bits */ 546 if (TC_H_MAJ(h)) 547 return -NLE_RANGE; 548 h <<= 16; 549 550 if ('\0' == colon[1]) { 551 /* XXXX: */ 552 *res = h; 553 } else { 554 /* XXXX:YYYY */ 555 uint32_t l = strtoul(colon+1, &end, 16); 556 557 /* check if we overlap with major part */ 558 if (TC_H_MAJ(l)) 559 return -NLE_RANGE; 560 561 if ('\0' != *end) 562 return -NLE_INVAL; 563 564 *res = (h | l); 565 } 566 } else if ('\0' == *colon) { 567 /* XXXXYYYY */ 568 *res = h; 569 } else 570 return -NLE_INVAL; 571 572 return 0; 573 } 574 575 /** @} */ 576 577 /** @} */ 578