1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * lib/route/class.c Traffic Classes
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation version 2.1
8 * of the License.
9 *
10 * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
11 */
12
13 /**
14 * @ingroup tc
15 * @defgroup class Traffic Classes
16 * @{
17 */
18
19 #include <netlink-private/netlink.h>
20 #include <netlink-private/tc.h>
21 #include <netlink/netlink.h>
22 #include <netlink-private/route/tc-api.h>
23 #include <netlink/route/class.h>
24 #include <netlink/route/qdisc.h>
25 #include <netlink/route/classifier.h>
26 #include <netlink/utils.h>
27
28 static struct nl_cache_ops rtnl_class_ops;
29 static struct nl_object_ops class_obj_ops;
30
class_dump_details(struct rtnl_tc * tc,struct nl_dump_params * p)31 static void class_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
32 {
33 struct rtnl_class *class = (struct rtnl_class *) tc;
34 char buf[32];
35
36 if (class->c_info)
37 nl_dump(p, "child-qdisc %s ",
38 rtnl_tc_handle2str(class->c_info, buf, sizeof(buf)));
39 }
40
41
class_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * pp)42 static int class_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
43 struct nlmsghdr *nlh, struct nl_parser_param *pp)
44 {
45 struct rtnl_class *class;
46 int err;
47
48 if (!(class = rtnl_class_alloc()))
49 return -NLE_NOMEM;
50
51 if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(class))) < 0)
52 goto errout;
53
54 err = pp->pp_cb(OBJ_CAST(class), pp);
55 errout:
56 rtnl_class_put(class);
57
58 return err;
59 }
60
class_request_update(struct nl_cache * cache,struct nl_sock * sk)61 static int class_request_update(struct nl_cache *cache, struct nl_sock *sk)
62 {
63 struct tcmsg tchdr = {
64 .tcm_family = AF_UNSPEC,
65 .tcm_ifindex = cache->c_iarg1,
66 };
67
68 return nl_send_simple(sk, RTM_GETTCLASS, NLM_F_DUMP, &tchdr,
69 sizeof(tchdr));
70 }
71
72 /**
73 * @name Allocation/Freeing
74 * @{
75 */
76
rtnl_class_alloc(void)77 struct rtnl_class *rtnl_class_alloc(void)
78 {
79 struct rtnl_tc *tc;
80
81 tc = TC_CAST(nl_object_alloc(&class_obj_ops));
82 if (tc)
83 tc->tc_type = RTNL_TC_TYPE_CLASS;
84
85 return (struct rtnl_class *) tc;
86 }
87
rtnl_class_put(struct rtnl_class * class)88 void rtnl_class_put(struct rtnl_class *class)
89 {
90 nl_object_put((struct nl_object *) class);
91 }
92
93 /** @} */
94
95
96 /**
97 * @name Addition/Modification/Deletion
98 * @{
99 */
100
class_build(struct rtnl_class * class,int type,int flags,struct nl_msg ** result)101 static int class_build(struct rtnl_class *class, int type, int flags,
102 struct nl_msg **result)
103 {
104 uint32_t needed = TCA_ATTR_PARENT | TCA_ATTR_HANDLE;
105
106 if ((class->ce_mask & needed) == needed &&
107 TC_H_MAJ(class->c_parent) && TC_H_MAJ(class->c_handle) &&
108 TC_H_MAJ(class->c_parent) != TC_H_MAJ(class->c_handle)) {
109 APPBUG("TC_H_MAJ(parent) must match TC_H_MAJ(handle)");
110 return -NLE_INVAL;
111 }
112
113 return rtnl_tc_msg_build(TC_CAST(class), type, flags, result);
114 }
115
116 /**
117 * Build a netlink message requesting the addition of a traffic class
118 * @arg class Traffic class to add
119 * @arg flags Additional netlink message flags
120 * @arg result Pointer to store resulting netlink message
121 *
122 * The behaviour of this function is identical to rtnl_class_add() with
123 * the exception that it will not send the message but return it int the
124 * provided return pointer instead.
125 *
126 * @see rtnl_class_add()
127 *
128 * @return 0 on success or a negative error code.
129 */
rtnl_class_build_add_request(struct rtnl_class * class,int flags,struct nl_msg ** result)130 int rtnl_class_build_add_request(struct rtnl_class *class, int flags,
131 struct nl_msg **result)
132 {
133 return class_build(class, RTM_NEWTCLASS, flags, result);
134 }
135
136 /**
137 * Add/Update traffic class
138 * @arg sk Netlink socket
139 * @arg class Traffic class to add
140 * @arg flags Additional netlink message flags
141 *
142 * Builds a \c RTM_NEWTCLASS netlink message requesting the addition
143 * of a new traffic class and sends the message to the kernel. The
144 * configuration of the traffic class is derived from the attributes
145 * of the specified traffic class.
146 *
147 * The following flags may be specified:
148 * - \c NLM_F_CREATE: Create traffic class if it does not exist,
149 * otherwise -NLE_OBJ_NOTFOUND is returned.
150 * - \c NLM_F_EXCL: Return -NLE_EXISTS if a traffic class with
151 * matching handle exists already.
152 *
153 * Existing traffic classes with matching handles will be updated,
154 * unless the flag \c NLM_F_EXCL is specified. If no matching traffic
155 * class exists, it will be created if the flag \c NLM_F_CREATE is set,
156 * otherwise the error -NLE_OBJ_NOTFOUND is returned.
157 *
158 * If the parent qdisc does not support classes, the error
159 * \c NLE_OPNOTSUPP is returned.
160 *
161 * After sending, the function will wait for the ACK or an eventual
162 * error message to be received and will therefore block until the
163 * operation has been completed.
164 *
165 * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
166 * this function to return immediately after sending. In this case,
167 * it is the responsibility of the caller to handle any error
168 * messages returned.
169 *
170 * @return 0 on success or a negative error code.
171 */
rtnl_class_add(struct nl_sock * sk,struct rtnl_class * class,int flags)172 int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags)
173 {
174 struct nl_msg *msg;
175 int err;
176
177 if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0)
178 return err;
179
180 return nl_send_sync(sk, msg);
181 }
182
183 /**
184 * Build netlink message requesting the deletion of a traffic class
185 * @arg class Traffic class to delete
186 * @arg result Pointer to store resulting netlink message
187 *
188 * The behaviour of this function is identical to rtnl_class_delete() with
189 * the exception that it will not send the message but return it in the
190 * provided return pointer instead.
191 *
192 * @see rtnl_class_delete()
193 *
194 * @return 0 on success or a negative error code.
195 */
rtnl_class_build_delete_request(struct rtnl_class * class,struct nl_msg ** result)196 int rtnl_class_build_delete_request(struct rtnl_class *class, struct nl_msg **result)
197 {
198 struct nl_msg *msg;
199 struct tcmsg tchdr;
200 uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE;
201
202 if ((class->ce_mask & required) != required) {
203 APPBUG("ifindex and handle must be specified");
204 return -NLE_MISSING_ATTR;
205 }
206
207 if (!(msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0)))
208 return -NLE_NOMEM;
209
210 memset(&tchdr, 0, sizeof(tchdr));
211 tchdr.tcm_family = AF_UNSPEC;
212 tchdr.tcm_ifindex = class->c_ifindex;
213 tchdr.tcm_handle = class->c_handle;
214
215 if (class->ce_mask & TCA_ATTR_PARENT)
216 tchdr.tcm_parent = class->c_parent;
217
218 if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
219 nlmsg_free(msg);
220 return -NLE_MSGSIZE;
221 }
222
223 *result = msg;
224 return 0;
225 }
226
227 /**
228 * Delete traffic class
229 * @arg sk Netlink socket
230 * @arg class Traffic class to delete
231 *
232 * Builds a \c RTM_DELTCLASS netlink message requesting the deletion
233 * of a traffic class and sends the message to the kernel.
234 *
235 * The message is constructed out of the following attributes:
236 * - \c ifindex and \c handle (required)
237 * - \c parent (optional, must match if provided)
238 *
239 * All other class attributes including all class type specific
240 * attributes are ignored.
241 *
242 * After sending, the function will wait for the ACK or an eventual
243 * error message to be received and will therefore block until the
244 * operation has been completed.
245 *
246 * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
247 * this function to return immediately after sending. In this case,
248 * it is the responsibility of the caller to handle any error
249 * messages returned.
250 *
251 * @return 0 on success or a negative error code.
252 */
rtnl_class_delete(struct nl_sock * sk,struct rtnl_class * class)253 int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class)
254 {
255 struct nl_msg *msg;
256 int err;
257
258 if ((err = rtnl_class_build_delete_request(class, &msg)) < 0)
259 return err;
260
261 return nl_send_sync(sk, msg);
262 }
263
264 /** @} */
265
266 /**
267 * @name Leaf Qdisc
268 * @{
269 */
270
271 /**
272 * Lookup the leaf qdisc of a traffic class
273 * @arg class the parent traffic class
274 * @arg cache a qdisc cache allocated using rtnl_qdisc_alloc_cache()
275 *
276 * @return Matching Qdisc or NULL if the traffic class has no leaf qdisc
277 */
rtnl_class_leaf_qdisc(struct rtnl_class * class,struct nl_cache * cache)278 struct rtnl_qdisc *rtnl_class_leaf_qdisc(struct rtnl_class *class,
279 struct nl_cache *cache)
280 {
281 struct rtnl_qdisc *leaf;
282
283 if (!class->c_info)
284 return NULL;
285
286 leaf = rtnl_qdisc_get_by_parent(cache, class->c_ifindex,
287 class->c_handle);
288 if (!leaf || leaf->q_handle != class->c_info)
289 return NULL;
290
291 return leaf;
292 }
293
294 /** @} */
295
296 /**
297 * @name Cache Related Functions
298 * @{
299 */
300
301 /**
302 * Allocate a cache and fill it with all configured traffic classes
303 * @arg sk Netlink socket
304 * @arg ifindex Interface index of the network device
305 * @arg result Pointer to store the created cache
306 *
307 * Allocates a new traffic class cache and fills it with a list of all
308 * configured traffic classes on a specific network device. Release the
309 * cache with nl_cache_free().
310 *
311 * @return 0 on success or a negative error code.
312 */
rtnl_class_alloc_cache(struct nl_sock * sk,int ifindex,struct nl_cache ** result)313 int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex,
314 struct nl_cache **result)
315 {
316 struct nl_cache * cache;
317 int err;
318
319 if (!ifindex) {
320 APPBUG("ifindex must be specified");
321 return -NLE_INVAL;
322 }
323
324 if (!(cache = nl_cache_alloc(&rtnl_class_ops)))
325 return -NLE_NOMEM;
326
327 cache->c_iarg1 = ifindex;
328
329 if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
330 nl_cache_free(cache);
331 return err;
332 }
333
334 *result = cache;
335 return 0;
336 }
337
338 /**
339 * Search traffic class by interface index and handle
340 * @arg cache Traffic class cache
341 * @arg ifindex Interface index
342 * @arg handle ID of traffic class
343 *
344 * Searches a traffic class cache previously allocated with
345 * rtnl_class_alloc_cache() and searches for a traffi class matching
346 * the interface index and handle.
347 *
348 * The reference counter is incremented before returning the traffic
349 * class, therefore the reference must be given back with rtnl_class_put()
350 * after usage.
351 *
352 * @return Traffic class or NULL if no match was found.
353 */
rtnl_class_get(struct nl_cache * cache,int ifindex,uint32_t handle)354 struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex,
355 uint32_t handle)
356 {
357 struct rtnl_class *class;
358
359 if (cache->c_ops != &rtnl_class_ops)
360 return NULL;
361
362 nl_list_for_each_entry(class, &cache->c_items, ce_list) {
363 if (class->c_handle == handle && class->c_ifindex == ifindex) {
364 nl_object_get((struct nl_object *) class);
365 return class;
366 }
367 }
368 return NULL;
369 }
370
371 /**
372 * Search class by interface index and parent
373 * @arg cache Traffic class cache
374 * @arg ifindex Interface index
375 * @arg parent Handle of parent qdisc
376 *
377 * Searches a class cache previously allocated with rtnl_class_alloc_cache()
378 * and searches for a class matching the interface index and parent qdisc.
379 *
380 * The reference counter is incremented before returning the class, therefore
381 * the reference must be given back with rtnl_class_put() after usage.
382 *
383 * @return pointer to class inside the cache or NULL if no match was found.
384 */
rtnl_class_get_by_parent(struct nl_cache * cache,int ifindex,uint32_t parent)385 struct rtnl_class *rtnl_class_get_by_parent(struct nl_cache *cache, int ifindex,
386 uint32_t parent)
387 {
388 struct rtnl_class *class;
389
390 if (cache->c_ops != &rtnl_class_ops)
391 return NULL;
392
393 nl_list_for_each_entry(class, &cache->c_items, ce_list) {
394 if (class->c_parent == parent && class->c_ifindex == ifindex) {
395 nl_object_get((struct nl_object *) class);
396 return class;
397 }
398 }
399
400 return NULL;
401 }
402
403 /** @} */
404
405 /**
406 * @name Deprecated Functions
407 * @{
408 */
409
410 /**
411 * Call a callback for each child of a class
412 *
413 * @deprecated Use of this function is deprecated, it does not allow
414 * to handle the out of memory situation that can occur.
415 */
rtnl_class_foreach_child(struct rtnl_class * class,struct nl_cache * cache,void (* cb)(struct nl_object *,void *),void * arg)416 void rtnl_class_foreach_child(struct rtnl_class *class, struct nl_cache *cache,
417 void (*cb)(struct nl_object *, void *), void *arg)
418 {
419 struct rtnl_class *filter;
420
421 filter = rtnl_class_alloc();
422 if (!filter)
423 return;
424
425 rtnl_tc_set_parent(TC_CAST(filter), class->c_handle);
426 rtnl_tc_set_ifindex(TC_CAST(filter), class->c_ifindex);
427 rtnl_tc_set_kind(TC_CAST(filter), class->c_kind);
428
429 nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
430 rtnl_class_put(filter);
431 }
432
433 /**
434 * Call a callback for each classifier attached to the class
435 *
436 * @deprecated Use of this function is deprecated, it does not allow
437 * to handle the out of memory situation that can occur.
438 */
rtnl_class_foreach_cls(struct rtnl_class * class,struct nl_cache * cache,void (* cb)(struct nl_object *,void *),void * arg)439 void rtnl_class_foreach_cls(struct rtnl_class *class, struct nl_cache *cache,
440 void (*cb)(struct nl_object *, void *), void *arg)
441 {
442 struct rtnl_cls *filter;
443
444 filter = rtnl_cls_alloc();
445 if (!filter)
446 return;
447
448 rtnl_tc_set_ifindex((struct rtnl_tc *) filter, class->c_ifindex);
449 rtnl_tc_set_parent((struct rtnl_tc *) filter, class->c_parent);
450
451 nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
452 rtnl_cls_put(filter);
453 }
454
455 /** @} */
456
457 static struct rtnl_tc_type_ops class_ops = {
458 .tt_type = RTNL_TC_TYPE_CLASS,
459 .tt_dump_prefix = "class",
460 .tt_dump = {
461 [NL_DUMP_DETAILS] = class_dump_details,
462 },
463 };
464
465 static struct nl_object_ops class_obj_ops = {
466 .oo_name = "route/class",
467 .oo_size = sizeof(struct rtnl_class),
468 .oo_free_data = rtnl_tc_free_data,
469 .oo_clone = rtnl_tc_clone,
470 .oo_dump = {
471 [NL_DUMP_LINE] = rtnl_tc_dump_line,
472 [NL_DUMP_DETAILS] = rtnl_tc_dump_details,
473 [NL_DUMP_STATS] = rtnl_tc_dump_stats,
474 },
475 .oo_compare = rtnl_tc_compare,
476 .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
477 };
478
479 static struct nl_cache_ops rtnl_class_ops = {
480 .co_name = "route/class",
481 .co_hdrsize = sizeof(struct tcmsg),
482 .co_msgtypes = {
483 { RTM_NEWTCLASS, NL_ACT_NEW, "new" },
484 { RTM_DELTCLASS, NL_ACT_DEL, "del" },
485 { RTM_GETTCLASS, NL_ACT_GET, "get" },
486 END_OF_MSGTYPES_LIST,
487 },
488 .co_protocol = NETLINK_ROUTE,
489 .co_groups = tc_groups,
490 .co_request_update = &class_request_update,
491 .co_msg_parser = &class_msg_parser,
492 .co_obj_ops = &class_obj_ops,
493 };
494
class_init(void)495 static void __init class_init(void)
496 {
497 rtnl_tc_type_register(&class_ops);
498 nl_cache_mngt_register(&rtnl_class_ops);
499 }
500
class_exit(void)501 static void __exit class_exit(void)
502 {
503 nl_cache_mngt_unregister(&rtnl_class_ops);
504 rtnl_tc_type_unregister(&class_ops);
505 }
506
507 /** @} */
508