]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
network: DHCPv4 client: add support to send arbitary option and data
[thirdparty/systemd.git] / src / network / networkd-network.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
f579559b 2
9aa5d8ba
YW
3#include <net/if.h>
4#include <netinet/in.h>
01234e1f 5#include <linux/netdevice.h>
69a93e7d 6
b5efdb8a 7#include "alloc-util.h"
f579559b
TG
8#include "conf-files.h"
9#include "conf-parser.h"
37de2509 10#include "dns-domain.h"
3ffd4af2 11#include "fd-util.h"
07630cea 12#include "hostname-util.h"
88295a05 13#include "in-addr-util.h"
fc2f9534 14#include "network-internal.h"
23f53b99 15#include "networkd-manager.h"
3ffd4af2 16#include "networkd-network.h"
6bedfcbb 17#include "parse-util.h"
8a516214 18#include "set.h"
cebe1257 19#include "socket-util.h"
8fcde012 20#include "stat-util.h"
8b43440b 21#include "string-table.h"
07630cea 22#include "string-util.h"
700f1186 23#include "strv.h"
07630cea 24#include "util.h"
f579559b 25
c448459d
ZJS
26/* Let's assume that anything above this number is a user misconfiguration. */
27#define MAX_NTP_SERVERS 128
28
add8d07d 29/* Set defaults following RFC7844 */
30void network_apply_anonymize_if_set(Network *network) {
31 if (!network->dhcp_anonymize)
32 return;
33 /* RFC7844 3.7
34 SHOULD NOT send the Host Name option */
35 network->dhcp_send_hostname = false;
36 /* RFC7844 section 3.:
37 MAY contain the Client Identifier option
38 Section 3.5:
39 clients MUST use client identifiers based solely
40 on the link-layer address */
41 /* NOTE: Using MAC, as it does not reveal extra information,
42 * and some servers might not answer if this option is not sent */
43 network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
44 /* RFC 7844 3.10:
45 SHOULD NOT use the Vendor Class Identifier option */
727ba17f 46 network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
add8d07d 47 /* RFC7844 section 3.6.:
48 The client intending to protect its privacy SHOULD only request a
49 minimal number of options in the PRL and SHOULD also randomly shuffle
50 the ordering of option codes in the PRL. If this random ordering
51 cannot be implemented, the client MAY order the option codes in the
52 PRL by option code number (lowest to highest).
53 */
54 /* NOTE: dhcp_use_mtu is false by default,
55 * though it was not initiallized to any value in network_load_one.
56 * Maybe there should be another var called *send*?
57 * (to use the MTU sent by the server but to do not send
58 * the option in the PRL). */
59 network->dhcp_use_mtu = false;
28522b0d 60 /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
61 * but this is needed to use them. */
62 network->dhcp_use_routes = true;
add8d07d 63 /* RFC7844 section 3.6.
64 * same comments as previous option */
65 network->dhcp_use_timezone = false;
66}
67
cebe1257
YW
68static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret_netdev) {
69 const char *kind_string;
70 NetDev *netdev;
71 int r;
72
96db6412
YW
73 /* For test-networkd-conf, the check must be earlier than the assertions. */
74 if (!name)
75 return 0;
76
cebe1257
YW
77 assert(network);
78 assert(network->manager);
79 assert(network->filename);
80 assert(ret_netdev);
81
cebe1257
YW
82 if (kind == _NETDEV_KIND_TUNNEL)
83 kind_string = "tunnel";
84 else {
85 kind_string = netdev_kind_to_string(kind);
86 if (!kind_string)
87 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
88 "%s: Invalid NetDev kind of %s, ignoring assignment.",
89 network->filename, name);
90 }
91
92 r = netdev_get(network->manager, name, &netdev);
93 if (r < 0)
94 return log_error_errno(r, "%s: %s NetDev could not be found, ignoring assignment.",
95 network->filename, name);
96
97 if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL &&
98 IN_SET(netdev->kind,
99 NETDEV_KIND_IPIP,
100 NETDEV_KIND_SIT,
101 NETDEV_KIND_GRE,
102 NETDEV_KIND_GRETAP,
103 NETDEV_KIND_IP6GRE,
104 NETDEV_KIND_IP6GRETAP,
105 NETDEV_KIND_VTI,
106 NETDEV_KIND_VTI6,
9282f75b
YW
107 NETDEV_KIND_IP6TNL,
108 NETDEV_KIND_ERSPAN)))
cebe1257
YW
109 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
110 "%s: NetDev %s is not a %s, ignoring assignment",
111 network->filename, name, kind_string);
112
113 *ret_netdev = netdev_ref(netdev);
114 return 1;
115}
116
117static int network_resolve_stacked_netdevs(Network *network) {
118 void *name, *kind;
119 Iterator i;
120 int r;
121
122 assert(network);
123
124 HASHMAP_FOREACH_KEY(kind, name, network->stacked_netdev_names, i) {
125 _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
126
127 r = network_resolve_netdev_one(network, name, PTR_TO_INT(kind), &netdev);
128 if (r <= 0)
129 continue;
130
131 r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
132 if (r < 0)
133 return log_oom();
134
135 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
136 if (r < 0)
137 return log_error_errno(r, "%s: Failed to add NetDev '%s' to network: %m",
138 network->filename, (const char *) name);
139
140 netdev = NULL;
141 }
142
143 return 0;
144}
145
96db6412 146int network_verify(Network *network) {
4bec2f23
YW
147 Address *address, *address_next;
148 Route *route, *route_next;
fcbf4cb7
YW
149 FdbEntry *fdb, *fdb_next;
150 Neighbor *neighbor, *neighbor_next;
151 AddressLabel *label, *label_next;
152 Prefix *prefix, *prefix_next;
153 RoutingPolicyRule *rule, *rule_next;
0321cea7
YW
154
155 assert(network);
156 assert(network->filename);
157
84ea567e
YW
158 if (set_isempty(network->match_mac) && strv_isempty(network->match_path) &&
159 strv_isempty(network->match_driver) && strv_isempty(network->match_type) &&
44005bfb
YW
160 strv_isempty(network->match_name) && strv_isempty(network->match_property) &&
161 !network->conditions)
84ea567e
YW
162 log_warning("%s: No valid settings found in the [Match] section. "
163 "The file will match all interfaces. "
164 "If that is intended, please add Name=* in the [Match] section.",
165 network->filename);
166
cebe1257 167 /* skip out early if configuration does not match the environment */
c4f58dea 168 if (!condition_test_list(network->conditions, NULL, NULL, NULL))
cebe1257 169 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
3772cfde
ZJS
170 "%s: Conditions in the file do not match the system environment, skipping.",
171 network->filename);
cebe1257
YW
172
173 (void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond);
174 (void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge);
175 (void) network_resolve_netdev_one(network, network->vrf_name, NETDEV_KIND_VRF, &network->vrf);
176 (void) network_resolve_stacked_netdevs(network);
177
178 /* Free unnecessary entries. */
179 network->bond_name = mfree(network->bond_name);
180 network->bridge_name = mfree(network->bridge_name);
181 network->vrf_name = mfree(network->vrf_name);
182 network->stacked_netdev_names = hashmap_free_free_key(network->stacked_netdev_names);
183
0321cea7
YW
184 if (network->bond) {
185 /* Bonding slave does not support addressing. */
186 if (network->ipv6_accept_ra > 0) {
ab24039f
ZJS
187 log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.",
188 network->filename);
0321cea7
YW
189 network->ipv6_accept_ra = 0;
190 }
191 if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) {
ab24039f
ZJS
192 log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.",
193 network->filename);
0321cea7
YW
194 network->link_local = ADDRESS_FAMILY_NO;
195 }
196 if (network->dhcp != ADDRESS_FAMILY_NO) {
ab24039f
ZJS
197 log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.",
198 network->filename);
0321cea7
YW
199 network->dhcp = ADDRESS_FAMILY_NO;
200 }
201 if (network->dhcp_server) {
ab24039f
ZJS
202 log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.",
203 network->filename);
0321cea7
YW
204 network->dhcp_server = false;
205 }
206 if (network->n_static_addresses > 0) {
ab24039f
ZJS
207 log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
208 network->filename);
0321cea7
YW
209 while ((address = network->static_addresses))
210 address_free(address);
211 }
212 if (network->n_static_routes > 0) {
ab24039f
ZJS
213 log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
214 network->filename);
0321cea7
YW
215 while ((route = network->static_routes))
216 route_free(route);
217 }
218 }
219
220 if (network->link_local < 0)
8f191801
YW
221 network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
222
44013aa4
YW
223 if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
224 if (network->ipv6_accept_ra > 0) {
225 log_warning("%s: IPv6AcceptRA= is enabled by the .network file but IPv6 link local addressing is disabled. "
226 "Disabling IPv6AcceptRA=.", network->filename);
227 network->ipv6_accept_ra = false;
228 }
229
230 if (FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6)) {
231 log_warning("%s: DHCPv6 client is enabled by the .network file but IPv6 link local addressing is disabled. "
232 "Disabling DHCPv6 client.", network->filename);
233 SET_FLAG(network->dhcp, ADDRESS_FAMILY_IPV6, false);
234 }
235
236 if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE) {
237 log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link local addressing is disabled. "
238 "Disabling IPv6PrefixDelegation=.", network->filename);
239 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
240 }
241 }
242
29e81083
YW
243 if (FLAGS_SET(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4) &&
244 !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4)) {
245 log_warning("%s: fallback assignment of IPv4 link local address is enabled but DHCPv4 is disabled. "
246 "Disabling the fallback assignment.", network->filename);
247 SET_FLAG(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4, false);
248 }
249
8f191801
YW
250 if (network->ipv6_accept_ra < 0 && network->bridge)
251 network->ipv6_accept_ra = false;
0321cea7
YW
252
253 /* IPMasquerade=yes implies IPForward=yes */
254 if (network->ip_masquerade)
255 network->ip_forward |= ADDRESS_FAMILY_IPV4;
256
933c70a0 257 if (network->mtu > 0 && network->dhcp_use_mtu) {
0321cea7
YW
258 log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. "
259 "Disabling UseMTU=.", network->filename);
260 network->dhcp_use_mtu = false;
261 }
262
7da377ef
SS
263 if (network->dhcp_critical >= 0) {
264 if (network->keep_configuration >= 0)
265 log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
266 "Ignoring CriticalConnection=.", network->filename);
267 else if (network->dhcp_critical)
268 /* CriticalConnection=yes also preserve foreign static configurations. */
269 network->keep_configuration = KEEP_CONFIGURATION_YES;
270 else
80060352 271 network->keep_configuration = KEEP_CONFIGURATION_NO;
7da377ef
SS
272 }
273
274 if (network->keep_configuration < 0)
80060352 275 network->keep_configuration = KEEP_CONFIGURATION_NO;
7da377ef 276
4bec2f23 277 LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses)
fcbf4cb7 278 if (address_section_verify(address) < 0)
4bec2f23 279 address_free(address);
4bec2f23 280
fcbf4cb7
YW
281 LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes)
282 if (route_section_verify(route, network) < 0)
4bec2f23 283 route_free(route);
0321cea7 284
fcbf4cb7
YW
285 LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries)
286 if (section_is_invalid(fdb->section))
287 fdb_entry_free(fdb);
288
289 LIST_FOREACH_SAFE(neighbors, neighbor, neighbor_next, network->neighbors)
044d4b40 290 if (neighbor_section_verify(neighbor) < 0)
fcbf4cb7
YW
291 neighbor_free(neighbor);
292
293 LIST_FOREACH_SAFE(labels, label, label_next, network->address_labels)
294 if (section_is_invalid(label->section))
295 address_label_free(label);
296
297 LIST_FOREACH_SAFE(prefixes, prefix, prefix_next, network->static_prefixes)
298 if (section_is_invalid(prefix->section))
299 prefix_free(prefix);
300
714a199e
YW
301 LIST_FOREACH_SAFE(prefixes, prefix, prefix_next, network->static_route_prefixes)
302 if (section_is_invalid(prefix->section))
303 prefix_free(prefix);
304
fcbf4cb7 305 LIST_FOREACH_SAFE(rules, rule, rule_next, network->rules)
0aabccc8 306 if (routing_policy_rule_section_verify(rule) < 0)
fcbf4cb7 307 routing_policy_rule_free(rule);
4912ab77 308
0321cea7
YW
309 return 0;
310}
311
212bd73c 312int network_load_one(Manager *manager, const char *filename) {
838b2f7a 313 _cleanup_free_ char *fname = NULL, *name = NULL;
35ac3b76 314 _cleanup_(network_unrefp) Network *network = NULL;
f579559b 315 _cleanup_fclose_ FILE *file = NULL;
047a0dac 316 const char *dropin_dirname;
838b2f7a 317 char *d;
f579559b
TG
318 int r;
319
bf1bc670
TA
320 assert(manager);
321 assert(filename);
322
f579559b
TG
323 file = fopen(filename, "re");
324 if (!file) {
325 if (errno == ENOENT)
326 return 0;
1e7a0e21
LP
327
328 return -errno;
f579559b
TG
329 }
330
ed88bcfb
ZJS
331 if (null_or_empty_fd(fileno(file))) {
332 log_debug("Skipping empty file: %s", filename);
6916ec29
TG
333 return 0;
334 }
335
838b2f7a
YW
336 fname = strdup(filename);
337 if (!fname)
338 return log_oom();
339
340 name = strdup(basename(filename));
341 if (!name)
342 return log_oom();
343
344 d = strrchr(name, '.');
345 if (!d)
346 return -EINVAL;
347
348 *d = '\0';
349
350 dropin_dirname = strjoina(name, ".network.d");
351
17f9c355 352 network = new(Network, 1);
f579559b
TG
353 if (!network)
354 return log_oom();
355
17f9c355 356 *network = (Network) {
838b2f7a
YW
357 .filename = TAKE_PTR(fname),
358 .name = TAKE_PTR(name),
17f9c355 359
715d398e 360 .manager = manager,
35ac3b76
YW
361 .n_ref = 1,
362
17f9c355 363 .required_for_online = true,
4ac77d63 364 .required_operstate_for_online = LINK_OPERSTATE_DEGRADED,
17f9c355 365 .dhcp = ADDRESS_FAMILY_NO,
7da377ef 366 .dhcp_critical = -1,
17f9c355 367 .dhcp_use_ntp = true,
299d578f 368 .dhcp_use_sip = true,
17f9c355
YW
369 .dhcp_use_dns = true,
370 .dhcp_use_hostname = true,
371 .dhcp_use_routes = true,
5238e957 372 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
17f9c355 373 .dhcp_send_hostname = true,
5f3b5f19 374 .dhcp_send_release = true,
17f9c355
YW
375 /* To enable/disable RFC7844 Anonymity Profiles */
376 .dhcp_anonymize = false,
377 .dhcp_route_metric = DHCP_ROUTE_METRIC,
c9c908a6 378 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
17f9c355
YW
379 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
380 .dhcp_route_table = RT_TABLE_MAIN,
381 .dhcp_route_table_set = false,
382 /* NOTE: from man: UseMTU=... Defaults to false*/
383 .dhcp_use_mtu = false,
384 /* NOTE: from man: UseTimezone=... Defaults to "no".*/
385 .dhcp_use_timezone = false,
386 .rapid_commit = true,
387
caa8ca42
SS
388 .dhcp6_use_ntp = true,
389 .dhcp6_use_dns = true,
390
17f9c355
YW
391 .dhcp_server_emit_dns = true,
392 .dhcp_server_emit_ntp = true,
299d578f 393 .dhcp_server_emit_sip = true,
17f9c355
YW
394 .dhcp_server_emit_router = true,
395 .dhcp_server_emit_timezone = true,
396
397 .router_emit_dns = true,
398 .router_emit_domains = true,
399
400 .use_bpdu = -1,
401 .hairpin = -1,
402 .fast_leave = -1,
403 .allow_port_to_be_root = -1,
404 .unicast_flood = -1,
7f15b714 405 .multicast_flood = -1,
d3aa8b49 406 .multicast_to_unicast = -1,
7f15b714
TJ
407 .neighbor_suppression = -1,
408 .learning = -1,
1087623b
SS
409 .bridge_proxy_arp = -1,
410 .bridge_proxy_arp_wifi = -1,
17f9c355 411 .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
0fadb2a4 412 .multicast_router = _MULTICAST_ROUTER_INVALID,
17f9c355
YW
413
414 .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
415
7ece6f58 416 .dns_default_route = -1,
17f9c355
YW
417 .llmnr = RESOLVE_SUPPORT_YES,
418 .mdns = RESOLVE_SUPPORT_NO,
419 .dnssec_mode = _DNSSEC_MODE_INVALID,
420 .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
421
0321cea7 422 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
2d792895 423 .link_local = _ADDRESS_FAMILY_INVALID,
17f9c355
YW
424
425 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
426 .ipv6_accept_ra = -1,
427 .ipv6_dad_transmits = -1,
428 .ipv6_hop_limit = -1,
429 .ipv6_proxy_ndp = -1,
430 .duid.type = _DUID_TYPE_INVALID,
431 .proxy_arp = -1,
432 .arp = -1,
433 .multicast = -1,
434 .allmulticast = -1,
435 .ipv6_accept_ra_use_dns = true,
062c2eea
SS
436 .ipv6_accept_ra_use_autonomous_prefix = true,
437 .ipv6_accept_ra_use_onlink_prefix = true,
17f9c355 438 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
d5fa3339 439 .ipv6_accept_ra_route_table_set = false,
c423be28 440
7da377ef
SS
441 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
442
c423be28 443 .can_triple_sampling = -1,
afe42aef 444 .ip_service_type = -1,
17f9c355 445 };
f579559b 446
dc0d4078 447 r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
23bb31aa
ZJS
448 "Match\0"
449 "Link\0"
450 "Network\0"
451 "Address\0"
e4a71bf3 452 "Neighbor\0"
95b74ef6 453 "IPv6AddressLabel\0"
bce67bbe 454 "RoutingPolicyRule\0"
23bb31aa
ZJS
455 "Route\0"
456 "DHCP\0"
457 "DHCPv4\0" /* compat */
caa8ca42 458 "DHCPv6\0"
23bb31aa
ZJS
459 "DHCPServer\0"
460 "IPv6AcceptRA\0"
a0e5c15d 461 "IPv6NDPProxyAddress\0"
23bb31aa
ZJS
462 "Bridge\0"
463 "BridgeFDB\0"
9d5d0090 464 "BridgeVLAN\0"
7d5cac19 465 "IPv6PrefixDelegation\0"
06828bb6 466 "IPv6Prefix\0"
203d4df5 467 "IPv6RoutePrefix\0"
06828bb6 468 "CAN\0",
23bb31aa 469 config_item_perf_lookup, network_network_gperf_lookup,
bcde742e 470 CONFIG_PARSE_WARN, network);
102bc043 471 if (r < 0)
f579559b 472 return r;
f579559b 473
add8d07d 474 network_apply_anonymize_if_set(network);
475
fa7cd711
YW
476 r = network_add_ipv4ll_route(network);
477 if (r < 0)
478 log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename);
479
5d5003ab
YW
480 r = network_add_default_route_on_device(network);
481 if (r < 0)
482 log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
483 network->filename);
484
715d398e 485 r = ordered_hashmap_ensure_allocated(&manager->networks, &string_hash_ops);
dbffab87
TG
486 if (r < 0)
487 return r;
488
715d398e 489 r = ordered_hashmap_put(manager->networks, network->name, network);
dbffab87
TG
490 if (r < 0)
491 return r;
492
0321cea7
YW
493 if (network_verify(network) < 0)
494 return 0;
b3070dc0 495
f579559b 496 network = NULL;
f579559b
TG
497 return 0;
498}
499
500int network_load(Manager *manager) {
477e73b5
ZJS
501 _cleanup_strv_free_ char **files = NULL;
502 char **f;
f579559b
TG
503 int r;
504
505 assert(manager);
506
715d398e 507 ordered_hashmap_clear_with_destructor(manager->networks, network_unref);
f579559b 508
dc0d4078 509 r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
f647962d
MS
510 if (r < 0)
511 return log_error_errno(r, "Failed to enumerate network files: %m");
f579559b 512
715d398e 513 STRV_FOREACH(f, files) {
f579559b
TG
514 r = network_load_one(manager, *f);
515 if (r < 0)
be711082 516 log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
f579559b
TG
517 }
518
f579559b
TG
519 return 0;
520}
521
35ac3b76 522static Network *network_free(Network *network) {
a0e5c15d 523 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
bce67bbe
SS
524 RoutingPolicyRule *rule;
525 FdbEntry *fdb_entry;
e4a71bf3 526 Neighbor *neighbor;
95b74ef6 527 AddressLabel *label;
057abfd8 528 Prefix *prefix;
bce67bbe 529 Address *address;
bce67bbe 530 Route *route;
f579559b
TG
531
532 if (!network)
35ac3b76 533 return NULL;
f579559b
TG
534
535 free(network->filename);
536
e90d0374 537 set_free_free(network->match_mac);
5256e00e
TG
538 strv_free(network->match_path);
539 strv_free(network->match_driver);
540 strv_free(network->match_type);
541 strv_free(network->match_name);
44005bfb 542 strv_free(network->match_property);
c4f58dea 543 condition_free_list(network->conditions);
f579559b
TG
544
545 free(network->description);
edb85f0d 546 free(network->dhcp_vendor_class_identifier);
af1c0de0 547 strv_free(network->dhcp_user_class);
27cb34f5 548 free(network->dhcp_hostname);
727b5734 549 set_free(network->dhcp_black_listed_ip);
5bc945be 550 set_free(network->dhcp_request_options);
c106cc36
TG
551 free(network->mac);
552
b0e39c82 553 strv_free(network->ntp);
5512a963 554 free(network->dns);
299d578f 555 strv_free(network->sip);
5e2a51d5
ZJS
556 ordered_set_free_free(network->search_domains);
557 ordered_set_free_free(network->route_domains);
0d4ad91d 558 strv_free(network->bind_carrier);
cdd7812b 559
5e2a51d5 560 ordered_set_free_free(network->router_search_domains);
cdd7812b 561 free(network->router_dns);
e520ce64 562 set_free_free(network->ndisc_black_listed_prefix);
3bef724f 563
cebe1257
YW
564 free(network->bridge_name);
565 free(network->bond_name);
566 free(network->vrf_name);
567 hashmap_free_free_key(network->stacked_netdev_names);
47e2dc31 568 netdev_unref(network->bridge);
47e2dc31 569 netdev_unref(network->bond);
6cb955c6 570 netdev_unref(network->vrf);
fa6f1e54 571 hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
326cb406 572
f048a16b 573 while ((route = network->static_routes))
f579559b
TG
574 route_free(route);
575
f048a16b 576 while ((address = network->static_addresses))
f579559b
TG
577 address_free(address);
578
b98b483b
AR
579 while ((fdb_entry = network->static_fdb_entries))
580 fdb_entry_free(fdb_entry);
581
a0e5c15d
FK
582 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
583 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
584
e4a71bf3
WKI
585 while ((neighbor = network->neighbors))
586 neighbor_free(neighbor);
587
95b74ef6
SS
588 while ((label = network->address_labels))
589 address_label_free(label);
590
057abfd8
PF
591 while ((prefix = network->static_prefixes))
592 prefix_free(prefix);
593
bce67bbe
SS
594 while ((rule = network->rules))
595 routing_policy_rule_free(rule);
596
6ae115c1
TG
597 hashmap_free(network->addresses_by_section);
598 hashmap_free(network->routes_by_section);
b98b483b 599 hashmap_free(network->fdb_entries_by_section);
e4a71bf3 600 hashmap_free(network->neighbors_by_section);
95b74ef6 601 hashmap_free(network->address_labels_by_section);
057abfd8 602 hashmap_free(network->prefixes_by_section);
bce67bbe 603 hashmap_free(network->rules_by_section);
6ae115c1 604
dbffab87 605 if (network->manager) {
715d398e
YW
606 if (network->manager->networks && network->name)
607 ordered_hashmap_remove(network->manager->networks, network->name);
27dfc982
YW
608
609 if (network->manager->duids_requesting_uuid)
610 set_remove(network->manager->duids_requesting_uuid, &network->duid);
dbffab87
TG
611 }
612
613 free(network->name);
f579559b 614
8eb9058d 615 free(network->dhcp_server_timezone);
1a04db0f
LP
616 free(network->dhcp_server_dns);
617 free(network->dhcp_server_ntp);
299d578f 618 free(network->dhcp_server_sip);
8eb9058d 619
8a516214
LP
620 set_free_free(network->dnssec_negative_trust_anchors);
621
cb29c156
SS
622 ordered_hashmap_free(network->dhcp_send_options);
623
35ac3b76 624 return mfree(network);
f579559b
TG
625}
626
35ac3b76
YW
627DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free);
628
dbffab87
TG
629int network_get_by_name(Manager *manager, const char *name, Network **ret) {
630 Network *network;
631
632 assert(manager);
633 assert(name);
634 assert(ret);
635
715d398e 636 network = ordered_hashmap_get(manager->networks, name);
dbffab87
TG
637 if (!network)
638 return -ENOENT;
639
640 *ret = network;
641
642 return 0;
643}
644
51517f9e 645int network_get(Manager *manager, sd_device *device,
505f8da7
TG
646 const char *ifname, const struct ether_addr *address,
647 Network **ret) {
51517f9e 648 Network *network;
715d398e 649 Iterator i;
f579559b
TG
650
651 assert(manager);
f579559b 652 assert(ret);
af3aa302 653
715d398e 654 ORDERED_HASHMAP_FOREACH(network, manager->networks, i)
44005bfb
YW
655 if (net_match_config(network->match_mac, network->match_path, network->match_driver,
656 network->match_type, network->match_name, network->match_property,
b38de0e9 657 device, address, ifname)) {
24c083df 658 if (network->match_name && device) {
ca6038b8
TG
659 const char *attr;
660 uint8_t name_assign_type = NET_NAME_UNKNOWN;
661
51517f9e 662 if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
dc751688 663 (void) safe_atou8(attr, &name_assign_type);
32bc8adc
TG
664
665 if (name_assign_type == NET_NAME_ENUM)
a2fae7bb
TG
666 log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
667 ifname, network->filename);
32bc8adc 668 else
a2fae7bb 669 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 670 } else
a2fae7bb 671 log_debug("%s: found matching network '%s'", ifname, network->filename);
32bc8adc 672
f579559b
TG
673 *ret = network;
674 return 0;
675 }
f579559b
TG
676
677 *ret = NULL;
678
679 return -ENOENT;
680}
681
7d342c03 682int network_apply(Network *network, Link *link) {
c4a03a56
TG
683 assert(network);
684 assert(link);
685
c9c908a6 686 link->network = network_ref(network);
f579559b 687
5512a963 688 if (network->n_dns > 0 ||
3df9bec5 689 !strv_isempty(network->ntp) ||
5e2a51d5
ZJS
690 !ordered_set_isempty(network->search_domains) ||
691 !ordered_set_isempty(network->route_domains))
84de38c5 692 link_dirty(link);
3bef724f 693
f579559b
TG
694 return 0;
695}
02b59d57 696
adfeee49 697bool network_has_static_ipv6_configurations(Network *network) {
439689c6 698 Address *address;
adfeee49
YW
699 Route *route;
700 FdbEntry *fdb;
701 Neighbor *neighbor;
439689c6
SS
702
703 assert(network);
704
adfeee49 705 LIST_FOREACH(addresses, address, network->static_addresses)
439689c6
SS
706 if (address->family == AF_INET6)
707 return true;
adfeee49
YW
708
709 LIST_FOREACH(routes, route, network->static_routes)
710 if (route->family == AF_INET6)
711 return true;
712
713 LIST_FOREACH(static_fdb_entries, fdb, network->static_fdb_entries)
714 if (fdb->family == AF_INET6)
715 return true;
716
717 LIST_FOREACH(neighbors, neighbor, network->neighbors)
718 if (neighbor->family == AF_INET6)
719 return true;
720
721 if (!LIST_IS_EMPTY(network->address_labels))
722 return true;
723
724 if (!LIST_IS_EMPTY(network->static_prefixes))
725 return true;
439689c6
SS
726
727 return false;
728}
729
cebe1257 730int config_parse_stacked_netdev(const char *unit,
02b59d57
TG
731 const char *filename,
732 unsigned line,
733 const char *section,
734 unsigned section_line,
735 const char *lvalue,
736 int ltype,
737 const char *rvalue,
738 void *data,
739 void *userdata) {
95dba435
YW
740 _cleanup_free_ char *name = NULL;
741 NetDevKind kind = ltype;
cebe1257 742 Hashmap **h = data;
02b59d57
TG
743 int r;
744
745 assert(filename);
746 assert(lvalue);
747 assert(rvalue);
748 assert(data);
95dba435
YW
749 assert(IN_SET(kind,
750 NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
69c317a0 751 NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN,
98d20a17 752 NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL,
753 NETDEV_KIND_XFRM));
54abf461 754
cebe1257 755 if (!ifname_valid(rvalue)) {
3772cfde
ZJS
756 log_syntax(unit, LOG_ERR, filename, line, 0,
757 "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
758 return 0;
759 }
760
cebe1257
YW
761 name = strdup(rvalue);
762 if (!name)
763 return log_oom();
3e570042 764
cebe1257
YW
765 r = hashmap_ensure_allocated(h, &string_hash_ops);
766 if (r < 0)
767 return log_oom();
326cb406 768
cebe1257 769 r = hashmap_put(*h, name, INT_TO_PTR(kind));
83ec4592 770 if (r < 0)
3772cfde 771 log_syntax(unit, LOG_ERR, filename, line, r,
83ec4592
ZJS
772 "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
773 else if (r == 0)
774 log_syntax(unit, LOG_DEBUG, filename, line, r,
775 "NetDev '%s' specified twice, ignoring.", name);
776 else
777 name = NULL;
47e2dc31 778
fe6b2d55
TG
779 return 0;
780}
7951dea2 781
3df9bec5
LP
782int config_parse_domains(
783 const char *unit,
784 const char *filename,
785 unsigned line,
786 const char *section,
787 unsigned section_line,
788 const char *lvalue,
789 int ltype,
790 const char *rvalue,
791 void *data,
792 void *userdata) {
793
794 const char *p;
795 Network *n = data;
6192b846
TG
796 int r;
797
3df9bec5
LP
798 assert(n);
799 assert(lvalue);
800 assert(rvalue);
6192b846 801
3df9bec5 802 if (isempty(rvalue)) {
5e2a51d5
ZJS
803 n->search_domains = ordered_set_free_free(n->search_domains);
804 n->route_domains = ordered_set_free_free(n->route_domains);
3df9bec5
LP
805 return 0;
806 }
67272d15 807
3df9bec5
LP
808 p = rvalue;
809 for (;;) {
810 _cleanup_free_ char *w = NULL, *normalized = NULL;
811 const char *domain;
812 bool is_route;
813
814 r = extract_first_word(&p, &w, NULL, 0);
815 if (r < 0) {
ab24039f
ZJS
816 log_syntax(unit, LOG_ERR, filename, line, r,
817 "Failed to extract search or route domain, ignoring: %s", rvalue);
3df9bec5
LP
818 break;
819 }
820 if (r == 0)
821 break;
822
823 is_route = w[0] == '~';
824 domain = is_route ? w + 1 : w;
825
826 if (dns_name_is_root(domain) || streq(domain, "*")) {
ab24039f
ZJS
827 /* If the root domain appears as is, or the special token "*" is found, we'll
828 * consider this as routing domain, unconditionally. */
3df9bec5 829 is_route = true;
ab24039f
ZJS
830 domain = "."; /* make sure we don't allow empty strings, thus write the root
831 * domain as "." */
3df9bec5 832 } else {
7470cc4c 833 r = dns_name_normalize(domain, 0, &normalized);
3df9bec5 834 if (r < 0) {
ab24039f
ZJS
835 log_syntax(unit, LOG_ERR, filename, line, r,
836 "'%s' is not a valid domain name, ignoring.", domain);
3df9bec5
LP
837 continue;
838 }
839
840 domain = normalized;
841
842 if (is_localhost(domain)) {
ab24039f
ZJS
843 log_syntax(unit, LOG_ERR, filename, line, 0,
844 "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
845 domain);
37de2509 846 continue;
3df9bec5 847 }
37de2509 848 }
40274ed6 849
5e2a51d5
ZJS
850 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
851 r = ordered_set_ensure_allocated(set, &string_hash_ops);
852 if (r < 0)
853 return r;
854
855 r = ordered_set_put_strdup(*set, domain);
09451975
LP
856 if (r < 0)
857 return log_oom();
40274ed6 858 }
f15b6e5a 859
6192b846
TG
860 return 0;
861}
862
60c35566 863int config_parse_ipv6token(
7f77697a
TG
864 const char* unit,
865 const char *filename,
866 unsigned line,
867 const char *section,
868 unsigned section_line,
869 const char *lvalue,
870 int ltype,
871 const char *rvalue,
872 void *data,
873 void *userdata) {
874
875 union in_addr_union buffer;
876 struct in6_addr *token = data;
877 int r;
878
879 assert(filename);
880 assert(lvalue);
881 assert(rvalue);
882 assert(token);
883
884 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
885 if (r < 0) {
ab24039f
ZJS
886 log_syntax(unit, LOG_ERR, filename, line, r,
887 "Failed to parse IPv6 token, ignoring: %s", rvalue);
7f77697a
TG
888 return 0;
889 }
890
c606db69 891 if (in_addr_is_null(AF_INET6, &buffer)) {
ab24039f
ZJS
892 log_syntax(unit, LOG_ERR, filename, line, 0,
893 "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
7f77697a
TG
894 return 0;
895 }
896
897 if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
ab24039f
ZJS
898 log_syntax(unit, LOG_ERR, filename, line, 0,
899 "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
7f77697a
TG
900 return 0;
901 }
902
903 *token = buffer.in6;
904
905 return 0;
906}
8add5f79 907
49092e22 908static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
1f0d9695
LP
909 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
910 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
911 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
49092e22
SS
912};
913
b146ad71
YW
914DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
915 IPV6_PRIVACY_EXTENSIONS_YES);
49092e22
SS
916
917int config_parse_ipv6_privacy_extensions(
918 const char* unit,
919 const char *filename,
920 unsigned line,
921 const char *section,
922 unsigned section_line,
923 const char *lvalue,
924 int ltype,
925 const char *rvalue,
926 void *data,
927 void *userdata) {
928
b146ad71 929 IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data;
49092e22
SS
930
931 assert(filename);
932 assert(lvalue);
933 assert(rvalue);
934 assert(ipv6_privacy_extensions);
935
b146ad71
YW
936 s = ipv6_privacy_extensions_from_string(rvalue);
937 if (s < 0) {
938 if (streq(rvalue, "kernel"))
939 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
940 else {
941 log_syntax(unit, LOG_ERR, filename, line, 0,
942 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
943 return 0;
49092e22 944 }
49092e22
SS
945 }
946
b146ad71
YW
947 *ipv6_privacy_extensions = s;
948
49092e22
SS
949 return 0;
950}
a7d0ef44 951
1ac608c9
LP
952int config_parse_hostname(
953 const char *unit,
954 const char *filename,
955 unsigned line,
956 const char *section,
957 unsigned section_line,
958 const char *lvalue,
959 int ltype,
960 const char *rvalue,
961 void *data,
962 void *userdata) {
963
6528693a
YW
964 _cleanup_free_ char *hn = NULL;
965 char **hostname = data;
a7d0ef44
SS
966 int r;
967
968 assert(filename);
969 assert(lvalue);
970 assert(rvalue);
971
1ac608c9 972 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
a7d0ef44
SS
973 if (r < 0)
974 return r;
975
1ac608c9 976 if (!hostname_is_valid(hn, false)) {
ab24039f
ZJS
977 log_syntax(unit, LOG_ERR, filename, line, 0,
978 "Hostname is not valid, ignoring assignment: %s", rvalue);
a7d0ef44
SS
979 return 0;
980 }
981
6528693a
YW
982 r = dns_name_is_valid(hn);
983 if (r < 0) {
ab24039f
ZJS
984 log_syntax(unit, LOG_ERR, filename, line, r,
985 "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
6528693a
YW
986 return 0;
987 }
988 if (r == 0) {
ab24039f
ZJS
989 log_syntax(unit, LOG_ERR, filename, line, 0,
990 "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
6528693a
YW
991 return 0;
992 }
993
994 return free_and_replace(*hostname, hn);
a7d0ef44 995}
8eb9058d
LP
996
997int config_parse_timezone(
998 const char *unit,
999 const char *filename,
1000 unsigned line,
1001 const char *section,
1002 unsigned section_line,
1003 const char *lvalue,
1004 int ltype,
1005 const char *rvalue,
1006 void *data,
1007 void *userdata) {
1008
19f9e4e2
YW
1009 _cleanup_free_ char *tz = NULL;
1010 char **datap = data;
8eb9058d
LP
1011 int r;
1012
1013 assert(filename);
1014 assert(lvalue);
1015 assert(rvalue);
1016
1017 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
1018 if (r < 0)
1019 return r;
1020
089fb865 1021 if (!timezone_is_valid(tz, LOG_ERR)) {
ab24039f
ZJS
1022 log_syntax(unit, LOG_ERR, filename, line, 0,
1023 "Timezone is not valid, ignoring assignment: %s", rvalue);
8eb9058d
LP
1024 return 0;
1025 }
1026
19f9e4e2 1027 return free_and_replace(*datap, tz);
8eb9058d 1028}
1a04db0f 1029
53253824
SS
1030int config_parse_dns(
1031 const char *unit,
1032 const char *filename,
1033 unsigned line,
1034 const char *section,
1035 unsigned section_line,
1036 const char *lvalue,
1037 int ltype,
1038 const char *rvalue,
1039 void *data,
1040 void *userdata) {
1041
1042 Network *n = userdata;
1043 int r;
1044
1045 assert(filename);
1046 assert(lvalue);
1047 assert(rvalue);
1048
1049 for (;;) {
1050 _cleanup_free_ char *w = NULL;
1051 union in_addr_union a;
5512a963 1052 struct in_addr_data *m;
53253824
SS
1053 int family;
1054
5512a963 1055 r = extract_first_word(&rvalue, &w, NULL, 0);
53253824
SS
1056 if (r == -ENOMEM)
1057 return log_oom();
1058 if (r < 0) {
ab24039f
ZJS
1059 log_syntax(unit, LOG_ERR, filename, line, r,
1060 "Invalid syntax, ignoring: %s", rvalue);
53253824
SS
1061 break;
1062 }
5512a963
LP
1063 if (r == 0)
1064 break;
53253824
SS
1065
1066 r = in_addr_from_string_auto(w, &family, &a);
1067 if (r < 0) {
ab24039f
ZJS
1068 log_syntax(unit, LOG_ERR, filename, line, r,
1069 "Failed to parse dns server address, ignoring: %s", w);
53253824
SS
1070 continue;
1071 }
1072
62d74c78 1073 m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
5512a963 1074 if (!m)
53253824
SS
1075 return log_oom();
1076
5512a963
LP
1077 m[n->n_dns++] = (struct in_addr_data) {
1078 .family = family,
1079 .address = a,
1080 };
1081
1082 n->dns = m;
53253824
SS
1083 }
1084
1085 return 0;
1086}
1087
8a516214
LP
1088int config_parse_dnssec_negative_trust_anchors(
1089 const char *unit,
1090 const char *filename,
1091 unsigned line,
1092 const char *section,
1093 unsigned section_line,
1094 const char *lvalue,
1095 int ltype,
1096 const char *rvalue,
1097 void *data,
1098 void *userdata) {
1099
1100 const char *p = rvalue;
1101 Network *n = data;
1102 int r;
1103
3df9bec5 1104 assert(n);
8a516214
LP
1105 assert(lvalue);
1106 assert(rvalue);
1107
1108 if (isempty(rvalue)) {
1109 n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
1110 return 0;
1111 }
1112
1113 for (;;) {
1114 _cleanup_free_ char *w = NULL;
1115
1116 r = extract_first_word(&p, &w, NULL, 0);
1117 if (r < 0) {
ab24039f
ZJS
1118 log_syntax(unit, LOG_ERR, filename, line, r,
1119 "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
8a516214
LP
1120 break;
1121 }
1122 if (r == 0)
1123 break;
1124
1125 r = dns_name_is_valid(w);
1126 if (r <= 0) {
ab24039f
ZJS
1127 log_syntax(unit, LOG_ERR, filename, line, r,
1128 "%s is not a valid domain name, ignoring.", w);
8a516214
LP
1129 continue;
1130 }
1131
cbbf38ae
LP
1132 r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
1133 if (r < 0)
1134 return log_oom();
1135
8a516214
LP
1136 r = set_put(n->dnssec_negative_trust_anchors, w);
1137 if (r < 0)
1138 return log_oom();
1139 if (r > 0)
1140 w = NULL;
1141 }
1142
1143 return 0;
1144}
b2a81c0b 1145
26575990
LP
1146int config_parse_ntp(
1147 const char *unit,
1148 const char *filename,
1149 unsigned line,
1150 const char *section,
1151 unsigned section_line,
1152 const char *lvalue,
1153 int ltype,
1154 const char *rvalue,
1155 void *data,
1156 void *userdata) {
1157
1158 char ***l = data;
1159 int r;
1160
1161 assert(l);
1162 assert(lvalue);
1163 assert(rvalue);
1164
1165 if (isempty(rvalue)) {
1166 *l = strv_free(*l);
1167 return 0;
1168 }
1169
1170 for (;;) {
1171 _cleanup_free_ char *w = NULL;
1172
1173 r = extract_first_word(&rvalue, &w, NULL, 0);
1174 if (r == -ENOMEM)
1175 return log_oom();
1176 if (r < 0) {
ab24039f
ZJS
1177 log_syntax(unit, LOG_ERR, filename, line, r,
1178 "Failed to extract NTP server name, ignoring: %s", rvalue);
26575990
LP
1179 break;
1180 }
1181 if (r == 0)
1182 break;
1183
1184 r = dns_name_is_valid_or_address(w);
1185 if (r <= 0) {
ab24039f
ZJS
1186 log_syntax(unit, LOG_ERR, filename, line, r,
1187 "%s is not a valid domain name or IP address, ignoring.", w);
26575990 1188 continue;
af1c0de0
SS
1189 }
1190
c448459d
ZJS
1191 if (strv_length(*l) > MAX_NTP_SERVERS) {
1192 log_syntax(unit, LOG_WARNING, filename, line, 0,
1193 "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
1194 MAX_NTP_SERVERS, w);
1195 break;
1196 }
1197
1198 r = strv_consume(l, TAKE_PTR(w));
af1c0de0
SS
1199 if (r < 0)
1200 return log_oom();
af1c0de0
SS
1201 }
1202
1203 return 0;
1204}
1205
4ac77d63
YW
1206int config_parse_required_for_online(
1207 const char *unit,
1208 const char *filename,
1209 unsigned line,
1210 const char *section,
1211 unsigned section_line,
1212 const char *lvalue,
1213 int ltype,
1214 const char *rvalue,
1215 void *data,
1216 void *userdata) {
1217
1218 Network *network = data;
1219 LinkOperationalState s;
1220 bool required = true;
1221 int r;
1222
1223 if (isempty(rvalue)) {
1224 network->required_for_online = true;
1225 network->required_operstate_for_online = LINK_OPERSTATE_DEGRADED;
1226 return 0;
1227 }
1228
1229 s = link_operstate_from_string(rvalue);
1230 if (s < 0) {
1231 r = parse_boolean(rvalue);
1232 if (r < 0) {
1233 log_syntax(unit, LOG_ERR, filename, line, r,
1234 "Failed to parse %s= setting, ignoring assignment: %s",
1235 lvalue, rvalue);
1236 return 0;
1237 }
1238
1239 required = r;
1240 s = LINK_OPERSTATE_DEGRADED;
1241 }
1242
1243 network->required_for_online = required;
1244 network->required_operstate_for_online = s;
1245
1246 return 0;
1247}
7da377ef
SS
1248
1249DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
1250 "Failed to parse KeepConfiguration= setting");
1251
1252static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
95355a28
YW
1253 [KEEP_CONFIGURATION_NO] = "no",
1254 [KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
1255 [KEEP_CONFIGURATION_DHCP] = "dhcp",
1256 [KEEP_CONFIGURATION_STATIC] = "static",
1257 [KEEP_CONFIGURATION_YES] = "yes",
7da377ef
SS
1258};
1259
1260DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);