xref: /aosp_15_r20/external/libnl/lib/route/tc.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2003-2011 Thomas Graf <[email protected]>
4  */
5 
6 /**
7  * @ingroup rtnl
8  * @defgroup tc Traffic Control
9  * @{
10  */
11 
12 #include "nl-default.h"
13 
14 #include <linux/if_arp.h>
15 #include <linux/gen_stats.h>
16 
17 #include <linux/atm.h>
18 
19 #include <netlink/netlink.h>
20 #include <netlink/utils.h>
21 #include <netlink/route/rtnl.h>
22 #include <netlink/route/link.h>
23 #include <netlink/route/tc.h>
24 
25 #include "tc-api.h"
26 
27 /** @cond SKIP */
28 
29 static struct nl_list_head tc_ops_list[__RTNL_TC_TYPE_MAX];
30 static struct rtnl_tc_type_ops *tc_type_ops[__RTNL_TC_TYPE_MAX];
31 
32 static struct nla_policy tc_policy[TCA_MAX+1] = {
33 	[TCA_KIND]	= { .type = NLA_STRING,
34 			    .maxlen = TCKINDSIZ },
35 	[TCA_CHAIN]	= { .type = NLA_U32 },
36 	[TCA_STATS]	= { .minlen = sizeof(struct tc_stats) },
37 	[TCA_STATS2]	= { .type = NLA_NESTED },
38 };
39 
tca_parse(struct nlattr ** tb,int maxattr,struct rtnl_tc * g,const struct nla_policy * policy)40 int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tc *g,
41 	      const struct nla_policy *policy)
42 {
43 
44 	if (g->ce_mask & TCA_ATTR_OPTS)
45 		return nla_parse(tb, maxattr,
46 				 (struct nlattr *) g->tc_opts->d_data,
47 				 g->tc_opts->d_size, policy);
48 	else {
49 		/* Ugly but tb[] must be in a defined state even if no
50 		 * attributes can be found. */
51 		memset(tb, 0, sizeof(struct nlattr *) * (maxattr + 1));
52 		return 0;
53 	}
54 }
55 
56 static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = {
57 	[TCA_STATS_BASIC]    = { .minlen = sizeof(struct gnet_stats_basic) },
58 	[TCA_STATS_RATE_EST] = { .minlen = sizeof(struct gnet_stats_rate_est) },
59 	[TCA_STATS_QUEUE]    = { .minlen = sizeof(struct gnet_stats_queue) },
60 };
61 
rtnl_tc_msg_parse(struct nlmsghdr * n,struct rtnl_tc * tc)62 int rtnl_tc_msg_parse(struct nlmsghdr *n, struct rtnl_tc *tc)
63 {
64 	struct nl_cache *link_cache;
65 	struct rtnl_tc_ops *ops;
66 	struct nlattr *tb[TCA_MAX + 1];
67 	char kind[TCKINDSIZ];
68 	struct tcmsg *tm;
69 	int err;
70 
71 	tc->ce_msgtype = n->nlmsg_type;
72 
73 	err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy);
74 	if (err < 0)
75 		return err;
76 
77 	if (tb[TCA_KIND] == NULL)
78 		return -NLE_MISSING_ATTR;
79 
80 	nla_strlcpy(kind, tb[TCA_KIND], sizeof(kind));
81 	rtnl_tc_set_kind(tc, kind);
82 
83 	if (tb[TCA_CHAIN])
84 	        rtnl_tc_set_chain(tc, nla_get_u32(tb[TCA_CHAIN]));
85 
86 	tm = nlmsg_data(n);
87 	tc->tc_family  = tm->tcm_family;
88 	tc->tc_ifindex = tm->tcm_ifindex;
89 	tc->tc_handle  = tm->tcm_handle;
90 	tc->tc_parent  = tm->tcm_parent;
91 	tc->tc_info    = tm->tcm_info;
92 
93 	tc->ce_mask |= (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE|
94 		        TCA_ATTR_PARENT | TCA_ATTR_INFO);
95 
96 	if (tb[TCA_OPTIONS]) {
97 		tc->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]);
98 		if (!tc->tc_opts)
99 			return -NLE_NOMEM;
100 		tc->ce_mask |= TCA_ATTR_OPTS;
101 	}
102 
103 	if (tb[TCA_STATS2]) {
104 		struct nlattr *tbs[TCA_STATS_MAX + 1];
105 
106 		err = nla_parse_nested(tbs, TCA_STATS_MAX, tb[TCA_STATS2],
107 				       tc_stats2_policy);
108 		if (err < 0)
109 			return err;
110 
111 		if (tbs[TCA_STATS_BASIC]) {
112 			struct gnet_stats_basic bs;
113 
114 			memcpy(&bs, nla_data(tbs[TCA_STATS_BASIC]), sizeof(bs));
115 			tc->tc_stats[RTNL_TC_BYTES]	= bs.bytes;
116 			tc->tc_stats[RTNL_TC_PACKETS]	= bs.packets;
117 		}
118 
119 		if (tbs[TCA_STATS_RATE_EST]) {
120 			struct gnet_stats_rate_est *re;
121 
122 			re = nla_data(tbs[TCA_STATS_RATE_EST]);
123 			tc->tc_stats[RTNL_TC_RATE_BPS]	= re->bps;
124 			tc->tc_stats[RTNL_TC_RATE_PPS]	= re->pps;
125 		}
126 
127 		if (tbs[TCA_STATS_QUEUE]) {
128 			struct gnet_stats_queue *q;
129 
130 			q = nla_data(tbs[TCA_STATS_QUEUE]);
131 			tc->tc_stats[RTNL_TC_QLEN]	= q->qlen;
132 			tc->tc_stats[RTNL_TC_BACKLOG]	= q->backlog;
133 			tc->tc_stats[RTNL_TC_DROPS]	= q->drops;
134 			tc->tc_stats[RTNL_TC_REQUEUES]	= q->requeues;
135 			tc->tc_stats[RTNL_TC_OVERLIMITS]	= q->overlimits;
136 		}
137 
138 		tc->ce_mask |= TCA_ATTR_STATS;
139 
140 		if (tbs[TCA_STATS_APP]) {
141 			tc->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]);
142 			if (tc->tc_xstats == NULL)
143 				return -NLE_NOMEM;
144 			tc->ce_mask |= TCA_ATTR_XSTATS;
145 		} else
146 			goto compat_xstats;
147 	} else {
148 		if (tb[TCA_STATS]) {
149 			struct tc_stats st;
150 
151 			memcpy(&st, nla_data(tb[TCA_STATS]), sizeof(st));
152 			tc->tc_stats[RTNL_TC_BYTES]	= st.bytes;
153 			tc->tc_stats[RTNL_TC_PACKETS]	= st.packets;
154 			tc->tc_stats[RTNL_TC_RATE_BPS]	= st.bps;
155 			tc->tc_stats[RTNL_TC_RATE_PPS]	= st.pps;
156 			tc->tc_stats[RTNL_TC_QLEN]	= st.qlen;
157 			tc->tc_stats[RTNL_TC_BACKLOG]	= st.backlog;
158 			tc->tc_stats[RTNL_TC_DROPS]	= st.drops;
159 			tc->tc_stats[RTNL_TC_OVERLIMITS]= st.overlimits;
160 
161 			tc->ce_mask |= TCA_ATTR_STATS;
162 		}
163 
164 compat_xstats:
165 		if (tb[TCA_XSTATS]) {
166 			tc->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]);
167 			if (tc->tc_xstats == NULL)
168 				return -NLE_NOMEM;
169 			tc->ce_mask |= TCA_ATTR_XSTATS;
170 		}
171 	}
172 
173 	ops = rtnl_tc_get_ops(tc);
174 	if (ops && ops->to_msg_parser) {
175 		void *data = rtnl_tc_data(tc);
176 
177 		if (!data)
178 			return -NLE_NOMEM;
179 
180 		err = ops->to_msg_parser(tc, data);
181 		if (err < 0)
182 			return err;
183 	}
184 
185 	if ((link_cache = __nl_cache_mngt_require("route/link"))) {
186 		struct rtnl_link *link;
187 
188 		if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) {
189 			rtnl_tc_set_link(tc, link);
190 
191 			/* rtnl_tc_set_link incs refcnt */
192 			rtnl_link_put(link);
193 		}
194 	}
195 
196 	return 0;
197 }
198 
rtnl_tc_msg_build(struct rtnl_tc * tc,int type,int flags,struct nl_msg ** result)199 int rtnl_tc_msg_build(struct rtnl_tc *tc, int type, int flags,
200 		      struct nl_msg **result)
201 {
202 	struct nl_msg *msg;
203 	struct rtnl_tc_ops *ops;
204 	struct tcmsg tchdr = {
205 		.tcm_family = AF_UNSPEC,
206 		.tcm_ifindex = tc->tc_ifindex,
207 		.tcm_handle = tc->tc_handle,
208 		.tcm_parent = tc->tc_parent,
209 	};
210 	int err;
211 
212 	msg = nlmsg_alloc_simple(type, flags);
213 	if (!msg)
214 		return -NLE_NOMEM;
215 
216 	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
217 		err = -NLE_MSGSIZE;
218 		goto out_err;
219 	}
220 
221 	if (tc->ce_mask & TCA_ATTR_KIND)
222 		NLA_PUT_STRING(msg, TCA_KIND, tc->tc_kind);
223 
224 	if (tc->ce_mask & TCA_ATTR_CHAIN)
225 	        NLA_PUT_U32(msg, TCA_CHAIN, tc->tc_chain);
226 
227 	ops = rtnl_tc_get_ops(tc);
228 	if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) {
229 		struct nlattr *opts;
230 		void *data = rtnl_tc_data(tc);
231 
232 		if (ops->to_msg_fill) {
233 			if (!(opts = nla_nest_start(msg, TCA_OPTIONS))) {
234 				err = -NLE_NOMEM;
235 				goto out_err;
236 			}
237 
238 			if ((err = ops->to_msg_fill(tc, data, msg)) < 0)
239 				goto out_err;
240 
241 			if (strcmp("cgroup", tc->tc_kind))
242 				nla_nest_end(msg, opts);
243 			else
244 				nla_nest_end_keep_empty(msg, opts);
245 		} else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0)
246 			goto out_err;
247 	}
248 
249 	*result = msg;
250 	return 0;
251 
252 nla_put_failure:
253 	err = -NLE_NOMEM;
254 out_err:
255 	nlmsg_free(msg);
256 	return err;
257 }
258 
259 
260 /** @endcond */
261 
262 /**
263  * @name Attributes
264  * @{
265  */
266 
267 /**
268  * Set interface index of traffic control object
269  * @arg tc		traffic control object
270  * @arg ifindex		interface index.
271  *
272  * Sets the interface index of a traffic control object. The interface
273  * index defines the network device which this tc object is attached to.
274  * This function will overwrite any network device assigned with previous
275  * calls to rtnl_tc_set_ifindex() or rtnl_tc_set_link().
276  */
rtnl_tc_set_ifindex(struct rtnl_tc * tc,int ifindex)277 void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex)
278 {
279 	/* Obsolete possible old link reference */
280 	rtnl_link_put(tc->tc_link);
281 	tc->tc_link = NULL;
282 	tc->ce_mask &= ~TCA_ATTR_LINK;
283 
284 	tc->tc_ifindex = ifindex;
285 	tc->ce_mask |= TCA_ATTR_IFINDEX;
286 }
287 
288 /**
289  * Return interface index of traffic control object
290  * @arg tc		traffic control object
291  */
rtnl_tc_get_ifindex(struct rtnl_tc * tc)292 int rtnl_tc_get_ifindex(struct rtnl_tc *tc)
293 {
294 	return tc->tc_ifindex;
295 }
296 
297 /**
298  * Set link of traffic control object
299  * @arg tc		traffic control object
300  * @arg link		link object
301  *
302  * Sets the link of a traffic control object. This function serves
303  * the same purpose as rtnl_tc_set_ifindex() but due to the continued
304  * allowed access to the link object it gives it the possibility to
305  * retrieve sane default values for the the MTU and the linktype.
306  * Always prefer this function over rtnl_tc_set_ifindex() if you can
307  * spare to have an additional link object around.
308  */
rtnl_tc_set_link(struct rtnl_tc * tc,struct rtnl_link * link)309 void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link)
310 {
311 	rtnl_link_put(tc->tc_link);
312 
313 	if (!link)
314 		return;
315 	if (!link->l_index)
316 		BUG();
317 
318 	nl_object_get(OBJ_CAST(link));
319 	tc->tc_link = link;
320 	tc->tc_ifindex = link->l_index;
321 	tc->ce_mask |= TCA_ATTR_LINK | TCA_ATTR_IFINDEX;
322 }
323 
324 /**
325  * Get link of traffic control object
326  * @arg tc		traffic control object
327  *
328  * Returns the link of a traffic control object. The link is only
329  * returned if it has been set before via rtnl_tc_set_link() or
330  * if a link cache was available while parsing the tc object. This
331  * function may still return NULL even if an ifindex is assigned to
332  * the tc object. It will _not_ look up the link by itself.
333  *
334  * @note The returned link will have its reference counter incremented.
335  *       It is in the responsibility of the caller to return the
336  *       reference.
337  *
338  * @return link object or NULL if not set.
339  */
rtnl_tc_get_link(struct rtnl_tc * tc)340 struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *tc)
341 {
342 	if (tc->tc_link) {
343 		nl_object_get(OBJ_CAST(tc->tc_link));
344 		return tc->tc_link;
345 	}
346 
347 	return NULL;
348 }
349 
350 /**
351  * Set the Maximum Transmission Unit (MTU) of traffic control object
352  * @arg tc		traffic control object
353  * @arg mtu		largest packet size expected
354  *
355  * Sets the MTU of a traffic control object. Not all traffic control
356  * objects will make use of this but it helps while calculating rate
357  * tables. This value is typically derived directly from the link
358  * the tc object is attached to if the link has been assigned via
359  * rtnl_tc_set_link(). It is usually not necessary to set the MTU
360  * manually, this function is provided to allow overwriting the derived
361  * value.
362  */
rtnl_tc_set_mtu(struct rtnl_tc * tc,uint32_t mtu)363 void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu)
364 {
365 	tc->tc_mtu = mtu;
366 	tc->ce_mask |= TCA_ATTR_MTU;
367 }
368 
369 /**
370  * Return the MTU of traffic control object
371  * @arg tc		traffic control object
372  *
373  * Returns the MTU of a traffic control object which has been set via:
374  * -# User specified value set via rtnl_tc_set_mtu()
375  * -# Dervied from link set via rtnl_tc_set_link()
376  * -# Fall back to default: ethernet = 1500
377  */
rtnl_tc_get_mtu(struct rtnl_tc * tc)378 uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc)
379 {
380 	if (tc->ce_mask & TCA_ATTR_MTU)
381 		return tc->tc_mtu;
382 	else if (tc->ce_mask & TCA_ATTR_LINK)
383 		return tc->tc_link->l_mtu;
384 	else
385 		return 1500; /* default to ethernet */
386 }
387 
388 /**
389  * Set the Minimum Packet Unit (MPU) of a traffic control object
390  * @arg tc		traffic control object
391  * @arg mpu		minimum packet size expected
392  *
393  * Sets the MPU of a traffic contorl object. It specifies the minimum
394  * packet size to ever hit this traffic control object. Not all traffic
395  * control objects will make use of this but it helps while calculating
396  * rate tables.
397  */
rtnl_tc_set_mpu(struct rtnl_tc * tc,uint32_t mpu)398 void rtnl_tc_set_mpu(struct rtnl_tc *tc, uint32_t mpu)
399 {
400 	tc->tc_mpu = mpu;
401 	tc->ce_mask |= TCA_ATTR_MPU;
402 }
403 
404 /**
405  * Return the Minimum Packet Unit (MPU) of a traffic control object
406  * @arg tc		traffic control object
407  *
408  * @return The MPU previously set via rtnl_tc_set_mpu() or 0.
409  */
rtnl_tc_get_mpu(struct rtnl_tc * tc)410 uint32_t rtnl_tc_get_mpu(struct rtnl_tc *tc)
411 {
412 	return tc->tc_mpu;
413 }
414 
415 /**
416  * Set per packet overhead of a traffic control object
417  * @arg tc		traffic control object
418  * @arg overhead	overhead per packet in bytes
419  *
420  * Sets the per packet overhead in bytes occuring on the link not seen
421  * by the kernel. This value can be used to correct size calculations
422  * if the packet size on the wire does not match the packet sizes seen
423  * in the network stack. Not all traffic control objects will make use
424  * this but it helps while calculating accurate packet sizes in the
425  * kernel.
426  */
rtnl_tc_set_overhead(struct rtnl_tc * tc,uint32_t overhead)427 void rtnl_tc_set_overhead(struct rtnl_tc *tc, uint32_t overhead)
428 {
429 	tc->tc_overhead = overhead;
430 	tc->ce_mask |= TCA_ATTR_OVERHEAD;
431 }
432 
433 /**
434  * Return per packet overhead of a traffic control object
435  * @arg tc		traffic control object
436  *
437  * @return The overhead previously set by rtnl_tc_set_overhead() or 0.
438  */
rtnl_tc_get_overhead(struct rtnl_tc * tc)439 uint32_t rtnl_tc_get_overhead(struct rtnl_tc *tc)
440 {
441 	return tc->tc_overhead;
442 }
443 
444 /**
445  * Set the linktype of a traffic control object
446  * @arg tc		traffic control object
447  * @arg type		type of link (e.g. ARPHRD_ATM, ARPHRD_ETHER)
448  *
449  * Overwrites the type of link this traffic control object is attached to.
450  * This value is typically derived from the link this tc object is attached
451  * if the link has been assigned via rtnl_tc_set_link(). It is usually not
452  * necessary to set the linktype manually. This function is provided to
453  * allow overwriting the linktype.
454  */
rtnl_tc_set_linktype(struct rtnl_tc * tc,uint32_t type)455 void rtnl_tc_set_linktype(struct rtnl_tc *tc, uint32_t type)
456 {
457 	tc->tc_linktype = type;
458 	tc->ce_mask |= TCA_ATTR_LINKTYPE;
459 }
460 
461 /**
462  * Return the linktype of a traffic control object
463  * @arg tc		traffic control object
464  *
465  * Returns the linktype of the link the traffic control object is attached to:
466  * -# User specified value via rtnl_tc_set_linktype()
467  * -# Value derived from link set via rtnl_tc_set_link()
468  * -# Default fall-back: ARPHRD_ETHER
469  */
rtnl_tc_get_linktype(struct rtnl_tc * tc)470 uint32_t rtnl_tc_get_linktype(struct rtnl_tc *tc)
471 {
472 	if (tc->ce_mask & TCA_ATTR_LINKTYPE)
473 		return tc->tc_linktype;
474 	else if (tc->ce_mask & TCA_ATTR_LINK)
475 		return tc->tc_link->l_arptype;
476 	else
477 		return ARPHRD_ETHER; /* default to ethernet */
478 }
479 
480 /**
481  * Set identifier of traffic control object
482  * @arg tc		traffic control object
483  * @arg id		unique identifier
484  */
rtnl_tc_set_handle(struct rtnl_tc * tc,uint32_t id)485 void rtnl_tc_set_handle(struct rtnl_tc *tc, uint32_t id)
486 {
487 	tc->tc_handle = id;
488 	tc->ce_mask |= TCA_ATTR_HANDLE;
489 }
490 
491 /**
492  * Return identifier of a traffic control object
493  * @arg tc		traffic control object
494  */
rtnl_tc_get_handle(struct rtnl_tc * tc)495 uint32_t rtnl_tc_get_handle(struct rtnl_tc *tc)
496 {
497 	return tc->tc_handle;
498 }
499 
500 /**
501  * Set the parent identifier of a traffic control object
502  * @arg tc		traffic control object
503  * @arg parent		identifier of parent traffif control object
504  *
505  */
rtnl_tc_set_parent(struct rtnl_tc * tc,uint32_t parent)506 void rtnl_tc_set_parent(struct rtnl_tc *tc, uint32_t parent)
507 {
508 	tc->tc_parent = parent;
509 	tc->ce_mask |= TCA_ATTR_PARENT;
510 }
511 
512 /**
513  * Return parent identifier of a traffic control object
514  * @arg tc		traffic control object
515  */
rtnl_tc_get_parent(struct rtnl_tc * tc)516 uint32_t rtnl_tc_get_parent(struct rtnl_tc *tc)
517 {
518 	return tc->tc_parent;
519 }
520 
521 /**
522  * Define the type of traffic control object
523  * @arg tc		traffic control object
524  * @arg kind		name of the tc object type
525  *
526  * @return 0 on success or a negative error code
527  */
rtnl_tc_set_kind(struct rtnl_tc * tc,const char * kind)528 int rtnl_tc_set_kind(struct rtnl_tc *tc, const char *kind)
529 {
530 	if (tc->ce_mask & TCA_ATTR_KIND)
531 		return -NLE_EXIST;
532 
533 	if (   !kind
534 	    || strlen (kind) >= sizeof (tc->tc_kind))
535 		return -NLE_INVAL;
536 
537 	_nl_strncpy_assert(tc->tc_kind, kind, sizeof(tc->tc_kind));
538 
539 	tc->ce_mask |= TCA_ATTR_KIND;
540 
541 	/* Force allocation of data */
542 	rtnl_tc_data(tc);
543 
544 	return 0;
545 }
546 
547 /**
548  * Return kind of traffic control object
549  * @arg tc		traffic control object
550  *
551  * @return Kind of traffic control object or NULL if not set.
552  */
rtnl_tc_get_kind(struct rtnl_tc * tc)553 char *rtnl_tc_get_kind(struct rtnl_tc *tc)
554 {
555 	if (tc->ce_mask & TCA_ATTR_KIND)
556 		return tc->tc_kind;
557 	else
558 		return NULL;
559 }
560 
561 /**
562  * Return value of a statistical counter of a traffic control object
563  * @arg tc		traffic control object
564  * @arg id		identifier of statistical counter
565  *
566  * @return Value of requested statistic counter or 0.
567  */
rtnl_tc_get_stat(struct rtnl_tc * tc,enum rtnl_tc_stat id)568 uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id)
569 {
570 	if ((unsigned int) id > RTNL_TC_STATS_MAX)
571 		return 0;
572 
573 	return tc->tc_stats[id];
574 }
575 
576 /**
577  * Set the chain index of a traffic control object
578  * @arg tc		traffic control object
579  * @arg chain		chain index of traffic control object
580  *
581  */
rtnl_tc_set_chain(struct rtnl_tc * tc,uint32_t chain)582 void rtnl_tc_set_chain(struct rtnl_tc *tc, uint32_t chain)
583 {
584 	tc->tc_chain = chain;
585 	tc->ce_mask |= TCA_ATTR_CHAIN;
586 }
587 
588 /**
589  * Return chain index of traffic control object
590  * @arg tc		traffic control object
591  * @arg out_value       output argument.
592  *
593  * @return 0 of the output value was successfully returned, or a negative
594  *   error code on failure.
595  */
rtnl_tc_get_chain(struct rtnl_tc * tc,uint32_t * out_value)596 int rtnl_tc_get_chain(struct rtnl_tc *tc, uint32_t *out_value)
597 {
598 	if (!(tc->ce_mask & TCA_ATTR_CHAIN))
599 		return -NLE_MISSING_ATTR;
600 	*out_value = tc->tc_chain;
601 	return 0;
602 }
603 
604 /** @} */
605 
606 /**
607  * @name Utilities
608  * @{
609  */
610 
611 static const struct trans_tbl tc_stats[] = {
612 	__ADD(RTNL_TC_PACKETS, packets),
613 	__ADD(RTNL_TC_BYTES, bytes),
614 	__ADD(RTNL_TC_RATE_BPS, rate_bps),
615 	__ADD(RTNL_TC_RATE_PPS, rate_pps),
616 	__ADD(RTNL_TC_QLEN, qlen),
617 	__ADD(RTNL_TC_BACKLOG, backlog),
618 	__ADD(RTNL_TC_DROPS, drops),
619 	__ADD(RTNL_TC_REQUEUES, requeues),
620 	__ADD(RTNL_TC_OVERLIMITS, overlimits),
621 };
622 
rtnl_tc_stat2str(enum rtnl_tc_stat st,char * buf,size_t len)623 char *rtnl_tc_stat2str(enum rtnl_tc_stat st, char *buf, size_t len)
624 {
625 	return __type2str(st, buf, len, tc_stats, ARRAY_SIZE(tc_stats));
626 }
627 
rtnl_tc_str2stat(const char * name)628 int rtnl_tc_str2stat(const char *name)
629 {
630 	return __str2type(name, tc_stats, ARRAY_SIZE(tc_stats));
631 }
632 
633 /**
634  * Calculate time required to transmit buffer at a specific rate
635  * @arg bufsize		Size of buffer to be transmited in bytes.
636  * @arg rate		Transmit rate in bytes per second.
637  *
638  * Calculates the number of micro seconds required to transmit a
639  * specific buffer at a specific transmit rate.
640  *
641  * @f[
642  *   txtime=\frac{bufsize}{rate}10^6
643  * @f]
644  *
645  * @return Required transmit time in micro seconds.
646  */
rtnl_tc_calc_txtime(int bufsize,int rate)647 int rtnl_tc_calc_txtime(int bufsize, int rate)
648 {
649 	return ((double) bufsize / (double) rate) * 1000000.0;
650 }
651 
652 /**
653  * Calculate buffer size able to transmit in a specific time and rate.
654  * @arg txtime		Available transmit time in micro seconds.
655  * @arg rate		Transmit rate in bytes per second.
656  *
657  * Calculates the size of the buffer that can be transmitted in a
658  * specific time period at a specific transmit rate.
659  *
660  * @f[
661  *   bufsize=\frac{{txtime} \times {rate}}{10^6}
662  * @f]
663  *
664  * @return Size of buffer in bytes.
665  */
rtnl_tc_calc_bufsize(int txtime,int rate)666 int rtnl_tc_calc_bufsize(int txtime, int rate)
667 {
668 	return ((double) txtime * (double) rate) / 1000000.0;
669 }
670 
671 /**
672  * Calculate the binary logarithm for a specific cell size
673  * @arg cell_size	Size of cell, must be a power of two.
674  * @return Binary logarithm of cell size or a negative error code.
675  */
rtnl_tc_calc_cell_log(int cell_size)676 int rtnl_tc_calc_cell_log(int cell_size)
677 {
678 	int i;
679 
680 	for (i = 0; i < 32; i++)
681 		if ((((uint32_t)1u) << i) == ((uint32_t)cell_size))
682 			return i;
683 
684 	return -NLE_INVAL;
685 }
686 
687 
688 /** @} */
689 
690 /**
691  * @name Rate Tables
692  * @{
693  */
694 
695 /*
696  * COPYRIGHT NOTE:
697  * align_to_atm() and adjust_size() derived/coped from iproute2 source.
698  */
699 
700 /*
701  * The align to ATM cells is used for determining the (ATM) SAR
702  * alignment overhead at the ATM layer. (SAR = Segmentation And
703  * Reassembly).  This is for example needed when scheduling packet on
704  * an ADSL connection.  Note that the extra ATM-AAL overhead is _not_
705  * included in this calculation. This overhead is added in the kernel
706  * before doing the rate table lookup, as this gives better precision
707  * (as the table will always be aligned for 48 bytes).
708  *  --Hawk, d.7/11-2004. <[email protected]>
709  */
align_to_atm(unsigned int size)710 static unsigned int align_to_atm(unsigned int size)
711 {
712 	int linksize, cells;
713 	cells = size / ATM_CELL_PAYLOAD;
714 	if ((size % ATM_CELL_PAYLOAD) > 0)
715 		cells++;
716 
717 	linksize = cells * ATM_CELL_SIZE; /* Use full cell size to add ATM tax */
718 	return linksize;
719 }
720 
adjust_size(unsigned int size,unsigned int mpu,uint32_t linktype)721 static unsigned int adjust_size(unsigned int size, unsigned int mpu,
722 				uint32_t linktype)
723 {
724 	if (size < mpu)
725 		size = mpu;
726 
727 	switch (linktype) {
728 	case ARPHRD_ATM:
729 		return align_to_atm(size);
730 
731 	case ARPHRD_ETHER:
732 	default:
733 		return size;
734 	}
735 }
736 
737 /**
738  * Compute a transmission time lookup table
739  * @arg tc		traffic control object
740  * @arg spec		Rate specification
741  * @arg dst		Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[].
742  *
743  * Computes a table of RTNL_TC_RTABLE_SIZE entries specyfing the
744  * transmission times for various packet sizes, e.g. the transmission
745  * time for a packet of size \c pktsize could be looked up:
746  * @code
747  * txtime = table[pktsize >> log2(mtu)];
748  * @endcode
749  */
rtnl_tc_build_rate_table(struct rtnl_tc * tc,struct rtnl_ratespec * spec,uint32_t * dst)750 int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *spec,
751 			     uint32_t *dst)
752 {
753 	uint32_t mtu = rtnl_tc_get_mtu(tc);
754 	uint32_t linktype = rtnl_tc_get_linktype(tc);
755 	uint8_t cell_log = spec->rs_cell_log;
756 	unsigned int size, i;
757 
758 	spec->rs_mpu = rtnl_tc_get_mpu(tc);
759 	spec->rs_overhead = rtnl_tc_get_overhead(tc);
760 
761 	if (mtu == 0)
762 		mtu = 2047;
763 
764 	if (cell_log == UINT8_MAX) {
765 		/*
766 		 * cell_log not specified, calculate it. It has to specify the
767 		 * minimum number of rshifts required to break the MTU to below
768 		 * RTNL_TC_RTABLE_SIZE.
769 		 */
770 		cell_log = 0;
771 		while ((mtu >> cell_log) >= RTNL_TC_RTABLE_SIZE)
772 			cell_log++;
773 	}
774 
775 	for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) {
776 		size = adjust_size((i + 1) << cell_log, spec->rs_mpu, linktype);
777 		dst[i] = nl_us2ticks(rtnl_tc_calc_txtime64(size, spec->rs_rate64));
778 	}
779 
780 	spec->rs_cell_align = -1;
781 	spec->rs_cell_log = cell_log;
782 
783 	return 0;
784 }
785 
786 /** @} */
787 
788 /**
789  * @name TC implementation of cache functions
790  */
791 
rtnl_tc_free_data(struct nl_object * obj)792 void rtnl_tc_free_data(struct nl_object *obj)
793 {
794 	struct rtnl_tc *tc = TC_CAST(obj);
795 	struct rtnl_tc_ops *ops;
796 
797 	rtnl_link_put(tc->tc_link);
798 	nl_data_free(tc->tc_opts);
799 	nl_data_free(tc->tc_xstats);
800 
801 	if (tc->tc_subdata) {
802 		ops = rtnl_tc_get_ops(tc);
803 		if (ops && ops->to_free_data)
804 			ops->to_free_data(tc, nl_data_get(tc->tc_subdata));
805 
806 		nl_data_free(tc->tc_subdata);
807 	}
808 }
809 
rtnl_tc_clone(struct nl_object * dstobj,struct nl_object * srcobj)810 int rtnl_tc_clone(struct nl_object *dstobj, struct nl_object *srcobj)
811 {
812 	struct rtnl_tc *dst = TC_CAST(dstobj);
813 	struct rtnl_tc *src = TC_CAST(srcobj);
814 	struct rtnl_tc_ops *ops;
815 
816 	dst->tc_opts = NULL;
817 	dst->tc_xstats = NULL;
818 	dst->tc_subdata = NULL;
819 	dst->tc_link = NULL;
820 	dst->tc_ops = NULL;
821 
822 	if (src->tc_link) {
823 		nl_object_get(OBJ_CAST(src->tc_link));
824 		dst->tc_link = src->tc_link;
825 	}
826 
827 	dst->ce_mask &= ~(TCA_ATTR_OPTS |
828 	                  TCA_ATTR_XSTATS);
829 
830 	if (src->tc_opts) {
831 		dst->tc_opts = nl_data_clone(src->tc_opts);
832 		if (!dst->tc_opts)
833 			return -NLE_NOMEM;
834 		dst->ce_mask |= TCA_ATTR_OPTS;
835 	}
836 
837 	if (src->tc_xstats) {
838 		dst->tc_xstats = nl_data_clone(src->tc_xstats);
839 		if (!dst->tc_xstats)
840 			return -NLE_NOMEM;
841 		dst->ce_mask |= TCA_ATTR_XSTATS;
842 	}
843 
844 	if (src->tc_subdata) {
845 		if (!(dst->tc_subdata = nl_data_clone(src->tc_subdata))) {
846 			return -NLE_NOMEM;
847 		}
848 
849 		/* Warning: if the data contains pointer, then at this point, dst->tc_subdata
850 		 * will alias those pointers.
851 		 *
852 		 * ops->to_clone() MUST fix that.
853 		 *
854 		 * If the type is actually "struct rtnl_act", then to_clone() must also
855 		 * fix dangling "a_next" pointer. */
856 
857 		ops = rtnl_tc_get_ops(src);
858 		if (ops && ops->to_clone) {
859 			return ops->to_clone(rtnl_tc_data(dst), rtnl_tc_data(src));
860 		}
861 	}
862 
863 	return 0;
864 }
865 
tc_dump(struct rtnl_tc * tc,enum nl_dump_type type,struct nl_dump_params * p)866 static int tc_dump(struct rtnl_tc *tc, enum nl_dump_type type,
867 		   struct nl_dump_params *p)
868 {
869 	struct rtnl_tc_type_ops *type_ops;
870 	struct rtnl_tc_ops *ops;
871 	void *data = rtnl_tc_data(tc);
872 
873 	type_ops = tc_type_ops[tc->tc_type];
874 	if (type_ops && type_ops->tt_dump[type])
875 		type_ops->tt_dump[type](tc, p);
876 
877 	ops = rtnl_tc_get_ops(tc);
878 	if (ops && ops->to_dump[type]) {
879 		ops->to_dump[type](tc, data, p);
880 		return 1;
881 	}
882 
883 	return 0;
884 }
885 
rtnl_tc_dump_line(struct nl_object * obj,struct nl_dump_params * p)886 void rtnl_tc_dump_line(struct nl_object *obj, struct nl_dump_params *p)
887 {
888 	struct rtnl_tc_type_ops *type_ops;
889 	struct rtnl_tc *tc = TC_CAST(obj);
890 	struct nl_cache *link_cache;
891 	char buf[32];
892 
893 	nl_new_line(p);
894 
895 	type_ops = tc_type_ops[tc->tc_type];
896 	if (type_ops && type_ops->tt_dump_prefix)
897 		nl_dump(p, "%s ", type_ops->tt_dump_prefix);
898 
899 	nl_dump(p, "%s ", tc->tc_kind);
900 
901 	if ((link_cache = nl_cache_mngt_require_safe("route/link"))) {
902 		nl_dump(p, "dev %s ",
903 			rtnl_link_i2name(link_cache, tc->tc_ifindex,
904 					 buf, sizeof(buf)));
905 	} else
906 		nl_dump(p, "dev %u ", tc->tc_ifindex);
907 
908 	nl_dump(p, "id %s ",
909 		rtnl_tc_handle2str(tc->tc_handle, buf, sizeof(buf)));
910 
911 	nl_dump(p, "parent %s",
912 		rtnl_tc_handle2str(tc->tc_parent, buf, sizeof(buf)));
913 
914 	tc_dump(tc, NL_DUMP_LINE, p);
915 	nl_dump(p, "\n");
916 
917 	if (link_cache)
918 		nl_cache_put(link_cache);
919 }
920 
rtnl_tc_dump_details(struct nl_object * obj,struct nl_dump_params * p)921 void rtnl_tc_dump_details(struct nl_object *obj, struct nl_dump_params *p)
922 {
923 	struct rtnl_tc *tc = TC_CAST(obj);
924 
925 	rtnl_tc_dump_line(OBJ_CAST(tc), p);
926 
927 	nl_dump_line(p, "  ");
928 
929 	if (tc->ce_mask & TCA_ATTR_MTU)
930 		nl_dump(p, " mtu %u", tc->tc_mtu);
931 
932 	if (tc->ce_mask & TCA_ATTR_MPU)
933 		nl_dump(p, " mpu %u", tc->tc_mpu);
934 
935 	if (tc->ce_mask & TCA_ATTR_OVERHEAD)
936 		nl_dump(p, " overhead %u", tc->tc_overhead);
937 
938 	if (!tc_dump(tc, NL_DUMP_DETAILS, p))
939 		nl_dump(p, "no options");
940 	nl_dump(p, "\n");
941 }
942 
rtnl_tc_dump_stats(struct nl_object * obj,struct nl_dump_params * p)943 void rtnl_tc_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
944 {
945 	struct rtnl_tc *tc = TC_CAST(obj);
946 	char *unit;
947 	float res;
948 
949 	rtnl_tc_dump_details(OBJ_CAST(tc), p);
950 
951 	nl_dump_line(p,
952 	             "  stats: %-14s %-10s   %-10s %-10s %-10s %-10s\n",
953 	             "bytes", "packets", "drops", "overlimits", "qlen", "backlog");
954 
955 	res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_BYTES], &unit);
956 
957 	nl_dump_line(
958 		p,
959 		"       %10.2f %3s   %10llu   %-10llu %-10llu %-10llu %-10llu\n",
960 		res, unit, (long long unsigned)tc->tc_stats[RTNL_TC_PACKETS],
961 		(long long unsigned)tc->tc_stats[RTNL_TC_DROPS],
962 		(long long unsigned)tc->tc_stats[RTNL_TC_OVERLIMITS],
963 		(long long unsigned)tc->tc_stats[RTNL_TC_QLEN],
964 		(long long unsigned)tc->tc_stats[RTNL_TC_BACKLOG]);
965 
966 	res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_RATE_BPS], &unit);
967 
968 	nl_dump_line(p, "       %10.2f %3s/s %10llu/s\n", res, unit,
969 		     (long long unsigned)tc->tc_stats[RTNL_TC_RATE_PPS]);
970 }
971 
rtnl_tc_compare(struct nl_object * aobj,struct nl_object * bobj,uint64_t attrs,int flags)972 uint64_t rtnl_tc_compare(struct nl_object *aobj, struct nl_object *bobj,
973 			 uint64_t attrs, int flags)
974 {
975 	struct rtnl_tc *a = TC_CAST(aobj);
976 	struct rtnl_tc *b = TC_CAST(bobj);
977 	uint64_t diff = 0;
978 
979 #define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR)
980 	diff |= _DIFF(TCA_ATTR_HANDLE, a->tc_handle != b->tc_handle);
981 	diff |= _DIFF(TCA_ATTR_PARENT, a->tc_parent != b->tc_parent);
982 	diff |= _DIFF(TCA_ATTR_IFINDEX, a->tc_ifindex != b->tc_ifindex);
983 	diff |= _DIFF(TCA_ATTR_KIND, strcmp(a->tc_kind, b->tc_kind));
984 #undef _DIFF
985 
986 	return diff;
987 }
988 
989 /** @} */
990 
991 /**
992  * @name Modules API
993  */
994 
rtnl_tc_lookup_ops(enum rtnl_tc_type type,const char * kind)995 struct rtnl_tc_ops *rtnl_tc_lookup_ops(enum rtnl_tc_type type, const char *kind)
996 {
997 	struct rtnl_tc_ops *ops;
998 
999 	nl_list_for_each_entry(ops, &tc_ops_list[type], to_list)
1000 		if (!strcmp(kind, ops->to_kind))
1001 			return ops;
1002 
1003 	return NULL;
1004 }
1005 
rtnl_tc_get_ops(struct rtnl_tc * tc)1006 struct rtnl_tc_ops *rtnl_tc_get_ops(struct rtnl_tc *tc)
1007 {
1008 	if (!tc->tc_ops)
1009 		tc->tc_ops = rtnl_tc_lookup_ops(tc->tc_type, tc->tc_kind);
1010 
1011 	return tc->tc_ops;
1012 }
1013 
1014 /**
1015  * Register a traffic control module
1016  * @arg ops		traffic control module operations
1017  */
rtnl_tc_register(struct rtnl_tc_ops * ops)1018 int rtnl_tc_register(struct rtnl_tc_ops *ops)
1019 {
1020 	static int init = 0;
1021 
1022 	/*
1023 	 * Initialiation hack, make sure list is initialized when
1024 	 * the first tc module registers. Putting this in a
1025 	 * separate _nl_init would required correct ordering of init
1026 	 * functions
1027 	 */
1028 	if (!init) {
1029 		int i;
1030 
1031 		for (i = 0; i < __RTNL_TC_TYPE_MAX; i++)
1032 			nl_init_list_head(&tc_ops_list[i]);
1033 
1034 		init = 1;
1035 	}
1036 
1037 	if (!ops->to_kind || ops->to_type > RTNL_TC_TYPE_MAX)
1038 		BUG();
1039 
1040 	if (rtnl_tc_lookup_ops(ops->to_type, ops->to_kind))
1041 		return -NLE_EXIST;
1042 
1043 	nl_list_add_tail(&ops->to_list, &tc_ops_list[ops->to_type]);
1044 
1045 	return 0;
1046 }
1047 
1048 /**
1049  * Unregister a traffic control module
1050  * @arg ops		traffic control module operations
1051  */
rtnl_tc_unregister(struct rtnl_tc_ops * ops)1052 void rtnl_tc_unregister(struct rtnl_tc_ops *ops)
1053 {
1054 	nl_list_del(&ops->to_list);
1055 }
1056 
1057 /**
1058  * Returns the private data of the traffic control object.
1059  * Contrary to rtnl_tc_data(), this returns NULL if the data is
1060  * not yet allocated
1061  * @arg tc		traffic control object
1062  *
1063  * @return pointer to the private data or NULL if not allocated.
1064  */
rtnl_tc_data_peek(struct rtnl_tc * tc)1065 void *rtnl_tc_data_peek(struct rtnl_tc *tc)
1066 {
1067 	return tc->tc_subdata ? nl_data_get(tc->tc_subdata) : NULL;
1068 }
1069 
1070 /**
1071  * Return pointer to private data of traffic control object
1072  * @arg tc		traffic control object
1073  *
1074  * Allocates the private traffic control object data section
1075  * as necessary and returns it.
1076  *
1077  * @return Pointer to private tc data or NULL if allocation failed.
1078  */
rtnl_tc_data(struct rtnl_tc * tc)1079 void *rtnl_tc_data(struct rtnl_tc *tc)
1080 {
1081 	if (!tc->tc_subdata) {
1082 		size_t size;
1083 
1084 		if (!tc->tc_ops) {
1085 			if (!rtnl_tc_get_ops(tc))
1086 				return NULL;
1087 		}
1088 
1089 		if (!(size = tc->tc_ops->to_size))
1090 			BUG();
1091 
1092 		if (!(tc->tc_subdata = nl_data_alloc(NULL, size)))
1093 			return NULL;
1094 	}
1095 
1096 	return nl_data_get(tc->tc_subdata);
1097 }
1098 
1099 /**
1100  * Check traffic control object type and return private data section
1101  * @arg tc		traffic control object
1102  * @arg ops		expected traffic control object operations
1103  * @arg err		the place where saves the error code if fails
1104  *
1105  * Checks whether the traffic control object matches the type
1106  * specified with the traffic control object operations. If the
1107  * type matches, the private tc object data is returned. If type
1108  * mismatches, APPBUG() will print a application bug warning.
1109  *
1110  * @see rtnl_tc_data()
1111  *
1112  * @return Pointer to private tc data or NULL if type mismatches.
1113  */
rtnl_tc_data_check(struct rtnl_tc * tc,struct rtnl_tc_ops * ops,int * err)1114 void *rtnl_tc_data_check(struct rtnl_tc *tc, struct rtnl_tc_ops *ops, int *err)
1115 {
1116 	void *ret;
1117 
1118 	if (tc->tc_ops != ops) {
1119 		char buf[64];
1120 
1121 		snprintf(buf, sizeof(buf),
1122 			 "tc object %p used in %s context but is of type %s",
1123 			 tc, ops->to_kind, tc->tc_ops->to_kind);
1124 		APPBUG(buf);
1125 
1126 		if (err)
1127 			*err = -NLE_OPNOTSUPP;
1128 		return NULL;
1129 	}
1130 
1131 	ret = rtnl_tc_data(tc);
1132 	if (ret == NULL) {
1133 		if (err)
1134 			*err = -NLE_NOMEM;
1135 	}
1136 
1137 	return ret;
1138 }
1139 
1140 struct nl_af_group tc_groups[] = {
1141 	{ AF_UNSPEC,	RTNLGRP_TC },
1142 	{ END_OF_GROUP_LIST },
1143 };
1144 
1145 
rtnl_tc_type_register(struct rtnl_tc_type_ops * ops)1146 void rtnl_tc_type_register(struct rtnl_tc_type_ops *ops)
1147 {
1148 	if (ops->tt_type > RTNL_TC_TYPE_MAX)
1149 		BUG();
1150 
1151 	tc_type_ops[ops->tt_type] = ops;
1152 }
1153 
rtnl_tc_type_unregister(struct rtnl_tc_type_ops * ops)1154 void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *ops)
1155 {
1156 	if (ops->tt_type > RTNL_TC_TYPE_MAX)
1157 		BUG();
1158 
1159 	tc_type_ops[ops->tt_type] = NULL;
1160 }
1161 
1162 /** @} */
1163 
1164 /** @} */
1165