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