diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 4741e873181..2df79106906 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1349,6 +1349,14 @@ IPv6Token=prefixstable:2002:da8:1:: no. + + Blackhole= + + Takes a boolean. If enabled, packets to the corresponding routes are discarded + silently, and Gateway= cannot be specified. Defaults to + no. + + diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 8f8aff602a0..08da9120ad3 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -894,6 +894,8 @@ Manager* manager_free(Manager *m) { m->routes = set_free(m->routes); m->routes_foreign = set_free(m->routes_foreign); + m->nexthops = set_free(m->nexthops); + m->nexthops_foreign = set_free(m->nexthops_foreign); m->nexthops_by_id = hashmap_free(m->nexthops_by_id); sd_event_source_unref(m->speed_meter_event_source); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 3075b8f7076..c0cd4275170 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -64,6 +64,10 @@ struct Manager { /* Manage nexthops by id. */ Hashmap *nexthops_by_id; + /* Manager stores nexthops without RTA_OIF attribute. */ + Set *nexthops; + Set *nexthops_foreign; + /* Manager stores routes without RTA_OIF attribute. */ Set *routes; Set *routes_foreign; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 6e70e979891..b31224413ee 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -190,6 +190,7 @@ NextHop.Id, config_parse_nexthop_id, NextHop.Gateway, config_parse_nexthop_gateway, 0, 0 NextHop.Family, config_parse_nexthop_family, 0, 0 NextHop.OnLink, config_parse_nexthop_onlink, 0, 0 +NextHop.Blackhole, config_parse_nexthop_blackhole, 0, 0 DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCPv4.UseDNS, config_parse_dhcp_use_dns, 0, 0 DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns) diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index f98adf468e9..0b598823f65 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -33,6 +33,14 @@ NextHop *nexthop_free(NextHop *nexthop) { hashmap_remove(nexthop->link->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); } + if (nexthop->manager) { + set_remove(nexthop->manager->nexthops, nexthop); + set_remove(nexthop->manager->nexthops_foreign, nexthop); + + if (nexthop->id > 0) + hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); + } + return mfree(nexthop); } @@ -95,6 +103,7 @@ static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) { assert(nexthop); siphash24_compress(&nexthop->id, sizeof(nexthop->id), state); + siphash24_compress(&nexthop->blackhole, sizeof(nexthop->blackhole), state); siphash24_compress(&nexthop->family, sizeof(nexthop->family), state); switch (nexthop->family) { @@ -116,6 +125,10 @@ static int nexthop_compare_func(const NextHop *a, const NextHop *b) { if (r != 0) return r; + r = CMP(a->blackhole, b->blackhole); + if (r != 0) + return r; + r = CMP(a->family, b->family); if (r != 0) return r; @@ -133,6 +146,18 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( nexthop_compare_func, nexthop_free); +static void nexthop_copy(NextHop *dest, const NextHop *src) { + assert(dest); + assert(src); + + /* This only copies entries used in the above hash and compare functions. */ + + dest->id = src->id; + dest->blackhole = src->blackhole; + dest->family = src->family; + dest->gw = src->gw; +} + int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) { NextHop *nh; @@ -150,20 +175,20 @@ int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) { return 0; } -static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) { +static int nexthop_get(Manager *manager, Link *link, const NextHop *in, NextHop **ret) { NextHop *existing; - assert(link); + assert(manager || link); assert(in); - existing = set_get(link->nexthops, in); + existing = set_get(link ? link->nexthops : manager->nexthops, in); if (existing) { if (ret) *ret = existing; return 1; } - existing = set_get(link->nexthops_foreign, in); + existing = set_get(link ? link->nexthops_foreign : manager->nexthops_foreign, in); if (existing) { if (ret) *ret = existing; @@ -173,11 +198,11 @@ static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) { return -ENOENT; } -static int nexthop_add_internal(Link *link, Set **nexthops, const NextHop *in, NextHop **ret) { +static int nexthop_add_internal(Manager *manager, Link *link, Set **nexthops, const NextHop *in, NextHop **ret) { _cleanup_(nexthop_freep) NextHop *nexthop = NULL; int r; - assert(link); + assert(manager || link); assert(nexthops); assert(in); @@ -185,9 +210,7 @@ static int nexthop_add_internal(Link *link, Set **nexthops, const NextHop *in, N if (r < 0) return r; - nexthop->id = in->id; - nexthop->family = in->family; - nexthop->gw = in->gw; + nexthop_copy(nexthop, in); r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop); if (r < 0) @@ -196,6 +219,7 @@ static int nexthop_add_internal(Link *link, Set **nexthops, const NextHop *in, N return -EEXIST; nexthop->link = link; + nexthop->manager = manager; if (ret) *ret = nexthop; @@ -204,8 +228,9 @@ static int nexthop_add_internal(Link *link, Set **nexthops, const NextHop *in, N return 0; } -static int nexthop_add_foreign(Link *link, const NextHop *in, NextHop **ret) { - return nexthop_add_internal(link, &link->nexthops_foreign, in, ret); +static int nexthop_add_foreign(Manager *manager, Link *link, const NextHop *in, NextHop **ret) { + assert(manager || link); + return nexthop_add_internal(manager, link, link ? &link->nexthops_foreign : &manager->nexthops_foreign, in, ret); } static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) { @@ -213,20 +238,30 @@ static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) { NextHop *nexthop; int r; - r = nexthop_get(link, in, &nexthop); + assert(link); + assert(in); + + if (in->blackhole) + r = nexthop_get(link->manager, NULL, in, &nexthop); + else + r = nexthop_get(NULL, link, in, &nexthop); if (r == -ENOENT) { /* NextHop does not exist, create a new one */ - r = nexthop_add_internal(link, &link->nexthops, in, &nexthop); + r = nexthop_add_internal(link->manager, + in->blackhole ? NULL : link, + in->blackhole ? &link->manager->nexthops : &link->nexthops, + in, &nexthop); if (r < 0) return r; is_new = true; } else if (r == 0) { /* Take over a foreign nexthop */ - r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop); + r = set_ensure_put(in->blackhole ? &link->manager->nexthops : &link->nexthops, + &nexthop_hash_ops, nexthop); if (r < 0) return r; - set_remove(link->nexthops_foreign, nexthop); + set_remove(in->blackhole ? link->manager->nexthops_foreign : link->nexthops_foreign, nexthop); } else if (r == 1) { /* NextHop exists, do nothing */ ; @@ -238,11 +273,13 @@ static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) { return is_new; } -static int nexthop_update(Link *link, NextHop *nexthop, const NextHop *in) { +static int nexthop_update(Manager *manager, Link *link, NextHop *nexthop, const NextHop *in) { + Set *nexthops; int r; - assert(link); - assert(link->manager); + /* link may be NULL. */ + + assert(manager); assert(nexthop); assert(in); assert(in->id > 0); @@ -255,19 +292,21 @@ static int nexthop_update(Link *link, NextHop *nexthop, const NextHop *in) { return -EINVAL; } - nexthop = set_remove(link->nexthops, nexthop); + nexthops = link ? link->nexthops : manager->nexthops; + + nexthop = set_remove(nexthops, nexthop); if (!nexthop) return -ENOENT; nexthop->id = in->id; - r = set_put(link->nexthops, nexthop); + r = set_put(nexthops, nexthop); if (r <= 0) { int k; /* On failure, revert the change. */ nexthop->id = 0; - k = set_put(link->nexthops, nexthop); + k = set_put(nexthops, nexthop); if (k <= 0) { nexthop_free(nexthop); return k < 0 ? k : -EEXIST; @@ -277,13 +316,14 @@ static int nexthop_update(Link *link, NextHop *nexthop, const NextHop *in) { } set_manager: - return hashmap_ensure_put(&link->manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop); + return hashmap_ensure_put(&manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop); } static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *str, const Link *link) { assert(nexthop); assert(str); - assert(link); + + /* link may be NULL. */ if (DEBUG_LOGGING) { _cleanup_free_ char *gw = NULL; @@ -291,11 +331,11 @@ static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *s (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw); if (nexthop->id == id) - log_link_debug(link, "%s nexthop: id: %"PRIu32", gw: %s", - str, nexthop->id, strna(gw)); + log_link_debug(link, "%s nexthop: id: %"PRIu32", gw: %s, blackhole: %s", + str, nexthop->id, strna(gw), yes_no(nexthop->blackhole)); else - log_link_debug(link, "%s nexthop: id: %"PRIu32"→%"PRIu32", gw: %s", - str, nexthop->id, id, strna(gw)); + log_link_debug(link, "%s nexthop: id: %"PRIu32"→%"PRIu32", gw: %s, blackhole: %s", + str, nexthop->id, id, strna(gw), yes_no(nexthop->blackhole)); } } @@ -353,19 +393,25 @@ static int nexthop_configure(const NextHop *nexthop, Link *link) { return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m"); } - r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m"); - - if (in_addr_is_set(nexthop->family, &nexthop->gw)) { - r = netlink_message_append_in_addr_union(req, NHA_GATEWAY, nexthop->family, &nexthop->gw); + if (nexthop->blackhole) { + r = sd_netlink_message_append_flag(req, NHA_BLACKHOLE); if (r < 0) - return log_link_error_errno(link, r, "Could not append NHA_GATEWAY attribute: %m"); + return log_link_error_errno(link, r, "Could not append NHA_BLACKHOLE attribute: %m"); + } else { + r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m"); - if (nexthop->onlink > 0) { - r = sd_rtnl_message_nexthop_set_flags(req, RTNH_F_ONLINK); + if (in_addr_is_set(nexthop->family, &nexthop->gw)) { + r = netlink_message_append_in_addr_union(req, NHA_GATEWAY, nexthop->family, &nexthop->gw); if (r < 0) - return log_link_error_errno(link, r, "Failed to set RTNH_F_ONLINK flag: %m"); + return log_link_error_errno(link, r, "Could not append NHA_GATEWAY attribute: %m"); + + if (nexthop->onlink > 0) { + r = sd_rtnl_message_nexthop_set_flags(req, RTNH_F_ONLINK); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set RTNH_F_ONLINK flag: %m"); + } } } @@ -431,7 +477,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, NextHop *nexthop = NULL; uint32_t ifindex; uint16_t type; - Link *link; + Link *link = NULL; int r; assert(rtnl); @@ -456,22 +502,21 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, } r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex); - if (r == -ENODATA) { - log_warning_errno(r, "rtnl: received nexthop message without NHA_OIF attribute, ignoring: %m"); - return 0; - } else if (r < 0) { + if (r < 0 && r != -ENODATA) { log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m"); return 0; - } else if (ifindex <= 0) { - log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex); - return 0; - } + } else if (r >= 0) { + if (ifindex <= 0) { + log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex); + return 0; + } - r = link_get(m, ifindex, &link); - if (r < 0 || !link) { - if (!m->enumerating) - log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex); - return 0; + r = link_get(m, ifindex, &link); + if (r < 0 || !link) { + if (!m->enumerating) + log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex); + return 0; + } } r = nexthop_new(&tmp); @@ -491,6 +536,13 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, return 0; } + r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m"); + return 0; + } + tmp->blackhole = r; + r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id); if (r == -ENODATA) { log_link_warning_errno(link, r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m"); @@ -503,7 +555,12 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, return 0; } - r = nexthop_get(link, tmp, &nexthop); + /* All blackhole nexthops are managed by Manager. Note that the linux kernel does not set + * NHA_OID attribute when NHA_BLACKHOLE is set. Just for safety. */ + if (tmp->blackhole) + link = NULL; + + r = nexthop_get(m, link, tmp, &nexthop); if (r < 0) { uint32_t id; @@ -512,7 +569,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, id = tmp->id; tmp->id = 0; - (void) nexthop_get(link, tmp, &nexthop); + (void) nexthop_get(m, link, tmp, &nexthop); tmp->id = id; } @@ -523,14 +580,14 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, log_nexthop_debug(nexthop, tmp->id, "Received remembered", link); else { log_nexthop_debug(tmp, tmp->id, "Remembering foreign", link); - r = nexthop_add_foreign(link, tmp, &nexthop); + r = nexthop_add_foreign(m, link, tmp, &nexthop); if (r < 0) { log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m"); return 0; } } - r = nexthop_update(link, nexthop, tmp); + r = nexthop_update(m, link, nexthop, tmp); if (r < 0) { log_link_warning_errno(link, r, "Could not update nexthop, ignoring: %m"); return 0; @@ -556,6 +613,12 @@ static int nexthop_section_verify(NextHop *nh) { /* When no Gateway= is specified, assume IPv4. */ nh->family = AF_INET; + if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw)) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: blackhole nexthop cannot have gateway address. " + "Ignoring [NextHop] section from line %u.", + nh->section->filename, nh->section->line); + if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw) && ordered_hashmap_isempty(nh->network->addresses_by_section)) { /* If no address is configured, in most cases the gateway cannot be reachable. @@ -784,3 +847,42 @@ int config_parse_nexthop_onlink( TAKE_PTR(n); return 0; } + +int config_parse_nexthop_blackhole( + 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) { + + _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = nexthop_new_static(network, filename, section_line, &n); + if (r < 0) + return log_oom(); + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + n->blackhole = r; + + TAKE_PTR(n); + return 0; +} diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index 0356e997ecb..9ead5fc95ca 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -20,11 +20,13 @@ typedef struct NextHop { Network *network; NetworkConfigSection *section; + Manager *manager; Link *link; unsigned char protocol; uint32_t id; + bool blackhole; int family; union in_addr_union gw; int onlink; @@ -43,3 +45,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_family); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_onlink); +CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_blackhole); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index aa744d54700..a74541a6c95 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -451,34 +451,18 @@ static int route_get(const Manager *manager, const Link *link, const Route *in, assert(manager || link); assert(in); - if (link) { - existing = set_get(link->routes, in); - if (existing) { - if (ret) - *ret = existing; - return 1; - } + existing = set_get(link ? link->routes : manager->routes, in); + if (existing) { + if (ret) + *ret = existing; + return 1; + } - existing = set_get(link->routes_foreign, in); - if (existing) { - if (ret) - *ret = existing; - return 0; - } - } else { - existing = set_get(manager->routes, in); - if (existing) { - if (ret) - *ret = existing; - return 1; - } - - existing = set_get(manager->routes_foreign, in); - if (existing) { - if (ret) - *ret = existing; - return 0; - } + existing = set_get(link ? link->routes_foreign : manager->routes_foreign, in); + if (existing) { + if (ret) + *ret = existing; + return 0; } return -ENOENT; @@ -488,6 +472,8 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, c assert(dest); assert(src); + /* This only copies entries used by the above hash and compare functions. */ + dest->family = src->family; dest->src = src->src; dest->src_prefixlen = src->src_prefixlen; @@ -496,7 +482,10 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, c dest->prefsrc = src->prefsrc; dest->scope = src->scope; dest->protocol = src->protocol; - dest->type = src->type; + if (nh && nh->blackhole) + dest->type = RTN_BLACKHOLE; + else + dest->type = src->type; dest->tos = src->tos; dest->priority = src->priority; dest->table = src->table; @@ -593,19 +582,11 @@ static int route_add(Manager *manager, Link *link, const Route *in, const Multip is_new = true; } else if (r == 0) { /* Take over a foreign route */ - if (link) { - r = set_ensure_put(&link->routes, &route_hash_ops, route); - if (r < 0) - return r; + r = set_ensure_put(link ? &link->routes : &manager->routes, &route_hash_ops, route); + if (r < 0) + return r; - set_remove(link->routes_foreign, route); - } else { - r = set_ensure_put(&manager->routes, &route_hash_ops, route); - if (r < 0) - return r; - - set_remove(manager->routes_foreign, route); - } + set_remove(link ? link->routes_foreign : manager->routes_foreign, route); } else if (r == 1) { /* Route exists, do nothing */ ; @@ -981,8 +962,8 @@ static int route_add_and_setup_timer(Link *link, const Route *route, const Multi (void) manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh); - if (route_type_is_reject(route)) - k = route_add(link->manager, NULL, route, NULL, NULL, &nr); + if (route_type_is_reject(route) || (nh && nh->blackhole)) + k = route_add(link->manager, NULL, route, NULL, nh, &nr); else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex) k = route_add(NULL, link, route, m, nh, &nr); else { diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 04a2a4c9c19..48f0ca89515 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -353,6 +353,7 @@ Id= Gateway= Family= OnLink= +Blackhole= [QDisc] Parent= Handle= diff --git a/test/test-network/conf/25-nexthop.network b/test/test-network/conf/25-nexthop.network index a0b220f9184..8b3e4b65c56 100644 --- a/test/test-network/conf/25-nexthop.network +++ b/test/test-network/conf/25-nexthop.network @@ -28,9 +28,27 @@ Id=5 Gateway=192.168.10.1 OnLink=yes +[NextHop] +Id=6 +Family=ipv4 +Blackhole=yes + +[NextHop] +Id=7 +Family=ipv6 +Blackhole=yes + [NextHop] Gateway=192.168.5.2 +[NextHop] +Family=ipv4 +Blackhole=yes + +[NextHop] +Family=ipv6 +Blackhole=yes + [Route] NextHop=1 Destination=10.10.10.10 @@ -46,3 +64,11 @@ Destination=2001:1234:5:8f62::1 [Route] NextHop=5 Destination=10.10.10.12 + +[Route] +NextHop=6 +Destination=10.10.10.13 + +[Route] +NextHop=7 +Destination=2001:1234:5:8f62::2 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 2effa42e16d..6c17b204a11 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -404,6 +404,13 @@ def remove_routes(routes): for route_type, addr in routes: call('ip route del', route_type, addr, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) +def remove_blackhole_nexthops(): + ret = run('ip nexthop show dev lo', stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + if ret.returncode == 0: + for line in ret.stdout.rstrip().splitlines(): + id = line.split()[1] + call(f'ip nexthop del id {id}') + def remove_l2tp_tunnels(tunnel_ids): output = check_output('ip l2tp show tunnel') for tid in tunnel_ids: @@ -1831,12 +1838,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): routes = [['blackhole', '202.54.1.2'], ['unreachable', '202.54.1.3'], ['prohibit', '202.54.1.4']] def setUp(self): + remove_blackhole_nexthops() remove_routing_policy_rule_tables(self.routing_policy_rule_tables) remove_routes(self.routes) remove_links(self.links) stop_networkd(show_logs=False) def tearDown(self): + remove_blackhole_nexthops() remove_routing_policy_rule_tables(self.routing_policy_rule_tables) remove_routes(self.routes) remove_links(self.links) @@ -2829,6 +2838,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink') self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99') + # kernel manages blackhole nexthops on lo + output = check_output('ip nexthop list dev lo') + print(output) + self.assertIn('id 6 blackhole', output) + self.assertIn('id 7 blackhole', output) + output = check_output('ip route show dev veth99 10.10.10.10') print(output) self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output) @@ -2845,6 +2860,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output) + output = check_output('ip route show 10.10.10.13') + print(output) + self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output) + + output = check_output('ip -6 route show 2001:1234:5:8f62::2') + print(output) + self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output) + def test_qdisc(self): copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev', '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')