xref: /aosp_15_r20/external/libnl/lib/route/nh.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2022 Stanislav Zaikin <[email protected]>
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 */
20 struct 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 
43 struct 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 
52 static struct nl_cache_ops rtnl_nh_ops;
53 static struct nl_object_ops nh_obj_ops;
54 
rtnl_nh_grp_alloc(unsigned size)55 static 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 
rtnl_nh_grp_put(nl_nh_group_t * nhg)76 static 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 
rtnh_nh_grp_cmp(const nl_nh_group_t * a,const nl_nh_group_t * b)91 static 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 
rtnh_nh_grp_clone(nl_nh_group_t * src,nl_nh_group_t ** dst)104 static 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 
rtnl_nh_alloc(void)124 struct rtnl_nh *rtnl_nh_alloc(void)
125 {
126 	return (struct rtnl_nh *)nl_object_alloc(&nh_obj_ops);
127 }
128 
nh_clone(struct nl_object * _src,struct nl_object * _dst)129 static 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 
nh_free(struct nl_object * obj)156 static 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 
rtnl_nh_put(struct rtnl_nh * nh)165 void 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 
nexthop_keygen(struct nl_object * obj,uint32_t * hashkey,uint32_t table_sz)172 static 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 
rtnl_nh_set_gateway(struct rtnl_nh * nexthop,struct nl_addr * addr)189 int 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 
rtnl_nh_get_gateway(struct rtnl_nh * nexthop)201 struct nl_addr *rtnl_nh_get_gateway(struct rtnl_nh *nexthop)
202 {
203 	return nexthop->nh_gateway;
204 }
205 
rtnl_nh_set_fdb(struct rtnl_nh * nexthop,int value)206 int 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 
rtnl_nh_get_oif(struct rtnl_nh * nexthop)216 int 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 
rtnl_nh_get_fdb(struct rtnl_nh * nexthop)224 int rtnl_nh_get_fdb(struct rtnl_nh *nexthop)
225 {
226 	return nexthop->ce_mask & NH_ATTR_FLAG_FDB;
227 }
228 
rtnl_nh_get_group_entry(struct rtnl_nh * nexthop,int n)229 int 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 
rtnl_nh_get_group_size(struct rtnl_nh * nexthop)240 int 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 
rtnl_nh_grp_info(unsigned size,const struct nexthop_grp * vi,nl_nh_group_t ** nvi)250 static 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 
rtnl_nh_get_id(struct rtnl_nh * nh)268 int 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 
nexthop_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * n,struct nl_parser_param * pp)276 static 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 
nexthop_request_update(struct nl_cache * cache,struct nl_sock * sk)355 static 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 
dump_nh_group(nl_nh_group_t * group,struct nl_dump_params * dp)376 static 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 
nh_dump_line(struct nl_object * obj,struct nl_dump_params * dp)386 static 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 
nh_dump_details(struct nl_object * nh,struct nl_dump_params * dp)422 static void nh_dump_details(struct nl_object *nh, struct nl_dump_params *dp)
423 {
424 	nh_dump_line(nh, dp);
425 }
426 
nh_compare(struct nl_object * a,struct nl_object * b,uint64_t attrs,int loose)427 static 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 
rtnl_nh_get(struct nl_cache * cache,int nhid)449 struct 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  */
rtnl_nh_alloc_cache_flags(struct nl_sock * sk,int family,struct nl_cache ** result,unsigned int flags)491 static 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  */
rtnl_nh_alloc_cache(struct nl_sock * sk,int family,struct nl_cache ** result)540 int 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 
546 static 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 
561 static struct nl_af_group nh_groups[] = {
562 	{ AF_UNSPEC, RTNLGRP_NEXTHOP },
563 	{ END_OF_GROUP_LIST },
564 };
565 
566 static 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 
nexthop_init(void)582 static void _nl_init nexthop_init(void)
583 {
584 	nl_cache_mngt_register(&rtnl_nh_ops);
585 }
586 
nexthop_exit(void)587 static void _nl_exit nexthop_exit(void)
588 {
589 	nl_cache_mngt_unregister(&rtnl_nh_ops);
590 }
591