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