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