network: introduce multipath route

Closes #12541.
This commit is contained in:
Yu Watanabe 2019-11-29 00:36:02 +09:00
parent 6497a8aa9b
commit 6ff5cc6b7a
6 changed files with 221 additions and 1 deletions

View File

@ -1380,6 +1380,16 @@
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>

View 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);

View 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)

View 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;

View 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);

View File

@ -123,6 +123,7 @@ FastOpenNoCookie=
Source=
Metric=
TTLPropagate=
MultiPathRoute=
[Network]
IPv6DuplicateAddressDetection=
IPMasquerade=