libnl 3.10.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
10#include <netlink/route/nh.h>
11#include <netlink/hashtable.h>
12#include <netlink/route/nexthop.h>
13
14#include "nl-aux-route/nl-route.h"
15#include "nl-route.h"
16#include "nl-priv-dynamic-core/nl-core.h"
17#include "nl-priv-dynamic-core/cache-api.h"
18
19/** @cond SKIP */
20struct rtnl_nh {
21 NLHDR_COMMON
22
23 uint8_t nh_family;
24 uint32_t nh_flags;
25
26 uint32_t nh_id;
27 uint32_t nh_group_type;
28 nl_nh_group_t *nh_group;
29 uint32_t nh_oif;
30 struct nl_addr *nh_gateway;
31};
32
33#define NH_ATTR_FLAGS (1 << 0)
34#define NH_ATTR_ID (1 << 1)
35#define NH_ATTR_GROUP (1 << 2)
36#define NH_ATTR_FLAG_BLACKHOLE (1 << 3)
37#define NH_ATTR_OIF (1 << 4)
38#define NH_ATTR_GATEWAY (1 << 5)
39#define NH_ATTR_FLAG_GROUPS (1 << 6)
40#define NH_ATTR_FLAG_FDB (1 << 8)
41/** @endcond */
42
43struct nla_policy rtnl_nh_policy[NHA_MAX + 1] = {
44 [NHA_UNSPEC] = { .type = NLA_UNSPEC },
45 [NHA_ID] = { .type = NLA_U32 },
46 [NHA_GROUP] = { .type = NLA_NESTED },
47 [NHA_GROUP_TYPE] = { .type = NLA_U16 },
48 [NHA_BLACKHOLE] = { .type = NLA_UNSPEC },
49 [NHA_OIF] = { .type = NLA_U32 },
50};
51
52static struct nl_cache_ops rtnl_nh_ops;
53static struct nl_object_ops nh_obj_ops;
54
55static nl_nh_group_t *rtnl_nh_grp_alloc(unsigned size)
56{
57 nl_nh_group_t *nhg;
58
59 _nl_assert(size <= (unsigned)INT_MAX);
60
61 if (!(nhg = calloc(1, sizeof(*nhg))))
62 return NULL;
63
64 nhg->size = size;
65
66 if (!(nhg->entries = calloc(size, sizeof(*nhg->entries)))) {
67 free(nhg);
68 return NULL;
69 }
70
71 nhg->ce_refcnt = 1;
72
73 return nhg;
74}
75
76static void rtnl_nh_grp_put(nl_nh_group_t *nhg)
77{
78 if (!nhg)
79 return;
80
81 _nl_assert(nhg->ce_refcnt > 0);
82
83 nhg->ce_refcnt--;
84
85 if (nhg->ce_refcnt > 0)
86 return;
87
88 free(nhg);
89}
90
91static int rtnh_nh_grp_cmp(const nl_nh_group_t *a, const nl_nh_group_t *b)
92{
93 unsigned i;
94
95 _NL_CMP_SELF(a, b);
96 _NL_CMP_DIRECT(a->size, b->size);
97 for (i = 0; i < a->size; i++) {
98 _NL_CMP_DIRECT(a->entries[i].nh_id, b->entries[i].nh_id);
99 _NL_CMP_DIRECT(a->entries[i].weight, b->entries[i].weight);
100 }
101 return 0;
102}
103
104static int rtnh_nh_grp_clone(nl_nh_group_t *src, nl_nh_group_t **dst)
105{
106 nl_nh_group_t *ret;
107 unsigned i;
108
109 ret = rtnl_nh_grp_alloc(src->size);
110
111 if (!ret)
112 return -NLE_NOMEM;
113
114 for (i = 0; i < src->size; i++) {
115 ret->entries[i].nh_id = src->entries[i].nh_id;
116 ret->entries[i].weight = src->entries[i].weight;
117 }
118
119 *dst = ret;
120
121 return NLE_SUCCESS;
122}
123
124struct rtnl_nh *rtnl_nh_alloc(void)
125{
126 return (struct rtnl_nh *)nl_object_alloc(&nh_obj_ops);
127}
128
129static int nh_clone(struct nl_object *_src, struct nl_object *_dst)
130{
131 struct rtnl_nh *dst = nl_object_priv(_dst);
132 struct rtnl_nh *src = nl_object_priv(_src);
133
134 dst->nh_flags = src->nh_flags;
135 dst->nh_family = src->nh_family;
136 dst->nh_id = src->nh_id;
137 dst->nh_oif = src->nh_oif;
138 dst->ce_mask = src->ce_mask;
139
140 if (src->nh_gateway) {
141 dst->nh_gateway = nl_addr_clone(src->nh_gateway);
142 if (!dst->nh_gateway) {
143 return -NLE_NOMEM;
144 }
145 }
146
147 if (src->nh_group) {
148 if (rtnh_nh_grp_clone(src->nh_group, &dst->nh_group) < 0) {
149 return -NLE_NOMEM;
150 }
151 }
152
153 return 0;
154}
155
156static void nh_free(struct nl_object *obj)
157{
158 struct rtnl_nh *nh = nl_object_priv(obj);
159 nl_addr_put(nh->nh_gateway);
160
161 if (nh->nh_group)
162 rtnl_nh_grp_put(nh->nh_group);
163}
164
165void rtnl_nh_put(struct rtnl_nh *nh)
166{
167 struct nl_object *obj = (struct nl_object *)nh;
168
169 nl_object_put(obj);
170}
171
172static void nexthop_keygen(struct nl_object *obj, uint32_t *hashkey,
173 uint32_t table_sz)
174{
175 struct rtnl_nh *nexthop = nl_object_priv(obj);
176 unsigned int lkey_sz;
177 struct nexthop_hash_key {
178 uint32_t nh_id;
179 } _nl_packed lkey;
180
181 lkey_sz = sizeof(lkey);
182 lkey.nh_id = nexthop->nh_id;
183
184 *hashkey = nl_hash(&lkey, lkey_sz, 0) % table_sz;
185
186 return;
187}
188
189int rtnl_nh_set_gateway(struct rtnl_nh *nexthop, struct nl_addr *addr)
190{
191 if (nexthop->ce_mask & NH_ATTR_GATEWAY) {
192 nl_addr_put(nexthop->nh_gateway);
193 }
194
195 nexthop->nh_gateway = nl_addr_clone(addr);
196 nexthop->ce_mask |= NH_ATTR_GATEWAY;
197
198 return 0;
199}
200
201struct nl_addr *rtnl_nh_get_gateway(struct rtnl_nh *nexthop)
202{
203 return nexthop->nh_gateway;
204}
205
206int rtnl_nh_set_fdb(struct rtnl_nh *nexthop, int value)
207{
208 if (value)
209 nexthop->ce_mask |= NH_ATTR_FLAG_FDB;
210 else
211 nexthop->ce_mask &= ~NH_ATTR_FLAG_FDB;
212
213 return 0;
214}
215
216int rtnl_nh_get_oif(struct rtnl_nh *nexthop)
217{
218 if (nexthop->ce_mask & NH_ATTR_OIF)
219 return nexthop->nh_oif;
220
221 return 0;
222}
223
224int rtnl_nh_get_fdb(struct rtnl_nh *nexthop)
225{
226 return nexthop->ce_mask & NH_ATTR_FLAG_FDB;
227}
228
229int rtnl_nh_get_group_entry(struct rtnl_nh *nexthop, int n)
230{
231 if (!(nexthop->ce_mask & NH_ATTR_GROUP) || !nexthop->nh_group)
232 return -NLE_MISSING_ATTR;
233
234 if (n < 0 || ((unsigned)n) >= nexthop->nh_group->size)
235 return -NLE_INVAL;
236
237 return nexthop->nh_group->entries[n].nh_id;
238}
239
240int rtnl_nh_get_group_size(struct rtnl_nh *nexthop)
241{
242 if (!(nexthop->ce_mask & NH_ATTR_GROUP) || !nexthop->nh_group)
243 return -NLE_MISSING_ATTR;
244
245 _nl_assert(nexthop->nh_group->size <= INT_MAX);
246
247 return (int)nexthop->nh_group->size;
248}
249
250static int rtnl_nh_grp_info(unsigned size, const struct nexthop_grp *vi,
251 nl_nh_group_t **nvi)
252{
253 nl_nh_group_t *ret;
254 unsigned i;
255
256 if (!(ret = rtnl_nh_grp_alloc(size)))
257 return -NLE_NOMEM;
258
259 for (i = 0; i < size; i++) {
260 ret->entries[i].nh_id = vi[i].id;
261 ret->entries[i].weight = vi[i].weight;
262 }
263
264 *nvi = ret;
265 return NLE_SUCCESS;
266}
267
268int rtnl_nh_get_id(struct rtnl_nh *nh)
269{
270 if (nh->ce_mask & NH_ATTR_ID)
271 return nh->nh_id;
272
273 return -NLE_INVAL;
274}
275
276static int nexthop_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
277 struct nlmsghdr *n, struct nl_parser_param *pp)
278{
279 _nl_auto_rtnl_nh struct rtnl_nh *nexthop = NULL;
280 struct nhmsg *ifi;
281 struct nlattr *tb[NHA_MAX + 1];
282 int err;
283 int family;
284
285 nexthop = rtnl_nh_alloc();
286 if (nexthop == NULL)
287 return -NLE_NOMEM;
288
289 nexthop->ce_msgtype = n->nlmsg_type;
290
291 if (!nlmsg_valid_hdr(n, sizeof(*ifi)))
292 return -NLE_MSG_TOOSHORT;
293
294 ifi = nlmsg_data(n);
295 family = ifi->nh_family;
296 nexthop->nh_family = family;
297 nexthop->nh_flags = ifi->nh_flags;
298 nexthop->ce_mask = (NH_ATTR_FLAGS);
299
300 err = nlmsg_parse(n, sizeof(*ifi), tb, NHA_MAX, rtnl_nh_policy);
301 if (err < 0)
302 return err;
303
304 if (tb[NHA_ID]) {
305 nexthop->nh_id = nla_get_u32(tb[NHA_ID]);
306 nexthop->ce_mask |= NH_ATTR_ID;
307 }
308
309 if (tb[NHA_OIF]) {
310 nexthop->nh_oif = nla_get_u32(tb[NHA_OIF]);
311 nexthop->ce_mask |= NH_ATTR_OIF;
312 }
313
314 if (tb[NHA_GATEWAY]) {
315 nexthop->nh_gateway =
316 nl_addr_alloc_attr(tb[NHA_GATEWAY], family);
317 nexthop->ce_mask |= NH_ATTR_GATEWAY;
318 }
319
320 if (tb[NHA_BLACKHOLE]) {
321 nexthop->ce_mask |= NH_ATTR_FLAG_BLACKHOLE;
322 }
323
324 if (tb[NHA_GROUPS]) {
325 nexthop->ce_mask |= NH_ATTR_FLAG_GROUPS;
326 }
327
328 if (tb[NHA_FDB]) {
329 nexthop->ce_mask |= NH_ATTR_FLAG_FDB;
330 }
331
332 if (tb[NHA_GROUP]) {
333 nl_nh_group_t *nh_group = NULL;
334 const void *data;
335 unsigned size;
336 unsigned len;
337
338 data = nla_data(tb[NHA_GROUP]);
339 len = _nla_len(tb[NHA_GROUP]);
340 size = len / sizeof(struct nexthop_grp);
341
342 err = rtnl_nh_grp_info(size, (const struct nexthop_grp *)data,
343 &nh_group);
344 if (err < 0) {
345 return err;
346 }
347
348 nexthop->nh_group = nh_group;
349 nexthop->ce_mask |= NH_ATTR_GROUP;
350 }
351
352 return pp->pp_cb((struct nl_object *)nexthop, pp);
353}
354
355static int nexthop_request_update(struct nl_cache *cache, struct nl_sock *sk)
356{
357 _nl_auto_nl_msg struct nl_msg *msg = NULL;
358 int family = cache->c_iarg1;
359 struct nhmsg hdr = { .nh_family = family };
360 int err;
361
362 msg = nlmsg_alloc_simple(RTM_GETNEXTHOP, NLM_F_DUMP);
363 if (!msg)
364 return -NLE_NOMEM;
365
366 if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0)
367 return -NLE_MSGSIZE;
368
369 err = nl_send_auto(sk, msg);
370 if (err < 0)
371 return err;
372
373 return NLE_SUCCESS;
374}
375
376static void dump_nh_group(nl_nh_group_t *group, struct nl_dump_params *dp)
377{
378 unsigned i;
379
380 nl_dump(dp, " nh_grp:");
381 for (i = 0; i < group->size; i++) {
382 nl_dump(dp, " %u", group->entries[i].nh_id);
383 }
384}
385
386static void nh_dump_line(struct nl_object *obj, struct nl_dump_params *dp)
387{
388 struct nl_cache *cache;
389 char buf[128];
390 struct rtnl_nh *nh = nl_object_priv(obj);
391
392 cache = nl_cache_mngt_require_safe("route/nh");
393
394 if (nh->ce_mask & NH_ATTR_ID)
395 nl_dump(dp, "nhid %u", nh->nh_id);
396
397 if (nh->ce_mask & NH_ATTR_OIF)
398 nl_dump(dp, " oif %d", nh->nh_oif);
399
400 if (nh->ce_mask & NH_ATTR_GATEWAY)
401 nl_dump(dp, " via %s",
402 nl_addr2str(nh->nh_gateway, buf, sizeof(buf)));
403
404 if (nh->ce_mask & NH_ATTR_FLAG_BLACKHOLE)
405 nl_dump(dp, " blackhole");
406
407 if (nh->ce_mask & NH_ATTR_FLAG_GROUPS)
408 nl_dump(dp, " groups");
409
410 if (nh->ce_mask & NH_ATTR_GROUP)
411 dump_nh_group(nh->nh_group, dp);
412
413 if (nh->ce_mask & NH_ATTR_FLAG_FDB)
414 nl_dump(dp, " fdb");
415
416 nl_dump(dp, "\n");
417
418 if (cache)
419 nl_cache_put(cache);
420}
421
422static void nh_dump_details(struct nl_object *nh, struct nl_dump_params *dp)
423{
424 nh_dump_line(nh, dp);
425}
426
427static uint64_t nh_compare(struct nl_object *a, struct nl_object *b,
428 uint64_t attrs, int loose)
429{
430 int diff = 0;
431 struct rtnl_nh *src = nl_object_priv(a);
432 struct rtnl_nh *dst = nl_object_priv(b);
433
434#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR)
435 diff |= _DIFF(NH_ATTR_ID, src->nh_id != dst->nh_id);
436 diff |= _DIFF(NH_ATTR_GATEWAY,
437 nl_addr_cmp(src->nh_gateway, dst->nh_gateway));
438 diff |= _DIFF(NH_ATTR_OIF, src->nh_oif != dst->nh_oif);
439 diff |= _DIFF(NH_ATTR_GROUP,
440 rtnh_nh_grp_cmp(src->nh_group, dst->nh_group));
441 diff |= _DIFF(NH_ATTR_FLAG_FDB, false);
442 diff |= _DIFF(NH_ATTR_FLAG_GROUPS, false);
443 diff |= _DIFF(NH_ATTR_FLAG_BLACKHOLE, false);
444#undef _DIFF
445
446 return diff;
447}
448
449struct rtnl_nh *rtnl_nh_get(struct nl_cache *cache, int nhid)
450{
451 struct rtnl_nh *nh;
452
453 if (cache->c_ops != &rtnl_nh_ops)
454 return NULL;
455
456 nl_list_for_each_entry(nh, &cache->c_items, ce_list) {
457 if (nh->nh_id == ((unsigned)nhid)) {
458 nl_object_get((struct nl_object *)nh);
459 return nh;
460 }
461 }
462
463 return NULL;
464}
465
466/**
467 * Allocate nexthop cache and fill in all configured nexthops.
468 * @arg sk Netnexthop socket.
469 * @arg family nexthop address family or AF_UNSPEC
470 * @arg result Pointer to store resulting cache.
471 * @arg flags Flags to set in nexthop cache before filling
472 *
473 * Allocates and initializes a new nexthop cache. If \c sk is valid, a netnexthop
474 * message is sent to the kernel requesting a full dump of all configured
475 * nexthops. The returned messages are parsed and filled into the cache. If
476 * the operation succeeds, the resulting cache will contain a nexthop object for
477 * each nexthop configured in the kernel. If \c sk is NULL, returns 0 but the
478 * cache is still empty.
479 *
480 * If \c family is set to an address family other than \c AF_UNSPEC the
481 * contents of the cache can be limited to a specific address family.
482 * Currently the following address families are supported:
483 * - AF_BRIDGE
484 * - AF_INET6
485 *
486 * @route_doc{nexthop_list, Get List of nexthops}
487 * @see rtnl_nh_get()
488 * @see rtnl_nh_get_by_name()
489 * @return 0 on success or a negative error code.
490 */
491static int rtnl_nh_alloc_cache_flags(struct nl_sock *sk, int family,
492 struct nl_cache **result,
493 unsigned int flags)
494{
495 struct nl_cache *cache;
496 int err;
497
498 cache = nl_cache_alloc(&rtnl_nh_ops);
499 if (!cache)
500 return -NLE_NOMEM;
501
502 cache->c_iarg1 = family;
503
504 if (flags)
505 nl_cache_set_flags(cache, flags);
506
507 if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
508 nl_cache_free(cache);
509 return err;
510 }
511
512 *result = cache;
513 return 0;
514}
515
516/**
517 * Allocate nexthop cache and fill in all configured nexthops.
518 * @arg sk Netnexthop socket.
519 * @arg family nexthop address family or AF_UNSPEC
520 * @arg result Pointer to store resulting cache.
521 *
522 * Allocates and initializes a new nexthop cache. If \c sk is valid, a netnexthop
523 * message is sent to the kernel requesting a full dump of all configured
524 * nexthops. The returned messages are parsed and filled into the cache. If
525 * the operation succeeds, the resulting cache will contain a nexthop object for
526 * each nexthop configured in the kernel. If \c sk is NULL, returns 0 but the
527 * cache is still empty.
528 *
529 * If \c family is set to an address family other than \c AF_UNSPEC the
530 * contents of the cache can be limited to a specific address family.
531 * Currently the following address families are supported:
532 * - AF_BRIDGE
533 * - AF_INET6
534 *
535 * @route_doc{nexthop_list, Get List of nexthops}
536 * @see rtnl_nh_get()
537 * @see rtnl_nh_get_by_name()
538 * @return 0 on success or a negative error code.
539 */
540int rtnl_nh_alloc_cache(struct nl_sock *sk, int family,
541 struct nl_cache **result)
542{
543 return rtnl_nh_alloc_cache_flags(sk, family, result, 0);
544}
545
546static struct nl_object_ops nh_obj_ops = {
547 .oo_name = "route/nh",
548 .oo_size = sizeof(struct rtnl_nh),
549 .oo_free_data = nh_free,
550 .oo_clone = nh_clone,
551 .oo_dump = {
552 [NL_DUMP_LINE] = nh_dump_line,
553 [NL_DUMP_DETAILS] = nh_dump_details,
554 },
555 .oo_compare = nh_compare,
556 .oo_keygen = nexthop_keygen,
557 .oo_attrs2str = rtnl_route_nh_flags2str,
558 .oo_id_attrs = NH_ATTR_ID,
559};
560
561static struct nl_af_group nh_groups[] = {
562 { AF_UNSPEC, RTNLGRP_NEXTHOP },
563 { END_OF_GROUP_LIST },
564};
565
566static struct nl_cache_ops rtnl_nh_ops = {
567 .co_name = "route/nh",
568 .co_hdrsize = sizeof(struct nhmsg),
569 .co_msgtypes = {
570 { RTM_NEWNEXTHOP, NL_ACT_NEW, "new" },
571 { RTM_DELNEXTHOP, NL_ACT_DEL, "del" },
572 { RTM_GETNEXTHOP, NL_ACT_GET, "get" },
573 END_OF_MSGTYPES_LIST,
574 },
575 .co_protocol = NETLINK_ROUTE,
576 .co_groups = nh_groups,
577 .co_request_update = nexthop_request_update,
578 .co_msg_parser = nexthop_msg_parser,
579 .co_obj_ops = &nh_obj_ops,
580};
581
582static void _nl_init nexthop_init(void)
583{
584 nl_cache_mngt_register(&rtnl_nh_ops);
585}
586
587static void _nl_exit nexthop_exit(void)
588{
589 nl_cache_mngt_unregister(&rtnl_nh_ops);
590}
struct nl_addr * nl_addr_alloc_attr(const struct nlattr *nla, int family)
Allocate abstract address based on Netlink attribute.
Definition addr.c:261
int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b)
Compare abstract addresses.
Definition addr.c:587
struct nl_addr * nl_addr_clone(const struct nl_addr *addr)
Clone existing abstract address object.
Definition addr.c:495
char * nl_addr2str(const struct nl_addr *addr, char *buf, size_t size)
Convert abstract address object to character string.
Definition addr.c:1001
void nl_addr_put(struct nl_addr *addr)
Decrease the reference counter of an abstract address.
Definition addr.c:541
uint32_t nla_get_u32(const struct nlattr *nla)
Return payload of 32 bit integer attribute.
Definition attr.c:712
void * nla_data(const struct nlattr *nla)
Return pointer to the payload section.
Definition attr.c:119
@ NLA_UNSPEC
Unspecified type, binary data chunk.
Definition attr.h:34
@ NLA_U16
16 bit integer
Definition attr.h:36
@ NLA_NESTED
Nested attributes.
Definition attr.h:42
@ NLA_U32
32 bit integer
Definition attr.h:37
int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
Unregister a set of cache operations.
Definition cache_mngt.c:287
int nl_cache_mngt_register(struct nl_cache_ops *ops)
Register a set of cache operations.
Definition cache_mngt.c:252
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
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:1041
void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags)
Set cache flags.
Definition cache.c:614
void nl_cache_free(struct nl_cache *cache)
Free a cache.
Definition cache.c:409
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate new cache.
Definition cache.c:184
struct nl_msg * nlmsg_alloc_simple(int nlmsgtype, int flags)
Allocate a new netlink message.
Definition msg.c:352
void * nlmsg_data(const struct nlmsghdr *nlh)
Return pointer to message payload.
Definition msg.c:108
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
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
void nl_object_put(struct nl_object *obj)
Release a reference from an object.
Definition object.c:221
void nl_object_get(struct nl_object *obj)
Acquire a reference on a object.
Definition object.c:210
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
int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg)
Finalize and transmit Netlink message.
Definition nl.c:515
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition utils.c:1015
@ NL_DUMP_LINE
Dump object briefly on one line.
Definition types.h:20
@ NL_DUMP_DETAILS
Dump all attributes but no statistics.
Definition types.h:21
Dumping parameters.
Definition types.h:32
uint32_t nh_id
Definition nh.h:19
uint8_t weight
Definition nh.h:20
Attribute validation policy.
Definition attr.h:63
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition attr.h:65