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:
committed by
Topi Miettinen
parent
e8f1b50f27
commit
ab51fd9dbd
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user