From e520ce64405dedfc1bdd3888f926a69f7d812ab9 Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Fri, 17 May 2019 21:51:20 +0530 Subject: [PATCH] networkd: Ability to selectively ignore IPv6 prefixes supplied via router advertisement Closes https://github.com/systemd/systemd/issues/10647 --- man/systemd.network.xml | 7 ++ src/network/networkd-ndisc.c | 119 +++++++++++++++++- src/network/networkd-ndisc.h | 3 + src/network/networkd-network-gperf.gperf | 2 + src/network/networkd-network.c | 1 + src/network/networkd-network.h | 1 + .../fuzz-network-parser/directives.network | 1 + 7 files changed, 132 insertions(+), 2 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 2191eeb797..695bcaa0a4 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1606,6 +1606,13 @@ + + BlackList= + + A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router advertisements in the list are ignored. + + + diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 28d46f5fe3..3016f3448b 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 0b614bf50f..dc0a44f523 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 4d2f41fe40..9ef07ea372 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 ac8c08456b..a5e7cad58a 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); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 7c434f6af2..d2a0b8c5f1 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 c905be0bcc..bba8948d35 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=