]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: nexthop: add Blackhole= setting in [NextHop] section
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 14 Feb 2021 17:56:24 +0000 (02:56 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 22 Feb 2021 17:21:17 +0000 (02:21 +0900)
As similar to unreachable type routes, blackhole nexthops do not have
NHA_OID attribute, so they are managed by Manager.

man/systemd.network.xml
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-network-gperf.gperf
src/network/networkd-nexthop.c
src/network/networkd-nexthop.h
src/network/networkd-route.c
test/fuzz/fuzz-network-parser/directives.network

index 4741e8731811a7cba4db24b05f9d92391a03094b..2df7910690637609b0c77100e99b145ef395fb8b 100644 (file)
@@ -1349,6 +1349,14 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
             <literal>no</literal>.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>Blackhole=</varname></term>
+          <listitem>
+            <para>Takes a boolean. If enabled, packets to the corresponding routes are discarded
+            silently, and <varname>Gateway=</varname> cannot be specified. Defaults to
+            <literal>no</literal>.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
index 8f8aff602a0c0851fd3a97812f3008b6bf40f549..08da9120ad32592f2285a3ac8232cf37d82f424a 100644 (file)
@@ -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);
index 3075b8f70761e072d16e95251f704f23b31c211d..c0cd4275170d08a02352efe02987cb94bb43c017 100644 (file)
@@ -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;
index 6e70e979891494f3774f5b097319bc6d83fdf6d7..b31224413eeb1e09bf4437ac641ec14ea55da37a 100644 (file)
@@ -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)
index f98adf468e9973510cbb61ca157462cb8f82f897..0b598823f6535d4888e04360a1e96016e800d917 100644 (file)
@@ -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_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_GATEWAY attribute: %m");
+                        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;
+}
index 0356e997ecb9cc2a39ffad742abaca61a170b49f..9ead5fc95ca41d6704b6e085b534008371130fea 100644 (file)
@@ -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);
index aa744d54700ad94db2e922d27b95bbf742753f1f..882b71f9d953748eaf2c3f404b3dc32eab80b383 100644 (file)
@@ -496,7 +496,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;
@@ -981,8 +984,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 {
index 04a2a4c9c193c3b047c9efd5ad2e3c002bb856ee..48f0ca895153e29bcd162e79cefe4b0ead03eb2d 100644 (file)
@@ -353,6 +353,7 @@ Id=
 Gateway=
 Family=
 OnLink=
+Blackhole=
 [QDisc]
 Parent=
 Handle=