]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: add NextHop= setting in [Route] section
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 9 Feb 2021 09:52:57 +0000 (18:52 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 19 Feb 2021 13:42:39 +0000 (22:42 +0900)
man/systemd.network.xml
src/network/networkd-network-gperf.gperf
src/network/networkd-nexthop.c
src/network/networkd-nexthop.h
src/network/networkd-route.c
src/network/networkd-route.h
test/fuzz/fuzz-network-parser/directives.network

index 610799724b615025b4ed9d6d19bab4d5eedd2946..dfc1dd37ff75f126826b9b3fd23e6e1650613cbf 100644 (file)
@@ -1554,6 +1554,13 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
             times. If an empty string is assigned, then the all previous assignments are cleared.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>NextHop=</varname></term>
+          <listitem>
+            <para>Specifies the nexthop id. Takes an unsigned integer in the range 1…4294967295.
+            If set, the corresponding [NextHop] section must be configured. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
index e7e51e2f19ced07d6067f1ffddd2d3935041e0d6..e74a44b2a64697c2340fb3bd5094526788ef07ba 100644 (file)
@@ -185,6 +185,7 @@ Route.QuickAck,                              config_parse_route_boolean,
 Route.FastOpenNoCookie,                      config_parse_route_boolean,                               0,                             0
 Route.TTLPropagate,                          config_parse_route_boolean,                               0,                             0
 Route.MultiPathRoute,                        config_parse_multipath_route,                             0,                             0
+Route.NextHop,                               config_parse_route_nexthop,                               0,                             0
 NextHop.Id,                                  config_parse_nexthop_id,                                  0,                             0
 NextHop.Gateway,                             config_parse_nexthop_gateway,                             0,                             0
 NextHop.Family,                              config_parse_nexthop_family,                              0,                             0
index 9f94b64107224ad41007a75e1ad0dbc353a6c6fb..be41f1e9d688c6ea0e327100e0a02c1c365c1c52 100644 (file)
@@ -132,6 +132,23 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 nexthop_compare_func,
                 nexthop_free);
 
+int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
+        NextHop *nh;
+
+        assert(manager);
+
+        if (id == 0)
+                return -EINVAL;
+
+        nh = hashmap_get(manager->nexthops_by_id, UINT32_TO_PTR(id));
+        if (!nh)
+                return -ENOENT;
+
+        if (ret)
+                *ret = nh;
+        return 0;
+}
+
 static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) {
         NextHop *existing;
 
index 80af1e1feeb012d14a86d856bbcada3d4c11d073..06673d4369dbf95b2d89df591d990231d9b6f6c2 100644 (file)
@@ -35,6 +35,7 @@ void network_drop_invalid_nexthops(Network *network);
 
 int link_set_nexthops(Link *link);
 
+int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret);
 int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);
index bf0e35b7dada3039ec452856857325a65dea795e..09ad84640330d66f77a70b56b91c481846432c08 100644 (file)
@@ -328,6 +328,7 @@ void route_hash_func(const Route *route, struct siphash *state) {
                 siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
 
                 siphash24_compress(&route->advmss, sizeof(route->advmss), state);
+                siphash24_compress(&route->nexthop_id, sizeof(route->nexthop_id), state);
 
                 break;
         default:
@@ -416,6 +417,10 @@ int route_compare_func(const Route *a, const Route *b) {
                 if (r != 0)
                         return r;
 
+                r = CMP(a->nexthop_id, b->nexthop_id);
+                if (r != 0)
+                        return r;
+
                 return 0;
         default:
                 /* treat any other address family as AF_UNSPEC */
@@ -479,7 +484,7 @@ static int route_get(const Manager *manager, const Link *link, const Route *in,
         return -ENOENT;
 }
 
-static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) {
+static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh) {
         assert(dest);
         assert(src);
 
@@ -498,9 +503,14 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) {
         dest->initcwnd = src->initcwnd;
         dest->initrwnd = src->initrwnd;
         dest->lifetime = src->lifetime;
-        dest->advmss= src->advmss;
+        dest->advmss = src->advmss;
+        dest->nexthop_id = src->nexthop_id;
 
-        if (m) {
+        if (nh) {
+                dest->gw_family = nh->family;
+                dest->gw = nh->gw;
+                dest->gw_weight = src->gw_weight;
+        } else if (m) {
                 dest->gw_family = m->gateway.family;
                 dest->gw = m->gateway.address;
                 dest->gw_weight = m->weight;
@@ -523,7 +533,7 @@ static int route_add_internal(Manager *manager, Link *link, Set **routes, const
         if (r < 0)
                 return r;
 
-        route_copy(route, in, NULL);
+        route_copy(route, in, NULL, NULL);
 
         r = set_ensure_put(routes, &route_hash_ops, route);
         if (r < 0)
@@ -547,7 +557,7 @@ static int route_add_foreign(Manager *manager, Link *link, const Route *in, Rout
         return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret);
 }
 
-static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, Route **ret) {
+static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, const NextHop *nh, Route **ret) {
         _cleanup_(route_freep) Route *tmp = NULL;
         bool is_new = false;
         Route *route;
@@ -556,14 +566,21 @@ static int route_add(Manager *manager, Link *link, const Route *in, const Multip
         assert(manager || link);
         assert(in);
 
-        if (m) {
+        if (nh) {
+                r = route_new(&tmp);
+                if (r < 0)
+                        return r;
+
+                route_copy(tmp, in, NULL, nh);
+                in = tmp;
+        } else if (m) {
                 assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex));
 
                 r = route_new(&tmp);
                 if (r < 0)
                         return r;
 
-                route_copy(tmp, in, m);
+                route_copy(tmp, in, m, NULL);
                 in = tmp;
         }
 
@@ -722,7 +739,7 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not set route type: %m");
 
-        if (!route_type_is_reject(route)) {
+        if (!route_type_is_reject(route) && route->nexthop_id == 0) {
                 assert(link); /* Those routes must be attached to a specific link */
 
                 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
@@ -730,6 +747,12 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req
                         return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
         }
 
+        if (route->nexthop_id > 0) {
+                r = sd_netlink_message_append_u32(req, RTA_NH_ID, route->nexthop_id);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append RTA_NH_ID attribute: %m");
+        }
+
         r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
@@ -893,7 +916,7 @@ int link_drop_foreign_routes(Link *link) {
                         continue;
 
                 if (link_has_route(link, route))
-                        k = route_add(NULL, link, route, NULL, NULL);
+                        k = route_add(NULL, link, route, NULL, NULL, NULL);
                 else
                         k = route_remove(route, NULL, link, NULL);
                 if (k < 0 && r >= 0)
@@ -947,16 +970,20 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
 
 static int route_add_and_setup_timer(Link *link, const Route *route, const MultipathRoute *m, Route **ret) {
         _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
+        NextHop *nh = NULL;
         Route *nr;
         int r, k;
 
         assert(link);
+        assert(link->manager);
         assert(route);
 
+        (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, &nr);
+                k = route_add(link->manager, NULL, route, NULL, NULL, &nr);
         else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex)
-                k = route_add(NULL, link, route, m, &nr);
+                k = route_add(NULL, link, route, m, nh, &nr);
         else {
                 Link *link_gw;
 
@@ -964,7 +991,7 @@ static int route_add_and_setup_timer(Link *link, const Route *route, const Multi
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
 
-                k = route_add(NULL, link_gw, route, m, &nr);
+                k = route_add(NULL, link_gw, route, m, NULL, &nr);
         }
         if (k < 0)
                 return log_link_error_errno(link, k, "Could not add route: %m");
@@ -1258,6 +1285,9 @@ static bool route_has_gateway(const Route *route) {
         if (!ordered_set_isempty(route->multipath_routes))
                 return true;
 
+        if (route->nexthop_id > 0)
+                return true;
+
         return false;
 }
 
@@ -1356,13 +1386,30 @@ int link_set_routes(Link *link) {
 static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
         _cleanup_(route_freep) Route *nr = NULL;
         Route *route = NULL;
+        NextHop *nh = NULL;
         int r;
 
         assert(manager);
         assert(tmp);
         assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
 
-        if (m) {
+        (void) manager_get_nexthop_by_id(manager, tmp->nexthop_id, &nh);
+
+        if (nh) {
+                if (link && link != nh->link)
+                        return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
+                                                      "rtnl: received RTA_OIF and ifindex of nexthop corresponding to RTA_NH_ID do not match, ignoring.");
+
+                link = nh->link;
+
+                r = route_new(&nr);
+                if (r < 0)
+                        return log_oom();
+
+                route_copy(nr, tmp, NULL, nh);
+
+                tmp = nr;
+        } else if (m) {
                 if (link)
                         return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
                                                 "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
@@ -1381,7 +1428,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const
                 if (r < 0)
                         return log_oom();
 
-                route_copy(nr, tmp, m);
+                route_copy(nr, tmp, m, NULL);
 
                 tmp = nr;
         }
@@ -1573,6 +1620,12 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 return 0;
         }
 
+        r = sd_netlink_message_read_u32(message, RTA_NH_ID, &tmp->nexthop_id);
+        if (r < 0 && r != -ENODATA) {
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid nexthop id, ignoring: %m");
+                return 0;
+        }
+
         r = sd_netlink_message_enter_container(message, RTA_METRICS);
         if (r < 0 && r != -ENODATA) {
                 log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m");
@@ -1966,6 +2019,59 @@ int config_parse_route_scope(
         return 0;
 }
 
+int config_parse_route_nexthop(
+                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_(route_free_or_set_invalidp) Route *n = NULL;
+        uint32_t id;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = route_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 route, ignoring assignment: %m");
+                return 0;
+        }
+
+        if (isempty(rvalue)) {
+                n->nexthop_id = 0;
+                TAKE_PTR(n);
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &id);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nexthop ID, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+        if (id == 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid nexthop ID, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        n->nexthop_id = id;
+        TAKE_PTR(n);
+        return 0;
+}
+
 int config_parse_route_table(
                 const char *unit,
                 const char *filename,
@@ -2656,6 +2762,14 @@ static int route_section_verify(Route *route, Network *network) {
                                                          route->section->filename, route->section->line);
         }
 
+        if (route->nexthop_id > 0 &&
+            (in_addr_is_set(route->gw_family, &route->gw) ||
+             !ordered_set_isempty(route->multipath_routes)))
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
+                                         "Ignoring [Route] section from line %u.",
+                                         route->section->filename, route->section->line);
+
         return 0;
 }
 
index 92ef00e31f7984688188aa67e3da75122db02d11..331f1f9ea2e0f113bb2a6c41b6cd5bb406ee1ff7 100644 (file)
@@ -44,6 +44,7 @@ typedef struct Route {
         unsigned char pref;
         unsigned flags;
         int gateway_onlink;
+        uint32_t nexthop_id;
 
         bool scope_set:1;
         bool table_set:1;
@@ -105,3 +106,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
 CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
 CONFIG_PARSER_PROTOTYPE(config_parse_tcp_advmss);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_table_names);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop);
index d20fb78f153f68e2ddf8b4a3ce4c72a6ad24d097..90420f42b59fee18e20e944e983c1c83fb0cdb1a 100644 (file)
@@ -166,6 +166,7 @@ Metric=
 TTLPropagate=
 MultiPathRoute=
 TCPAdvertisedMaximumSegmentSize=
+NextHop=
 [Network]
 IPv6DuplicateAddressDetection=
 IPMasquerade=