Merge pull request #24570 from topimiettinen/nft-sets-v2

network: firewall integration with NFT sets
This commit is contained in:
Yu Watanabe
2023-09-01 20:13:39 +09:00
committed by GitHub
21 changed files with 817 additions and 33 deletions
+124
View File
@@ -1324,6 +1324,86 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
<xi:include href="version-info.xml" xpointer="v252"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NFTSet=</varname><replaceable>source</replaceable>:<replaceable>family</replaceable>:<replaceable>table</replaceable>:<replaceable>set</replaceable></term>
<listitem>
<para>This setting provides a method for integrating network configuration into firewall rules with
<ulink url="https://netfilter.org/projects/nftables/index.html">NFT</ulink> sets. The benefit of
using the setting is that static network configuration (or dynamically obtained network addresses,
see similar directives in other sections) can be used in firewall rules with the indirection of NFT
set types. For example, access could be granted for hosts in the local subnetwork only. Firewall
rules using IP address of an interface are also instantly updated when the network configuration
changes, for example via DHCP.</para>
<para>This option expects a whitespace separated list of NFT set definitions. Each definition
consists of a colon-separated tuple of source type (one of <literal>address</literal>,
<literal>prefix</literal> or <literal>ifindex</literal>), NFT address family (one of
<literal>arp</literal>, <literal>bridge</literal>, <literal>inet</literal>, <literal>ip</literal>,
<literal>ip6</literal>, or <literal>netdev</literal>), table name and set name. The names of tables
and sets must conform to lexical restrictions of NFT table names. The type of the element used in
the NFT filter must match the type implied by the directive (<literal>address</literal>,
<literal>prefix</literal> or <literal>ifindex</literal>) and address type (IPv4 or IPv6) as shown
in the table below.</para>
<table>
<title>Defined <varname>source type</varname> values</title>
<tgroup cols='3'>
<colspec colname='source type'/>
<colspec colname='description'/>
<colspec colname='NFT type name'/>
<thead>
<row>
<entry>Source type</entry>
<entry>Description</entry>
<entry>Corresponding NFT type name</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>address</literal></entry>
<entry>host IP address</entry>
<entry><literal>ipv4_addr</literal> or <literal>ipv6_addr</literal></entry>
</row>
<row>
<entry><literal>prefix</literal></entry>
<entry>network prefix</entry>
<entry><literal>ipv4_addr</literal> or <literal>ipv6_addr</literal>, with <literal>flags interval</literal></entry>
</row>
<row>
<entry><literal>ifindex</literal></entry>
<entry>interface index</entry>
<entry><literal>iface_index</literal></entry>
</row>
</tbody>
</tgroup>
</table>
<para>When an interface is configured with IP addresses, the addresses, subnetwork masks or
interface index will be appended to the NFT sets. The information will be removed when the
interface is deconfigured. <command>systemd-networkd</command> only inserts elements to (or removes
from) the sets, so the related NFT rules, tables and sets must be prepared elsewhere in
advance. Failures to manage the sets will be ignored.</para>
<para>Example:
<programlisting>[Address]
NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
Corresponding NFT rules:
<programlisting>table netdev filter {
set eth_ipv4_prefix {
type ipv4_addr
flags interval
}
chain eth_ingress {
type filter hook ingress device "eth0" priority filter; policy drop;
ip daddr != @eth_ipv4_prefix drop
accept
}
}</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@@ -2497,6 +2577,17 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
<xi:include href="version-info.xml" xpointer="v252"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NFTSet=</varname></term>
<listitem>
<para>This applies the NFT set for the network configuration received with DHCP, like
<varname>NFTSet=</varname> in [Address] section applies it to static configuration. See
<varname>NFTSet=</varname> in [Address] section for more details. For <literal>address</literal> or
<literal>prefix</literal> source types, the type of the element used in the NFT filter must be
<literal>ipv4_addr</literal>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@@ -2656,6 +2747,17 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NFTSet=</varname></term>
<listitem>
<para>This applies the NFT set for the network configuration received with DHCP, like
<varname>NFTSet=</varname> in [Address] section applies it to static configuration. See
<varname>NFTSet=</varname> in [Address] section for more details. For <literal>address</literal>
or <literal>prefix</literal> source types, the type of the element used in the NFT filter must be
<literal>ipv6_addr</literal>.</para>
</listitem>
</varlistentry>
<!-- How to communicate with the server -->
<varlistentry>
@@ -2767,6 +2869,17 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
<xi:include href="version-info.xml" xpointer="v252"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NFTSet=</varname></term>
<listitem>
<para>This applies the NFT set for the network configuration received with DHCP, like
<varname>NFTSet=</varname> in [Address] section applies it to static configuration. See
<varname>NFTSet=</varname> in [Address] section for more details. For <literal>address</literal> or
<literal>prefix</literal> source types, the type of the element used in the NFT filter must be
<literal>ipv6_addr</literal>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@@ -3119,6 +3232,17 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<xi:include href="version-info.xml" xpointer="v252"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NFTSet=</varname></term>
<listitem>
<para>This applies the NFT set for the network configuration received with RA, like
<varname>NFTSet=</varname> in [Address] section applies it to static configuration. See
<varname>NFTSet=</varname> in [Address] section for more details. For <literal>address</literal> or
<literal>prefix</literal> source types, the type of the element used in the NFT filter must be
<literal>ipv6_addr</literal>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
+19
View File
@@ -747,3 +747,22 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
return store_loadavg_fixed_point(i, f, ret);
}
/* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
* https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
bool nft_identifier_valid(const char *id) {
if (!id)
return false;
size_t len = strlen(id);
if (len == 0 || len > 31)
return false;
if (!ascii_isalpha(id[0]))
return false;
for (size_t i = 1; i < len; i++)
if (!ascii_isalpha(id[i]) && !ascii_isdigit(id[i]) && !IN_SET(id[i], '/', '\\', '_', '.'))
return false;
return true;
}
+2
View File
@@ -152,3 +152,5 @@ int parse_oom_score_adjust(const char *s, int *ret);
* to a loadavg_t. */
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret);
int parse_loadavg_fixed_point(const char *s, loadavg_t *ret);
bool nft_identifier_valid(const char *id);
@@ -170,6 +170,8 @@ int netlink_add_match_internal(
#define NETLINK_DONT_DESTROY(nl) \
_cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##nl = sd_netlink_ref(nl)
bool nfproto_is_valid(int nfproto);
/* nfnl */
/* TODO: to be exported later */
int sd_nfnl_socket_open(sd_netlink **ret);
@@ -12,7 +12,7 @@
#include "netlink-types.h"
#include "netlink-util.h"
static bool nfproto_is_valid(int nfproto) {
bool nfproto_is_valid(int nfproto) {
return IN_SET(nfproto,
NFPROTO_UNSPEC,
NFPROTO_INET,
@@ -20,8 +20,7 @@ static bool nfproto_is_valid(int nfproto) {
NFPROTO_ARP,
NFPROTO_NETDEV,
NFPROTO_BRIDGE,
NFPROTO_IPV6,
NFPROTO_DECNET);
NFPROTO_IPV6);
}
int sd_nfnl_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int nfproto, uint16_t subsys, uint16_t msg_type, uint16_t flags) {
+127
View File
@@ -173,6 +173,7 @@ Address *address_free(Address *address) {
config_section_free(address->section);
free(address->label);
free(address->netlabel);
nft_set_context_clear(&address->nft_set_context);
return mfree(address);
}
@@ -502,6 +503,8 @@ int address_dup(const Address *src, Address **ret) {
dest->link = NULL;
dest->label = NULL;
dest->netlabel = NULL;
dest->nft_set_context.sets = NULL;
dest->nft_set_context.n_sets = 0;
if (src->family == AF_INET) {
r = free_and_strdup(&dest->label, src->label);
@@ -513,6 +516,10 @@ int address_dup(const Address *src, Address **ret) {
if (r < 0)
return r;
r = nft_set_context_dup(&src->nft_set_context, &dest->nft_set_context);
if (r < 0)
return r;
*ret = TAKE_PTR(dest);
return 0;
}
@@ -555,6 +562,82 @@ static int address_set_masquerade(Address *address, bool add) {
return 0;
}
static void address_modify_nft_set_context(Address *address, bool add, NFTSetContext *nft_set_context) {
int r;
assert(address);
assert(address->link);
assert(address->link->manager);
assert(nft_set_context);
if (!address->link->manager->fw_ctx) {
r = fw_ctx_new(&address->link->manager->fw_ctx);
if (r < 0)
return;
}
FOREACH_ARRAY(nft_set, nft_set_context->sets, nft_set_context->n_sets) {
uint32_t ifindex;
assert(nft_set);
switch (nft_set->source) {
case NFT_SET_SOURCE_ADDRESS:
r = nft_set_element_modify_ip(address->link->manager->fw_ctx, add, nft_set->nfproto, address->family, nft_set->table, nft_set->set,
&address->in_addr);
break;
case NFT_SET_SOURCE_PREFIX:
r = nft_set_element_modify_iprange(address->link->manager->fw_ctx, add, nft_set->nfproto, address->family, nft_set->table, nft_set->set,
&address->in_addr, address->prefixlen);
break;
case NFT_SET_SOURCE_IFINDEX:
ifindex = address->link->ifindex;
r = nft_set_element_modify_any(address->link->manager->fw_ctx, add, nft_set->nfproto, nft_set->table, nft_set->set,
&ifindex, sizeof(ifindex));
break;
default:
assert_not_reached();
}
if (r < 0)
log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring",
add? "add" : "delete",
nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set,
IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
else
log_debug("%s NFT set: family %s, table %s, set %s, IP address %s",
add ? "Added" : "Deleted",
nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set,
IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
}
}
static void address_modify_nft_set(Address *address, bool add) {
assert(address);
assert(address->link);
if (!IN_SET(address->family, AF_INET, AF_INET6))
return;
if (!address->link->network)
return;
switch (address->source) {
case NETWORK_CONFIG_SOURCE_DHCP4:
return address_modify_nft_set_context(address, add, &address->link->network->dhcp_nft_set_context);
case NETWORK_CONFIG_SOURCE_DHCP6:
return address_modify_nft_set_context(address, add, &address->link->network->dhcp6_nft_set_context);
case NETWORK_CONFIG_SOURCE_DHCP_PD:
return address_modify_nft_set_context(address, add, &address->link->network->dhcp_pd_nft_set_context);
case NETWORK_CONFIG_SOURCE_NDISC:
return address_modify_nft_set_context(address, add, &address->link->network->ndisc_nft_set_context);
case NETWORK_CONFIG_SOURCE_STATIC:
return address_modify_nft_set_context(address, add, &address->nft_set_context);
default:
return;
}
}
static int address_add(Link *link, Address *address) {
int r;
@@ -596,6 +679,8 @@ static int address_update(Address *address) {
address_add_netlabel(address);
address_modify_nft_set(address, /* add = */ true);
if (address_is_ready(address) && address->callback) {
r = address->callback(address);
if (r < 0)
@@ -615,6 +700,8 @@ static int address_drop(Address *address) {
if (r < 0)
log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
address_modify_nft_set(address, /* add = */ false);
address_del_netlabel(address);
address_free(address);
@@ -1664,6 +1751,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
address->source = a->source;
address->provider = a->provider;
(void) free_and_strdup_warn(&address->netlabel, a->netlabel);
nft_set_context_clear(&address->nft_set_context);
(void) nft_set_context_dup(&a->nft_set_context, &address->nft_set_context);
address->requested_as_null = a->requested_as_null;
address->callback = a->callback;
}
@@ -2352,3 +2441,41 @@ int network_drop_invalid_addresses(Network *network) {
return 0;
}
int config_parse_address_ip_nft_set(
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;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(network);
r = address_new_static(network, filename, section_line, &n);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to allocate a new address, ignoring assignment: %m");
return 0;
}
r = config_parse_nft_set(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->nft_set_context, network);
if (r < 0)
return r;
TAKE_PTR(n);
return 0;
}
+4
View File
@@ -6,6 +6,7 @@
#include <stdio.h>
#include "conf-parser.h"
#include "firewall-util.h"
#include "in-addr-util.h"
#include "networkd-link.h"
#include "networkd-util.h"
@@ -59,6 +60,8 @@ struct Address {
/* Called when address become ready */
address_ready_callback_t callback;
NFTSetContext nft_set_context;
};
const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) _warn_unused_result_;
@@ -132,3 +135,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_address_scope);
CONFIG_PARSER_PROTOTYPE(config_parse_address_route_metric);
CONFIG_PARSER_PROTOTYPE(config_parse_duplicate_address_detection);
CONFIG_PARSER_PROTOTYPE(config_parse_address_netlabel);
CONFIG_PARSER_PROTOTYPE(config_parse_address_ip_nft_set);
+5
View File
@@ -161,6 +161,7 @@ Address.DuplicateAddressDetection, config_parse_duplicate_address_dete
Address.Scope, config_parse_address_scope, 0, 0
Address.RouteMetric, config_parse_address_route_metric, 0, 0
Address.NetLabel, config_parse_address_netlabel, 0, 0
Address.NFTSet, config_parse_address_ip_nft_set, 0, 0
IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
IPv6AddressLabel.Label, config_parse_address_label, 0, 0
Neighbor.Address, config_parse_neighbor_address, 0, 0
@@ -257,6 +258,7 @@ DHCPv4.InitialAdvertisedReceiveWindow, config_parse_tcp_window,
DHCPv4.FallbackLeaseLifetimeSec, config_parse_dhcp_fallback_lease_lifetime, 0, 0
DHCPv4.Use6RD, config_parse_bool, 0, offsetof(Network, dhcp_use_6rd)
DHCPv4.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_netlabel)
DHCPv4.NFTSet, config_parse_nft_set, 0, offsetof(Network, dhcp_nft_set_context)
DHCPv6.UseAddress, config_parse_bool, 0, offsetof(Network, dhcp6_use_address)
DHCPv6.UseDelegatedPrefix, config_parse_bool, 0, offsetof(Network, dhcp6_use_pd_prefix)
DHCPv6.UseDNS, config_parse_dhcp_use_dns, AF_INET6, 0
@@ -278,6 +280,7 @@ DHCPv6.DUIDRawData, config_parse_duid_rawdata,
DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_use_rapid_commit)
DHCPv6.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp6_netlabel)
DHCPv6.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp6_send_release)
DHCPv6.NFTSet, config_parse_nft_set, 0, offsetof(Network, dhcp6_nft_set_context)
IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_gateway)
IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_route_prefix)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
@@ -300,6 +303,7 @@ IPv6AcceptRA.RouteAllowList, config_parse_in_addr_prefixes,
IPv6AcceptRA.RouteDenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_route_prefix)
IPv6AcceptRA.Token, config_parse_address_generation_type, 0, offsetof(Network, ndisc_tokens)
IPv6AcceptRA.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, ndisc_netlabel)
IPv6AcceptRA.NFTSet, config_parse_nft_set, 0, offsetof(Network, ndisc_nft_set_context)
DHCPServer.ServerAddress, config_parse_dhcp_server_address, 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)
@@ -367,6 +371,7 @@ DHCPPrefixDelegation.ManageTemporaryAddress, config_parse_bool,
DHCPPrefixDelegation.Token, config_parse_address_generation_type, 0, offsetof(Network, dhcp_pd_tokens)
DHCPPrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_pd_route_metric)
DHCPPrefixDelegation.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_pd_netlabel)
DHCPPrefixDelegation.NFTSet, config_parse_nft_set, 0, offsetof(Network, dhcp_pd_nft_set_context)
IPv6SendRA.RouterLifetimeSec, config_parse_router_lifetime, 0, offsetof(Network, router_lifetime_usec)
IPv6SendRA.RetransmitSec, config_parse_router_retransmit, 0, offsetof(Network, router_retransmit_usec)
IPv6SendRA.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
+4
View File
@@ -725,6 +725,7 @@ static Network *network_free(Network *network) {
ordered_hashmap_free(network->dhcp_client_send_options);
ordered_hashmap_free(network->dhcp_client_send_vendor_options);
free(network->dhcp_netlabel);
nft_set_context_clear(&network->dhcp_nft_set_context);
/* DHCPv6 client */
free(network->dhcp6_mudurl);
@@ -734,11 +735,13 @@ static Network *network_free(Network *network) {
ordered_hashmap_free(network->dhcp6_client_send_options);
ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
free(network->dhcp6_netlabel);
nft_set_context_clear(&network->dhcp6_nft_set_context);
/* DHCP PD */
free(network->dhcp_pd_uplink_name);
set_free(network->dhcp_pd_tokens);
free(network->dhcp_pd_netlabel);
nft_set_context_clear(&network->dhcp_pd_nft_set_context);
/* Router advertisement */
ordered_set_free(network->router_search_domains);
@@ -754,6 +757,7 @@ static Network *network_free(Network *network) {
set_free(network->ndisc_allow_listed_route_prefix);
set_free(network->ndisc_tokens);
free(network->ndisc_netlabel);
nft_set_context_clear(&network->ndisc_nft_set_context);
/* LLDP */
free(network->lldp_mudurl);
+5
View File
@@ -10,6 +10,7 @@
#include "bridge.h"
#include "condition.h"
#include "conf-parser.h"
#include "firewall-util.h"
#include "hashmap.h"
#include "ipoib.h"
#include "net-condition.h"
@@ -163,6 +164,7 @@ struct Network {
OrderedHashmap *dhcp_client_send_options;
OrderedHashmap *dhcp_client_send_vendor_options;
char *dhcp_netlabel;
NFTSetContext dhcp_nft_set_context;
/* DHCPv6 Client support */
bool dhcp6_use_address;
@@ -191,6 +193,7 @@ struct Network {
Set *dhcp6_request_options;
char *dhcp6_netlabel;
bool dhcp6_send_release;
NFTSetContext dhcp6_nft_set_context;
/* DHCP Server Support */
bool dhcp_server;
@@ -251,6 +254,7 @@ struct Network {
int dhcp_pd_uplink_index;
char *dhcp_pd_uplink_name;
char *dhcp_pd_netlabel;
NFTSetContext dhcp_pd_nft_set_context;
/* Bridge Support */
int use_bpdu;
@@ -343,6 +347,7 @@ struct Network {
Set *ndisc_allow_listed_route_prefix;
Set *ndisc_tokens;
char *ndisc_netlabel;
NFTSetContext ndisc_nft_set_context;
/* LLDP support */
LLDPMode lldp_mode; /* LLDP reception */
+303 -26
View File
@@ -14,6 +14,8 @@
#include "sd-netlink.h"
#include "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
#include "firewall-util.h"
#include "firewall-util-private.h"
#include "in-addr-util.h"
@@ -21,6 +23,7 @@
#include "netlink-internal.h"
#include "netlink-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "time-util.h"
#define NFT_SYSTEMD_DNAT_MAP_NAME "map_port_ipport"
@@ -598,7 +601,8 @@ static int nft_new_map(
static int nft_add_element(
sd_netlink *nfnl,
sd_netlink_message **ret,
int family,
int nfproto,
const char *table_name,
const char *set_name,
const void *key,
uint32_t klen,
@@ -610,10 +614,12 @@ static int nft_add_element(
assert(nfnl);
assert(ret);
assert(IN_SET(family, AF_INET, AF_INET6));
assert(nfproto_is_valid(nfproto));
assert(table_name);
assert(set_name);
assert(key);
assert(data);
assert(data || dlen == 0);
/*
* Ideally there would be an API that provides:
@@ -628,7 +634,7 @@ static int nft_add_element(
* This replicated here and each element gets added to the set
* one-by-one.
*/
r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ true, family, NFT_SYSTEMD_TABLE_NAME, set_name);
r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ true, nfproto, table_name, set_name);
if (r < 0)
return r;
@@ -653,7 +659,8 @@ static int nft_add_element(
static int nft_del_element(
sd_netlink *nfnl,
sd_netlink_message **ret,
int family,
int nfproto,
const char *table_name,
const char *set_name,
const void *key,
uint32_t klen,
@@ -665,12 +672,13 @@ static int nft_del_element(
assert(nfnl);
assert(ret);
assert(IN_SET(family, AF_INET, AF_INET6));
assert(nfproto_is_valid(nfproto));
assert(table_name);
assert(set_name);
assert(key);
assert(data);
assert(data || dlen == 0);
r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ false, family, NFT_SYSTEMD_TABLE_NAME, set_name);
r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ false, nfproto, table_name, set_name);
if (r < 0)
return r;
@@ -791,7 +799,7 @@ static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
return 0;
}
int fw_nftables_init(FirewallContext *ctx) {
int fw_nftables_init_full(FirewallContext *ctx, bool init_tables) {
_cleanup_(sd_netlink_unrefp) sd_netlink *nfnl = NULL;
int r;
@@ -802,20 +810,26 @@ int fw_nftables_init(FirewallContext *ctx) {
if (r < 0)
return r;
r = fw_nftables_init_family(nfnl, AF_INET);
if (r < 0)
return r;
if (socket_ipv6_is_supported()) {
r = fw_nftables_init_family(nfnl, AF_INET6);
if (init_tables) {
r = fw_nftables_init_family(nfnl, AF_INET);
if (r < 0)
log_debug_errno(r, "Failed to init ipv6 NAT: %m");
return r;
if (socket_ipv6_is_supported()) {
r = fw_nftables_init_family(nfnl, AF_INET6);
if (r < 0)
return log_error_errno(r, "Failed to init ipv6 NAT: %m");
}
}
ctx->nfnl = TAKE_PTR(nfnl);
return 0;
}
int fw_nftables_init(FirewallContext *ctx) {
return fw_nftables_init_full(ctx, /* init_tables= */ true);
}
void fw_nftables_exit(FirewallContext *ctx) {
assert(ctx);
@@ -891,18 +905,24 @@ static int nft_message_append_setelem_ip6range(
return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
}
static int fw_nftables_add_masquerade_internal(
sd_netlink *nfnl,
int nft_set_element_modify_iprange(
FirewallContext *ctx,
bool add,
int nfproto,
int af,
const char *table,
const char *set,
const union in_addr_union *source,
unsigned int source_prefixlen) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(nfnl);
assert(ctx->nfnl);
assert(IN_SET(af, AF_INET, AF_INET6));
assert(nfproto_is_valid(nfproto));
assert(table);
assert(set);
if (!source || source_prefixlen == 0)
return -EINVAL;
@@ -910,7 +930,7 @@ static int fw_nftables_add_masquerade_internal(
if (af == AF_INET6 && source_prefixlen < 8)
return -EINVAL;
r = sd_nfnl_nft_message_new_setelems(nfnl, &m, add, af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
r = sd_nfnl_nft_message_new_setelems(ctx->nfnl, &m, add, nfproto, table, set);
if (r < 0)
return r;
@@ -921,7 +941,81 @@ static int fw_nftables_add_masquerade_internal(
if (r < 0)
return r;
return sd_nfnl_call_batch(nfnl, &m, 1, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
return sd_nfnl_call_batch(ctx->nfnl, &m, 1, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
}
int nft_set_element_modify_ip(
FirewallContext *ctx,
bool add,
int nfproto,
int af,
const char *table,
const char *set,
const union in_addr_union *source) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(ctx->nfnl);
assert(IN_SET(af, AF_INET, AF_INET6));
assert(nfproto_is_valid(nfproto));
assert(table);
assert(set);
if (!source)
return -EINVAL;
r = sd_nfnl_nft_message_new_setelems(ctx->nfnl, &m, add, nfproto, table, set);
if (r < 0)
return r;
r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
if (r < 0)
return r;
r = sd_nfnl_nft_message_append_setelem(m, 0, source, FAMILY_ADDRESS_SIZE(af), NULL, 0, 0);
if (r < 0)
return r;
r = sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
if (r < 0)
return r;
return sd_nfnl_call_batch(ctx->nfnl, &m, 1, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
}
int nft_set_element_modify_any(FirewallContext *ctx, bool add, int nfproto, const char *table, const char *set, const void *element, size_t element_size) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(ctx);
assert(ctx->nfnl);
assert(nfproto_is_valid(nfproto));
assert(table);
assert(set);
assert(element);
if (add)
r = nft_add_element(ctx->nfnl, &m, nfproto, table, set, element, element_size, NULL, 0);
else
r = nft_del_element(ctx->nfnl, &m, nfproto, table, set, element, element_size, NULL, 0);
if (r < 0)
return r;
return sd_nfnl_call_batch(ctx->nfnl, &m, 1, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
}
static int af_to_nfproto(int af) {
assert(IN_SET(af, AF_INET, AF_INET6));
switch (af) {
case AF_INET:
return NFPROTO_IPV4;
case AF_INET6:
return NFPROTO_IPV6;
default:
assert_not_reached();
}
}
int fw_nftables_add_masquerade(
@@ -940,7 +1034,8 @@ int fw_nftables_add_masquerade(
if (!socket_ipv6_is_supported() && af == AF_INET6)
return -EOPNOTSUPP;
r = fw_nftables_add_masquerade_internal(ctx->nfnl, add, af, source, source_prefixlen);
r = nft_set_element_modify_iprange(ctx, add, af_to_nfproto(af), af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME,
source, source_prefixlen);
if (r != -ENOENT)
return r;
@@ -965,7 +1060,8 @@ int fw_nftables_add_masquerade(
if (r < 0)
return r;
return fw_nftables_add_masquerade_internal(ctx->nfnl, add, af, source, source_prefixlen);
return nft_set_element_modify_iprange(ctx, add, af_to_nfproto(af), af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME,
source, source_prefixlen);
}
static int fw_nftables_add_local_dnat_internal(
@@ -1023,7 +1119,8 @@ static int fw_nftables_add_local_dnat_internal(
else
memcpy(data, &previous_remote->in6, sizeof(previous_remote->in6));
r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_DNAT_MAP_NAME,
key, sizeof(key), data, dlen);
if (r < 0)
return r;
}
@@ -1034,9 +1131,11 @@ static int fw_nftables_add_local_dnat_internal(
memcpy(data, &remote->in6, sizeof(remote->in6));
if (add)
r = nft_add_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
r = nft_add_element(nfnl, &messages[msgcnt++], af_to_nfproto(af), NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_DNAT_MAP_NAME,
key, sizeof(key), data, dlen);
else
r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
r = nft_del_element(nfnl, &messages[msgcnt++], af_to_nfproto(af), NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_DNAT_MAP_NAME,
key, sizeof(key), data, dlen);
if (r < 0)
return r;
@@ -1087,3 +1186,181 @@ int fw_nftables_add_local_dnat(
/* table created anew; previous address already gone */
return fw_nftables_add_local_dnat_internal(ctx->nfnl, add, af, protocol, local_port, remote, remote_port, NULL);
}
static const char *const nfproto_table[] = {
[NFPROTO_ARP] = "arp",
[NFPROTO_BRIDGE] = "bridge",
[NFPROTO_INET] = "inet",
[NFPROTO_IPV4] = "ip",
[NFPROTO_IPV6] = "ip6",
[NFPROTO_NETDEV] = "netdev",
};
DEFINE_STRING_TABLE_LOOKUP(nfproto, int);
static const char *const nft_set_source_table[] = {
[NFT_SET_SOURCE_ADDRESS] = "address",
[NFT_SET_SOURCE_PREFIX] = "prefix",
[NFT_SET_SOURCE_IFINDEX] = "ifindex",
};
DEFINE_STRING_TABLE_LOOKUP(nft_set_source, int);
void nft_set_context_clear(NFTSetContext *s) {
assert(s);
FOREACH_ARRAY(nft_set, s->sets, s->n_sets) {
free(nft_set->table);
free(nft_set->set);
}
s->n_sets = 0;
s->sets = mfree(s->sets);
}
static int nft_set_add(NFTSetContext *s, NFTSetSource source, int nfproto, const char *table, const char *set) {
_cleanup_free_ char *table_dup = NULL, *set_dup = NULL;
assert(s);
assert(IN_SET(source, NFT_SET_SOURCE_ADDRESS, NFT_SET_SOURCE_PREFIX, NFT_SET_SOURCE_IFINDEX));
assert(nfproto_is_valid(nfproto));
assert(table);
assert(set);
table_dup = strdup(table);
if (!table_dup)
return -ENOMEM;
set_dup = strdup(set);
if (!set_dup)
return -ENOMEM;
if (!GREEDY_REALLOC(s->sets, s->n_sets + 1))
return -ENOMEM;
s->sets[s->n_sets++] = (NFTSet) {
.source = source,
.nfproto = nfproto,
.table = TAKE_PTR(table_dup),
.set = TAKE_PTR(set_dup),
};
return 0;
}
int nft_set_context_dup(const NFTSetContext *src, NFTSetContext *dst) {
int r;
_cleanup_(nft_set_context_clear) NFTSetContext d = (NFTSetContext) {};
assert(src);
assert(dst);
FOREACH_ARRAY(nft_set, src->sets, src->n_sets) {
r = nft_set_add(&d, nft_set->source, nft_set->nfproto, nft_set->table, nft_set->set);
if (r < 0)
return r;
}
*dst = TAKE_STRUCT(d);
return 0;
}
int config_parse_nft_set(
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) {
NFTSetContext *nft_set_context = ASSERT_PTR(data);
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(nft_set_context);
if (isempty(rvalue)) {
nft_set_context_clear(nft_set_context);
return 0;
}
for (const char *p = rvalue;;) {
_cleanup_free_ char *tuple = NULL, *source_str = NULL, *family_str = NULL, *table = NULL, *set = NULL;
const char *q = NULL;
int nfproto;
NFTSetSource source;
r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
_cleanup_free_ char *esc = NULL;
esc = cescape(rvalue);
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax %s=%s, ignoring: %m", lvalue, strna(esc));
return 0;
}
if (r == 0)
return 0;
q = tuple;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE, &source_str, &family_str, &table, &set, NULL);
if (r == -ENOMEM)
return log_oom();
if (r != 4 || !isempty(q)) {
_cleanup_free_ char *esc = NULL;
esc = cescape(tuple);
return log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse NFT set %s, ignoring", strna(esc));
}
assert(source_str);
assert(family_str);
assert(table);
assert(set);
source = nft_set_source_from_string(source_str);
if (source < 0) {
_cleanup_free_ char *esc = NULL;
esc = cescape(source_str);
return log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown NFT source %s, ignoring", strna(esc));
}
nfproto = nfproto_from_string(family_str);
if (nfproto < 0) {
_cleanup_free_ char *esc = NULL;
esc = cescape(family_str);
return log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown NFT protocol family %s, ignoring", strna(esc));
}
if (!nft_identifier_valid(table)) {
_cleanup_free_ char *esc = NULL;
esc = cescape(table);
return log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid table name %s, ignoring", strna(esc));
}
if (!nft_identifier_valid(set)) {
_cleanup_free_ char *esc = NULL;
esc = cescape(set);
return log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid set name %s, ignoring", strna(esc));
}
r = nft_set_add(nft_set_context, source, nfproto, table, set);
if (r < 0)
return r;
}
assert_not_reached();
}
+1
View File
@@ -26,6 +26,7 @@ struct FirewallContext {
const char *firewall_backend_to_string(FirewallBackend b) _const_;
int fw_nftables_init(FirewallContext *ctx);
int fw_nftables_init_full(FirewallContext *ctx, bool init_tables);
void fw_nftables_exit(FirewallContext *ctx);
int fw_nftables_add_masquerade(
+8 -4
View File
@@ -20,13 +20,13 @@ static const char * const firewall_backend_table[_FW_BACKEND_MAX] = {
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(firewall_backend, FirewallBackend);
static void firewall_backend_probe(FirewallContext *ctx) {
static void firewall_backend_probe(FirewallContext *ctx, bool init_tables) {
assert(ctx);
if (ctx->backend != _FW_BACKEND_INVALID)
return;
if (fw_nftables_init(ctx) >= 0)
if (fw_nftables_init_full(ctx, init_tables) >= 0)
ctx->backend = FW_BACKEND_NFTABLES;
else
#if HAVE_LIBIPTC
@@ -41,7 +41,7 @@ static void firewall_backend_probe(FirewallContext *ctx) {
log_debug("No firewall backend found.");
}
int fw_ctx_new(FirewallContext **ret) {
int fw_ctx_new_full(FirewallContext **ret, bool init_tables) {
_cleanup_free_ FirewallContext *ctx = NULL;
ctx = new(FirewallContext, 1);
@@ -52,12 +52,16 @@ int fw_ctx_new(FirewallContext **ret) {
.backend = _FW_BACKEND_INVALID,
};
firewall_backend_probe(ctx);
firewall_backend_probe(ctx, init_tables);
*ret = TAKE_PTR(ctx);
return 0;
}
int fw_ctx_new(FirewallContext **ret) {
return fw_ctx_new_full(ret, /* init_tables= */ true);
}
FirewallContext *fw_ctx_free(FirewallContext *ctx) {
if (!ctx)
return NULL;
+61
View File
@@ -4,11 +4,13 @@
#include <stdbool.h>
#include <stdint.h>
#include "conf-parser.h"
#include "in-addr-util.h"
typedef struct FirewallContext FirewallContext;
int fw_ctx_new(FirewallContext **ret);
int fw_ctx_new_full(FirewallContext **ret, bool init_tables);
FirewallContext *fw_ctx_free(FirewallContext *ctx);
DEFINE_TRIVIAL_CLEANUP_FUNC(FirewallContext *, fw_ctx_free);
@@ -29,3 +31,62 @@ int fw_add_local_dnat(
const union in_addr_union *remote,
uint16_t remote_port,
const union in_addr_union *previous_remote);
typedef enum NFTSetSource {
NFT_SET_SOURCE_ADDRESS,
NFT_SET_SOURCE_PREFIX,
NFT_SET_SOURCE_IFINDEX,
_NFT_SET_SOURCE_MAX,
_NFT_SET_SOURCE_INVALID = -EINVAL,
} NFTSetSource;
typedef struct NFTSet {
NFTSetSource source;
int nfproto;
char *table;
char *set;
} NFTSet;
typedef struct NFTSetContext {
NFTSet *sets;
size_t n_sets;
} NFTSetContext;
void nft_set_context_clear(NFTSetContext *s);
int nft_set_context_dup(const NFTSetContext *src, NFTSetContext *dst);
const char *nfproto_to_string(int i) _const_;
int nfproto_from_string(const char *s) _pure_;
const char *nft_set_source_to_string(int i) _const_;
int nft_set_source_from_string(const char *s) _pure_;
int nft_set_element_modify_iprange(
FirewallContext *ctx,
bool add,
int nfproto,
int af,
const char *table,
const char *set,
const union in_addr_union *source,
unsigned int source_prefixlen);
int nft_set_element_modify_ip(
FirewallContext *ctx,
bool add,
int nfproto,
int af,
const char *table,
const char *set,
const union in_addr_union *source);
int nft_set_element_modify_any(
FirewallContext *ctx,
bool add,
int nfproto,
const char *table,
const char *set,
const void *element,
size_t element_size);
CONFIG_PARSER_PROTOTYPE(config_parse_nft_set);
+4
View File
@@ -330,6 +330,10 @@ executables += [
'conditions' : ['HAVE_KMOD'],
'type' : 'manual',
},
test_template + {
'sources' : files('test-nft-set.c'),
'type' : 'manual',
},
test_template + {
'sources' : files('test-nscd-flush.c'),
'conditions' : ['ENABLE_NSCD'],
+70
View File
@@ -0,0 +1,70 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <assert.h>
#include <unistd.h>
#include "firewall-util.h"
#include "in-addr-util.h"
#include "log.h"
#include "netlink-internal.h"
#include "parse-util.h"
#include "string-util.h"
#include "tests.h"
int main(int argc, char **argv) {
int r;
assert_se(argc == 7);
test_setup_logging(LOG_DEBUG);
if (getuid() != 0)
return log_tests_skipped("not root");
int nfproto;
nfproto = nfproto_from_string(argv[2]);
assert_se(nfproto_is_valid(nfproto));
const char *table = argv[3], *set = argv[4];
FirewallContext *ctx;
r = fw_ctx_new(&ctx);
assert_se(r == 0);
bool add;
if (streq(argv[1], "add"))
add = true;
else
add = false;
if (streq(argv[5], "uint32")) {
uint32_t element;
r = safe_atou32(argv[6], &element);
assert_se(r == 0);
r = nft_set_element_modify_any(ctx, add, nfproto, table, set, &element, sizeof(element));
assert_se(r == 0);
} else if (streq(argv[5], "in_addr")) {
union in_addr_union addr;
int af;
r = in_addr_from_string_auto(argv[6], &af, &addr);
assert_se(r == 0);
r = nft_set_element_modify_ip(ctx, add, nfproto, af, table, set, &addr);
assert_se(r == 0);
} else if (streq(argv[5], "network")) {
union in_addr_union addr;
int af;
unsigned char prefixlen;
r = in_addr_prefix_from_string_auto(argv[6], &af, &addr, &prefixlen);
assert_se(r == 0);
r = nft_set_element_modify_iprange(ctx, add, nfproto, af, table, set, &addr, prefixlen);
assert_se(r == 0);
}
return 0;
}
@@ -177,3 +177,4 @@ Address=::/73
Address=10.10.1.1/24
# just a random label which should exist
NetLabel=system_u:object_r:root_t:s0
NFTSet=address:inet:sd_test:addr4 prefix:inet:sd_test:network4 ifindex:inet:sd_test:ifindex
@@ -28,6 +28,7 @@ AllowList=192.168.5.0/24 192.168.6.0/24
DenyList=192.168.5.0/24
# just a random label which should exist
NetLabel=system_u:object_r:root_t:s0
NFTSet=address:inet:sd_test:addr4 prefix:inet:sd_test:network4 ifindex:inet:sd_test:ifindex
[Route]
Destination=192.168.5.0/24
@@ -16,3 +16,4 @@ Token=eui64
Token=::1a:2b:3c:4d
# just a random label which should exist
NetLabel=system_u:object_r:root_t:s0
NFTSet=address:inet:sd_test:addr6 prefix:inet:sd_test:network6 ifindex:inet:sd_test:ifindex
@@ -9,3 +9,4 @@ IPv6AcceptRA=true
UseDomains=yes
# just a random label which should exist
NetLabel=system_u:object_r:root_t:s0
NFTSet=address:inet:sd_test:addr6 prefix:inet:sd_test:network6 ifindex:inet:sd_test:ifindex
@@ -944,6 +944,37 @@ class Utilities():
print(output)
self.assertRegex(output, f'interface:{interface},address:{address},label:"{label}"')
def setup_nftset(self, filter_name, filter_type, flags=''):
if not shutil.which('nft'):
print('## Setting up NFT sets skipped: nft command not found.')
else:
if call(f'nft add table inet sd_test') != 0:
print('## Setting up NFT table failed.')
self.fail()
if call(f'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
print('## Setting up NFT sets failed.')
self.fail()
def teardown_nftset(self, *filters):
if not shutil.which('nft'):
print('## Tearing down NFT sets skipped: nft command not found.')
else:
for filter_name in filters:
if call(f'nft delete set inet sd_test {filter_name}') != 0:
print('## Tearing down NFT sets failed.')
self.fail()
if call(f'nft delete table inet sd_test') != 0:
print('## Tearing down NFT table failed.')
self.fail()
def check_nftset(self, filter_name, contents):
if not shutil.which('nft'):
print('## Checking NFT sets skipped: nft command not found.')
else:
output = check_output(f'nft list set inet sd_test {filter_name}')
print(output)
self.assertRegex(output, r'.*elements = { [^}]*' + contents + r'[^}]* }.*')
class NetworkctlTests(unittest.TestCase, Utilities):
def setUp(self):
@@ -2432,6 +2463,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_address_static(self):
copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
start_networkd()
self.setup_nftset('addr4', 'ipv4_addr')
self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
self.setup_nftset('ifindex', 'iface_index')
self.wait_online(['dummy98:routable'])
self.verify_address_static(
@@ -2459,6 +2493,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
flag3=' noprefixroute',
flag4=' home mngtmpaddr',
)
# nft set
self.check_nftset('addr4', r'10\.10\.1\.1')
self.check_nftset('network4', r'10\.10\.1\.0/24')
self.check_nftset('ifindex', 'dummy98')
self.teardown_nftset('addr4', 'network4', 'ifindex')
copy_network_unit('25-address-static.network.d/10-override.conf')
networkctl_reload()
@@ -4702,6 +4742,9 @@ class NetworkdRATests(unittest.TestCase, Utilities):
def test_ipv6_prefix_delegation(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
self.setup_nftset('addr6', 'ipv6_addr')
self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
self.setup_nftset('ifindex', 'iface_index')
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:degraded'])
@@ -4721,6 +4764,14 @@ class NetworkdRATests(unittest.TestCase, Utilities):
self.check_netlabel('veth99', '2002:da8:1::/64')
self.check_netlabel('veth99', '2002:da8:2::/64')
self.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
self.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
self.check_nftset('network6', '2002:da8:1::/64')
self.check_nftset('network6', '2002:da8:2::/64')
self.check_nftset('ifindex', 'veth99')
self.teardown_nftset('addr6', 'network6', 'ifindex')
def test_ipv6_token_static(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
start_networkd()
@@ -5048,6 +5099,10 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
def test_dhcp_client_ipv4_only(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
self.setup_nftset('addr4', 'ipv4_addr')
self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
self.setup_nftset('ifindex', 'iface_index')
start_networkd()
self.wait_online(['veth-peer:carrier'])
start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
@@ -5163,6 +5218,12 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.check_netlabel('veth99', r'192\.168\.5\.0/24')
self.check_nftset('addr4', r'192\.168\.5\.1')
self.check_nftset('network4', r'192\.168\.5\.0/24')
self.check_nftset('ifindex', 'veth99')
self.teardown_nftset('addr4', 'network4', 'ifindex')
def test_dhcp_client_ipv4_use_routes_gateway(self):
first = True
for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
@@ -5619,6 +5680,10 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
'12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
'13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
self.setup_nftset('addr6', 'ipv6_addr')
self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
self.setup_nftset('ifindex', 'iface_index')
start_networkd()
self.wait_online(['veth-peer:routable'])
start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
@@ -5806,6 +5871,13 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
self.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
self.check_nftset('ifindex', 'dummy98')
self.teardown_nftset('addr6', 'network6', 'ifindex')
def verify_dhcp4_6rd(self, tunnel_name):
print('### ip -4 address show dev veth-peer scope global')
output = check_output('ip -4 address show dev veth-peer scope global')