1
0
mirror of https://github.com/systemd/systemd synced 2026-03-24 15:55:00 +01:00

Compare commits

...

8 Commits

Author SHA1 Message Date
Yu Watanabe
801cf85935
Merge pull request #20377 from yuwata/network-bridge-fdb-20305
network: always append new bridge FDB entries
2021-08-05 02:44:24 +09:00
Zbigniew Jędrzejewski-Szmek
b176d4d377
Merge pull request #19944 from yuwata/network-radv-introduce-uplink-interface
network: introduce UplinkInterface= in [IPv6SendRA]
2021-08-04 19:36:42 +02:00
Yu Watanabe
4a906586f8 test-network: add a testcase for UplinkInterface= in [IPv6SendRA] 2021-08-04 22:21:00 +09:00
Yu Watanabe
63295b42ae network: introduce UplinkInterface= in [IPv6SendRA] 2021-08-04 22:20:56 +09:00
Yu Watanabe
a254fab20d network: use request queue to configure IPv6 RA engine 2021-08-04 22:19:14 +09:00
Yu Watanabe
2b24292692 network: update comment and man page 2021-08-04 22:19:14 +09:00
Yu Watanabe
cca07d910a test-network: add a testcase for vxlan with IPv6 local address 2021-08-04 18:33:23 +09:00
Yu Watanabe
74c1ab841f sd-netlink: always append new bridge FDB entries
This partially reverts 192a9d95ea3e058afd824d38a9cea16ad0a84a57 (#19432).

Fixes #20305.
2021-08-04 18:16:47 +09:00
22 changed files with 365 additions and 135 deletions

View File

@ -2382,12 +2382,12 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<varlistentry> <varlistentry>
<term><varname>UplinkInterface=</varname></term> <term><varname>UplinkInterface=</varname></term>
<listitem><para>Specifies name or index of uplink interface, or one of the special values <listitem><para>Specifies the name or the index of the uplink interface, or one of the special
<literal>:none</literal> and <literal>:auto</literal>. When emitting DNS, NTP, or SIP servers values <literal>:none</literal> and <literal>:auto</literal>. When emitting DNS, NTP, or SIP
are enabled but no servers are specified, the servers configured in the uplink interface will servers is enabled but no servers are specified, the servers configured in the uplink interface
be emitted. When <literal>:auto</literal>, the link which has default gateway with higher will be emitted. When <literal>:auto</literal>, the link which has a default gateway with the
priority will be automatically selected. When <literal>:none</literal>, no uplink interface highest priority will be automatically selected. When <literal>:none</literal>, no uplink
will be selected. Defaults to <literal>:auto</literal>.</para></listitem> interface will be selected. Defaults to <literal>:auto</literal>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -2586,18 +2586,28 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
for details. Defaults to <literal>medium</literal>.</para></listitem> for details. Defaults to <literal>medium</literal>.</para></listitem>
</varlistentry> </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> <varlistentry>
<term><varname>EmitDNS=</varname></term> <term><varname>EmitDNS=</varname></term>
<term><varname>DNS=</varname></term> <term><varname>DNS=</varname></term>
<listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses that <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses
are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is that are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is true.
true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case <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 the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS servers are
from the [Network] section. If the [Network] section does not contain any DNS servers either, DNS read from the [Network] section. If the [Network] section does not contain any DNS servers
servers from the uplink with the highest priority default route are used. When either, DNS servers from the uplink interface specified in <varname>UplinkInterface=</varname>
<varname>EmitDNS=</varname> is false, no DNS server information is sent in Router Advertisement will be used. When <varname>EmitDNS=</varname> is false, no DNS server information is sent in
messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem> Router Advertisement messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -2605,11 +2615,12 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<term><varname>Domains=</varname></term> <term><varname>Domains=</varname></term>
<listitem><para>A list of DNS search domains distributed via Router Advertisement messages when <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 <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> is empty, DNS search
are read from the [Network] section. If the [Network] section does not contain any DNS search domains domains are read from the [Network] section. If the [Network] section does not contain any DNS
either, DNS search domains from the uplink with the highest priority default route are used. When search domains either, DNS search domains from the uplink interface specified in
<varname>EmitDomains=</varname> is false, no DNS search domain information is sent in Router <varname>UplinkInterface=</varname> will be used. When <varname>EmitDomains=</varname> is false,
Advertisement messages. <varname>EmitDomains=</varname> defaults to true.</para></listitem> no DNS search domain information is sent in Router Advertisement messages.
<varname>EmitDomains=</varname> defaults to true.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -443,8 +443,12 @@ int sd_rtnl_message_new_neigh(sd_netlink *rtnl, sd_netlink_message **ret, uint16
if (r < 0) if (r < 0)
return r; return r;
if (nlmsg_type == RTM_NEWNEIGH) if (nlmsg_type == RTM_NEWNEIGH) {
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; if (ndm_family == AF_BRIDGE)
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
else
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
}
ndm = NLMSG_DATA((*ret)->hdr); ndm = NLMSG_DATA((*ret)->hdr);

View File

@ -106,7 +106,7 @@ static int dhcp_server_find_uplink(Link *link, Link **ret) {
if (link->network->dhcp_server_uplink_index > 0) if (link->network->dhcp_server_uplink_index > 0)
return link_get_by_index(link->manager, link->network->dhcp_server_uplink_index, ret); 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. */ /* It is not necessary to propagate error in automatic selection. */
if (manager_find_uplink(link->manager, AF_INET, link, ret) < 0) if (manager_find_uplink(link->manager, AF_INET, link, ret) < 0)
*ret = NULL; *ret = NULL;
@ -663,55 +663,3 @@ int config_parse_dhcp_server_address(
network->dhcp_server_address_prefixlen = prefixlen; network->dhcp_server_address_prefixlen = prefixlen;
return 0; 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;
}

View File

@ -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_relay_agent_suboption);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_uplink);

View File

@ -618,12 +618,8 @@ static int link_acquire_dynamic_ipv6_conf(Link *link) {
log_link_debug(link, "Starting IPv6 Router Advertisements"); 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); 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"); 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) if (r < 0)
return r; return r;
r = radv_configure(link); r = link_request_radv(link);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -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.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) 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.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.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.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.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.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains)
IPv6SendRA.Domains, config_parse_radv_search_domains, 0, 0 IPv6SendRA.Domains, config_parse_radv_search_domains, 0, 0
IPv6SendRA.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec) 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.Prefix, config_parse_prefix, 0, 0
IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0 IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0 IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0

View File

@ -640,6 +640,7 @@ static Network *network_free(Network *network) {
free(network->dhcp_server_timezone); free(network->dhcp_server_timezone);
free(network->dhcp_server_uplink_name); 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++) for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
free(network->dhcp_server_emit[t].addresses); free(network->dhcp_server_emit[t].addresses);
@ -1185,6 +1186,73 @@ int config_parse_link_group(
return 0; 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, DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
"Failed to parse RequiredFamilyForOnline= setting"); "Failed to parse RequiredFamilyForOnline= setting");

View File

@ -26,6 +26,10 @@
#include "resolve-util.h" #include "resolve-util.h"
#include "socket-netlink.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 { typedef enum KeepConfiguration {
KEEP_CONFIGURATION_NO = 0, KEEP_CONFIGURATION_NO = 0,
KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0, KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0,
@ -224,6 +228,8 @@ struct Network {
struct in6_addr *router_dns; struct in6_addr *router_dns;
unsigned n_router_dns; unsigned n_router_dns;
OrderedSet *router_search_domains; OrderedSet *router_search_domains;
int router_uplink_index;
char *router_uplink_name;
/* DHCPv6 Prefix Delegation support */ /* DHCPv6 Prefix Delegation support */
int dhcp6_pd; 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_ipv6_link_local_address_gen_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy); CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_link_group); 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); const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);

View File

@ -43,6 +43,8 @@ static void request_free_object(RequestType type, void *object) {
case REQUEST_TYPE_NEXTHOP: case REQUEST_TYPE_NEXTHOP:
nexthop_free(object); nexthop_free(object);
break; break;
case REQUEST_TYPE_RADV:
break;
case REQUEST_TYPE_ROUTE: case REQUEST_TYPE_ROUTE:
route_free(object); route_free(object);
break; break;
@ -110,7 +112,7 @@ static void request_hash_func(const Request *req, struct siphash *state) {
trivial_hash_func(req->object, state); trivial_hash_func(req->object, state);
break; break;
case REQUEST_TYPE_DHCP_SERVER: case REQUEST_TYPE_DHCP_SERVER:
/* This type does not have object. */ /* This type does not have an object. */
break; break;
case REQUEST_TYPE_IPV6_PROXY_NDP: case REQUEST_TYPE_IPV6_PROXY_NDP:
in6_addr_hash_func(req->ipv6_proxy_ndp, state); in6_addr_hash_func(req->ipv6_proxy_ndp, state);
@ -121,6 +123,9 @@ static void request_hash_func(const Request *req, struct siphash *state) {
case REQUEST_TYPE_NEXTHOP: case REQUEST_TYPE_NEXTHOP:
nexthop_hash_func(req->nexthop, state); nexthop_hash_func(req->nexthop, state);
break; break;
case REQUEST_TYPE_RADV:
/* This type does not have an object. */
break;
case REQUEST_TYPE_ROUTE: case REQUEST_TYPE_ROUTE:
route_hash_func(req->route, state); route_hash_func(req->route, state);
break; 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); return nexthop_compare_func(a->nexthop, b->nexthop);
case REQUEST_TYPE_ROUTE: case REQUEST_TYPE_ROUTE:
return route_compare_func(a->route, b->route); return route_compare_func(a->route, b->route);
case REQUEST_TYPE_RADV:
return 0;
case REQUEST_TYPE_ROUTING_POLICY_RULE: case REQUEST_TYPE_ROUTING_POLICY_RULE:
return routing_policy_rule_compare_func(a->rule, b->rule); return routing_policy_rule_compare_func(a->rule, b->rule);
case REQUEST_TYPE_SET_LINK: case REQUEST_TYPE_SET_LINK:
@ -211,10 +218,14 @@ int link_queue_request(
assert(IN_SET(type, assert(IN_SET(type,
REQUEST_TYPE_ACTIVATE_LINK, REQUEST_TYPE_ACTIVATE_LINK,
REQUEST_TYPE_DHCP_SERVER, REQUEST_TYPE_DHCP_SERVER,
REQUEST_TYPE_RADV,
REQUEST_TYPE_SET_LINK, REQUEST_TYPE_SET_LINK,
REQUEST_TYPE_UP_DOWN) || REQUEST_TYPE_UP_DOWN) ||
object); 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); req = new(Request, 1);
if (!req) { if (!req) {
@ -298,6 +309,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
case REQUEST_TYPE_NEXTHOP: case REQUEST_TYPE_NEXTHOP:
r = request_process_nexthop(req); r = request_process_nexthop(req);
break; break;
case REQUEST_TYPE_RADV:
r = request_process_radv(req);
break;
case REQUEST_TYPE_ROUTE: case REQUEST_TYPE_ROUTE:
r = request_process_route(req); r = request_process_route(req);
break; break;

View File

@ -31,6 +31,7 @@ typedef enum RequestType {
REQUEST_TYPE_IPV6_PROXY_NDP, REQUEST_TYPE_IPV6_PROXY_NDP,
REQUEST_TYPE_NEIGHBOR, REQUEST_TYPE_NEIGHBOR,
REQUEST_TYPE_NEXTHOP, REQUEST_TYPE_NEXTHOP,
REQUEST_TYPE_RADV,
REQUEST_TYPE_ROUTE, REQUEST_TYPE_ROUTE,
REQUEST_TYPE_ROUTING_POLICY_RULE, REQUEST_TYPE_ROUTING_POLICY_RULE,
REQUEST_TYPE_SET_LINK, REQUEST_TYPE_SET_LINK,

View File

@ -10,6 +10,7 @@
#include "networkd-link.h" #include "networkd-link.h"
#include "networkd-manager.h" #include "networkd-manager.h"
#include "networkd-network.h" #include "networkd-network.h"
#include "networkd-queue.h"
#include "networkd-radv.h" #include "networkd-radv.h"
#include "parse-util.h" #include "parse-util.h"
#include "string-util.h" #include "string-util.h"
@ -583,10 +584,7 @@ static int radv_set_dns(Link *link, Link *uplink) {
goto set_dns; goto set_dns;
if (uplink) { if (uplink) {
if (!uplink->network) { assert(uplink->network);
log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us");
return 0;
}
r = network_get_ipv6_dns(uplink->network, &dns, &n_dns); r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
if (r > 0) if (r > 0)
@ -595,7 +593,7 @@ static int radv_set_dns(Link *link, Link *uplink) {
return 0; return 0;
set_dns: set_dns:
return sd_radv_set_rdnss(link->radv, return sd_radv_set_rdnss(link->radv,
DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
dns, n_dns); dns, n_dns);
@ -622,10 +620,7 @@ static int radv_set_domains(Link *link, Link *uplink) {
goto set_domains; goto set_domains;
if (uplink) { if (uplink) {
if (!uplink->network) { assert(uplink->network);
log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us");
return 0;
}
search_domains = uplink->network->search_domains; search_domains = uplink->network->search_domains;
if (search_domains) if (search_domains)
@ -634,7 +629,7 @@ static int radv_set_domains(Link *link, Link *uplink) {
return 0; return 0;
set_domains: set_domains:
s = ordered_set_get_strv(search_domains); s = ordered_set_get_strv(search_domains);
if (!s) if (!s)
return log_oom(); return log_oom();
@ -645,20 +640,23 @@ static int radv_set_domains(Link *link, Link *uplink) {
} }
int radv_emit_dns(Link *link) { static int radv_find_uplink(Link *link, Link **ret) {
Link *uplink = NULL; assert(link);
int r;
(void) manager_find_uplink(link->manager, AF_INET6, link, &uplink); if (link->network->router_uplink_name)
return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
r = radv_set_dns(link, uplink); if (link->network->router_uplink_index > 0)
if (r < 0) return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
log_link_warning_errno(link, r, "Could not set RA DNS: %m");
r = radv_set_domains(link, uplink); if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
if (r < 0) /* It is not necessary to propagate error in automatic selection. */
log_link_warning_errno(link, r, "Could not set RA Domains: %m"); if (manager_find_uplink(link->manager, AF_INET6, link, ret) < 0)
*ret = NULL;
return 0;
}
*ret = NULL;
return 0; return 0;
} }
@ -671,8 +669,9 @@ static bool link_radv_enabled(Link *link) {
return link->network->router_prefix_delegation; return link->network->router_prefix_delegation;
} }
int radv_configure(Link *link) { static int radv_configure(Link *link) {
uint16_t router_lifetime; uint16_t router_lifetime;
Link *uplink = NULL;
RoutePrefix *q; RoutePrefix *q;
Prefix *p; Prefix *p;
int r; int r;
@ -680,9 +679,6 @@ int radv_configure(Link *link) {
assert(link); assert(link);
assert(link->network); assert(link->network);
if (!link_radv_enabled(link))
return 0;
if (link->radv) if (link->radv)
return -EBUSY; return -EBUSY;
@ -748,6 +744,16 @@ int radv_configure(Link *link) {
return r; return r;
} }
(void) radv_find_uplink(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; return 0;
} }
@ -779,6 +785,96 @@ int radv_update_mac(Link *link) {
return 0; return 0;
} }
static int radv_is_ready_to_configure(Link *link) {
bool needs_uplink = false;
int r;
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;
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;
}
int request_process_radv(Request *req) {
Link *link;
int r;
assert(req);
assert(req->link);
assert(req->type == REQUEST_TYPE_RADV);
link = req->link;
r = radv_is_ready_to_configure(link);
if (r <= 0)
return r;
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( int radv_add_prefix(
Link *link, Link *link,
const struct in6_addr *prefix, const struct in6_addr *prefix,

View File

@ -14,8 +14,9 @@
#include "conf-parser.h" #include "conf-parser.h"
#include "networkd-util.h" #include "networkd-util.h"
typedef struct Network Network;
typedef struct Link Link; typedef struct Link Link;
typedef struct Network Network;
typedef struct Request Request;
typedef enum RADVPrefixDelegation { typedef enum RADVPrefixDelegation {
RADV_PREFIX_DELEGATION_NONE = 0, 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_drop_invalid_route_prefixes(Network *network);
void network_adjust_radv(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_update_mac(Link *link);
int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
uint32_t lifetime_preferred, uint32_t lifetime_valid); 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_; const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_; RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;

View File

@ -307,6 +307,7 @@ EmitDNS=
EmitDomains= EmitDomains=
Managed= Managed=
OtherInformation= OtherInformation=
UplinkInterface=
[IPv6PrefixDelegation] [IPv6PrefixDelegation]
RouterPreference= RouterPreference=
DNSLifetimeSec= DNSLifetimeSec=

View File

@ -0,0 +1,7 @@
[NetDev]
Name=vxlan97
Kind=vxlan
[VXLAN]
VNI=4831583
Local=fe80::281:8eff:fef0:73aa

View File

@ -13,3 +13,7 @@ Prefix=2002:da8:1:0::/64
[DHCPServer] [DHCPServer]
DNS=192.168.5.1 DNS=192.168.5.1
NTP=192.168.5.1 NTP=192.168.5.1
[IPv6SendRA]
EmitDNS=no
EmitDomains=no

View File

@ -9,3 +9,4 @@ IPv6AcceptRA=yes
RouterDenyList=2001::1 RouterDenyList=2001::1
PrefixDenyList=2001:db8:0:2:: PrefixDenyList=2001:db8:0:2::
RouteDenyList=2001:db1:fff:: RouteDenyList=2001:db1:fff::
UseDomains=yes

View File

@ -11,3 +11,4 @@ PrefixAllowList=2001:db8:0:1:: 2001:db8:0:1::
PrefixDenyList=2001:db8:0:1:: 2001:db8:0:1:: PrefixDenyList=2001:db8:0:1:: 2001:db8:0:1::
RouteAllowList=2001:db0:fff:: 2001:db0:fff:: RouteAllowList=2001:db0:fff:: 2001:db0:fff::
RouteDenyList=2001:db0:fff:: 2001:db0:fff:: RouteDenyList=2001:db0:fff:: 2001:db0:fff::
UseDomains=yes

View File

@ -5,6 +5,9 @@ Name=veth99
DHCP=no DHCP=no
IPv6SendRA=yes IPv6SendRA=yes
[IPv6SendRA]
UplinkInterface=dummy98
[IPv6Prefix] [IPv6Prefix]
Prefix=2001:db8:0:1::/64 Prefix=2001:db8:0:1::/64

View File

@ -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

View File

@ -0,0 +1,24 @@
[Match]
Name=vxlan97
[Network]
IPv6AcceptRA=no
LinkLocalAddressing=yes
[BridgeFDB]
MACAddress=00:00:00:00:00:00
Destination=fe80::27c:16ff:fec0:6c74
OutgoingInterface=test1
VNI=4831583
[BridgeFDB]
MACAddress=00:00:00:00:00:00
Destination=fe80::2a2:e4ff:fef9:2269
OutgoingInterface=test1
VNI=4831583
[BridgeFDB]
MACAddress=00:00:00:00:00:00
Destination=fe80::23b:d2ff:fe95:967f
OutgoingInterface=test1
VNI=4831583

View File

@ -5,3 +5,4 @@ Name=test1
IPv6AcceptRA=false IPv6AcceptRA=false
LinkLocalAddressing=yes LinkLocalAddressing=yes
VXLAN=vxlan99 VXLAN=vxlan99
VXLAN=vxlan97

View File

@ -854,6 +854,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'vtitun98', 'vtitun98',
'vtitun99', 'vtitun99',
'vxcan99', 'vxcan99',
'vxlan97',
'vxlan98', 'vxlan98',
'vxlan99', 'vxlan99',
'wg97', 'wg97',
@ -944,6 +945,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-vti-tunnel.netdev', '25-vti-tunnel.netdev',
'25-vxcan.netdev', '25-vxcan.netdev',
'25-vxlan-independent.netdev', '25-vxlan-independent.netdev',
'25-vxlan-ipv6.netdev',
'25-vxlan.netdev', '25-vxlan.netdev',
'25-wireguard-23-peers.netdev', '25-wireguard-23-peers.netdev',
'25-wireguard-23-peers.network', '25-wireguard-23-peers.network',
@ -974,6 +976,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'sit.network', 'sit.network',
'vti6.network', 'vti6.network',
'vti.network', 'vti.network',
'vxlan-ipv6.network',
'vxlan-test1.network', 'vxlan-test1.network',
'vxlan.network', 'vxlan.network',
'xfrm.network', 'xfrm.network',
@ -1635,36 +1638,43 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
def test_vxlan(self): def test_vxlan(self):
copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network', copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network',
'25-vxlan-ipv6.netdev', 'vxlan-ipv6.network',
'25-vxlan-independent.netdev', 'netdev-link-local-addressing-yes.network', '25-vxlan-independent.netdev', 'netdev-link-local-addressing-yes.network',
'11-dummy.netdev', 'vxlan-test1.network') '11-dummy.netdev', 'vxlan-test1.network')
start_networkd() start_networkd()
self.wait_online(['test1:degraded', 'vxlan99:degraded', 'vxlan98:degraded']) self.wait_online(['test1:degraded', 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded'])
output = check_output('ip -d link show vxlan99') output = check_output('ip -d link show vxlan99')
print(output) print(output)
self.assertRegex(output, '999') self.assertIn('999', output)
self.assertRegex(output, '5555') self.assertIn('5555', output)
self.assertRegex(output, 'l2miss') self.assertIn('l2miss', output)
self.assertRegex(output, 'l3miss') self.assertIn('l3miss', output)
self.assertRegex(output, 'udpcsum') self.assertIn('udpcsum', output)
self.assertRegex(output, 'udp6zerocsumtx') self.assertIn('udp6zerocsumtx', output)
self.assertRegex(output, 'udp6zerocsumrx') self.assertIn('udp6zerocsumrx', output)
self.assertRegex(output, 'remcsumtx') self.assertIn('remcsumtx', output)
self.assertRegex(output, 'remcsumrx') self.assertIn('remcsumrx', output)
self.assertRegex(output, 'gbp') self.assertIn('gbp', output)
output = check_output('bridge fdb show dev vxlan99') output = check_output('bridge fdb show dev vxlan99')
print(output) print(output)
self.assertRegex(output, '00:11:22:33:44:55 dst 10.0.0.5 self permanent') self.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output)
self.assertRegex(output, '00:11:22:33:44:66 dst 10.0.0.6 self permanent') self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
self.assertRegex(output, '00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent') self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'vxlan99', env=env) output = check_output(*networkctl_cmd, '-n', '0', 'status', 'vxlan99', env=env)
print(output) print(output)
self.assertRegex(output, 'VNI: 999') self.assertIn('VNI: 999', output)
self.assertRegex(output, 'Destination Port: 5555') self.assertIn('Destination Port: 5555', output)
self.assertRegex(output, 'Underlying Device: test1') self.assertIn('Underlying Device: test1', output)
output = check_output('bridge fdb show dev vxlan97')
print(output)
self.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output)
self.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output)
self.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output)
def test_macsec(self): def test_macsec(self):
copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key', copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
@ -4685,14 +4695,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') 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): class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
links = ['veth99'] links = [
'dummy98',
'veth99',
]
units = [ units = [
'12-dummy.netdev',
'25-veth.netdev', '25-veth.netdev',
'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix-client-deny-list.network',
'ipv6ra-prefix-client.network', 'ipv6ra-prefix-client.network',
'ipv6ra-prefix.network' 'ipv6ra-prefix.network',
] 'ipv6ra-uplink.network',
]
def setUp(self): def setUp(self):
remove_links(self.links) remove_links(self.links)
@ -4705,10 +4720,11 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
stop_networkd(show_logs=True) stop_networkd(show_logs=True)
def test_ipv6_route_prefix(self): 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() 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') output = check_output('ip address show dev veth-peer')
print(output) print(output)
@ -4727,11 +4743,20 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
self.assertNotIn('inet6 2001:db8:0:1:', output) self.assertNotIn('inet6 2001:db8:0:1:', output)
self.assertIn('inet6 2001:db8:0:2:', 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): 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() 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') output = check_output('ip address show dev veth-peer')
print(output) print(output)
@ -4750,6 +4775,14 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
self.assertNotIn('inet6 2001:db8:0:1:', output) self.assertNotIn('inet6 2001:db8:0:1:', output)
self.assertIn('inet6 2001:db8:0:2:', 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): class NetworkdMTUTests(unittest.TestCase, Utilities):
links = ['dummy98'] links = ['dummy98']