]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: introduce multipath route
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 28 Nov 2019 15:36:02 +0000 (00:36 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 18 Dec 2019 13:12:57 +0000 (22:12 +0900)
Closes #12541.

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

index 958511176123d3dc1e55ab22185ad6a292560007..3be311d70b9bb29eb67c52e8b04a6e757171cbd5 100644 (file)
             service type to CS6 (network control) or CS4 (Realtime). Defaults to CS6.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>MultiPathRoute=<replaceable>address</replaceable>[@<replaceable>name</replaceable>] [<replaceable>weight</replaceable>]</varname></term>
+          <listitem>
+            <para>Configures multipath route. Multipath routing is the technique of using multiple
+            alternative paths through a network. Takes gateway address. Optionally, takes a network
+            interface name or index separated with <literal>@</literal>, and a weight in 1..256 for
+            this multipath route separated with whitespace. This setting can be specified multiple
+            times. If an empty string is assigned, then the all previous assignments are cleared.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
index dcdab6c83eb70855ced9c985a71493f1eb7530a3..6010648ec86b857665d549c72c460a95ba6f6ab6 100644 (file)
@@ -1040,7 +1040,7 @@ int link_request_set_routes(Link *link) {
         for (phase = 0; phase < _PHASE_MAX; phase++)
                 LIST_FOREACH(routes, rt, link->network->static_routes) {
 
-                        if (in_addr_is_null(rt->family, &rt->gw) != (phase == PHASE_NON_GATEWAY))
+                        if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
                                 continue;
 
                         r = route_configure(rt, link, route_handler);
index b06d32ec6d3ee2cfcf7b12e6ae22afd5dca1bc16..04c618827ff7841c7b2fc3b1acf2d20a2127ffca 100644 (file)
@@ -146,6 +146,7 @@ Route.InitialAdvertisedReceiveWindow,   config_parse_tcp_window,
 Route.QuickAck,                         config_parse_quickack,                           0,                             0
 Route.FastOpenNoCookie,                 config_parse_fast_open_no_cookie,                0,                             0
 Route.TTLPropagate,                     config_parse_route_ttl_propagate,                0,                             0
+Route.MultiPathRoute,                   config_parse_multipath_route,                    0,                             0
 NextHop.Id,                             config_parse_nexthop_id,                         0,                             0
 NextHop.Gateway,                        config_parse_nexthop_gateway,                    0,                             0
 DHCPv4.ClientIdentifier,                config_parse_dhcp_client_identifier,             0,                             offsetof(Network, dhcp_client_identifier)
index 2a42628e5b95e603333efc36d0b5090acce4e2fd..ecc8d219b7d1d59da62a3a31b19b94de35ddd23c 100644 (file)
@@ -144,6 +144,8 @@ void route_free(Route *route) {
                 set_remove(route->link->routes_foreign, route);
         }
 
+        ordered_set_free_free(route->multipath_routes);
+
         sd_event_source_unref(route->expire);
 
         free(route);
@@ -516,6 +518,88 @@ int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
         return 1;
 }
 
+static int append_nexthop_one(Route *route, MultipathRoute *m, struct rtattr **rta, size_t offset) {
+        struct rtnexthop *rtnh;
+        struct rtattr *new_rta;
+        int r;
+
+        assert(route);
+        assert(m);
+        assert(rta);
+        assert(*rta);
+
+        new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
+        if (!new_rta)
+                return -ENOMEM;
+        *rta = new_rta;
+
+        rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
+        *rtnh = (struct rtnexthop) {
+                .rtnh_len = sizeof(*rtnh),
+                .rtnh_ifindex = m->ifindex,
+                .rtnh_hops = m->weight > 0 ? m->weight - 1 : 0,
+        };
+
+        (*rta)->rta_len += sizeof(struct rtnexthop);
+
+        if (route->family == m->gateway.family) {
+                r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
+                if (r < 0)
+                        goto clear;
+                rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
+                rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
+        } else {
+                r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
+                if (r < 0)
+                        goto clear;
+                rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
+                rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
+        }
+
+        return 0;
+
+clear:
+        (*rta)->rta_len -= sizeof(struct rtnexthop);
+        return r;
+}
+
+static int append_nexthops(Route *route, sd_netlink_message *req) {
+        _cleanup_free_ struct rtattr *rta = NULL;
+        struct rtnexthop *rtnh;
+        MultipathRoute *m;
+        size_t offset;
+        Iterator i;
+        int r;
+
+        if (ordered_set_isempty(route->multipath_routes))
+                return 0;
+
+        rta = new(struct rtattr, 1);
+        if (!rta)
+                return -ENOMEM;
+
+        *rta = (struct rtattr) {
+                .rta_type = RTA_MULTIPATH,
+                .rta_len = RTA_LENGTH(0),
+        };
+        offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
+
+        ORDERED_SET_FOREACH(m, route->multipath_routes, i) {
+                r = append_nexthop_one(route, m, &rta, offset);
+                if (r < 0)
+                        return r;
+
+                rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
+                offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
+        }
+
+        r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int route_configure(
                 Route *route,
                 Link *link,
@@ -699,6 +783,10 @@ int route_configure(
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
 
+        r = append_nexthops(route, req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
+
         r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
                                link_netlink_destroy_callback, link);
         if (r < 0)
@@ -1480,6 +1568,113 @@ int config_parse_route_ttl_propagate(
         return 0;
 }
 
+int config_parse_multipath_route(
+                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_(route_free_or_set_invalidp) Route *n = NULL;
+        _cleanup_free_ char *word = NULL, *buf = NULL;
+        _cleanup_free_ MultipathRoute *m = NULL;
+        Network *network = userdata;
+        const char *p, *ip, *dev;
+        union in_addr_union a;
+        int family, r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = route_new_static(network, filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                n->multipath_routes = ordered_set_free_free(n->multipath_routes);
+                return 0;
+        }
+
+        m = new0(MultipathRoute, 1);
+        if (!m)
+                return log_oom();
+
+        p = rvalue;
+        r = extract_first_word(&p, &word, NULL, 0);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r <= 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Invalid multipath route option, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        dev = strchr(word, '@');
+        if (dev) {
+                buf = strndup(word, dev - word);
+                if (!buf)
+                        return log_oom();
+                ip = buf;
+                dev++;
+        } else
+                ip = word;
+
+        r = in_addr_from_string_auto(ip, &family, &a);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
+                return 0;
+        }
+        m->gateway.address = a;
+        m->gateway.family = family;
+
+        if (dev) {
+                r = parse_ifindex_or_ifname(dev, &m->ifindex);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Invalid interface name or index, ignoring assignment: %s", dev);
+                        return 0;
+                }
+        }
+
+        if (!isempty(p)) {
+                r = safe_atou32(p, &m->weight);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Invalid multipath route weight, ignoring assignment: %s", p);
+                        return 0;
+                }
+                if (m->weight == 0 || m->weight > 256) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                                   "Invalid multipath route weight, ignoring assignment: %s", p);
+                        return 0;
+                }
+        }
+
+        r = ordered_set_ensure_allocated(&n->multipath_routes, NULL);
+        if (r < 0)
+                return log_oom();
+
+        r = ordered_set_put(n->multipath_routes, m);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to store multipath route, ignoring assignment: %m");
+                return 0;
+        }
+
+        TAKE_PTR(m);
+        TAKE_PTR(n);
+        return 0;
+}
+
 int route_section_verify(Route *route, Network *network) {
         if (section_is_invalid(route->section))
                 return -EINVAL;
index 89d54020db82465df71feccda373b477ee91d4a8..91bba368ee21f73e14c5108a889f19667ef3dc64 100644 (file)
@@ -10,6 +10,17 @@ typedef struct NetworkConfigSection NetworkConfigSection;
 #include "networkd-network.h"
 #include "networkd-util.h"
 
+typedef struct MultipathRouteVia {
+        uint16_t family;
+        union in_addr_union address;
+} _packed_ MultipathRouteVia;
+
+typedef struct MultipathRoute {
+        MultipathRouteVia gateway;
+        int ifindex;
+        uint32_t weight;
+} MultipathRoute;
+
 struct Route {
         Network *network;
         NetworkConfigSection *section;
@@ -42,6 +53,7 @@ struct Route {
         union in_addr_union dst;
         union in_addr_union src;
         union in_addr_union prefsrc;
+        OrderedSet *multipath_routes;
 
         usec_t lifetime;
         sd_event_source *expire;
@@ -96,3 +108,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_quickack);
 CONFIG_PARSER_PROTOTYPE(config_parse_fast_open_no_cookie);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_ttl_propagate);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
+CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
index 4513bbb529e06a5e6cba3744bd3f8613fa790f34..92d1e2b6e10930b4fa57a0b74d679357c7a00a5c 100644 (file)
@@ -123,6 +123,7 @@ FastOpenNoCookie=
 Source=
 Metric=
 TTLPropagate=
+MultiPathRoute=
 [Network]
 IPv6DuplicateAddressDetection=
 IPMasquerade=