diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 4279326bc27..695bcaa0a47 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1532,6 +1532,13 @@
+
+ BlackList=
+
+ A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected.
+
+
+
@@ -1602,7 +1609,7 @@
BlackList=
- A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected.
+ A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router advertisements in the list are ignored.
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index 1d5c6412b5b..c1fab51b5e4 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -738,3 +738,15 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
}
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
+
+static void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
+ assert(addr);
+
+ siphash24_compress(addr, sizeof(*addr), state);
+}
+
+static int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
+ return memcmp(a, b, sizeof(*a));
+}
+
+DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index a6a685b9188..2ca7f4b32fc 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -72,3 +72,4 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
extern const struct hash_ops in_addr_data_hash_ops;
+extern const struct hash_ops in6_addr_hash_ops;
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 1af25dc812c..de177e6d1ab 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -1264,18 +1264,6 @@ static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *
return 0;
}
-static void dhcp6_prefixes_hash_func(const struct in6_addr *addr, struct siphash *state) {
- assert(addr);
-
- siphash24_compress(addr, sizeof(*addr), state);
-}
-
-static int dhcp6_prefixes_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
- return memcmp(a, b, sizeof(*a));
-}
-
-DEFINE_PRIVATE_HASH_OPS(dhcp6_prefixes_hash_ops, struct in6_addr, dhcp6_prefixes_hash_func, dhcp6_prefixes_compare_func);
-
int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
_cleanup_free_ struct in6_addr *a = NULL;
_cleanup_free_ char *buf = NULL;
@@ -1308,7 +1296,7 @@ int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
if (!a)
return -ENOMEM;
- r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &dhcp6_prefixes_hash_ops);
+ r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
if (r < 0)
return r;
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 28d46f5fe37..3016f3448bd 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -557,6 +557,46 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
return 0;
}
+static int ndisc_prefix_is_black_listed(Link *link, sd_ndisc_router *rt) {
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(rt);
+
+ for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
+ union in_addr_union a;
+ uint8_t type;
+
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
+ if (r == 0) /* EOF */
+ return false;
+
+ r = sd_ndisc_router_option_get_type(rt, &type);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
+
+ if (type != SD_NDISC_OPTION_PREFIX_INFORMATION)
+ continue;
+
+ r = sd_ndisc_router_prefix_get_address(rt, &a.in6);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix address: %m");
+
+ if (set_contains(link->network->ndisc_black_listed_prefix, &a.in6)) {
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *b = NULL;
+
+ (void) in_addr_to_string(AF_INET6, &a, &b);
+ log_link_debug(link, "Prefix '%s' is black listed, ignoring", strna(b));
+ }
+
+ return true;
+ }
+ }
+}
+
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
uint64_t flags;
int r;
@@ -581,8 +621,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
}
}
- (void) ndisc_router_process_default(link, rt);
- (void) ndisc_router_process_options(link, rt);
+ if (ndisc_prefix_is_black_listed(link, rt) == 0) {
+ (void) ndisc_router_process_default(link, rt);
+ (void) ndisc_router_process_options(link, rt);
+ }
return r;
}
@@ -672,3 +714,76 @@ void ndisc_flush(Link *link) {
link->ndisc_rdnss = set_free_free(link->ndisc_rdnss);
link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
}
+
+int config_parse_ndisc_black_listed_prefix(
+ 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 = data;
+ const char *p;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ network->ndisc_black_listed_prefix = set_free_free(network->ndisc_black_listed_prefix);
+ return 0;
+ }
+
+ for (p = rvalue;;) {
+ _cleanup_free_ char *n = NULL;
+ _cleanup_free_ struct in6_addr *a = NULL;
+ union in_addr_union ip;
+
+ r = extract_first_word(&p, &n, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse NDISC black listed prefix, ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ r = in_addr_from_string(AF_INET6, n, &ip);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "NDISC black listed prefix is invalid, ignoring assignment: %s", n);
+ continue;
+ }
+
+ r = set_ensure_allocated(&network->ndisc_black_listed_prefix, &in6_addr_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ a = newdup(struct in6_addr, &ip.in6, 1);
+ if (!a)
+ return log_oom();
+
+ r = set_put(network->ndisc_black_listed_prefix, a);
+ if (r < 0) {
+ if (r == -EEXIST)
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "NDISC black listed prefixs is duplicated, ignoring assignment: %s", n);
+ else
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store NDISC black listed prefix '%s', ignoring assignment: %m", n);
+ continue;
+ }
+
+ TAKE_PTR(a);
+ }
+
+ return 0;
+}
diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h
index 0b614bf50ff..dc0a44f5230 100644
--- a/src/network/networkd-ndisc.h
+++ b/src/network/networkd-ndisc.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include "conf-parser.h"
#include "networkd-link.h"
#include "time-util.h"
@@ -21,3 +22,5 @@ static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
int ndisc_configure(Link *link);
void ndisc_vacuum(Link *link);
void ndisc_flush(Link *link);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_black_listed_prefix);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 4d2f41fe400..9ef07ea372b 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -6,6 +6,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "conf-parser.h"
#include "network-internal.h"
#include "networkd-conf.h"
+#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "vlan-util.h"
%}
@@ -162,6 +163,7 @@ IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool,
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0
+IPv6AcceptRA.BlackList, config_parse_ndisc_black_listed_prefix, 0, 0
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index fcfb6d2e17f..a5e7cad58a4 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -505,6 +505,7 @@ static Network *network_free(Network *network) {
ordered_set_free_free(network->router_search_domains);
free(network->router_dns);
+ set_free_free(network->ndisc_black_listed_prefix);
free(network->bridge_name);
free(network->bond_name);
@@ -1658,11 +1659,6 @@ int config_parse_dhcp_black_listed_ip_address(
return log_oom();
r = set_put(network->dhcp_black_listed_ip, UINT32_TO_PTR(ip.in.s_addr));
- if (r == -EEXIST) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "DHCP black listed ip address is duplicated, ignoring assignment: %s", n);
- continue;
- }
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to store DHCP black listed ip address '%s', ignoring assignment: %m", n);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 7c434f6af22..d2a0b8c5f14 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -214,6 +214,7 @@ struct Network {
DHCPUseDomains ipv6_accept_ra_use_domains;
uint32_t ipv6_accept_ra_route_table;
bool ipv6_accept_ra_route_table_set;
+ Set *ndisc_black_listed_prefix;
union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index c905be0bcca..bba8948d352 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -198,6 +198,7 @@ RouteTable=
UseDNS=
UseAutonomousPrefix=
UseOnLinkPrefix=
+BlackList=
[DHCPServer]
EmitNTP=
PoolSize=