1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-10-26 17:25:34 +03:00

network: introduce per-interface IP forwarding settings

This deprecates IPForward= setting, which unconditionally controled
the global setting, even though it is a setting in .network file.

Instead, this introduces new IPv4Forwarding= and IPv6Forwarding=
settings both in .network and networkd.conf.
If these settings are specified in a .network file, then the
per-interface forwarding setting will be configured.
If specified in networkd.conf, then the global IP forwarding setting will
be configured.

Closes #30648.
This commit is contained in:
Yu Watanabe 2024-02-21 06:20:45 +09:00 committed by Luca Boccassi
parent 755fdfffa0
commit 3976c43092
24 changed files with 197 additions and 125 deletions

View File

@ -116,6 +116,34 @@
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv4Forwarding=</varname></term>
<listitem>
<para>Configures IPv4 packet forwarding for the system. Takes a boolean value. This controls the
<filename>net.ipv4.conf.default.forwarding</filename> and
<filename>net.ipv4.conf.all.forwarding</filename>sysctl options. See
<ulink url="https://docs.kernel.org/networking/ip-sysctl.html">IP Sysctl</ulink>
for more details about the sysctl options. Defaults to unset and the sysctl options will not be
changed.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6Forwarding=</varname></term>
<listitem>
<para>Configures IPv6 packet forwarding for the system. Takes a boolean value. This controls the
<filename>net.ipv6.conf.default.forwarding</filename> and
<filename>net.ipv6.conf.all.forwarding</filename> sysctl options. See
<ulink url="https://docs.kernel.org/networking/ip-sysctl.html">IP Sysctl</ulink>
for more details about the sysctl options. Defaults to unset and the sysctl options will not be
changed.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6PrivacyExtensions=</varname></term>
<listitem>

View File

@ -803,26 +803,43 @@ Table=1234</programlisting></para>
</varlistentry>
<varlistentry>
<term><varname>IPForward=</varname></term>
<term><varname>IPv4Forwarding=</varname></term>
<listitem>
<para>Configures IP packet forwarding for the system. If enabled, incoming packets on any
network interface will be forwarded to any other interfaces according to the routing table.
Takes a boolean, or the values <literal>ipv4</literal> or <literal>ipv6</literal>, which only
enable IP packet forwarding for the specified address family. This controls the
<filename>net.ipv4.ip_forward</filename> and <filename>net.ipv6.conf.all.forwarding</filename>
sysctl options of the network interface (see
<para>Configures IPv4 packet forwarding for the interface. Takes a boolean value. This controls the
<filename>net.ipv4.conf.<replaceable>INTERFACE</replaceable>.forwarding</filename> sysctl option of
the network interface. See
<ulink url="https://docs.kernel.org/networking/ip-sysctl.html">IP Sysctl</ulink>
for details about sysctl options). Defaults to <literal>no</literal>.</para>
for more details about the sysctl option. Defaults to true if <varname>IPMasquerade=</varname> is
enabled for IPv4, otherwise the value specified to the same setting in
<citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
will be used. If none of them are specified, the sysctl option will not be changed.</para>
<para>Note: this setting controls a global kernel option, and does so one way only: if a
network that has this setting enabled is set up the global setting is turned on. However,
it is never turned off again, even after all networks with this setting enabled are shut
down again.</para>
<para>To allow IP packet forwarding only between specific network interfaces use a firewall.
<para>To control the global setting, use the same setting in
<citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
<xi:include href="version-info.xml" xpointer="v219"/>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6Forwarding=</varname></term>
<listitem>
<para>Configures IPv6 packet forwarding for the interface. Takes a boolean value. This controls the
<filename>net.ipv6.conf.<replaceable>INTERFACE</replaceable>.forwarding</filename> sysctl option of
the network interface. See
<ulink url="https://docs.kernel.org/networking/ip-sysctl.html">IP Sysctl</ulink>
for more details about the sysctl option. Defaults to true if <varname>IPMasquerade=</varname> is
enabled for IPv6 or <varname>IPv6SendRA=</varname> is enabled, otherwise the value specified to the
same setting in
<citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
will be used. If none of them are specified, the sysctl option will not be changed.</para>
<para>To control the global setting, use the same setting in
<citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
@ -832,9 +849,7 @@ Table=1234</programlisting></para>
<para>Configures IP masquerading for the network interface. If enabled, packets forwarded
from the network interface will be appear as coming from the local host. Takes one of
<literal>ipv4</literal>, <literal>ipv6</literal>, <literal>both</literal>, or
<literal>no</literal>. Defaults to <literal>no</literal>. If enabled, this automatically sets
<varname>IPForward=</varname> to one of <literal>ipv4</literal>, <literal>ipv6</literal> or
<literal>yes</literal>.</para>
<literal>no</literal>. Defaults to <literal>no</literal>.</para>
<para>Note. Any positive boolean values such as <literal>yes</literal> or
<literal>true</literal> are now deprecated. Please use one of the values in the above.</para>

View File

@ -27,6 +27,8 @@ Network.ManageForeignRoutingPolicyRules, config_parse_bool,
Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes)
Network.ManageForeignNextHops, config_parse_bool, 0, offsetof(Manager, manage_foreign_nexthops)
Network.RouteTable, config_parse_route_table_names, 0, 0
Network.IPv4Forwarding, config_parse_tristate, 0, offsetof(Manager, ip_forwarding[0])
Network.IPv6Forwarding, config_parse_tristate, 0, offsetof(Manager, ip_forwarding[1])
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Manager, ipv6_privacy_extensions)
DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp_duid)
DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp_duid)

View File

@ -8,6 +8,7 @@
#include "networkd-link.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-sysctl.h"
#include "parse-util.h"
#include "string-table.h"
#include "string-util.h"
@ -69,9 +70,8 @@ int link_lldp_tx_configure(Link *link) {
SD_LLDP_SYSTEM_CAPABILITIES_STATION |
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE |
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER,
(link->network->ip_forward != ADDRESS_FAMILY_NO) ?
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER :
SD_LLDP_SYSTEM_CAPABILITIES_STATION);
(link_get_ip_forwarding(link, AF_INET) > 0 || link_get_ip_forwarding(link, AF_INET6) > 0) ?
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : SD_LLDP_SYSTEM_CAPABILITIES_STATION);
if (r < 0)
return r;

View File

@ -577,6 +577,7 @@ int manager_new(Manager **ret, bool test_mode) {
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
.ip_forwarding = { -1, -1, },
};
*ret = TAKE_PTR(m);
@ -659,6 +660,8 @@ int manager_start(Manager *m) {
assert(m);
manager_set_sysctl(m);
r = manager_start_speed_meter(m);
if (r < 0)
return log_error_errno(r, "Failed to initialize speed meter: %m");

View File

@ -107,6 +107,9 @@ struct Manager {
Hashmap *tuntap_fds_by_name;
unsigned reloading;
/* sysctl */
int ip_forwarding[2];
};
int manager_new(Manager **ret, bool test_mode);

View File

@ -20,6 +20,7 @@
#include "networkd-queue.h"
#include "networkd-route.h"
#include "networkd-state-file.h"
#include "networkd-sysctl.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@ -52,8 +53,16 @@ bool link_ipv6_accept_ra_enabled(Link *link) {
if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
return false;
assert(link->network->ipv6_accept_ra >= 0);
return link->network->ipv6_accept_ra;
if (link->network->ipv6_accept_ra >= 0)
return link->network->ipv6_accept_ra;
/* Accept RAs if IPv6 forwarding is disabled, and ignore RAs if IPv6 forwarding is enabled. */
int t = link_get_ip_forwarding(link, AF_INET6);
if (t >= 0)
return !t;
/* Otherwise, defaults to true. */
return true;
}
void network_adjust_ipv6_accept_ra(Network *network) {
@ -66,10 +75,6 @@ void network_adjust_ipv6_accept_ra(Network *network) {
network->ipv6_accept_ra = false;
}
if (network->ipv6_accept_ra < 0)
/* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
network->ipv6_accept_ra = !FLAGS_SET(network->ip_forward, ADDRESS_FAMILY_IPV6);
/* When RouterAllowList=, PrefixAllowList= or RouteAllowList= are specified, then
* RouterDenyList=, PrefixDenyList= or RouteDenyList= are ignored, respectively. */
if (!set_isempty(network->ndisc_allow_listed_router))

View File

@ -124,7 +124,9 @@ Network.DNSOverTLS, config_parse_dns_over_tls_mode,
Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, offsetof(Network, dnssec_negative_trust_anchors)
Network.NTP, config_parse_ntp, 0, offsetof(Network, ntp)
Network.IPForward, config_parse_address_family_with_kernel, 0, offsetof(Network, ip_forward)
Network.IPForward, config_parse_ip_forward_deprecated, 0, 0
Network.IPv4Forwarding, config_parse_tristate, 0, offsetof(Network, ip_forwarding[0])
Network.IPv6Forwarding, config_parse_tristate, 0, offsetof(Network, ip_forwarding[1])
Network.IPMasquerade, config_parse_ip_masquerade, 0, offsetof(Network, ip_masquerade)
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
Network.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)

View File

@ -225,9 +225,6 @@ int network_verify(Network *network) {
network->ipv6ll_address_gen_mode < 0)
network->ipv6ll_address_gen_mode = IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY;
/* IPMasquerade implies IPForward */
network->ip_forward |= network->ip_masquerade;
network_adjust_ipv6_proxy_ndp(network);
network_adjust_ipv6_accept_ra(network);
network_adjust_dhcp(network);
@ -465,6 +462,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.link_local = _ADDRESS_FAMILY_INVALID,
.ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
.ip_forwarding = { -1, -1, },
.ipv4_accept_local = -1,
.ipv4_route_localnet = -1,
.ipv6_privacy_extensions = _IPV6_PRIVACY_EXTENSIONS_INVALID,

View File

@ -320,7 +320,7 @@ struct Network {
int ipoib_umcast;
/* sysctl settings */
AddressFamily ip_forward;
int ip_forwarding[2];
int ipv4_accept_local;
int ipv4_route_localnet;
int ipv6_dad_transmits;

View File

@ -840,7 +840,7 @@ int route_section_verify_nexthops(Route *route) {
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (route->nexthop.family == AF_INET6 && !route->network->ipv6_accept_ra)
if (route->nexthop.family == AF_INET6 && route->network->ipv6_accept_ra == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
"Ignoring [Route] section from line %u.",

View File

@ -4,6 +4,7 @@
#include <linux/if.h>
#include <linux/if_arp.h>
#include "af-list.h"
#include "missing_network.h"
#include "networkd-link.h"
#include "networkd-manager.h"
@ -13,6 +14,40 @@
#include "string-table.h"
#include "sysctl-util.h"
static void manager_set_ip_forwarding(Manager *manager, int family) {
int r, t;
assert(manager);
assert(IN_SET(family, AF_INET, AF_INET6));
if (family == AF_INET6 && !socket_ipv6_is_supported())
return;
t = manager->ip_forwarding[family == AF_INET6];
if (t < 0)
return; /* keep */
/* First, set the default value. */
r = sysctl_write_ip_property_boolean(family, "default", "forwarding", t);
if (r < 0)
log_warning_errno(r, "Failed to %s the default %s forwarding: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
/* Then, set the value to all interfaces. */
r = sysctl_write_ip_property_boolean(family, "all", "forwarding", t);
if (r < 0)
log_warning_errno(r, "Failed to %s %s forwarding for all interfaces: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
}
void manager_set_sysctl(Manager *manager) {
assert(manager);
assert(!manager->test_mode);
manager_set_ip_forwarding(manager, AF_INET);
manager_set_ip_forwarding(manager, AF_INET6);
}
static bool link_is_configured_for_family(Link *link, int family) {
assert(link);
@ -70,48 +105,50 @@ static int link_set_proxy_arp_pvlan(Link *link) {
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp_pvlan", link->network->proxy_arp_pvlan > 0);
}
static bool link_ip_forward_enabled(Link *link, int family) {
int link_get_ip_forwarding(Link *link, int family) {
assert(link);
assert(link->manager);
assert(link->network);
assert(IN_SET(family, AF_INET, AF_INET6));
/* If it is explicitly specified, then honor the setting. */
int t = link->network->ip_forwarding[family == AF_INET6];
if (t >= 0)
return t;
/* If IPMasquerade= is enabled, also enable IP forwarding. */
if (family == AF_INET && FLAGS_SET(link->network->ip_masquerade, ADDRESS_FAMILY_IPV4))
return true;
if (family == AF_INET6 && FLAGS_SET(link->network->ip_masquerade, ADDRESS_FAMILY_IPV6))
return true;
/* If IPv6SendRA= is enabled, also enable IPv6 forwarding. */
if (family == AF_INET6 && link_radv_enabled(link))
return true;
/* Otherwise, use the global setting. */
return link->manager->ip_forwarding[family == AF_INET6];
}
static int link_set_ip_forwarding(Link *link, int family) {
int r, t;
assert(link);
assert(IN_SET(family, AF_INET, AF_INET6));
if (!link_is_configured_for_family(link, family))
return false;
return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
}
static int link_set_ipv4_forward(Link *link) {
assert(link);
if (!link_ip_forward_enabled(link, AF_INET))
return 0;
/* We propagate the forwarding flag from one interface to the
* global setting one way. This means: as long as at least one
* interface was configured at any time that had IP forwarding
* enabled the setting will stay on for good. We do this
* primarily to keep IPv4 and IPv6 packet forwarding behaviour
* somewhat in sync (see below). */
t = link_get_ip_forwarding(link, family);
if (t < 0)
return 0; /* keep */
return sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1");
}
r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to %s %s forwarding, ignoring: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
static int link_set_ipv6_forward(Link *link) {
assert(link);
if (!link_ip_forward_enabled(link, AF_INET6))
return 0;
/* On Linux, the IPv6 stack does not know a per-interface
* packet forwarding setting: either packet forwarding is on
* for all, or off for all. We hence don't bother with a
* per-interface setting, but simply propagate the interface
* flag, if it is set, to the global flag, one-way. Note that
* while IPv4 would allow a per-interface flag, we expose the
* same behaviour there and also propagate the setting from
* one to all, to keep things simple (see above). */
return sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1");
return 0;
}
static int link_set_ipv4_rp_filter(Link *link) {
@ -291,13 +328,8 @@ int link_set_sysctl(Link *link) {
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure proxy ARP private VLAN for interface, ignoring: %m");
r = link_set_ipv4_forward(link);
if (r < 0)
log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
r = link_set_ipv6_forward(link);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");
(void) link_set_ip_forwarding(link, AF_INET);
(void) link_set_ip_forwarding(link, AF_INET6);
r = link_set_ipv6_privacy_extensions(link);
if (r < 0)
@ -371,3 +403,24 @@ static const char* const ip_reverse_path_filter_table[_IP_REVERSE_PATH_FILTER_MA
DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter, IPReversePathFilter);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter, ip_reverse_path_filter, IPReversePathFilter,
"Failed to parse IP reverse path filter option");
int config_parse_ip_forward_deprecated(
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) {
assert(filename);
log_syntax(unit, LOG_WARNING, filename, line, 0,
"IPForward= setting is deprecated. "
"Please use IPv4Forwarding= and/or IPv6Forwarding= in networkd.conf for global setting, "
"and the same settings in .network files for per-interface setting.");
return 0;
}

View File

@ -6,6 +6,7 @@
#include "conf-parser.h"
typedef struct Link Link;
typedef struct Manager Manager;
typedef enum IPv6PrivacyExtensions {
/* These values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values. Do not reorder! */
@ -26,6 +27,9 @@ typedef enum IPReversePathFilter {
_IP_REVERSE_PATH_FILTER_INVALID = -EINVAL,
} IPReversePathFilter;
void manager_set_sysctl(Manager *manager);
int link_get_ip_forwarding(Link *link, int family);
int link_set_sysctl(Link *link);
int link_set_ipv6_mtu(Link *link);
@ -37,3 +41,4 @@ IPReversePathFilter ip_reverse_path_filter_from_string(const char *s) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_reverse_path_filter);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_forward_deprecated);

View File

@ -116,48 +116,6 @@ DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_deprecated_address_family, AddressFa
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(ip_masquerade_address_family, AddressFamily);
DEFINE_STRING_TABLE_LOOKUP(dhcp_lease_server_type, sd_dhcp_lease_server_type_t);
int config_parse_address_family_with_kernel(
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) {
AddressFamily *fwd = data, s;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
/* This function is mostly obsolete now. It simply redirects
* "kernel" to "no". In older networkd versions we used to
* distinguish IPForward=off from IPForward=kernel, where the
* former would explicitly turn off forwarding while the
* latter would simply not touch the setting. But that logic
* is gone, hence silently accept the old setting, but turn it
* to "no". */
s = address_family_from_string(rvalue);
if (s < 0) {
if (streq(rvalue, "kernel"))
s = ADDRESS_FAMILY_NO;
else {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue);
return 0;
}
}
*fwd = s;
return 0;
}
int config_parse_ip_masquerade(
const char *unit,
const char *filename,

View File

@ -52,7 +52,6 @@ static inline uint32_t usec_to_sec(usec_t usec, usec_t now_usec) {
}
CONFIG_PARSER_PROTOTYPE(config_parse_link_local_address_family);
CONFIG_PARSER_PROTOTYPE(config_parse_address_family_with_kernel);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_masquerade);
CONFIG_PARSER_PROTOTYPE(config_parse_mud_url);

View File

@ -5,7 +5,7 @@ Name=client-peer
[Network]
Address=192.168.6.2/24
DHCPServer=yes
IPForward=ipv4
IPv4Forwarding=yes
IPv6AcceptRA=no
[DHCPServer]

View File

@ -4,5 +4,5 @@ Name=client
[Network]
DHCP=yes
IPForward=ipv4
IPv4Forwarding=yes
IPv6AcceptRA=no

View File

@ -4,5 +4,5 @@ Name=server-peer
[Network]
Address=192.168.5.2/24
IPForward=ipv4
IPv4Forwarding=yes
IPv6AcceptRA=no

View File

@ -4,7 +4,7 @@ Name=server
[Network]
Address=192.168.5.1/24
IPForward=ipv4
IPv4Forwarding=yes
DHCPServer=yes
IPv6AcceptRA=no

View File

@ -9,7 +9,6 @@ IPv6ProxyNDPAddress=2607:5300:203:5215:3::1
IPv6ProxyNDPAddress=2607:5300:203:5215:2::1
IPv6ProxyNDPAddress=2607:5300:203:5215:1::1
IPv6AcceptRA=no
IPForward=yes
Address=66.70.129.136/32
Address=66.70.129.142/32
Address=66.70.129.143/32

View File

@ -3,7 +3,8 @@
Name=dummy98
[Network]
IPForward=yes
IPv4Forwarding=yes
IPv6Forwarding=yes
IPv6DuplicateAddressDetection=3
IPv6HopLimit=5
IPv4ProxyARP=yes

View File

@ -4,7 +4,6 @@ Name=bridge99
[Network]
VLAN=vlan99
IPForward=yes
ConfigureWithoutCarrier=yes
LLDP=yes
IPv6AcceptRA=false

View File

@ -4,7 +4,6 @@ Name=test1
[Network]
IPv6AcceptRA=no
IPForward=yes
Bridge=bridge99
LinkLocalAddressing=no
EmitLLDP=nearest-bridge

View File

@ -5223,6 +5223,9 @@ class NetworkdRATests(unittest.TestCase, Utilities):
start_networkd()
self.wait_online('veth99:routable', 'veth-peer:degraded')
# IPv6SendRA=yes implies IPv6Forwarding.
self.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
output = resolvectl('dns', 'veth99')
print(output)
self.assertRegex(output, 'fe80::')