From 2b242926927b6baed5c4d0c6b84d99b31142c9c6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 13 Jul 2021 21:27:51 +0900 Subject: [PATCH 1/4] network: update comment and man page --- man/systemd.network.xml | 12 ++++++------ src/network/networkd-queue.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index b8bb8aedf4..0de1994e20 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2382,12 +2382,12 @@ IPv6Token=prefixstable:2002:da8:1:: UplinkInterface= - Specifies name or index of uplink interface, or one of the special values - :none and :auto. When emitting DNS, NTP, or SIP servers - are enabled but no servers are specified, the servers configured in the uplink interface will - be emitted. When :auto, the link which has default gateway with higher - priority will be automatically selected. When :none, no uplink interface - will be selected. Defaults to :auto. + Specifies the name or the index of the uplink interface, or one of the special + values :none and :auto. When emitting DNS, NTP, or SIP + servers is enabled but no servers are specified, the servers configured in the uplink interface + will be emitted. When :auto, the link which has a default gateway with the + highest priority will be automatically selected. When :none, no uplink + interface will be selected. Defaults to :auto. diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index db682d434b..bc0012e6d8 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -110,7 +110,7 @@ static void request_hash_func(const Request *req, struct siphash *state) { trivial_hash_func(req->object, state); break; case REQUEST_TYPE_DHCP_SERVER: - /* This type does not have object. */ + /* This type does not have an object. */ break; case REQUEST_TYPE_IPV6_PROXY_NDP: in6_addr_hash_func(req->ipv6_proxy_ndp, state); From a254fab20dcad1f605f979da3373ff20d2e65db6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jun 2021 03:37:57 +0900 Subject: [PATCH 2/4] network: use request queue to configure IPv6 RA engine --- src/network/networkd-link.c | 8 +-- src/network/networkd-queue.c | 16 +++++- src/network/networkd-queue.h | 1 + src/network/networkd-radv.c | 98 +++++++++++++++++++++++++++--------- src/network/networkd-radv.h | 8 +-- 5 files changed, 98 insertions(+), 33 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 3a56f89f0b..17fb932f81 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -618,12 +618,8 @@ static int link_acquire_dynamic_ipv6_conf(Link *link) { log_link_debug(link, "Starting IPv6 Router Advertisements"); - r = radv_emit_dns(link); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to configure DNS or Domains in IPv6 Router Advertisement: %m"); - r = sd_radv_start(link->radv); - if (r < 0 && r != -EBUSY) + if (r < 0) return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m"); } @@ -1147,7 +1143,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = radv_configure(link); + r = link_request_radv(link); if (r < 0) return r; diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index bc0012e6d8..27a16f2951 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -43,6 +43,8 @@ static void request_free_object(RequestType type, void *object) { case REQUEST_TYPE_NEXTHOP: nexthop_free(object); break; + case REQUEST_TYPE_RADV: + break; case REQUEST_TYPE_ROUTE: route_free(object); break; @@ -121,6 +123,9 @@ static void request_hash_func(const Request *req, struct siphash *state) { case REQUEST_TYPE_NEXTHOP: nexthop_hash_func(req->nexthop, state); break; + case REQUEST_TYPE_RADV: + /* This type does not have an object. */ + break; case REQUEST_TYPE_ROUTE: route_hash_func(req->route, state); break; @@ -174,6 +179,8 @@ static int request_compare_func(const struct Request *a, const struct Request *b return nexthop_compare_func(a->nexthop, b->nexthop); case REQUEST_TYPE_ROUTE: return route_compare_func(a->route, b->route); + case REQUEST_TYPE_RADV: + return 0; case REQUEST_TYPE_ROUTING_POLICY_RULE: return routing_policy_rule_compare_func(a->rule, b->rule); case REQUEST_TYPE_SET_LINK: @@ -211,10 +218,14 @@ int link_queue_request( assert(IN_SET(type, REQUEST_TYPE_ACTIVATE_LINK, REQUEST_TYPE_DHCP_SERVER, + REQUEST_TYPE_RADV, REQUEST_TYPE_SET_LINK, REQUEST_TYPE_UP_DOWN) || object); - assert(type == REQUEST_TYPE_DHCP_SERVER || netlink_handler); + assert(IN_SET(type, + REQUEST_TYPE_DHCP_SERVER, + REQUEST_TYPE_RADV) || + netlink_handler); req = new(Request, 1); if (!req) { @@ -298,6 +309,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) { case REQUEST_TYPE_NEXTHOP: r = request_process_nexthop(req); break; + case REQUEST_TYPE_RADV: + r = request_process_radv(req); + break; case REQUEST_TYPE_ROUTE: r = request_process_route(req); break; diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index 1f4b15f087..85d2be7367 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -31,6 +31,7 @@ typedef enum RequestType { REQUEST_TYPE_IPV6_PROXY_NDP, REQUEST_TYPE_NEIGHBOR, REQUEST_TYPE_NEXTHOP, + REQUEST_TYPE_RADV, REQUEST_TYPE_ROUTE, REQUEST_TYPE_ROUTING_POLICY_RULE, REQUEST_TYPE_SET_LINK, diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 805d3fff6c..a21f69d6f3 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -10,6 +10,7 @@ #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-network.h" +#include "networkd-queue.h" #include "networkd-radv.h" #include "parse-util.h" #include "string-util.h" @@ -595,7 +596,7 @@ static int radv_set_dns(Link *link, Link *uplink) { return 0; - set_dns: +set_dns: return sd_radv_set_rdnss(link->radv, DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), dns, n_dns); @@ -634,7 +635,7 @@ static int radv_set_domains(Link *link, Link *uplink) { return 0; - set_domains: +set_domains: s = ordered_set_get_strv(search_domains); if (!s) return log_oom(); @@ -645,23 +646,6 @@ static int radv_set_domains(Link *link, Link *uplink) { } -int radv_emit_dns(Link *link) { - Link *uplink = NULL; - int r; - - (void) manager_find_uplink(link->manager, AF_INET6, link, &uplink); - - r = radv_set_dns(link, uplink); - if (r < 0) - log_link_warning_errno(link, r, "Could not set RA DNS: %m"); - - r = radv_set_domains(link, uplink); - if (r < 0) - log_link_warning_errno(link, r, "Could not set RA Domains: %m"); - - return 0; -} - static bool link_radv_enabled(Link *link) { assert(link); @@ -671,8 +655,9 @@ static bool link_radv_enabled(Link *link) { return link->network->router_prefix_delegation; } -int radv_configure(Link *link) { +static int radv_configure(Link *link) { uint16_t router_lifetime; + Link *uplink = NULL; RoutePrefix *q; Prefix *p; int r; @@ -680,9 +665,6 @@ int radv_configure(Link *link) { assert(link); assert(link->network); - if (!link_radv_enabled(link)) - return 0; - if (link->radv) return -EBUSY; @@ -748,6 +730,16 @@ int radv_configure(Link *link) { return r; } + (void) manager_find_uplink(link->manager, AF_INET6, link, &uplink); + + r = radv_set_dns(link, uplink); + if (r < 0) + return log_link_debug_errno(link, r, "Could not set RA DNS: %m"); + + r = radv_set_domains(link, uplink); + if (r < 0) + return log_link_debug_errno(link, r, "Could not set RA Domains: %m"); + return 0; } @@ -779,6 +771,66 @@ int radv_update_mac(Link *link) { return 0; } +static bool radv_is_ready_to_configure(Link *link) { + assert(link); + assert(link->network); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return false; + + if (in6_addr_is_null(&link->ipv6ll_address)) + return false; + + return true; +} + +int request_process_radv(Request *req) { + Link *link; + int r; + + assert(req); + assert(req->link); + assert(req->type == REQUEST_TYPE_RADV); + + link = req->link; + + if (!radv_is_ready_to_configure(link)) + return 0; + + r = radv_configure(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m"); + + if (link_has_carrier(link)) { + r = sd_radv_start(link->radv); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m"); + } + + log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.", + link_has_carrier(link) ? " and started." : ""); + return 1; +} + +int link_request_radv(Link *link) { + int r; + + assert(link); + + if (!link_radv_enabled(link)) + return 0; + + if (link->radv) + return 0; + + r = link_queue_request(link, REQUEST_TYPE_RADV, NULL, false, NULL, NULL, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request IPv6 Router Advertisement engine: %m"); + + log_link_debug(link, "IPv6 Router Advertisement engine is requested."); + return 0; +} + int radv_add_prefix( Link *link, const struct in6_addr *prefix, diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index f6efd32697..a8adb9bce1 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -14,8 +14,9 @@ #include "conf-parser.h" #include "networkd-util.h" -typedef struct Network Network; typedef struct Link Link; +typedef struct Network Network; +typedef struct Request Request; typedef enum RADVPrefixDelegation { RADV_PREFIX_DELEGATION_NONE = 0, @@ -50,12 +51,13 @@ void network_drop_invalid_prefixes(Network *network); void network_drop_invalid_route_prefixes(Network *network); void network_adjust_radv(Network *network); -int radv_emit_dns(Link *link); -int radv_configure(Link *link); int radv_update_mac(Link *link); int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid); +int request_process_radv(Request *req); +int link_request_radv(Link *link); + const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_; RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_; From 63295b42aee11579e4c5a909ff4f1249a18eaf93 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jun 2021 03:51:57 +0900 Subject: [PATCH 3/4] network: introduce UplinkInterface= in [IPv6SendRA] --- man/systemd.network.xml | 37 ++++++---- src/network/networkd-dhcp-server.c | 54 +-------------- src/network/networkd-dhcp-server.h | 1 - src/network/networkd-network-gperf.gperf | 3 +- src/network/networkd-network.c | 68 +++++++++++++++++++ src/network/networkd-network.h | 7 ++ src/network/networkd-radv.c | 68 +++++++++++++++---- .../fuzz-network-parser/directives.network | 1 + 8 files changed, 159 insertions(+), 80 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 0de1994e20..299c56df0a 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2586,18 +2586,28 @@ IPv6Token=prefixstable:2002:da8:1:: for details. Defaults to medium. + + UplinkInterface= + Specifies the name or the index of the uplink interface, or one of the special + values :none and :auto. When emitting DNS servers or + search domains is enabled but no servers are specified, the servers configured in the uplink + interface will be emitted. When :auto, the link which has a default gateway + with the highest priority will be automatically selected. When :none, no + uplink interface will be selected. Defaults to :auto. + + EmitDNS= DNS= - DNS= specifies a list of recursive DNS server IPv6 addresses that - are distributed via Router Advertisement messages when EmitDNS= is - true. DNS= also takes special value _link_local; in that case - the IPv6 link local address is distributed. If DNS= is empty, DNS servers are read - from the [Network] section. If the [Network] section does not contain any DNS servers either, DNS - servers from the uplink with the highest priority default route are used. When - EmitDNS= is false, no DNS server information is sent in Router Advertisement - messages. EmitDNS= defaults to true. + DNS= specifies a list of recursive DNS server IPv6 addresses + that are distributed via Router Advertisement messages when EmitDNS= is true. + DNS= also takes special value _link_local; in that case + the IPv6 link local address is distributed. If DNS= is empty, DNS servers are + read from the [Network] section. If the [Network] section does not contain any DNS servers + either, DNS servers from the uplink interface specified in UplinkInterface= + will be used. When EmitDNS= is false, no DNS server information is sent in + Router Advertisement messages. EmitDNS= defaults to true. @@ -2605,11 +2615,12 @@ IPv6Token=prefixstable:2002:da8:1:: Domains= A list of DNS search domains distributed via Router Advertisement messages when - EmitDomains= is true. If Domains= is empty, DNS search domains - are read from the [Network] section. If the [Network] section does not contain any DNS search domains - either, DNS search domains from the uplink with the highest priority default route are used. When - EmitDomains= is false, no DNS search domain information is sent in Router - Advertisement messages. EmitDomains= defaults to true. + EmitDomains= is true. If Domains= is empty, DNS search + domains are read from the [Network] section. If the [Network] section does not contain any DNS + search domains either, DNS search domains from the uplink interface specified in + UplinkInterface= will be used. When EmitDomains= is false, + no DNS search domain information is sent in Router Advertisement messages. + EmitDomains= defaults to true. diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index e4be034a4c..f190bcf428 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -106,7 +106,7 @@ static int dhcp_server_find_uplink(Link *link, Link **ret) { if (link->network->dhcp_server_uplink_index > 0) return link_get_by_index(link->manager, link->network->dhcp_server_uplink_index, ret); - if (link->network->dhcp_server_uplink_index == 0) { + if (link->network->dhcp_server_uplink_index == UPLINK_INDEX_AUTO) { /* It is not necessary to propagate error in automatic selection. */ if (manager_find_uplink(link->manager, AF_INET, link, ret) < 0) *ret = NULL; @@ -663,55 +663,3 @@ int config_parse_dhcp_server_address( network->dhcp_server_address_prefixlen = prefixlen; return 0; } - -int config_parse_dhcp_server_uplink( - 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; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue) || streq(rvalue, ":auto")) { - network->dhcp_server_uplink_index = 0; /* uplink will be selected automatically */ - network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name); - return 0; - } - - if (streq(rvalue, ":none")) { - network->dhcp_server_uplink_index = -1; /* uplink will not be selected automatically */ - network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name); - return 0; - } - - r = parse_ifindex(rvalue); - if (r > 0) { - network->dhcp_server_uplink_index = r; - network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name); - return 0; - } - - if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - r = free_and_strdup_warn(&network->dhcp_server_uplink_name, rvalue); - if (r < 0) - return r; - - network->dhcp_server_uplink_index = 0; - return 0; -} diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index 3f23f97bb8..a02cd995ec 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -15,4 +15,3 @@ int request_process_dhcp_server(Request *req); 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); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_uplink); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index de4120c47e..9166376aaa 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -265,7 +265,7 @@ IPv6AcceptRA.PrefixDenyList, config_parse_ndisc_address_filter, 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.UplinkInterface, config_parse_dhcp_server_uplink, 0, 0 +DHCPServer.UplinkInterface, config_parse_uplink, 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) @@ -333,6 +333,7 @@ IPv6SendRA.DNS, config_parse_radv_dns, IPv6SendRA.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains) IPv6SendRA.Domains, config_parse_radv_search_domains, 0, 0 IPv6SendRA.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec) +IPv6SendRA.UplinkInterface, config_parse_uplink, 0, 0 IPv6Prefix.Prefix, config_parse_prefix, 0, 0 IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0 IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 850b4f449e..f16553a6d9 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -640,6 +640,7 @@ static Network *network_free(Network *network) { free(network->dhcp_server_timezone); free(network->dhcp_server_uplink_name); + free(network->router_uplink_name); for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++) free(network->dhcp_server_emit[t].addresses); @@ -1185,6 +1186,73 @@ int config_parse_link_group( return 0; } + +int config_parse_uplink( + 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; + int *index, r; + char **name; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + + if (streq(section, "DHCPServer")) { + index = &network->dhcp_server_uplink_index; + name = &network->dhcp_server_uplink_name; + } else if (streq(section, "IPv6SendRA")) { + index = &network->router_uplink_index; + name = &network->router_uplink_name; + } else + assert_not_reached(); + + if (isempty(rvalue) || streq(rvalue, ":auto")) { + *index = UPLINK_INDEX_AUTO; + *name = mfree(*name); + return 0; + } + + if (streq(rvalue, ":none")) { + *index = UPLINK_INDEX_NONE; + *name = mfree(*name); + return 0; + } + + r = parse_ifindex(rvalue); + if (r > 0) { + *index = r; + *name = mfree(*name); + return 0; + } + + if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + /* The interface name will be resolved later. */ + r = free_and_strdup_warn(name, rvalue); + if (r < 0) + return r; + + /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean + * an uplink interface will be selected automatically. */ + *index = UPLINK_INDEX_AUTO; + return 0; +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily, "Failed to parse RequiredFamilyForOnline= setting"); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index b39063fe8a..bf0e1a733e 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -26,6 +26,10 @@ #include "resolve-util.h" #include "socket-netlink.h" +/* Special values for *_uplink_index. */ +#define UPLINK_INDEX_AUTO 0 /* uplink will be selected automatically */ +#define UPLINK_INDEX_NONE -1 /* uplink will not be selected automatically */ + typedef enum KeepConfiguration { KEEP_CONFIGURATION_NO = 0, KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0, @@ -224,6 +228,8 @@ struct Network { struct in6_addr *router_dns; unsigned n_router_dns; OrderedSet *router_search_domains; + int router_uplink_index; + char *router_uplink_name; /* DHCPv6 Prefix Delegation support */ int dhcp6_pd; @@ -364,6 +370,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode); CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy); CONFIG_PARSER_PROTOTYPE(config_parse_link_group); +CONFIG_PARSER_PROTOTYPE(config_parse_uplink); const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index a21f69d6f3..953a12b572 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -584,10 +584,7 @@ static int radv_set_dns(Link *link, Link *uplink) { goto set_dns; if (uplink) { - if (!uplink->network) { - log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us"); - return 0; - } + assert(uplink->network); r = network_get_ipv6_dns(uplink->network, &dns, &n_dns); if (r > 0) @@ -623,10 +620,7 @@ static int radv_set_domains(Link *link, Link *uplink) { goto set_domains; if (uplink) { - if (!uplink->network) { - log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us"); - return 0; - } + assert(uplink->network); search_domains = uplink->network->search_domains; if (search_domains) @@ -646,6 +640,26 @@ set_domains: } +static int radv_find_uplink(Link *link, Link **ret) { + assert(link); + + if (link->network->router_uplink_name) + return link_get_by_name(link->manager, link->network->router_uplink_name, ret); + + if (link->network->router_uplink_index > 0) + return link_get_by_index(link->manager, link->network->router_uplink_index, ret); + + if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) { + /* It is not necessary to propagate error in automatic selection. */ + if (manager_find_uplink(link->manager, AF_INET6, link, ret) < 0) + *ret = NULL; + return 0; + } + + *ret = NULL; + return 0; +} + static bool link_radv_enabled(Link *link) { assert(link); @@ -730,7 +744,7 @@ static int radv_configure(Link *link) { return r; } - (void) manager_find_uplink(link->manager, AF_INET6, link, &uplink); + (void) radv_find_uplink(link, &uplink); r = radv_set_dns(link, uplink); if (r < 0) @@ -771,7 +785,10 @@ int radv_update_mac(Link *link) { return 0; } -static bool radv_is_ready_to_configure(Link *link) { +static int radv_is_ready_to_configure(Link *link) { + bool needs_uplink = false; + int r; + assert(link); assert(link->network); @@ -781,6 +798,32 @@ static bool radv_is_ready_to_configure(Link *link) { if (in6_addr_is_null(&link->ipv6ll_address)) return false; + if (link->network->router_emit_dns && !link->network->router_dns) { + _cleanup_free_ struct in6_addr *dns = NULL; + size_t n_dns; + + r = network_get_ipv6_dns(link->network, &dns, &n_dns); + if (r < 0) + return r; + + needs_uplink = r == 0; + } + + if (link->network->router_emit_domains && + !link->network->router_search_domains && + !link->network->search_domains) + needs_uplink = true; + + if (needs_uplink) { + Link *uplink = NULL; + + if (radv_find_uplink(link, &uplink) < 0) + return false; + + if (uplink && !uplink->network) + return false; + } + return true; } @@ -794,8 +837,9 @@ int request_process_radv(Request *req) { link = req->link; - if (!radv_is_ready_to_configure(link)) - return 0; + r = radv_is_ready_to_configure(link); + if (r <= 0) + return r; r = radv_configure(link); if (r < 0) diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index f1305a04b2..2dca1cda3a 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -307,6 +307,7 @@ EmitDNS= EmitDomains= Managed= OtherInformation= +UplinkInterface= [IPv6PrefixDelegation] RouterPreference= DNSLifetimeSec= From 4a906586f80190ad7e4b08d6b5d07182b0df1938 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 16 Jun 2021 04:50:49 +0900 Subject: [PATCH 4/4] test-network: add a testcase for UplinkInterface= in [IPv6SendRA] --- .../conf/dhcp-server-with-ipv6-prefix.network | 4 ++ .../ipv6ra-prefix-client-deny-list.network | 1 + .../conf/ipv6ra-prefix-client.network | 1 + test/test-network/conf/ipv6ra-prefix.network | 3 ++ test/test-network/conf/ipv6ra-uplink.network | 8 ++++ test/test-network/systemd-networkd-tests.py | 37 +++++++++++++++---- 6 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 test/test-network/conf/ipv6ra-uplink.network diff --git a/test/test-network/conf/dhcp-server-with-ipv6-prefix.network b/test/test-network/conf/dhcp-server-with-ipv6-prefix.network index 7a1cfc65e1..4c89035139 100644 --- a/test/test-network/conf/dhcp-server-with-ipv6-prefix.network +++ b/test/test-network/conf/dhcp-server-with-ipv6-prefix.network @@ -13,3 +13,7 @@ Prefix=2002:da8:1:0::/64 [DHCPServer] DNS=192.168.5.1 NTP=192.168.5.1 + +[IPv6SendRA] +EmitDNS=no +EmitDomains=no diff --git a/test/test-network/conf/ipv6ra-prefix-client-deny-list.network b/test/test-network/conf/ipv6ra-prefix-client-deny-list.network index ce7a76f70c..be0d2e40c5 100644 --- a/test/test-network/conf/ipv6ra-prefix-client-deny-list.network +++ b/test/test-network/conf/ipv6ra-prefix-client-deny-list.network @@ -9,3 +9,4 @@ IPv6AcceptRA=yes RouterDenyList=2001::1 PrefixDenyList=2001:db8:0:2:: RouteDenyList=2001:db1:fff:: +UseDomains=yes diff --git a/test/test-network/conf/ipv6ra-prefix-client.network b/test/test-network/conf/ipv6ra-prefix-client.network index 58883658f3..e18ecaf290 100644 --- a/test/test-network/conf/ipv6ra-prefix-client.network +++ b/test/test-network/conf/ipv6ra-prefix-client.network @@ -11,3 +11,4 @@ PrefixAllowList=2001:db8:0:1:: 2001:db8:0:1:: PrefixDenyList=2001:db8:0:1:: 2001:db8:0:1:: RouteAllowList=2001:db0:fff:: 2001:db0:fff:: RouteDenyList=2001:db0:fff:: 2001:db0:fff:: +UseDomains=yes diff --git a/test/test-network/conf/ipv6ra-prefix.network b/test/test-network/conf/ipv6ra-prefix.network index cfb03f50c4..72ad808b71 100644 --- a/test/test-network/conf/ipv6ra-prefix.network +++ b/test/test-network/conf/ipv6ra-prefix.network @@ -5,6 +5,9 @@ Name=veth99 DHCP=no IPv6SendRA=yes +[IPv6SendRA] +UplinkInterface=dummy98 + [IPv6Prefix] Prefix=2001:db8:0:1::/64 diff --git a/test/test-network/conf/ipv6ra-uplink.network b/test/test-network/conf/ipv6ra-uplink.network new file mode 100644 index 0000000000..9146def210 --- /dev/null +++ b/test/test-network/conf/ipv6ra-uplink.network @@ -0,0 +1,8 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=2001:db8:1:1::1/64 +DNS=2001:db8:1:1::2 +Domains=example.com diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index dc31d133c4..11e97e0ade 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -4685,14 +4685,19 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): - links = ['veth99'] + links = [ + 'dummy98', + 'veth99', + ] units = [ + '12-dummy.netdev', '25-veth.netdev', 'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix-client.network', - 'ipv6ra-prefix.network' - ] + 'ipv6ra-prefix.network', + 'ipv6ra-uplink.network', + ] def setUp(self): remove_links(self.links) @@ -4705,10 +4710,11 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): stop_networkd(show_logs=True) def test_ipv6_route_prefix(self): - copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network') + copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network', + '12-dummy.netdev', 'ipv6ra-uplink.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) output = check_output('ip address show dev veth-peer') print(output) @@ -4727,11 +4733,20 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): self.assertNotIn('inet6 2001:db8:0:1:', output) self.assertIn('inet6 2001:db8:0:2:', output) + output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env) + print(output) + self.assertRegex(output, '2001:db8:1:1::2') + + output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env) + print(output) + self.assertIn('example.com', output) + def test_ipv6_route_prefix_deny_list(self): - copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix.network') + copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix.network', + '12-dummy.netdev', 'ipv6ra-uplink.network') start_networkd() - self.wait_online(['veth99:routable', 'veth-peer:routable']) + self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) output = check_output('ip address show dev veth-peer') print(output) @@ -4750,6 +4765,14 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): self.assertNotIn('inet6 2001:db8:0:1:', output) self.assertIn('inet6 2001:db8:0:2:', output) + output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env) + print(output) + self.assertRegex(output, '2001:db8:1:1::2') + + output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env) + print(output) + self.assertIn('example.com', output) + class NetworkdMTUTests(unittest.TestCase, Utilities): links = ['dummy98']