]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-state-file.c
tree-wide: use UINT64_MAX or friends
[thirdparty/systemd.git] / src / network / networkd-state-file.c
CommitLineData
3b5a4fc6
YW
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <netinet/in.h>
4#include <linux/if.h>
5
6#include "alloc-util.h"
7#include "dns-domain.h"
8#include "fd-util.h"
9#include "fileio.h"
10#include "fs-util.h"
11#include "network-internal.h"
12#include "networkd-link.h"
13#include "networkd-manager-bus.h"
14#include "networkd-manager.h"
15#include "networkd-network.h"
16#include "networkd-state-file.h"
17#include "ordered-set.h"
18#include "set.h"
19#include "strv.h"
20#include "tmpfile-util.h"
21
22static int ordered_set_put_dns_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) {
23 const char *p;
24 int r;
25
26 assert(s);
27 assert(dns);
28
29 if (dns->ifindex != 0 && dns->ifindex != ifindex)
30 return 0;
31
32 p = in_addr_full_to_string(dns);
33 if (!p)
34 return 0;
35
36 r = ordered_set_put_strdup(s, p);
37 if (r == -EEXIST)
38 return 0;
39
40 return r;
41}
42
43static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) {
44 int r, c = 0;
45
46 assert(s);
47 assert(dns || n == 0);
48
49 for (unsigned i = 0; i < n; i++) {
50 r = ordered_set_put_dns_server(s, ifindex, dns[i]);
51 if (r < 0)
52 return r;
53
54 c += r;
55 }
56
57 return c;
58}
59
60static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address) {
61 char *p;
62 int r;
63
64 assert(s);
65 assert(address);
66
67 r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
68 if (r < 0)
69 return r;
70
71 r = ordered_set_consume(s, p);
72 if (r == -EEXIST)
73 return 0;
74
75 return r;
76}
77
44e1f7e3
YW
78static int ordered_set_put_in4_addrv(
79 OrderedSet *s,
80 const struct in_addr *addresses,
81 size_t n,
82 bool (*predicate)(const struct in_addr *addr)) {
83
3b5a4fc6
YW
84 int r, c = 0;
85
86 assert(s);
87 assert(n == 0 || addresses);
88
89 for (size_t i = 0; i < n; i++) {
90 if (predicate && !predicate(&addresses[i]))
91 continue;
92 r = ordered_set_put_in4_addr(s, addresses+i);
93 if (r < 0)
94 return r;
95
96 c += r;
97 }
98
99 return c;
100}
101
102int manager_save(Manager *m) {
103 _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL;
104 const char *operstate_str, *carrier_state_str, *address_state_str;
105 LinkOperationalState operstate = LINK_OPERSTATE_OFF;
106 LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
107 LinkAddressState address_state = LINK_ADDRESS_STATE_OFF;
d23a66f2 108 _cleanup_(unlink_and_freep) char *temp_path = NULL;
3b5a4fc6
YW
109 _cleanup_strv_free_ char **p = NULL;
110 _cleanup_fclose_ FILE *f = NULL;
111 Link *link;
112 int r;
113
114 assert(m);
115 assert(m->state_file);
116
117 /* We add all NTP and DNS server to a set, to filter out duplicates */
118 dns = ordered_set_new(&string_hash_ops);
119 if (!dns)
120 return -ENOMEM;
121
122 ntp = ordered_set_new(&string_hash_ops);
123 if (!ntp)
124 return -ENOMEM;
125
126 sip = ordered_set_new(&string_hash_ops);
127 if (!sip)
128 return -ENOMEM;
129
130 search_domains = ordered_set_new(&dns_name_hash_ops);
131 if (!search_domains)
132 return -ENOMEM;
133
134 route_domains = ordered_set_new(&dns_name_hash_ops);
135 if (!route_domains)
136 return -ENOMEM;
137
138 HASHMAP_FOREACH(link, m->links) {
139 const struct in_addr *addresses;
140
141 if (link->flags & IFF_LOOPBACK)
142 continue;
143
144 if (link->operstate > operstate)
145 operstate = link->operstate;
146
147 if (link->carrier_state > carrier_state)
148 carrier_state = link->carrier_state;
149
150 if (link->address_state > address_state)
151 address_state = link->address_state;
152
153 if (!link->network)
154 continue;
155
156 /* First add the static configured entries */
f5fbe71d 157 if (link->n_dns != UINT_MAX)
3b5a4fc6
YW
158 r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns);
159 else
160 r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns);
161 if (r < 0)
162 return r;
163
164 r = ordered_set_put_strdupv(ntp, link->ntp ?: link->network->ntp);
165 if (r < 0)
166 return r;
167
168 r = ordered_set_put_string_set(search_domains, link->search_domains ?: link->network->search_domains);
169 if (r < 0)
170 return r;
171
172 r = ordered_set_put_string_set(route_domains, link->route_domains ?: link->network->route_domains);
173 if (r < 0)
174 return r;
175
176 if (!link->dhcp_lease)
177 continue;
178
179 /* Secondly, add the entries acquired via DHCP */
180 if (link->network->dhcp_use_dns) {
181 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
182 if (r > 0) {
183 r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local);
184 if (r < 0)
185 return r;
186 } else if (r < 0 && r != -ENODATA)
187 return r;
188 }
189
190 if (link->network->dhcp_use_ntp) {
191 r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
192 if (r > 0) {
193 r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local);
194 if (r < 0)
195 return r;
196 } else if (r < 0 && r != -ENODATA)
197 return r;
198 }
199
200 if (link->network->dhcp_use_sip) {
201 r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
202 if (r > 0) {
203 r = ordered_set_put_in4_addrv(sip, addresses, r, in4_addr_is_non_local);
204 if (r < 0)
205 return r;
206 } else if (r < 0 && r != -ENODATA)
207 return r;
208 }
209
210 if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
211 const char *domainname;
212 char **domains = NULL;
213
214 OrderedSet *target_domains = (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) ? search_domains : route_domains;
215 r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
216 if (r >= 0) {
217 r = ordered_set_put_strdup(target_domains, domainname);
218 if (r < 0)
219 return r;
220 } else if (r != -ENODATA)
221 return r;
222
223 r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains);
224 if (r >= 0) {
225 r = ordered_set_put_strdupv(target_domains, domains);
226 if (r < 0)
227 return r;
228 } else if (r != -ENODATA)
229 return r;
230 }
231 }
232
233 if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED)
234 carrier_state = LINK_CARRIER_STATE_CARRIER;
235
236 operstate_str = link_operstate_to_string(operstate);
237 assert(operstate_str);
238
239 carrier_state_str = link_carrier_state_to_string(carrier_state);
240 assert(carrier_state_str);
241
242 address_state_str = link_address_state_to_string(address_state);
243 assert(address_state_str);
244
245 r = fopen_temporary(m->state_file, &f, &temp_path);
246 if (r < 0)
247 return r;
248
249 (void) fchmod(fileno(f), 0644);
250
251 fprintf(f,
252 "# This is private data. Do not parse.\n"
253 "OPER_STATE=%s\n"
254 "CARRIER_STATE=%s\n"
255 "ADDRESS_STATE=%s\n",
256 operstate_str, carrier_state_str, address_state_str);
257
258 ordered_set_print(f, "DNS=", dns);
259 ordered_set_print(f, "NTP=", ntp);
260 ordered_set_print(f, "SIP=", sip);
261 ordered_set_print(f, "DOMAINS=", search_domains);
262 ordered_set_print(f, "ROUTE_DOMAINS=", route_domains);
263
264 r = fflush_and_check(f);
265 if (r < 0)
d23a66f2 266 return r;
3b5a4fc6
YW
267
268 r = conservative_rename(temp_path, m->state_file);
269 if (r < 0)
d23a66f2
YW
270 return r;
271
272 temp_path = mfree(temp_path);
3b5a4fc6
YW
273
274 if (m->operational_state != operstate) {
275 m->operational_state = operstate;
276 if (strv_extend(&p, "OperationalState") < 0)
277 log_oom();
278 }
279
280 if (m->carrier_state != carrier_state) {
281 m->carrier_state = carrier_state;
282 if (strv_extend(&p, "CarrierState") < 0)
283 log_oom();
284 }
285
286 if (m->address_state != address_state) {
287 m->address_state = address_state;
288 if (strv_extend(&p, "AddressState") < 0)
289 log_oom();
290 }
291
292 if (p) {
293 r = manager_send_changed_strv(m, p);
294 if (r < 0)
d23a66f2 295 log_warning_errno(r, "Could not emit changed properties, ignoring: %m");
3b5a4fc6
YW
296 }
297
298 m->dirty = false;
299
300 return 0;
3b5a4fc6
YW
301}
302
303static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
304 bool space = false;
305 Link *link;
306
307 assert(f);
308 assert(prefix);
309
310 if (hashmap_isempty(h))
311 return;
312
313 fputs(prefix, f);
314 HASHMAP_FOREACH(link, h) {
315 if (space)
316 fputc(' ', f);
317
318 fprintf(f, "%i", link->ifindex);
319 space = true;
320 }
321
322 fputc('\n', f);
323}
324
325static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) {
326 for (unsigned j = 0; j < n_dns; j++) {
327 const char *str;
328
329 if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex)
330 continue;
331
332 str = in_addr_full_to_string(dns[j]);
333 if (!str)
334 continue;
335
336 if (*space)
337 fputc(' ', f);
338 fputs(str, f);
339 *space = true;
340 }
341}
342
343static void serialize_addresses(
344 FILE *f,
345 const char *lvalue,
346 bool *space,
347 char **addresses,
348 sd_dhcp_lease *lease,
349 bool conditional,
350 sd_dhcp_lease_server_type_t what,
351 sd_dhcp6_lease *lease6,
352 bool conditional6,
353 int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**),
354 int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) {
3b5a4fc6
YW
355
356 bool _space = false;
44e1f7e3
YW
357 int r;
358
3b5a4fc6
YW
359 if (!space)
360 space = &_space;
361
362 if (lvalue)
363 fprintf(f, "%s=", lvalue);
364 fputstrv(f, addresses, NULL, space);
365
366 if (lease && conditional) {
367 const struct in_addr *lease_addresses;
368
369 r = sd_dhcp_lease_get_servers(lease, what, &lease_addresses);
370 if (r > 0)
371 serialize_in_addrs(f, lease_addresses, r, space, in4_addr_is_non_local);
372 }
373
374 if (lease6 && conditional6 && lease6_get_addr) {
375 const struct in6_addr *in6_addrs;
376
377 r = lease6_get_addr(lease6, &in6_addrs);
378 if (r > 0)
379 serialize_in6_addrs(f, in6_addrs, r, space);
380 }
381
382 if (lease6 && conditional6 && lease6_get_fqdn) {
383 char **in6_hosts;
384
385 r = lease6_get_fqdn(lease6, &in6_hosts);
386 if (r > 0)
387 fputstrv(f, in6_hosts, NULL, space);
388 }
389
390 if (lvalue)
391 fputc('\n', f);
392}
393
394int link_save(Link *link) {
395 const char *admin_state, *oper_state, *carrier_state, *address_state;
d23a66f2 396 _cleanup_(unlink_and_freep) char *temp_path = NULL;
3b5a4fc6
YW
397 _cleanup_fclose_ FILE *f = NULL;
398 int r;
399
400 assert(link);
401 assert(link->state_file);
402 assert(link->lease_file);
403 assert(link->manager);
404
d23a66f2 405 if (link->state == LINK_STATE_LINGER)
3b5a4fc6 406 return 0;
3b5a4fc6
YW
407
408 link_lldp_save(link);
409
410 admin_state = link_state_to_string(link->state);
411 assert(admin_state);
412
413 oper_state = link_operstate_to_string(link->operstate);
414 assert(oper_state);
415
416 carrier_state = link_carrier_state_to_string(link->carrier_state);
417 assert(carrier_state);
418
419 address_state = link_address_state_to_string(link->address_state);
420 assert(address_state);
421
422 r = fopen_temporary(link->state_file, &f, &temp_path);
423 if (r < 0)
d23a66f2 424 return r;
3b5a4fc6
YW
425
426 (void) fchmod(fileno(f), 0644);
427
428 fprintf(f,
429 "# This is private data. Do not parse.\n"
430 "ADMIN_STATE=%s\n"
431 "OPER_STATE=%s\n"
432 "CARRIER_STATE=%s\n"
433 "ADDRESS_STATE=%s\n",
434 admin_state, oper_state, carrier_state, address_state);
435
436 if (link->network) {
437 char **dhcp6_domains = NULL, **dhcp_domains = NULL;
438 const char *dhcp_domainname = NULL, *p;
439 bool space;
440
441 fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
442 yes_no(link->network->required_for_online));
443
444 LinkOperationalStateRange st = link->network->required_operstate_for_online;
445 fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n",
446 strempty(link_operstate_to_string(st.min)),
447 st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
448 st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
449
450 fprintf(f, "ACTIVATION_POLICY=%s\n",
451 activation_policy_to_string(link->network->activation_policy));
452
453 fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
454
455 /************************************************************/
456
457 fputs("DNS=", f);
458 space = false;
f5fbe71d 459 if (link->n_dns != UINT_MAX)
3b5a4fc6
YW
460 link_save_dns(link, f, link->dns, link->n_dns, &space);
461 else
462 link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
463
464 serialize_addresses(f, NULL, &space,
465 NULL,
466 link->dhcp_lease,
467 link->network->dhcp_use_dns,
468 SD_DHCP_LEASE_DNS,
469 link->dhcp6_lease,
470 link->network->dhcp6_use_dns,
471 sd_dhcp6_lease_get_dns,
472 NULL);
473
474 /* Make sure to flush out old entries before we use the NDisc data */
475 ndisc_vacuum(link);
476
477 if (link->network->ipv6_accept_ra_use_dns && link->ndisc_rdnss) {
478 NDiscRDNSS *dd;
479
480 SET_FOREACH(dd, link->ndisc_rdnss)
481 serialize_in6_addrs(f, &dd->address, 1, &space);
482 }
483
484 fputc('\n', f);
485
486 /************************************************************/
487
488 serialize_addresses(f, "NTP", NULL,
489 link->ntp ?: link->network->ntp,
490 link->dhcp_lease,
491 link->network->dhcp_use_ntp,
492 SD_DHCP_LEASE_NTP,
493 link->dhcp6_lease,
494 link->network->dhcp6_use_ntp,
495 sd_dhcp6_lease_get_ntp_addrs,
496 sd_dhcp6_lease_get_ntp_fqdn);
497
498 serialize_addresses(f, "SIP", NULL,
499 NULL,
500 link->dhcp_lease,
501 link->network->dhcp_use_sip,
502 SD_DHCP_LEASE_SIP,
503 NULL, false, NULL, NULL);
504
505 /************************************************************/
506
507 if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
508 if (link->dhcp_lease) {
509 (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
510 (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains);
511 }
512 if (link->dhcp6_lease)
513 (void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains);
514 }
515
516 fputs("DOMAINS=", f);
517 space = false;
518 ORDERED_SET_FOREACH(p, link->search_domains ?: link->network->search_domains)
519 fputs_with_space(f, p, NULL, &space);
520
521 if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
522 if (dhcp_domainname)
523 fputs_with_space(f, dhcp_domainname, NULL, &space);
524 if (dhcp_domains)
525 fputstrv(f, dhcp_domains, NULL, &space);
526 if (dhcp6_domains)
527 fputstrv(f, dhcp6_domains, NULL, &space);
528 }
529
530 if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_YES) {
531 NDiscDNSSL *dd;
532
533 SET_FOREACH(dd, link->ndisc_dnssl)
534 fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
535 }
536
537 fputc('\n', f);
538
539 /************************************************************/
540
541 fputs("ROUTE_DOMAINS=", f);
542 space = false;
543 ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains)
544 fputs_with_space(f, p, NULL, &space);
545
546 if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
547 if (dhcp_domainname)
548 fputs_with_space(f, dhcp_domainname, NULL, &space);
549 if (dhcp_domains)
550 fputstrv(f, dhcp_domains, NULL, &space);
551 if (dhcp6_domains)
552 fputstrv(f, dhcp6_domains, NULL, &space);
553 }
554
555 if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_ROUTE) {
556 NDiscDNSSL *dd;
557
558 SET_FOREACH(dd, link->ndisc_dnssl)
559 fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
560 }
561
562 fputc('\n', f);
563
564 /************************************************************/
565
566 fprintf(f, "LLMNR=%s\n",
567 resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr));
568
569 /************************************************************/
570
571 fprintf(f, "MDNS=%s\n",
572 resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns));
573
574 /************************************************************/
575
576 int dns_default_route =
577 link->dns_default_route >= 0 ? link->dns_default_route :
578 link->network->dns_default_route;
579 if (dns_default_route >= 0)
580 fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(dns_default_route));
581
582 /************************************************************/
583
584 DnsOverTlsMode dns_over_tls_mode =
585 link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID ? link->dns_over_tls_mode :
586 link->network->dns_over_tls_mode;
587 if (dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
588 fprintf(f, "DNS_OVER_TLS=%s\n", dns_over_tls_mode_to_string(dns_over_tls_mode));
589
590 /************************************************************/
591
592 DnssecMode dnssec_mode =
593 link->dnssec_mode != _DNSSEC_MODE_INVALID ? link->dnssec_mode :
594 link->network->dnssec_mode;
595 if (dnssec_mode != _DNSSEC_MODE_INVALID)
596 fprintf(f, "DNSSEC=%s\n", dnssec_mode_to_string(dnssec_mode));
597
598 /************************************************************/
599
600 Set *nta_anchors = link->dnssec_negative_trust_anchors;
601 if (set_isempty(nta_anchors))
602 nta_anchors = link->network->dnssec_negative_trust_anchors;
603
604 if (!set_isempty(nta_anchors)) {
605 const char *n;
606
607 fputs("DNSSEC_NTA=", f);
608 space = false;
609 SET_FOREACH(n, nta_anchors)
610 fputs_with_space(f, n, NULL, &space);
611 fputc('\n', f);
612 }
613 }
614
615 print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links);
616 print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links);
617
618 if (link->dhcp_lease) {
619 r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
620 if (r < 0)
d23a66f2 621 return r;
3b5a4fc6
YW
622
623 fprintf(f,
624 "DHCP_LEASE=%s\n",
625 link->lease_file);
626 } else
627 (void) unlink(link->lease_file);
628
629 r = link_serialize_dhcp6_client(link, f);
630 if (r < 0)
d23a66f2 631 return r;
3b5a4fc6
YW
632
633 r = fflush_and_check(f);
634 if (r < 0)
d23a66f2 635 return r;
3b5a4fc6
YW
636
637 r = conservative_rename(temp_path, link->state_file);
638 if (r < 0)
d23a66f2 639 return r;
3b5a4fc6 640
d23a66f2 641 temp_path = mfree(temp_path);
3b5a4fc6 642
d23a66f2 643 return 0;
3b5a4fc6
YW
644}
645
646void link_dirty(Link *link) {
647 int r;
648
649 assert(link);
650 assert(link->manager);
651
652 /* The serialized state in /run is no longer up-to-date. */
653
654 /* Also mark manager dirty as link is dirty */
655 link->manager->dirty = true;
656
657 r = set_ensure_put(&link->manager->dirty_links, NULL, link);
658 if (r <= 0)
659 /* Ignore allocation errors and don't take another ref if the link was already dirty */
660 return;
661 link_ref(link);
662}
663
664void link_clean(Link *link) {
665 assert(link);
666 assert(link->manager);
667
668 /* The serialized state in /run is up-to-date */
669
670 link_unref(set_remove(link->manager->dirty_links, link));
671}
672
673int link_save_and_clean(Link *link) {
674 int r;
675
676 r = link_save(link);
677 if (r < 0)
678 return r;
679
680 link_clean(link);
681 return 0;
682}