network: firewall integration with NFT sets

New directives `NFTSet=`, `IPv4NFTSet=` and `IPv6NFTSet=` provide a method for
integrating configuration of dynamic networks into firewall rules with NFT
sets.

/etc/systemd/network/eth.network
```
[DHCPv4]
...
NFTSet=netdev:filter:eth_ipv4_address
```

```
table netdev filter {
        set eth_ipv4_address {
                type ipv4_addr
                flags interval
        }
        chain eth_ingress {
                type filter hook ingress device "eth0" priority filter; policy drop;
                ip saddr != @eth_ipv4_address drop
                accept
        }
}
```
```
sudo nft list set netdev filter eth_ipv4_address
table netdev filter {
        set eth_ipv4_address {
                type ipv4_addr
                flags interval
                elements = { 10.0.0.0/24 }
        }
}
```
This commit is contained in:
Topi Miettinen
2022-06-08 16:12:25 +00:00
committed by Topi Miettinen
parent e8f1b50f27
commit ab51fd9dbd
13 changed files with 792 additions and 7 deletions
+170
View File
@@ -139,6 +139,8 @@ Address *address_free(Address *address) {
config_section_free(address->section);
free(address->label);
set_free(address->netlabels);
nft_set_context_free_many(address->ipv4_nft_set_context, &address->n_ipv4_nft_set_contexts);
nft_set_context_free_many(address->ipv6_nft_set_context, &address->n_ipv6_nft_set_contexts);
return mfree(address);
}
@@ -450,6 +452,102 @@ static int address_set_masquerade(Address *address, bool add) {
return 0;
}
static void address_add_nft_set_context(const Address *address, const NFTSetContext *nft_set_context, size_t n_nft_set_contexts) {
assert(address);
for (size_t i = 0; i < n_nft_set_contexts; i++) {
int r;
r = nft_set_element_add_in_addr(&nft_set_context[i], address->family,
&address->in_addr, address->prefixlen);
if (r < 0) {
_cleanup_free_ char *addr_str = NULL;
(void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &addr_str);
log_warning_errno(r, "Adding NFT family %s table %s set %s for IP address %s failed, ignoring",
nfproto_to_string(nft_set_context[i].nfproto),
nft_set_context[i].table,
nft_set_context[i].set,
strna(addr_str));
}
}
}
static void address_del_nft_set_context(const Address *address, const NFTSetContext *nft_set_context, size_t n_nft_set_contexts) {
assert(address);
for (size_t i = 0; i < n_nft_set_contexts; i++) {
int r;
r = nft_set_element_del_in_addr(&nft_set_context[i], address->family,
&address->in_addr, address->prefixlen);
if (r < 0) {
_cleanup_free_ char *addr_str = NULL;
(void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &addr_str);
log_warning_errno(r, "Deleting NFT family %s table %s set %s for IP address %s failed, ignoring",
nfproto_to_string(nft_set_context[i].nfproto),
nft_set_context[i].table,
nft_set_context[i].set,
strna(addr_str));
}
}
}
static void address_add_nft_set(const Address *address) {
assert(address);
assert(address->link);
if (!address->link->network || !IN_SET(address->family, AF_INET, AF_INET6))
return;
switch (address->source) {
case NETWORK_CONFIG_SOURCE_DHCP4:
return address_add_nft_set_context(address, address->link->network->dhcp_nft_set_context, address->link->network->n_dhcp_nft_set_contexts);
case NETWORK_CONFIG_SOURCE_DHCP6:
return address_add_nft_set_context(address, address->link->network->dhcp6_nft_set_context, address->link->network->n_dhcp6_nft_set_contexts);
case NETWORK_CONFIG_SOURCE_DHCP_PD:
return address_add_nft_set_context(address, address->link->network->dhcp_pd_nft_set_context, address->link->network->n_dhcp_pd_nft_set_contexts);
case NETWORK_CONFIG_SOURCE_NDISC:
return address_add_nft_set_context(address, address->link->network->ndisc_nft_set_context, address->link->network->n_ndisc_nft_set_contexts);
case NETWORK_CONFIG_SOURCE_STATIC:
if (address->family == AF_INET)
return address_add_nft_set_context(address, address->ipv4_nft_set_context, address->n_ipv4_nft_set_contexts);
else
return address_add_nft_set_context(address, address->ipv6_nft_set_context, address->n_ipv6_nft_set_contexts);
default:
return;
}
}
static void address_del_nft_set(const Address *address) {
assert(address);
assert(address->link);
if (!address->link->network || !IN_SET(address->family, AF_INET, AF_INET6))
return;
switch (address->source) {
case NETWORK_CONFIG_SOURCE_DHCP4:
return address_del_nft_set_context(address, address->link->network->dhcp_nft_set_context, address->link->network->n_dhcp_nft_set_contexts);
case NETWORK_CONFIG_SOURCE_DHCP6:
return address_del_nft_set_context(address, address->link->network->dhcp6_nft_set_context, address->link->network->n_dhcp6_nft_set_contexts);
case NETWORK_CONFIG_SOURCE_DHCP_PD:
return address_del_nft_set_context(address, address->link->network->dhcp_pd_nft_set_context, address->link->network->n_dhcp_pd_nft_set_contexts);
case NETWORK_CONFIG_SOURCE_NDISC:
return address_del_nft_set_context(address, address->link->network->ndisc_nft_set_context, address->link->network->n_ndisc_nft_set_contexts);
case NETWORK_CONFIG_SOURCE_STATIC:
if (address->family == AF_INET)
return address_del_nft_set_context(address, address->ipv4_nft_set_context, address->n_ipv4_nft_set_contexts);
else
return address_del_nft_set_context(address, address->ipv6_nft_set_context, address->n_ipv6_nft_set_contexts);
default:
return;
}
}
static int address_add(Link *link, Address *address) {
int r;
@@ -496,6 +594,8 @@ static int address_update(Address *address) {
address_add_netlabel(address);
address_add_nft_set(address);
if (address_is_ready(address) && address->callback) {
r = address->callback(address);
if (r < 0)
@@ -522,6 +622,8 @@ static int address_drop(Address *address) {
if (r < 0)
log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
address_del_nft_set(address);
address_del_netlabel(address);
if (address->state == 0)
@@ -2084,3 +2186,71 @@ int network_drop_invalid_addresses(Network *network) {
return 0;
}
int config_parse_address_ipv4_nft_set_context(
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(section);
assert(lvalue);
assert(rvalue);
assert(data);
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 new address, ignoring assignment: %m");
return 0;
}
return config_parse_nft_set_context(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->ipv4_nft_set_context, &n->n_ipv4_nft_set_contexts);
}
int config_parse_address_ipv6_nft_set_context(
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(section);
assert(lvalue);
assert(rvalue);
assert(data);
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 new address, ignoring assignment: %m");
return 0;
}
return config_parse_nft_set_context(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->ipv6_nft_set_context, &n->n_ipv6_nft_set_contexts);
}
+6
View File
@@ -8,6 +8,7 @@
#include "sd-ipv4acd.h"
#include "conf-parser.h"
#include "firewall-util.h"
#include "in-addr-util.h"
#include "networkd-link.h"
#include "networkd-util.h"
@@ -64,6 +65,9 @@ struct Address {
/* NetLabel */
Set *netlabels;
NFTSetContext *ipv4_nft_set_context, *ipv6_nft_set_context;
size_t n_ipv4_nft_set_contexts, n_ipv6_nft_set_contexts;
};
const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) _warn_unused_result_;
@@ -139,3 +143,5 @@ 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_ipv4_nft_set_context);
CONFIG_PARSER_PROTOTYPE(config_parse_address_ipv6_nft_set_context);
+6
View File
@@ -158,6 +158,8 @@ 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.IPv4NFTSet, config_parse_address_ipv4_nft_set_context, 0, 0
Address.IPv6NFTSet, config_parse_address_ipv6_nft_set_context, 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
@@ -246,6 +248,7 @@ DHCPv4.RouteMTUBytes, config_parse_mtu,
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_netlabel, 0, offsetof(Network, dhcp_netlabels)
DHCPv4.NFTSet, config_parse_dhcp_nft_set_context, 0, 0
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
@@ -264,6 +267,7 @@ DHCPv6.IAID, config_parse_iaid,
DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid)
DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid)
DHCPv6.NetLabel, config_parse_netlabel, 0, offsetof(Network, dhcp6_netlabels)
DHCPv6.NFTSet, config_parse_dhcp6_nft_set_context, 0, 0
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)
@@ -282,6 +286,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_netlabel, 0, offsetof(Network, ndisc_netlabels)
IPv6AcceptRA.NFTSet, config_parse_ndisc_nft_set_context, 0, 0
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)
@@ -349,6 +354,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_netlabel, 0, offsetof(Network, dhcp_pd_netlabels)
DHCPPrefixDelegation.NFTSet, config_parse_dhcp_pd_nft_set_context, 0, 0
IPv6SendRA.RouterLifetimeSec, config_parse_router_lifetime, 0, offsetof(Network, router_lifetime_usec)
IPv6SendRA.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
IPv6SendRA.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
+88
View File
@@ -690,6 +690,8 @@ static Network *network_free(Network *network) {
strv_free(network->dhcp6_vendor_class);
set_free(network->dhcp_netlabels);
set_free(network->dhcp6_netlabels);
nft_set_context_free_many(network->dhcp_nft_set_context, &network->n_dhcp_nft_set_contexts);
nft_set_context_free_many(network->dhcp6_nft_set_context, &network->n_dhcp6_nft_set_contexts);
strv_free(network->ntp);
for (unsigned i = 0; i < network->n_dns; i++)
@@ -758,6 +760,8 @@ static Network *network_free(Network *network) {
set_free(network->ndisc_tokens);
set_free(network->dhcp_pd_netlabels);
set_free(network->ndisc_netlabels);
nft_set_context_free_many(network->dhcp_pd_nft_set_context, &network->n_dhcp_pd_nft_set_contexts);
nft_set_context_free_many(network->ndisc_nft_set_context, &network->n_ndisc_nft_set_contexts);
return mfree(network);
}
@@ -1302,6 +1306,90 @@ int config_parse_ignore_carrier_loss(
return 0;
}
int config_parse_dhcp_nft_set_context(
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;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(network);
return config_parse_nft_set_context(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &network->dhcp_nft_set_context, &network->n_dhcp_nft_set_contexts);
}
int config_parse_dhcp6_nft_set_context(
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;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(network);
return config_parse_nft_set_context(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &network->dhcp6_nft_set_context, &network->n_dhcp6_nft_set_contexts);
}
int config_parse_dhcp_pd_nft_set_context(
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;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(network);
return config_parse_nft_set_context(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &network->dhcp_pd_nft_set_context, &network->n_dhcp_pd_nft_set_contexts);
}
int config_parse_ndisc_nft_set_context(
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;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(network);
return config_parse_nft_set_context(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &network->ndisc_nft_set_context, &network->n_ndisc_nft_set_contexts);
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
"Failed to parse RequiredFamilyForOnline= setting");
+13
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"
@@ -156,6 +157,8 @@ struct Network {
OrderedHashmap *dhcp_client_send_options;
OrderedHashmap *dhcp_client_send_vendor_options;
Set *dhcp_netlabels;
NFTSetContext *dhcp_nft_set_context;
size_t n_dhcp_nft_set_contexts;
/* DHCPv6 Client support */
bool dhcp6_use_address;
@@ -181,6 +184,8 @@ struct Network {
OrderedHashmap *dhcp6_client_send_vendor_options;
Set *dhcp6_request_options;
Set *dhcp6_netlabels;
NFTSetContext *dhcp6_nft_set_context;
size_t n_dhcp6_nft_set_contexts;
/* DHCP Server Support */
bool dhcp_server;
@@ -238,6 +243,8 @@ struct Network {
int dhcp_pd_uplink_index;
char *dhcp_pd_uplink_name;
Set *dhcp_pd_netlabels;
NFTSetContext *dhcp_pd_nft_set_context;
size_t n_dhcp_pd_nft_set_contexts;
/* Bridge Support */
int use_bpdu;
@@ -323,6 +330,8 @@ struct Network {
Set *ndisc_allow_listed_route_prefix;
Set *ndisc_tokens;
Set *ndisc_netlabels;
NFTSetContext *ndisc_nft_set_context;
size_t n_ndisc_nft_set_contexts;
/* LLDP support */
LLDPMode lldp_mode; /* LLDP reception */
@@ -388,6 +397,10 @@ CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_link_group);
CONFIG_PARSER_PROTOTYPE(config_parse_ignore_carrier_loss);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_nft_set_context);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_nft_set_context);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_nft_set_context);
CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_nft_set_context);
const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);