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
31 static struct nl_object_ops cls_obj_ops;
32 static struct nl_cache_ops rtnl_cls_ops;
33
34
cls_build(struct rtnl_cls * cls,int type,int flags,struct nl_msg ** result)35 static 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
rtnl_cls_alloc(void)64 struct 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
rtnl_cls_put(struct rtnl_cls * cls)75 void 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
rtnl_cls_set_prio(struct rtnl_cls * cls,uint16_t prio)87 void 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
rtnl_cls_get_prio(struct rtnl_cls * cls)93 uint16_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
rtnl_cls_set_protocol(struct rtnl_cls * cls,uint16_t protocol)101 void 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
rtnl_cls_get_protocol(struct rtnl_cls * cls)107 uint16_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 */
rtnl_cls_build_add_request(struct rtnl_cls * cls,int flags,struct nl_msg ** result)137 int 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 */
rtnl_cls_add(struct nl_sock * sk,struct rtnl_cls * cls,int flags)184 int 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 */
rtnl_cls_build_change_request(struct rtnl_cls * cls,int flags,struct nl_msg ** result)208 int 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 */
rtnl_cls_change(struct nl_sock * sk,struct rtnl_cls * cls,int flags)226 int 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 */
rtnl_cls_build_delete_request(struct rtnl_cls * cls,int flags,struct nl_msg ** result)251 int 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 */
rtnl_cls_delete(struct nl_sock * sk,struct rtnl_cls * cls,int flags)295 int 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 */
rtnl_cls_alloc_cache(struct nl_sock * sk,int ifindex,uint32_t parent,struct nl_cache ** result)327 int 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
cls_dump_line(struct rtnl_tc * tc,struct nl_dump_params * p)349 static 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
cls_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * pp)358 static 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);
378 errout:
379 rtnl_cls_put(cls);
380
381 return err;
382 }
383
cls_request_update(struct nl_cache * cache,struct nl_sock * sk)384 static 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
396 static 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
404 static 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
420 static 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
cls_init(void)434 static void __init cls_init(void)
435 {
436 rtnl_tc_type_register(&cls_ops);
437 nl_cache_mngt_register(&rtnl_cls_ops);
438 }
439
cls_exit(void)440 static 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