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