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