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