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