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