mirror of
https://github.com/systemd/systemd.git
synced 2024-11-02 02:21:44 +03:00
network: manage multipath routes separately
This commit is contained in:
parent
de52a83cb7
commit
f9bb333832
@ -457,36 +457,50 @@ static int route_get(const Manager *manager, const Link *link, const Route *in,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, Route **ret) {
|
||||
static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) {
|
||||
assert(dest);
|
||||
assert(src);
|
||||
|
||||
dest->family = src->family;
|
||||
dest->src = src->src;
|
||||
dest->src_prefixlen = src->src_prefixlen;
|
||||
dest->dst = src->dst;
|
||||
dest->dst_prefixlen = src->dst_prefixlen;
|
||||
dest->prefsrc = src->prefsrc;
|
||||
dest->scope = src->scope;
|
||||
dest->protocol = src->protocol;
|
||||
dest->type = src->type;
|
||||
dest->tos = src->tos;
|
||||
dest->priority = src->priority;
|
||||
dest->table = src->table;
|
||||
dest->initcwnd = src->initcwnd;
|
||||
dest->initrwnd = src->initrwnd;
|
||||
dest->lifetime = src->lifetime;
|
||||
|
||||
if (m) {
|
||||
dest->gw_family = m->gateway.family;
|
||||
dest->gw = m->gateway.address;
|
||||
dest->gw_weight = m->weight;
|
||||
} else {
|
||||
dest->gw_family = src->gw_family;
|
||||
dest->gw = src->gw;
|
||||
}
|
||||
}
|
||||
|
||||
static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, const MultipathRoute *m, Route **ret) {
|
||||
_cleanup_(route_freep) Route *route = NULL;
|
||||
int r;
|
||||
|
||||
assert(manager || link);
|
||||
assert(routes);
|
||||
assert(in);
|
||||
assert(!m || (link && (m->ifindex == 0 || m->ifindex == link->ifindex)));
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
route->family = in->family;
|
||||
route->src = in->src;
|
||||
route->src_prefixlen = in->src_prefixlen;
|
||||
route->dst = in->dst;
|
||||
route->dst_prefixlen = in->dst_prefixlen;
|
||||
route->gw_family = in->gw_family;
|
||||
route->gw = in->gw;
|
||||
route->prefsrc = in->prefsrc;
|
||||
route->scope = in->scope;
|
||||
route->protocol = in->protocol;
|
||||
route->type = in->type;
|
||||
route->tos = in->tos;
|
||||
route->priority = in->priority;
|
||||
route->table = in->table;
|
||||
route->initcwnd = in->initcwnd;
|
||||
route->initrwnd = in->initrwnd;
|
||||
route->lifetime = in->lifetime;
|
||||
route_copy(route, in, m);
|
||||
|
||||
r = set_ensure_put(routes, &route_hash_ops, route);
|
||||
if (r < 0)
|
||||
@ -507,10 +521,10 @@ static int route_add_internal(Manager *manager, Link *link, Set **routes, const
|
||||
|
||||
static int route_add_foreign(Manager *manager, Link *link, const Route *in, Route **ret) {
|
||||
assert(manager || link);
|
||||
return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret);
|
||||
return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, NULL, ret);
|
||||
}
|
||||
|
||||
static int route_add(Manager *manager, Link *link, const Route *in, Route **ret) {
|
||||
static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, Route **ret) {
|
||||
Route *route;
|
||||
int r;
|
||||
|
||||
@ -520,7 +534,7 @@ static int route_add(Manager *manager, Link *link, const Route *in, Route **ret)
|
||||
r = route_get(manager, link, in, &route);
|
||||
if (r == -ENOENT) {
|
||||
/* Route does not exist, create a new one */
|
||||
r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, &route);
|
||||
r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, m, &route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (r == 0) {
|
||||
@ -724,7 +738,7 @@ int link_drop_foreign_routes(Link *link) {
|
||||
continue;
|
||||
|
||||
if (link_is_static_route_configured(link, route))
|
||||
k = route_add(NULL, link, route, NULL);
|
||||
k = route_add(NULL, link, route, NULL, NULL);
|
||||
else
|
||||
k = route_remove(route, NULL, link, NULL);
|
||||
if (k < 0 && r >= 0)
|
||||
@ -768,6 +782,47 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
Route *nr;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(route);
|
||||
|
||||
if (IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW))
|
||||
r = route_add(link->manager, NULL, route, NULL, &nr);
|
||||
else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex)
|
||||
r = route_add(NULL, link, route, m, &nr);
|
||||
else {
|
||||
Link *link_gw;
|
||||
|
||||
r = link_get(link->manager, m->ifindex, &link_gw);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
|
||||
|
||||
r = route_add(NULL, link_gw, route, m, &nr);
|
||||
}
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not add route: %m");
|
||||
|
||||
/* TODO: drop expiration handling once it can be pushed into the kernel */
|
||||
if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
|
||||
r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
|
||||
nr->lifetime, 0, route_expire_handler, nr);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
|
||||
}
|
||||
|
||||
sd_event_source_unref(nr->expire);
|
||||
nr->expire = TAKE_PTR(expire);
|
||||
|
||||
if (ret)
|
||||
*ret = nr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int append_nexthop_one(const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
|
||||
struct rtnexthop *rtnh;
|
||||
struct rtattr *new_rta;
|
||||
@ -856,9 +911,7 @@ int route_configure(
|
||||
Route **ret) {
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
|
||||
unsigned flags;
|
||||
Route *nr;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -1054,26 +1107,26 @@ int route_configure(
|
||||
|
||||
link_ref(link);
|
||||
|
||||
if (IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW) || !ordered_set_isempty(route->multipath_routes))
|
||||
r = route_add(link->manager, NULL, route, &nr);
|
||||
else
|
||||
r = route_add(NULL, link, route, &nr);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not add route: %m");
|
||||
if (ordered_set_isempty(route->multipath_routes)) {
|
||||
Route *nr;
|
||||
|
||||
/* TODO: drop expiration handling once it can be pushed into the kernel */
|
||||
if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
|
||||
r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
|
||||
nr->lifetime, 0, route_expire_handler, nr);
|
||||
r = route_add_and_setup_timer(link, route, NULL, &nr);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
|
||||
}
|
||||
|
||||
sd_event_source_unref(nr->expire);
|
||||
nr->expire = TAKE_PTR(expire);
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = nr;
|
||||
} else {
|
||||
MultipathRoute *m;
|
||||
|
||||
assert(!ret);
|
||||
|
||||
ORDERED_SET_FOREACH(m, route->multipath_routes) {
|
||||
r = route_add_and_setup_timer(link, route, m, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -1161,14 +1214,105 @@ int link_set_routes(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
|
||||
_cleanup_(route_freep) Route *tmp = NULL;
|
||||
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;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(tmp);
|
||||
assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
|
||||
|
||||
if (m) {
|
||||
if (link)
|
||||
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
|
||||
"rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
|
||||
|
||||
if (m->ifindex <= 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"rtnl: received multipath route with invalid ifindex, ignoring.");
|
||||
|
||||
r = link_get(manager, m->ifindex, &link);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = route_new(&nr);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
route_copy(nr, tmp, m);
|
||||
|
||||
tmp = nr;
|
||||
}
|
||||
|
||||
(void) route_get(manager, link, tmp, &route);
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
|
||||
*buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL;
|
||||
char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX],
|
||||
buf_protocol[ROUTE_PROTOCOL_STR_MAX];
|
||||
|
||||
if (!in_addr_is_null(tmp->family, &tmp->dst)) {
|
||||
(void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst);
|
||||
(void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen);
|
||||
}
|
||||
if (!in_addr_is_null(tmp->family, &tmp->src))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->src, &buf_src);
|
||||
if (!in_addr_is_null(tmp->gw_family, &tmp->gw))
|
||||
(void) in_addr_to_string(tmp->gw_family, &tmp->gw, &buf_gw);
|
||||
if (!in_addr_is_null(tmp->family, &tmp->prefsrc))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc);
|
||||
|
||||
log_link_debug(link,
|
||||
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
|
||||
(!route && !manager->manage_foreign_routes) ? "Ignoring received foreign" :
|
||||
type == RTM_DELROUTE ? "Forgetting" :
|
||||
route ? "Received remembered" : "Remembering",
|
||||
strna(buf_dst), strempty(buf_dst_prefixlen),
|
||||
strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
|
||||
format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
|
||||
format_route_table(tmp->table, buf_table, sizeof buf_table),
|
||||
format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol),
|
||||
strna(route_type_to_string(tmp->type)));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case RTM_NEWROUTE:
|
||||
if (!route && manager->manage_foreign_routes) {
|
||||
/* A route appeared that we did not request */
|
||||
r = route_add_foreign(manager, link, tmp, NULL);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELROUTE:
|
||||
route_free(route);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Received route message with invalid RTNL message type");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
|
||||
_cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
|
||||
_cleanup_(route_freep) Route *tmp = NULL;
|
||||
_cleanup_free_ void *rta_multipath = NULL;
|
||||
Link *link = NULL;
|
||||
uint32_t ifindex;
|
||||
uint16_t type;
|
||||
unsigned char table;
|
||||
RouteVia via;
|
||||
size_t rta_len;
|
||||
int r;
|
||||
|
||||
assert(rtnl);
|
||||
@ -1370,57 +1514,28 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
|
||||
}
|
||||
}
|
||||
|
||||
(void) route_get(m, link, tmp, &route);
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
|
||||
*buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL;
|
||||
char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX],
|
||||
buf_protocol[ROUTE_PROTOCOL_STR_MAX];
|
||||
|
||||
if (!in_addr_is_null(tmp->family, &tmp->dst)) {
|
||||
(void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst);
|
||||
(void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen);
|
||||
}
|
||||
if (!in_addr_is_null(tmp->family, &tmp->src))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->src, &buf_src);
|
||||
if (!in_addr_is_null(tmp->gw_family, &tmp->gw))
|
||||
(void) in_addr_to_string(tmp->gw_family, &tmp->gw, &buf_gw);
|
||||
if (!in_addr_is_null(tmp->family, &tmp->prefsrc))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc);
|
||||
|
||||
log_link_debug(link,
|
||||
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
|
||||
(!route && !m->manage_foreign_routes) ? "Ignoring received foreign" :
|
||||
type == RTM_DELROUTE ? "Forgetting" :
|
||||
route ? "Received remembered" : "Remembering",
|
||||
strna(buf_dst), strempty(buf_dst_prefixlen),
|
||||
strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
|
||||
format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
|
||||
format_route_table(tmp->table, buf_table, sizeof buf_table),
|
||||
format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol),
|
||||
strna(route_type_to_string(tmp->type)));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case RTM_NEWROUTE:
|
||||
if (!route && m->manage_foreign_routes) {
|
||||
/* A route appeared that we did not request */
|
||||
r = route_add_foreign(m, link, tmp, &route);
|
||||
r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta_multipath);
|
||||
if (r < 0 && r != -ENODATA) {
|
||||
log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
|
||||
return 0;
|
||||
} else if (r >= 0) {
|
||||
r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &multipath_routes);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
|
||||
log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
if (ordered_set_isempty(multipath_routes))
|
||||
(void) process_route_one(m, link, type, tmp, NULL);
|
||||
else {
|
||||
MultipathRoute *mr;
|
||||
|
||||
case RTM_DELROUTE:
|
||||
route_free(route);
|
||||
ORDERED_SET_FOREACH(mr, multipath_routes) {
|
||||
r = process_route_one(m, link, type, tmp, mr);
|
||||
if (r < 0)
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Received route message with invalid RTNL message type");
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -1457,11 +1572,9 @@ int link_deserialize_routes(Link *link, const char *routes) {
|
||||
assert(link);
|
||||
|
||||
for (const char *p = routes;; ) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
|
||||
_cleanup_(route_freep) Route *tmp = NULL;
|
||||
_cleanup_free_ char *route_str = NULL;
|
||||
char *prefixlen_str;
|
||||
Route *route;
|
||||
|
||||
r = extract_first_word(&p, &route_str, NULL, 0);
|
||||
if (r < 0)
|
||||
@ -1500,20 +1613,9 @@ int link_deserialize_routes(Link *link, const char *routes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r = route_add(NULL, link, tmp, &route);
|
||||
r = route_add_and_setup_timer(link, tmp, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "Failed to add route: %m");
|
||||
|
||||
if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
|
||||
r = sd_event_add_time(link->manager->event, &expire,
|
||||
clock_boottime_or_monotonic(),
|
||||
route->lifetime, 0, route_expire_handler, route);
|
||||
if (r < 0)
|
||||
log_link_debug_errno(link, r, "Could not arm route expiration handler: %m");
|
||||
}
|
||||
|
||||
sd_event_source_unref(route->expire);
|
||||
route->expire = TAKE_PTR(expire);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ typedef struct Route {
|
||||
|
||||
int family;
|
||||
int gw_family;
|
||||
uint32_t gw_weight;
|
||||
int quickack;
|
||||
int fast_open_no_cookie;
|
||||
int ttl_propagate;
|
||||
|
Loading…
Reference in New Issue
Block a user