]>
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 | ||
352eba2e | 258 | static 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 |
275 | static 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 | 318 | static 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 | 344 | static 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 | 361 | static 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 | 398 | static 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 | 462 | static 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 |
484 | static 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 | 530 | static 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 | 548 | static 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 | ||
594 | int 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 | 623 | static 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 | 668 | int 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 |
687 | void 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 |
703 | static 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 |
753 | int 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 | 912 | static 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 | 967 | void 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 |
977 | int 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 | ||
1027 | int 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 | |
1072 | int 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 | |
1138 | int 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 | |
1175 | int 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 | |
1214 | int 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 | } |