]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #19611 from yuwata/network-dhcp-server-introduce-server-address
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 19 May 2021 01:29:43 +0000 (10:29 +0900)
committerGitHub <noreply@github.com>
Wed, 19 May 2021 01:29:43 +0000 (10:29 +0900)
network: dhcp-server: introduce ServerAddress= setting

18 files changed:
man/systemd.network.xml
src/network/networkd-address-label.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp-server.h
src/network/networkd-dhcp6.c
src/network/networkd-dhcp6.h
src/network/networkd-ipv4ll.c
src/network/networkd-ndisc.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/shared/conf-parser.c
src/shared/conf-parser.h
test/fuzz/fuzz-network-parser/directives.network
test/test-network/conf/dhcp-server-timezone-router.network
test/test-network/conf/dhcp-server.network

index fe330be0be70db4c4a4fdf5c8cf5c8b0b187b29a..02e464b193db39b92ce874f04b20ade02a663e0b 100644 (file)
@@ -2344,6 +2344,14 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
 
     <variablelist class='network-directives'>
 
+      <varlistentry>
+        <term><varname>ServerAddress=</varname></term>
+        <listitem><para>Specifies server address for the DHCP server. Takes an IPv4 address with prefix
+        length, e.g., <literal>192.168.0.1/24</literal>. This setting may be useful when the link which
+        DHCP server running on has multiple static addresses. When unset, one of static addresses in
+        the link will be automatically selected. Defaults to unset.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>PoolOffset=</varname></term>
         <term><varname>PoolSize=</varname></term>
index 7b7b72469e7456bfc8600e56c5eb62828f5cb310..9c9d28386881d2cd29ef01bd11c14386aba15002 100644 (file)
@@ -225,7 +225,7 @@ int config_parse_address_label(
                 return 0;
         }
 
-        if (k == 0xffffffffUL) {
+        if (k == UINT32_C(0xffffffff)) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Address label is invalid, ignoring: %s", rvalue);
                 return 0;
         }
index daf959ec5f3d75dcbcb50bd93545f269dd771a6c..2bc860954bd30563367d76f35b4711c8a0efe72d 100644 (file)
@@ -146,12 +146,38 @@ Address *address_free(Address *address) {
 static bool address_may_have_broadcast(const Address *a) {
         assert(a);
 
+        if (a->family != AF_INET)
+                return false;
+
+        if (in4_addr_is_set(&a->in_addr_peer.in))
+                return false;
+
         /* A /31 or /32 IPv4 address does not have a broadcast address.
          * See https://tools.ietf.org/html/rfc3021 */
+        if (a->prefixlen > 30)
+                return false;
+
+        if (a->set_broadcast >= 0)
+                return a->set_broadcast;
+
+        return true; /* Defaults to true. */
+}
+
+void address_set_broadcast(Address *a) {
+        assert(a);
+
+        if (!address_may_have_broadcast(a))
+                return;
 
-        return a->family == AF_INET &&
-                in4_addr_is_null(&a->in_addr_peer.in) &&
-                a->prefixlen <= 30;
+        /* If explicitly configured, do not update the address. */
+        if (in4_addr_is_set(&a->broadcast))
+                return;
+
+        /* If Address= is 0.0.0.0, then the broadcast address will be set later in address_acquire(). */
+        if (in4_addr_is_null(&a->in_addr.in))
+                return;
+
+        a->broadcast.s_addr = a->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> a->prefixlen);
 }
 
 static bool address_may_set_broadcast(const Address *a, const Link *link) {
@@ -161,9 +187,6 @@ static bool address_may_set_broadcast(const Address *a, const Link *link) {
         if (!address_may_have_broadcast(a))
                 return false;
 
-        if (a->set_broadcast >= 0)
-                return a->set_broadcast;
-
         /* Typical configuration for wireguard does not set broadcast. */
         return !streq_ptr(link->kind, "wireguard");
 }
@@ -466,7 +489,7 @@ int address_get(Link *link, const Address *in, Address **ret) {
         return -ENOENT;
 }
 
-int link_has_ipv6_address(Link *link, const struct in6_addr *address) {
+int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret) {
         _cleanup_(address_freep) Address *a = NULL;
         int r;
 
@@ -482,10 +505,10 @@ int link_has_ipv6_address(Link *link, const struct in6_addr *address) {
         a->family = AF_INET6;
         a->in_addr.in6 = *address;
 
-        return address_get(link, a, NULL) >= 0;
+        return address_get(link, a, ret);
 }
 
-static int link_get_ipv4_address(Set *addresses, const struct in_addr *address, Address **ret) {
+static int addresses_get_ipv4_address(Set *addresses, const struct in_addr *address, Address **ret) {
         Address *a;
 
         assert(address);
@@ -506,7 +529,35 @@ static int link_get_ipv4_address(Set *addresses, const struct in_addr *address,
         return -ENOENT;
 }
 
+int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) {
+        int r;
+
+        assert(link);
+        assert(address);
+
+        if (prefixlen != 0) {
+                _cleanup_(address_freep) Address *a = NULL;
+
+                /* If prefixlen is set, then we can use address_get(). */
+
+                r = address_new(&a);
+                if (r < 0)
+                        return r;
+
+                a->family = AF_INET;
+                a->in_addr.in = *address;
+                a->prefixlen = prefixlen;
+
+                return address_get(link, a, ret);
+        }
+
+        if (addresses_get_ipv4_address(link->addresses, address, ret) >= 0)
+                return 0;
+        return addresses_get_ipv4_address(link->addresses_foreign, address, ret);
+}
+
 int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) {
+        Address *a;
         Link *link;
         int r;
 
@@ -514,18 +565,12 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union
         assert(IN_SET(family, AF_INET, AF_INET6));
         assert(address);
 
-        if (family == AF_INET)
-                HASHMAP_FOREACH(link, manager->links) {
-                        Address *a;
-
-                        if (link_get_ipv4_address(link->addresses, &address->in, &a) >= 0)
-                                return !check_ready || address_is_ready(a);
-                        if (link_get_ipv4_address(link->addresses_foreign, &address->in, &a) >= 0)
+        if (family == AF_INET) {
+                HASHMAP_FOREACH(link, manager->links)
+                        if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0)
                                 return !check_ready || address_is_ready(a);
-                }
-        else {
+        } else {
                 _cleanup_(address_freep) Address *tmp = NULL;
-                Address *a;
 
                 r = address_new(&tmp);
                 if (r < 0)
@@ -825,7 +870,6 @@ int link_drop_addresses(Link *link) {
 
 static int address_acquire(Link *link, const Address *original, Address **ret) {
         union in_addr_union in_addr = IN_ADDR_NULL;
-        struct in_addr broadcast = {};
         _cleanup_(address_freep) Address *na = NULL;
         int r;
 
@@ -847,16 +891,10 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
         if (r == 0)
                 return -EBUSY;
 
-        if (original->family == AF_INET) {
-                /* Pick first address in range for ourselves ... */
+        /* Pick first address in range for ourselves. */
+        if (original->family == AF_INET)
                 in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
-
-                /* .. and use last as broadcast address */
-                if (original->prefixlen > 30)
-                        broadcast.s_addr = 0;
-                else
-                        broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen);
-        } else if (original->family == AF_INET6)
+        else if (original->family == AF_INET6)
                 in_addr.in6.s6_addr[15] |= 1;
 
         r = address_new(&na);
@@ -867,8 +905,8 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
         if (r < 0)
                 return r;
 
-        na->broadcast = broadcast;
         na->in_addr = in_addr;
+        address_set_broadcast(na);
 
         r = set_ensure_put(&link->pool_addresses, &address_hash_ops, na);
         if (r < 0)
@@ -1119,6 +1157,32 @@ int link_request_static_addresses(Link *link) {
                 req->after_configure = static_address_after_configure;
         }
 
+        if (in4_addr_is_set(&link->network->dhcp_server_address)) {
+                _cleanup_(address_freep) Address *address = NULL;
+
+                r = address_new(&address);
+                if (r < 0)
+                        return log_oom();
+
+                address->family = AF_INET;
+                address->in_addr.in = link->network->dhcp_server_address;
+                address->prefixlen = link->network->dhcp_server_address_prefixlen;
+                address_set_broadcast(address);
+
+                /* The same address may be explicitly configured in [Address] or [Network] section.
+                 * Configure the DHCP server address only when it is not. */
+                if (!link_is_static_address_configured(link, address)) {
+                        Request *req;
+
+                        r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages,
+                                                 static_address_handler, &req);
+                        if (r < 0)
+                                return r;
+
+                        req->after_configure = static_address_after_configure;
+                }
+        }
+
         if (link->static_address_messages == 0) {
                 link->static_addresses_configured = true;
                 link_check_ready(link);
@@ -1960,10 +2024,9 @@ static int address_section_verify(Address *address) {
                                          address->section->filename, address->section->line);
         }
 
-        if (address_may_have_broadcast(address)) {
-                if (address->broadcast.s_addr == 0 && address->set_broadcast != 0)
-                        address->broadcast.s_addr = address->in_addr.in.s_addr | htobe32(0xfffffffflu >> address->prefixlen);
-        } else if (address->broadcast.s_addr != 0) {
+        if (address_may_have_broadcast(address))
+                address_set_broadcast(address);
+        else if (address->broadcast.s_addr != 0) {
                 log_warning("%s: broadcast address is set for IPv6 address or IPv4 address with prefixlength larger than 30. "
                             "Ignoring Broadcast= setting in the [Address] section from line %u.",
                             address->section->filename, address->section->line);
index e9fddddf9f71938c878dd81611989542bc2ff8d3..f63848e2e7a14b3637a2226835564c61785a8b43 100644 (file)
@@ -57,6 +57,7 @@ int address_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Lin
 int address_remove(const Address *address, Link *link, link_netlink_message_handler_t callback);
 bool address_equal(const Address *a1, const Address *a2);
 bool address_is_ready(const Address *a);
+void address_set_broadcast(Address *a);
 
 int generate_ipv6_eui_64_address(const Link *link, struct in6_addr *ret);
 
@@ -65,7 +66,8 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
 int link_drop_addresses(Link *link);
 int link_drop_foreign_addresses(Link *link);
 bool link_address_is_dynamic(const Link *link, const Address *address);
-int link_has_ipv6_address(Link *link, const struct in6_addr *address);
+int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret);
+int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret);
 int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);
 
 void ipv4_dad_unref(Link *link);
index d2efc301855e36f97552518871dafb2c6aa8fb8f..0a396a957f70483245cf875df20caf86381b7ffd 100644 (file)
@@ -29,33 +29,70 @@ static bool link_dhcp4_server_enabled(Link *link) {
         if (!link->network)
                 return false;
 
-        if (link->network->bond)
-                return false;
-
         if (link->iftype == ARPHRD_CAN)
                 return false;
 
         return link->network->dhcp_server;
 }
 
-static Address* link_find_dhcp_server_address(Link *link) {
+void network_adjust_dhcp_server(Network *network) {
+        assert(network);
+
+        if (!network->dhcp_server)
+                return;
+
+        if (network->bond) {
+                log_warning("%s: DHCPServer= is enabled for bond slave. Disabling DHCP server.",
+                            network->filename);
+                network->dhcp_server = false;
+                return;
+        }
+
+        if (!in4_addr_is_set(&network->dhcp_server_address)) {
+                Address *address;
+                bool have = false;
+
+                ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) {
+                        if (section_is_invalid(address->section))
+                                continue;
+                        if (address->family == AF_INET &&
+                            !in4_addr_is_localhost(&address->in_addr.in) &&
+                            in4_addr_is_null(&address->in_addr_peer.in)) {
+                                have = true;
+                                break;
+                        }
+                }
+                if (!have) {
+                        log_warning("%s: DHCPServer= is enabled, but no static address configured. "
+                                    "Disabling DHCP server.",
+                                    network->filename);
+                        network->dhcp_server = false;
+                        return;
+                }
+        }
+}
+
+static int link_find_dhcp_server_address(Link *link, Address **ret) {
         Address *address;
 
         assert(link);
         assert(link->network);
 
-        /* The first statically configured address if there is any */
-        ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section)
-                if (address->family == AF_INET &&
-                    in_addr_is_set(address->family, &address->in_addr))
-                        return address;
+        /* If ServerAddress= is specified, then use the address. */
+        if (in4_addr_is_set(&link->network->dhcp_server_address))
+                return link_get_ipv4_address(link, &link->network->dhcp_server_address,
+                                             link->network->dhcp_server_address_prefixlen, ret);
 
-        /* If that didn't work, find a suitable address we got from the pool */
-        SET_FOREACH(address, link->pool_addresses)
-                if (address->family == AF_INET)
-                        return address;
+        /* If not, then select one from static addresses. */
+        SET_FOREACH(address, link->static_addresses)
+                if (address->family == AF_INET &&
+                    !in4_addr_is_localhost(&address->in_addr.in) &&
+                    in4_addr_is_null(&address->in_addr_peer.in)) {
+                        *ret = address;
+                        return 0;
+                }
 
-        return NULL;
+        return -ENOENT;
 }
 
 static int link_push_uplink_to_dhcp_server(
@@ -277,10 +314,9 @@ int dhcp4_server_configure(Link *link) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to set callback for DHCPv4 server instance: %m");
 
-        address = link_find_dhcp_server_address(link);
-        if (!address)
-                return log_link_error_errno(link, SYNTHETIC_ERRNO(EBUSY),
-                                            "Failed to find suitable address for DHCPv4 server instance.");
+        r = link_find_dhcp_server_address(link, &address);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to find suitable address for DHCPv4 server instance: %m");
 
         /* use the server address' subnet as the pool */
         r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
@@ -429,7 +465,6 @@ int config_parse_dhcp_server_relay_agent_suboption(
         assert(lvalue);
         assert(rvalue);
 
-
         if (isempty(rvalue)) {
                 *suboption_value = mfree(*suboption_value);
                 return 0;
@@ -444,32 +479,6 @@ int config_parse_dhcp_server_relay_agent_suboption(
         return free_and_strdup(suboption_value, empty_to_null(p));
 }
 
-int config_parse_dhcp_server_relay_target(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        union in_addr_union a;
-        int r;
-
-        r = in_addr_from_string(AF_INET, rvalue, &a);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse %s= address '%s', ignoring: %m", lvalue, rvalue);
-                return 0;
-        }
-        network->dhcp_server_relay_target = a.in;
-        return r;
-}
-
 int config_parse_dhcp_server_emit(
                 const char *unit,
                 const char *filename,
@@ -518,3 +527,48 @@ int config_parse_dhcp_server_emit(
                 emit->addresses[emit->n_addresses++] = a.in;
         }
 }
+
+int config_parse_dhcp_server_address(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = userdata;
+        union in_addr_union a;
+        unsigned char prefixlen;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                network->dhcp_server_address = (struct in_addr) {};
+                network->dhcp_server_address_prefixlen = 0;
+                return 0;
+        }
+
+        r = in_addr_prefix_from_string(rvalue, AF_INET, &a, &prefixlen);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+        if (in4_addr_is_null(&a.in) || in4_addr_is_localhost(&a.in)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "DHCP server address cannot be the ANY address or a localhost address, "
+                           "ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        network->dhcp_server_address = a.in;
+        network->dhcp_server_address_prefixlen = prefixlen;
+        return 0;
+}
index 7a1e3a51737f3a0ce008acb0d29b2f4eabef3d7e..d58978cc051f6629ea2432ca75f264c5b51fca80 100644 (file)
@@ -2,13 +2,14 @@
 #pragma once
 
 #include "conf-parser.h"
-#include "networkd-link.h"
-#include "networkd-util.h"
 
 typedef struct Link Link;
+typedef struct Network Network;
+
+void network_adjust_dhcp_server(Network *network);
 
 int dhcp4_server_configure(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_target);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address);
index 7061afe0a2db18eb70cfabd391f2c2e52015b561..e8c7d8ebd85a2185d68bdefa8c1e6ab64cde7e71 100644 (file)
@@ -1888,47 +1888,3 @@ int config_parse_dhcp6_pd_subnet_id(
 
         return 0;
 }
-
-int config_parse_dhcp6_pd_token(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        struct in6_addr *addr = data;
-        union in_addr_union tmp;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if (isempty(rvalue)) {
-                *addr = (struct in6_addr) {};
-                return 0;
-        }
-
-        r = in_addr_from_string(AF_INET6, rvalue, &tmp);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse DHCPv6 Prefix Delegation token, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        if (in_addr_is_null(AF_INET6, &tmp)) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "DHCPv6 Prefix Delegation token cannot be the ANY address, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        *addr = tmp.in6;
-
-        return 0;
-}
index f24d6eea5a734d52fcfbdf0b34ba2c981da4f826..82becb03217e18ad32f1210e79306764aee5735c 100644 (file)
@@ -41,7 +41,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_subnet_id);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_token);
 
 const char* dhcp6_client_start_mode_to_string(DHCP6ClientStartMode i) _const_;
 DHCP6ClientStartMode dhcp6_client_start_mode_from_string(const char *s) _pure_;
index 8e2d761cf904a0a2ffe53be123ac5767c1d1a037..12ba5c085ad8d0595ecae2581db3b11b08e8b17a 100644 (file)
@@ -31,9 +31,9 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
         address->family = AF_INET;
         address->in_addr.in = addr;
         address->prefixlen = 16;
-        address->broadcast.s_addr = address->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> address->prefixlen);
         address->scope = RT_SCOPE_LINK;
         address->route_metric = IPV4LL_ROUTE_METRIC;
+        address_set_broadcast(address);
 
         *ret = TAKE_PTR(address);
         return 0;
index cfcf4cc36aa8bc0d563b1b4b1f1ec5582f36958c..56aa5ec8a77c18b0bc8374cb12fd27fcb81e92b6 100644 (file)
@@ -541,7 +541,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
 
-        if (link_has_ipv6_address(link, &gateway) > 0) {
+        if (link_get_ipv6_address(link, &gateway, NULL) >= 0) {
                 if (DEBUG_LOGGING) {
                         _cleanup_free_ char *buffer = NULL;
 
@@ -918,7 +918,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
 
-        if (link_has_ipv6_address(link, &gateway) > 0) {
+        if (link_get_ipv6_address(link, &gateway, NULL) >= 0) {
                 if (DEBUG_LOGGING) {
                         _cleanup_free_ char *buf = NULL;
 
index 732b53f71dde1cf6e8bd4605f9b10d5d8b4926c8..6616b1477448a1727b8161c9dc17bffd30712ebc 100644 (file)
@@ -261,7 +261,8 @@ IPv6AcceptRA.PrefixAllowList,                config_parse_ndisc_address_filter,
 IPv6AcceptRA.PrefixDenyList,                 config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_prefix)
 IPv6AcceptRA.RouteAllowList,                 config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_allow_listed_route_prefix)
 IPv6AcceptRA.RouteDenyList,                  config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_route_prefix)
-DHCPServer.RelayTarget,                      config_parse_dhcp_server_relay_target,                    0,                             0
+DHCPServer.ServerAddress,                    config_parse_dhcp_server_address,                         0,                             0
+DHCPServer.RelayTarget,                      config_parse_in_addr_non_null,                            AF_INET,                       offsetof(Network, dhcp_server_relay_target)
 DHCPServer.RelayAgentCircuitId,              config_parse_dhcp_server_relay_agent_suboption,           0,                             offsetof(Network, dhcp_server_relay_agent_circuit_id)
 DHCPServer.RelayAgentRemoteId,               config_parse_dhcp_server_relay_agent_suboption,           0,                             offsetof(Network, dhcp_server_relay_agent_remote_id)
 DHCPServer.MaxLeaseTimeSec,                  config_parse_sec,                                         0,                             offsetof(Network, dhcp_server_max_lease_time_usec)
@@ -314,7 +315,7 @@ DHCPv6PrefixDelegation.SubnetId,             config_parse_dhcp6_pd_subnet_id,
 DHCPv6PrefixDelegation.Announce,             config_parse_bool,                                        0,                             offsetof(Network, dhcp6_pd_announce)
 DHCPv6PrefixDelegation.Assign,               config_parse_bool,                                        0,                             offsetof(Network, dhcp6_pd_assign)
 DHCPv6PrefixDelegation.ManageTemporaryAddress, config_parse_bool,                                      0,                             offsetof(Network, dhcp6_pd_manage_temporary_address)
-DHCPv6PrefixDelegation.Token,                config_parse_dhcp6_pd_token,                              0,                             offsetof(Network, dhcp6_pd_token)
+DHCPv6PrefixDelegation.Token,                config_parse_in_addr_non_null,                            AF_INET6,                      offsetof(Network, dhcp6_pd_token)
 DHCPv6PrefixDelegation.RouteMetric,          config_parse_uint32,                                      0,                             offsetof(Network, dhcp6_pd_route_metric)
 IPv6SendRA.RouterLifetimeSec,                config_parse_sec,                                         0,                             offsetof(Network, router_lifetime_usec)
 IPv6SendRA.Managed,                          config_parse_bool,                                        0,                             offsetof(Network, router_managed)
index 482c31064fc2d7ea7bd346f946bfeabeebc7c13a..6f68759a88ce4e0fec2c9a48f7b1dc9b827e7002 100644 (file)
@@ -240,6 +240,8 @@ int network_verify(Network *network) {
         network_drop_invalid_traffic_control(network);
         network_drop_invalid_sr_iov(network);
 
+        network_adjust_dhcp_server(network);
+
         return 0;
 }
 
index e27a27cd02dcaea38ffa165457722618c075cf16..c1316d2abc9544d901cc1ab13f2555ed5a54e0b5 100644 (file)
@@ -189,10 +189,11 @@ struct Network {
         /* DHCP Server Support */
         bool dhcp_server;
         bool dhcp_server_bind_to_interface;
+        unsigned char dhcp_server_address_prefixlen;
+        struct in_addr dhcp_server_address;
         struct in_addr dhcp_server_relay_target;
         char *dhcp_server_relay_agent_circuit_id;
         char *dhcp_server_relay_agent_remote_id;
-
         NetworkDHCPServerEmitAddress dhcp_server_emit[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
         bool dhcp_server_emit_router;
         bool dhcp_server_emit_timezone;
index fa4079cff7717bbeb63a521959c4ef08b6fce57e..ce3af64962178febfdac9283502b3d0991778d13 100644 (file)
@@ -16,6 +16,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "in-addr-util.h"
 #include "log.h"
 #include "macro.h"
 #include "missing_network.h"
@@ -1359,5 +1360,57 @@ int config_parse_hwaddrs(
         }
 }
 
+int config_parse_in_addr_non_null(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        /* data must be a pointer to struct in_addr or in6_addr, and the type is determined by ltype. */
+        struct in_addr *ipv4 = data;
+        struct in6_addr *ipv6 = data;
+        union in_addr_union a;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+        assert(IN_SET(ltype, AF_INET, AF_INET6));
+
+        if (isempty(rvalue)) {
+                if (ltype == AF_INET)
+                        *ipv4 = (struct in_addr) {};
+                else
+                        *ipv6 = (struct in6_addr) {};
+                return 0;
+        }
+
+        r = in_addr_from_string(ltype, rvalue, &a);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (!in_addr_is_set(ltype, &a)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "%s= cannot be the ANY address, ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (ltype == AF_INET)
+                *ipv4 = a.in;
+        else
+                *ipv6 = a.in6;
+        return 0;
+}
+
 DEFINE_CONFIG_PARSE(config_parse_percent, parse_percent, "Failed to parse percent value");
 DEFINE_CONFIG_PARSE(config_parse_permyriad, parse_permyriad, "Failed to parse permyriad value");
index c4b9891428d27cb576cdf026fcfcb846703d82a7..c3a138274db3651a7a72053f0e8fca18f6d55b14 100644 (file)
@@ -149,6 +149,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
 CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
 CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
 CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs);
+CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
 CONFIG_PARSER_PROTOTYPE(config_parse_percent);
 CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
 
index 127f26461e03687723545223fcfff67261ea383e..9cbc9e80888002f588b53a19e671844e8c44739c 100644 (file)
@@ -364,6 +364,7 @@ BindToInterface=
 RelayTarget=
 RelayAgentCircuitId=
 RelayAgentRemoteId=
+ServerAddress=
 [NextHop]
 Id=
 Gateway=
index 34e2d737ce8ce2bbaffc92e06530b3661532ae25..13044e77ae828d397fd494a682cfa5bd70df2707 100644 (file)
@@ -4,9 +4,11 @@ Name=veth-peer
 [Network]
 IPv6AcceptRA=false
 Address=192.168.5.1/24
+Address=192.168.5.2/24
 DHCPServer=yes
 
 [DHCPServer]
+Address=192.168.5.1/24
 PoolOffset=10
 PoolSize=50
 EmitRouter=yes
index 439258a53994c8a887257b941bc2817fea0107d3..cb1f45b00458275bc295bba4449e1a2e82e48778 100644 (file)
@@ -2,11 +2,11 @@
 Name=veth-peer
 
 [Network]
-Address=192.168.5.1/24
 IPv6AcceptRA=false
 DHCPServer=yes
 
 [DHCPServer]
+ServerAddress=192.168.5.1/24
 PoolOffset=10
 PoolSize=50
 DNS=192.168.5.1