#include "networkd-route.h"
#include "parse-util.h"
#include "set.h"
+#include "socket-netlink.h"
#include "string-table.h"
#include "string-util.h"
+#include "strv.h"
#include "strxcpyx.h"
#include "sysctl-util.h"
-#include "util.h"
+#include "vrf.h"
#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
set_remove(route->link->routes_foreign, route);
}
+ ordered_set_free_free(route->multipath_routes);
+
sd_event_source_unref(route->expire);
free(route);
route->initrwnd = in->initrwnd;
route->lifetime = in->lifetime;
- r = set_ensure_allocated(routes, &route_hash_ops);
- if (r < 0)
- return r;
-
- r = set_put(*routes, route);
+ r = set_ensure_put(routes, &route_hash_ops, route);
if (r < 0)
return r;
if (r == 0)
return r;
} else if (r == 0) {
/* Take over a foreign route */
- r = set_ensure_allocated(&link->routes, &route_hash_ops);
- if (r < 0)
- return r;
-
- r = set_put(link->routes, route);
+ r = set_ensure_put(&link->routes, &route_hash_ops, route);
if (r < 0)
return r;
r = route_remove(route, route->link, NULL);
if (r < 0)
- log_warning_errno(r, "Could not remove route: %m");
+ log_link_warning_errno(route->link, r, "Could not remove route: %m");
else
route_free(route);
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,
- link_netlink_message_handler_t callback) {
+ link_netlink_message_handler_t callback,
+ Route **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
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)
sd_event_source_unref(route->expire);
route->expire = TAKE_PTR(expire);
+ if (ret)
+ *ret = route;
+
return 1;
}
/* we are not in an Route section, so treat
* this as the special '0' section */
r = route_new_static(network, NULL, 0, &n);
- } else
+ 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;
+ }
+ } else {
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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 (streq(rvalue, "_dhcp")) {
+ n->gateway_from_dhcp = true;
+ TAKE_PTR(n);
+ return 0;
+ }
+ }
if (n->family == AF_UNSPEC)
r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
else
r = in_addr_from_string(n->family, rvalue, &n->gw);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
+ log_syntax(unit, LOG_WARNING, filename, line, r,
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
return 0;
}
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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 (n->family == AF_UNSPEC)
r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
else
r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
return 0;
}
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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 (streq(lvalue, "Destination")) {
buffer = &n->dst;
else
r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
return 0;
}
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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;
+ }
r = safe_atou32(rvalue, &n->priority);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
+ log_syntax(unit, LOG_WARNING, filename, line, r,
"Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
return 0;
}
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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;
+ }
r = route_scope_from_string(rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route scope: %s", rvalue);
return 0;
}
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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;
+ }
r = route_table_from_string(rvalue);
if (r >= 0)
else {
r = safe_atou32(rvalue, &n->table);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
+ log_syntax(unit, LOG_WARNING, filename, line, r,
"Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
return 0;
}
return 0;
}
-int config_parse_gateway_onlink(
+int config_parse_route_boolean(
const char *unit,
const char *filename,
unsigned line,
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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;
+ }
r = parse_boolean(rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
+ log_syntax(unit, LOG_WARNING, filename, line, r,
"Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
return 0;
}
- n->gateway_onlink = r;
+ if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
+ n->gateway_onlink = r;
+ else if (streq(lvalue, "QuickAck"))
+ n->quickack = r;
+ else if (streq(lvalue, "FastOpenNoCookie"))
+ n->fast_open_no_cookie = r;
+ else if (streq(lvalue, "TTLPropagate"))
+ n->ttl_propagate = r;
+ else
+ assert_not_reached("Invalid lvalue");
TAKE_PTR(n);
return 0;
int r;
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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 (streq(rvalue, "low"))
n->pref = ICMPV6_ROUTER_PREF_LOW;
else if (streq(rvalue, "high"))
n->pref = ICMPV6_ROUTER_PREF_HIGH;
else {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route preference: %s", rvalue);
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
return 0;
}
int r;
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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;
+ }
r = route_protocol_from_string(rvalue);
if (r >= 0)
else {
r = safe_atou8(rvalue , &n->protocol);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
+ log_syntax(unit, LOG_WARNING, filename, line, r,
"Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
return 0;
}
int t, r;
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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;
+ }
t = route_type_from_string(rvalue);
if (t < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
"Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
return 0;
}
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
+ 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;
+ }
r = parse_size(rvalue, 1024, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
+ log_syntax(unit, LOG_WARNING, filename, line, r,
"Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
return 0;
}
if (k > UINT32_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
"Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
return 0;
}
return 0;
}
-int config_parse_quickack(
+int config_parse_route_mtu(
const char *unit,
const char *filename,
unsigned line,
void *data,
void *userdata) {
- _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
Network *network = userdata;
- int k, r;
+ _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+ int r;
assert(filename);
assert(section);
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
-
- k = parse_boolean(rvalue);
- if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, k,
- "Failed to parse TCP quickack, ignoring: %s", rvalue);
+ 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;
}
- n->quickack = !!k;
+ r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
+ if (r < 0)
+ return r;
+
TAKE_PTR(n);
return 0;
}
-int config_parse_fast_open_no_cookie(
+int config_parse_multipath_route(
const char *unit,
const char *filename,
unsigned line,
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;
- int k, r;
+ const char *p, *ip, *dev;
+ union in_addr_union a;
+ int family, r;
assert(filename);
assert(section);
assert(data);
r = route_new_static(network, filename, section_line, &n);
- if (r < 0)
- return r;
-
- k = parse_boolean(rvalue);
- if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, k,
- "Failed to parse TCP fastopen no cookie, ignoring: %s", rvalue);
+ 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;
}
- n->fast_open_no_cookie = k;
- TAKE_PTR(n);
- return 0;
-}
-
-int config_parse_route_mtu(
- 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;
- int 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;
+ }
- r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
- if (r < 0)
- return r;
+ 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_WARNING, filename, line, r,
+ "Invalid multipath route option, ignoring assignment: %s", rvalue);
+ return 0;
+ }
- TAKE_PTR(n);
- return 0;
-}
+ dev = strchr(word, '@');
+ if (dev) {
+ buf = strndup(word, dev - word);
+ if (!buf)
+ return log_oom();
+ ip = buf;
+ dev++;
+ } else
+ ip = word;
-int config_parse_route_ttl_propagate(
- 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) {
+ r = in_addr_from_string_auto(ip, &family, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
+ return 0;
+ }
+ m->gateway.address = a;
+ m->gateway.family = family;
- Network *network = userdata;
- _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
- int r, k;
+ if (dev) {
+ r = resolve_interface(NULL, dev);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid interface name or index, ignoring assignment: %s", dev);
+ return 0;
+ }
+ m->ifindex = r;
+ }
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ if (!isempty(p)) {
+ r = safe_atou32(p, &m->weight);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid multipath route weight, ignoring assignment: %s", p);
+ return 0;
+ }
+ if (m->weight == 0 || m->weight > 256) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid multipath route weight, ignoring assignment: %s", p);
+ return 0;
+ }
+ }
- r = route_new_static(network, filename, section_line, &n);
+ r = ordered_set_ensure_allocated(&n->multipath_routes, NULL);
if (r < 0)
- return r;
+ return log_oom();
- k = parse_boolean(rvalue);
- if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, k,
- "Failed to parse TTLPropagate= value, ignoring: %s", rvalue);
+ r = ordered_set_put(n->multipath_routes, m);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to store multipath route, ignoring assignment: %m");
return 0;
}
- n->ttl_propagate = k;
-
+ TAKE_PTR(m);
TAKE_PTR(n);
return 0;
}
route->section->filename, route->section->line);
}
+ if (!route->table_set && network->vrf) {
+ route->table = VRF(network->vrf)->table;
+ route->table_set = true;
+ }
+
if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
route->table = RT_TABLE_LOCAL;