libnl  1.1
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 
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 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 
150 compat_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 
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  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;
184 errout:
185  return nl_get_errno();
186 }
187 
188 int 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 
213 int 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 
219 int 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 
252 int 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 
271 void tca_set_ifindex(struct rtnl_tca *t, int ifindex)
272 {
273  t->tc_ifindex = ifindex;
274  t->ce_mask |= TCA_ATTR_IFINDEX;
275 }
276 
277 int 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 
285 void 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 
291 uint32_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 
299 void 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 
305 uint32_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 
313 void 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 
319 char *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 
327 uint64_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 
335 struct 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 
357 nla_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  */
383 int 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  */
406 int 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  */
420 int 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  */
454 int 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  */
493 char * 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  */
528 int 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 /** @} */
Bytes seen.
Definition: tc.h:29
int rtnl_tc_calc_txtime(int bufsize, int rate)
Calculate time required to transmit buffer at a specific rate.
Definition: tc.c:383
void * nlmsg_data(const struct nlmsghdr *nlh)
head of message payload
Definition: msg.c:218
#define RTNL_TC_RTABLE_SIZE
Number of entries in a transmission time lookup table.
Definition: tc.h:50
int rtnl_tc_calc_cell_log(int cell_size)
Calculate the binary logarithm for a specific cell size.
Definition: tc.c:420
struct nl_data * nl_data_clone(struct nl_data *src)
Clone an abstract data object.
Definition: data.c:69
Current packet/s (rate estimator)
Definition: tc.h:31
attribute validation policy
Definition: attr.h:73
Number of overlimits.
Definition: tc.h:36
void nlmsg_free(struct nl_msg *n)
Free a netlink message.
Definition: msg.c:656
Backlog length.
Definition: tc.h:33
Netlink message header.
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
Queue length.
Definition: tc.h:32
Packets seen.
Definition: tc.h:28
#define NLA_PUT_STRING(n, attrtype, value)
Add a character string netlink attribute to a netlink message.
Definition: attr.h:219
nested attributes
Definition: attr.h:44
Current bits/s (rate estimator)
Definition: tc.h:30
character string
Definition: attr.h:41
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_calc_bufsize(int txtime, int rate)
Calculate buffer size able to transmit in a specific time and rate.
Definition: tc.c:406
double nl_cancel_down_bytes(unsigned long long l, char **unit)
Cancel down a byte counter.
Definition: utils.c:188
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, struct nla_policy *policy)
parse nested attributes
Definition: attr.c:308
struct nl_cache * nl_cache_mngt_require(const char *name)
Demand the use of a global cache.
Definition: cache_mngt.c:229
void * nla_data(const struct nlattr *nla)
head of payload
Definition: attr.c:151
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
uint16_t minlen
Minimal length of payload required to be available.
Definition: attr.h:78
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
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition: attr.h:75
struct nl_msg * nlmsg_alloc_simple(int nlmsgtype, int flags)
Allocate a new netlink message.
Definition: msg.c:448
Dumping parameters.
Definition: types.h:36
struct nl_data * nla_get_data(struct nlattr *nla)
Return payload of abstract data attribute.
Definition: attr.c:777
Packets dropped.
Definition: tc.h:34
Number of requeues.
Definition: tc.h:35
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_str2handle(const char *name, uint32_t *res)
Convert a charactering strint to a traffic control handle.
Definition: tc.c:528
size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
Copy string attribute payload into a sized buffer.
Definition: attr.c:408
void nl_data_free(struct nl_data *data)
Free an abstract data object.
Definition: data.c:110