2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2017-04-25 09:32:59 +02:00
|
|
|
#include <linux/icmpv6.h>
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2013-10-17 03:18:36 +02:00
|
|
|
#include "conf-parser.h"
|
2015-10-03 18:40:28 +02:00
|
|
|
#include "in-addr-util.h"
|
2018-12-04 08:26:09 +01:00
|
|
|
#include "missing_network.h"
|
2015-08-27 13:59:06 +02:00
|
|
|
#include "netlink-util.h"
|
2019-06-29 20:57:47 +02:00
|
|
|
#include "networkd-ipv4ll.h"
|
2016-11-13 04:59:06 +01:00
|
|
|
#include "networkd-manager.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
#include "networkd-route.h"
|
|
|
|
#include "parse-util.h"
|
2015-10-25 14:46:21 +01:00
|
|
|
#include "set.h"
|
2020-01-11 11:21:01 +01:00
|
|
|
#include "socket-netlink.h"
|
2019-07-07 01:43:33 +02:00
|
|
|
#include "string-table.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2019-07-07 14:50:05 +02:00
|
|
|
#include "strxcpyx.h"
|
2016-08-21 15:06:28 +02:00
|
|
|
#include "sysctl-util.h"
|
2020-02-26 07:42:32 +01:00
|
|
|
#include "vrf.h"
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2016-08-21 15:06:28 +02:00
|
|
|
#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
|
|
|
|
|
|
|
|
static unsigned routes_max(void) {
|
|
|
|
static thread_local unsigned cached = 0;
|
|
|
|
|
|
|
|
_cleanup_free_ char *s4 = NULL, *s6 = NULL;
|
|
|
|
unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
|
|
|
|
|
|
|
|
if (cached > 0)
|
|
|
|
return cached;
|
|
|
|
|
|
|
|
if (sysctl_read("net/ipv4/route/max_size", &s4) >= 0) {
|
|
|
|
truncate_nl(s4);
|
|
|
|
if (safe_atou(s4, &val4) >= 0 &&
|
|
|
|
val4 == 2147483647U)
|
|
|
|
/* This is the default "no limit" value in the kernel */
|
|
|
|
val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sysctl_read("net/ipv6/route/max_size", &s6) >= 0) {
|
|
|
|
truncate_nl(s6);
|
|
|
|
(void) safe_atou(s6, &val6);
|
|
|
|
}
|
|
|
|
|
|
|
|
cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
|
|
|
|
MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
|
|
|
|
return cached;
|
|
|
|
}
|
2016-06-03 19:14:12 +02:00
|
|
|
|
2015-10-09 23:43:52 +02:00
|
|
|
int route_new(Route **ret) {
|
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
|
|
|
_cleanup_(route_freep) Route *route = NULL;
|
2015-09-21 15:53:40 +02:00
|
|
|
|
2018-11-12 06:55:52 +01:00
|
|
|
route = new(Route, 1);
|
2015-09-21 15:53:40 +02:00
|
|
|
if (!route)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-11-12 06:55:52 +01:00
|
|
|
*route = (Route) {
|
|
|
|
.family = AF_UNSPEC,
|
|
|
|
.scope = RT_SCOPE_UNIVERSE,
|
|
|
|
.protocol = RTPROT_UNSPEC,
|
|
|
|
.type = RTN_UNICAST,
|
|
|
|
.table = RT_TABLE_MAIN,
|
|
|
|
.lifetime = USEC_INFINITY,
|
|
|
|
.quickack = -1,
|
2019-05-13 13:15:33 +02:00
|
|
|
.fast_open_no_cookie = -1,
|
2019-02-28 02:10:38 +01:00
|
|
|
.gateway_onlink = -1,
|
2019-05-16 11:49:08 +02:00
|
|
|
.ttl_propagate = -1,
|
2018-11-12 06:55:52 +01:00
|
|
|
};
|
2015-09-21 15:53:40 +02:00
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(route);
|
2015-09-21 15:53:40 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-01 05:19:53 +01:00
|
|
|
static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
|
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
|
|
|
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
|
|
|
_cleanup_(route_freep) Route *route = NULL;
|
2015-09-21 15:53:40 +02:00
|
|
|
int r;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2016-06-03 19:14:12 +02:00
|
|
|
assert(network);
|
|
|
|
assert(ret);
|
2017-02-17 15:34:25 +01:00
|
|
|
assert(!!filename == (section_line > 0));
|
2016-06-03 19:14:12 +02:00
|
|
|
|
2017-02-17 15:34:25 +01:00
|
|
|
if (filename) {
|
2017-02-15 05:30:35 +01:00
|
|
|
r = network_config_section_new(filename, section_line, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
route = hashmap_get(network->routes_by_section, n);
|
2013-11-19 16:54:42 +01:00
|
|
|
if (route) {
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(route);
|
2013-11-19 16:54:42 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-21 15:06:28 +02:00
|
|
|
if (network->n_static_routes >= routes_max())
|
2016-06-03 19:14:12 +02:00
|
|
|
return -E2BIG;
|
|
|
|
|
2015-10-09 23:43:52 +02:00
|
|
|
r = route_new(&route);
|
2015-09-21 15:53:40 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-01-28 20:00:47 +01:00
|
|
|
|
2015-10-09 23:43:52 +02:00
|
|
|
route->protocol = RTPROT_STATIC;
|
2018-11-12 06:57:04 +01:00
|
|
|
route->network = network;
|
|
|
|
LIST_PREPEND(routes, network->static_routes, route);
|
|
|
|
network->n_static_routes++;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2017-02-17 15:34:25 +01:00
|
|
|
if (filename) {
|
2018-04-05 07:26:26 +02:00
|
|
|
route->section = TAKE_PTR(n);
|
2016-04-25 09:07:10 +02:00
|
|
|
|
2018-11-12 07:24:11 +01:00
|
|
|
r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-02-22 06:25:03 +01:00
|
|
|
r = hashmap_put(network->routes_by_section, route->section, route);
|
2016-02-24 21:37:42 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-11-19 16:54:42 +01:00
|
|
|
}
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(route);
|
2013-10-17 03:18:36 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void route_free(Route *route) {
|
|
|
|
if (!route)
|
|
|
|
return;
|
|
|
|
|
2014-01-01 15:16:10 +01:00
|
|
|
if (route->network) {
|
2014-05-15 20:10:33 +02:00
|
|
|
LIST_REMOVE(routes, route->network->static_routes, route);
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2016-06-03 19:14:12 +02:00
|
|
|
assert(route->network->n_static_routes > 0);
|
|
|
|
route->network->n_static_routes--;
|
|
|
|
|
2017-02-17 15:26:10 +01:00
|
|
|
if (route->section)
|
2017-02-15 05:30:35 +01:00
|
|
|
hashmap_remove(route->network->routes_by_section, route->section);
|
2014-01-01 15:16:10 +01:00
|
|
|
}
|
2013-11-19 16:54:42 +01:00
|
|
|
|
2017-02-17 15:26:10 +01:00
|
|
|
network_config_section_free(route->section);
|
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
if (route->link) {
|
|
|
|
set_remove(route->link->routes, route);
|
|
|
|
set_remove(route->link->routes_foreign, route);
|
|
|
|
}
|
|
|
|
|
2019-11-28 16:36:02 +01:00
|
|
|
ordered_set_free_free(route->multipath_routes);
|
|
|
|
|
2015-10-26 12:29:37 +01:00
|
|
|
sd_event_source_unref(route->expire);
|
|
|
|
|
2013-10-17 03:18:36 +02:00
|
|
|
free(route);
|
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
static void route_hash_func(const Route *route, struct siphash *state) {
|
2015-10-03 18:40:28 +02:00
|
|
|
assert(route);
|
|
|
|
|
|
|
|
siphash24_compress(&route->family, sizeof(route->family), state);
|
|
|
|
|
2019-07-16 18:47:52 +02:00
|
|
|
switch (route->family) {
|
|
|
|
case AF_INET:
|
|
|
|
case AF_INET6:
|
|
|
|
siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
|
2019-09-17 19:11:31 +02:00
|
|
|
siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
|
|
|
|
|
2019-07-16 18:47:52 +02:00
|
|
|
siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
|
2019-09-17 19:11:31 +02:00
|
|
|
siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
|
|
|
|
|
|
|
|
siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
|
|
|
|
|
2019-07-16 18:47:52 +02:00
|
|
|
siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
|
|
|
|
|
|
|
|
siphash24_compress(&route->tos, sizeof(route->tos), state);
|
|
|
|
siphash24_compress(&route->priority, sizeof(route->priority), state);
|
|
|
|
siphash24_compress(&route->table, sizeof(route->table), state);
|
|
|
|
siphash24_compress(&route->protocol, sizeof(route->protocol), state);
|
|
|
|
siphash24_compress(&route->scope, sizeof(route->scope), state);
|
|
|
|
siphash24_compress(&route->type, sizeof(route->type), state);
|
2019-09-17 19:11:31 +02:00
|
|
|
|
2019-09-09 17:18:05 +02:00
|
|
|
siphash24_compress(&route->initcwnd, sizeof(route->initcwnd), state);
|
|
|
|
siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
|
2019-07-16 18:47:52 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* treat any other address family as AF_UNSPEC */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-09 17:17:28 +02:00
|
|
|
static int route_compare_func(const Route *a, const Route *b) {
|
2019-07-16 18:47:52 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
r = CMP(a->family, b->family);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
switch (a->family) {
|
|
|
|
case AF_INET:
|
|
|
|
case AF_INET6:
|
|
|
|
r = CMP(a->dst_prefixlen, b->dst_prefixlen);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-07-16 18:47:52 +02:00
|
|
|
r = CMP(a->src_prefixlen, b->src_prefixlen);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = CMP(a->tos, b->tos);
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = CMP(a->priority, b->priority);
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = CMP(a->table, b->table);
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = CMP(a->protocol, b->protocol);
|
2019-09-09 17:18:05 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = CMP(a->scope, b->scope);
|
2019-09-09 17:18:05 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = CMP(a->type, b->type);
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = CMP(a->initcwnd, b->initcwnd);
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
r = CMP(a->initrwnd, b->initrwnd);
|
2019-07-16 18:47:52 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-17 19:11:31 +02:00
|
|
|
return 0;
|
2019-07-16 18:47:52 +02:00
|
|
|
default:
|
|
|
|
/* treat any other address family as AF_UNSPEC */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
2019-09-09 17:17:28 +02:00
|
|
|
route_hash_ops,
|
2019-07-16 18:47:52 +02:00
|
|
|
Route,
|
2019-09-09 17:17:28 +02:00
|
|
|
route_hash_func,
|
|
|
|
route_compare_func,
|
2019-07-16 18:47:52 +02:00
|
|
|
route_free);
|
|
|
|
|
2018-11-06 13:28:12 +01:00
|
|
|
bool route_equal(Route *r1, Route *r2) {
|
|
|
|
if (r1 == r2)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!r1 || !r2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return route_compare_func(r1, r2) == 0;
|
|
|
|
}
|
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
int route_get(Link *link, Route *in, Route **ret) {
|
|
|
|
|
|
|
|
Route *existing;
|
2016-06-03 19:51:48 +02:00
|
|
|
|
|
|
|
assert(link);
|
2019-09-09 17:16:41 +02:00
|
|
|
assert(in);
|
2015-10-25 14:46:21 +01:00
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
existing = set_get(link->routes, in);
|
2015-10-25 14:46:21 +01:00
|
|
|
if (existing) {
|
2016-06-03 19:51:48 +02:00
|
|
|
if (ret)
|
|
|
|
*ret = existing;
|
2015-10-25 14:46:21 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
existing = set_get(link->routes_foreign, in);
|
2016-06-03 19:51:48 +02:00
|
|
|
if (existing) {
|
|
|
|
if (ret)
|
|
|
|
*ret = existing;
|
|
|
|
return 0;
|
|
|
|
}
|
2015-10-25 14:46:21 +01:00
|
|
|
|
2016-06-03 19:51:48 +02:00
|
|
|
return -ENOENT;
|
2015-10-25 14:46:21 +01:00
|
|
|
}
|
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) {
|
2016-06-03 19:54:35 +02:00
|
|
|
|
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
|
|
|
_cleanup_(route_freep) Route *route = NULL;
|
2015-10-25 14:46:21 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(link);
|
|
|
|
assert(routes);
|
2019-09-09 17:16:41 +02:00
|
|
|
assert(in);
|
2015-10-25 14:46:21 +01:00
|
|
|
|
|
|
|
r = route_new(&route);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
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 = 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;
|
2015-10-25 14:46:21 +01:00
|
|
|
|
|
|
|
r = set_ensure_allocated(routes, &route_hash_ops);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = set_put(*routes, route);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2019-08-11 01:56:37 +02:00
|
|
|
if (r == 0)
|
|
|
|
return -EEXIST;
|
2015-10-25 14:46:21 +01:00
|
|
|
|
|
|
|
route->link = link;
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
*ret = route;
|
|
|
|
|
|
|
|
route = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
int route_add_foreign(Link *link, Route *in, Route **ret) {
|
|
|
|
return route_add_internal(link, &link->routes_foreign, in, ret);
|
2015-10-25 14:46:21 +01:00
|
|
|
}
|
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
int route_add(Link *link, Route *in, Route **ret) {
|
2016-06-03 19:54:35 +02:00
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
Route *route;
|
|
|
|
int r;
|
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
r = route_get(link, in, &route);
|
2015-10-25 14:46:21 +01:00
|
|
|
if (r == -ENOENT) {
|
|
|
|
/* Route does not exist, create a new one */
|
2019-09-09 17:16:41 +02:00
|
|
|
r = route_add_internal(link, &link->routes, in, &route);
|
2015-10-25 14:46:21 +01:00
|
|
|
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);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
set_remove(link->routes_foreign, route);
|
|
|
|
} else if (r == 1) {
|
|
|
|
/* Route exists, do nothing */
|
|
|
|
;
|
|
|
|
} else
|
|
|
|
return r;
|
|
|
|
|
2016-08-16 11:07:42 +02:00
|
|
|
if (ret)
|
|
|
|
*ret = route;
|
2015-10-25 14:46:21 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-28 21:06:52 +01:00
|
|
|
static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
2018-11-28 21:22:42 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(link);
|
|
|
|
assert(link->ifname);
|
|
|
|
|
|
|
|
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
r = sd_netlink_message_get_errno(m);
|
|
|
|
if (r < 0 && r != -ESRCH)
|
2019-11-30 07:54:07 +01:00
|
|
|
log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
|
2018-11-28 21:22:42 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-09-24 15:25:20 +02:00
|
|
|
int route_remove(Route *route, Link *link,
|
2018-11-28 21:06:52 +01:00
|
|
|
link_netlink_message_handler_t callback) {
|
2018-05-11 05:36:15 +02:00
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
2014-02-28 16:10:20 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(link);
|
|
|
|
assert(link->manager);
|
|
|
|
assert(link->manager->rtnl);
|
|
|
|
assert(link->ifindex > 0);
|
2017-10-04 16:01:32 +02:00
|
|
|
assert(IN_SET(route->family, AF_INET, AF_INET6));
|
2014-02-28 16:10:20 +01:00
|
|
|
|
|
|
|
r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
|
2014-07-22 23:54:47 +02:00
|
|
|
RTM_DELROUTE, route->family,
|
|
|
|
route->protocol);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2019-07-07 14:50:05 +02:00
|
|
|
if (DEBUG_LOGGING) {
|
|
|
|
_cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
|
2019-07-09 17:18:53 +02:00
|
|
|
char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
|
2019-07-07 14:50:05 +02:00
|
|
|
|
|
|
|
if (!in_addr_is_null(route->family, &route->dst)) {
|
|
|
|
(void) in_addr_to_string(route->family, &route->dst, &dst);
|
|
|
|
(void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
|
|
|
|
}
|
|
|
|
if (!in_addr_is_null(route->family, &route->src))
|
|
|
|
(void) in_addr_to_string(route->family, &route->src, &src);
|
|
|
|
if (!in_addr_is_null(route->family, &route->gw))
|
|
|
|
(void) in_addr_to_string(route->family, &route->gw, &gw);
|
|
|
|
if (!in_addr_is_null(route->family, &route->prefsrc))
|
|
|
|
(void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
|
|
|
|
|
2019-07-09 17:18:53 +02:00
|
|
|
log_link_debug(link, "Removing route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
|
2019-07-07 14:50:05 +02:00
|
|
|
strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
|
|
|
|
format_route_scope(route->scope, scope, sizeof(scope)),
|
|
|
|
format_route_table(route->table, table, sizeof(table)),
|
2019-07-09 17:18:53 +02:00
|
|
|
format_route_protocol(route->protocol, protocol, sizeof(protocol)),
|
2019-07-07 14:50:05 +02:00
|
|
|
strna(route_type_to_string(route->type)));
|
|
|
|
}
|
|
|
|
|
2019-02-03 00:09:13 +01:00
|
|
|
if (in_addr_is_null(route->family, &route->gw) == 0) {
|
2019-02-03 01:47:25 +01:00
|
|
|
r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (route->dst_prefixlen) {
|
2019-02-03 01:47:25 +01:00
|
|
|
r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
|
|
|
r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
}
|
|
|
|
|
2014-12-04 15:52:21 +01:00
|
|
|
if (route->src_prefixlen) {
|
2019-02-03 01:47:25 +01:00
|
|
|
r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
|
2014-12-04 15:52:21 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
|
2014-12-04 15:52:21 +01:00
|
|
|
|
|
|
|
r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set source prefix length: %m");
|
2014-12-04 15:52:21 +01:00
|
|
|
}
|
|
|
|
|
2019-02-03 00:09:13 +01:00
|
|
|
if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
|
2019-02-03 01:47:25 +01:00
|
|
|
r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
|
2014-09-05 11:56:02 +02:00
|
|
|
}
|
|
|
|
|
2014-02-28 16:10:20 +01:00
|
|
|
r = sd_rtnl_message_route_set_scope(req, route->scope);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set scope: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2015-10-26 17:04:57 +01:00
|
|
|
r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2018-10-13 08:01:47 +02:00
|
|
|
if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
|
2017-09-19 16:28:26 +02:00
|
|
|
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
|
2017-09-19 16:28:26 +02:00
|
|
|
}
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2018-11-28 21:06:52 +01:00
|
|
|
r = netlink_call_async(link->manager->rtnl, NULL, req,
|
|
|
|
callback ?: route_remove_handler,
|
|
|
|
link_netlink_destroy_callback, link);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2014-07-03 09:43:31 +02:00
|
|
|
link_ref(link);
|
|
|
|
|
2014-02-28 16:10:20 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-26 12:29:37 +01:00
|
|
|
int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
|
|
|
|
Route *route = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(route);
|
|
|
|
|
2018-11-28 21:22:42 +01:00
|
|
|
r = route_remove(route, route->link, NULL);
|
2015-10-26 12:29:37 +01:00
|
|
|
if (r < 0)
|
2020-01-07 04:53:19 +01:00
|
|
|
log_link_warning_errno(route->link, r, "Could not remove route: %m");
|
2016-08-18 09:46:21 +02:00
|
|
|
else
|
2016-05-18 03:36:43 +02:00
|
|
|
route_free(route);
|
2015-10-26 12:29:37 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-11-28 16:36:02 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-06-03 19:51:48 +02:00
|
|
|
int route_configure(
|
|
|
|
Route *route,
|
|
|
|
Link *link,
|
2018-11-28 21:06:52 +01:00
|
|
|
link_netlink_message_handler_t callback) {
|
2016-06-03 19:51:48 +02:00
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
|
|
|
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
|
2013-10-17 03:18:36 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(link);
|
2013-11-14 16:22:51 +01:00
|
|
|
assert(link->manager);
|
|
|
|
assert(link->manager->rtnl);
|
2013-10-17 03:18:36 +02:00
|
|
|
assert(link->ifindex > 0);
|
2017-10-04 16:01:32 +02:00
|
|
|
assert(IN_SET(route->family, AF_INET, AF_INET6));
|
2018-11-28 21:29:09 +01:00
|
|
|
assert(callback);
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
if (route_get(link, route, NULL) <= 0 &&
|
2016-08-21 15:06:28 +02:00
|
|
|
set_size(link->routes) >= routes_max())
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
|
|
|
|
"Too many routes are configured, refusing: %m");
|
2016-06-03 19:51:48 +02:00
|
|
|
|
2019-02-11 13:28:13 +01:00
|
|
|
if (DEBUG_LOGGING) {
|
|
|
|
_cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
|
2019-07-09 17:18:53 +02:00
|
|
|
char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
|
2019-02-11 13:28:13 +01:00
|
|
|
|
|
|
|
if (!in_addr_is_null(route->family, &route->dst)) {
|
|
|
|
(void) in_addr_to_string(route->family, &route->dst, &dst);
|
|
|
|
(void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
|
|
|
|
}
|
|
|
|
if (!in_addr_is_null(route->family, &route->src))
|
|
|
|
(void) in_addr_to_string(route->family, &route->src, &src);
|
|
|
|
if (!in_addr_is_null(route->family, &route->gw))
|
|
|
|
(void) in_addr_to_string(route->family, &route->gw, &gw);
|
|
|
|
if (!in_addr_is_null(route->family, &route->prefsrc))
|
|
|
|
(void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
|
|
|
|
|
2019-07-09 17:18:53 +02:00
|
|
|
log_link_debug(link, "Configuring route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
|
2019-07-07 14:50:05 +02:00
|
|
|
strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
|
|
|
|
format_route_scope(route->scope, scope, sizeof(scope)),
|
|
|
|
format_route_table(route->table, table, sizeof(table)),
|
2019-07-09 17:18:53 +02:00
|
|
|
format_route_protocol(route->protocol, protocol, sizeof(protocol)),
|
2019-07-07 14:50:05 +02:00
|
|
|
strna(route_type_to_string(route->type)));
|
2019-02-11 13:28:13 +01:00
|
|
|
}
|
|
|
|
|
2014-02-19 23:54:58 +01:00
|
|
|
r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
|
2014-07-22 23:54:47 +02:00
|
|
|
RTM_NEWROUTE, route->family,
|
|
|
|
route->protocol);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2019-02-03 00:09:13 +01:00
|
|
|
if (in_addr_is_null(route->family, &route->gw) == 0) {
|
2019-02-03 01:47:25 +01:00
|
|
|
r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
|
2016-05-03 19:48:21 +02:00
|
|
|
|
|
|
|
r = sd_rtnl_message_route_set_family(req, route->family);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set route family: %m");
|
2013-10-17 03:18:36 +02:00
|
|
|
}
|
|
|
|
|
2019-08-29 01:13:15 +02:00
|
|
|
if (route->dst_prefixlen > 0) {
|
2019-02-03 01:47:25 +01:00
|
|
|
r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
|
2013-11-19 16:54:42 +01:00
|
|
|
|
2013-12-07 23:03:19 +01:00
|
|
|
r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
|
2013-12-07 21:18:44 +01:00
|
|
|
}
|
|
|
|
|
2019-08-29 01:13:15 +02:00
|
|
|
if (route->src_prefixlen > 0) {
|
2019-02-03 01:47:25 +01:00
|
|
|
r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
|
2014-12-04 15:52:21 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
|
2014-12-04 15:52:21 +01:00
|
|
|
|
|
|
|
r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set source prefix length: %m");
|
2014-12-04 15:52:21 +01:00
|
|
|
}
|
|
|
|
|
2019-02-03 00:09:13 +01:00
|
|
|
if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
|
2019-02-03 01:47:25 +01:00
|
|
|
r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
|
2014-09-05 11:56:02 +02:00
|
|
|
}
|
|
|
|
|
2014-02-28 16:10:20 +01:00
|
|
|
r = sd_rtnl_message_route_set_scope(req, route->scope);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set scope: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2019-02-28 02:10:38 +01:00
|
|
|
if (route->gateway_onlink >= 0)
|
|
|
|
SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
|
|
|
|
|
networkd: ndisc - handle router advertisement in userspace
Router Discovery is a core part of IPv6, which by default is handled by the kernel.
However, the kernel implementation is meant as a fall-back, and to fully support
the protocol a userspace implementation is desired.
The protocol essentially listens for Router Advertisement packets from routers
on the local link and use these to configure the client automatically. The four
main pieces of information are: what kind (if any) of DHCPv6 configuration should
be performed; a default gateway; the prefixes that should be considered to be on
the local link; and the prefixes with which we can preform SLAAC in order to pick
a global IPv6 address.
A lot of additional information is also available, which we do not yet fully
support, but which will eventually allow us to avoid the need for DHCPv6 in the
common case.
Short-term, the reason for wanting this is in userspace was the desire to fully
track all the addresses on links we manage, and that is not possible for addresses
managed by the kernel (as the kernel does not expose to us the fact that it
manages these addresses). Moreover, we would like to support stable privacy
addresses, which will soon be mandated and the legacy MAC-based global addresses
deprecated, to do this well we need to handle the generation in userspace. Lastly,
more long-term we wish to support more RA options than what the kernel exposes.
2015-11-03 13:02:16 +01:00
|
|
|
r = sd_rtnl_message_route_set_flags(req, route->flags);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set flags: %m");
|
2016-05-03 19:48:21 +02:00
|
|
|
|
2016-08-18 09:54:12 +02:00
|
|
|
if (route->table != RT_TABLE_MAIN) {
|
2016-05-03 19:48:21 +02:00
|
|
|
if (route->table < 256) {
|
|
|
|
r = sd_rtnl_message_route_set_table(req, route->table);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set route table: %m");
|
2016-05-03 19:48:21 +02:00
|
|
|
} else {
|
|
|
|
r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set route table: %m");
|
2016-05-03 19:48:21 +02:00
|
|
|
|
2016-05-06 06:19:49 +02:00
|
|
|
/* Table attribute to allow more than 256. */
|
2016-05-03 19:48:21 +02:00
|
|
|
r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m");
|
2016-05-03 19:48:21 +02:00
|
|
|
}
|
|
|
|
}
|
networkd: ndisc - handle router advertisement in userspace
Router Discovery is a core part of IPv6, which by default is handled by the kernel.
However, the kernel implementation is meant as a fall-back, and to fully support
the protocol a userspace implementation is desired.
The protocol essentially listens for Router Advertisement packets from routers
on the local link and use these to configure the client automatically. The four
main pieces of information are: what kind (if any) of DHCPv6 configuration should
be performed; a default gateway; the prefixes that should be considered to be on
the local link; and the prefixes with which we can preform SLAAC in order to pick
a global IPv6 address.
A lot of additional information is also available, which we do not yet fully
support, but which will eventually allow us to avoid the need for DHCPv6 in the
common case.
Short-term, the reason for wanting this is in userspace was the desire to fully
track all the addresses on links we manage, and that is not possible for addresses
managed by the kernel (as the kernel does not expose to us the fact that it
manages these addresses). Moreover, we would like to support stable privacy
addresses, which will soon be mandated and the legacy MAC-based global addresses
deprecated, to do this well we need to handle the generation in userspace. Lastly,
more long-term we wish to support more RA options than what the kernel exposes.
2015-11-03 13:02:16 +01:00
|
|
|
|
2015-10-26 17:04:57 +01:00
|
|
|
r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
networkd: ndisc - handle router advertisement in userspace
Router Discovery is a core part of IPv6, which by default is handled by the kernel.
However, the kernel implementation is meant as a fall-back, and to fully support
the protocol a userspace implementation is desired.
The protocol essentially listens for Router Advertisement packets from routers
on the local link and use these to configure the client automatically. The four
main pieces of information are: what kind (if any) of DHCPv6 configuration should
be performed; a default gateway; the prefixes that should be considered to be on
the local link; and the prefixes with which we can preform SLAAC in order to pick
a global IPv6 address.
A lot of additional information is also available, which we do not yet fully
support, but which will eventually allow us to avoid the need for DHCPv6 in the
common case.
Short-term, the reason for wanting this is in userspace was the desire to fully
track all the addresses on links we manage, and that is not possible for addresses
managed by the kernel (as the kernel does not expose to us the fact that it
manages these addresses). Moreover, we would like to support stable privacy
addresses, which will soon be mandated and the legacy MAC-based global addresses
deprecated, to do this well we need to handle the generation in userspace. Lastly,
more long-term we wish to support more RA options than what the kernel exposes.
2015-11-03 13:02:16 +01:00
|
|
|
r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
|
networkd: ndisc - handle router advertisement in userspace
Router Discovery is a core part of IPv6, which by default is handled by the kernel.
However, the kernel implementation is meant as a fall-back, and to fully support
the protocol a userspace implementation is desired.
The protocol essentially listens for Router Advertisement packets from routers
on the local link and use these to configure the client automatically. The four
main pieces of information are: what kind (if any) of DHCPv6 configuration should
be performed; a default gateway; the prefixes that should be considered to be on
the local link; and the prefixes with which we can preform SLAAC in order to pick
a global IPv6 address.
A lot of additional information is also available, which we do not yet fully
support, but which will eventually allow us to avoid the need for DHCPv6 in the
common case.
Short-term, the reason for wanting this is in userspace was the desire to fully
track all the addresses on links we manage, and that is not possible for addresses
managed by the kernel (as the kernel does not expose to us the fact that it
manages these addresses). Moreover, we would like to support stable privacy
addresses, which will soon be mandated and the legacy MAC-based global addresses
deprecated, to do this well we need to handle the generation in userspace. Lastly,
more long-term we wish to support more RA options than what the kernel exposes.
2015-11-03 13:02:16 +01:00
|
|
|
|
2018-02-26 15:33:16 +01:00
|
|
|
if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
|
|
|
|
r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
|
|
|
|
DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC));
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m");
|
2018-02-26 15:33:16 +01:00
|
|
|
}
|
|
|
|
|
2017-09-19 16:28:26 +02:00
|
|
|
r = sd_rtnl_message_route_set_type(req, route->type);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not set route type: %m");
|
2017-09-19 16:28:26 +02:00
|
|
|
|
2018-10-13 08:01:47 +02:00
|
|
|
if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
|
2017-09-19 16:28:26 +02:00
|
|
|
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
|
2017-09-19 16:28:26 +02:00
|
|
|
}
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2019-05-16 11:49:08 +02:00
|
|
|
if (route->ttl_propagate >= 0) {
|
|
|
|
r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
|
|
|
|
if (r < 0)
|
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
|
|
|
|
}
|
|
|
|
|
2016-11-23 22:32:19 +01:00
|
|
|
r = sd_netlink_message_open_container(req, RTA_METRICS);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
|
2016-11-23 22:32:19 +01:00
|
|
|
|
|
|
|
if (route->mtu > 0) {
|
|
|
|
r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m");
|
2016-11-23 22:32:19 +01:00
|
|
|
}
|
|
|
|
|
2018-01-02 10:20:15 +01:00
|
|
|
if (route->initcwnd > 0) {
|
2017-12-29 15:18:05 +01:00
|
|
|
r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m");
|
2017-12-29 15:18:05 +01:00
|
|
|
}
|
|
|
|
|
2018-01-02 10:20:15 +01:00
|
|
|
if (route->initrwnd > 0) {
|
2017-12-29 15:18:05 +01:00
|
|
|
r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m");
|
2017-12-29 15:18:05 +01:00
|
|
|
}
|
|
|
|
|
2019-05-14 04:39:54 +02:00
|
|
|
if (route->quickack >= 0) {
|
2018-01-20 00:49:15 +01:00
|
|
|
r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m");
|
2018-01-20 00:49:15 +01:00
|
|
|
}
|
|
|
|
|
2019-05-13 13:15:33 +02:00
|
|
|
if (route->fast_open_no_cookie >= 0) {
|
|
|
|
r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
|
|
|
|
if (r < 0)
|
|
|
|
return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m");
|
|
|
|
}
|
|
|
|
|
2016-11-23 22:32:19 +01:00
|
|
|
r = sd_netlink_message_close_container(req);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
|
2016-11-23 22:32:19 +01:00
|
|
|
|
2019-11-28 16:36:02 +01:00
|
|
|
r = append_nexthops(route, req);
|
|
|
|
if (r < 0)
|
|
|
|
return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
|
|
|
|
|
2018-11-28 21:06:52 +01:00
|
|
|
r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
|
|
|
|
link_netlink_destroy_callback, link);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2014-07-03 09:43:31 +02:00
|
|
|
link_ref(link);
|
|
|
|
|
2019-09-09 17:16:41 +02:00
|
|
|
r = route_add(link, route, &route);
|
2015-10-25 14:46:21 +01:00
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not add route: %m");
|
2015-10-25 14:46:21 +01:00
|
|
|
|
2015-10-26 12:29:37 +01:00
|
|
|
/* TODO: drop expiration handling once it can be pushed into the kernel */
|
2018-02-26 15:33:16 +01:00
|
|
|
if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
|
2015-10-26 12:29:37 +01:00
|
|
|
r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
|
|
|
|
route->lifetime, 0, route_expire_handler, route);
|
|
|
|
if (r < 0)
|
2019-03-11 06:30:45 +01:00
|
|
|
return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
|
2015-10-26 12:29:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sd_event_source_unref(route->expire);
|
2018-04-05 07:26:26 +02:00
|
|
|
route->expire = TAKE_PTR(expire);
|
2015-10-26 12:29:37 +01:00
|
|
|
|
2019-06-11 16:26:11 +02:00
|
|
|
return 1;
|
2013-10-17 03:18:36 +02:00
|
|
|
}
|
|
|
|
|
2019-03-05 02:51:57 +01:00
|
|
|
int network_add_ipv4ll_route(Network *network) {
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2019-03-05 02:51:57 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(network);
|
|
|
|
|
|
|
|
if (!network->ipv4ll_route)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* IPv4LLRoute= is in [Network] section. */
|
|
|
|
r = route_new_static(network, NULL, 0, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
n->family = AF_INET;
|
|
|
|
n->dst_prefixlen = 16;
|
|
|
|
n->scope = RT_SCOPE_LINK;
|
2019-07-07 02:40:17 +02:00
|
|
|
n->scope_set = true;
|
|
|
|
n->table_set = true;
|
2019-03-05 02:51:57 +01:00
|
|
|
n->priority = IPV4LL_ROUTE_METRIC;
|
|
|
|
n->protocol = RTPROT_STATIC;
|
|
|
|
|
|
|
|
TAKE_PTR(n);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-05-14 09:43:14 +02:00
|
|
|
int network_add_default_route_on_device(Network *network) {
|
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(network);
|
|
|
|
|
|
|
|
if (!network->default_route_on_device)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* DefaultRouteOnDevice= is in [Network] section. */
|
|
|
|
r = route_new_static(network, NULL, 0, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
n->family = AF_INET;
|
2019-08-29 13:24:27 +02:00
|
|
|
n->scope = RT_SCOPE_LINK;
|
|
|
|
n->scope_set = true;
|
|
|
|
n->protocol = RTPROT_STATIC;
|
2019-05-14 09:43:14 +02:00
|
|
|
|
|
|
|
TAKE_PTR(n);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-07 01:43:33 +02:00
|
|
|
static const char * const route_type_table[__RTN_MAX] = {
|
|
|
|
[RTN_UNICAST] = "unicast",
|
2019-07-07 02:40:17 +02:00
|
|
|
[RTN_LOCAL] = "local",
|
|
|
|
[RTN_BROADCAST] = "broadcast",
|
|
|
|
[RTN_ANYCAST] = "anycast",
|
|
|
|
[RTN_MULTICAST] = "multicast",
|
2019-07-07 01:43:33 +02:00
|
|
|
[RTN_BLACKHOLE] = "blackhole",
|
|
|
|
[RTN_UNREACHABLE] = "unreachable",
|
|
|
|
[RTN_PROHIBIT] = "prohibit",
|
|
|
|
[RTN_THROW] = "throw",
|
2019-07-07 02:40:17 +02:00
|
|
|
[RTN_NAT] = "nat",
|
|
|
|
[RTN_XRESOLVE] = "xresolve",
|
2019-07-07 01:43:33 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
assert_cc(__RTN_MAX <= UCHAR_MAX);
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(route_type, int);
|
|
|
|
|
2019-07-07 14:50:05 +02:00
|
|
|
static const char * const route_scope_table[] = {
|
|
|
|
[RT_SCOPE_UNIVERSE] = "global",
|
|
|
|
[RT_SCOPE_SITE] = "site",
|
|
|
|
[RT_SCOPE_LINK] = "link",
|
|
|
|
[RT_SCOPE_HOST] = "host",
|
|
|
|
[RT_SCOPE_NOWHERE] = "nowhere",
|
|
|
|
};
|
|
|
|
|
2019-07-08 13:19:28 +02:00
|
|
|
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int);
|
2019-07-07 14:50:05 +02:00
|
|
|
|
|
|
|
const char *format_route_scope(int scope, char *buf, size_t size) {
|
|
|
|
const char *s;
|
|
|
|
char *p = buf;
|
|
|
|
|
|
|
|
s = route_scope_to_string(scope);
|
|
|
|
if (s)
|
|
|
|
strpcpy(&p, size, s);
|
|
|
|
else
|
|
|
|
strpcpyf(&p, size, "%d", scope);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * const route_table_table[] = {
|
|
|
|
[RT_TABLE_DEFAULT] = "default",
|
|
|
|
[RT_TABLE_MAIN] = "main",
|
|
|
|
[RT_TABLE_LOCAL] = "local",
|
|
|
|
};
|
|
|
|
|
2019-07-08 13:19:28 +02:00
|
|
|
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
|
2019-07-07 14:50:05 +02:00
|
|
|
|
|
|
|
const char *format_route_table(int table, char *buf, size_t size) {
|
|
|
|
const char *s;
|
|
|
|
char *p = buf;
|
|
|
|
|
|
|
|
s = route_table_to_string(table);
|
|
|
|
if (s)
|
|
|
|
strpcpy(&p, size, s);
|
|
|
|
else
|
|
|
|
strpcpyf(&p, size, "%d", table);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2019-07-08 13:36:19 +02:00
|
|
|
static const char * const route_protocol_table[] = {
|
|
|
|
[RTPROT_KERNEL] = "kernel",
|
|
|
|
[RTPROT_BOOT] = "boot",
|
|
|
|
[RTPROT_STATIC] = "static",
|
|
|
|
};
|
|
|
|
|
2019-07-14 02:51:54 +02:00
|
|
|
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_protocol, int);
|
|
|
|
|
|
|
|
static const char * const route_protocol_full_table[] = {
|
|
|
|
[RTPROT_REDIRECT] = "redirect",
|
|
|
|
[RTPROT_KERNEL] = "kernel",
|
|
|
|
[RTPROT_BOOT] = "boot",
|
|
|
|
[RTPROT_STATIC] = "static",
|
|
|
|
[RTPROT_GATED] = "gated",
|
|
|
|
[RTPROT_RA] = "ra",
|
|
|
|
[RTPROT_MRT] = "mrt",
|
|
|
|
[RTPROT_ZEBRA] = "zebra",
|
|
|
|
[RTPROT_BIRD] = "bird",
|
|
|
|
[RTPROT_DNROUTED] = "dnrouted",
|
|
|
|
[RTPROT_XORP] = "xorp",
|
|
|
|
[RTPROT_NTK] = "ntk",
|
|
|
|
[RTPROT_DHCP] = "dhcp",
|
|
|
|
[RTPROT_MROUTED] = "mrouted",
|
|
|
|
[RTPROT_BABEL] = "babel",
|
|
|
|
[RTPROT_BGP] = "bgp",
|
|
|
|
[RTPROT_ISIS] = "isis",
|
|
|
|
[RTPROT_OSPF] = "ospf",
|
|
|
|
[RTPROT_RIP] = "rip",
|
|
|
|
[RTPROT_EIGRP] = "eigrp",
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(route_protocol_full, int);
|
2019-07-09 17:18:53 +02:00
|
|
|
|
|
|
|
const char *format_route_protocol(int protocol, char *buf, size_t size) {
|
|
|
|
const char *s;
|
|
|
|
char *p = buf;
|
|
|
|
|
2019-07-14 02:51:54 +02:00
|
|
|
s = route_protocol_full_to_string(protocol);
|
2019-07-09 17:18:53 +02:00
|
|
|
if (s)
|
|
|
|
strpcpy(&p, size, s);
|
|
|
|
else
|
|
|
|
strpcpyf(&p, size, "%d", protocol);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
2019-07-08 13:36:19 +02:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_gateway(
|
|
|
|
const char *unit,
|
2013-10-17 03:18:36 +02:00
|
|
|
const char *filename,
|
|
|
|
unsigned line,
|
|
|
|
const char *section,
|
2013-11-19 16:17:55 +01:00
|
|
|
unsigned section_line,
|
2013-10-17 03:18:36 +02:00
|
|
|
const char *lvalue,
|
|
|
|
int ltype,
|
|
|
|
const char *rvalue,
|
|
|
|
void *data,
|
|
|
|
void *userdata) {
|
2014-08-11 22:44:51 +02:00
|
|
|
|
2013-11-19 16:54:42 +01:00
|
|
|
Network *network = userdata;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2018-12-03 10:22:05 +01:00
|
|
|
int r;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
|
|
|
assert(filename);
|
2013-11-19 16:54:42 +01:00
|
|
|
assert(section);
|
2013-10-17 03:18:36 +02:00
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
2013-11-28 17:17:04 +01:00
|
|
|
if (streq(section, "Network")) {
|
|
|
|
/* we are not in an Route section, so treat
|
|
|
|
* this as the special '0' section */
|
2017-02-15 05:30:35 +01:00
|
|
|
r = route_new_static(network, NULL, 0, &n);
|
2020-01-07 06:43:09 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
} else {
|
2017-02-15 05:30:35 +01:00
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
2020-01-07 06:43:09 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2020-02-05 05:41:30 +01:00
|
|
|
if (streq(rvalue, "_dhcp")) {
|
2020-01-07 06:43:09 +01:00
|
|
|
n->gateway_from_dhcp = true;
|
|
|
|
TAKE_PTR(n);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2019-02-28 02:37:58 +01:00
|
|
|
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);
|
2013-10-17 03:18:36 +02:00
|
|
|
if (r < 0) {
|
2019-02-28 02:37:58 +01:00
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
|
|
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
|
2013-10-17 03:18:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2013-10-17 03:18:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2013-11-19 16:54:42 +01:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_preferred_src(
|
|
|
|
const char *unit,
|
2015-09-23 17:26:36 +02:00
|
|
|
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;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2018-12-03 10:22:05 +01:00
|
|
|
int r;
|
2015-09-23 17:26:36 +02:00
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
2017-02-15 05:30:35 +01:00
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
2015-09-23 17:26:36 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-02-28 02:37:58 +01:00
|
|
|
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);
|
2015-09-23 17:26:36 +02:00
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
|
2019-02-28 02:37:58 +01:00
|
|
|
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
|
2015-09-23 17:26:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2015-09-23 17:26:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_destination(
|
|
|
|
const char *unit,
|
2013-11-19 16:54:42 +01:00
|
|
|
const char *filename,
|
|
|
|
unsigned line,
|
|
|
|
const char *section,
|
|
|
|
unsigned section_line,
|
|
|
|
const char *lvalue,
|
|
|
|
int ltype,
|
|
|
|
const char *rvalue,
|
|
|
|
void *data,
|
|
|
|
void *userdata) {
|
2014-08-11 22:44:51 +02:00
|
|
|
|
2013-11-19 16:54:42 +01:00
|
|
|
Network *network = userdata;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2018-12-03 10:22:05 +01:00
|
|
|
union in_addr_union *buffer;
|
|
|
|
unsigned char *prefixlen;
|
2017-05-12 03:53:12 +02:00
|
|
|
int r;
|
2013-11-19 16:54:42 +01:00
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
2017-02-15 05:30:35 +01:00
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
2013-11-19 16:54:42 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-12-04 15:52:21 +01:00
|
|
|
if (streq(lvalue, "Destination")) {
|
2018-12-03 10:22:05 +01:00
|
|
|
buffer = &n->dst;
|
|
|
|
prefixlen = &n->dst_prefixlen;
|
2014-12-04 15:52:21 +01:00
|
|
|
} else if (streq(lvalue, "Source")) {
|
2018-12-03 10:22:05 +01:00
|
|
|
buffer = &n->src;
|
|
|
|
prefixlen = &n->src_prefixlen;
|
2014-12-04 15:52:21 +01:00
|
|
|
} else
|
|
|
|
assert_not_reached(lvalue);
|
|
|
|
|
2019-02-28 02:37:58 +01:00
|
|
|
if (n->family == AF_UNSPEC)
|
|
|
|
r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
|
|
|
|
else
|
|
|
|
r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
|
2018-12-03 10:22:05 +01:00
|
|
|
if (r < 0) {
|
2019-02-28 02:37:58 +01:00
|
|
|
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
|
|
|
|
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
|
2018-12-03 10:22:05 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2013-11-19 16:54:42 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-07-10 19:39:58 +02:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_route_priority(
|
|
|
|
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) {
|
|
|
|
|
2014-07-10 19:39:58 +02:00
|
|
|
Network *network = userdata;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2014-07-10 19:39:58 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
2017-02-15 05:30:35 +01:00
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
2014-07-10 19:39:58 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
r = safe_atou32(rvalue, &n->priority);
|
2016-06-13 15:57:17 +02:00
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
|
|
"Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-07-10 19:39:58 +02:00
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2014-07-10 19:39:58 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2015-02-09 16:22:34 +01:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_route_scope(
|
|
|
|
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) {
|
|
|
|
|
2015-02-09 16:22:34 +01:00
|
|
|
Network *network = userdata;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2015-02-09 16:22:34 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
2017-02-15 05:30:35 +01:00
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
2015-02-09 16:22:34 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-07-08 13:19:28 +02:00
|
|
|
r = route_scope_from_string(rvalue);
|
|
|
|
if (r < 0) {
|
2015-09-30 18:22:42 +02:00
|
|
|
log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
|
2015-02-09 16:22:34 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-08 13:19:28 +02:00
|
|
|
n->scope = r;
|
2019-07-07 02:40:17 +02:00
|
|
|
n->scope_set = true;
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2015-02-09 16:22:34 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2016-05-03 19:48:21 +02:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_route_table(
|
|
|
|
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) {
|
|
|
|
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2016-05-03 19:48:21 +02:00
|
|
|
Network *network = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
2017-02-15 05:30:35 +01:00
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
2016-05-03 19:48:21 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-07-08 13:19:28 +02:00
|
|
|
r = route_table_from_string(rvalue);
|
|
|
|
if (r >= 0)
|
|
|
|
n->table = r;
|
|
|
|
else {
|
|
|
|
r = safe_atou32(rvalue, &n->table);
|
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
|
|
"Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
2016-05-03 19:48:21 +02:00
|
|
|
}
|
|
|
|
|
2019-07-07 02:40:17 +02:00
|
|
|
n->table_set = true;
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2016-05-03 19:48:21 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2017-04-21 11:22:30 +02:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_gateway_onlink(
|
|
|
|
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) {
|
|
|
|
|
2017-04-21 11:22:30 +02:00
|
|
|
Network *network = userdata;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2017-04-21 11:22:30 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
r = parse_boolean(rvalue);
|
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
2019-02-27 09:43:08 +01:00
|
|
|
"Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
|
2017-04-21 11:22:30 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-02-28 02:10:38 +01:00
|
|
|
n->gateway_onlink = r;
|
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2017-04-25 09:32:59 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_ipv6_route_preference(
|
|
|
|
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) {
|
|
|
|
|
2017-04-25 09:32:59 +02:00
|
|
|
Network *network = userdata;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2017-04-25 09:32:59 +02:00
|
|
|
int r;
|
|
|
|
|
2017-04-25 16:29:39 +02:00
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-04-25 09:32:59 +02:00
|
|
|
if (streq(rvalue, "low"))
|
|
|
|
n->pref = ICMPV6_ROUTER_PREF_LOW;
|
|
|
|
else if (streq(rvalue, "medium"))
|
|
|
|
n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
|
|
|
|
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);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-04-21 11:22:30 +02:00
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2017-04-21 11:22:30 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2017-05-09 20:01:25 +02:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_route_protocol(
|
|
|
|
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) {
|
|
|
|
|
2017-05-09 20:01:25 +02:00
|
|
|
Network *network = userdata;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2017-05-09 20:01:25 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-07-08 13:36:19 +02:00
|
|
|
r = route_protocol_from_string(rvalue);
|
|
|
|
if (r >= 0)
|
|
|
|
n->protocol = r;
|
2017-05-09 20:01:25 +02:00
|
|
|
else {
|
|
|
|
r = safe_atou8(rvalue , &n->protocol);
|
|
|
|
if (r < 0) {
|
2019-02-28 02:39:14 +01:00
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
|
|
"Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
|
2017-05-09 20:01:25 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2017-05-09 20:01:25 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2017-09-19 16:28:26 +02:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_route_type(
|
|
|
|
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) {
|
|
|
|
|
2017-09-19 16:28:26 +02:00
|
|
|
Network *network = userdata;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2019-07-07 01:43:33 +02:00
|
|
|
int t, r;
|
2017-09-19 16:28:26 +02:00
|
|
|
|
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-07-07 01:43:33 +02:00
|
|
|
t = route_type_from_string(rvalue);
|
|
|
|
if (t < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, 0,
|
2019-02-28 02:39:14 +01:00
|
|
|
"Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
|
2017-09-19 16:28:26 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-07 01:43:33 +02:00
|
|
|
n->type = (unsigned char) t;
|
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2017-09-19 16:28:26 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2017-12-29 15:18:05 +01:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_tcp_window(
|
|
|
|
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) {
|
|
|
|
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2018-01-02 10:20:15 +01:00
|
|
|
Network *network = userdata;
|
|
|
|
uint64_t k;
|
2017-12-29 15:18:05 +01:00
|
|
|
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;
|
|
|
|
|
2018-01-02 10:20:15 +01:00
|
|
|
r = parse_size(rvalue, 1024, &k);
|
2019-02-28 02:39:14 +01:00
|
|
|
if (r < 0) {
|
2017-12-29 15:18:05 +01:00
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
2019-02-28 02:39:14 +01:00
|
|
|
"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,
|
|
|
|
"Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
|
2017-12-29 15:18:05 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (streq(lvalue, "InitialCongestionWindow"))
|
|
|
|
n->initcwnd = k;
|
|
|
|
else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
|
|
|
|
n->initrwnd = k;
|
2019-02-28 02:39:14 +01:00
|
|
|
else
|
|
|
|
assert_not_reached("Invalid TCP window type.");
|
2017-12-29 15:18:05 +01:00
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2017-12-29 15:18:05 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2018-01-20 00:49:15 +01:00
|
|
|
|
2018-05-11 05:36:15 +02:00
|
|
|
int config_parse_quickack(
|
|
|
|
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) {
|
|
|
|
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2018-01-20 00:49:15 +01:00
|
|
|
Network *network = userdata;
|
|
|
|
int k, 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;
|
|
|
|
|
|
|
|
k = parse_boolean(rvalue);
|
|
|
|
if (k < 0) {
|
2019-02-28 02:39:14 +01:00
|
|
|
log_syntax(unit, LOG_ERR, filename, line, k,
|
|
|
|
"Failed to parse TCP quickack, ignoring: %s", rvalue);
|
2018-01-20 00:49:15 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
n->quickack = !!k;
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2018-01-20 00:49:15 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2018-05-21 13:33:36 +02:00
|
|
|
|
2019-05-13 13:15:33 +02:00
|
|
|
int config_parse_fast_open_no_cookie(
|
|
|
|
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;
|
|
|
|
Network *network = userdata;
|
|
|
|
int k, 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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
n->fast_open_no_cookie = k;
|
|
|
|
TAKE_PTR(n);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-21 13:33:36 +02:00
|
|
|
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;
|
2019-03-01 05:27:47 +01:00
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
2018-05-21 13:33:36 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2018-06-03 04:18:23 +02:00
|
|
|
TAKE_PTR(n);
|
2018-05-21 13:33:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2019-03-01 05:27:47 +01:00
|
|
|
|
2019-05-16 11:49:08 +02:00
|
|
|
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) {
|
|
|
|
|
|
|
|
Network *network = userdata;
|
|
|
|
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
|
|
|
|
int r, k;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
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 TTLPropagate= value, ignoring: %s", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
n->ttl_propagate = k;
|
|
|
|
|
|
|
|
TAKE_PTR(n);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-28 16:36:02 +01:00
|
|
|
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) {
|
2020-01-11 11:21:01 +01:00
|
|
|
r = resolve_interface(NULL, dev);
|
2019-11-28 16:36:02 +01:00
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
|
|
"Invalid interface name or index, ignoring assignment: %s", dev);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-12-18 13:54:13 +01:00
|
|
|
m->ifindex = r;
|
2019-11-28 16:36:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-03-01 05:27:47 +01:00
|
|
|
int route_section_verify(Route *route, Network *network) {
|
|
|
|
if (section_is_invalid(route->section))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (route->family == AF_UNSPEC) {
|
|
|
|
assert(route->section);
|
|
|
|
|
|
|
|
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"%s: Route section without Gateway=, Destination=, Source=, "
|
|
|
|
"or PreferredSource= field configured. "
|
|
|
|
"Ignoring [Route] section from line %u.",
|
|
|
|
route->section->filename, route->section->line);
|
|
|
|
}
|
|
|
|
|
2020-02-26 07:42:32 +01:00
|
|
|
if (!route->table_set && network->vrf) {
|
|
|
|
route->table = VRF(network->vrf)->table;
|
|
|
|
route->table_set = true;
|
|
|
|
}
|
|
|
|
|
2019-11-26 04:41:54 +01:00
|
|
|
if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
|
|
|
|
route->table = RT_TABLE_LOCAL;
|
|
|
|
|
|
|
|
if (!route->scope_set && route->family != AF_INET6) {
|
|
|
|
if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
|
|
|
|
route->scope = RT_SCOPE_HOST;
|
|
|
|
else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
|
|
|
|
route->scope = RT_SCOPE_LINK;
|
2019-07-07 02:40:17 +02:00
|
|
|
}
|
|
|
|
|
2019-03-01 05:27:47 +01:00
|
|
|
if (network->n_static_addresses == 0 &&
|
|
|
|
in_addr_is_null(route->family, &route->gw) == 0 &&
|
|
|
|
route->gateway_onlink < 0) {
|
|
|
|
log_warning("%s: Gateway= without static address configured. "
|
|
|
|
"Enabling GatewayOnLink= option.",
|
|
|
|
network->filename);
|
|
|
|
route->gateway_onlink = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|