xref: /aosp_15_r20/external/libnl/lib/route/cls.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2003-2013 Thomas Graf <[email protected]>
4  */
5 
6 /**
7  * @ingroup tc
8  * @defgroup cls Classifiers
9  * @{
10  */
11 
12 #include "nl-default.h"
13 
14 #include <linux/ethtool.h>
15 
16 #include <netlink/netlink.h>
17 #include <netlink/utils.h>
18 #include <netlink/route/classifier.h>
19 #include <netlink/route/link.h>
20 
21 #include "nl-route.h"
22 #include "tc-api.h"
23 
24 /** @cond SKIP */
25 struct rtnl_cls {
26 	NL_TC_GENERIC(c);
27 	uint16_t c_prio;
28 	uint16_t c_protocol;
29 };
30 
31 #define CLS_ATTR_PRIO		(TCA_ATTR_MAX << 1)
32 #define CLS_ATTR_PROTOCOL	(TCA_ATTR_MAX << 2)
33 /** @endcond */
34 
35 static struct nl_object_ops cls_obj_ops;
36 static struct nl_cache_ops rtnl_cls_ops;
37 
38 
cls_build(struct rtnl_cls * cls,int type,int flags,struct nl_msg ** result)39 static int cls_build(struct rtnl_cls *cls, int type, int flags,
40 		     struct nl_msg **result)
41 {
42 	int err, prio, proto;
43 	struct tcmsg *tchdr;
44 	uint32_t required = TCA_ATTR_IFINDEX;
45 
46 	if ((cls->ce_mask & required) != required) {
47 		APPBUG("ifindex must be specified");
48 		return -NLE_MISSING_ATTR;
49 	}
50 
51 	err = rtnl_tc_msg_build(TC_CAST(cls), type, flags, result);
52 	if (err < 0)
53 		return err;
54 
55 	tchdr = nlmsg_data(nlmsg_hdr(*result));
56 	prio = rtnl_cls_get_prio(cls);
57 	proto = rtnl_cls_get_protocol(cls);
58 	tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto));
59 
60 	return 0;
61 }
62 
63 /**
64  * @name Allocation/Freeing
65  * @{
66  */
67 
rtnl_cls_alloc(void)68 struct rtnl_cls *rtnl_cls_alloc(void)
69 {
70 	struct rtnl_tc *tc;
71 
72 	tc = TC_CAST(nl_object_alloc(&cls_obj_ops));
73 	if (tc)
74 		tc->tc_type = RTNL_TC_TYPE_CLS;
75 
76 	return (struct rtnl_cls *) tc;
77 }
78 
rtnl_cls_put(struct rtnl_cls * cls)79 void rtnl_cls_put(struct rtnl_cls *cls)
80 {
81 	nl_object_put((struct nl_object *) cls);
82 }
83 
84 /** @} */
85 
86 /**
87  * @name Attributes
88  * @{
89  */
90 
rtnl_cls_set_prio(struct rtnl_cls * cls,uint16_t prio)91 void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio)
92 {
93 	cls->c_prio = prio;
94 	cls->ce_mask |= CLS_ATTR_PRIO;
95 }
96 
rtnl_cls_get_prio(struct rtnl_cls * cls)97 uint16_t rtnl_cls_get_prio(struct rtnl_cls *cls)
98 {
99 	if (cls->ce_mask & CLS_ATTR_PRIO)
100 		return cls->c_prio;
101 	else
102 		return 0;
103 }
104 
rtnl_cls_set_protocol(struct rtnl_cls * cls,uint16_t protocol)105 void rtnl_cls_set_protocol(struct rtnl_cls *cls, uint16_t protocol)
106 {
107 	cls->c_protocol = protocol;
108 	cls->ce_mask |= CLS_ATTR_PROTOCOL;
109 }
110 
rtnl_cls_get_protocol(struct rtnl_cls * cls)111 uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls)
112 {
113 	if (cls->ce_mask & CLS_ATTR_PROTOCOL)
114 		return cls->c_protocol;
115 	else
116 		return ETH_P_ALL;
117 }
118 
119 /** @} */
120 
121 
122 /**
123  * @name Addition/Modification/Deletion
124  * @{
125  */
126 
127 /**
128  * Build a netlink message requesting the addition of a classifier
129  * @arg cls		Classifier to add
130  * @arg flags		Additional netlink message flags
131  * @arg result		Pointer to store resulting netlink message
132  *
133  * The behaviour of this function is identical to rtnl_cls_add() with
134  * the exception that it will not send the message but return it int the
135  * provided return pointer instead.
136  *
137  * @see rtnl_cls_add()
138  *
139  * @return 0 on success or a negative error code.
140  */
rtnl_cls_build_add_request(struct rtnl_cls * cls,int flags,struct nl_msg ** result)141 int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags,
142 			       struct nl_msg **result)
143 {
144 	if (!(flags & NLM_F_CREATE) && !(cls->ce_mask & CLS_ATTR_PRIO)) {
145 		APPBUG("prio must be specified if not a new classifier");
146 		return -NLE_MISSING_ATTR;
147 	}
148 
149 	return cls_build(cls, RTM_NEWTFILTER, flags, result);
150 }
151 
152 /**
153  * Add/Update classifier
154  * @arg sk		Netlink socket
155  * @arg cls		Classifier to add/update
156  * @arg flags		Additional netlink message flags
157  *
158  * Builds a \c RTM_NEWTFILTER netlink message requesting the addition
159  * of a new classifier and sends the message to the kernel. The
160  * configuration of the classifier is derived from the attributes of
161  * the specified traffic class.
162  *
163  * The following flags may be specified:
164  *  - \c NLM_F_CREATE:  Create classifier if it does not exist,
165  *                      otherwise -NLE_OBJ_NOTFOUND is returned.
166  *  - \c NLM_F_EXCL:    Return -NLE_EXISTS if a classifier with
167  *                      matching handle exists already.
168  *
169  * Existing classifiers with matching handles will be updated, unless
170  * the flag \c NLM_F_EXCL is specified. If no matching classifier
171  * exists, it will be created if the flag \c NLM_F_CREATE is set,
172  * otherwise the error -NLE_OBJ_NOTFOUND is returned.
173  *
174  * If the parent qdisc does not support classes, the error
175  * \c NLE_OPNOTSUPP is returned.
176  *
177  * After sending, the function will wait for the ACK or an eventual
178  * error message to be received and will therefore block until the
179  * operation has been completed.
180  *
181  * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
182  *       this function to return immediately after sending. In this case,
183  *       it is the responsibility of the caller to handle any error
184  *       messages returned.
185  *
186  * @return 0 on success or a negative error code.
187  */
rtnl_cls_add(struct nl_sock * sk,struct rtnl_cls * cls,int flags)188 int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
189 {
190 	struct nl_msg *msg;
191 	int err;
192 
193 	if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0)
194 		return err;
195 
196 	return nl_send_sync(sk, msg);
197 }
198 
199 /**
200  * Build a netlink message to change classifier attributes
201  * @arg cls		classifier to change
202  * @arg flags		additional netlink message flags
203  * @arg result		Pointer to store resulting message.
204  *
205  * Builds a new netlink message requesting a change of a neigh
206  * attributes. The netlink message header isn't fully equipped with
207  * all relevant fields and must thus be sent out via nl_send_auto_complete()
208  * or supplemented as needed.
209  *
210  * @return 0 on success or a negative error code.
211  */
rtnl_cls_build_change_request(struct rtnl_cls * cls,int flags,struct nl_msg ** result)212 int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags,
213 				  struct nl_msg **result)
214 {
215 	return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result);
216 }
217 
218 /**
219  * Change a classifier
220  * @arg sk		Netlink socket.
221  * @arg cls		classifier to change
222  * @arg flags		additional netlink message flags
223  *
224  * Builds a netlink message by calling rtnl_cls_build_change_request(),
225  * sends the request to the kernel and waits for the next ACK to be
226  * received and thus blocks until the request has been processed.
227  *
228  * @return 0 on success or a negative error if an error occured.
229  */
rtnl_cls_change(struct nl_sock * sk,struct rtnl_cls * cls,int flags)230 int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
231 {
232 	struct nl_msg *msg;
233 	int err;
234 
235 	if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0)
236 		return err;
237 
238 	return nl_send_sync(sk, msg);
239 }
240 
241 /**
242  * Build netlink message requesting the deletion of a classifier
243  * @arg cls		Classifier to delete
244  * @arg flags		Additional netlink message flags
245  * @arg result		Pointer to store resulting netlink message
246  *
247  * The behaviour of this function is identical to rtnl_cls_delete() with
248  * the exception that it will not send the message but return it in the
249  * provided return pointer instead.
250  *
251  * @see rtnl_cls_delete()
252  *
253  * @return 0 on success or a negative error code.
254  */
rtnl_cls_build_delete_request(struct rtnl_cls * cls,int flags,struct nl_msg ** result)255 int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags,
256 				  struct nl_msg **result)
257 {
258 	uint32_t required = CLS_ATTR_PRIO;
259 
260 	if ((cls->ce_mask & required) != required) {
261 		APPBUG("prio must be specified");
262 		return -NLE_MISSING_ATTR;
263 	}
264 
265 	return cls_build(cls, RTM_DELTFILTER, flags, result);
266 }
267 
268 /**
269  * Delete classifier
270  * @arg sk		Netlink socket
271  * @arg cls		Classifier to delete
272  * @arg flags		Additional netlink message flags
273  *
274  * Builds a \c RTM_DELTFILTER netlink message requesting the deletion
275  * of a classifier and sends the message to the kernel.
276  *
277  * The message is constructed out of the following attributes:
278  * - \c ifindex (required)
279  * - \c prio (required)
280  * - \c protocol (required)
281  * - \c handle (required)
282  * - \c parent (optional, if not specified parent equals root-qdisc)
283  * - \c kind (optional, must match if provided)
284  *
285  * All other classifier attributes including all class type specific
286  * attributes are ignored.
287  *
288  * After sending, the function will wait for the ACK or an eventual
289  * error message to be received and will therefore block until the
290  * operation has been completed.
291  *
292  * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
293  *       this function to return immediately after sending. In this case,
294  *       it is the responsibility of the caller to handle any error
295  *       messages returned.
296  *
297  * @return 0 on success or a negative error code.
298  */
rtnl_cls_delete(struct nl_sock * sk,struct rtnl_cls * cls,int flags)299 int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
300 {
301 	struct nl_msg *msg;
302 	int err;
303 
304 	if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0)
305 		return err;
306 
307 	return nl_send_sync(sk, msg);
308 }
309 
310 /** @} */
311 
312 /**
313  * @name Cache Related Functions
314  * @{
315  */
316 
317 /**
318  * Allocate a cache and fill it with all configured classifiers
319  * @arg sk		Netlink socket
320  * @arg ifindex		Interface index of the network device
321  * @arg parent		Parent qdisc/traffic class class
322  * @arg result		Pointer to store the created cache
323  *
324  * Allocates a new classifier cache and fills it with a list of all
325  * configured classifier attached to the specified parent qdisc/traffic
326  * class on the specified network device. Release the cache with
327  * nl_cache_free().
328  *
329  * @return 0 on success or a negative error code.
330  */
rtnl_cls_alloc_cache(struct nl_sock * sk,int ifindex,uint32_t parent,struct nl_cache ** result)331 int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent,
332 			 struct nl_cache **result)
333 {
334 	struct nl_cache * cache;
335 	int err;
336 
337 	if (!(cache = nl_cache_alloc(&rtnl_cls_ops)))
338 		return -NLE_NOMEM;
339 
340 	cache->c_iarg1 = ifindex;
341 	cache->c_iarg2 = parent;
342 
343 	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
344 		nl_cache_free(cache);
345 		return err;
346 	}
347 
348 	*result = cache;
349 	return 0;
350 }
351 
352 /**
353  * Set interface index and parent handle for classifier cache.
354  * @arg cache 		Pointer to cache
355  * @arg parent 		Parent qdisc/traffic class class
356  *
357  * Set the interface index and parent handle of a classifier cache.
358  * This is useful for reusing some existed classifier cache to reduce
359  * the overhead introduced by memory allocation.
360  *
361  * @return void.
362  */
rtnl_cls_cache_set_tc_params(struct nl_cache * cache,int ifindex,uint32_t parent)363 void rtnl_cls_cache_set_tc_params(struct nl_cache *cache,
364 				  int ifindex, uint32_t parent)
365 {
366 	cache->c_iarg1 = ifindex;
367 	cache->c_iarg2 = parent;
368 }
369 
370 /**
371  * Search classifier by interface index, parent and handle
372  * @arg cache           Classifier cache
373  * @arg ifindex         Interface index
374  * @arg parent          Parent
375  * @arg handle          Handle
376  *
377  * Searches a classifier cache previously allocated with rtnl_cls_alloc_cache()
378  * and searches for a classifier matching the interface index, parent
379  * and handle.
380  *
381  * The reference counter is incremented before returning the classifier,
382  * therefore the reference must be given back with rtnl_cls_put() after usage.
383  *
384  * @return Classifier or NULL if no match was found.
385  */
rtnl_cls_find_by_handle(struct nl_cache * cache,int ifindex,uint32_t parent,uint32_t handle)386 struct rtnl_cls *rtnl_cls_find_by_handle(struct nl_cache *cache, int ifindex, uint32_t parent,
387                                          uint32_t handle)
388 {
389 	struct rtnl_cls *cls;
390 
391 	if (cache->c_ops != &rtnl_cls_ops)
392 		return NULL;
393 
394 	nl_list_for_each_entry(cls, &cache->c_items, ce_list) {
395 		if ((cls->c_parent == parent) &&
396 		    cls->c_ifindex == ((unsigned)ifindex) &&
397 		    (cls->c_handle == handle)) {
398 			nl_object_get((struct nl_object *) cls);
399 			return cls;
400 		}
401 	}
402 
403 	return NULL;
404 }
405 
406 /**
407  * Search classifier by interface index, parent and priority
408  * @arg cache           Classifier cache
409  * @arg ifindex         Interface index
410  * @arg parent          Parent
411  * @arg prio            Priority
412  *
413  * Searches a classifier cache previously allocated with rtnl_cls_alloc_cache()
414  * and searches for a classifier matching the interface index, parent
415  * and prio.
416  *
417  * The reference counter is incremented before returning the classifier,
418  * therefore the reference must be given back with rtnl_cls_put() after usage.
419  *
420  * @return Classifier or NULL if no match was found.
421  */
rtnl_cls_find_by_prio(struct nl_cache * cache,int ifindex,uint32_t parent,uint16_t prio)422 struct rtnl_cls *rtnl_cls_find_by_prio(struct nl_cache *cache, int ifindex,
423                                        uint32_t parent, uint16_t prio)
424 {
425 	struct rtnl_cls *cls;
426 
427 	if (cache->c_ops != &rtnl_cls_ops)
428 		return NULL;
429 
430 	nl_list_for_each_entry(cls, &cache->c_items, ce_list) {
431 		if ((cls->c_parent == parent) &&
432 		    cls->c_ifindex == ((unsigned)ifindex) &&
433 		    (cls->c_prio == prio)) {
434 			nl_object_get((struct nl_object *)cls);
435 			return cls;
436 		}
437 	}
438 
439 	return NULL;
440 }
441 
442 /** @} */
443 
cls_dump_line(struct rtnl_tc * tc,struct nl_dump_params * p)444 static void cls_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p)
445 {
446 	struct rtnl_cls *cls = (struct rtnl_cls *) tc;
447 	char buf[32];
448 
449 	nl_dump(p, " prio %u protocol %s", cls->c_prio,
450 		nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf)));
451 }
452 
cls_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * pp)453 static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
454 			  struct nlmsghdr *nlh, struct nl_parser_param *pp)
455 {
456 	struct rtnl_cls *cls;
457 	int err;
458 
459 	if (!(cls = rtnl_cls_alloc()))
460 		return -NLE_NOMEM;
461 
462 	if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(cls))) < 0)
463 		goto errout;
464 
465 	cls->c_prio = TC_H_MAJ(cls->c_info) >> 16;
466 	if (cls->c_prio)
467 		cls->ce_mask |= CLS_ATTR_PRIO;
468 	cls->c_protocol = ntohs(TC_H_MIN(cls->c_info));
469 	if (cls->c_protocol)
470 		cls->ce_mask |= CLS_ATTR_PROTOCOL;
471 
472 	err = pp->pp_cb(OBJ_CAST(cls), pp);
473 errout:
474 	rtnl_cls_put(cls);
475 
476 	return err;
477 }
478 
cls_request_update(struct nl_cache * cache,struct nl_sock * sk)479 static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk)
480 {
481 	struct tcmsg tchdr = {
482 		.tcm_family = AF_UNSPEC,
483 		.tcm_ifindex = cache->c_iarg1,
484 		.tcm_parent = cache->c_iarg2,
485 	};
486 
487 	return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr,
488 			      sizeof(tchdr));
489 }
490 
491 static struct rtnl_tc_type_ops cls_ops = {
492 	.tt_type		= RTNL_TC_TYPE_CLS,
493 	.tt_dump_prefix		= "cls",
494 	.tt_dump = {
495 		[NL_DUMP_LINE]	= cls_dump_line,
496 	},
497 };
498 
499 static struct nl_cache_ops rtnl_cls_ops = {
500 	.co_name		= "route/cls",
501 	.co_hdrsize		= sizeof(struct tcmsg),
502 	.co_msgtypes		= {
503 					{ RTM_NEWTFILTER, NL_ACT_NEW, "new" },
504 					{ RTM_DELTFILTER, NL_ACT_DEL, "del" },
505 					{ RTM_GETTFILTER, NL_ACT_GET, "get" },
506 					END_OF_MSGTYPES_LIST,
507 				  },
508 	.co_protocol		= NETLINK_ROUTE,
509 	.co_groups		= tc_groups,
510 	.co_request_update	= cls_request_update,
511 	.co_msg_parser		= cls_msg_parser,
512 	.co_obj_ops		= &cls_obj_ops,
513 };
514 
515 static struct nl_object_ops cls_obj_ops = {
516 	.oo_name		= "route/cls",
517 	.oo_size		= sizeof(struct rtnl_cls),
518 	.oo_free_data		= rtnl_tc_free_data,
519 	.oo_clone		= rtnl_tc_clone,
520 	.oo_dump = {
521 	    [NL_DUMP_LINE]	= rtnl_tc_dump_line,
522 	    [NL_DUMP_DETAILS]	= rtnl_tc_dump_details,
523 	    [NL_DUMP_STATS]	= rtnl_tc_dump_stats,
524 	},
525 	.oo_compare		= rtnl_tc_compare,
526 	.oo_id_attrs		= (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
527 };
528 
cls_init(void)529 static void _nl_init cls_init(void)
530 {
531 	rtnl_tc_type_register(&cls_ops);
532 	nl_cache_mngt_register(&rtnl_cls_ops);
533 }
534 
cls_exit(void)535 static void _nl_exit cls_exit(void)
536 {
537 	nl_cache_mngt_unregister(&rtnl_cls_ops);
538 	rtnl_tc_type_unregister(&cls_ops);
539 }
540 
541 /** @} */
542