]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later |
c16c7808 SS |
2 | * Copyright © 2019 VMware, Inc. |
3 | */ | |
4 | ||
8031e5ca | 5 | #include <net/if.h> |
c16c7808 SS |
6 | #include <linux/nexthop.h> |
7 | ||
8 | #include "alloc-util.h" | |
c16c7808 | 9 | #include "netlink-util.h" |
75156ccb | 10 | #include "networkd-link.h" |
c16c7808 | 11 | #include "networkd-manager.h" |
75156ccb | 12 | #include "networkd-network.h" |
c16c7808 | 13 | #include "networkd-nexthop.h" |
76c5a0f2 | 14 | #include "networkd-queue.h" |
344b3cff | 15 | #include "networkd-route-util.h" |
c16c7808 SS |
16 | #include "parse-util.h" |
17 | #include "set.h" | |
7dc7ab31 | 18 | #include "stdio-util.h" |
c16c7808 | 19 | #include "string-util.h" |
c16c7808 | 20 | |
75156ccb | 21 | NextHop *nexthop_free(NextHop *nexthop) { |
4736035a | 22 | if (!nexthop) |
75156ccb | 23 | return NULL; |
4736035a YW |
24 | |
25 | if (nexthop->network) { | |
b82663dd YW |
26 | assert(nexthop->section); |
27 | hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section); | |
4736035a YW |
28 | } |
29 | ||
307fe3cd | 30 | config_section_free(nexthop->section); |
4736035a | 31 | |
30f10837 | 32 | if (nexthop->manager) { |
352eba2e YW |
33 | assert(nexthop->id > 0); |
34 | hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); | |
30f10837 YW |
35 | } |
36 | ||
228c3e21 YW |
37 | hashmap_free_free(nexthop->group); |
38 | ||
75156ccb | 39 | return mfree(nexthop); |
4736035a YW |
40 | } |
41 | ||
307fe3cd | 42 | DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_free); |
4736035a | 43 | |
352eba2e YW |
44 | DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( |
45 | nexthop_hash_ops, | |
46 | void, | |
47 | trivial_hash_func, | |
48 | trivial_compare_func, | |
49 | NextHop, | |
50 | nexthop_free); | |
51 | ||
4736035a | 52 | static int nexthop_new(NextHop **ret) { |
c16c7808 SS |
53 | _cleanup_(nexthop_freep) NextHop *nexthop = NULL; |
54 | ||
55 | nexthop = new(NextHop, 1); | |
56 | if (!nexthop) | |
57 | return -ENOMEM; | |
58 | ||
59 | *nexthop = (NextHop) { | |
60 | .family = AF_UNSPEC, | |
2ddd52d1 | 61 | .onlink = -1, |
c16c7808 SS |
62 | }; |
63 | ||
64 | *ret = TAKE_PTR(nexthop); | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
69 | static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) { | |
307fe3cd | 70 | _cleanup_(config_section_freep) ConfigSection *n = NULL; |
c16c7808 SS |
71 | _cleanup_(nexthop_freep) NextHop *nexthop = NULL; |
72 | int r; | |
73 | ||
74 | assert(network); | |
75 | assert(ret); | |
b82663dd YW |
76 | assert(filename); |
77 | assert(section_line > 0); | |
c16c7808 | 78 | |
307fe3cd | 79 | r = config_section_new(filename, section_line, &n); |
b82663dd YW |
80 | if (r < 0) |
81 | return r; | |
c16c7808 | 82 | |
b82663dd YW |
83 | nexthop = hashmap_get(network->nexthops_by_section, n); |
84 | if (nexthop) { | |
85 | *ret = TAKE_PTR(nexthop); | |
86 | return 0; | |
c16c7808 SS |
87 | } |
88 | ||
89 | r = nexthop_new(&nexthop); | |
90 | if (r < 0) | |
91 | return r; | |
92 | ||
93 | nexthop->protocol = RTPROT_STATIC; | |
94 | nexthop->network = network; | |
b82663dd | 95 | nexthop->section = TAKE_PTR(n); |
89fa0133 | 96 | nexthop->source = NETWORK_CONFIG_SOURCE_STATIC; |
c16c7808 | 97 | |
307fe3cd | 98 | r = hashmap_ensure_put(&network->nexthops_by_section, &config_section_hash_ops, nexthop->section, nexthop); |
b82663dd YW |
99 | if (r < 0) |
100 | return r; | |
c16c7808 SS |
101 | |
102 | *ret = TAKE_PTR(nexthop); | |
c16c7808 SS |
103 | return 0; |
104 | } | |
105 | ||
09d09207 | 106 | static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) { |
c16c7808 | 107 | assert(nexthop); |
352eba2e | 108 | assert(state); |
c16c7808 SS |
109 | |
110 | siphash24_compress(&nexthop->id, sizeof(nexthop->id), state); | |
352eba2e | 111 | } |
c16c7808 | 112 | |
352eba2e YW |
113 | static int nexthop_compare_func(const NextHop *a, const NextHop *b) { |
114 | assert(a); | |
115 | assert(b); | |
c16c7808 | 116 | |
352eba2e | 117 | return CMP(a->id, b->id); |
c16c7808 SS |
118 | } |
119 | ||
352eba2e | 120 | static int nexthop_compare_full(const NextHop *a, const NextHop *b) { |
c16c7808 SS |
121 | int r; |
122 | ||
352eba2e YW |
123 | assert(a); |
124 | assert(b); | |
125 | ||
126 | /* This compares detailed configs, except for ID and ifindex. */ | |
127 | ||
0e9d129c YW |
128 | r = CMP(a->protocol, b->protocol); |
129 | if (r != 0) | |
130 | return r; | |
131 | ||
352eba2e YW |
132 | r = CMP(a->flags, b->flags); |
133 | if (r != 0) | |
134 | return r; | |
135 | ||
136 | r = CMP(hashmap_size(a->group), hashmap_size(b->group)); | |
c16c7808 SS |
137 | if (r != 0) |
138 | return r; | |
139 | ||
352eba2e YW |
140 | if (!hashmap_isempty(a->group)) { |
141 | struct nexthop_grp *ga; | |
142 | ||
143 | HASHMAP_FOREACH(ga, a->group) { | |
144 | struct nexthop_grp *gb; | |
145 | ||
146 | gb = hashmap_get(b->group, UINT32_TO_PTR(ga->id)); | |
147 | if (!gb) | |
148 | return CMP(ga, gb); | |
149 | ||
150 | r = CMP(ga->weight, gb->weight); | |
151 | if (r != 0) | |
152 | return r; | |
153 | } | |
154 | } | |
155 | ||
30f10837 YW |
156 | r = CMP(a->blackhole, b->blackhole); |
157 | if (r != 0) | |
158 | return r; | |
159 | ||
c16c7808 SS |
160 | r = CMP(a->family, b->family); |
161 | if (r != 0) | |
162 | return r; | |
163 | ||
352eba2e YW |
164 | if (IN_SET(a->family, AF_INET, AF_INET6)) { |
165 | r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family)); | |
166 | if (r != 0) | |
167 | return r; | |
168 | } | |
c16c7808 | 169 | |
cf5a228f | 170 | return 0; |
c16c7808 SS |
171 | } |
172 | ||
228c3e21 YW |
173 | static int nexthop_dup(const NextHop *src, NextHop **ret) { |
174 | _cleanup_(nexthop_freep) NextHop *dest = NULL; | |
175 | struct nexthop_grp *nhg; | |
176 | int r; | |
177 | ||
30f10837 | 178 | assert(src); |
228c3e21 YW |
179 | assert(ret); |
180 | ||
181 | dest = newdup(NextHop, src, 1); | |
182 | if (!dest) | |
183 | return -ENOMEM; | |
184 | ||
185 | /* unset all pointers */ | |
186 | dest->manager = NULL; | |
228c3e21 YW |
187 | dest->network = NULL; |
188 | dest->section = NULL; | |
189 | dest->group = NULL; | |
30f10837 | 190 | |
228c3e21 YW |
191 | HASHMAP_FOREACH(nhg, src->group) { |
192 | _cleanup_free_ struct nexthop_grp *g = NULL; | |
30f10837 | 193 | |
228c3e21 YW |
194 | g = newdup(struct nexthop_grp, nhg, 1); |
195 | if (!g) | |
196 | return -ENOMEM; | |
197 | ||
198 | r = hashmap_ensure_put(&dest->group, NULL, UINT32_TO_PTR(g->id), g); | |
199 | if (r < 0) | |
200 | return r; | |
201 | if (r > 0) | |
202 | TAKE_PTR(g); | |
203 | } | |
204 | ||
205 | *ret = TAKE_PTR(dest); | |
206 | return 0; | |
30f10837 YW |
207 | } |
208 | ||
84faee41 YW |
209 | static bool nexthop_bound_to_link(const NextHop *nexthop) { |
210 | assert(nexthop); | |
211 | return !nexthop->blackhole && hashmap_isempty(nexthop->group); | |
212 | } | |
213 | ||
ac217903 | 214 | int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret) { |
324e3422 YW |
215 | NextHop *nh; |
216 | ||
217 | assert(manager); | |
218 | ||
219 | if (id == 0) | |
220 | return -EINVAL; | |
221 | ||
222 | nh = hashmap_get(manager->nexthops_by_id, UINT32_TO_PTR(id)); | |
223 | if (!nh) | |
224 | return -ENOENT; | |
225 | ||
226 | if (ret) | |
227 | *ret = nh; | |
228 | return 0; | |
229 | } | |
230 | ||
352eba2e | 231 | static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) { |
89fa0133 | 232 | NextHop *nexthop; |
352eba2e | 233 | int ifindex; |
c16c7808 | 234 | |
352eba2e YW |
235 | assert(link); |
236 | assert(link->manager); | |
c16c7808 SS |
237 | assert(in); |
238 | ||
89fa0133 | 239 | if (in->id > 0) |
352eba2e | 240 | return nexthop_get_by_id(link->manager, in->id, ret); |
c16c7808 | 241 | |
352eba2e | 242 | ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0; |
c16c7808 | 243 | |
352eba2e YW |
244 | HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { |
245 | if (nexthop->ifindex != ifindex) | |
246 | continue; | |
247 | if (nexthop_compare_full(nexthop, in) != 0) | |
89fa0133 | 248 | continue; |
c16c7808 | 249 | |
89fa0133 YW |
250 | if (ret) |
251 | *ret = nexthop; | |
252 | return 0; | |
253 | } | |
c16c7808 | 254 | |
89fa0133 | 255 | return -ENOENT; |
228c3e21 YW |
256 | } |
257 | ||
cdf3bee6 YW |
258 | static int nexthop_get_request_by_id(Manager *manager, uint32_t id, Request **ret) { |
259 | Request *req; | |
260 | ||
261 | assert(manager); | |
262 | ||
263 | req = ordered_set_get( | |
264 | manager->request_queue, | |
265 | &(Request) { | |
266 | .type = REQUEST_TYPE_NEXTHOP, | |
267 | .userdata = (void*) &(const NextHop) { .id = id }, | |
268 | .hash_func = (hash_func_t) nexthop_hash_func, | |
269 | .compare_func = (compare_func_t) nexthop_compare_func, | |
270 | }); | |
271 | if (!req) | |
272 | return -ENOENT; | |
273 | ||
274 | if (ret) | |
275 | *ret = req; | |
276 | return 0; | |
277 | } | |
278 | ||
279 | static int nexthop_get_request(Link *link, const NextHop *in, Request **ret) { | |
280 | Request *req; | |
281 | int ifindex; | |
282 | ||
283 | assert(link); | |
284 | assert(link->manager); | |
285 | assert(in); | |
286 | ||
287 | if (in->id > 0) | |
288 | return nexthop_get_request_by_id(link->manager, in->id, ret); | |
289 | ||
290 | ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0; | |
291 | ||
292 | ORDERED_SET_FOREACH(req, link->manager->request_queue) { | |
293 | if (req->type != REQUEST_TYPE_NEXTHOP) | |
294 | continue; | |
295 | ||
296 | NextHop *nexthop = ASSERT_PTR(req->userdata); | |
297 | if (nexthop->ifindex != ifindex) | |
298 | continue; | |
299 | if (nexthop_compare_full(nexthop, in) != 0) | |
300 | continue; | |
301 | ||
302 | if (ret) | |
303 | *ret = req; | |
304 | return 0; | |
305 | } | |
306 | ||
307 | return -ENOENT; | |
308 | } | |
309 | ||
310 | static int nexthop_add_new(Manager *manager, uint32_t id, NextHop **ret) { | |
311 | _cleanup_(nexthop_freep) NextHop *nexthop = NULL; | |
c16c7808 SS |
312 | int r; |
313 | ||
352eba2e | 314 | assert(manager); |
cdf3bee6 YW |
315 | assert(id > 0); |
316 | ||
317 | r = nexthop_new(&nexthop); | |
318 | if (r < 0) | |
319 | return r; | |
320 | ||
321 | nexthop->id = id; | |
89fa0133 | 322 | |
352eba2e YW |
323 | r = hashmap_ensure_put(&manager->nexthops_by_id, &nexthop_hash_ops, UINT32_TO_PTR(nexthop->id), nexthop); |
324 | if (r < 0) | |
325 | return r; | |
326 | if (r == 0) | |
327 | return -EEXIST; | |
c16c7808 | 328 | |
352eba2e | 329 | nexthop->manager = manager; |
cdf3bee6 YW |
330 | |
331 | if (ret) | |
332 | *ret = nexthop; | |
333 | ||
334 | TAKE_PTR(nexthop); | |
352eba2e | 335 | return 0; |
c16c7808 SS |
336 | } |
337 | ||
89fa0133 YW |
338 | static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) { |
339 | _cleanup_set_free_ Set *ids = NULL; | |
340 | Network *network; | |
bfe60434 YW |
341 | int r; |
342 | ||
30f10837 | 343 | assert(manager); |
bfe60434 | 344 | assert(nexthop); |
bfe60434 | 345 | |
89fa0133 YW |
346 | if (nexthop->id > 0) |
347 | return 0; | |
30f10837 | 348 | |
bbc05bec YW |
349 | /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by |
350 | * nexthop_section_verify(). */ | |
351 | assert(manager->manage_foreign_nexthops); | |
352 | ||
89fa0133 | 353 | /* Find the lowest unused ID. */ |
bfe60434 | 354 | |
89fa0133 YW |
355 | ORDERED_HASHMAP_FOREACH(network, manager->networks) { |
356 | NextHop *tmp; | |
bfe60434 | 357 | |
89fa0133 YW |
358 | HASHMAP_FOREACH(tmp, network->nexthops_by_section) { |
359 | if (tmp->id == 0) | |
360 | continue; | |
bfe60434 | 361 | |
89fa0133 YW |
362 | r = set_ensure_put(&ids, NULL, UINT32_TO_PTR(tmp->id)); |
363 | if (r < 0) | |
364 | return r; | |
bfe60434 | 365 | } |
89fa0133 | 366 | } |
bfe60434 | 367 | |
c624143b | 368 | for (uint32_t id = 1; id < UINT32_MAX; id++) { |
ac217903 | 369 | if (nexthop_get_by_id(manager, id, NULL) >= 0) |
89fa0133 | 370 | continue; |
cdf3bee6 YW |
371 | if (nexthop_get_request_by_id(manager, id, NULL) >= 0) |
372 | continue; | |
89fa0133 YW |
373 | if (set_contains(ids, UINT32_TO_PTR(id))) |
374 | continue; | |
c624143b YW |
375 | |
376 | nexthop->id = id; | |
377 | return 0; | |
bfe60434 YW |
378 | } |
379 | ||
c624143b | 380 | return -EBUSY; |
bfe60434 YW |
381 | } |
382 | ||
352eba2e | 383 | static void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *manager) { |
84dbb3fd | 384 | _cleanup_free_ char *state = NULL, *group = NULL, *flags = NULL; |
228c3e21 | 385 | struct nexthop_grp *nhg; |
352eba2e | 386 | Link *link = NULL; |
7653a9dc | 387 | |
56223d92 YW |
388 | assert(nexthop); |
389 | assert(str); | |
352eba2e | 390 | assert(manager); |
56223d92 | 391 | |
7653a9dc YW |
392 | if (!DEBUG_LOGGING) |
393 | return; | |
56223d92 | 394 | |
352eba2e | 395 | (void) link_get_by_index(manager, nexthop->ifindex, &link); |
89fa0133 | 396 | (void) network_config_state_to_string_alloc(nexthop->state, &state); |
b07d8145 | 397 | (void) route_flags_to_string_alloc(nexthop->flags, &flags); |
56223d92 | 398 | |
228c3e21 | 399 | HASHMAP_FOREACH(nhg, nexthop->group) |
c0f86d66 | 400 | (void) strextendf_with_separator(&group, ",", "%"PRIu32":%"PRIu32, nhg->id, nhg->weight+1u); |
228c3e21 | 401 | |
b07d8145 | 402 | log_link_debug(link, "%s %s nexthop (%s): id: %"PRIu32", gw: %s, blackhole: %s, group: %s, flags: %s", |
89fa0133 | 403 | str, strna(network_config_source_to_string(nexthop->source)), strna(state), |
84dbb3fd ZJS |
404 | nexthop->id, |
405 | IN_ADDR_TO_STRING(nexthop->family, &nexthop->gw), | |
406 | yes_no(nexthop->blackhole), strna(group), strna(flags)); | |
56223d92 YW |
407 | } |
408 | ||
89fa0133 | 409 | static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
25b82b6e YW |
410 | int r; |
411 | ||
412 | assert(m); | |
76c5a0f2 | 413 | |
89fa0133 | 414 | /* link may be NULL. */ |
25b82b6e | 415 | |
89fa0133 | 416 | if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) |
25b82b6e YW |
417 | return 1; |
418 | ||
419 | r = sd_netlink_message_get_errno(m); | |
420 | if (r < 0 && r != -ENOENT) | |
421 | log_link_message_warning_errno(link, m, r, "Could not drop nexthop, ignoring"); | |
422 | ||
423 | return 1; | |
424 | } | |
425 | ||
89fa0133 | 426 | static int nexthop_remove(NextHop *nexthop) { |
754252f9 | 427 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
89fa0133 | 428 | Manager *manager; |
352eba2e | 429 | Link *link = NULL; |
cdf3bee6 | 430 | Request *req; |
25b82b6e YW |
431 | int r; |
432 | ||
352eba2e | 433 | manager = ASSERT_PTR(ASSERT_PTR(nexthop)->manager); |
25b82b6e YW |
434 | |
435 | /* link may be NULL. */ | |
352eba2e | 436 | (void) link_get_by_index(manager, nexthop->ifindex, &link); |
25b82b6e YW |
437 | |
438 | if (nexthop->id == 0) { | |
439 | log_link_debug(link, "Cannot remove nexthop without valid ID, ignoring."); | |
440 | return 0; | |
441 | } | |
442 | ||
352eba2e | 443 | log_nexthop_debug(nexthop, "Removing", manager); |
25b82b6e | 444 | |
754252f9 | 445 | r = sd_rtnl_message_new_nexthop(manager->rtnl, &m, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC); |
25b82b6e YW |
446 | if (r < 0) |
447 | return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m"); | |
448 | ||
754252f9 | 449 | r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id); |
25b82b6e YW |
450 | if (r < 0) |
451 | return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m"); | |
452 | ||
754252f9 | 453 | r = netlink_call_async(manager->rtnl, NULL, m, nexthop_remove_handler, |
89fa0133 | 454 | link ? link_netlink_destroy_callback : NULL, link); |
25b82b6e YW |
455 | if (r < 0) |
456 | return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); | |
457 | ||
458 | link_ref(link); /* link may be NULL, link_ref() is OK with that */ | |
459 | ||
89fa0133 | 460 | nexthop_enter_removing(nexthop); |
cdf3bee6 YW |
461 | if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0) |
462 | nexthop_enter_removing(req->userdata); | |
463 | ||
25b82b6e YW |
464 | return 0; |
465 | } | |
466 | ||
54ff39f7 | 467 | static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) { |
a79a8d16 | 468 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
7b3a7581 | 469 | int r; |
c16c7808 | 470 | |
70984148 | 471 | assert(nexthop); |
a533473a | 472 | assert(nexthop->id > 0); |
70984148 | 473 | assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6)); |
c16c7808 SS |
474 | assert(link); |
475 | assert(link->manager); | |
476 | assert(link->manager->rtnl); | |
477 | assert(link->ifindex > 0); | |
54ff39f7 | 478 | assert(req); |
c16c7808 | 479 | |
352eba2e | 480 | log_nexthop_debug(nexthop, "Configuring", link->manager); |
c16c7808 | 481 | |
a79a8d16 | 482 | r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &m, RTM_NEWNEXTHOP, nexthop->family, nexthop->protocol); |
c16c7808 | 483 | if (r < 0) |
a79a8d16 | 484 | return r; |
c16c7808 | 485 | |
a533473a YW |
486 | r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id); |
487 | if (r < 0) | |
488 | return r; | |
c16c7808 | 489 | |
228c3e21 YW |
490 | if (!hashmap_isempty(nexthop->group)) { |
491 | _cleanup_free_ struct nexthop_grp *group = NULL; | |
492 | struct nexthop_grp *p, *nhg; | |
493 | ||
494 | group = new(struct nexthop_grp, hashmap_size(nexthop->group)); | |
495 | if (!group) | |
496 | return log_oom(); | |
497 | ||
498 | p = group; | |
499 | HASHMAP_FOREACH(nhg, nexthop->group) | |
500 | *p++ = *nhg; | |
501 | ||
a79a8d16 | 502 | r = sd_netlink_message_append_data(m, NHA_GROUP, group, sizeof(struct nexthop_grp) * hashmap_size(nexthop->group)); |
228c3e21 | 503 | if (r < 0) |
a79a8d16 | 504 | return r; |
228c3e21 YW |
505 | |
506 | } else if (nexthop->blackhole) { | |
a79a8d16 | 507 | r = sd_netlink_message_append_flag(m, NHA_BLACKHOLE); |
30f10837 | 508 | if (r < 0) |
a79a8d16 | 509 | return r; |
30f10837 | 510 | } else { |
352eba2e YW |
511 | assert(nexthop->ifindex == link->ifindex); |
512 | ||
513 | r = sd_netlink_message_append_u32(m, NHA_OIF, nexthop->ifindex); | |
c16c7808 | 514 | if (r < 0) |
a79a8d16 | 515 | return r; |
2ddd52d1 | 516 | |
30f10837 | 517 | if (in_addr_is_set(nexthop->family, &nexthop->gw)) { |
a79a8d16 | 518 | r = netlink_message_append_in_addr_union(m, NHA_GATEWAY, nexthop->family, &nexthop->gw); |
2ddd52d1 | 519 | if (r < 0) |
a79a8d16 | 520 | return r; |
30f10837 | 521 | |
a79a8d16 | 522 | r = sd_rtnl_message_nexthop_set_flags(m, nexthop->flags & RTNH_F_ONLINK); |
17f8d8f9 | 523 | if (r < 0) |
a79a8d16 | 524 | return r; |
2ddd52d1 | 525 | } |
c16c7808 SS |
526 | } |
527 | ||
80d62d4f | 528 | return request_call_netlink_async(link->manager->rtnl, m, req); |
c16c7808 SS |
529 | } |
530 | ||
80d62d4f | 531 | static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, NextHop *nexthop) { |
3eacccf4 YW |
532 | int r; |
533 | ||
80d62d4f | 534 | assert(m); |
3eacccf4 | 535 | assert(link); |
3eacccf4 YW |
536 | |
537 | r = sd_netlink_message_get_errno(m); | |
538 | if (r < 0 && r != -EEXIST) { | |
539 | log_link_message_warning_errno(link, m, r, "Could not set nexthop"); | |
540 | link_enter_failed(link); | |
541 | return 1; | |
542 | } | |
543 | ||
76c5a0f2 | 544 | if (link->static_nexthop_messages == 0) { |
3eacccf4 YW |
545 | log_link_debug(link, "Nexthops set"); |
546 | link->static_nexthops_configured = true; | |
76c5a0f2 | 547 | link_check_ready(link); |
3eacccf4 YW |
548 | } |
549 | ||
550 | return 1; | |
551 | } | |
552 | ||
8bed7c55 YW |
553 | static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { |
554 | struct nexthop_grp *nhg; | |
555 | ||
556 | assert(link); | |
557 | assert(nexthop); | |
558 | ||
559 | if (!link_is_ready_to_configure(link, false)) | |
560 | return false; | |
561 | ||
84faee41 | 562 | if (nexthop_bound_to_link(nexthop)) { |
352eba2e YW |
563 | assert(nexthop->ifindex == link->ifindex); |
564 | ||
8bed7c55 YW |
565 | /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated |
566 | * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of | |
567 | * kernel. */ | |
568 | if (link->set_flags_messages > 0) | |
569 | return false; | |
570 | if (!FLAGS_SET(link->flags, IFF_UP)) | |
571 | return false; | |
572 | } | |
573 | ||
574 | /* All group members must be configured first. */ | |
575 | HASHMAP_FOREACH(nhg, nexthop->group) { | |
576 | NextHop *g; | |
577 | ||
ac217903 | 578 | if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0) |
8bed7c55 YW |
579 | return false; |
580 | ||
581 | if (!nexthop_exists(g)) | |
582 | return false; | |
583 | } | |
584 | ||
585 | if (nexthop->id == 0) { | |
586 | Request *req; | |
587 | ||
588 | ORDERED_SET_FOREACH(req, link->manager->request_queue) { | |
589 | if (req->type != REQUEST_TYPE_NEXTHOP) | |
590 | continue; | |
09d09207 | 591 | if (((NextHop*) req->userdata)->id != 0) |
8bed7c55 YW |
592 | return false; /* first configure nexthop with id. */ |
593 | } | |
594 | } | |
595 | ||
596 | return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw); | |
597 | } | |
598 | ||
09d09207 | 599 | static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) { |
cdf3bee6 | 600 | NextHop *existing; |
8bed7c55 YW |
601 | int r; |
602 | ||
603 | assert(req); | |
ff51134c | 604 | assert(link); |
cdf3bee6 | 605 | assert(link->manager); |
ff51134c | 606 | assert(nexthop); |
8bed7c55 YW |
607 | |
608 | if (!nexthop_is_ready_to_configure(link, nexthop)) | |
609 | return 0; | |
610 | ||
54ff39f7 | 611 | r = nexthop_configure(nexthop, link, req); |
8bed7c55 YW |
612 | if (r < 0) |
613 | return log_link_warning_errno(link, r, "Failed to configure nexthop"); | |
614 | ||
615 | nexthop_enter_configuring(nexthop); | |
cdf3bee6 YW |
616 | if (nexthop_get_by_id(link->manager, nexthop->id, &existing) >= 0) |
617 | nexthop_enter_configuring(existing); | |
618 | ||
8bed7c55 YW |
619 | return 1; |
620 | } | |
621 | ||
cdf3bee6 YW |
622 | static int link_request_nexthop(Link *link, const NextHop *nexthop) { |
623 | _cleanup_(nexthop_freep) NextHop *tmp = NULL; | |
624 | NextHop *existing = NULL; | |
89fa0133 YW |
625 | int r; |
626 | ||
76c5a0f2 | 627 | assert(link); |
cdf3bee6 | 628 | assert(link->manager); |
76c5a0f2 | 629 | assert(nexthop); |
89fa0133 YW |
630 | assert(nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN); |
631 | ||
cdf3bee6 YW |
632 | if (nexthop_get_request(link, nexthop, NULL) >= 0) |
633 | return 0; /* already requested, skipping. */ | |
89fa0133 | 634 | |
cdf3bee6 YW |
635 | r = nexthop_dup(nexthop, &tmp); |
636 | if (r < 0) | |
637 | return r; | |
76c5a0f2 | 638 | |
cdf3bee6 | 639 | if (nexthop_get(link, nexthop, &existing) < 0) { |
89fa0133 YW |
640 | r = nexthop_acquire_id(link->manager, tmp); |
641 | if (r < 0) | |
642 | return r; | |
cdf3bee6 YW |
643 | } else { |
644 | /* Copy ID */ | |
645 | assert(tmp->id == 0 || tmp->id == existing->id); | |
646 | tmp->id = existing->id; | |
89fa0133 | 647 | |
cdf3bee6 YW |
648 | /* Copy state for logging below. */ |
649 | tmp->state = existing->state; | |
650 | } | |
89fa0133 | 651 | |
cdf3bee6 YW |
652 | if (nexthop_bound_to_link(tmp)) |
653 | tmp->ifindex = link->ifindex; | |
89fa0133 | 654 | |
cdf3bee6 | 655 | log_nexthop_debug(tmp, "Requesting", link->manager); |
09d09207 | 656 | r = link_queue_request_safe(link, REQUEST_TYPE_NEXTHOP, |
cdf3bee6 YW |
657 | tmp, |
658 | nexthop_free, | |
09d09207 YW |
659 | nexthop_hash_func, |
660 | nexthop_compare_func, | |
661 | nexthop_process_request, | |
662 | &link->static_nexthop_messages, | |
663 | static_nexthop_handler, | |
664 | NULL); | |
89fa0133 YW |
665 | if (r <= 0) |
666 | return r; | |
667 | ||
cdf3bee6 YW |
668 | nexthop_enter_requesting(tmp); |
669 | if (existing) | |
670 | nexthop_enter_requesting(existing); | |
671 | ||
672 | TAKE_PTR(tmp); | |
89fa0133 | 673 | return 1; |
76c5a0f2 YW |
674 | } |
675 | ||
676 | int link_request_static_nexthops(Link *link, bool only_ipv4) { | |
c133770a YW |
677 | NextHop *nh; |
678 | int r; | |
679 | ||
680 | assert(link); | |
681 | assert(link->network); | |
682 | ||
683 | link->static_nexthops_configured = false; | |
684 | ||
76c5a0f2 YW |
685 | HASHMAP_FOREACH(nh, link->network->nexthops_by_section) { |
686 | if (only_ipv4 && nh->family != AF_INET) | |
687 | continue; | |
055e4909 | 688 | |
80d62d4f | 689 | r = link_request_nexthop(link, nh); |
76c5a0f2 YW |
690 | if (r < 0) |
691 | return log_link_warning_errno(link, r, "Could not request nexthop: %m"); | |
692 | } | |
c133770a | 693 | |
76c5a0f2 | 694 | if (link->static_nexthop_messages == 0) { |
c133770a | 695 | link->static_nexthops_configured = true; |
76c5a0f2 YW |
696 | link_check_ready(link); |
697 | } else { | |
698 | log_link_debug(link, "Requesting nexthops"); | |
699 | link_set_state(link, LINK_STATE_CONFIGURING); | |
c133770a YW |
700 | } |
701 | ||
f345918d | 702 | return 0; |
c133770a YW |
703 | } |
704 | ||
352eba2e | 705 | static void link_mark_nexthops(Link *link, bool foreign) { |
89fa0133 | 706 | NextHop *nexthop; |
352eba2e | 707 | Link *other; |
25b82b6e | 708 | |
352eba2e YW |
709 | assert(link); |
710 | assert(link->manager); | |
25b82b6e | 711 | |
89fa0133 | 712 | /* First, mark all nexthops. */ |
352eba2e | 713 | HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { |
89fa0133 YW |
714 | /* do not touch nexthop created by the kernel */ |
715 | if (nexthop->protocol == RTPROT_KERNEL) | |
716 | continue; | |
25b82b6e | 717 | |
a0e99a37 YW |
718 | /* When 'foreign' is true, mark only foreign nexthops, and vice versa. */ |
719 | if (foreign != (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN)) | |
89fa0133 | 720 | continue; |
25b82b6e | 721 | |
89fa0133 YW |
722 | /* Ignore nexthops not assigned yet or already removed. */ |
723 | if (!nexthop_exists(nexthop)) | |
724 | continue; | |
25b82b6e | 725 | |
352eba2e YW |
726 | /* Ignore nexthops bound to other links. */ |
727 | if (nexthop->ifindex > 0 && nexthop->ifindex != link->ifindex) | |
728 | continue; | |
729 | ||
89fa0133 YW |
730 | nexthop_mark(nexthop); |
731 | } | |
25b82b6e | 732 | |
89fa0133 | 733 | /* Then, unmark all nexthops requested by active links. */ |
352eba2e YW |
734 | HASHMAP_FOREACH(other, link->manager->links_by_index) { |
735 | if (!foreign && other == link) | |
25b82b6e YW |
736 | continue; |
737 | ||
352eba2e | 738 | if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) |
89fa0133 | 739 | continue; |
25b82b6e | 740 | |
352eba2e | 741 | HASHMAP_FOREACH(nexthop, other->network->nexthops_by_section) { |
89fa0133 | 742 | NextHop *existing; |
25b82b6e | 743 | |
352eba2e | 744 | if (nexthop_get(other, nexthop, &existing) >= 0) |
89fa0133 YW |
745 | nexthop_unmark(existing); |
746 | } | |
747 | } | |
b4564f4e YW |
748 | } |
749 | ||
352eba2e | 750 | int link_drop_nexthops(Link *link, bool foreign) { |
25b82b6e | 751 | NextHop *nexthop; |
1339b950 | 752 | int r = 0; |
25b82b6e YW |
753 | |
754 | assert(link); | |
755 | assert(link->manager); | |
89fa0133 | 756 | |
352eba2e | 757 | link_mark_nexthops(link, foreign); |
89fa0133 | 758 | |
352eba2e | 759 | HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { |
89fa0133 YW |
760 | if (!nexthop_is_marked(nexthop)) |
761 | continue; | |
762 | ||
1339b950 | 763 | RET_GATHER(r, nexthop_remove(nexthop)); |
25b82b6e YW |
764 | } |
765 | ||
352eba2e | 766 | return r; |
25b82b6e YW |
767 | } |
768 | ||
b4564f4e YW |
769 | void link_foreignize_nexthops(Link *link) { |
770 | NextHop *nexthop; | |
771 | ||
772 | assert(link); | |
352eba2e | 773 | assert(link->manager); |
b4564f4e | 774 | |
352eba2e | 775 | link_mark_nexthops(link, /* foreign = */ false); |
b4564f4e | 776 | |
352eba2e | 777 | HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { |
b4564f4e YW |
778 | if (!nexthop_is_marked(nexthop)) |
779 | continue; | |
780 | ||
781 | nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN; | |
782 | } | |
783 | } | |
784 | ||
58830ef5 YW |
785 | static int nexthop_update_group(NextHop *nexthop, const struct nexthop_grp *group, size_t size) { |
786 | _cleanup_hashmap_free_free_ Hashmap *h = NULL; | |
787 | size_t n_group; | |
788 | int r; | |
789 | ||
790 | assert(nexthop); | |
791 | assert(group || size == 0); | |
792 | ||
793 | if (size == 0 || size % sizeof(struct nexthop_grp) != 0) | |
794 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
795 | "rtnl: received nexthop message with invalid nexthop group size, ignoring."); | |
796 | ||
797 | if ((uintptr_t) group % alignof(struct nexthop_grp) != 0) | |
798 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
799 | "rtnl: received nexthop message with invalid alignment, ignoring."); | |
800 | ||
801 | n_group = size / sizeof(struct nexthop_grp); | |
802 | for (size_t i = 0; i < n_group; i++) { | |
803 | _cleanup_free_ struct nexthop_grp *nhg = NULL; | |
804 | ||
805 | if (group[i].id == 0) { | |
806 | log_debug("rtnl: received nexthop message with invalid ID in group, ignoring."); | |
807 | continue; | |
808 | } | |
809 | ||
810 | if (group[i].weight > 254) { | |
811 | log_debug("rtnl: received nexthop message with invalid weight in group, ignoring."); | |
812 | continue; | |
813 | } | |
814 | ||
815 | nhg = newdup(struct nexthop_grp, group + i, 1); | |
816 | if (!nhg) | |
817 | return log_oom(); | |
818 | ||
819 | r = hashmap_ensure_put(&h, NULL, UINT32_TO_PTR(nhg->id), nhg); | |
820 | if (r == -ENOMEM) | |
821 | return log_oom(); | |
822 | if (r < 0) { | |
823 | log_debug_errno(r, "Failed to store nexthop group, ignoring: %m"); | |
824 | continue; | |
825 | } | |
826 | if (r > 0) | |
827 | TAKE_PTR(nhg); | |
828 | } | |
829 | ||
830 | hashmap_free_free(nexthop->group); | |
831 | nexthop->group = TAKE_PTR(h); | |
832 | return 0; | |
833 | } | |
834 | ||
454c87b5 | 835 | int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { |
228c3e21 | 836 | _cleanup_free_ void *raw_group = NULL; |
228c3e21 | 837 | size_t raw_group_size; |
454c87b5 | 838 | uint16_t type; |
cdf3bee6 YW |
839 | uint32_t id, ifindex; |
840 | NextHop *nexthop = NULL; | |
841 | Request *req = NULL; | |
842 | bool is_new = false; | |
454c87b5 YW |
843 | int r; |
844 | ||
845 | assert(rtnl); | |
846 | assert(message); | |
847 | assert(m); | |
848 | ||
849 | if (sd_netlink_message_is_error(message)) { | |
850 | r = sd_netlink_message_get_errno(message); | |
851 | if (r < 0) | |
852 | log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring"); | |
853 | ||
854 | return 0; | |
855 | } | |
856 | ||
857 | r = sd_netlink_message_get_type(message, &type); | |
858 | if (r < 0) { | |
859 | log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); | |
860 | return 0; | |
861 | } else if (!IN_SET(type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP)) { | |
862 | log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type); | |
863 | return 0; | |
864 | } | |
865 | ||
cdf3bee6 YW |
866 | r = sd_netlink_message_read_u32(message, NHA_ID, &id); |
867 | if (r == -ENODATA) { | |
868 | log_warning_errno(r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m"); | |
8c112218 | 869 | return 0; |
cdf3bee6 YW |
870 | } else if (r < 0) { |
871 | log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m"); | |
454c87b5 | 872 | return 0; |
cdf3bee6 YW |
873 | } else if (id == 0) { |
874 | log_warning("rtnl: received nexthop message with invalid nexthop ID, ignoring: %m"); | |
e89f6ed4 ZJS |
875 | return 0; |
876 | } | |
454c87b5 | 877 | |
cdf3bee6 YW |
878 | (void) nexthop_get_by_id(m, id, &nexthop); |
879 | (void) nexthop_get_request_by_id(m, id, &req); | |
0e9d129c | 880 | |
cdf3bee6 YW |
881 | if (type == RTM_DELNEXTHOP) { |
882 | if (nexthop) { | |
883 | nexthop_enter_removed(nexthop); | |
884 | log_nexthop_debug(nexthop, "Forgetting removed", m); | |
885 | nexthop_free(nexthop); | |
886 | } else | |
887 | log_nexthop_debug(&(const NextHop) { .id = id }, "Kernel removed unknown", m); | |
888 | ||
889 | if (req) | |
890 | nexthop_enter_removed(req->userdata); | |
17f8d8f9 | 891 | |
f96f4ebc | 892 | return 0; |
228c3e21 YW |
893 | } |
894 | ||
cdf3bee6 YW |
895 | /* If we did not know the nexthop, then save it. */ |
896 | if (!nexthop) { | |
897 | r = nexthop_add_new(m, id, &nexthop); | |
898 | if (r < 0) { | |
899 | log_warning_errno(r, "Failed to add received nexthop, ignoring: %m"); | |
228c3e21 YW |
900 | return 0; |
901 | } | |
454c87b5 | 902 | |
cdf3bee6 | 903 | is_new = true; |
30f10837 | 904 | } |
30f10837 | 905 | |
cdf3bee6 YW |
906 | /* Also update information that cannot be obtained through netlink notification. */ |
907 | if (req && req->waiting_reply) { | |
908 | NextHop *n = ASSERT_PTR(req->userdata); | |
454c87b5 | 909 | |
cdf3bee6 YW |
910 | nexthop->source = n->source; |
911 | } | |
30f10837 | 912 | |
cdf3bee6 YW |
913 | r = sd_rtnl_message_get_family(message, &nexthop->family); |
914 | if (r < 0) | |
915 | log_debug_errno(r, "rtnl: could not get nexthop family, ignoring: %m"); | |
352eba2e | 916 | |
cdf3bee6 YW |
917 | r = sd_rtnl_message_nexthop_get_protocol(message, &nexthop->protocol); |
918 | if (r < 0) | |
919 | log_debug_errno(r, "rtnl: could not get nexthop protocol, ignoring: %m"); | |
454c87b5 | 920 | |
cdf3bee6 YW |
921 | r = sd_rtnl_message_nexthop_get_flags(message, &nexthop->flags); |
922 | if (r < 0) | |
923 | log_debug_errno(r, "rtnl: could not get nexthop flags, ignoring: %m"); | |
89fa0133 | 924 | |
cdf3bee6 YW |
925 | r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group); |
926 | if (r == -ENODATA) | |
927 | nexthop->group = hashmap_free_free(nexthop->group); | |
928 | else if (r < 0) | |
929 | log_debug_errno(r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m"); | |
930 | else | |
931 | (void) nexthop_update_group(nexthop, raw_group, raw_group_size); | |
454c87b5 | 932 | |
cdf3bee6 YW |
933 | if (nexthop->family != AF_UNSPEC) { |
934 | r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw); | |
935 | if (r == -ENODATA) | |
936 | nexthop->gw = IN_ADDR_NULL; | |
937 | else if (r < 0) | |
938 | log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m"); | |
454c87b5 YW |
939 | } |
940 | ||
cdf3bee6 YW |
941 | r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE); |
942 | if (r < 0) | |
943 | log_debug_errno(r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m"); | |
944 | else | |
945 | nexthop->blackhole = r; | |
946 | ||
947 | r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex); | |
948 | if (r == -ENODATA) | |
949 | nexthop->ifindex = 0; | |
950 | else if (r < 0) | |
951 | log_debug_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m"); | |
952 | else if (ifindex > INT32_MAX) | |
953 | log_debug_errno(r, "rtnl: received invalid NHA_OIF attribute, ignoring: %m"); | |
954 | else | |
955 | nexthop->ifindex = (int) ifindex; | |
956 | ||
957 | /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not | |
958 | * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */ | |
959 | if (!nexthop_bound_to_link(nexthop)) | |
960 | nexthop->ifindex = 0; | |
961 | ||
962 | nexthop_enter_configured(nexthop); | |
963 | if (req) | |
964 | nexthop_enter_configured(req->userdata); | |
965 | ||
966 | log_nexthop_debug(nexthop, is_new ? "Remembering" : "Received remembered", m); | |
454c87b5 YW |
967 | return 1; |
968 | } | |
969 | ||
0992f9fb | 970 | static int nexthop_section_verify(NextHop *nh) { |
c16c7808 SS |
971 | if (section_is_invalid(nh->section)) |
972 | return -EINVAL; | |
973 | ||
bbc05bec YW |
974 | if (!nh->network->manager->manage_foreign_nexthops && nh->id == 0) |
975 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
976 | "%s: [NextHop] section without specifying Id= is not supported " | |
977 | "if ManageForeignNextHops=no is set in networkd.conf. " | |
978 | "Ignoring [NextHop] section from line %u.", | |
979 | nh->section->filename, nh->section->line); | |
980 | ||
228c3e21 YW |
981 | if (!hashmap_isempty(nh->group)) { |
982 | if (in_addr_is_set(nh->family, &nh->gw)) | |
983 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
984 | "%s: nexthop group cannot have gateway address. " | |
985 | "Ignoring [NextHop] section from line %u.", | |
986 | nh->section->filename, nh->section->line); | |
987 | ||
988 | if (nh->family != AF_UNSPEC) | |
989 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
990 | "%s: nexthop group cannot have Family= setting. " | |
991 | "Ignoring [NextHop] section from line %u.", | |
992 | nh->section->filename, nh->section->line); | |
993 | ||
994 | if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw)) | |
995 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
996 | "%s: nexthop group cannot be a blackhole. " | |
997 | "Ignoring [NextHop] section from line %u.", | |
998 | nh->section->filename, nh->section->line); | |
999 | } else if (nh->family == AF_UNSPEC) | |
1000 | /* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */ | |
e9c4253d | 1001 | nh->family = AF_INET; |
c16c7808 | 1002 | |
30f10837 YW |
1003 | if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw)) |
1004 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
1005 | "%s: blackhole nexthop cannot have gateway address. " | |
1006 | "Ignoring [NextHop] section from line %u.", | |
1007 | nh->section->filename, nh->section->line); | |
1008 | ||
2ddd52d1 YW |
1009 | if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw) && |
1010 | ordered_hashmap_isempty(nh->network->addresses_by_section)) { | |
1011 | /* If no address is configured, in most cases the gateway cannot be reachable. | |
1012 | * TODO: we may need to improve the condition above. */ | |
1013 | log_warning("%s: Gateway= without static address configured. " | |
1014 | "Enabling OnLink= option.", | |
1015 | nh->section->filename); | |
1016 | nh->onlink = true; | |
1017 | } | |
1018 | ||
17f8d8f9 YW |
1019 | if (nh->onlink >= 0) |
1020 | SET_FLAG(nh->flags, RTNH_F_ONLINK, nh->onlink); | |
1021 | ||
c16c7808 SS |
1022 | return 0; |
1023 | } | |
1024 | ||
13ffa39f | 1025 | void network_drop_invalid_nexthops(Network *network) { |
0992f9fb YW |
1026 | NextHop *nh; |
1027 | ||
1028 | assert(network); | |
1029 | ||
1030 | HASHMAP_FOREACH(nh, network->nexthops_by_section) | |
1031 | if (nexthop_section_verify(nh) < 0) | |
1032 | nexthop_free(nh); | |
1033 | } | |
1034 | ||
c16c7808 SS |
1035 | int config_parse_nexthop_id( |
1036 | const char *unit, | |
1037 | const char *filename, | |
1038 | unsigned line, | |
1039 | const char *section, | |
1040 | unsigned section_line, | |
1041 | const char *lvalue, | |
1042 | int ltype, | |
1043 | const char *rvalue, | |
1044 | void *data, | |
1045 | void *userdata) { | |
1046 | ||
1047 | _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL; | |
1048 | Network *network = userdata; | |
acfd8491 | 1049 | uint32_t id; |
c16c7808 SS |
1050 | int r; |
1051 | ||
1052 | assert(filename); | |
1053 | assert(section); | |
1054 | assert(lvalue); | |
1055 | assert(rvalue); | |
1056 | assert(data); | |
1057 | ||
1058 | r = nexthop_new_static(network, filename, section_line, &n); | |
1059 | if (r < 0) | |
d96edb2c | 1060 | return log_oom(); |
c16c7808 | 1061 | |
acfd8491 YW |
1062 | if (isempty(rvalue)) { |
1063 | n->id = 0; | |
1064 | TAKE_PTR(n); | |
1065 | return 0; | |
1066 | } | |
1067 | ||
1068 | r = safe_atou32(rvalue, &id); | |
c16c7808 | 1069 | if (r < 0) { |
d96edb2c | 1070 | log_syntax(unit, LOG_WARNING, filename, line, r, |
c16c7808 SS |
1071 | "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue); |
1072 | return 0; | |
1073 | } | |
acfd8491 YW |
1074 | if (id == 0) { |
1075 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1076 | "Invalid nexthop id \"%s\", ignoring assignment: %m", rvalue); | |
1077 | return 0; | |
1078 | } | |
c16c7808 | 1079 | |
acfd8491 | 1080 | n->id = id; |
c16c7808 SS |
1081 | TAKE_PTR(n); |
1082 | return 0; | |
1083 | } | |
1084 | ||
1085 | int config_parse_nexthop_gateway( | |
1086 | const char *unit, | |
1087 | const char *filename, | |
1088 | unsigned line, | |
1089 | const char *section, | |
1090 | unsigned section_line, | |
1091 | const char *lvalue, | |
1092 | int ltype, | |
1093 | const char *rvalue, | |
1094 | void *data, | |
1095 | void *userdata) { | |
1096 | ||
1097 | _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL; | |
1098 | Network *network = userdata; | |
1099 | int r; | |
1100 | ||
1101 | assert(filename); | |
1102 | assert(section); | |
1103 | assert(lvalue); | |
1104 | assert(rvalue); | |
1105 | assert(data); | |
1106 | ||
1107 | r = nexthop_new_static(network, filename, section_line, &n); | |
1108 | if (r < 0) | |
d96edb2c | 1109 | return log_oom(); |
c16c7808 | 1110 | |
0008b5ae YW |
1111 | if (isempty(rvalue)) { |
1112 | n->family = AF_UNSPEC; | |
1113 | n->gw = IN_ADDR_NULL; | |
1114 | ||
1115 | TAKE_PTR(n); | |
1116 | return 0; | |
1117 | } | |
1118 | ||
c16c7808 SS |
1119 | r = in_addr_from_string_auto(rvalue, &n->family, &n->gw); |
1120 | if (r < 0) { | |
d96edb2c | 1121 | log_syntax(unit, LOG_WARNING, filename, line, r, |
c16c7808 SS |
1122 | "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); |
1123 | return 0; | |
1124 | } | |
1125 | ||
1126 | TAKE_PTR(n); | |
1127 | return 0; | |
1128 | } | |
f1923efc YW |
1129 | |
1130 | int config_parse_nexthop_family( | |
1131 | const char *unit, | |
1132 | const char *filename, | |
1133 | unsigned line, | |
1134 | const char *section, | |
1135 | unsigned section_line, | |
1136 | const char *lvalue, | |
1137 | int ltype, | |
1138 | const char *rvalue, | |
1139 | void *data, | |
1140 | void *userdata) { | |
1141 | ||
1142 | _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL; | |
1143 | Network *network = userdata; | |
1144 | AddressFamily a; | |
1145 | int r; | |
1146 | ||
1147 | assert(filename); | |
1148 | assert(section); | |
1149 | assert(lvalue); | |
1150 | assert(rvalue); | |
1151 | assert(data); | |
1152 | ||
1153 | r = nexthop_new_static(network, filename, section_line, &n); | |
1154 | if (r < 0) | |
1155 | return log_oom(); | |
1156 | ||
1157 | if (isempty(rvalue) && | |
94876904 | 1158 | !in_addr_is_set(n->family, &n->gw)) { |
f1923efc YW |
1159 | /* Accept an empty string only when Gateway= is null or not specified. */ |
1160 | n->family = AF_UNSPEC; | |
1161 | TAKE_PTR(n); | |
1162 | return 0; | |
1163 | } | |
1164 | ||
1165 | a = nexthop_address_family_from_string(rvalue); | |
1166 | if (a < 0) { | |
1167 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1168 | "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); | |
1169 | return 0; | |
1170 | } | |
1171 | ||
94876904 | 1172 | if (in_addr_is_set(n->family, &n->gw) && |
f1923efc YW |
1173 | ((a == ADDRESS_FAMILY_IPV4 && n->family == AF_INET6) || |
1174 | (a == ADDRESS_FAMILY_IPV6 && n->family == AF_INET))) { | |
1175 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1176 | "Specified family '%s' conflicts with the family of the previously specified Gateway=, " | |
1177 | "ignoring assignment.", rvalue); | |
1178 | return 0; | |
1179 | } | |
1180 | ||
89346ac6 | 1181 | switch (a) { |
f1923efc YW |
1182 | case ADDRESS_FAMILY_IPV4: |
1183 | n->family = AF_INET; | |
1184 | break; | |
1185 | case ADDRESS_FAMILY_IPV6: | |
1186 | n->family = AF_INET6; | |
1187 | break; | |
1188 | default: | |
04499a70 | 1189 | assert_not_reached(); |
f1923efc YW |
1190 | } |
1191 | ||
1192 | TAKE_PTR(n); | |
1193 | return 0; | |
1194 | } | |
2ddd52d1 YW |
1195 | |
1196 | int config_parse_nexthop_onlink( | |
1197 | const char *unit, | |
1198 | const char *filename, | |
1199 | unsigned line, | |
1200 | const char *section, | |
1201 | unsigned section_line, | |
1202 | const char *lvalue, | |
1203 | int ltype, | |
1204 | const char *rvalue, | |
1205 | void *data, | |
1206 | void *userdata) { | |
1207 | ||
1208 | _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL; | |
1209 | Network *network = userdata; | |
1210 | int r; | |
1211 | ||
1212 | assert(filename); | |
1213 | assert(section); | |
1214 | assert(lvalue); | |
1215 | assert(rvalue); | |
1216 | assert(data); | |
1217 | ||
1218 | r = nexthop_new_static(network, filename, section_line, &n); | |
1219 | if (r < 0) | |
1220 | return log_oom(); | |
1221 | ||
b71a721f | 1222 | r = parse_tristate(rvalue, &n->onlink); |
2ddd52d1 YW |
1223 | if (r < 0) { |
1224 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1225 | "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); | |
1226 | return 0; | |
1227 | } | |
1228 | ||
2ddd52d1 YW |
1229 | TAKE_PTR(n); |
1230 | return 0; | |
1231 | } | |
30f10837 YW |
1232 | |
1233 | int config_parse_nexthop_blackhole( | |
1234 | const char *unit, | |
1235 | const char *filename, | |
1236 | unsigned line, | |
1237 | const char *section, | |
1238 | unsigned section_line, | |
1239 | const char *lvalue, | |
1240 | int ltype, | |
1241 | const char *rvalue, | |
1242 | void *data, | |
1243 | void *userdata) { | |
1244 | ||
1245 | _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL; | |
1246 | Network *network = userdata; | |
1247 | int r; | |
1248 | ||
1249 | assert(filename); | |
1250 | assert(section); | |
1251 | assert(lvalue); | |
1252 | assert(rvalue); | |
1253 | assert(data); | |
1254 | ||
1255 | r = nexthop_new_static(network, filename, section_line, &n); | |
1256 | if (r < 0) | |
1257 | return log_oom(); | |
1258 | ||
1259 | r = parse_boolean(rvalue); | |
1260 | if (r < 0) { | |
1261 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1262 | "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); | |
1263 | return 0; | |
1264 | } | |
1265 | ||
1266 | n->blackhole = r; | |
1267 | ||
1268 | TAKE_PTR(n); | |
1269 | return 0; | |
1270 | } | |
228c3e21 YW |
1271 | |
1272 | int config_parse_nexthop_group( | |
1273 | const char *unit, | |
1274 | const char *filename, | |
1275 | unsigned line, | |
1276 | const char *section, | |
1277 | unsigned section_line, | |
1278 | const char *lvalue, | |
1279 | int ltype, | |
1280 | const char *rvalue, | |
1281 | void *data, | |
1282 | void *userdata) { | |
1283 | ||
1284 | _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL; | |
1285 | Network *network = userdata; | |
1286 | int r; | |
1287 | ||
1288 | assert(filename); | |
1289 | assert(section); | |
1290 | assert(lvalue); | |
1291 | assert(rvalue); | |
1292 | assert(data); | |
1293 | ||
1294 | r = nexthop_new_static(network, filename, section_line, &n); | |
1295 | if (r < 0) | |
1296 | return log_oom(); | |
1297 | ||
1298 | if (isempty(rvalue)) { | |
1299 | n->group = hashmap_free_free(n->group); | |
1300 | TAKE_PTR(n); | |
1301 | return 0; | |
1302 | } | |
1303 | ||
1304 | for (const char *p = rvalue;;) { | |
1305 | _cleanup_free_ struct nexthop_grp *nhg = NULL; | |
1306 | _cleanup_free_ char *word = NULL; | |
1307 | uint32_t w; | |
1308 | char *sep; | |
1309 | ||
1310 | r = extract_first_word(&p, &word, NULL, 0); | |
1311 | if (r == -ENOMEM) | |
1312 | return log_oom(); | |
1313 | if (r < 0) { | |
1314 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1315 | "Invalid %s=, ignoring assignment: %s", lvalue, rvalue); | |
1316 | return 0; | |
1317 | } | |
1318 | if (r == 0) | |
1319 | break; | |
1320 | ||
1321 | nhg = new0(struct nexthop_grp, 1); | |
1322 | if (!nhg) | |
1323 | return log_oom(); | |
1324 | ||
1325 | sep = strchr(word, ':'); | |
1326 | if (sep) { | |
1327 | *sep++ = '\0'; | |
1328 | r = safe_atou32(sep, &w); | |
1329 | if (r < 0) { | |
1330 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1331 | "Failed to parse weight for nexthop group, ignoring assignment: %s:%s", | |
1332 | word, sep); | |
1333 | continue; | |
1334 | } | |
1335 | if (w == 0 || w > 256) { | |
1336 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1337 | "Invalid weight for nexthop group, ignoring assignment: %s:%s", | |
1338 | word, sep); | |
1339 | continue; | |
1340 | } | |
1341 | /* See comments in config_parse_multipath_route(). */ | |
1342 | nhg->weight = w - 1; | |
1343 | } | |
1344 | ||
1345 | r = safe_atou32(word, &nhg->id); | |
1346 | if (r < 0) { | |
1347 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1348 | "Failed to parse nexthop ID in %s=, ignoring assignment: %s%s%s", | |
1349 | lvalue, word, sep ? ":" : "", strempty(sep)); | |
1350 | continue; | |
1351 | } | |
1352 | if (nhg->id == 0) { | |
1353 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1354 | "Nexthop ID in %s= must be positive, ignoring assignment: %s%s%s", | |
1355 | lvalue, word, sep ? ":" : "", strempty(sep)); | |
1356 | continue; | |
1357 | } | |
1358 | ||
1359 | r = hashmap_ensure_put(&n->group, NULL, UINT32_TO_PTR(nhg->id), nhg); | |
1360 | if (r == -ENOMEM) | |
1361 | return log_oom(); | |
1362 | if (r == -EEXIST) { | |
1363 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1364 | "Nexthop ID %"PRIu32" is specified multiple times in %s=, ignoring assignment: %s%s%s", | |
1365 | nhg->id, lvalue, word, sep ? ":" : "", strempty(sep)); | |
1366 | continue; | |
1367 | } | |
1368 | assert(r > 0); | |
1369 | TAKE_PTR(nhg); | |
1370 | } | |
1371 | ||
1372 | TAKE_PTR(n); | |
1373 | return 0; | |
1374 | } |