]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-nexthop.c
network: use request queue to configure addresses, routes, and nexthops
[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 <linux/nexthop.h>
6
7 #include "alloc-util.h"
8 #include "netlink-util.h"
9 #include "networkd-link.h"
10 #include "networkd-manager.h"
11 #include "networkd-network.h"
12 #include "networkd-nexthop.h"
13 #include "networkd-queue.h"
14 #include "networkd-route.h"
15 #include "parse-util.h"
16 #include "set.h"
17 #include "string-util.h"
18
19 NextHop *nexthop_free(NextHop *nexthop) {
20 if (!nexthop)
21 return NULL;
22
23 if (nexthop->network) {
24 assert(nexthop->section);
25 hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
26 }
27
28 network_config_section_free(nexthop->section);
29
30 if (nexthop->link) {
31 set_remove(nexthop->link->nexthops, nexthop);
32 set_remove(nexthop->link->nexthops_foreign, nexthop);
33
34 if (nexthop->link->manager && nexthop->id > 0)
35 hashmap_remove(nexthop->link->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
36 }
37
38 if (nexthop->manager) {
39 set_remove(nexthop->manager->nexthops, nexthop);
40 set_remove(nexthop->manager->nexthops_foreign, nexthop);
41
42 if (nexthop->id > 0)
43 hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
44 }
45
46 return mfree(nexthop);
47 }
48
49 DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free);
50
51 static int nexthop_new(NextHop **ret) {
52 _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
53
54 nexthop = new(NextHop, 1);
55 if (!nexthop)
56 return -ENOMEM;
57
58 *nexthop = (NextHop) {
59 .family = AF_UNSPEC,
60 .onlink = -1,
61 };
62
63 *ret = TAKE_PTR(nexthop);
64
65 return 0;
66 }
67
68 static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
69 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
70 _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
71 int r;
72
73 assert(network);
74 assert(ret);
75 assert(filename);
76 assert(section_line > 0);
77
78 r = network_config_section_new(filename, section_line, &n);
79 if (r < 0)
80 return r;
81
82 nexthop = hashmap_get(network->nexthops_by_section, n);
83 if (nexthop) {
84 *ret = TAKE_PTR(nexthop);
85 return 0;
86 }
87
88 r = nexthop_new(&nexthop);
89 if (r < 0)
90 return r;
91
92 nexthop->protocol = RTPROT_STATIC;
93 nexthop->network = network;
94 nexthop->section = TAKE_PTR(n);
95
96 r = hashmap_ensure_put(&network->nexthops_by_section, &network_config_hash_ops, nexthop->section, nexthop);
97 if (r < 0)
98 return r;
99
100 *ret = TAKE_PTR(nexthop);
101 return 0;
102 }
103
104 static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
105 assert(nexthop);
106
107 siphash24_compress(&nexthop->protocol, sizeof(nexthop->protocol), state);
108 siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
109 siphash24_compress(&nexthop->blackhole, sizeof(nexthop->blackhole), state);
110 siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
111
112 switch (nexthop->family) {
113 case AF_INET:
114 case AF_INET6:
115 siphash24_compress(&nexthop->gw, FAMILY_ADDRESS_SIZE(nexthop->family), state);
116
117 break;
118 default:
119 /* treat any other address family as AF_UNSPEC */
120 break;
121 }
122 }
123
124 static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
125 int r;
126
127 r = CMP(a->protocol, b->protocol);
128 if (r != 0)
129 return r;
130
131 r = CMP(a->id, b->id);
132 if (r != 0)
133 return r;
134
135 r = CMP(a->blackhole, b->blackhole);
136 if (r != 0)
137 return r;
138
139 r = CMP(a->family, b->family);
140 if (r != 0)
141 return r;
142
143 if (IN_SET(a->family, AF_INET, AF_INET6))
144 return memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
145
146 return 0;
147 }
148
149 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
150 nexthop_hash_ops,
151 NextHop,
152 nexthop_hash_func,
153 nexthop_compare_func,
154 nexthop_free);
155
156 static bool nexthop_equal(const NextHop *a, const NextHop *b) {
157 if (a == b)
158 return true;
159
160 if (!a || !b)
161 return false;
162
163 return nexthop_compare_func(a, b) == 0;
164 }
165
166 static void nexthop_copy(NextHop *dest, const NextHop *src) {
167 assert(dest);
168 assert(src);
169
170 /* This only copies entries used in the above hash and compare functions. */
171
172 dest->protocol = src->protocol;
173 dest->id = src->id;
174 dest->blackhole = src->blackhole;
175 dest->family = src->family;
176 dest->gw = src->gw;
177 }
178
179 int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
180 NextHop *nh;
181
182 assert(manager);
183
184 if (id == 0)
185 return -EINVAL;
186
187 nh = hashmap_get(manager->nexthops_by_id, UINT32_TO_PTR(id));
188 if (!nh)
189 return -ENOENT;
190
191 if (ret)
192 *ret = nh;
193 return 0;
194 }
195
196 static int nexthop_get(Manager *manager, Link *link, const NextHop *in, NextHop **ret) {
197 NextHop *existing;
198
199 assert(manager || link);
200 assert(in);
201
202 existing = set_get(link ? link->nexthops : manager->nexthops, in);
203 if (existing) {
204 if (ret)
205 *ret = existing;
206 return 1;
207 }
208
209 existing = set_get(link ? link->nexthops_foreign : manager->nexthops_foreign, in);
210 if (existing) {
211 if (ret)
212 *ret = existing;
213 return 0;
214 }
215
216 return -ENOENT;
217 }
218
219 static int nexthop_add_internal(Manager *manager, Link *link, Set **nexthops, const NextHop *in, NextHop **ret) {
220 _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
221 int r;
222
223 assert(manager || link);
224 assert(nexthops);
225 assert(in);
226
227 r = nexthop_new(&nexthop);
228 if (r < 0)
229 return r;
230
231 nexthop_copy(nexthop, in);
232
233 r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop);
234 if (r < 0)
235 return r;
236 if (r == 0)
237 return -EEXIST;
238
239 nexthop->link = link;
240 nexthop->manager = manager;
241
242 if (ret)
243 *ret = nexthop;
244
245 TAKE_PTR(nexthop);
246 return 0;
247 }
248
249 static int nexthop_add_foreign(Manager *manager, Link *link, const NextHop *in, NextHop **ret) {
250 assert(manager || link);
251 return nexthop_add_internal(manager, link, link ? &link->nexthops_foreign : &manager->nexthops_foreign, in, ret);
252 }
253
254 static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) {
255 bool is_new = false;
256 NextHop *nexthop;
257 int r;
258
259 assert(link);
260 assert(in);
261
262 if (in->blackhole)
263 r = nexthop_get(link->manager, NULL, in, &nexthop);
264 else
265 r = nexthop_get(NULL, link, in, &nexthop);
266 if (r == -ENOENT) {
267 /* NextHop does not exist, create a new one */
268 r = nexthop_add_internal(link->manager,
269 in->blackhole ? NULL : link,
270 in->blackhole ? &link->manager->nexthops : &link->nexthops,
271 in, &nexthop);
272 if (r < 0)
273 return r;
274 is_new = true;
275 } else if (r == 0) {
276 /* Take over a foreign nexthop */
277 r = set_ensure_put(in->blackhole ? &link->manager->nexthops : &link->nexthops,
278 &nexthop_hash_ops, nexthop);
279 if (r < 0)
280 return r;
281
282 set_remove(in->blackhole ? link->manager->nexthops_foreign : link->nexthops_foreign, nexthop);
283 } else if (r == 1) {
284 /* NextHop exists, do nothing */
285 ;
286 } else
287 return r;
288
289 if (ret)
290 *ret = nexthop;
291 return is_new;
292 }
293
294 static int nexthop_update(Manager *manager, Link *link, NextHop *nexthop, const NextHop *in) {
295 Set *nexthops;
296 int r;
297
298 /* link may be NULL. */
299
300 assert(manager);
301 assert(nexthop);
302 assert(in);
303 assert(in->id > 0);
304
305 /* This updates nexthop ID if necessary, and register the nexthop to Manager. */
306
307 if (nexthop->id > 0) {
308 if (nexthop->id == in->id)
309 goto set_manager;
310 return -EINVAL;
311 }
312
313 nexthops = link ? link->nexthops : manager->nexthops;
314
315 nexthop = set_remove(nexthops, nexthop);
316 if (!nexthop)
317 return -ENOENT;
318
319 nexthop->id = in->id;
320
321 r = set_put(nexthops, nexthop);
322 if (r <= 0) {
323 int k;
324
325 /* On failure, revert the change. */
326 nexthop->id = 0;
327 k = set_put(nexthops, nexthop);
328 if (k <= 0) {
329 nexthop_free(nexthop);
330 return k < 0 ? k : -EEXIST;
331 }
332
333 return r < 0 ? r : -EEXIST;
334 }
335
336 set_manager:
337 return hashmap_ensure_put(&manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop);
338 }
339
340 static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *str, const Link *link) {
341 _cleanup_free_ char *gw = NULL;
342
343 assert(nexthop);
344 assert(str);
345
346 /* link may be NULL. */
347
348 if (!DEBUG_LOGGING)
349 return;
350
351 (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
352
353 if (nexthop->id == id)
354 log_link_debug(link, "%s nexthop: id: %"PRIu32", gw: %s, blackhole: %s",
355 str, nexthop->id, strna(gw), yes_no(nexthop->blackhole));
356 else
357 log_link_debug(link, "%s nexthop: id: %"PRIu32"→%"PRIu32", gw: %s, blackhole: %s",
358 str, nexthop->id, id, strna(gw), yes_no(nexthop->blackhole));
359 }
360
361 static int link_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
362 int r;
363
364 assert(m);
365 assert(link);
366 assert(link->nexthop_remove_messages > 0);
367
368 link->nexthop_remove_messages--;
369
370 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
371 return 1;
372
373 r = sd_netlink_message_get_errno(m);
374 if (r < 0 && r != -ENOENT)
375 log_link_message_warning_errno(link, m, r, "Could not drop nexthop, ignoring");
376
377 return 1;
378 }
379
380 static int manager_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) {
381 int r;
382
383 assert(m);
384 assert(manager);
385 assert(manager->nexthop_remove_messages > 0);
386
387 manager->nexthop_remove_messages--;
388
389 r = sd_netlink_message_get_errno(m);
390 if (r < 0 && r != -ENOENT)
391 log_message_warning_errno(m, r, "Could not drop nexthop, ignoring");
392
393 return 1;
394 }
395
396 static int nexthop_remove(const NextHop *nexthop, Manager *manager, Link *link) {
397 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
398 int r;
399
400 assert(nexthop);
401 assert(manager);
402
403 /* link may be NULL. */
404
405 if (nexthop->id == 0) {
406 log_link_debug(link, "Cannot remove nexthop without valid ID, ignoring.");
407 return 0;
408 }
409
410 log_nexthop_debug(nexthop, nexthop->id, "Removing", link);
411
412 r = sd_rtnl_message_new_nexthop(manager->rtnl, &req, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC);
413 if (r < 0)
414 return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m");
415
416 r = sd_netlink_message_append_u32(req, NHA_ID, nexthop->id);
417 if (r < 0)
418 return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
419
420 if (link)
421 r = netlink_call_async(manager->rtnl, NULL, req, link_nexthop_remove_handler,
422 link_netlink_destroy_callback, link);
423 else
424 r = netlink_call_async(manager->rtnl, NULL, req, manager_nexthop_remove_handler,
425 NULL, manager);
426 if (r < 0)
427 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
428
429 link_ref(link); /* link may be NULL, link_ref() is OK with that */
430
431 if (link)
432 link->nexthop_remove_messages++;
433 else
434 manager->nexthop_remove_messages++;
435
436 return 0;
437 }
438
439 static int nexthop_configure(
440 const NextHop *nexthop,
441 Link *link,
442 link_netlink_message_handler_t callback,
443 NextHop **ret) {
444
445 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
446 int r, k;
447
448 assert(link);
449 assert(link->manager);
450 assert(link->manager->rtnl);
451 assert(link->ifindex > 0);
452 assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
453 assert(callback);
454
455 log_nexthop_debug(nexthop, nexthop->id, "Configuring", link);
456
457 r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
458 RTM_NEWNEXTHOP, nexthop->family,
459 nexthop->protocol);
460 if (r < 0)
461 return log_link_error_errno(link, r, "Could not create RTM_NEWNEXTHOP message: %m");
462
463 if (nexthop->id > 0) {
464 r = sd_netlink_message_append_u32(req, NHA_ID, nexthop->id);
465 if (r < 0)
466 return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
467 }
468
469 if (nexthop->blackhole) {
470 r = sd_netlink_message_append_flag(req, NHA_BLACKHOLE);
471 if (r < 0)
472 return log_link_error_errno(link, r, "Could not append NHA_BLACKHOLE attribute: %m");
473 } else {
474 r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex);
475 if (r < 0)
476 return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m");
477
478 if (in_addr_is_set(nexthop->family, &nexthop->gw)) {
479 r = netlink_message_append_in_addr_union(req, NHA_GATEWAY, nexthop->family, &nexthop->gw);
480 if (r < 0)
481 return log_link_error_errno(link, r, "Could not append NHA_GATEWAY attribute: %m");
482
483 if (nexthop->onlink > 0) {
484 r = sd_rtnl_message_nexthop_set_flags(req, RTNH_F_ONLINK);
485 if (r < 0)
486 return log_link_error_errno(link, r, "Failed to set RTNH_F_ONLINK flag: %m");
487 }
488 }
489 }
490
491 k = nexthop_add(link, nexthop, ret);
492 if (k < 0)
493 return log_link_error_errno(link, k, "Could not add nexthop: %m");
494
495 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
496 link_netlink_destroy_callback, link);
497 if (r < 0)
498 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
499
500 link_ref(link);
501
502 return k;
503 }
504
505 static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
506 int r;
507
508 assert(link);
509 assert(link->static_nexthop_messages > 0);
510
511 link->static_nexthop_messages--;
512
513 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
514 return 1;
515
516 r = sd_netlink_message_get_errno(m);
517 if (r < 0 && r != -EEXIST) {
518 log_link_message_warning_errno(link, m, r, "Could not set nexthop");
519 link_enter_failed(link);
520 return 1;
521 }
522
523 if (link->static_nexthop_messages == 0) {
524 log_link_debug(link, "Nexthops set");
525 link->static_nexthops_configured = true;
526 link_check_ready(link);
527 }
528
529 return 1;
530 }
531
532 static int link_request_nexthop(
533 Link *link,
534 NextHop *nexthop,
535 bool consume_object,
536 unsigned *message_counter,
537 link_netlink_message_handler_t netlink_handler,
538 Request **ret) {
539
540 assert(link);
541 assert(nexthop);
542
543 log_nexthop_debug(nexthop, nexthop->id, "Requesting", link);
544 return link_queue_request(link, REQUEST_TYPE_NEXTHOP, nexthop, consume_object,
545 message_counter, netlink_handler, ret);
546 }
547
548 int link_request_static_nexthops(Link *link, bool only_ipv4) {
549 NextHop *nh;
550 int r;
551
552 assert(link);
553 assert(link->network);
554
555 link->static_nexthops_configured = false;
556
557 HASHMAP_FOREACH(nh, link->network->nexthops_by_section) {
558 if (only_ipv4 && nh->family != AF_INET)
559 continue;
560
561 r = link_request_nexthop(link, nh, false, &link->static_nexthop_messages,
562 static_nexthop_handler, NULL);
563 if (r < 0)
564 return log_link_warning_errno(link, r, "Could not request nexthop: %m");
565 }
566
567 if (link->static_nexthop_messages == 0) {
568 link->static_nexthops_configured = true;
569 link_check_ready(link);
570 } else {
571 log_link_debug(link, "Requesting nexthops");
572 link_set_state(link, LINK_STATE_CONFIGURING);
573 }
574
575 return 0;
576 }
577
578 static bool link_has_nexthop(const Link *link, const NextHop *nexthop) {
579 NextHop *net_nexthop;
580
581 assert(link);
582 assert(nexthop);
583
584 if (!link->network)
585 return false;
586
587 HASHMAP_FOREACH(net_nexthop, link->network->nexthops_by_section)
588 if (nexthop_equal(net_nexthop, nexthop))
589 return true;
590
591 return false;
592 }
593
594 static bool links_have_nexthop(const Manager *manager, const NextHop *nexthop, const Link *except) {
595 Link *link;
596
597 assert(manager);
598
599 HASHMAP_FOREACH(link, manager->links) {
600 if (link == except)
601 continue;
602
603 if (link_has_nexthop(link, nexthop))
604 return true;
605 }
606
607 return false;
608 }
609
610 static int manager_drop_nexthops_internal(Manager *manager, bool foreign, const Link *except) {
611 NextHop *nexthop;
612 Set *nexthops;
613 int k, r = 0;
614
615 assert(manager);
616
617 nexthops = foreign ? manager->nexthops_foreign : manager->nexthops;
618 SET_FOREACH(nexthop, nexthops) {
619 /* do not touch nexthop created by the kernel */
620 if (nexthop->protocol == RTPROT_KERNEL)
621 continue;
622
623 /* The nexthop will be configured later, or already configured by a link. */
624 if (links_have_nexthop(manager, nexthop, except))
625 continue;
626
627 /* The existing links do not have the nexthop. Let's drop this now. It may be
628 * re-configured later. */
629 k = nexthop_remove(nexthop, manager, NULL);
630 if (k < 0 && r >= 0)
631 r = k;
632 }
633
634 return r;
635 }
636
637 static int manager_drop_foreign_nexthops(Manager *manager) {
638 return manager_drop_nexthops_internal(manager, true, NULL);
639 }
640
641 static int manager_drop_nexthops(Manager *manager, const Link *except) {
642 return manager_drop_nexthops_internal(manager, false, except);
643 }
644
645 int link_drop_foreign_nexthops(Link *link) {
646 NextHop *nexthop;
647 int k, r = 0;
648
649 assert(link);
650 assert(link->manager);
651
652 SET_FOREACH(nexthop, link->nexthops_foreign) {
653 /* do not touch nexthop created by the kernel */
654 if (nexthop->protocol == RTPROT_KERNEL)
655 continue;
656
657 if (link_has_nexthop(link, nexthop))
658 k = nexthop_add(link, nexthop, NULL);
659 else
660 k = nexthop_remove(nexthop, link->manager, link);
661 if (k < 0 && r >= 0)
662 r = k;
663 }
664
665 k = manager_drop_foreign_nexthops(link->manager);
666 if (k < 0 && r >= 0)
667 r = k;
668
669 return r;
670 }
671
672 int link_drop_nexthops(Link *link) {
673 NextHop *nexthop;
674 int k, r = 0;
675
676 assert(link);
677 assert(link->manager);
678
679 SET_FOREACH(nexthop, link->nexthops) {
680 /* do not touch nexthop created by the kernel */
681 if (nexthop->protocol == RTPROT_KERNEL)
682 continue;
683
684 k = nexthop_remove(nexthop, link->manager, link);
685 if (k < 0 && r >= 0)
686 r = k;
687 }
688
689 k = manager_drop_nexthops(link->manager, link);
690 if (k < 0 && r >= 0)
691 r = k;
692
693 return r;
694 }
695
696 static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
697 assert(link);
698 assert(nexthop);
699
700 if (nexthop->blackhole) {
701 if (link->manager->nexthop_remove_messages > 0)
702 return false;
703 } else {
704 Link *l;
705
706 HASHMAP_FOREACH(l, link->manager->links) {
707 if (l->address_remove_messages > 0)
708 return false;
709 if (l->nexthop_remove_messages > 0)
710 return false;
711 if (l->route_remove_messages > 0)
712 return false;
713 }
714 }
715
716 if (nexthop->id == 0) {
717 Request *req;
718
719 ORDERED_SET_FOREACH(req, link->manager->request_queue) {
720 if (req->type != REQUEST_TYPE_NEXTHOP)
721 continue;
722 if (req->nexthop->id != 0)
723 return false; /* first configure nexthop with id. */
724 }
725 }
726
727 if (nexthop->onlink <= 0 &&
728 in_addr_is_set(nexthop->family, &nexthop->gw) &&
729 !manager_address_is_reachable(link->manager, nexthop->family, &nexthop->gw))
730 return false;
731
732 return true;
733 }
734
735 int request_process_nexthop(Request *req) {
736 NextHop *ret;
737 int r;
738
739 assert(req);
740 assert(req->link);
741 assert(req->nexthop);
742 assert(req->type == REQUEST_TYPE_NEXTHOP);
743
744 if (!link_is_ready_to_configure(req->link, false))
745 return 0;
746
747 if (!nexthop_is_ready_to_configure(req->link, req->nexthop))
748 return 0;
749
750 r = nexthop_configure(req->nexthop, req->link, req->netlink_handler, &ret);
751 if (r < 0)
752 return r;
753
754 if (req->after_configure) {
755 r = req->after_configure(req, ret);
756 if (r < 0)
757 return r;
758 }
759
760 return 1;
761 }
762
763 int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
764 _cleanup_(nexthop_freep) NextHop *tmp = NULL;
765 NextHop *nexthop = NULL;
766 uint32_t ifindex;
767 uint16_t type;
768 Link *link = NULL;
769 int r;
770
771 assert(rtnl);
772 assert(message);
773 assert(m);
774
775 if (sd_netlink_message_is_error(message)) {
776 r = sd_netlink_message_get_errno(message);
777 if (r < 0)
778 log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring");
779
780 return 0;
781 }
782
783 r = sd_netlink_message_get_type(message, &type);
784 if (r < 0) {
785 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
786 return 0;
787 } else if (!IN_SET(type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP)) {
788 log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type);
789 return 0;
790 }
791
792 r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
793 if (r < 0 && r != -ENODATA) {
794 log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
795 return 0;
796 } else if (r >= 0) {
797 if (ifindex <= 0) {
798 log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex);
799 return 0;
800 }
801
802 r = link_get(m, ifindex, &link);
803 if (r < 0 || !link) {
804 if (!m->enumerating)
805 log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex);
806 return 0;
807 }
808 }
809
810 r = nexthop_new(&tmp);
811 if (r < 0)
812 return log_oom();
813
814 r = sd_rtnl_message_get_family(message, &tmp->family);
815 if (r < 0) {
816 log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m");
817 return 0;
818 } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
819 log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
820 return 0;
821 }
822
823 r = sd_rtnl_message_nexthop_get_protocol(message, &tmp->protocol);
824 if (r < 0) {
825 log_link_warning_errno(link, r, "rtnl: could not get nexthop protocol, ignoring: %m");
826 return 0;
827 }
828
829 r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw);
830 if (r < 0 && r != -ENODATA) {
831 log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
832 return 0;
833 }
834
835 r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE);
836 if (r < 0) {
837 log_link_warning_errno(link, r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
838 return 0;
839 }
840 tmp->blackhole = r;
841
842 r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
843 if (r == -ENODATA) {
844 log_link_warning_errno(link, r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
845 return 0;
846 } else if (r < 0) {
847 log_link_warning_errno(link, r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
848 return 0;
849 } else if (tmp->id == 0) {
850 log_link_warning(link, "rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
851 return 0;
852 }
853
854 /* All blackhole nexthops are managed by Manager. Note that the linux kernel does not set
855 * NHA_OID attribute when NHA_BLACKHOLE is set. Just for safety. */
856 if (tmp->blackhole)
857 link = NULL;
858
859 r = nexthop_get(m, link, tmp, &nexthop);
860 if (r < 0) {
861 uint32_t id;
862
863 /* The nexthop may be created without setting NHA_ID. */
864
865 id = tmp->id;
866 tmp->id = 0;
867
868 (void) nexthop_get(m, link, tmp, &nexthop);
869
870 tmp->id = id;
871 }
872
873 switch (type) {
874 case RTM_NEWNEXTHOP:
875 if (nexthop)
876 log_nexthop_debug(nexthop, tmp->id, "Received remembered", link);
877 else {
878 log_nexthop_debug(tmp, tmp->id, "Remembering foreign", link);
879 r = nexthop_add_foreign(m, link, tmp, &nexthop);
880 if (r < 0) {
881 log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m");
882 return 0;
883 }
884 }
885
886 r = nexthop_update(m, link, nexthop, tmp);
887 if (r < 0) {
888 log_link_warning_errno(link, r, "Could not update nexthop, ignoring: %m");
889 return 0;
890 }
891 break;
892 case RTM_DELNEXTHOP:
893 log_nexthop_debug(tmp, tmp->id, nexthop ? "Forgetting" : "Kernel removed unknown", link);
894 nexthop_free(nexthop);
895 break;
896
897 default:
898 assert_not_reached("Received invalid RTNL message type");
899 }
900
901 return 1;
902 }
903
904 static int nexthop_section_verify(NextHop *nh) {
905 if (section_is_invalid(nh->section))
906 return -EINVAL;
907
908 if (nh->family == AF_UNSPEC)
909 /* When no Gateway= is specified, assume IPv4. */
910 nh->family = AF_INET;
911
912 if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw))
913 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
914 "%s: blackhole nexthop cannot have gateway address. "
915 "Ignoring [NextHop] section from line %u.",
916 nh->section->filename, nh->section->line);
917
918 if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw) &&
919 ordered_hashmap_isempty(nh->network->addresses_by_section)) {
920 /* If no address is configured, in most cases the gateway cannot be reachable.
921 * TODO: we may need to improve the condition above. */
922 log_warning("%s: Gateway= without static address configured. "
923 "Enabling OnLink= option.",
924 nh->section->filename);
925 nh->onlink = true;
926 }
927
928 return 0;
929 }
930
931 void network_drop_invalid_nexthops(Network *network) {
932 NextHop *nh;
933
934 assert(network);
935
936 HASHMAP_FOREACH(nh, network->nexthops_by_section)
937 if (nexthop_section_verify(nh) < 0)
938 nexthop_free(nh);
939 }
940
941 int config_parse_nexthop_id(
942 const char *unit,
943 const char *filename,
944 unsigned line,
945 const char *section,
946 unsigned section_line,
947 const char *lvalue,
948 int ltype,
949 const char *rvalue,
950 void *data,
951 void *userdata) {
952
953 _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
954 Network *network = userdata;
955 uint32_t id;
956 int r;
957
958 assert(filename);
959 assert(section);
960 assert(lvalue);
961 assert(rvalue);
962 assert(data);
963
964 r = nexthop_new_static(network, filename, section_line, &n);
965 if (r < 0)
966 return log_oom();
967
968 if (isempty(rvalue)) {
969 n->id = 0;
970 TAKE_PTR(n);
971 return 0;
972 }
973
974 r = safe_atou32(rvalue, &id);
975 if (r < 0) {
976 log_syntax(unit, LOG_WARNING, filename, line, r,
977 "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue);
978 return 0;
979 }
980 if (id == 0) {
981 log_syntax(unit, LOG_WARNING, filename, line, 0,
982 "Invalid nexthop id \"%s\", ignoring assignment: %m", rvalue);
983 return 0;
984 }
985
986 n->id = id;
987 TAKE_PTR(n);
988 return 0;
989 }
990
991 int config_parse_nexthop_gateway(
992 const char *unit,
993 const char *filename,
994 unsigned line,
995 const char *section,
996 unsigned section_line,
997 const char *lvalue,
998 int ltype,
999 const char *rvalue,
1000 void *data,
1001 void *userdata) {
1002
1003 _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
1004 Network *network = userdata;
1005 int r;
1006
1007 assert(filename);
1008 assert(section);
1009 assert(lvalue);
1010 assert(rvalue);
1011 assert(data);
1012
1013 r = nexthop_new_static(network, filename, section_line, &n);
1014 if (r < 0)
1015 return log_oom();
1016
1017 if (isempty(rvalue)) {
1018 n->family = AF_UNSPEC;
1019 n->gw = IN_ADDR_NULL;
1020
1021 TAKE_PTR(n);
1022 return 0;
1023 }
1024
1025 r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
1026 if (r < 0) {
1027 log_syntax(unit, LOG_WARNING, filename, line, r,
1028 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
1029 return 0;
1030 }
1031
1032 TAKE_PTR(n);
1033 return 0;
1034 }
1035
1036 int config_parse_nexthop_family(
1037 const char *unit,
1038 const char *filename,
1039 unsigned line,
1040 const char *section,
1041 unsigned section_line,
1042 const char *lvalue,
1043 int ltype,
1044 const char *rvalue,
1045 void *data,
1046 void *userdata) {
1047
1048 _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
1049 Network *network = userdata;
1050 AddressFamily a;
1051 int r;
1052
1053 assert(filename);
1054 assert(section);
1055 assert(lvalue);
1056 assert(rvalue);
1057 assert(data);
1058
1059 r = nexthop_new_static(network, filename, section_line, &n);
1060 if (r < 0)
1061 return log_oom();
1062
1063 if (isempty(rvalue) &&
1064 !in_addr_is_set(n->family, &n->gw)) {
1065 /* Accept an empty string only when Gateway= is null or not specified. */
1066 n->family = AF_UNSPEC;
1067 TAKE_PTR(n);
1068 return 0;
1069 }
1070
1071 a = nexthop_address_family_from_string(rvalue);
1072 if (a < 0) {
1073 log_syntax(unit, LOG_WARNING, filename, line, 0,
1074 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
1075 return 0;
1076 }
1077
1078 if (in_addr_is_set(n->family, &n->gw) &&
1079 ((a == ADDRESS_FAMILY_IPV4 && n->family == AF_INET6) ||
1080 (a == ADDRESS_FAMILY_IPV6 && n->family == AF_INET))) {
1081 log_syntax(unit, LOG_WARNING, filename, line, 0,
1082 "Specified family '%s' conflicts with the family of the previously specified Gateway=, "
1083 "ignoring assignment.", rvalue);
1084 return 0;
1085 }
1086
1087 switch(a) {
1088 case ADDRESS_FAMILY_IPV4:
1089 n->family = AF_INET;
1090 break;
1091 case ADDRESS_FAMILY_IPV6:
1092 n->family = AF_INET6;
1093 break;
1094 default:
1095 assert_not_reached("Invalid family.");
1096 }
1097
1098 TAKE_PTR(n);
1099 return 0;
1100 }
1101
1102 int config_parse_nexthop_onlink(
1103 const char *unit,
1104 const char *filename,
1105 unsigned line,
1106 const char *section,
1107 unsigned section_line,
1108 const char *lvalue,
1109 int ltype,
1110 const char *rvalue,
1111 void *data,
1112 void *userdata) {
1113
1114 _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
1115 Network *network = userdata;
1116 int r;
1117
1118 assert(filename);
1119 assert(section);
1120 assert(lvalue);
1121 assert(rvalue);
1122 assert(data);
1123
1124 r = nexthop_new_static(network, filename, section_line, &n);
1125 if (r < 0)
1126 return log_oom();
1127
1128 if (isempty(rvalue)) {
1129 n->onlink = -1;
1130 TAKE_PTR(n);
1131 return 0;
1132 }
1133
1134 r = parse_boolean(rvalue);
1135 if (r < 0) {
1136 log_syntax(unit, LOG_WARNING, filename, line, r,
1137 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1138 return 0;
1139 }
1140
1141 n->onlink = r;
1142
1143 TAKE_PTR(n);
1144 return 0;
1145 }
1146
1147 int config_parse_nexthop_blackhole(
1148 const char *unit,
1149 const char *filename,
1150 unsigned line,
1151 const char *section,
1152 unsigned section_line,
1153 const char *lvalue,
1154 int ltype,
1155 const char *rvalue,
1156 void *data,
1157 void *userdata) {
1158
1159 _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
1160 Network *network = userdata;
1161 int r;
1162
1163 assert(filename);
1164 assert(section);
1165 assert(lvalue);
1166 assert(rvalue);
1167 assert(data);
1168
1169 r = nexthop_new_static(network, filename, section_line, &n);
1170 if (r < 0)
1171 return log_oom();
1172
1173 r = parse_boolean(rvalue);
1174 if (r < 0) {
1175 log_syntax(unit, LOG_WARNING, filename, line, r,
1176 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
1177 return 0;
1178 }
1179
1180 n->blackhole = r;
1181
1182 TAKE_PTR(n);
1183 return 0;
1184 }