for details. Defaults to <literal>medium</literal>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>UplinkInterface=</varname></term>
+ <listitem><para>Specifies the name or the index of the uplink interface, or one of the special
+ values <literal>:none</literal> and <literal>:auto</literal>. 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 <literal>:auto</literal>, the link which has a default gateway
+ with the highest priority will be automatically selected. When <literal>:none</literal>, no
+ uplink interface will be selected. Defaults to <literal>:auto</literal>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>EmitDNS=</varname></term>
<term><varname>DNS=</varname></term>
- <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses that
- are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is
- true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case
- the IPv6 link local address is distributed. If <varname>DNS=</varname> 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
- <varname>EmitDNS=</varname> is false, no DNS server information is sent in Router Advertisement
- messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
+ <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses
+ that are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is true.
+ <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case
+ the IPv6 link local address is distributed. If <varname>DNS=</varname> 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 <varname>UplinkInterface=</varname>
+ will be used. When <varname>EmitDNS=</varname> is false, no DNS server information is sent in
+ Router Advertisement messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Domains=</varname></term>
<listitem><para>A list of DNS search domains distributed via Router Advertisement messages when
- <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> 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
- <varname>EmitDomains=</varname> is false, no DNS search domain information is sent in Router
- Advertisement messages. <varname>EmitDomains=</varname> defaults to true.</para></listitem>
+ <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> 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
+ <varname>UplinkInterface=</varname> will be used. When <varname>EmitDomains=</varname> is false,
+ no DNS search domain information is sent in Router Advertisement messages.
+ <varname>EmitDomains=</varname> defaults to true.</para></listitem>
</varlistentry>
<varlistentry>
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;
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;
-}
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);
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)
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
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);
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");
#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,
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;
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);
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)
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)
}
+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);
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)
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);
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;
}
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)
EmitDomains=
Managed=
OtherInformation=
+UplinkInterface=
[IPv6PrefixDelegation]
RouterPreference=
DNSLifetimeSec=