From: Yu Watanabe Date: Tue, 15 Jun 2021 18:51:57 +0000 (+0900) Subject: network: introduce UplinkInterface= in [IPv6SendRA] X-Git-Tag: v250-rc1~879^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=63295b42aee11579e4c5a909ff4f1249a18eaf93;p=thirdparty%2Fsystemd.git network: introduce UplinkInterface= in [IPv6SendRA] --- diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 0de1994e209..299c56df0ad 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 e4be034a4c7..f190bcf428b 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 3f23f97bb8d..a02cd995ec0 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 de4120c47ec..9166376aaa8 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 850b4f449e1..f16553a6d9d 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 b39063fe8ad..bf0e1a733e7 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 a21f69d6f3b..953a12b5724 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 f1305a04b2b..2dca1cda3ab 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=