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