libnl: /data/src/libnl/lib/route/nh.c Source File

libnl 3.12.0

nh.c
1/* SPDX-License-Identifier: LGPL-2.1-only */
2/*
3 * Copyright (c) 2022 Stanislav Zaikin <zstaseg@gmail.com>
4 */
5
6#include "nl-default.h"
7
8#include <linux/nexthop.h>
9#include <linux/lwtunnel.h>
10#include <linux/mpls_iptunnel.h>
11
12#include <netlink/route/nh.h>
13#include <netlink/hashtable.h>
14#include <netlink/route/nexthop.h>
15
16#include "nexthop-encap.h"
17#include "nl-aux-route/nl-route.h"
18#include "nl-route.h"
19#include "nl-priv-dynamic-core/nl-core.h"
20#include "nl-priv-dynamic-core/cache-api.h"
21
22/** @cond SKIP */
23struct rtnl_nh {
24 NLHDR_COMMON
25
26 uint8_t nh_family;
27 uint32_t nh_flags;
28
29 uint32_t nh_id;
30 uint16_t nh_group_type;
31 nl_nh_group_t *nh_group;
32 uint32_t nh_oif;
33 struct nl_addr *nh_gateway;
34 struct rtnl_nh_encap *nh_encap;
35
36 /* Resilient nexthop group parameters */
37 uint16_t res_grp_buckets;
38 uint32_t res_grp_idle_timer;
39 uint32_t res_grp_unbalanced_timer;
40};
41
42#define NH_ATTR_FLAGS (1 << 0)
43#define NH_ATTR_ID (1 << 1)
44#define NH_ATTR_GROUP (1 << 2)
45#define NH_ATTR_FLAG_BLACKHOLE (1 << 3)
46#define NH_ATTR_OIF (1 << 4)
47#define NH_ATTR_GATEWAY (1 << 5)
48#define NH_ATTR_FLAG_GROUPS (1 << 6)
49#define NH_ATTR_GROUP_TYPE (1 << 7)
50#define NH_ATTR_FLAG_FDB (1 << 8)
51/* Resilient nexthop group attributes */
52#define NH_ATTR_RES_GROUP (1 << 9)
53#define NH_ATTR_RES_BUCKETS (1 << 10)
54#define NH_ATTR_RES_IDLE_TIMER (1 << 11)
55#define NH_ATTR_RES_UNBALANCED_TIMER (1 << 12)
56#define NH_ATTR_ENCAP (1 << 13)
57/** @endcond */
58
59struct nla_policy rtnl_nh_policy[NHA_MAX + 1] = {
60 [NHA_UNSPEC] = { .type = NLA_UNSPEC },
61 [NHA_ID] = { .type = NLA_U32 },
62 [NHA_GROUP] = { .type = NLA_NESTED },
63 [NHA_GROUP_TYPE] = { .type = NLA_U16 },
64 [NHA_BLACKHOLE] = { .type = NLA_UNSPEC },
65 [NHA_OIF] = { .type = NLA_U32 },
66 [NHA_RES_GROUP] = { .type = NLA_NESTED },
67 [NHA_ENCAP] = { .type = NLA_NESTED },
68 [NHA_ENCAP_TYPE] = { .type = NLA_U16 },
69};
70
71static struct nl_cache_ops rtnl_nh_ops;
72static struct nl_object_ops nh_obj_ops;
73
74static nl_nh_group_t *rtnl_nh_grp_alloc(unsigned size)
75{
76 nl_nh_group_t *nhg;
77
78 _nl_assert(size <= (unsigned)INT_MAX);
79
80 if (!(nhg = calloc(1, sizeof(*nhg))))
81 return NULL;
82
83 nhg->size = size;
84
85 if (!(nhg->entries = calloc(size, sizeof(*nhg->entries)))) {
86 free(nhg);
87 return NULL;
88 }
89
90 nhg->ce_refcnt = 1;
91
92 return nhg;
93}
94
95static void rtnl_nh_grp_put(nl_nh_group_t *nhg)
96{
97 if (!nhg)
98 return;
99
100 _nl_assert(nhg->ce_refcnt > 0);
101
102 nhg->ce_refcnt--;
103
104 if (nhg->ce_refcnt > 0)
105 return;
106
107 free(nhg->entries);
108 free(nhg);
109}
110
111static int rtnh_nh_grp_cmp(const nl_nh_group_t *a, const nl_nh_group_t *b)
112{
113 unsigned i;
114
115 _NL_CMP_SELF(a, b);
116 _NL_CMP_DIRECT(a->size, b->size);
117 for (i = 0; i < a->size; i++) {
118 _NL_CMP_DIRECT(a->entries[i].nh_id, b->entries[i].nh_id);
119 _NL_CMP_DIRECT(a->entries[i].weight, b->entries[i].weight);
120 }
121 return 0;
122}
123
124static int rtnh_nh_grp_clone(nl_nh_group_t *src, nl_nh_group_t **dst)
125{
126 nl_nh_group_t *ret;
127 unsigned i;
128
129 ret = rtnl_nh_grp_alloc(src->size);
130
131 if (!ret)
132 return -NLE_NOMEM;
133
134 for (i = 0; i < src->size; i++) {
135 ret->entries[i].nh_id = src->entries[i].nh_id;
136 ret->entries[i].weight = src->entries[i].weight;
137 }
138
139 *dst = ret;
140
141 return NLE_SUCCESS;
142}
143
144struct rtnl_nh *rtnl_nh_alloc(void)
145{
146 return (struct rtnl_nh *)nl_object_alloc(&nh_obj_ops);
147}
148
149static int nh_clone(struct nl_object *_src, struct nl_object *_dst)
150{
151 struct rtnl_nh *dst = nl_object_priv(_dst);
152 struct rtnl_nh *src = nl_object_priv(_src);
153
154 dst->nh_flags = src->nh_flags;
155 dst->nh_family = src->nh_family;
156 dst->nh_id = src->nh_id;
157 dst->nh_oif = src->nh_oif;
158 dst->nh_group_type = src->nh_group_type;
159 dst->res_grp_buckets = src->res_grp_buckets;
160 dst->res_grp_idle_timer = src->res_grp_idle_timer;
161 dst->res_grp_unbalanced_timer = src->res_grp_unbalanced_timer;
162 dst->ce_mask = src->ce_mask;
163
164 if (src->nh_encap) {
165 dst->nh_encap = rtnl_nh_encap_clone(src->nh_encap);
166 if (!dst->nh_encap)
167 return -NLE_NOMEM;
168 dst->ce_mask |= NH_ATTR_ENCAP;
169 }
170
171 if (src->nh_gateway) {
172 dst->nh_gateway = nl_addr_clone(src->nh_gateway);
173 if (!dst->nh_gateway) {
174 return -NLE_NOMEM;
175 }
176 }
177
178 if (src->nh_group) {
179 if (rtnh_nh_grp_clone(src->nh_group, &dst->nh_group) < 0) {
180 return -NLE_NOMEM;
181 }
182 }
183
184 return 0;
185}
186
187static void nh_free(struct nl_object *obj)
188{
189 struct rtnl_nh *nh = nl_object_priv(obj);
190
191 nl_addr_put(nh->nh_gateway);
192 rtnl_nh_encap_free(nh->nh_encap);
193 rtnl_nh_grp_put(nh->nh_group);
194}
195
196void rtnl_nh_put(struct rtnl_nh *nh)
197{
198 struct nl_object *obj = (struct nl_object *)nh;
199
200 nl_object_put(obj);
201}
202
203static void nexthop_keygen(struct nl_object *obj, uint32_t *hashkey,
204 uint32_t table_sz)
205{
206 struct rtnl_nh *nexthop = nl_object_priv(obj);
207 unsigned int lkey_sz;
208 struct nexthop_hash_key {
209 uint32_t nh_id;
210 } _nl_packed lkey;
211
212 lkey_sz = sizeof(lkey);
213 lkey.nh_id = nexthop->nh_id;
214
215 *hashkey = nl_hash(&lkey, lkey_sz, 0) % table_sz;
216
217 return;
218}
219
220int rtnl_nh_set_gateway(struct rtnl_nh *nexthop, struct nl_addr *addr)
221{
222 struct nl_addr *old = NULL;
223
224 if (!nexthop)
225 return -NLE_INVAL;
226
227 /* preserve old pointer to release after successful update */
228 old = nexthop->nh_gateway;
229
230 if (addr) {
231 struct nl_addr *cloned = nl_addr_clone(addr);
232 if (!cloned)
233 return -NLE_NOMEM;
234
235 nexthop->nh_gateway = cloned;
236 nexthop->ce_mask |= NH_ATTR_GATEWAY;
237 } else {
238 nexthop->nh_gateway = NULL;
239 nexthop->ce_mask &= ~NH_ATTR_GATEWAY;
240 }
241
242 if (old)
243 nl_addr_put(old);
244
245 return 0;
246}
247
248struct nl_addr *rtnl_nh_get_gateway(struct rtnl_nh *nexthop)
249{
250 return nexthop->nh_gateway;
251}
252
253/**
254 * Set nexthop encapsulation
255 * @arg nh Nexthop object
256 * @arg encap Encapsulation descriptor
257 *
258 * Assigns ownership of the encapsulation object to the nexthop. Any
259 * previously configured encapsulation is released. Passing a NULL
260 * encapsulation clears the encapsulation on the nexthop.
261 *
262 * On failure, the function consumes and frees encap.
263 *
264 * @return 0 on success, or the appropriate error-code on failure.
265 */
266int rtnl_nh_set_encap(struct rtnl_nh *nh, struct rtnl_nh_encap *encap)
267{
268 if (!nh) {
269 rtnl_nh_encap_free(encap);
270 return -NLE_INVAL;
271 }
272
273 if (encap && !encap->ops) {
274 rtnl_nh_encap_free(encap);
275 return -NLE_INVAL;
276 }
277
278 rtnl_nh_encap_free(nh->nh_encap);
279
280 if (encap) {
281 nh->nh_encap = encap;
282 nh->ce_mask |= NH_ATTR_ENCAP;
283 } else {
284 nh->nh_encap = NULL;
285 nh->ce_mask &= ~NH_ATTR_ENCAP;
286 }
287
288 return 0;
289}
290
291struct rtnl_nh_encap *rtnl_nh_get_encap(struct rtnl_nh *nh)
292{
293 if (!nh || !(nh->ce_mask & NH_ATTR_ENCAP))
294 return NULL;
295
296 return nh->nh_encap;
297}
298
299int rtnl_nh_set_fdb(struct rtnl_nh *nexthop, int value)
300{
301 if (value)
302 nexthop->ce_mask |= NH_ATTR_FLAG_FDB;
303 else
304 nexthop->ce_mask &= ~NH_ATTR_FLAG_FDB;
305
306 return 0;
307}
308
309int rtnl_nh_get_oif(struct rtnl_nh *nexthop)
310{
311 if (nexthop->ce_mask & NH_ATTR_OIF)
312 return nexthop->nh_oif;
313
314 return 0;
315}
316
317int rtnl_nh_set_oif(struct rtnl_nh *nexthop, uint32_t ifindex)
318{
319 if (!nexthop)
320 return -NLE_INVAL;
321
322 nexthop->nh_oif = (uint32_t)ifindex;
323
324 if (nexthop->nh_oif)
325 nexthop->ce_mask |= NH_ATTR_OIF;
326 else
327 nexthop->ce_mask &= ~NH_ATTR_OIF;
328
329 return 0;
330}
331
332int rtnl_nh_get_fdb(struct rtnl_nh *nexthop)
333{
334 return nexthop->ce_mask & NH_ATTR_FLAG_FDB;
335}
336
337int rtnl_nh_set_family(struct rtnl_nh *nexthop, uint8_t family)
338{
339 if (!nexthop)
340 return -NLE_INVAL;
341
342 nexthop->nh_family = family;
343
344 return 0;
345}
346
347int rtnl_nh_get_family(struct rtnl_nh *nexthop)
348{
349 if (!nexthop)
350 return -NLE_INVAL;
351
352 return nexthop->nh_family;
353}
354
355int rtnl_nh_set_group_type(struct rtnl_nh *nexthop, uint16_t group_type)
356{
357 if (!nexthop)
358 return -NLE_INVAL;
359
360 nexthop->nh_group_type = group_type;
361 nexthop->ce_mask |= NH_ATTR_GROUP_TYPE;
362
363 return 0;
364}
365
366int rtnl_nh_get_group_type(struct rtnl_nh *nexthop)
367{
368 if (!nexthop)
369 return -NLE_INVAL;
370
371 if (!(nexthop->ce_mask & NH_ATTR_GROUP_TYPE))
372 return -NLE_INVAL;
373
374 return (int)nexthop->nh_group_type;
375}
376
377static int _nh_resilient_check(struct rtnl_nh *nexthop)
378{
379 if (!nexthop)
380 return -NLE_INVAL;
381
382 /* Group type must be explicitly set to resilient */
383 if (nexthop->nh_group_type != NEXTHOP_GRP_TYPE_RES)
384 return -NLE_INVAL;
385
386 return 0;
387}
388
389int rtnl_nh_set_res_group_bucket_size(struct rtnl_nh *nexthop, uint16_t buckets)
390{
391 int err = _nh_resilient_check(nexthop);
392 if (err < 0)
393 return err;
394
395 nexthop->res_grp_buckets = buckets;
396
397 if (buckets) {
398 nexthop->ce_mask |= NH_ATTR_RES_BUCKETS;
399 } else {
400 nexthop->ce_mask &= ~NH_ATTR_RES_BUCKETS;
401 }
402
403 return 0;
404}
405
406int rtnl_nh_get_res_group_bucket_size(struct rtnl_nh *nexthop)
407{
408 int err = _nh_resilient_check(nexthop);
409 if (err < 0)
410 return err;
411
412 if (!(nexthop->ce_mask & NH_ATTR_RES_BUCKETS))
413 return -NLE_MISSING_ATTR;
414
415 return nexthop->res_grp_buckets;
416}
417
418int rtnl_nh_set_res_group_idle_timer(struct rtnl_nh *nexthop,
419 uint32_t idle_timer)
420{
421 int err = _nh_resilient_check(nexthop);
422 if (err < 0)
423 return err;
424
425 nexthop->res_grp_idle_timer = idle_timer;
426 nexthop->ce_mask |= NH_ATTR_RES_IDLE_TIMER;
427
428 return 0;
429}
430
431int rtnl_nh_get_res_group_idle_timer(struct rtnl_nh *nexthop,
432 uint32_t *out_value)
433{
434 int err = _nh_resilient_check(nexthop);
435 if (err < 0)
436 return err;
437
438 if (!out_value)
439 return -NLE_INVAL;
440
441 if (!(nexthop->ce_mask & NH_ATTR_RES_IDLE_TIMER))
442 return -NLE_MISSING_ATTR;
443
444 *out_value = nexthop->res_grp_idle_timer;
445
446 return 0;
447}
448
449int rtnl_nh_set_res_group_unbalanced_timer(struct rtnl_nh *nexthop,
450 uint32_t unbalanced_timer)
451{
452 int err = _nh_resilient_check(nexthop);
453 if (err < 0)
454 return err;
455
456 nexthop->res_grp_unbalanced_timer = unbalanced_timer;
457 nexthop->ce_mask |= NH_ATTR_RES_UNBALANCED_TIMER;
458
459 return 0;
460}
461
462int rtnl_nh_get_res_group_unbalanced_timer(struct rtnl_nh *nexthop,
463 uint32_t *out_value)
464{
465 int err = _nh_resilient_check(nexthop);
466 if (err < 0)
467 return err;
468
469 if (!out_value)
470 return -NLE_INVAL;
471
472 if (!(nexthop->ce_mask & NH_ATTR_RES_UNBALANCED_TIMER))
473 return -NLE_MISSING_ATTR;
474
475 *out_value = nexthop->res_grp_unbalanced_timer;
476
477 return 0;
478}
479
480int rtnl_nh_set_group(struct rtnl_nh *nexthop,
481 const nl_nh_group_info_t *entries, unsigned size)
482{
483 nl_nh_group_t *nhg = NULL;
484
485 if (!nexthop)
486 return -NLE_INVAL;
487
488 if (size > 0 && !entries)
489 return -NLE_INVAL;
490
491 if (size == 0) {
492 /* size is 0, thus we want to remove the nh group */
493
494 rtnl_nh_grp_put(nexthop->nh_group);
495 nexthop->nh_group = NULL;
496 nexthop->ce_mask &= ~NH_ATTR_GROUP;
497
498 return 0;
499 }
500
501 nhg = rtnl_nh_grp_alloc(size);
502 if (!nhg)
503 return -NLE_NOMEM;
504
505 memcpy(nhg->entries, entries, size * sizeof(*nhg->entries));
506
507 /* Replace an existing group if present. */
508 rtnl_nh_grp_put(nexthop->nh_group);
509 nexthop->nh_group = nhg;
510 nexthop->ce_mask |= NH_ATTR_GROUP;
511
512 return 0;
513}
514
515int rtnl_nh_get_group_entry(struct rtnl_nh *nexthop, int n)
516{
517 if (!(nexthop->ce_mask & NH_ATTR_GROUP) || !nexthop->nh_group)
518 return -NLE_MISSING_ATTR;
519
520 if (n < 0 || ((unsigned)n) >= nexthop->nh_group->size)
521 return -NLE_INVAL;
522
523 return nexthop->nh_group->entries[n].nh_id;
524}
525
526int rtnl_nh_get_group_size(struct rtnl_nh *nexthop)
527{
528 if (!(nexthop->ce_mask & NH_ATTR_GROUP) || !nexthop->nh_group)
529 return -NLE_MISSING_ATTR;
530
531 _nl_assert(nexthop->nh_group->size <= INT_MAX);
532
533 return (int)nexthop->nh_group->size;
534}
535
536static int rtnl_nh_grp_info(unsigned size, const struct nexthop_grp *vi,
537 nl_nh_group_t **nvi)
538{
539 nl_nh_group_t *ret;
540 unsigned i;
541
542 if (!(ret = rtnl_nh_grp_alloc(size)))
543 return -NLE_NOMEM;
544
545 for (i = 0; i < size; i++) {
546 ret->entries[i].nh_id = vi[i].id;
547 ret->entries[i].weight = vi[i].weight;
548 }
549
550 *nvi = ret;
551 return NLE_SUCCESS;
552}
553
554int rtnl_nh_get_id(struct rtnl_nh *nh)
555{
556 if (nh->ce_mask & NH_ATTR_ID)
557 return nh->nh_id;
558
559 return -NLE_INVAL;
560}
561
562int rtnl_nh_set_id(struct rtnl_nh *nh, uint32_t id)
563{
564 if (!nh)
565 return -NLE_INVAL;
566
567 nh->nh_id = id;
568
569 if (nh->nh_id)
570 nh->ce_mask |= NH_ATTR_ID;
571 else
572 nh->ce_mask &= ~NH_ATTR_ID;
573
574 return 0;
575}
576
577/* ------------------------------------------------------------------------- */
578/* Message construction & kernel interaction */
579/* ------------------------------------------------------------------------- */
580
581/* Build a netlink message representing the supplied nexthop object. */
582static int rtnl_nh_build_msg(struct nl_msg *msg, struct rtnl_nh *nh)
583{
584 struct nhmsg hdr = {
585 .nh_family = nh->nh_family,
586 .nh_protocol = 0, /* kernel will fill in */
587 .nh_flags = nh->nh_flags,
588 };
589
590 if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0)
591 return -NLE_MSGSIZE;
592
593 /* Optional attributes */
594 if (nh->ce_mask & NH_ATTR_ID)
595 NLA_PUT_U32(msg, NHA_ID, nh->nh_id);
596
597 if (nh->ce_mask & NH_ATTR_OIF)
598 NLA_PUT_U32(msg, NHA_OIF, nh->nh_oif);
599
600 if (nh->ce_mask & NH_ATTR_GATEWAY) {
601 if (!nh->nh_gateway)
602 return -NLE_INVAL;
603 NLA_PUT_ADDR(msg, NHA_GATEWAY, nh->nh_gateway);
604 }
605
606 if (nh->ce_mask & NH_ATTR_FLAG_BLACKHOLE)
607 NLA_PUT_FLAG(msg, NHA_BLACKHOLE);
608
609 if (nh->ce_mask & NH_ATTR_ENCAP) {
610 struct nlattr *encap;
611
612 if (!nh->nh_encap || !nh->nh_encap->ops)
613 return -NLE_INVAL;
614
615 NLA_PUT_U16(msg, NHA_ENCAP_TYPE, nh->nh_encap->ops->encap_type);
616
617 encap = nla_nest_start(msg, NHA_ENCAP);
618 if (!encap)
619 goto nla_put_failure;
620
621 if (nh->nh_encap->ops->build_msg) {
622 int err = nh->nh_encap->ops->build_msg(
623 msg, nh->nh_encap->priv);
624 if (err < 0)
625 return err;
626 }
627 nla_nest_end(msg, encap);
628 }
629
630 /* Nexthop group */
631 if (nh->ce_mask & NH_ATTR_GROUP) {
632 struct nexthop_grp *grp;
633 struct nlattr *attr;
634 unsigned int sz;
635
636 if (!nh->nh_group || nh->nh_group->size == 0)
637 return -NLE_INVAL;
638
639 sz = nh->nh_group->size * sizeof(struct nexthop_grp);
640 attr = nla_reserve(msg, NHA_GROUP, sz);
641 if (!attr)
642 goto nla_put_failure;
643
644 grp = nla_data(attr);
645 for (unsigned int i = 0; i < nh->nh_group->size; i++) {
646 grp[i].id = nh->nh_group->entries[i].nh_id;
647 grp[i].weight = nh->nh_group->entries[i].weight;
648 grp[i].resvd1 = 0;
649 grp[i].resvd2 = 0;
650 }
651
652 /* Optional group type */
653 if (nh->nh_group_type)
654 NLA_PUT_U16(msg, NHA_GROUP_TYPE, nh->nh_group_type);
655
656 /* If the group type is resilient and the caller supplied additional
657 * resilient parameters (bucket size, timers, ...), add them as a
658 * nested NHA_RES_GROUP attribute. Only pass through the parameters
659 * that were explicitly set on the nexthop object.
660 */
661 if (nh->nh_group_type == NEXTHOP_GRP_TYPE_RES &&
662 (nh->ce_mask &
663 (NH_ATTR_RES_BUCKETS | NH_ATTR_RES_IDLE_TIMER |
664 NH_ATTR_RES_UNBALANCED_TIMER))) {
665 struct nlattr *res_grp;
666
667 res_grp = nla_nest_start(msg, NHA_RES_GROUP);
668 if (!res_grp)
669 goto nla_put_failure;
670
671 if (nh->ce_mask & NH_ATTR_RES_BUCKETS)
672 NLA_PUT_U16(msg, NHA_RES_GROUP_BUCKETS,
673 nh->res_grp_buckets);
674
675 if (nh->ce_mask & NH_ATTR_RES_IDLE_TIMER)
676 NLA_PUT_U32(msg, NHA_RES_GROUP_IDLE_TIMER,
677 nh->res_grp_idle_timer);
678
679 if (nh->ce_mask & NH_ATTR_RES_UNBALANCED_TIMER)
680 NLA_PUT_U32(msg, NHA_RES_GROUP_UNBALANCED_TIMER,
681 nh->res_grp_unbalanced_timer);
682
683 nla_nest_end(msg, res_grp);
684 }
685 }
686
687 return 0;
688
689nla_put_failure:
690 return -NLE_MSGSIZE;
691}
692
693/* Helper to build generic nexthop request messages */
694static int build_nh_msg(struct rtnl_nh *tmpl, int cmd, int flags,
695 struct nl_msg **result)
696{
697 _nl_auto_nl_msg struct nl_msg *msg = NULL;
698 int err;
699
700 msg = nlmsg_alloc_simple(cmd, flags);
701 if (!msg)
702 return -NLE_NOMEM;
703
704 err = rtnl_nh_build_msg(msg, tmpl);
705 if (err < 0) {
706 return err;
707 }
708
709 *result = _nl_steal_pointer(&msg);
710 return 0;
711}
712
713static int rtnl_nh_build_add_request(struct rtnl_nh *tmpl, int flags,
714 struct nl_msg **result)
715{
716 return build_nh_msg(tmpl, RTM_NEWNEXTHOP, NLM_F_CREATE | flags, result);
717}
718
719int rtnl_nh_add(struct nl_sock *sk, struct rtnl_nh *nh, int flags)
720{
721 _nl_auto_nl_msg struct nl_msg *msg = NULL;
722 int err;
723
724 err = rtnl_nh_build_add_request(nh, flags, &msg);
725 if (err < 0)
726 return err;
727
728 err = nl_send_auto_complete(sk, msg);
729 if (err < 0)
730 return err;
731
732 return wait_for_ack(sk);
733}
734
735struct rtnl_nh_encap *rtnl_nh_encap_alloc(void)
736{
737 return calloc(1, sizeof(struct rtnl_nh_encap));
738}
739
740int rtnl_nh_encap_get_type(struct rtnl_nh_encap *nh_encap)
741{
742 if (!nh_encap)
743 return -NLE_INVAL;
744 if (!nh_encap->ops)
745 return -NLE_INVAL;
746
747 return nh_encap->ops->encap_type;
748}
749
750void rtnl_nh_encap_free(struct rtnl_nh_encap *nh_encap)
751{
752 if (!nh_encap)
753 return;
754
755 if (nh_encap->ops && nh_encap->ops->destructor)
756 nh_encap->ops->destructor(nh_encap->priv);
757
758 free(nh_encap->priv);
759 free(nh_encap);
760}
761
762struct rtnl_nh_encap *rtnl_nh_encap_clone(struct rtnl_nh_encap *src)
763{
764 _nl_auto_rtnl_nh_encap struct rtnl_nh_encap *new_encap = NULL;
765
766 if (!src)
767 return NULL;
768
769 new_encap = rtnl_nh_encap_alloc();
770 if (!new_encap)
771 return NULL;
772
773 new_encap->ops = src->ops;
774 if (new_encap->ops) {
775 new_encap->priv = new_encap->ops->clone(src->priv);
776 if (!new_encap->priv)
777 return NULL;
778 }
779
780 return _nl_steal_pointer(&new_encap);
781}
782
783/*
784 * Retrieve the encapsulation associated with a nexthop if any.
785 */
786struct rtnl_nh_encap *rtnl_route_nh_get_encap(struct rtnl_nexthop *nh)
787{
788 if (!nh)
789 return NULL;
790
791 return nh->rtnh_encap;
792}
793
794static struct nla_policy nh_res_group_policy[NHA_RES_GROUP_MAX + 1] = {
795 [NHA_RES_GROUP_UNSPEC] = { .type = NLA_UNSPEC },
796 [NHA_RES_GROUP_BUCKETS] = { .type = NLA_U16 },
797 [NHA_RES_GROUP_IDLE_TIMER] = { .type = NLA_U32 },
798 [NHA_RES_GROUP_UNBALANCED_TIMER] = { .type = NLA_U32 },
799 [NHA_RES_GROUP_UNBALANCED_TIME] = { .type = NLA_U64 },
800};
801
802static int nexthop_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
803 struct nlmsghdr *n, struct nl_parser_param *pp)
804{
805 _nl_auto_rtnl_nh struct rtnl_nh *nexthop = NULL;
806 struct nhmsg *ifi;
807 struct nlattr *tb[NHA_MAX + 1];
808 int err;
809 int family;
810
811 nexthop = rtnl_nh_alloc();
812 if (nexthop == NULL)
813 return -NLE_NOMEM;
814
815 nexthop->ce_msgtype = n->nlmsg_type;
816
817 if (!nlmsg_valid_hdr(n, sizeof(*ifi)))
818 return -NLE_MSG_TOOSHORT;
819
820 ifi = nlmsg_data(n);
821 family = ifi->nh_family;
822 nexthop->nh_family = family;
823 nexthop->nh_flags = ifi->nh_flags;
824 nexthop->ce_mask = (NH_ATTR_FLAGS);
825
826 err = nlmsg_parse(n, sizeof(*ifi), tb, NHA_MAX, rtnl_nh_policy);
827 if (err < 0)
828 return err;
829
830 if (tb[NHA_ID]) {
831 nexthop->nh_id = nla_get_u32(tb[NHA_ID]);
832 nexthop->ce_mask |= NH_ATTR_ID;
833 }
834
835 if (tb[NHA_OIF]) {
836 nexthop->nh_oif = nla_get_u32(tb[NHA_OIF]);
837 nexthop->ce_mask |= NH_ATTR_OIF;
838 }
839
840 if (tb[NHA_GATEWAY]) {
841 nexthop->nh_gateway =
842 nl_addr_alloc_attr(tb[NHA_GATEWAY], family);
843 nexthop->ce_mask |= NH_ATTR_GATEWAY;
844 }
845
846 if (tb[NHA_GROUP_TYPE]) {
847 nexthop->nh_group_type = nla_get_u16(tb[NHA_GROUP_TYPE]);
848 nexthop->ce_mask |= NH_ATTR_GROUP_TYPE;
849 }
850
851 if (tb[NHA_ENCAP] && tb[NHA_ENCAP_TYPE]) {
852 _nl_auto_rtnl_nh_encap struct rtnl_nh_encap *nh_encap = NULL;
853
854 err = nh_encap_parse_msg(tb[NHA_ENCAP], tb[NHA_ENCAP_TYPE],
855 &nh_encap);
856 if (err < 0)
857 return err;
858
859 err = rtnl_nh_set_encap(nexthop, _nl_steal_pointer(&nh_encap));
860 if (err < 0)
861 return err;
862 }
863
864 if (tb[NHA_BLACKHOLE]) {
865 nexthop->ce_mask |= NH_ATTR_FLAG_BLACKHOLE;
866 }
867
868 if (tb[NHA_GROUPS]) {
869 nexthop->ce_mask |= NH_ATTR_FLAG_GROUPS;
870 }
871
872 if (tb[NHA_FDB]) {
873 nexthop->ce_mask |= NH_ATTR_FLAG_FDB;
874 }
875
876 if (tb[NHA_GROUP]) {
877 nl_nh_group_t *nh_group = NULL;
878 const void *data;
879 unsigned size;
880 unsigned len;
881
882 data = nla_data(tb[NHA_GROUP]);
883 len = _nla_len(tb[NHA_GROUP]);
884 size = len / sizeof(struct nexthop_grp);
885
886 err = rtnl_nh_grp_info(size, (const struct nexthop_grp *)data,
887 &nh_group);
888 if (err < 0) {
889 return err;
890 }
891
892 nexthop->nh_group = nh_group;
893 nexthop->ce_mask |= NH_ATTR_GROUP;
894 }
895
896 /* Parse resilient nexthop group parameters if present */
897 if (tb[NHA_RES_GROUP]) {
898 struct nlattr *rg[NHA_RES_GROUP_MAX + 1];
899
900 err = nla_parse_nested(rg, NHA_RES_GROUP_MAX, tb[NHA_RES_GROUP],
901 nh_res_group_policy);
902 if (err < 0)
903 return err;
904
905 if (rg[NHA_RES_GROUP_BUCKETS]) {
906 nexthop->res_grp_buckets =
907 nla_get_u16(rg[NHA_RES_GROUP_BUCKETS]);
908 nexthop->ce_mask |= NH_ATTR_RES_BUCKETS;
909 }
910 if (rg[NHA_RES_GROUP_IDLE_TIMER]) {
911 nexthop->res_grp_idle_timer =
912 nla_get_u32(rg[NHA_RES_GROUP_IDLE_TIMER]);
913 nexthop->ce_mask |= NH_ATTR_RES_IDLE_TIMER;
914 }
915 if (rg[NHA_RES_GROUP_UNBALANCED_TIMER]) {
916 nexthop->res_grp_unbalanced_timer =
917 nla_get_u32(rg[NHA_RES_GROUP_UNBALANCED_TIMER]);
918 nexthop->ce_mask |= NH_ATTR_RES_UNBALANCED_TIMER;
919 }
920 /* Mark that we have a resilient group block */
921 nexthop->ce_mask |= NH_ATTR_RES_GROUP;
922 }
923
924 return pp->pp_cb((struct nl_object *)nexthop, pp);
925}
926
927static int nexthop_request_update(struct nl_cache *cache, struct nl_sock *sk)
928{
929 _nl_auto_nl_msg struct nl_msg *msg = NULL;
930 int family = cache->c_iarg1;
931 struct nhmsg hdr = { .nh_family = family };
932 int err;
933
934 msg = nlmsg_alloc_simple(RTM_GETNEXTHOP, NLM_F_DUMP);
935 if (!msg)
936 return -NLE_NOMEM;
937
938 if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0)
939 return -NLE_MSGSIZE;
940
941 err = nl_send_auto(sk, msg);
942 if (err < 0)
943 return err;
944
945 return NLE_SUCCESS;
946}
947
948static void dump_nh_group(nl_nh_group_t *group, struct nl_dump_params *dp)
949{
950 unsigned i;
951
952 nl_dump(dp, " nh_grp:");
953 for (i = 0; i < group->size; i++) {
954 nl_dump(dp, " %u", group->entries[i].nh_id);
955 }
956}
957
958static void nh_dump_line(struct nl_object *obj, struct nl_dump_params *dp)
959{
960 struct nl_cache *cache;
961 char buf[128];
962 struct rtnl_nh *nh = nl_object_priv(obj);
963
964 cache = nl_cache_mngt_require_safe("route/nh");
965
966 if (nh->ce_mask & NH_ATTR_ID)
967 nl_dump(dp, "nhid %u", nh->nh_id);
968
969 if (nh->ce_mask & NH_ATTR_OIF)
970 nl_dump(dp, " oif %d", nh->nh_oif);
971
972 if (nh->ce_mask & NH_ATTR_GATEWAY)
973 nl_dump(dp, " via %s",
974 nl_addr2str(nh->nh_gateway, buf, sizeof(buf)));
975
976 if (nh->ce_mask & NH_ATTR_ENCAP && nh->nh_encap)
977 nh_encap_dump(nh->nh_encap, dp);
978
979 if (nh->ce_mask & NH_ATTR_FLAG_BLACKHOLE)
980 nl_dump(dp, " blackhole");
981
982 if (nh->ce_mask & NH_ATTR_FLAG_GROUPS)
983 nl_dump(dp, " groups");
984
985 if (nh->ce_mask & NH_ATTR_GROUP)
986 dump_nh_group(nh->nh_group, dp);
987
988 /* Dump resilient group parameters */
989 if (nh->nh_group_type == NEXTHOP_GRP_TYPE_RES) {
990 if (nh->ce_mask & NH_ATTR_RES_BUCKETS)
991 nl_dump(dp, " buckets %u", nh->res_grp_buckets);
992 if (nh->ce_mask & NH_ATTR_RES_IDLE_TIMER)
993 nl_dump(dp, " idle-timer %u", nh->res_grp_idle_timer);
994 if (nh->ce_mask & NH_ATTR_RES_UNBALANCED_TIMER)
995 nl_dump(dp, " unbalanced-timer %u",
996 nh->res_grp_unbalanced_timer);
997 }
998
999 if (nh->ce_mask & NH_ATTR_FLAG_FDB)
1000 nl_dump(dp, " fdb");
1001
1002 nl_dump(dp, "\n");
1003
1004 if (cache)
1005 nl_cache_put(cache);
1006}
1007
1008static void nh_dump_details(struct nl_object *nh, struct nl_dump_params *dp)
1009{
1010 nh_dump_line(nh, dp);
1011}
1012
1013static uint64_t nh_compare(struct nl_object *a, struct nl_object *b,
1014 uint64_t attrs, int loose)
1015{
1016 int diff = 0;
1017 struct rtnl_nh *src = nl_object_priv(a);
1018 struct rtnl_nh *dst = nl_object_priv(b);
1019
1020#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR)
1021 diff |= _DIFF(NH_ATTR_ID, src->nh_id != dst->nh_id);
1022 diff |= _DIFF(NH_ATTR_GATEWAY,
1023 nl_addr_cmp(src->nh_gateway, dst->nh_gateway));
1024 diff |= _DIFF(NH_ATTR_OIF, src->nh_oif != dst->nh_oif);
1025 diff |= _DIFF(NH_ATTR_GROUP,
1026 rtnh_nh_grp_cmp(src->nh_group, dst->nh_group));
1027 diff |= _DIFF(NH_ATTR_GROUP_TYPE,
1028 src->nh_group_type != dst->nh_group_type);
1029 diff |= _DIFF(NH_ATTR_FLAG_FDB, false);
1030 diff |= _DIFF(NH_ATTR_FLAG_GROUPS, false);
1031 diff |= _DIFF(NH_ATTR_FLAG_BLACKHOLE, false);
1032 diff |= _DIFF(NH_ATTR_RES_BUCKETS,
1033 src->res_grp_buckets != dst->res_grp_buckets);
1034 diff |= _DIFF(NH_ATTR_RES_IDLE_TIMER,
1035 src->res_grp_idle_timer != dst->res_grp_idle_timer);
1036 diff |= _DIFF(NH_ATTR_RES_UNBALANCED_TIMER,
1037 src->res_grp_unbalanced_timer !=
1038 dst->res_grp_unbalanced_timer);
1039 diff |= _DIFF(NH_ATTR_ENCAP,
1040 nh_encap_compare(src->nh_encap, dst->nh_encap));
1041#undef _DIFF
1042
1043 return diff;
1044}
1045
1046struct rtnl_nh *rtnl_nh_get(struct nl_cache *cache, int nhid)
1047{
1048 struct rtnl_nh *nh;
1049
1050 if (cache->c_ops != &rtnl_nh_ops)
1051 return NULL;
1052
1053 nl_list_for_each_entry(nh, &cache->c_items, ce_list) {
1054 if (nh->nh_id == ((unsigned)nhid)) {
1055 nl_object_get((struct nl_object *)nh);
1056 return nh;
1057 }
1058 }
1059
1060 return NULL;
1061}
1062
1063/**
1064 * Allocate nexthop cache and fill in all configured nexthops.
1065 * @arg sk Netnexthop socket.
1066 * @arg family nexthop address family or AF_UNSPEC
1067 * @arg result Pointer to store resulting cache.
1068 * @arg flags Flags to set in nexthop cache before filling
1069 *
1070 * Allocates and initializes a new nexthop cache. If \c sk is valid, a netnexthop
1071 * message is sent to the kernel requesting a full dump of all configured
1072 * nexthops. The returned messages are parsed and filled into the cache. If
1073 * the operation succeeds, the resulting cache will contain a nexthop object for
1074 * each nexthop configured in the kernel. If \c sk is NULL, returns 0 but the
1075 * cache is still empty.
1076 *
1077 * If \c family is set to an address family other than \c AF_UNSPEC the
1078 * contents of the cache can be limited to a specific address family.
1079 * Currently the following address families are supported:
1080 * - AF_BRIDGE
1081 * - AF_INET6
1082 *
1083 * @route_doc{nexthop_list, Get List of nexthops}
1084 * @see rtnl_nh_get()
1085 * @see rtnl_nh_get_by_name()
1086 * @return 0 on success or a negative error code.
1087 */
1088static int rtnl_nh_alloc_cache_flags(struct nl_sock *sk, int family,
1089 struct nl_cache **result,
1090 unsigned int flags)
1091{
1092 struct nl_cache *cache;
1093 int err;
1094
1095 cache = nl_cache_alloc(&rtnl_nh_ops);
1096 if (!cache)
1097 return -NLE_NOMEM;
1098
1099 cache->c_iarg1 = family;
1100
1101 if (flags)
1102 nl_cache_set_flags(cache, flags);
1103
1104 if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
1105 nl_cache_free(cache);
1106 return err;
1107 }
1108
1109 *result = cache;
1110 return 0;
1111}
1112
1113/**
1114 * Allocate nexthop cache and fill in all configured nexthops.
1115 * @arg sk Netnexthop socket.
1116 * @arg family nexthop address family or AF_UNSPEC
1117 * @arg result Pointer to store resulting cache.
1118 *
1119 * Allocates and initializes a new nexthop cache. If \c sk is valid, a netnexthop
1120 * message is sent to the kernel requesting a full dump of all configured
1121 * nexthops. The returned messages are parsed and filled into the cache. If
1122 * the operation succeeds, the resulting cache will contain a nexthop object for
1123 * each nexthop configured in the kernel. If \c sk is NULL, returns 0 but the
1124 * cache is still empty.
1125 *
1126 * If \c family is set to an address family other than \c AF_UNSPEC the
1127 * contents of the cache can be limited to a specific address family.
1128 * Currently the following address families are supported:
1129 * - AF_BRIDGE
1130 * - AF_INET6
1131 *
1132 * @route_doc{nexthop_list, Get List of nexthops}
1133 * @see rtnl_nh_get()
1134 * @see rtnl_nh_get_by_name()
1135 * @return 0 on success or a negative error code.
1136 */
1137int rtnl_nh_alloc_cache(struct nl_sock *sk, int family,
1138 struct nl_cache **result)
1139{
1140 return rtnl_nh_alloc_cache_flags(sk, family, result, 0);
1141}
1142
1143static struct nl_object_ops nh_obj_ops = {
1144 .oo_name = "route/nh",
1145 .oo_size = sizeof(struct rtnl_nh),
1146 .oo_free_data = nh_free,
1147 .oo_clone = nh_clone,
1148 .oo_dump = {
1149 [NL_DUMP_LINE] = nh_dump_line,
1150 [NL_DUMP_DETAILS] = nh_dump_details,
1151 },
1152 .oo_compare = nh_compare,
1153 .oo_keygen = nexthop_keygen,
1154 .oo_attrs2str = rtnl_route_nh_flags2str,
1155 .oo_id_attrs = NH_ATTR_ID,
1156};
1157
1158static struct nl_af_group nh_groups[] = {
1159 { AF_UNSPEC, RTNLGRP_NEXTHOP },
1160 { END_OF_GROUP_LIST },
1161};
1162
1163static struct nl_cache_ops rtnl_nh_ops = {
1164 .co_name = "route/nh",
1165 .co_hdrsize = sizeof(struct nhmsg),
1166 .co_msgtypes = {
1167 { RTM_NEWNEXTHOP, NL_ACT_NEW, "new" },
1168 { RTM_DELNEXTHOP, NL_ACT_DEL, "del" },
1169 { RTM_GETNEXTHOP, NL_ACT_GET, "get" },
1170 END_OF_MSGTYPES_LIST,
1171 },
1172 .co_protocol = NETLINK_ROUTE,
1173 .co_groups = nh_groups,
1174 .co_request_update = nexthop_request_update,
1175 .co_msg_parser = nexthop_msg_parser,
1176 .co_obj_ops = &nh_obj_ops,
1177};
1178
1179static void _nl_init nexthop_init(void)
1180{
1181 nl_cache_mngt_register(&rtnl_nh_ops);
1182}
1183
1184static void _nl_exit nexthop_exit(void)
1185{
1186 nl_cache_mngt_unregister(&rtnl_nh_ops);
1187}
nl_addr_alloc_attr
struct nl_addr * nl_addr_alloc_attr(const struct nlattr *nla, int family)
Allocate abstract address based on Netlink attribute.
Definition addr.c:261
nl_addr_cmp
int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b)
Compare abstract addresses.
Definition addr.c:587
nl_addr_clone
struct nl_addr * nl_addr_clone(const struct nl_addr *addr)
Clone existing abstract address object.
Definition addr.c:495
nl_addr2str
char * nl_addr2str(const struct nl_addr *addr, char *buf, size_t size)
Convert abstract address object to character string.
Definition addr.c:1001
nl_addr_put
void nl_addr_put(struct nl_addr *addr)
Decrease the reference counter of an abstract address.
Definition addr.c:541
nla_get_u32
uint32_t nla_get_u32(const struct nlattr *nla)
Return payload of 32 bit integer attribute.
Definition attr.c:714
nla_get_u16
uint16_t nla_get_u16(const struct nlattr *nla)
Return payload of 16 bit integer attribute.
Definition attr.c:664
NLA_PUT_FLAG
#define NLA_PUT_FLAG(msg, attrtype)
Add flag attribute to netlink message.
Definition attr.h:272
NLA_PUT_U16
#define NLA_PUT_U16(msg, attrtype, value)
Add 16 bit integer attribute to netlink message.
Definition attr.h:219
NLA_PUT_ADDR
#define NLA_PUT_ADDR(msg, attrtype, addr)
Add address attribute to netlink message.
Definition attr.h:290
nla_data
void * nla_data(const struct nlattr *nla)
Return pointer to the payload section.
Definition attr.c:119
NLA_PUT_U32
#define NLA_PUT_U32(msg, attrtype, value)
Add 32 bit integer attribute to netlink message.
Definition attr.h:237
nla_nest_start
struct nlattr * nla_nest_start(struct nl_msg *msg, int attrtype)
Start a new level of nested attributes.
Definition attr.c:974
nla_parse_nested
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, const struct nla_policy *policy)
Create attribute index based on nested attribute.
Definition attr.c:1101
nla_nest_end
int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
Finalize nesting of attributes.
Definition attr.c:1037
nla_reserve
struct nlattr * nla_reserve(struct nl_msg *msg, int attrtype, int attrlen)
Reserve space for a attribute.
Definition attr.c:461
NLA_U64
@ NLA_U64
64 bit integer
Definition attr.h:38
NLA_UNSPEC
@ NLA_UNSPEC
Unspecified type, binary data chunk.
Definition attr.h:34
NLA_U16
@ NLA_U16
16 bit integer
Definition attr.h:36
NLA_NESTED
@ NLA_NESTED
Nested attributes.
Definition attr.h:42
NLA_U32
@ NLA_U32
32 bit integer
Definition attr.h:37
nl_cache_mngt_unregister
int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
Unregister a set of cache operations.
Definition cache_mngt.c:287
nl_cache_mngt_register
int nl_cache_mngt_register(struct nl_cache_ops *ops)
Register a set of cache operations.
Definition cache_mngt.c:252
nl_cache_mngt_require_safe
struct nl_cache * nl_cache_mngt_require_safe(const char *name)
Return cache previously provided via nl_cache_mngt_provide()
Definition cache_mngt.c:430
nl_cache_refill
int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
(Re)fill a cache with the contents in the kernel.
Definition cache.c:1043
nl_cache_set_flags
void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags)
Set cache flags.
Definition cache.c:608
nl_cache_free
void nl_cache_free(struct nl_cache *cache)
Free a cache.
Definition cache.c:403
nl_cache_alloc
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate new cache.
Definition cache.c:185
nlmsg_alloc_simple
struct nl_msg * nlmsg_alloc_simple(int nlmsgtype, int flags)
Allocate a new netlink message.
Definition msg.c:352
nlmsg_data
void * nlmsg_data(const struct nlmsghdr *nlh)
Return pointer to message payload.
Definition msg.c:108
nlmsg_parse
int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy)
parse attributes of a netlink message
Definition msg.c:219
nlmsg_append
int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad)
Append data to tail of a netlink message.
Definition msg.c:456
nl_object_put
void nl_object_put(struct nl_object *obj)
Release a reference from an object.
Definition object.c:221
nl_object_get
void nl_object_get(struct nl_object *obj)
Acquire a reference on a object.
Definition object.c:210
nl_object_alloc
struct nl_object * nl_object_alloc(struct nl_object_ops *ops)
Allocate a new object of kind specified by the operations handle.
Definition object.c:55
nl_send_auto
int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg)
Finalize and transmit Netlink message.
Definition nl.c:515
nl_send_auto_complete
int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
Definition nl.c:1250
nl_dump
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition utils.c:1015
NL_DUMP_LINE
@ NL_DUMP_LINE
Dump object briefly on one line.
Definition types.h:20
NL_DUMP_DETAILS
@ NL_DUMP_DETAILS
Dump all attributes but no statistics.
Definition types.h:21
nl_dump_params
Dumping parameters.
Definition types.h:32
nl_nh_group_info::nh_id
uint32_t nh_id
Definition nh.h:19
nl_nh_group_info::weight
uint8_t weight
Definition nh.h:20
nla_policy
Attribute validation policy.
Definition attr.h:66
rtnl_nh_encap
Definition nl-route.h:79