libnl 1.1.4
tc.c
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-2006 Thomas Graf <tgraf@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
29static 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
36int 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
52static 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
58int 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 nl_error(EINVAL, "Missing tca kind TLV");
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 = nla_get_data(tb[TCA_OPTIONS]);
85 if (!g->tc_opts)
86 return nl_errno(ENOMEM);
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 = nla_get_data(tbs[TCA_STATS_APP]);
130 if (g->tc_xstats == NULL)
131 return -ENOMEM;
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
150compat_xstats:
151 if (tb[TCA_XSTATS]) {
152 g->tc_xstats = nla_get_data(tb[TCA_XSTATS]);
153 if (g->tc_xstats == NULL)
154 return -ENOMEM;
155 g->ce_mask |= TCA_ATTR_XSTATS;
156 }
157 }
158
159
160 return 0;
161}
162
163void tca_free_data(struct rtnl_tca *tca)
164{
165 nl_data_free(tca->tc_opts);
166 nl_data_free(tca->tc_xstats);
167}
168
169int 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 goto errout;
175 }
176
177 if (src->tc_xstats) {
178 dst->tc_xstats = nl_data_clone(src->tc_xstats);
179 if (!dst->tc_xstats)
180 goto errout;
181 }
182
183 return 0;
184errout:
185 return nl_get_errno();
186}
187
188int tca_dump_brief(struct rtnl_tca *g, const char *type,
189 struct nl_dump_params *p, int line)
190{
191 char handle[32], parent[32];
192 struct nl_cache *link_cache;
193
194 link_cache = nl_cache_mngt_require("route/link");
195
196 dp_dump(p, "%s %s ", g->tc_kind, type);
197
198 if (link_cache) {
199 char buf[32];
200 dp_dump(p, "dev %s ",
201 rtnl_link_i2name(link_cache, g->tc_ifindex,
202 buf, sizeof(buf)));
203 } else
204 dp_dump(p, "dev %u ", g->tc_ifindex);
205
206 dp_dump(p, "handle %s parent %s",
207 rtnl_tc_handle2str(g->tc_handle, handle, sizeof(handle)),
208 rtnl_tc_handle2str(g->tc_parent, parent, sizeof(parent)));
209
210 return 1;
211}
212
213int tca_dump_full(struct rtnl_tca *g, struct nl_dump_params *p, int line)
214{
215 dp_dump_line(p, line++, " ");
216 return line;
217}
218
219int tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p, int line)
220{
221 char *unit, fmt[64];
222 float res;
223 strcpy(fmt, " %7.2f %s %10u %10u %10u %10u %10u\n");
224
225 dp_dump_line(p, line++,
226 " Stats: bytes packets drops overlimits" \
227 " qlen backlog\n");
228
229 res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_BYTES], &unit);
230 if (*unit == 'B')
231 fmt[11] = '9';
232
233 dp_dump_line(p, line++, fmt, res, unit,
234 g->tc_stats[RTNL_TC_PACKETS],
235 g->tc_stats[RTNL_TC_DROPS],
236 g->tc_stats[RTNL_TC_OVERLIMITS],
237 g->tc_stats[RTNL_TC_QLEN],
238 g->tc_stats[RTNL_TC_BACKLOG]);
239
240 res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_RATE_BPS], &unit);
241
242 strcpy(fmt, " %7.2f %s/s%9u pps");
243
244 if (*unit == 'B')
245 fmt[11] = '9';
246
247 dp_dump_line(p, line++, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]);
248
249 return line;
250}
251
252int tca_compare(struct nl_object *_a, struct nl_object *_b,
253 uint32_t attrs, int flags)
254{
255 struct rtnl_tca *a = (struct rtnl_tca *) _a;
256 struct rtnl_tca *b = (struct rtnl_tca *) _b;
257 int diff = 0;
258
259#define TC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TCA_ATTR_##ATTR, a, b, EXPR)
260
261 diff |= TC_DIFF(HANDLE, a->tc_handle != b->tc_handle);
262 diff |= TC_DIFF(PARENT, a->tc_parent != b->tc_parent);
263 diff |= TC_DIFF(IFINDEX, a->tc_ifindex != b->tc_ifindex);
264 diff |= TC_DIFF(KIND, strcmp(a->tc_kind, b->tc_kind));
265
266#undef TC_DIFF
267
268 return diff;
269}
270
271void tca_set_ifindex(struct rtnl_tca *t, int ifindex)
272{
273 t->tc_ifindex = ifindex;
274 t->ce_mask |= TCA_ATTR_IFINDEX;
275}
276
277int tca_get_ifindex(struct rtnl_tca *t)
278{
279 if (t->ce_mask & TCA_ATTR_IFINDEX)
280 return t->tc_ifindex;
281 else
282 return RTNL_LINK_NOT_FOUND;
283}
284
285void tca_set_handle(struct rtnl_tca *t, uint32_t handle)
286{
287 t->tc_handle = handle;
288 t->ce_mask |= TCA_ATTR_HANDLE;
289}
290
291uint32_t tca_get_handle(struct rtnl_tca *t)
292{
293 if (t->ce_mask & TCA_ATTR_HANDLE)
294 return t->tc_handle;
295 else
296 return 0;
297}
298
299void tca_set_parent(struct rtnl_tca *t, uint32_t parent)
300{
301 t->tc_parent = parent;
302 t->ce_mask |= TCA_ATTR_PARENT;
303}
304
305uint32_t tca_get_parent(struct rtnl_tca *t)
306{
307 if (t->ce_mask & TCA_ATTR_PARENT)
308 return t->tc_parent;
309 else
310 return 0;
311}
312
313void tca_set_kind(struct rtnl_tca *t, const char *kind)
314{
315 strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1);
316 t->ce_mask |= TCA_ATTR_KIND;
317}
318
319char *tca_get_kind(struct rtnl_tca *t)
320{
321 if (t->ce_mask & TCA_ATTR_KIND)
322 return t->tc_kind;
323 else
324 return NULL;
325}
326
327uint64_t tca_get_stat(struct rtnl_tca *t, int id)
328{
329 if (id < 0 || id > RTNL_TC_STATS_MAX)
330 return 0;
331
332 return t->tc_stats[id];
333}
334
335struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags)
336{
337 struct nl_msg *msg;
338 struct tcmsg tchdr = {
339 .tcm_family = AF_UNSPEC,
340 .tcm_ifindex = tca->tc_ifindex,
341 .tcm_handle = tca->tc_handle,
342 .tcm_parent = tca->tc_parent,
343 };
344
345 msg = nlmsg_alloc_simple(type, flags);
346 if (!msg)
347 goto nla_put_failure;
348
349 if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
350 goto nla_put_failure;
351
352 if (tca->ce_mask & TCA_ATTR_KIND)
353 NLA_PUT_STRING(msg, TCA_KIND, tca->tc_kind);
354
355 return msg;
356
357nla_put_failure:
358 nlmsg_free(msg);
359 return NULL;
360}
361
362/** @endcond */
363
364/**
365 * @name Utilities
366 * @{
367 */
368
369/**
370 * Calculate time required to transmit buffer at a specific rate
371 * @arg bufsize Size of buffer to be transmited in bytes.
372 * @arg rate Transmit rate in bytes per second.
373 *
374 * Calculates the number of micro seconds required to transmit a
375 * specific buffer at a specific transmit rate.
376 *
377 * @f[
378 * txtime=\frac{bufsize}{rate}10^6
379 * @f]
380 *
381 * @return Required transmit time in micro seconds.
382 */
383int rtnl_tc_calc_txtime(int bufsize, int rate)
384{
385 double tx_time_secs;
386
387 tx_time_secs = (double) bufsize / (double) rate;
388
389 return tx_time_secs * 1000000.;
390}
391
392/**
393 * Calculate buffer size able to transmit in a specific time and rate.
394 * @arg txtime Available transmit time in micro seconds.
395 * @arg rate Transmit rate in bytes per second.
396 *
397 * Calculates the size of the buffer that can be transmitted in a
398 * specific time period at a specific transmit rate.
399 *
400 * @f[
401 * bufsize=\frac{{txtime} \times {rate}}{10^6}
402 * @f]
403 *
404 * @return Size of buffer in bytes.
405 */
406int rtnl_tc_calc_bufsize(int txtime, int rate)
407{
408 double bufsize;
409
410 bufsize = (double) txtime * (double) rate;
411
412 return bufsize / 1000000.;
413}
414
415/**
416 * Calculate the binary logarithm for a specific cell size
417 * @arg cell_size Size of cell, must be a power of two.
418 * @return Binary logirhtm of cell size or a negative error code.
419 */
420int rtnl_tc_calc_cell_log(int cell_size)
421{
422 int i;
423
424 for (i = 0; i < 32; i++)
425 if ((1 << i) == cell_size)
426 return i;
427
428 return nl_errno(EINVAL);
429}
430
431
432/** @} */
433
434/**
435 * @name Rate Tables
436 * @{
437 */
438
439/**
440 * Compute a transmission time lookup table
441 * @arg dst Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[].
442 * @arg mpu Minimal size of a packet at all times.
443 * @arg overhead Overhead to be added to each packet.
444 * @arg cell Size of cell, i.e. size of step between entries in bytes.
445 * @arg rate Rate in bytes per second.
446 *
447 * Computes a table of RTNL_TC_RTABLE_SIZE entries specyfing the
448 * transmission times for various packet sizes, e.g. the transmission
449 * time for a packet of size \c pktsize could be looked up:
450 * @code
451 * txtime = table[pktsize >> log2(cell)];
452 * @endcode
453 */
454int rtnl_tc_build_rate_table(uint32_t *dst, uint8_t mpu, uint8_t overhead,
455 int cell, int rate)
456{
457 int i, size, cell_log;
458
459 cell_log = rtnl_tc_calc_cell_log(cell);
460 if (cell_log < 0)
461 return cell_log;
462
463 for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) {
464 size = (i << cell_log) + overhead;
465 if (size < mpu)
466 size = mpu;
467
468 dst[i] = rtnl_tc_calc_txtime(size, rate);
469 }
470
471 return 0;
472}
473
474/** @} */
475
476/**
477 * @name Traffic Control Handle Translations
478 * @{
479 */
480
481/**
482 * Convert a traffic control handle to a character string (Reentrant).
483 * @arg handle traffic control handle
484 * @arg buf destination buffer
485 * @arg len buffer length
486 *
487 * Converts a tarffic control handle to a character string in the
488 * form of \c MAJ:MIN and stores it in the specified destination buffer.
489 *
490 * @return The destination buffer or the type encoded in hexidecimal
491 * form if no match was found.
492 */
493char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
494{
495 if (TC_H_ROOT == handle)
496 snprintf(buf, len, "root");
497 else if (TC_H_UNSPEC == handle)
498 snprintf(buf, len, "none");
499 else if (0 == TC_H_MAJ(handle))
500 snprintf(buf, len, ":%02x", TC_H_MIN(handle));
501 else if (0 == TC_H_MIN(handle))
502 snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16);
503 else
504 snprintf(buf, len, "%02x:%02x",
505 TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));
506
507 return buf;
508}
509
510/**
511 * Convert a charactering strint to a traffic control handle
512 * @arg name traffic control handle as character string
513 * @arg res destination buffer
514 *
515 * Converts the provided character string specifying a traffic
516 * control handle to the corresponding numeric value.
517 *
518 * The handle must be provided in one of the following formats:
519 * - root
520 * - none
521 * - XXXX:
522 * - :YYYY
523 * - XXXX:YYYY
524 * - XXXXYYYY
525 *
526 * @return 0 on success or a negative error code
527 */
528int rtnl_tc_str2handle(const char *name, uint32_t *res)
529{
530 char *colon, *end;
531 uint32_t h;
532
533 if (!strcasecmp(name, "root")) {
534 *res = TC_H_ROOT;
535 return 0;
536 }
537
538 if (!strcasecmp(name, "none")) {
539 *res = TC_H_UNSPEC;
540 return 0;
541 }
542
543 h = strtoul(name, &colon, 16);
544
545 if (colon == name) {
546 /* :YYYY */
547 h = 0;
548 if (':' != *colon)
549 return -EINVAL;
550 }
551
552 if (':' == *colon) {
553 /* check if we would lose bits */
554 if (TC_H_MAJ(h))
555 return -ERANGE;
556 h <<= 16;
557
558 if ('\0' == colon[1]) {
559 /* XXXX: */
560 *res = h;
561 } else {
562 /* XXXX:YYYY */
563 uint32_t l = strtoul(colon+1, &end, 16);
564
565 /* check if we overlap with major part */
566 if (TC_H_MAJ(l))
567 return -ERANGE;
568
569 if ('\0' != *end)
570 return -EINVAL;
571
572 *res = (h | l);
573 }
574 } else if ('\0' == *colon) {
575 /* XXXXYYYY */
576 *res = h;
577 } else
578 return -EINVAL;
579
580 return 0;
581}
582
583/** @} */
584
585/** @} */
struct nl_data * nla_get_data(struct nlattr *nla)
Return payload of abstract data attribute.
Definition: attr.c:776
void * nla_data(const struct nlattr *nla)
head of payload
Definition: attr.c:151
#define NLA_PUT_STRING(n, attrtype, value)
Add a character string netlink attribute to a netlink message.
Definition: attr.h:219
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, struct nla_policy *policy)
parse nested attributes
Definition: attr.c:307
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, struct nla_policy *policy)
Parse a stream of attributes into a tb buffer.
Definition: attr.c:262
size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
Copy string attribute payload into a sized buffer.
Definition: attr.c:407
@ NLA_STRING
character string
Definition: attr.h:41
@ NLA_NESTED
nested attributes
Definition: attr.h:44
struct nl_cache * nl_cache_mngt_require(const char *name)
Demand the use of a global cache.
Definition: cache_mngt.c:356
void nl_data_free(struct nl_data *data)
Free an abstract data object.
Definition: data.c:110
struct nl_data * nl_data_clone(struct nl_data *src)
Clone an abstract data object.
Definition: data.c:69
struct nl_msg * nlmsg_alloc_simple(int nlmsgtype, int flags)
Allocate a new netlink message.
Definition: msg.c:448
void nlmsg_free(struct nl_msg *n)
Free a netlink message.
Definition: msg.c:656
int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, struct nla_policy *policy)
parse attributes of a netlink message
Definition: msg.c:319
void * nlmsg_data(const struct nlmsghdr *nlh)
head of message payload
Definition: msg.c:218
int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad)
Append data to tail of a netlink message.
Definition: msg.c:549
int rtnl_tc_calc_cell_log(int cell_size)
Calculate the binary logarithm for a specific cell size.
Definition: tc.c:420
int rtnl_tc_calc_bufsize(int txtime, int rate)
Calculate buffer size able to transmit in a specific time and rate.
Definition: tc.c:406
#define RTNL_TC_RTABLE_SIZE
Number of entries in a transmission time lookup table.
Definition: tc.h:50
int rtnl_tc_str2handle(const char *name, uint32_t *res)
Convert a charactering strint to a traffic control handle.
Definition: tc.c:528
char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
Convert a traffic control handle to a character string (Reentrant).
Definition: tc.c:493
int rtnl_tc_build_rate_table(uint32_t *dst, uint8_t mpu, uint8_t overhead, int cell, int rate)
Compute a transmission time lookup table.
Definition: tc.c:454
int rtnl_tc_calc_txtime(int bufsize, int rate)
Calculate time required to transmit buffer at a specific rate.
Definition: tc.c:383
@ RTNL_TC_PACKETS
Packets seen.
Definition: tc.h:28
@ RTNL_TC_BYTES
Bytes seen.
Definition: tc.h:29
@ RTNL_TC_QLEN
Queue length.
Definition: tc.h:32
@ RTNL_TC_OVERLIMITS
Number of overlimits.
Definition: tc.h:36
@ RTNL_TC_DROPS
Packets dropped.
Definition: tc.h:34
@ RTNL_TC_RATE_PPS
Current packet/s (rate estimator)
Definition: tc.h:31
@ RTNL_TC_REQUEUES
Number of requeues.
Definition: tc.h:35
@ RTNL_TC_BACKLOG
Backlog length.
Definition: tc.h:33
@ RTNL_TC_RATE_BPS
Current bits/s (rate estimator)
Definition: tc.h:30
double nl_cancel_down_bytes(unsigned long long l, char **unit)
Cancel down a byte counter.
Definition: utils.c:189
Dumping parameters.
Definition: types.h:37
attribute validation policy
Definition: attr.h:73
uint16_t minlen
Minimal length of payload required to be available.
Definition: attr.h:78
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition: attr.h:75
Netlink message header.