xref: /aosp_15_r20/external/libnl/lib/route/nexthop.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2003-2008 Thomas Graf <[email protected]>
4  */
5 
6 /**
7  * @ingroup route_obj
8  * @defgroup nexthop Nexthop
9  * @{
10  */
11 
12 #include "nl-default.h"
13 
14 #include <netlink/netlink.h>
15 #include <netlink/utils.h>
16 #include <netlink/route/rtnl.h>
17 #include <netlink/route/route.h>
18 
19 #include "nexthop-encap.h"
20 #include "nl-route.h"
21 #include "nl-priv-dynamic-core/nl-core.h"
22 
23 /** @cond SKIP */
24 #define NH_ATTR_FLAGS   0x000001
25 #define NH_ATTR_WEIGHT  0x000002
26 #define NH_ATTR_IFINDEX 0x000004
27 #define NH_ATTR_GATEWAY 0x000008
28 #define NH_ATTR_REALMS  0x000010
29 #define NH_ATTR_NEWDST  0x000020
30 #define NH_ATTR_VIA     0x000040
31 #define NH_ATTR_ENCAP   0x000080
32 /** @endcond */
33 
34 /**
35  * @name Allocation/Freeing
36  * @{
37  */
38 
rtnl_route_nh_alloc(void)39 struct rtnl_nexthop *rtnl_route_nh_alloc(void)
40 {
41 	struct rtnl_nexthop *nh;
42 
43 	nh = calloc(1, sizeof(*nh));
44 	if (!nh)
45 		return NULL;
46 
47 	nl_init_list_head(&nh->rtnh_list);
48 
49 	return nh;
50 }
51 
rtnl_route_nh_clone(struct rtnl_nexthop * src)52 struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src)
53 {
54 	struct rtnl_nexthop *nh;
55 
56 	nh = rtnl_route_nh_alloc();
57 	if (!nh)
58 		return NULL;
59 
60 	nh->rtnh_flags = src->rtnh_flags;
61 	nh->rtnh_flag_mask = src->rtnh_flag_mask;
62 	nh->rtnh_weight = src->rtnh_weight;
63 	nh->rtnh_ifindex = src->rtnh_ifindex;
64 	nh->ce_mask = src->ce_mask;
65 
66 	if (src->rtnh_gateway) {
67 		nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway);
68 		if (!nh->rtnh_gateway) {
69 			free(nh);
70 			return NULL;
71 		}
72 	}
73 
74 	if (src->rtnh_newdst) {
75 		nh->rtnh_newdst = nl_addr_clone(src->rtnh_newdst);
76 		if (!nh->rtnh_newdst) {
77 			nl_addr_put(nh->rtnh_gateway);
78 			free(nh);
79 			return NULL;
80 		}
81 	}
82 
83 	if (src->rtnh_via) {
84 		nh->rtnh_via = nl_addr_clone(src->rtnh_via);
85 		if (!nh->rtnh_via) {
86 			nl_addr_put(nh->rtnh_gateway);
87 			nl_addr_put(nh->rtnh_newdst);
88 			free(nh);
89 			return NULL;
90 		}
91 	}
92 
93 	return nh;
94 }
95 
rtnl_route_nh_free(struct rtnl_nexthop * nh)96 void rtnl_route_nh_free(struct rtnl_nexthop *nh)
97 {
98 	nl_addr_put(nh->rtnh_gateway);
99 	nl_addr_put(nh->rtnh_newdst);
100 	nl_addr_put(nh->rtnh_via);
101 	if (nh->rtnh_encap) {
102 		if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor)
103 			nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv);
104 		free(nh->rtnh_encap->priv);
105 		free(nh->rtnh_encap);
106 	}
107 	free(nh);
108 }
109 
110 /** @} */
111 
rtnl_route_nh_compare(struct rtnl_nexthop * a,struct rtnl_nexthop * b,uint32_t attrs,int loose)112 int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b,
113 			  uint32_t attrs, int loose)
114 {
115 	uint32_t diff = 0;
116 
117 #define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR)
118 	diff |= _DIFF(NH_ATTR_IFINDEX, a->rtnh_ifindex != b->rtnh_ifindex);
119 	diff |= _DIFF(NH_ATTR_WEIGHT, a->rtnh_weight != b->rtnh_weight);
120 	diff |= _DIFF(NH_ATTR_REALMS, a->rtnh_realms != b->rtnh_realms);
121 	diff |= _DIFF(NH_ATTR_GATEWAY,
122 		      nl_addr_cmp(a->rtnh_gateway, b->rtnh_gateway));
123 	diff |= _DIFF(NH_ATTR_NEWDST,
124 		      nl_addr_cmp(a->rtnh_newdst, b->rtnh_newdst));
125 	diff |= _DIFF(NH_ATTR_VIA, nl_addr_cmp(a->rtnh_via, b->rtnh_via));
126 	diff |= _DIFF(NH_ATTR_ENCAP,
127 		      nh_encap_compare(a->rtnh_encap, b->rtnh_encap));
128 
129 	if (loose)
130 		diff |= _DIFF(NH_ATTR_FLAGS, (a->rtnh_flags ^ b->rtnh_flags) &
131 						     b->rtnh_flag_mask);
132 	else
133 		diff |= _DIFF(NH_ATTR_FLAGS, a->rtnh_flags != b->rtnh_flags);
134 #undef _DIFF
135 
136 	return diff;
137 }
138 
139 /**
140  * Check if the fixed attributes of two nexthops are identical, and may
141  * only differ in flags or weight.
142  *
143  * @arg a		a nexthop
144  * @arg b		another nexthop
145  *
146  * @return true if both nexthop have equal attributes, otherwise false.
147  */
rtnl_route_nh_identical(struct rtnl_nexthop * a,struct rtnl_nexthop * b)148 int rtnl_route_nh_identical(struct rtnl_nexthop *a, struct rtnl_nexthop *b)
149 {
150 	return !rtnl_route_nh_compare(a, b,
151 				      NH_ATTR_IFINDEX | NH_ATTR_REALMS |
152 				      NH_ATTR_GATEWAY | NH_ATTR_NEWDST |
153 				      NH_ATTR_VIA | NH_ATTR_ENCAP, 0);
154 }
155 
nh_dump_line(struct rtnl_nexthop * nh,struct nl_dump_params * dp)156 static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
157 {
158 	struct nl_cache *link_cache;
159 	char buf[128];
160 
161 	link_cache = nl_cache_mngt_require_safe("route/link");
162 
163 	if (nh->ce_mask & NH_ATTR_ENCAP)
164 		nh_encap_dump(nh->rtnh_encap, dp);
165 
166 	if (nh->ce_mask & NH_ATTR_NEWDST)
167 		nl_dump(dp, "as to %s ",
168 			nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf)));
169 
170 	nl_dump(dp, "via");
171 
172 	if (nh->ce_mask & NH_ATTR_VIA)
173 		nl_dump(dp, " %s",
174 			nl_addr2str(nh->rtnh_via, buf, sizeof(buf)));
175 
176 	if (nh->ce_mask & NH_ATTR_GATEWAY)
177 		nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway,
178 						   buf, sizeof(buf)));
179 
180 	if(nh->ce_mask & NH_ATTR_IFINDEX) {
181 		if (link_cache) {
182 			nl_dump(dp, " dev %s",
183 				rtnl_link_i2name(link_cache,
184 						 nh->rtnh_ifindex,
185 						 buf, sizeof(buf)));
186 		} else
187 			nl_dump(dp, " dev %d", nh->rtnh_ifindex);
188 	}
189 
190 	nl_dump(dp, " ");
191 
192 	if (link_cache)
193 		nl_cache_put(link_cache);
194 }
195 
nh_dump_details(struct rtnl_nexthop * nh,struct nl_dump_params * dp)196 static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
197 {
198 	struct nl_cache *link_cache;
199 	char buf[128];
200 
201 	link_cache = nl_cache_mngt_require_safe("route/link");
202 
203 	nl_dump(dp, "nexthop");
204 
205 	if (nh->ce_mask & NH_ATTR_ENCAP)
206 		nh_encap_dump(nh->rtnh_encap, dp);
207 
208 	if (nh->ce_mask & NH_ATTR_NEWDST)
209 		nl_dump(dp, " as to %s",
210 			nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf)));
211 
212 	if (nh->ce_mask & NH_ATTR_VIA)
213 		nl_dump(dp, " via %s",
214 			nl_addr2str(nh->rtnh_via, buf, sizeof(buf)));
215 
216 	if (nh->ce_mask & NH_ATTR_GATEWAY)
217 		nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway,
218 						   buf, sizeof(buf)));
219 
220 	if(nh->ce_mask & NH_ATTR_IFINDEX) {
221 		if (link_cache) {
222 			nl_dump(dp, " dev %s",
223 				rtnl_link_i2name(link_cache,
224 						 nh->rtnh_ifindex,
225 						 buf, sizeof(buf)));
226 		} else
227 			nl_dump(dp, " dev %d", nh->rtnh_ifindex);
228 	}
229 
230 	if (nh->ce_mask & NH_ATTR_WEIGHT)
231 		nl_dump(dp, " weight %u", nh->rtnh_weight);
232 
233 	if (nh->ce_mask & NH_ATTR_REALMS)
234 		nl_dump(dp, " realm %04x:%04x",
235 			RTNL_REALM_FROM(nh->rtnh_realms),
236 			RTNL_REALM_TO(nh->rtnh_realms));
237 
238 	if (nh->ce_mask & NH_ATTR_FLAGS)
239 		nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags,
240 							buf, sizeof(buf)));
241 
242 	if (link_cache)
243 		nl_cache_put(link_cache);
244 }
245 
rtnl_route_nh_dump(struct rtnl_nexthop * nh,struct nl_dump_params * dp)246 void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
247 {
248 	switch (dp->dp_type) {
249 	case NL_DUMP_LINE:
250 		nh_dump_line(nh, dp);
251 		break;
252 
253 	case NL_DUMP_DETAILS:
254 	case NL_DUMP_STATS:
255 		if (dp->dp_ivar == NH_DUMP_FROM_DETAILS)
256 			nh_dump_details(nh, dp);
257 		break;
258 
259 	default:
260 		break;
261 	}
262 }
263 
nh_set_encap(struct rtnl_nexthop * nh,struct rtnl_nh_encap * rtnh_encap)264 void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap)
265 {
266 	if (nh->rtnh_encap) {
267 		if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor)
268 			nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv);
269 		free(nh->rtnh_encap->priv);
270 		free(nh->rtnh_encap);
271 	}
272 
273 	if (rtnh_encap) {
274 		nh->rtnh_encap = rtnh_encap;
275 		nh->ce_mask |= NH_ATTR_ENCAP;
276 	} else {
277 		nh->rtnh_encap = NULL;
278 		nh->ce_mask &= ~NH_ATTR_ENCAP;
279 	}
280 }
281 
282 /**
283  * @name Attributes
284  * @{
285  */
286 
rtnl_route_nh_set_weight(struct rtnl_nexthop * nh,uint8_t weight)287 void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, uint8_t weight)
288 {
289 	nh->rtnh_weight = weight;
290 	nh->ce_mask |= NH_ATTR_WEIGHT;
291 }
292 
rtnl_route_nh_get_weight(struct rtnl_nexthop * nh)293 uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *nh)
294 {
295 	return nh->rtnh_weight;
296 }
297 
rtnl_route_nh_set_ifindex(struct rtnl_nexthop * nh,int ifindex)298 void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex)
299 {
300 	nh->rtnh_ifindex = ifindex;
301 	nh->ce_mask |= NH_ATTR_IFINDEX;
302 }
303 
rtnl_route_nh_get_ifindex(struct rtnl_nexthop * nh)304 int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh)
305 {
306 	return nh->rtnh_ifindex;
307 }
308 
309 /* FIXME: Convert to return an int */
rtnl_route_nh_set_gateway(struct rtnl_nexthop * nh,struct nl_addr * addr)310 void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr)
311 {
312 	struct nl_addr *old = nh->rtnh_gateway;
313 
314 	if (addr) {
315 		nh->rtnh_gateway = nl_addr_get(addr);
316 		nh->ce_mask |= NH_ATTR_GATEWAY;
317 	} else {
318 		nh->ce_mask &= ~NH_ATTR_GATEWAY;
319 		nh->rtnh_gateway = NULL;
320 	}
321 
322 	if (old)
323 		nl_addr_put(old);
324 }
325 
rtnl_route_nh_get_gateway(struct rtnl_nexthop * nh)326 struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh)
327 {
328 	return nh->rtnh_gateway;
329 }
330 
rtnl_route_nh_set_flags(struct rtnl_nexthop * nh,unsigned int flags)331 void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags)
332 {
333 	nh->rtnh_flag_mask |= flags;
334 	nh->rtnh_flags |= flags;
335 	nh->ce_mask |= NH_ATTR_FLAGS;
336 }
337 
rtnl_route_nh_unset_flags(struct rtnl_nexthop * nh,unsigned int flags)338 void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags)
339 {
340 	nh->rtnh_flag_mask |= flags;
341 	nh->rtnh_flags &= ~flags;
342 	nh->ce_mask |= NH_ATTR_FLAGS;
343 }
344 
rtnl_route_nh_get_flags(struct rtnl_nexthop * nh)345 unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh)
346 {
347 	return nh->rtnh_flags;
348 }
349 
rtnl_route_nh_set_realms(struct rtnl_nexthop * nh,uint32_t realms)350 void rtnl_route_nh_set_realms(struct rtnl_nexthop *nh, uint32_t realms)
351 {
352 	nh->rtnh_realms = realms;
353 	nh->ce_mask |= NH_ATTR_REALMS;
354 }
355 
rtnl_route_nh_get_realms(struct rtnl_nexthop * nh)356 uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh)
357 {
358 	return nh->rtnh_realms;
359 }
360 
rtnl_route_nh_set_newdst(struct rtnl_nexthop * nh,struct nl_addr * addr)361 int rtnl_route_nh_set_newdst(struct rtnl_nexthop *nh, struct nl_addr *addr)
362 {
363 	struct nl_addr *old = nh->rtnh_newdst;
364 
365 	if (addr) {
366 		nh->rtnh_newdst = nl_addr_get(addr);
367 		nh->ce_mask |= NH_ATTR_NEWDST;
368 	} else {
369 		nh->ce_mask &= ~NH_ATTR_NEWDST;
370 		nh->rtnh_newdst = NULL;
371 	}
372 
373 	if (old)
374 		nl_addr_put(old);
375 
376 	return 0;
377 }
378 
rtnl_route_nh_get_newdst(struct rtnl_nexthop * nh)379 struct nl_addr *rtnl_route_nh_get_newdst(struct rtnl_nexthop *nh)
380 {
381 	return nh->rtnh_newdst;
382 }
383 
rtnl_route_nh_set_via(struct rtnl_nexthop * nh,struct nl_addr * addr)384 int rtnl_route_nh_set_via(struct rtnl_nexthop *nh, struct nl_addr *addr)
385 {
386 	struct nl_addr *old = nh->rtnh_via;
387 
388 	if (addr) {
389 		nh->rtnh_via = nl_addr_get(addr);
390 		nh->ce_mask |= NH_ATTR_VIA;
391 	} else {
392 		nh->ce_mask &= ~NH_ATTR_VIA;
393 		nh->rtnh_via= NULL;
394 	}
395 
396 	if (old)
397 		nl_addr_put(old);
398 
399 	return 0;
400 }
401 
rtnl_route_nh_get_via(struct rtnl_nexthop * nh)402 struct nl_addr *rtnl_route_nh_get_via(struct rtnl_nexthop *nh)
403 {
404 	return nh->rtnh_via;
405 }
406 
407 /** @} */
408 
409 /**
410  * @name Nexthop Flags Translations
411  * @{
412  */
413 
414 static const struct trans_tbl nh_flags[] = {
415 	__ADD(RTNH_F_DEAD, dead),
416 	__ADD(RTNH_F_PERVASIVE, pervasive),
417 	__ADD(RTNH_F_ONLINK, onlink),
418 };
419 
rtnl_route_nh_flags2str(int flags,char * buf,size_t len)420 char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len)
421 {
422 	return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags));
423 }
424 
rtnl_route_nh_str2flags(const char * name)425 int rtnl_route_nh_str2flags(const char *name)
426 {
427 	return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags));
428 }
429 
430 /** @} */
431 
432 /** @} */
433