2013-10-17 03:18:36 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2013 Tom Gundersen <teg@jklm.no>
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
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"
|
2015-08-27 13:59:06 +02:00
|
|
|
#include "netlink-util.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"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2016-08-21 15:06:28 +02:00
|
|
|
#include "sysctl-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "util.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) {
|
2015-09-21 15:53:40 +02:00
|
|
|
_cleanup_route_free_ Route *route = NULL;
|
|
|
|
|
|
|
|
route = new0(Route, 1);
|
|
|
|
if (!route)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
route->family = AF_UNSPEC;
|
|
|
|
route->scope = RT_SCOPE_UNIVERSE;
|
2015-10-09 23:43:52 +02:00
|
|
|
route->protocol = RTPROT_UNSPEC;
|
2016-08-18 09:54:12 +02:00
|
|
|
route->table = RT_TABLE_MAIN;
|
2015-10-26 12:29:37 +01:00
|
|
|
route->lifetime = USEC_INFINITY;
|
2015-09-21 15:53:40 +02:00
|
|
|
|
|
|
|
*ret = route;
|
|
|
|
route = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-15 05:30:35 +01:00
|
|
|
int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
|
|
|
|
_cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
|
2013-10-17 03:18:36 +02:00
|
|
|
_cleanup_route_free_ 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) {
|
|
|
|
*ret = route;
|
|
|
|
route = NULL;
|
|
|
|
|
|
|
|
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;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2017-02-17 15:34:25 +01:00
|
|
|
if (filename) {
|
2017-02-15 05:30:35 +01:00
|
|
|
route->section = n;
|
2017-02-17 15:26:10 +01:00
|
|
|
n = NULL;
|
2016-04-25 09:07:10 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-02-24 21:37:42 +01:00
|
|
|
route->network = network;
|
2016-04-25 09:07:10 +02:00
|
|
|
LIST_PREPEND(routes, network->static_routes, route);
|
2016-06-03 19:14:12 +02:00
|
|
|
network->n_static_routes++;
|
2016-02-24 21:37:42 +01:00
|
|
|
|
2013-10-17 03:18:36 +02:00
|
|
|
*ret = route;
|
|
|
|
route = NULL;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-10-26 12:29:37 +01:00
|
|
|
sd_event_source_unref(route->expire);
|
|
|
|
|
2013-10-17 03:18:36 +02:00
|
|
|
free(route);
|
|
|
|
}
|
|
|
|
|
2015-10-03 18:40:28 +02:00
|
|
|
static void route_hash_func(const void *b, struct siphash *state) {
|
|
|
|
const Route *route = b;
|
|
|
|
|
|
|
|
assert(route);
|
|
|
|
|
|
|
|
siphash24_compress(&route->family, sizeof(route->family), state);
|
|
|
|
|
|
|
|
switch (route->family) {
|
|
|
|
case AF_INET:
|
|
|
|
case AF_INET6:
|
|
|
|
/* Equality of routes are given by the 4-touple
|
|
|
|
(dst_prefix,dst_prefixlen,tos,priority,table) */
|
2015-10-25 15:09:40 +01:00
|
|
|
siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
|
2015-10-03 18:40:28 +02:00
|
|
|
siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), 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);
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* treat any other address family as AF_UNSPEC */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int route_compare_func(const void *_a, const void *_b) {
|
|
|
|
const Route *a = _a, *b = _b;
|
|
|
|
|
|
|
|
if (a->family < b->family)
|
|
|
|
return -1;
|
|
|
|
if (a->family > b->family)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
switch (a->family) {
|
|
|
|
case AF_INET:
|
|
|
|
case AF_INET6:
|
|
|
|
if (a->dst_prefixlen < b->dst_prefixlen)
|
|
|
|
return -1;
|
|
|
|
if (a->dst_prefixlen > b->dst_prefixlen)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (a->tos < b->tos)
|
|
|
|
return -1;
|
|
|
|
if (a->tos > b->tos)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (a->priority < b->priority)
|
|
|
|
return -1;
|
|
|
|
if (a->priority > b->priority)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (a->table < b->table)
|
|
|
|
return -1;
|
|
|
|
if (a->table > b->table)
|
|
|
|
return 1;
|
|
|
|
|
2015-10-25 15:09:40 +01:00
|
|
|
return memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
|
2015-10-03 18:40:28 +02:00
|
|
|
default:
|
|
|
|
/* treat any other address family as AF_UNSPEC */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct hash_ops route_hash_ops = {
|
|
|
|
.hash = route_hash_func,
|
|
|
|
.compare = route_compare_func
|
|
|
|
};
|
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
int route_get(Link *link,
|
|
|
|
int family,
|
2016-06-03 19:51:48 +02:00
|
|
|
const union in_addr_union *dst,
|
2015-10-25 14:46:21 +01:00
|
|
|
unsigned char dst_prefixlen,
|
|
|
|
unsigned char tos,
|
|
|
|
uint32_t priority,
|
|
|
|
unsigned char table,
|
|
|
|
Route **ret) {
|
2016-06-03 19:51:48 +02:00
|
|
|
|
|
|
|
Route route, *existing;
|
|
|
|
|
|
|
|
assert(link);
|
|
|
|
assert(dst);
|
|
|
|
|
|
|
|
route = (Route) {
|
2015-10-25 14:46:21 +01:00
|
|
|
.family = family,
|
2016-06-03 19:51:48 +02:00
|
|
|
.dst = *dst,
|
2015-10-25 14:46:21 +01:00
|
|
|
.dst_prefixlen = dst_prefixlen,
|
|
|
|
.tos = tos,
|
|
|
|
.priority = priority,
|
|
|
|
.table = table,
|
2016-06-03 19:51:48 +02:00
|
|
|
};
|
2015-10-25 14:46:21 +01:00
|
|
|
|
|
|
|
existing = set_get(link->routes, &route);
|
|
|
|
if (existing) {
|
2016-06-03 19:51:48 +02:00
|
|
|
if (ret)
|
|
|
|
*ret = existing;
|
2015-10-25 14:46:21 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-06-03 19:51:48 +02:00
|
|
|
existing = set_get(link->routes_foreign, &route);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-06-03 19:54:35 +02:00
|
|
|
static int route_add_internal(
|
|
|
|
Link *link,
|
|
|
|
Set **routes,
|
|
|
|
int family,
|
|
|
|
const union in_addr_union *dst,
|
|
|
|
unsigned char dst_prefixlen,
|
|
|
|
unsigned char tos,
|
|
|
|
uint32_t priority,
|
|
|
|
unsigned char table,
|
|
|
|
Route **ret) {
|
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
_cleanup_route_free_ Route *route = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(link);
|
|
|
|
assert(routes);
|
|
|
|
assert(dst);
|
|
|
|
|
|
|
|
r = route_new(&route);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
route->family = family;
|
|
|
|
route->dst = *dst;
|
|
|
|
route->dst_prefixlen = dst_prefixlen;
|
|
|
|
route->tos = tos;
|
|
|
|
route->priority = priority;
|
|
|
|
route->table = table;
|
|
|
|
|
|
|
|
r = set_ensure_allocated(routes, &route_hash_ops);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = set_put(*routes, route);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
route->link = link;
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
*ret = route;
|
|
|
|
|
|
|
|
route = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-03 19:54:35 +02:00
|
|
|
int route_add_foreign(
|
|
|
|
Link *link,
|
|
|
|
int family,
|
|
|
|
const union in_addr_union *dst,
|
|
|
|
unsigned char dst_prefixlen,
|
|
|
|
unsigned char tos,
|
|
|
|
uint32_t priority,
|
|
|
|
unsigned char table,
|
|
|
|
Route **ret) {
|
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, tos, priority, table, ret);
|
|
|
|
}
|
|
|
|
|
2016-06-03 19:54:35 +02:00
|
|
|
int route_add(
|
|
|
|
Link *link,
|
2015-10-25 14:46:21 +01:00
|
|
|
int family,
|
2016-06-03 19:54:35 +02:00
|
|
|
const union in_addr_union *dst,
|
2015-10-25 14:46:21 +01:00
|
|
|
unsigned char dst_prefixlen,
|
|
|
|
unsigned char tos,
|
|
|
|
uint32_t priority,
|
2016-06-03 19:54:35 +02:00
|
|
|
unsigned char table,
|
|
|
|
Route **ret) {
|
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
Route *route;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = route_get(link, family, dst, dst_prefixlen, tos, priority, table, &route);
|
|
|
|
if (r == -ENOENT) {
|
|
|
|
/* Route does not exist, create a new one */
|
|
|
|
r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, tos, priority, table, &route);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
int route_update(Route *route,
|
2016-06-03 19:54:35 +02:00
|
|
|
const union in_addr_union *src,
|
2015-10-25 14:46:21 +01:00
|
|
|
unsigned char src_prefixlen,
|
2016-06-03 19:54:35 +02:00
|
|
|
const union in_addr_union *gw,
|
|
|
|
const union in_addr_union *prefsrc,
|
2015-10-25 14:46:21 +01:00
|
|
|
unsigned char scope,
|
|
|
|
unsigned char protocol) {
|
2016-06-03 19:54:35 +02:00
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
assert(route);
|
|
|
|
assert(src);
|
|
|
|
assert(gw);
|
|
|
|
assert(prefsrc);
|
|
|
|
|
|
|
|
route->src = *src;
|
|
|
|
route->src_prefixlen = src_prefixlen;
|
|
|
|
route->gw = *gw;
|
|
|
|
route->prefsrc = *prefsrc;
|
|
|
|
route->scope = scope;
|
|
|
|
route->protocol = protocol;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-24 15:25:20 +02:00
|
|
|
int route_remove(Route *route, Link *link,
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_message_handler_t callback) {
|
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);
|
|
|
|
assert(route->family == AF_INET || route->family == AF_INET6);
|
|
|
|
|
|
|
|
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)
|
|
|
|
return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2015-10-25 15:09:40 +01:00
|
|
|
if (!in_addr_is_null(route->family, &route->gw)) {
|
2014-11-25 00:51:31 +01:00
|
|
|
if (route->family == AF_INET)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
|
2014-11-25 00:51:31 +01:00
|
|
|
else if (route->family == AF_INET6)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (route->dst_prefixlen) {
|
|
|
|
if (route->family == AF_INET)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
|
2014-02-28 16:10:20 +01:00
|
|
|
else if (route->family == AF_INET6)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(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)
|
|
|
|
return log_error_errno(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) {
|
|
|
|
if (route->family == AF_INET)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
|
2014-12-04 15:52:21 +01:00
|
|
|
else if (route->family == AF_INET6)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
|
2014-12-04 15:52:21 +01:00
|
|
|
if (r < 0)
|
2016-05-14 22:47:59 +02:00
|
|
|
return log_error_errno(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)
|
|
|
|
return log_error_errno(r, "Could not set source prefix length: %m");
|
|
|
|
}
|
|
|
|
|
2015-10-25 15:09:40 +01:00
|
|
|
if (!in_addr_is_null(route->family, &route->prefsrc)) {
|
2014-09-05 11:56:02 +02:00
|
|
|
if (route->family == AF_INET)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
|
2014-09-05 11:56:02 +02:00
|
|
|
else if (route->family == AF_INET6)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(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)
|
|
|
|
return log_error_errno(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)
|
|
|
|
return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
|
2014-02-28 16:10:20 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(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;
|
|
|
|
}
|
|
|
|
|
2016-05-18 03:36:43 +02:00
|
|
|
static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
|
|
|
|
Link *link = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rtnl);
|
|
|
|
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 != -EEXIST)
|
|
|
|
log_link_warning_errno(link, r, "could not remove route: %m");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
2016-05-18 03:36:43 +02:00
|
|
|
r = route_remove(route, route->link, route_expire_callback);
|
2015-10-26 12:29:37 +01:00
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(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;
|
|
|
|
}
|
|
|
|
|
2016-06-03 19:51:48 +02:00
|
|
|
int route_configure(
|
|
|
|
Route *route,
|
|
|
|
Link *link,
|
|
|
|
sd_netlink_message_handler_t callback) {
|
|
|
|
|
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;
|
2015-10-26 12:29:37 +01:00
|
|
|
usec_t lifetime;
|
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);
|
|
|
|
assert(route->family == AF_INET || route->family == AF_INET6);
|
|
|
|
|
2016-06-03 19:51:48 +02:00
|
|
|
if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 &&
|
2016-08-21 15:06:28 +02:00
|
|
|
set_size(link->routes) >= routes_max())
|
2016-06-03 19:51:48 +02:00
|
|
|
return -E2BIG;
|
|
|
|
|
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)
|
|
|
|
return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-10-25 15:09:40 +01:00
|
|
|
if (!in_addr_is_null(route->family, &route->gw)) {
|
2014-11-25 00:51:31 +01:00
|
|
|
if (route->family == AF_INET)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
|
2014-11-25 00:51:31 +01:00
|
|
|
else if (route->family == AF_INET6)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(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)
|
|
|
|
return log_error_errno(r, "Could not set route family: %m");
|
2013-10-17 03:18:36 +02:00
|
|
|
}
|
|
|
|
|
2013-12-15 14:00:20 +01:00
|
|
|
if (route->dst_prefixlen) {
|
|
|
|
if (route->family == AF_INET)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
|
2013-12-15 14:00:20 +01:00
|
|
|
else if (route->family == AF_INET6)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(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)
|
|
|
|
return log_error_errno(r, "Could not set destination prefix length: %m");
|
2013-12-07 21:18:44 +01:00
|
|
|
}
|
|
|
|
|
2014-12-04 15:52:21 +01:00
|
|
|
if (route->src_prefixlen) {
|
|
|
|
if (route->family == AF_INET)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
|
2014-12-04 15:52:21 +01:00
|
|
|
else if (route->family == AF_INET6)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
|
2014-12-04 15:52:21 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
|
|
|
|
|
|
|
|
r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not set source prefix length: %m");
|
|
|
|
}
|
|
|
|
|
2015-10-25 15:09:40 +01:00
|
|
|
if (!in_addr_is_null(route->family, &route->prefsrc)) {
|
2014-09-05 11:56:02 +02:00
|
|
|
if (route->family == AF_INET)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
|
2014-09-05 11:56:02 +02:00
|
|
|
else if (route->family == AF_INET6)
|
2015-10-25 15:09:40 +01:00
|
|
|
r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(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)
|
|
|
|
return log_error_errno(r, "Could not set scope: %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_rtnl_message_route_set_flags(req, route->flags);
|
|
|
|
if (r < 0)
|
2016-05-03 19:48:21 +02:00
|
|
|
return log_error_errno(r, "Could not set flags: %m");
|
|
|
|
|
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)
|
|
|
|
return log_error_errno(r, "Could not set route table: %m");
|
|
|
|
} else {
|
|
|
|
r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not set route table: %m");
|
|
|
|
|
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)
|
|
|
|
return log_error_errno(r, "Could not append RTA_TABLE 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
|
|
|
|
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)
|
|
|
|
return log_error_errno(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)
|
|
|
|
return log_error_errno(r, "Could not append RTA_PREF attribute: %m");
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2016-11-23 22:32:19 +01:00
|
|
|
r = sd_netlink_message_open_container(req, RTA_METRICS);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
|
|
|
|
|
|
|
|
if (route->mtu > 0) {
|
|
|
|
r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not append RTAX_MTU attribute: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_close_container(req);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(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);
|
|
|
|
|
2015-10-26 12:29:37 +01:00
|
|
|
lifetime = route->lifetime;
|
|
|
|
|
|
|
|
r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route);
|
2015-10-25 14:46:21 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not add route: %m");
|
|
|
|
|
2015-10-26 12:29:37 +01:00
|
|
|
/* TODO: drop expiration handling once it can be pushed into the kernel */
|
|
|
|
route->lifetime = lifetime;
|
|
|
|
|
|
|
|
if (route->lifetime != USEC_INFINITY) {
|
|
|
|
r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
|
|
|
|
route->lifetime, 0, route_expire_handler, route);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not arm expiration timer: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
sd_event_source_unref(route->expire);
|
|
|
|
route->expire = expire;
|
|
|
|
expire = NULL;
|
|
|
|
|
2013-10-17 03:18:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int config_parse_gateway(const char *unit,
|
|
|
|
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;
|
2013-10-17 03:18:36 +02:00
|
|
|
_cleanup_route_free_ Route *n = NULL;
|
2014-08-11 22:44:51 +02:00
|
|
|
union in_addr_union buffer;
|
|
|
|
int r, f;
|
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);
|
|
|
|
} else
|
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
2013-11-28 17:17:04 +01:00
|
|
|
|
2013-10-17 03:18:36 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-08-11 22:44:51 +02:00
|
|
|
r = in_addr_from_string_auto(rvalue, &f, &buffer);
|
2013-10-17 03:18:36 +02:00
|
|
|
if (r < 0) {
|
2015-09-30 18:22:42 +02:00
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
|
2013-10-17 03:18:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-11 22:44:51 +02:00
|
|
|
n->family = f;
|
2015-10-25 15:09:40 +01:00
|
|
|
n->gw = buffer;
|
2013-10-17 03:18:36 +02:00
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-11-19 16:54:42 +01:00
|
|
|
|
2015-09-23 17:26:36 +02:00
|
|
|
int config_parse_preferred_src(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_ Route *n = NULL;
|
|
|
|
union in_addr_union buffer;
|
|
|
|
int r, f;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
r = in_addr_from_string_auto(rvalue, &f, &buffer);
|
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
|
|
|
|
"Preferred source is invalid, ignoring assignment: %s", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
n->family = f;
|
2015-10-25 15:09:40 +01:00
|
|
|
n->prefsrc = buffer;
|
2015-09-23 17:26:36 +02:00
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-19 16:54:42 +01:00
|
|
|
int config_parse_destination(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-08-11 22:44:51 +02:00
|
|
|
|
2013-11-19 16:54:42 +01:00
|
|
|
Network *network = userdata;
|
|
|
|
_cleanup_route_free_ Route *n = NULL;
|
2014-08-11 22:44:51 +02:00
|
|
|
union in_addr_union buffer;
|
2014-12-04 15:52:21 +01:00
|
|
|
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;
|
|
|
|
|
2017-05-11 06:42:54 +02:00
|
|
|
r = in_addr_prefix_from_string(rvalue, AF_INET, &buffer, &prefixlen);
|
2013-11-19 16:54:42 +01:00
|
|
|
if (r < 0) {
|
2017-05-11 06:42:54 +02:00
|
|
|
r = in_addr_prefix_from_string(rvalue, AF_INET6, &buffer, &prefixlen);
|
2013-12-07 23:03:19 +01:00
|
|
|
if (r < 0) {
|
2017-05-11 20:00:25 +02:00
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
|
|
"Route %s= prefix is invalid, ignoring assignment: %s",
|
|
|
|
lvalue, rvalue);
|
2013-12-07 23:03:19 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-11 06:42:54 +02:00
|
|
|
n->family = AF_INET6;
|
|
|
|
} else
|
|
|
|
n->family = AF_INET;
|
|
|
|
|
2014-12-04 15:52:21 +01:00
|
|
|
if (streq(lvalue, "Destination")) {
|
2015-10-25 15:09:40 +01:00
|
|
|
n->dst = buffer;
|
2014-12-04 15:52:21 +01:00
|
|
|
n->dst_prefixlen = prefixlen;
|
|
|
|
} else if (streq(lvalue, "Source")) {
|
2015-10-25 15:09:40 +01:00
|
|
|
n->src = buffer;
|
2014-12-04 15:52:21 +01:00
|
|
|
n->src_prefixlen = prefixlen;
|
|
|
|
} else
|
|
|
|
assert_not_reached(lvalue);
|
|
|
|
|
2013-11-19 16:54:42 +01:00
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-07-10 19:39:58 +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) {
|
|
|
|
Network *network = userdata;
|
|
|
|
_cleanup_route_free_ Route *n = NULL;
|
2016-06-13 15:57:17 +02:00
|
|
|
uint32_t k;
|
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;
|
|
|
|
|
2016-06-13 15:57:17 +02:00
|
|
|
r = safe_atou32(rvalue, &k);
|
|
|
|
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
|
|
|
|
2016-06-13 15:57:17 +02:00
|
|
|
n->priority = k;
|
2014-07-10 19:39:58 +02:00
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-02-09 16:22:34 +01: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) {
|
|
|
|
Network *network = userdata;
|
|
|
|
_cleanup_route_free_ Route *n = NULL;
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (streq(rvalue, "host"))
|
|
|
|
n->scope = RT_SCOPE_HOST;
|
|
|
|
else if (streq(rvalue, "link"))
|
|
|
|
n->scope = RT_SCOPE_LINK;
|
|
|
|
else if (streq(rvalue, "global"))
|
|
|
|
n->scope = RT_SCOPE_UNIVERSE;
|
|
|
|
else {
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-05-03 19:48:21 +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) {
|
|
|
|
_cleanup_route_free_ Route *n = NULL;
|
|
|
|
Network *network = userdata;
|
|
|
|
uint32_t k;
|
|
|
|
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;
|
|
|
|
|
|
|
|
r = safe_atou32(rvalue, &k);
|
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
|
|
"Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
n->table = k;
|
|
|
|
|
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-04-21 11:22:30 +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) {
|
|
|
|
Network *network = userdata;
|
|
|
|
_cleanup_route_free_ Route *n = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(section);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = parse_boolean(rvalue);
|
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
|
|
"Could not parse gateway onlink \"%s\", ignoring assignment: %m", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-07 13:03:28 +02:00
|
|
|
SET_FLAG(n->flags, RTNH_F_ONLINK, r);
|
2017-04-25 09:32:59 +02:00
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
Network *network = userdata;
|
|
|
|
_cleanup_route_free_ Route *n = NULL;
|
|
|
|
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
|
|
|
|
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-05-09 20:01:25 +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) {
|
|
|
|
Network *network = userdata;
|
|
|
|
_cleanup_route_free_ Route *n = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = route_new_static(network, filename, section_line, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (streq(rvalue, "kernel"))
|
|
|
|
n->protocol = RTPROT_KERNEL;
|
|
|
|
else if (streq(rvalue, "boot"))
|
|
|
|
n->protocol = RTPROT_BOOT;
|
|
|
|
else if (streq(rvalue, "static"))
|
|
|
|
n->protocol = RTPROT_STATIC;
|
|
|
|
else {
|
|
|
|
r = safe_atou8(rvalue , &n->protocol);
|
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|