]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-network.c
network: Fix "Unknown section 'DHCPv6PrefixDelegation'." message
[thirdparty/systemd.git] / src / network / networkd-network.c
index e8419426f2b3a761cf6719146527a61a1bc04c93..d34155b19f4c3e1e35652c66d1d7717f8d4bb969 100644 (file)
@@ -16,6 +16,7 @@
 #include "network-internal.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-sriov.h"
 #include "parse-util.h"
 #include "path-lookup.h"
 #include "set.h"
@@ -51,7 +52,7 @@ void network_apply_anonymize_if_set(Network *network) {
         /* RFC7844 section 3.6.:
          The client intending to protect its privacy SHOULD only request a
          minimal number of options in the PRL and SHOULD also randomly shuffle
-         the ordering of option codes in the PRL.  If this random ordering
+         the ordering of option codes in the PRL. If this random ordering
          cannot be implemented, the client MAY order the option codes in the
          PRL by option code number (lowest to highest).
         */
@@ -158,6 +159,7 @@ int network_verify(Network *network) {
         Route *route, *route_next;
         FdbEntry *fdb, *fdb_next;
         TrafficControl *tc;
+        SRIOV *sr_iov;
         Iterator i;
 
         assert(network);
@@ -166,7 +168,8 @@ int network_verify(Network *network) {
         if (set_isempty(network->match_mac) && set_isempty(network->match_permanent_mac) &&
             strv_isempty(network->match_path) && strv_isempty(network->match_driver) &&
             strv_isempty(network->match_type) && strv_isempty(network->match_name) &&
-            strv_isempty(network->match_property) && strv_isempty(network->match_ssid) && !network->conditions)
+            strv_isempty(network->match_property) && strv_isempty(network->match_wlan_iftype) &&
+            strv_isempty(network->match_ssid) && !network->conditions)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
                                          "%s: No valid settings found in the [Match] section, ignoring file. "
                                          "To match all interfaces, add Name=* in the [Match] section.",
@@ -329,6 +332,10 @@ int network_verify(Network *network) {
                 if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
                         traffic_control_free(tc);
 
+        ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section, i)
+                if (sr_iov_section_verify(sr_iov) < 0)
+                        sr_iov_free(sr_iov);
+
         return 0;
 }
 
@@ -409,18 +416,20 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .dhcp_use_timezone = false,
                 .rapid_commit = true,
 
+                .dhcp6_route_metric = DHCP_ROUTE_METRIC,
                 .dhcp6_use_ntp = true,
                 .dhcp6_use_dns = true,
 
-                .dhcp6_pd_assign_prefix = true,
+                .dhcp6_pd_subnet_id = -1,
+                .dhcp6_pd_assign = true,
+
+                .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
+                .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
+                .dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true,
 
-                .dhcp_server_emit_dns = true,
-                .dhcp_server_emit_ntp = true,
-                .dhcp_server_emit_sip = true,
                 .dhcp_server_emit_router = true,
                 .dhcp_server_emit_timezone = true,
 
-                .router_prefix_subnet_id = -1,
                 .router_emit_dns = true,
                 .router_emit_domains = true,
 
@@ -448,6 +457,9 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
 
                 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
                 .link_local = _ADDRESS_FAMILY_INVALID,
+                .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
+
+                .ipv4_accept_local = -1,
 
                 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
                 .ipv6_accept_ra = -1,
@@ -469,60 +481,67 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .configure_without_carrier = false,
                 .ignore_carrier_loss = -1,
                 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
-                .ipv6_address_gen_mode = _LINK_IPV6_ADDRESS_GEN_MODE_INVALID,
                 .can_triple_sampling = -1,
                 .can_termination = -1,
                 .ip_service_type = -1,
         };
 
-        r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
-                              "Match\0"
-                              "Link\0"
-                              "Network\0"
-                              "Address\0"
-                              "Neighbor\0"
-                              "IPv6AddressLabel\0"
-                              "RoutingPolicyRule\0"
-                              "Route\0"
-                              "NextHop\0"
-                              "DHCP\0" /* compat */
-                              "DHCPv4\0"
-                              "DHCPv6\0"
-                              "DHCPServer\0"
-                              "IPv6AcceptRA\0"
-                              "IPv6NDPProxyAddress\0"
-                              "Bridge\0"
-                              "BridgeFDB\0"
-                              "BridgeVLAN\0"
-                              "IPv6PrefixDelegation\0"
-                              "IPv6Prefix\0"
-                              "IPv6RoutePrefix\0"
-                              "LLDP\0"
-                              "TrafficControlQueueingDiscipline\0"
-                              "CAN\0"
-                              "QDisc\0"
-                              "BFIFO\0"
-                              "CAKE\0"
-                              "ControlledDelay\0"
-                              "DeficitRoundRobinScheduler\0"
-                              "DeficitRoundRobinSchedulerClass\0"
-                              "PFIFO\0"
-                              "PFIFOFast\0"
-                              "PFIFOHeadDrop\0"
-                              "FairQueueing\0"
-                              "FairQueueingControlledDelay\0"
-                              "GenericRandomEarlyDetection\0"
-                              "HeavyHitterFilter\0"
-                              "HierarchyTokenBucket\0"
-                              "HierarchyTokenBucketClass\0"
-                              "NetworkEmulator\0"
-                              "PIE\0"
-                              "StochasticFairBlue\0"
-                              "StochasticFairnessQueueing\0"
-                              "TokenBucketFilter\0"
-                              "TrivialLinkEqualizer\0",
-                              config_item_perf_lookup, network_network_gperf_lookup,
-                              CONFIG_PARSE_WARN, network);
+        r = config_parse_many(
+                        filename, NETWORK_DIRS, dropin_dirname,
+                        "Match\0"
+                        "Link\0"
+                        "SR-IOV\0"
+                        "Network\0"
+                        "Address\0"
+                        "Neighbor\0"
+                        "IPv6AddressLabel\0"
+                        "RoutingPolicyRule\0"
+                        "Route\0"
+                        "NextHop\0"
+                        "DHCP\0" /* compat */
+                        "DHCPv4\0"
+                        "DHCPv6\0"
+                        "DHCPv6PrefixDelegation\0"
+                        "DHCPServer\0"
+                        "IPv6AcceptRA\0"
+                        "IPv6NDPProxyAddress\0"
+                        "Bridge\0"
+                        "BridgeFDB\0"
+                        "BridgeVLAN\0"
+                        "IPv6PrefixDelegation\0"
+                        "IPv6Prefix\0"
+                        "IPv6RoutePrefix\0"
+                        "LLDP\0"
+                        "TrafficControlQueueingDiscipline\0"
+                        "CAN\0"
+                        "QDisc\0"
+                        "BFIFO\0"
+                        "CAKE\0"
+                        "ControlledDelay\0"
+                        "DeficitRoundRobinScheduler\0"
+                        "DeficitRoundRobinSchedulerClass\0"
+                        "EnhancedTransmissionSelection\0"
+                        "FairQueueing\0"
+                        "FairQueueingControlledDelay\0"
+                        "GenericRandomEarlyDetection\0"
+                        "HeavyHitterFilter\0"
+                        "HierarchyTokenBucket\0"
+                        "HierarchyTokenBucketClass\0"
+                        "NetworkEmulator\0"
+                        "PFIFO\0"
+                        "PFIFOFast\0"
+                        "PFIFOHeadDrop\0"
+                        "PIE\0"
+                        "QuickFairQueueing\0"
+                        "QuickFairQueueingClass\0"
+                        "StochasticFairBlue\0"
+                        "StochasticFairnessQueueing\0"
+                        "TokenBucketFilter\0"
+                        "TrivialLinkEqualizer\0",
+                        config_item_perf_lookup, network_network_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        network,
+                        &network->timestamp);
         if (r < 0)
                 return r;
 
@@ -537,11 +556,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
                                   network->filename);
 
-        struct stat stats;
-        if (stat(filename, &stats) < 0)
-                return -errno;
-        network->timestamp = timespec_load(&stats.st_mtim);
-
         if (network_verify(network) < 0)
                 /* Ignore .network files that do not match the conditions. */
                 return 0;
@@ -656,7 +670,8 @@ static Network *network_free(Network *network) {
         free(network->dhcp_mudurl);
         strv_free(network->dhcp_user_class);
         free(network->dhcp_hostname);
-        set_free(network->dhcp_black_listed_ip);
+        set_free(network->dhcp_deny_listed_ip);
+        set_free(network->dhcp_allow_listed_ip);
         set_free(network->dhcp_request_options);
         set_free(network->dhcp6_request_options);
         free(network->mac);
@@ -668,16 +683,16 @@ static Network *network_free(Network *network) {
                 sd_ipv4acd_unref(network->dhcp_acd);
 
         strv_free(network->ntp);
+        for (unsigned i = 0; i < network->n_dns; i++)
+                in_addr_full_free(network->dns[i]);
         free(network->dns);
-        strv_free(network->sip);
-        strv_free(network->smtp);
         ordered_set_free_free(network->search_domains);
         ordered_set_free_free(network->route_domains);
         strv_free(network->bind_carrier);
 
         ordered_set_free_free(network->router_search_domains);
         free(network->router_dns);
-        set_free_free(network->ndisc_black_listed_prefix);
+        set_free_free(network->ndisc_deny_listed_prefix);
 
         free(network->bridge_name);
         free(network->bond_name);
@@ -727,6 +742,7 @@ static Network *network_free(Network *network) {
         hashmap_free(network->prefixes_by_section);
         hashmap_free(network->route_prefixes_by_section);
         hashmap_free(network->rules_by_section);
+        ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
         ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
 
         if (network->manager &&
@@ -736,12 +752,9 @@ static Network *network_free(Network *network) {
         free(network->name);
 
         free(network->dhcp_server_timezone);
-        free(network->dhcp_server_dns);
-        free(network->dhcp_server_ntp);
-        free(network->dhcp_server_sip);
-        free(network->dhcp_server_pop3);
-        free(network->dhcp_server_smtp);
-        free(network->dhcp_server_lpr);
+
+        for (sd_dhcp_lease_server_type t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
+                free(network->dhcp_server_emit[t].addresses);
 
         set_free_free(network->dnssec_negative_trust_anchors);
 
@@ -751,7 +764,7 @@ static Network *network_free(Network *network) {
         ordered_hashmap_free(network->dhcp_client_send_vendor_options);
         ordered_hashmap_free(network->dhcp_server_send_options);
         ordered_hashmap_free(network->dhcp_server_send_vendor_options);
-        ordered_hashmap_free(network->ipv6_tokens);
+        ordered_set_free(network->ipv6_tokens);
         ordered_hashmap_free(network->dhcp6_client_send_options);
         ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
 
@@ -777,8 +790,8 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) {
 }
 
 int network_get(Manager *manager, unsigned short iftype, sd_device *device,
-                const char *ifname, char * const *alternative_names,
-                const struct ether_addr *address, const struct ether_addr *permanent_address,
+                const char *ifname, char * const *alternative_names, const char *driver,
+                const struct ether_addr *mac, const struct ether_addr *permanent_mac,
                 enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid,
                 Network **ret) {
         Network *network;
@@ -792,7 +805,7 @@ int network_get(Manager *manager, unsigned short iftype, sd_device *device,
                                      network->match_path, network->match_driver,
                                      network->match_type, network->match_name, network->match_property,
                                      network->match_wlan_iftype, network->match_ssid, network->match_bssid,
-                                     iftype, device, address, permanent_address,
+                                     device, mac, permanent_mac, driver, iftype,
                                      ifname, alternative_names, wlan_iftype, ssid, bssid)) {
                         if (network->match_name && device) {
                                 const char *attr;
@@ -892,7 +905,7 @@ int config_parse_stacked_netdev(const char *unit,
                       NETDEV_KIND_XFRM));
 
         if (!ifname_valid(rvalue)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
                 return 0;
         }
@@ -907,7 +920,7 @@ int config_parse_stacked_netdev(const char *unit,
 
         r = hashmap_put(*h, name, INT_TO_PTR(kind));
         if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
         else if (r == 0)
                 log_syntax(unit, LOG_DEBUG, filename, line, r,
@@ -930,7 +943,6 @@ int config_parse_domains(
                 void *data,
                 void *userdata) {
 
-        const char *p;
         Network *n = data;
         int r;
 
@@ -944,20 +956,21 @@ int config_parse_domains(
                 return 0;
         }
 
-        p = rvalue;
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL, *normalized = NULL;
                 const char *domain;
                 bool is_route;
 
                 r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract search or route domain, ignoring: %s", rvalue);
-                        break;
+                        return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
                 is_route = w[0] == '~';
                 domain = is_route ? w + 1 : w;
@@ -971,7 +984,7 @@ int config_parse_domains(
                 } else {
                         r = dns_name_normalize(domain, 0, &normalized);
                         if (r < 0) {
-                                log_syntax(unit, LOG_ERR, filename, line, r,
+                                log_syntax(unit, LOG_WARNING, filename, line, r,
                                            "'%s' is not a valid domain name, ignoring.", domain);
                                 continue;
                         }
@@ -979,7 +992,7 @@ int config_parse_domains(
                         domain = normalized;
 
                         if (is_localhost(domain)) {
-                                log_syntax(unit, LOG_ERR, filename, line, 0,
+                                log_syntax(unit, LOG_WARNING, filename, line, 0,
                                            "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
                                            domain);
                                 continue;
@@ -989,14 +1002,12 @@ int config_parse_domains(
                 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
                 r = ordered_set_ensure_allocated(set, &string_hash_ops);
                 if (r < 0)
-                        return r;
+                        return log_oom();
 
                 r = ordered_set_put_strdup(*set, domain);
                 if (r < 0)
                         return log_oom();
         }
-
-        return 0;
 }
 
 int config_parse_ipv6token(
@@ -1022,19 +1033,19 @@ int config_parse_ipv6token(
 
         r = in_addr_from_string(AF_INET6, rvalue, &buffer);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse IPv6 token, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (in_addr_is_null(AF_INET6, &buffer)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
                 return 0;
         }
 
         if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
                 return 0;
         }
@@ -1077,7 +1088,7 @@ int config_parse_ipv6_privacy_extensions(
                 if (streq(rvalue, "kernel"))
                         s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
                 else {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
                         return 0;
                 }
@@ -1113,19 +1124,19 @@ int config_parse_hostname(
                 return r;
 
         if (!hostname_is_valid(hn, false)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Hostname is not valid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         r = dns_name_is_valid(hn);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
                 return 0;
         }
         if (r == 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -1157,8 +1168,8 @@ int config_parse_timezone(
         if (r < 0)
                 return r;
 
-        if (!timezone_is_valid(tz, LOG_ERR)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+        if (!timezone_is_valid(tz, LOG_WARNING)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Timezone is not valid, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -1185,43 +1196,47 @@ int config_parse_dns(
         assert(lvalue);
         assert(rvalue);
 
-        for (;;) {
+        if (isempty(rvalue)) {
+                for (unsigned i = 0; i < n->n_dns; i++)
+                        in_addr_full_free(n->dns[i]);
+                n->dns = mfree(n->dns);
+                n->n_dns = 0;
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
                 _cleanup_free_ char *w = NULL;
-                union in_addr_union a;
-                struct in_addr_data *m;
-                int family;
+                struct in_addr_full **m;
 
-                r = extract_first_word(&rvalue, &w, NULL, 0);
+                r = extract_first_word(&p, &w, NULL, 0);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Invalid syntax, ignoring: %s", rvalue);
-                        break;
+                        return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
-                r = in_addr_from_string_auto(w, &family, &a);
+                r = in_addr_full_new_from_string(w, &dns);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to parse dns server address, ignoring: %s", w);
                         continue;
                 }
 
-                m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
+                if (IN_SET(dns->port, 53, 853))
+                        dns->port = 0;
+
+                m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
                 if (!m)
                         return log_oom();
 
-                m[n->n_dns++] = (struct in_addr_data) {
-                        .family = family,
-                        .address = a,
-                };
-
+                m[n->n_dns++] = TAKE_PTR(dns);
                 n->dns = m;
         }
-
-        return 0;
 }
 
 int config_parse_dnssec_negative_trust_anchors(
@@ -1236,7 +1251,6 @@ int config_parse_dnssec_negative_trust_anchors(
                 void *data,
                 void *userdata) {
 
-        const char *p = rvalue;
         Network *n = data;
         int r;
 
@@ -1249,37 +1263,31 @@ int config_parse_dnssec_negative_trust_anchors(
                 return 0;
         }
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL;
 
                 r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
-                        break;
+                        return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
                 r = dns_name_is_valid(w);
                 if (r <= 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "%s is not a valid domain name, ignoring.", w);
                         continue;
                 }
 
-                r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
+                r = set_ensure_consume(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops, TAKE_PTR(w));
                 if (r < 0)
                         return log_oom();
-
-                r = set_put(n->dnssec_negative_trust_anchors, w);
-                if (r < 0)
-                        return log_oom();
-                if (r > 0)
-                        w = NULL;
         }
-
-        return 0;
 }
 
 int config_parse_ntp(
@@ -1306,23 +1314,23 @@ int config_parse_ntp(
                 return 0;
         }
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL;
 
-                r = extract_first_word(&rvalue, &w, NULL, 0);
+                r = extract_first_word(&p, &w, NULL, 0);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract NTP server name, ignoring: %s", rvalue);
-                        break;
+                        return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
                 r = dns_name_is_valid_or_address(w);
                 if (r <= 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "%s is not a valid domain name or IP address, ignoring.", w);
                         continue;
                 }
@@ -1331,15 +1339,13 @@ int config_parse_ntp(
                         log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
                                    MAX_NTP_SERVERS, w);
-                        break;
+                        return 0;
                 }
 
                 r = strv_consume(l, TAKE_PTR(w));
                 if (r < 0)
                         return log_oom();
         }
-
-        return 0;
 }
 
 int config_parse_required_for_online(
@@ -1369,7 +1375,7 @@ int config_parse_required_for_online(
         if (r < 0) {
                 r = parse_boolean(rvalue);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to parse %s= setting, ignoring assignment: %s",
                                    lvalue, rvalue);
                         return 0;
@@ -1397,3 +1403,13 @@ static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
+
+static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {
+        [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64] = "eui64",
+        [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE] = "none",
+        [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
+        [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode");