From cf0747725ddb96cd664ea1f9b169febfa53d1615 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 14 May 2021 16:35:34 +0900 Subject: [PATCH 1/7] conf-parser: introduce config_parse_in_addr_non_null() --- src/shared/conf-parser.c | 53 ++++++++++++++++++++++++++++++++++++++++ src/shared/conf-parser.h | 1 + 2 files changed, 54 insertions(+) diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index fa4079cff7..ce3af64962 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -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"); diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index c4b9891428..c3a138274d 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -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); From c9f2db2c408d728f1a21f6951a8115470504bd0b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 14 May 2021 16:52:42 +0900 Subject: [PATCH 2/7] network: use config_parse_in_addr_non_null() --- src/network/networkd-dhcp-server.c | 26 -------------- src/network/networkd-dhcp-server.h | 1 - src/network/networkd-dhcp6.c | 44 ------------------------ src/network/networkd-dhcp6.h | 1 - src/network/networkd-network-gperf.gperf | 4 +-- 5 files changed, 2 insertions(+), 74 deletions(-) diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index d2efc30185..07d6eb95d1 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -444,32 +444,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, diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index 7a1e3a5173..e8c8f192f3 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -10,5 +10,4 @@ typedef struct Link Link; 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); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 7061afe0a2..e8c7d8ebd8 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -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; -} diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index f24d6eea5a..82becb0321 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -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_; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 732b53f71d..b3e03bff3f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -261,7 +261,7 @@ 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.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 +314,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) From 473680be325d9c849f5d344f682adc94b729b355 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 17 May 2021 19:31:55 +0900 Subject: [PATCH 3/7] network: introduce address_set_broadcast() --- src/network/networkd-address.c | 57 +++++++++++++++++++++------------- src/network/networkd-address.h | 1 + src/network/networkd-ipv4ll.c | 2 +- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 5a3388c3c4..c350b6e933 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -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; - return a->family == AF_INET && - in4_addr_is_null(&a->in_addr_peer.in) && - a->prefixlen <= 30; + 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; + + /* 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"); } @@ -825,7 +848,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 +869,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 +883,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) @@ -1957,10 +1973,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); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index e9fddddf9f..34dea4aa64 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -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); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 8e2d761cf9..12ba5c085a 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -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; From 99b5f4f7b850d54647c464063830ef8f28740c06 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 17 May 2021 19:33:02 +0900 Subject: [PATCH 4/7] network: use UINT32_C() macro --- src/network/networkd-address-label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/networkd-address-label.c b/src/network/networkd-address-label.c index 7b7b72469e..9c9d283868 100644 --- a/src/network/networkd-address-label.c +++ b/src/network/networkd-address-label.c @@ -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; } From 998545a7d9607b49facd0e9868b893ac24942c51 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 14 May 2021 17:33:32 +0900 Subject: [PATCH 5/7] network: address: introduce link_get_ipv4/ipv6_address() --- src/network/networkd-address.c | 48 +++++++++++++++++++++++++--------- src/network/networkd-address.h | 3 ++- src/network/networkd-ndisc.c | 4 +-- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index c350b6e933..462907439a 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -489,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; @@ -505,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); @@ -529,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; @@ -537,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) + 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); - if (link_get_ipv4_address(link->addresses_foreign, &address->in, &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) diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 34dea4aa64..f63848e2e7 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -66,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); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index cfcf4cc36a..56aa5ec8a7 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -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; From 0017ba3165f69e8afb7f73127281bb9a7e5b5641 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 14 May 2021 20:27:33 +0900 Subject: [PATCH 6/7] network: dhcp-server: introduce ServerAddress= setting This may be useful when the link which DHCP server running on has multiple static addresses. --- man/systemd.network.xml | 8 ++ src/network/networkd-address.c | 26 ++++ src/network/networkd-dhcp-server.c | 118 +++++++++++++++--- src/network/networkd-dhcp-server.h | 6 +- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 2 + src/network/networkd-network.h | 3 +- .../fuzz-network-parser/directives.network | 1 + 8 files changed, 143 insertions(+), 22 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index fe330be0be..02e464b193 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2344,6 +2344,14 @@ IPv6Token=prefixstable:2002:da8:1:: + + ServerAddress= + Specifies server address for the DHCP server. Takes an IPv4 address with prefix + length, e.g., 192.168.0.1/24. 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. + + PoolOffset= PoolSize= diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 462907439a..29ab83a425 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -1157,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); diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index 07d6eb95d1..0a396a957f 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -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 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 not, then select one from static addresses. */ + SET_FOREACH(address, link->static_addresses) if (address->family == AF_INET && - in_addr_is_set(address->family, &address->in_addr)) - return address; + !in4_addr_is_localhost(&address->in_addr.in) && + in4_addr_is_null(&address->in_addr_peer.in)) { + *ret = address; + return 0; + } - /* 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; - - 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; @@ -492,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; +} diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index e8c8f192f3..d58978cc05 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -2,12 +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_emit); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index b3e03bff3f..6616b14774 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -261,6 +261,7 @@ 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.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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 482c31064f..6f68759a88 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -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; } diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index e27a27cd02..c1316d2abc 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -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; diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 127f26461e..9cbc9e8088 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -364,6 +364,7 @@ BindToInterface= RelayTarget= RelayAgentCircuitId= RelayAgentRemoteId= +ServerAddress= [NextHop] Id= Gateway= From 72ffb9133d686bef6d9d79e9d2899571651d5c1b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 14 May 2021 20:57:10 +0900 Subject: [PATCH 7/7] test-network: add testcases for ServerAddress= --- test/test-network/conf/dhcp-server-timezone-router.network | 2 ++ test/test-network/conf/dhcp-server.network | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test-network/conf/dhcp-server-timezone-router.network b/test/test-network/conf/dhcp-server-timezone-router.network index 34e2d737ce..13044e77ae 100644 --- a/test/test-network/conf/dhcp-server-timezone-router.network +++ b/test/test-network/conf/dhcp-server-timezone-router.network @@ -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 diff --git a/test/test-network/conf/dhcp-server.network b/test/test-network/conf/dhcp-server.network index 439258a539..cb1f45b004 100644 --- a/test/test-network/conf/dhcp-server.network +++ b/test/test-network/conf/dhcp-server.network @@ -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