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