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