1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * lib/route/act.c Action
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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
11 */
12
13 /**
14 * @ingroup tc
15 * @defgroup act Action
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/link.h>
25 #include <netlink/route/action.h>
26
27
28 static struct nl_object_ops act_obj_ops;
29 static struct nl_cache_ops rtnl_act_ops;
30
rtnl_act_next(struct rtnl_act * act)31 struct rtnl_act * rtnl_act_next(struct rtnl_act *act)
32 {
33 if (act == NULL) {
34 return NULL;
35 }
36
37 return act->a_next;
38 }
39
rtnl_act_append(struct rtnl_act ** head,struct rtnl_act * new)40 int rtnl_act_append(struct rtnl_act **head, struct rtnl_act *new)
41 {
42 struct rtnl_act *p_act;
43 int count = 1;
44
45 if (*head == NULL) {
46 *head = new;
47 return 0;
48 }
49
50 p_act = *head;
51 while (p_act->a_next) {
52 ++count;
53 p_act = p_act->a_next;
54 }
55
56 if (count > TCA_ACT_MAX_PRIO)
57 return -NLE_RANGE;
58
59 p_act->a_next = new;
60 return 0;
61 }
62
rtnl_act_remove(struct rtnl_act ** head,struct rtnl_act * act)63 int rtnl_act_remove(struct rtnl_act **head, struct rtnl_act *act)
64 {
65 struct rtnl_act *a, **ap;
66
67 for (ap = head; (a = *ap) != NULL; ap = &a->a_next)
68 if (a == act)
69 break;
70 if (a) {
71 *ap = a->a_next;
72 a->a_next = NULL;
73 return 0;
74 }
75
76 return -NLE_OBJ_NOTFOUND;
77 }
78
rtnl_act_fill_one(struct nl_msg * msg,struct rtnl_act * act,int order)79 static int rtnl_act_fill_one(struct nl_msg *msg, struct rtnl_act *act, int order)
80 {
81 struct rtnl_tc *tc = TC_CAST(act);
82 struct rtnl_tc_ops *ops;
83 struct nlattr *nest;
84 int err = -NLE_NOMEM;
85
86 nest = nla_nest_start(msg, order);
87 if (!nest)
88 goto nla_put_failure;
89
90 if (tc->ce_mask & TCA_ATTR_KIND)
91 NLA_PUT_STRING(msg, TCA_ACT_KIND, tc->tc_kind);
92
93 ops = rtnl_tc_get_ops(tc);
94 if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) {
95 struct nlattr *opts;
96 void *data = rtnl_tc_data(tc);
97
98 if (ops->to_msg_fill) {
99 if (!(opts = nla_nest_start(msg, TCA_ACT_OPTIONS)))
100 goto nla_put_failure;
101
102 if ((err = ops->to_msg_fill(tc, data, msg)) < 0)
103 goto nla_put_failure;
104
105 nla_nest_end(msg, opts);
106 } else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0)
107 goto nla_put_failure;
108 }
109 nla_nest_end(msg, nest);
110 return 0;
111
112 nla_put_failure:
113 return err;
114 }
115
rtnl_act_fill(struct nl_msg * msg,int attrtype,struct rtnl_act * act)116 int rtnl_act_fill(struct nl_msg *msg, int attrtype, struct rtnl_act *act)
117 {
118 struct rtnl_act *p_act = act;
119 struct nlattr *nest;
120 int err, order = 0;
121
122 nest = nla_nest_start(msg, attrtype);
123 if (!nest)
124 return -NLE_MSGSIZE;
125
126 while (p_act) {
127 err = rtnl_act_fill_one(msg, p_act, ++order);
128 if (err)
129 return err;
130 p_act = p_act->a_next;
131 }
132
133 nla_nest_end(msg, nest);
134 return 0;
135 }
136
rtnl_act_msg_build(struct rtnl_act * act,int type,int flags,struct nl_msg ** result)137 static int rtnl_act_msg_build(struct rtnl_act *act, int type, int flags,
138 struct nl_msg **result)
139 {
140 struct nl_msg *msg;
141 struct tcamsg tcahdr = {
142 .tca_family = AF_UNSPEC,
143 };
144 int err = -NLE_MSGSIZE;
145
146 msg = nlmsg_alloc_simple(type, flags);
147 if (!msg)
148 return -NLE_NOMEM;
149
150 if (nlmsg_append(msg, &tcahdr, sizeof(tcahdr), NLMSG_ALIGNTO) < 0)
151 goto nla_put_failure;
152
153 err = rtnl_act_fill(msg, TCA_ACT_TAB, act);
154 if (err < 0)
155 goto nla_put_failure;
156
157 *result = msg;
158 return 0;
159
160 nla_put_failure:
161 nlmsg_free(msg);
162 return err;
163 }
164
act_build(struct rtnl_act * act,int type,int flags,struct nl_msg ** result)165 static int act_build(struct rtnl_act *act, int type, int flags,
166 struct nl_msg **result)
167 {
168 int err;
169
170 err = rtnl_act_msg_build(act, type, flags, result);
171 if (err < 0)
172 return err;
173 return 0;
174 }
175
176 /**
177 * @name Allocation/Freeing
178 * @{
179 */
180
rtnl_act_alloc(void)181 struct rtnl_act *rtnl_act_alloc(void)
182 {
183 struct rtnl_tc *tc;
184
185 tc = TC_CAST(nl_object_alloc(&act_obj_ops));
186 if (tc)
187 tc->tc_type = RTNL_TC_TYPE_ACT;
188
189 return (struct rtnl_act *) tc;
190 }
191
rtnl_act_get(struct rtnl_act * act)192 void rtnl_act_get(struct rtnl_act *act)
193 {
194 nl_object_get(OBJ_CAST(act));
195 }
196
rtnl_act_put(struct rtnl_act * act)197 void rtnl_act_put(struct rtnl_act *act)
198 {
199 nl_object_put((struct nl_object *) act);
200 }
201
202 /** @} */
203
204 /**
205 * @name Addition/Modification/Deletion
206 * @{
207 */
208
209 /**
210 * Build a netlink message requesting the addition of an action
211 * @arg act Action to add
212 * @arg flags Additional netlink message flags
213 * @arg result Pointer to store resulting netlink message
214 *
215 * The behaviour of this function is identical to rtnl_act_add() with
216 * the exception that it will not send the message but return it int the
217 * provided return pointer instead.
218 *
219 * @see rtnl_act_add()
220 *
221 * @return 0 on success or a negative error code.
222 */
rtnl_act_build_add_request(struct rtnl_act * act,int flags,struct nl_msg ** result)223 int rtnl_act_build_add_request(struct rtnl_act *act, int flags,
224 struct nl_msg **result)
225 {
226 return act_build(act, RTM_NEWACTION, flags, result);
227 }
228
229 /**
230 * Add/Update action
231 * @arg sk Netlink socket
232 * @arg act Action to add/update
233 * @arg flags Additional netlink message flags
234 *
235 * Builds a \c RTM_NEWACTION netlink message requesting the addition
236 * of a new action and sends the message to the kernel. The
237 * configuration of the action is derived from the attributes of
238 * the specified traffic class.
239 *
240 * The following flags may be specified:
241 * - \c NLM_F_CREATE: Create action if it does not exist,
242 * otherwise -NLE_OBJ_NOTFOUND is returned.
243 * - \c NLM_F_EXCL: Return -NLE_EXISTS if an action with
244 * matching handle exists already.
245 *
246 * Existing actions with matching handles will be updated, unless
247 * the flag \c NLM_F_EXCL is specified. If no matching action
248 * exists, it will be created if the flag \c NLM_F_CREATE is set,
249 * otherwise the error -NLE_OBJ_NOTFOUND is returned.
250 *
251 * After sending, the function will wait for the ACK or an eventual
252 * error message to be received and will therefore block until the
253 * operation has been completed.
254 *
255 * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
256 * this function to return immediately after sending. In this case,
257 * it is the responsibility of the caller to handle any error
258 * messages returned.
259 *
260 * @return 0 on success or a negative error code.
261 */
rtnl_act_add(struct nl_sock * sk,struct rtnl_act * act,int flags)262 int rtnl_act_add(struct nl_sock *sk, struct rtnl_act *act, int flags)
263 {
264 struct nl_msg *msg;
265 int err;
266
267 if ((err = rtnl_act_build_add_request(act, flags, &msg)) < 0)
268 return err;
269
270 return nl_send_sync(sk, msg);
271 }
272
273 /**
274 * Build a netlink message to change action attributes
275 * @arg act Action to change
276 * @arg flags additional netlink message flags
277 * @arg result Pointer to store resulting message.
278 *
279 * Builds a new netlink message requesting a change of a neigh
280 * attributes. The netlink message header isn't fully equipped with
281 * all relevant fields and must thus be sent out via nl_send_auto_complete()
282 * or supplemented as needed.
283 *
284 * @return 0 on success or a negative error code.
285 */
rtnl_act_build_change_request(struct rtnl_act * act,int flags,struct nl_msg ** result)286 int rtnl_act_build_change_request(struct rtnl_act *act, int flags,
287 struct nl_msg **result)
288 {
289 return act_build(act, RTM_NEWACTION, NLM_F_REPLACE | flags, result);
290 }
291
292 /**
293 * Change an action
294 * @arg sk Netlink socket.
295 * @arg act action to change
296 * @arg flags additional netlink message flags
297 *
298 * Builds a netlink message by calling rtnl_act_build_change_request(),
299 * sends the request to the kernel and waits for the next ACK to be
300 * received and thus blocks until the request has been processed.
301 *
302 * @return 0 on sucess or a negative error if an error occured.
303 */
rtnl_act_change(struct nl_sock * sk,struct rtnl_act * act,int flags)304 int rtnl_act_change(struct nl_sock *sk, struct rtnl_act *act, int flags)
305 {
306 struct nl_msg *msg;
307 int err;
308
309 if ((err = rtnl_act_build_change_request(act, flags, &msg)) < 0)
310 return err;
311
312 return nl_send_sync(sk, msg);
313 }
314
315 /**
316 * Build netlink message requesting the deletion of an action
317 * @arg act Action to delete
318 * @arg flags Additional netlink message flags
319 * @arg result Pointer to store resulting netlink message
320 *
321 * The behaviour of this function is identical to rtnl_act_delete() with
322 * the exception that it will not send the message but return it in the
323 * provided return pointer instead.
324 *
325 * @see rtnl_act_delete()
326 *
327 * @return 0 on success or a negative error code.
328 */
rtnl_act_build_delete_request(struct rtnl_act * act,int flags,struct nl_msg ** result)329 int rtnl_act_build_delete_request(struct rtnl_act *act, int flags,
330 struct nl_msg **result)
331 {
332 return act_build(act, RTM_DELACTION, flags, result);
333 }
334
335 /**
336 * Delete action
337 * @arg sk Netlink socket
338 * @arg act Action to delete
339 * @arg flags Additional netlink message flags
340 *
341 * Builds a \c RTM_DELACTION netlink message requesting the deletion
342 * of an action and sends the message to the kernel.
343 *
344 * The message is constructed out of the following attributes:
345 * - \c ifindex (required)
346 * - \c prio (required)
347 * - \c protocol (required)
348 * - \c handle (required)
349 * - \c parent (optional, if not specified parent equals root-qdisc)
350 * - \c kind (optional, must match if provided)
351 *
352 * All other action attributes including all class type specific
353 * attributes are ignored.
354 *
355 * After sending, the function will wait for the ACK or an eventual
356 * error message to be received and will therefore block until the
357 * operation has been completed.
358 *
359 * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
360 * this function to return immediately after sending. In this case,
361 * it is the responsibility of the caller to handle any error
362 * messages returned.
363 *
364 * @return 0 on success or a negative error code.
365 */
rtnl_act_delete(struct nl_sock * sk,struct rtnl_act * act,int flags)366 int rtnl_act_delete(struct nl_sock *sk, struct rtnl_act *act, int flags)
367 {
368 struct nl_msg *msg;
369 int err;
370
371 if ((err = rtnl_act_build_delete_request(act, flags, &msg)) < 0)
372 return err;
373
374 return nl_send_sync(sk, msg);
375 }
376
377 /** @} */
378
act_dump_line(struct rtnl_tc * tc,struct nl_dump_params * p)379 static void act_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p)
380 {
381 }
382
rtnl_act_put_all(struct rtnl_act ** head)383 void rtnl_act_put_all(struct rtnl_act **head)
384 {
385 struct rtnl_act *curr, *next;
386
387 curr = *head;
388 while (curr) {
389 next = curr->a_next;
390 rtnl_act_put(curr);
391 curr = next;
392 }
393 *head = NULL;
394 }
395
rtnl_act_parse(struct rtnl_act ** head,struct nlattr * tb)396 int rtnl_act_parse(struct rtnl_act **head, struct nlattr *tb)
397 {
398 struct rtnl_act *act;
399 struct rtnl_tc_ops *ops;
400 struct nlattr *tb2[TCA_ACT_MAX + 1];
401 struct nlattr *nla[TCA_ACT_MAX_PRIO + 1];
402 char kind[TCKINDSIZ];
403 int err, i;
404
405 err = nla_parse(nla, TCA_ACT_MAX_PRIO, nla_data(tb),
406 NLMSG_ALIGN(nla_len(tb)), NULL);
407 if (err < 0)
408 return err;
409
410 for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
411 struct rtnl_tc *tc;
412
413 if (nla[i] == NULL)
414 continue;
415
416 act = rtnl_act_alloc();
417 if (!act) {
418 err = -NLE_NOMEM;
419 goto err_free;
420 }
421 tc = TC_CAST(act);
422 err = nla_parse(tb2, TCA_ACT_MAX, nla_data(nla[i]),
423 nla_len(nla[i]), NULL);
424 if (err < 0)
425 goto err_free;
426
427 if (tb2[TCA_ACT_KIND] == NULL) {
428 err = -NLE_MISSING_ATTR;
429 goto err_free;
430 }
431
432 nla_strlcpy(kind, tb2[TCA_ACT_KIND], sizeof(kind));
433 rtnl_tc_set_kind(tc, kind);
434
435 if (tb2[TCA_ACT_OPTIONS]) {
436 tc->tc_opts = nl_data_alloc_attr(tb2[TCA_ACT_OPTIONS]);
437 if (!tc->tc_opts) {
438 err = -NLE_NOMEM;
439 goto err_free;
440 }
441 tc->ce_mask |= TCA_ATTR_OPTS;
442 }
443
444 ops = rtnl_tc_get_ops(tc);
445 if (ops && ops->to_msg_parser) {
446 void *data = rtnl_tc_data(tc);
447
448 if (!data) {
449 err = -NLE_NOMEM;
450 goto err_free;
451 }
452
453 err = ops->to_msg_parser(tc, data);
454 if (err < 0)
455 goto err_free;
456 }
457 err = rtnl_act_append(head, act);
458 if (err < 0)
459 goto err_free;
460 }
461 return 0;
462
463 err_free:
464 rtnl_act_put (act);
465 rtnl_act_put_all(head);
466
467 return err;
468 }
469
rtnl_act_msg_parse(struct nlmsghdr * n,struct rtnl_act ** act)470 static int rtnl_act_msg_parse(struct nlmsghdr *n, struct rtnl_act **act)
471 {
472 struct rtnl_tc *tc = TC_CAST(*act);
473 struct nl_cache *link_cache;
474 struct nlattr *tb[TCAA_MAX + 1];
475 struct tcamsg *tm;
476 int err;
477
478 tc->ce_msgtype = n->nlmsg_type;
479
480 err = nlmsg_parse(n, sizeof(*tm), tb, TCAA_MAX, NULL);
481 if (err < 0)
482 return err;
483
484 tm = nlmsg_data(n);
485 tc->tc_family = tm->tca_family;
486
487 if (tb[TCA_ACT_TAB] == NULL)
488 return -NLE_MISSING_ATTR;
489
490 err = rtnl_act_parse(act, tb[TCA_ACT_TAB]);
491 if (err < 0)
492 return err;
493
494 if ((link_cache = __nl_cache_mngt_require("route/link"))) {
495 struct rtnl_link *link;
496
497 if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) {
498 rtnl_tc_set_link(tc, link);
499
500 /* rtnl_tc_set_link incs refcnt */
501 rtnl_link_put(link);
502 }
503 }
504
505 return 0;
506 }
act_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * pp)507 static int act_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
508 struct nlmsghdr *nlh, struct nl_parser_param *pp)
509 {
510 struct rtnl_act *act, *p_act;
511 int err;
512
513 if (!(act = rtnl_act_alloc()))
514 return -NLE_NOMEM;
515
516 if ((err = rtnl_act_msg_parse(nlh, &act)) < 0)
517 goto errout;
518
519 p_act = act;
520 while(p_act) {
521 err = pp->pp_cb(OBJ_CAST(act), pp);
522 if (err)
523 break;
524 p_act = p_act->a_next;
525 }
526 errout:
527 rtnl_act_put(act);
528
529 return err;
530 }
531
act_request_update(struct nl_cache * cache,struct nl_sock * sk)532 static int act_request_update(struct nl_cache *cache, struct nl_sock *sk)
533 {
534 struct tcamsg tcahdr = {
535 .tca_family = AF_UNSPEC,
536 };
537
538 return nl_send_simple(sk, RTM_GETACTION, NLM_F_DUMP, &tcahdr,
539 sizeof(tcahdr));
540 }
541
542 static struct rtnl_tc_type_ops act_ops = {
543 .tt_type = RTNL_TC_TYPE_ACT,
544 .tt_dump_prefix = "act",
545 .tt_dump = {
546 [NL_DUMP_LINE] = act_dump_line,
547 },
548 };
549
550 static struct nl_cache_ops rtnl_act_ops = {
551 .co_name = "route/act",
552 .co_hdrsize = sizeof(struct tcmsg),
553 .co_msgtypes = {
554 { RTM_NEWACTION, NL_ACT_NEW, "new" },
555 { RTM_DELACTION, NL_ACT_DEL, "del" },
556 { RTM_GETACTION, NL_ACT_GET, "get" },
557 END_OF_MSGTYPES_LIST,
558 },
559 .co_protocol = NETLINK_ROUTE,
560 .co_request_update = act_request_update,
561 .co_msg_parser = act_msg_parser,
562 .co_obj_ops = &act_obj_ops,
563 };
564
565 static struct nl_object_ops act_obj_ops = {
566 .oo_name = "route/act",
567 .oo_size = sizeof(struct rtnl_act),
568 .oo_free_data = rtnl_tc_free_data,
569 .oo_clone = rtnl_tc_clone,
570 .oo_dump = {
571 [NL_DUMP_LINE] = rtnl_tc_dump_line,
572 [NL_DUMP_DETAILS] = rtnl_tc_dump_details,
573 [NL_DUMP_STATS] = rtnl_tc_dump_stats,
574 },
575 .oo_compare = rtnl_tc_compare,
576 .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
577 };
578
act_init(void)579 static void __init act_init(void)
580 {
581 rtnl_tc_type_register(&act_ops);
582 nl_cache_mngt_register(&rtnl_act_ops);
583 }
584
act_exit(void)585 static void __exit act_exit(void)
586 {
587 nl_cache_mngt_unregister(&rtnl_act_ops);
588 rtnl_tc_type_unregister(&act_ops);
589 }
590
591 /** @} */
592