]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-neighbor.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / network / networkd-neighbor.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "hashmap.h"
5 #include "netlink-util.h"
6 #include "networkd-link.h"
7 #include "networkd-manager.h"
8 #include "networkd-neighbor.h"
9 #include "networkd-network.h"
10 #include "set.h"
11
12 Neighbor *neighbor_free(Neighbor *neighbor) {
13 if (!neighbor)
14 return NULL;
15
16 if (neighbor->network) {
17 assert(neighbor->section);
18 hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
19 }
20
21 network_config_section_free(neighbor->section);
22
23 if (neighbor->link) {
24 set_remove(neighbor->link->neighbors, neighbor);
25 set_remove(neighbor->link->neighbors_foreign, neighbor);
26 }
27
28 return mfree(neighbor);
29 }
30
31 DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free);
32
33 static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
34 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
35 _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
36 int r;
37
38 assert(network);
39 assert(ret);
40 assert(filename);
41 assert(section_line > 0);
42
43 r = network_config_section_new(filename, section_line, &n);
44 if (r < 0)
45 return r;
46
47 neighbor = hashmap_get(network->neighbors_by_section, n);
48 if (neighbor) {
49 *ret = TAKE_PTR(neighbor);
50 return 0;
51 }
52
53 neighbor = new(Neighbor, 1);
54 if (!neighbor)
55 return -ENOMEM;
56
57 *neighbor = (Neighbor) {
58 .network = network,
59 .family = AF_UNSPEC,
60 .section = TAKE_PTR(n),
61 };
62
63 r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops);
64 if (r < 0)
65 return r;
66
67 r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor);
68 if (r < 0)
69 return r;
70
71 *ret = TAKE_PTR(neighbor);
72 return 0;
73 }
74
75 static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
76 assert(neighbor);
77
78 siphash24_compress(&neighbor->family, sizeof(neighbor->family), state);
79 siphash24_compress(&neighbor->lladdr_size, sizeof(neighbor->lladdr_size), state);
80
81 switch (neighbor->family) {
82 case AF_INET:
83 case AF_INET6:
84 /* Equality of neighbors are given by the pair (addr,lladdr) */
85 siphash24_compress(&neighbor->in_addr, FAMILY_ADDRESS_SIZE(neighbor->family), state);
86 break;
87 default:
88 /* treat any other address family as AF_UNSPEC */
89 break;
90 }
91
92 siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state);
93 }
94
95 static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
96 int r;
97
98 r = CMP(a->family, b->family);
99 if (r != 0)
100 return r;
101
102 r = CMP(a->lladdr_size, b->lladdr_size);
103 if (r != 0)
104 return r;
105
106 switch (a->family) {
107 case AF_INET:
108 case AF_INET6:
109 r = memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family));
110 if (r != 0)
111 return r;
112 }
113
114 return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size);
115 }
116
117 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free);
118
119 static int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) {
120 Neighbor *existing;
121
122 assert(link);
123 assert(in);
124
125 existing = set_get(link->neighbors, in);
126 if (existing) {
127 if (ret)
128 *ret = existing;
129 return 1;
130 }
131
132 existing = set_get(link->neighbors_foreign, in);
133 if (existing) {
134 if (ret)
135 *ret = existing;
136 return 0;
137 }
138
139 return -ENOENT;
140 }
141
142 static int neighbor_add_internal(Link *link, Set **neighbors, const Neighbor *in, Neighbor **ret) {
143 _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
144 int r;
145
146 assert(link);
147 assert(neighbors);
148 assert(in);
149
150 neighbor = new(Neighbor, 1);
151 if (!neighbor)
152 return -ENOMEM;
153
154 *neighbor = (Neighbor) {
155 .family = in->family,
156 .in_addr = in->in_addr,
157 .lladdr = in->lladdr,
158 .lladdr_size = in->lladdr_size,
159 };
160
161 r = set_ensure_put(neighbors, &neighbor_hash_ops, neighbor);
162 if (r < 0)
163 return r;
164 if (r == 0)
165 return -EEXIST;
166
167 neighbor->link = link;
168
169 if (ret)
170 *ret = neighbor;
171
172 TAKE_PTR(neighbor);
173 return 0;
174 }
175
176 static int neighbor_add(Link *link, const Neighbor *in, Neighbor **ret) {
177 Neighbor *neighbor;
178 int r;
179
180 r = neighbor_get(link, in, &neighbor);
181 if (r == -ENOENT) {
182 /* Neighbor doesn't exist, make a new one */
183 r = neighbor_add_internal(link, &link->neighbors, in, &neighbor);
184 if (r < 0)
185 return r;
186 } else if (r == 0) {
187 /* Neighbor is foreign, claim it as recognized */
188 r = set_ensure_put(&link->neighbors, &neighbor_hash_ops, neighbor);
189 if (r < 0)
190 return r;
191
192 set_remove(link->neighbors_foreign, neighbor);
193 } else if (r == 1) {
194 /* Neighbor already exists */
195 } else
196 return r;
197
198 if (ret)
199 *ret = neighbor;
200 return 0;
201 }
202
203 static int neighbor_add_foreign(Link *link, const Neighbor *in, Neighbor **ret) {
204 return neighbor_add_internal(link, &link->neighbors_foreign, in, ret);
205 }
206
207 static bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) {
208 if (n1 == n2)
209 return true;
210
211 if (!n1 || !n2)
212 return false;
213
214 return neighbor_compare_func(n1, n2) == 0;
215 }
216
217 static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
218 int r;
219
220 assert(m);
221 assert(link);
222 assert(link->neighbor_messages > 0);
223
224 link->neighbor_messages--;
225
226 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
227 return 1;
228
229 r = sd_netlink_message_get_errno(m);
230 if (r < 0 && r != -EEXIST)
231 /* Neighbor may not exist yet. So, do not enter failed state here. */
232 log_link_message_warning_errno(link, m, r, "Could not set neighbor, ignoring");
233
234 if (link->neighbor_messages == 0) {
235 log_link_debug(link, "Neighbors set");
236 link->neighbors_configured = true;
237 link_check_ready(link);
238 }
239
240 return 1;
241 }
242
243 static int neighbor_configure(Neighbor *neighbor, Link *link) {
244 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
245 int r;
246
247 assert(neighbor);
248 assert(link);
249 assert(link->ifindex > 0);
250 assert(link->manager);
251 assert(link->manager->rtnl);
252
253 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH,
254 link->ifindex, neighbor->family);
255 if (r < 0)
256 return log_link_error_errno(link, r, "Could not allocate RTM_NEWNEIGH message: %m");
257
258 r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT);
259 if (r < 0)
260 return log_link_error_errno(link, r, "Could not set state: %m");
261
262 r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE);
263 if (r < 0)
264 return log_link_error_errno(link, r, "Could not set flags: %m");
265
266 r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr, neighbor->lladdr_size);
267 if (r < 0)
268 return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m");
269
270 r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr);
271 if (r < 0)
272 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
273
274 r = netlink_call_async(link->manager->rtnl, NULL, req, neighbor_configure_handler,
275 link_netlink_destroy_callback, link);
276 if (r < 0)
277 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
278
279 link->neighbor_messages++;
280 link_ref(link);
281
282 r = neighbor_add(link, neighbor, NULL);
283 if (r < 0)
284 return log_link_error_errno(link, r, "Could not add neighbor: %m");
285
286 return 0;
287 }
288
289 int link_set_neighbors(Link *link) {
290 Neighbor *neighbor;
291 int r;
292
293 assert(link);
294 assert(link->network);
295 assert(link->state != _LINK_STATE_INVALID);
296
297 link->neighbors_configured = false;
298
299 HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) {
300 r = neighbor_configure(neighbor, link);
301 if (r < 0)
302 return log_link_warning_errno(link, r, "Could not set neighbor: %m");
303 }
304
305 if (link->neighbor_messages == 0) {
306 link->neighbors_configured = true;
307 link_check_ready(link);
308 } else {
309 log_link_debug(link, "Setting neighbors");
310 link_set_state(link, LINK_STATE_CONFIGURING);
311 }
312
313 return 0;
314 }
315
316 static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
317 int r;
318
319 assert(m);
320 assert(link);
321
322 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
323 return 1;
324
325 r = sd_netlink_message_get_errno(m);
326 if (r < 0 && r != -ESRCH)
327 /* Neighbor may not exist because it already got deleted, ignore that. */
328 log_link_message_warning_errno(link, m, r, "Could not remove neighbor");
329
330 return 1;
331 }
332
333 static int neighbor_remove(Neighbor *neighbor, Link *link) {
334 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
335 int r;
336
337 assert(neighbor);
338 assert(link);
339 assert(link->ifindex > 0);
340 assert(link->manager);
341 assert(link->manager->rtnl);
342
343 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_DELNEIGH,
344 link->ifindex, neighbor->family);
345 if (r < 0)
346 return log_link_error_errno(link, r, "Could not allocate RTM_DELNEIGH message: %m");
347
348 r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr);
349 if (r < 0)
350 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
351
352 r = netlink_call_async(link->manager->rtnl, NULL, req, neighbor_remove_handler,
353 link_netlink_destroy_callback, link);
354 if (r < 0)
355 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
356
357 link_ref(link);
358
359 return 0;
360 }
361
362 static bool link_is_neighbor_configured(Link *link, Neighbor *neighbor) {
363 Neighbor *net_neighbor;
364
365 assert(link);
366 assert(neighbor);
367
368 if (!link->network)
369 return false;
370
371 HASHMAP_FOREACH(net_neighbor, link->network->neighbors_by_section)
372 if (neighbor_equal(net_neighbor, neighbor))
373 return true;
374
375 return false;
376 }
377
378 int link_drop_foreign_neighbors(Link *link) {
379 Neighbor *neighbor;
380 int r;
381
382 assert(link);
383
384 SET_FOREACH(neighbor, link->neighbors_foreign)
385 if (link_is_neighbor_configured(link, neighbor)) {
386 r = neighbor_add(link, neighbor, NULL);
387 if (r < 0)
388 return r;
389 } else {
390 r = neighbor_remove(neighbor, link);
391 if (r < 0)
392 return r;
393 }
394
395 return 0;
396 }
397
398 int link_drop_neighbors(Link *link) {
399 Neighbor *neighbor;
400 int k, r = 0;
401
402 assert(link);
403
404 SET_FOREACH(neighbor, link->neighbors) {
405 k = neighbor_remove(neighbor, link);
406 if (k < 0 && r >= 0)
407 r = k;
408 }
409
410 return r;
411 }
412
413 static int manager_rtnl_process_neighbor_lladdr(sd_netlink_message *message, union lladdr_union *lladdr, size_t *size, char **str) {
414 int r;
415
416 assert(message);
417 assert(lladdr);
418 assert(size);
419 assert(str);
420
421 *str = NULL;
422
423 r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->ip.in6), &lladdr->ip.in6);
424 if (r >= 0) {
425 *size = sizeof(lladdr->ip.in6);
426 if (in_addr_to_string(AF_INET6, &lladdr->ip, str) < 0)
427 log_warning_errno(r, "Could not print lower address: %m");
428 return r;
429 }
430
431 r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->mac), &lladdr->mac);
432 if (r >= 0) {
433 *size = sizeof(lladdr->mac);
434 *str = new(char, ETHER_ADDR_TO_STRING_MAX);
435 if (!*str) {
436 log_oom();
437 return r;
438 }
439 ether_addr_to_string(&lladdr->mac, *str);
440 return r;
441 }
442
443 r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->ip.in), &lladdr->ip.in);
444 if (r >= 0) {
445 *size = sizeof(lladdr->ip.in);
446 if (in_addr_to_string(AF_INET, &lladdr->ip, str) < 0)
447 log_warning_errno(r, "Could not print lower address: %m");
448 return r;
449 }
450
451 return r;
452 }
453
454 int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
455 _cleanup_(neighbor_freep) Neighbor *tmp = NULL;
456 _cleanup_free_ char *addr_str = NULL, *lladdr_str = NULL;
457 Neighbor *neighbor = NULL;
458 uint16_t type, state;
459 int ifindex, r;
460 Link *link;
461
462 assert(rtnl);
463 assert(message);
464 assert(m);
465
466 if (sd_netlink_message_is_error(message)) {
467 r = sd_netlink_message_get_errno(message);
468 if (r < 0)
469 log_message_warning_errno(message, r, "rtnl: failed to receive neighbor message, ignoring");
470
471 return 0;
472 }
473
474 r = sd_netlink_message_get_type(message, &type);
475 if (r < 0) {
476 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
477 return 0;
478 } else if (!IN_SET(type, RTM_NEWNEIGH, RTM_DELNEIGH)) {
479 log_warning("rtnl: received unexpected message type %u when processing neighbor, ignoring.", type);
480 return 0;
481 }
482
483 r = sd_rtnl_message_neigh_get_state(message, &state);
484 if (r < 0) {
485 log_warning_errno(r, "rtnl: received neighbor message with invalid state, ignoring: %m");
486 return 0;
487 } else if (!FLAGS_SET(state, NUD_PERMANENT)) {
488 log_debug("rtnl: received non-static neighbor, ignoring.");
489 return 0;
490 }
491
492 r = sd_rtnl_message_neigh_get_ifindex(message, &ifindex);
493 if (r < 0) {
494 log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m");
495 return 0;
496 } else if (ifindex <= 0) {
497 log_warning("rtnl: received neighbor message with invalid ifindex %d, ignoring.", ifindex);
498 return 0;
499 }
500
501 r = link_get(m, ifindex, &link);
502 if (r < 0 || !link) {
503 /* when enumerating we might be out of sync, but we will get the neighbor again, so just
504 * ignore it */
505 if (!m->enumerating)
506 log_warning("rtnl: received neighbor for link '%d' we don't know about, ignoring.", ifindex);
507 return 0;
508 }
509
510 tmp = new0(Neighbor, 1);
511
512 r = sd_rtnl_message_neigh_get_family(message, &tmp->family);
513 if (r < 0) {
514 log_link_warning(link, "rtnl: received neighbor message without family, ignoring.");
515 return 0;
516 } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
517 log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family);
518 return 0;
519 }
520
521 r = netlink_message_read_in_addr_union(message, NDA_DST, tmp->family, &tmp->in_addr);
522 if (r < 0) {
523 log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m");
524 return 0;
525 }
526
527 if (in_addr_to_string(tmp->family, &tmp->in_addr, &addr_str) < 0)
528 log_link_warning_errno(link, r, "Could not print address: %m");
529
530 r = manager_rtnl_process_neighbor_lladdr(message, &tmp->lladdr, &tmp->lladdr_size, &lladdr_str);
531 if (r < 0) {
532 log_link_warning_errno(link, r, "rtnl: received neighbor message with invalid lladdr, ignoring: %m");
533 return 0;
534 }
535
536 (void) neighbor_get(link, tmp, &neighbor);
537
538 switch (type) {
539 case RTM_NEWNEIGH:
540 if (neighbor)
541 log_link_debug(link, "Received remembered neighbor: %s->%s",
542 strnull(addr_str), strnull(lladdr_str));
543 else {
544 /* A neighbor appeared that we did not request */
545 r = neighbor_add_foreign(link, tmp, NULL);
546 if (r < 0) {
547 log_link_warning_errno(link, r, "Failed to remember foreign neighbor %s->%s, ignoring: %m",
548 strnull(addr_str), strnull(lladdr_str));
549 return 0;
550 } else
551 log_link_debug(link, "Remembering foreign neighbor: %s->%s",
552 strnull(addr_str), strnull(lladdr_str));
553 }
554
555 break;
556
557 case RTM_DELNEIGH:
558 if (neighbor) {
559 log_link_debug(link, "Forgetting neighbor: %s->%s",
560 strnull(addr_str), strnull(lladdr_str));
561 (void) neighbor_free(neighbor);
562 } else
563 log_link_debug(link, "Kernel removed a neighbor we don't remember: %s->%s, ignoring.",
564 strnull(addr_str), strnull(lladdr_str));
565
566 break;
567
568 default:
569 assert_not_reached("Received invalid RTNL message type");
570 }
571
572 return 1;
573 }
574
575 static int neighbor_section_verify(Neighbor *neighbor) {
576 if (section_is_invalid(neighbor->section))
577 return -EINVAL;
578
579 if (neighbor->family == AF_UNSPEC)
580 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
581 "%s: Neighbor section without Address= configured. "
582 "Ignoring [Neighbor] section from line %u.",
583 neighbor->section->filename, neighbor->section->line);
584
585 if (neighbor->lladdr_size == 0)
586 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
587 "%s: Neighbor section without LinkLayerAddress= configured. "
588 "Ignoring [Neighbor] section from line %u.",
589 neighbor->section->filename, neighbor->section->line);
590
591 return 0;
592 }
593
594 void network_drop_invalid_neighbors(Network *network) {
595 Neighbor *neighbor;
596
597 assert(network);
598
599 HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
600 if (neighbor_section_verify(neighbor) < 0)
601 neighbor_free(neighbor);
602 }
603
604
605 int config_parse_neighbor_address(
606 const char *unit,
607 const char *filename,
608 unsigned line,
609 const char *section,
610 unsigned section_line,
611 const char *lvalue,
612 int ltype,
613 const char *rvalue,
614 void *data,
615 void *userdata) {
616
617 Network *network = userdata;
618 _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
619 int r;
620
621 assert(filename);
622 assert(section);
623 assert(lvalue);
624 assert(rvalue);
625 assert(data);
626
627 r = neighbor_new_static(network, filename, section_line, &n);
628 if (r < 0)
629 return log_oom();
630
631 r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
632 if (r < 0) {
633 log_syntax(unit, LOG_WARNING, filename, line, r,
634 "Neighbor Address is invalid, ignoring assignment: %s", rvalue);
635 return 0;
636 }
637
638 TAKE_PTR(n);
639
640 return 0;
641 }
642
643 int config_parse_neighbor_lladdr(
644 const char *unit,
645 const char *filename,
646 unsigned line,
647 const char *section,
648 unsigned section_line,
649 const char *lvalue,
650 int ltype,
651 const char *rvalue,
652 void *data,
653 void *userdata) {
654
655 Network *network = userdata;
656 _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
657 int family, r;
658
659 assert(filename);
660 assert(section);
661 assert(lvalue);
662 assert(rvalue);
663 assert(data);
664
665 r = neighbor_new_static(network, filename, section_line, &n);
666 if (r < 0)
667 return log_oom();
668
669 r = ether_addr_from_string(rvalue, &n->lladdr.mac);
670 if (r >= 0)
671 n->lladdr_size = sizeof(n->lladdr.mac);
672 else {
673 r = in_addr_from_string_auto(rvalue, &family, &n->lladdr.ip);
674 if (r < 0) {
675 log_syntax(unit, LOG_WARNING, filename, line, r,
676 "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s",
677 rvalue);
678 return 0;
679 }
680 n->lladdr_size = family == AF_INET ? sizeof(n->lladdr.ip.in) : sizeof(n->lladdr.ip.in6);
681 }
682
683 TAKE_PTR(n);
684
685 return 0;
686 }
687
688 int config_parse_neighbor_hwaddr(
689 const char *unit,
690 const char *filename,
691 unsigned line,
692 const char *section,
693 unsigned section_line,
694 const char *lvalue,
695 int ltype,
696 const char *rvalue,
697 void *data,
698 void *userdata) {
699
700 Network *network = userdata;
701 _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
702 int r;
703
704 assert(filename);
705 assert(section);
706 assert(lvalue);
707 assert(rvalue);
708 assert(data);
709
710 r = neighbor_new_static(network, filename, section_line, &n);
711 if (r < 0)
712 return log_oom();
713
714 r = ether_addr_from_string(rvalue, &n->lladdr.mac);
715 if (r < 0) {
716 log_syntax(unit, LOG_WARNING, filename, line, r,
717 "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue);
718 return 0;
719 }
720
721 n->lladdr_size = sizeof(n->lladdr.mac);
722 TAKE_PTR(n);
723
724 return 0;
725 }