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/>.
|
|
|
|
***/
|
|
|
|
|
2014-05-18 22:10:48 +02:00
|
|
|
#include <sys/socket.h>
|
2014-05-08 17:21:37 +02:00
|
|
|
#include <linux/if.h>
|
2014-01-05 23:01:10 +01:00
|
|
|
|
2015-08-27 13:59:06 +02:00
|
|
|
#include "sd-daemon.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "sd-netlink.h"
|
2015-08-27 13:59:06 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-02-04 15:00:20 +01:00
|
|
|
#include "bus-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "conf-parser.h"
|
2015-02-04 15:00:20 +01:00
|
|
|
#include "def.h"
|
2016-01-25 20:33:47 +01:00
|
|
|
#include "dns-domain.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-10-26 18:05:03 +01:00
|
|
|
#include "fileio.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "libudev-private.h"
|
2015-08-27 16:45:24 +02:00
|
|
|
#include "local-addresses.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "netlink-util.h"
|
2016-11-13 04:59:06 +01:00
|
|
|
#include "networkd-manager.h"
|
2016-01-25 20:31:11 +01:00
|
|
|
#include "ordered-set.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "path-util.h"
|
|
|
|
#include "set.h"
|
|
|
|
#include "udev-util.h"
|
|
|
|
#include "virt.h"
|
2014-04-15 14:21:44 +02:00
|
|
|
|
2014-11-27 18:50:48 +01:00
|
|
|
/* use 8 MB for receive socket kernel queue. */
|
|
|
|
#define RCVBUF_SIZE (8*1024*1024)
|
|
|
|
|
2014-01-08 23:41:41 +01:00
|
|
|
const char* const network_dirs[] = {
|
|
|
|
"/etc/systemd/network",
|
|
|
|
"/run/systemd/network",
|
|
|
|
"/usr/lib/systemd/network",
|
2014-03-14 18:19:29 +01:00
|
|
|
#ifdef HAVE_SPLIT_USR
|
2014-01-08 23:41:41 +01:00
|
|
|
"/lib/systemd/network",
|
|
|
|
#endif
|
|
|
|
NULL};
|
|
|
|
|
2014-06-18 18:22:14 +02:00
|
|
|
static int setup_default_address_pool(Manager *m) {
|
|
|
|
AddressPool *p;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
/* Add in the well-known private address ranges. */
|
|
|
|
|
|
|
|
r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-03 15:44:12 +01:00
|
|
|
static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
|
|
|
|
Manager *m = userdata;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
|
|
|
|
|
|
|
|
manager_connect_bus(m);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int manager_reset_all(Manager *m) {
|
|
|
|
Link *link;
|
|
|
|
Iterator i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(link, m->links, i) {
|
|
|
|
r = link_carrier_reset(link);
|
|
|
|
if (r < 0)
|
2015-08-26 20:43:28 +02:00
|
|
|
log_link_warning_errno(link, r, "Could not reset carrier: %m");
|
2015-02-03 15:44:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-29 18:35:10 +02:00
|
|
|
static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
|
2015-02-03 15:44:12 +01:00
|
|
|
Manager *m = userdata;
|
|
|
|
int b, r;
|
|
|
|
|
2015-04-29 18:35:10 +02:00
|
|
|
assert(message);
|
2015-02-03 15:44:12 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "b", &b);
|
|
|
|
if (r < 0) {
|
|
|
|
log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (b)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
log_debug("Coming back from suspend, resetting all connections...");
|
|
|
|
|
|
|
|
manager_reset_all(m);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int manager_connect_bus(Manager *m) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
r = sd_bus_default_system(&m->bus);
|
|
|
|
if (r == -ENOENT) {
|
|
|
|
/* We failed to connect? Yuck, we must be in early
|
|
|
|
* boot. Let's try in 5s again. As soon as we have
|
|
|
|
* kdbus we can stop doing this... */
|
|
|
|
|
|
|
|
log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m");
|
|
|
|
|
|
|
|
r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to install bus reconnect time event: %m");
|
|
|
|
|
|
|
|
return 0;
|
2015-08-06 00:31:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (r < 0)
|
2015-02-03 15:44:12 +01:00
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot,
|
|
|
|
"type='signal',"
|
|
|
|
"sender='org.freedesktop.login1',"
|
|
|
|
"interface='org.freedesktop.login1.Manager',"
|
|
|
|
"member='PrepareForSleep',"
|
|
|
|
"path='/org/freedesktop/login1'",
|
|
|
|
match_prepare_for_sleep,
|
|
|
|
m);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to add match for PrepareForSleep: %m");
|
|
|
|
|
2015-02-04 11:44:37 +01:00
|
|
|
r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/network1", "org.freedesktop.network1.Manager", manager_vtable, m);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to add manager object vtable: %m");
|
|
|
|
|
|
|
|
r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/link", "org.freedesktop.network1.Link", link_vtable, link_object_find, m);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to add link object vtable: %m");
|
|
|
|
|
|
|
|
r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/link", link_node_enumerator, m);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to add link enumerator: %m");
|
2015-02-08 13:27:56 +01:00
|
|
|
|
|
|
|
r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/network", "org.freedesktop.network1.Network", network_vtable, network_object_find, m);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to add network object vtable: %m");
|
|
|
|
|
|
|
|
r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/network", network_node_enumerator, m);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to add network enumerator: %m");
|
2015-02-04 11:44:37 +01:00
|
|
|
|
|
|
|
r = sd_bus_request_name(m->bus, "org.freedesktop.network1", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to register name: %m");
|
|
|
|
|
|
|
|
r = sd_bus_attach_event(m->bus, m->event, 0);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to attach bus to event loop: %m");
|
|
|
|
|
2016-11-22 08:36:20 +01:00
|
|
|
/* Did we get a timezone or transient hostname from DHCP while D-Bus wasn't up yet? */
|
|
|
|
if (m->dynamic_hostname) {
|
|
|
|
r = manager_set_hostname(m, m->dynamic_hostname);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
if (m->dynamic_timezone) {
|
|
|
|
r = manager_set_timezone(m, m->dynamic_timezone);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-02-03 15:44:12 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
static int manager_udev_process_link(Manager *m, struct udev_device *device) {
|
|
|
|
Link *link = NULL;
|
|
|
|
int r, ifindex;
|
2015-02-01 22:13:26 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
assert(m);
|
|
|
|
assert(device);
|
2015-02-01 22:13:26 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
if (!streq_ptr(udev_device_get_action(device), "add"))
|
|
|
|
return 0;
|
2015-02-01 22:13:26 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
ifindex = udev_device_get_ifindex(device);
|
|
|
|
if (ifindex <= 0) {
|
2015-08-26 20:43:28 +02:00
|
|
|
log_debug("Ignoring udev ADD event for device with invalid ifindex");
|
2015-02-03 15:44:50 +01:00
|
|
|
return 0;
|
2015-02-01 22:13:26 +01:00
|
|
|
}
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = link_get(m, ifindex, &link);
|
|
|
|
if (r == -ENODEV)
|
|
|
|
return 0;
|
|
|
|
else if (r < 0)
|
2013-10-17 03:18:36 +02:00
|
|
|
return r;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = link_initialized(link, device);
|
2015-02-02 23:21:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-27 18:50:48 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
|
|
|
|
Manager *m = userdata;
|
|
|
|
struct udev_monitor *monitor = m->udev_monitor;
|
|
|
|
_cleanup_udev_device_unref_ struct udev_device *device = NULL;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
device = udev_monitor_receive_device(monitor);
|
|
|
|
if (!device)
|
2013-11-24 23:37:56 +01:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
manager_udev_process_link(m, device);
|
2013-10-17 03:18:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
static int manager_connect_udev(Manager *m) {
|
|
|
|
int r;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
/* udev does not initialize devices inside containers,
|
|
|
|
* so we rely on them being already initialized before
|
|
|
|
* entering the container */
|
2015-09-07 13:42:47 +02:00
|
|
|
if (detect_container() > 0)
|
2015-02-03 15:44:50 +01:00
|
|
|
return 0;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
m->udev = udev_new();
|
|
|
|
if (!m->udev)
|
|
|
|
return -ENOMEM;
|
2013-11-24 23:37:56 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
|
|
|
|
if (!m->udev_monitor)
|
|
|
|
return -ENOMEM;
|
2013-11-24 23:37:56 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
|
2013-11-24 23:37:56 +01:00
|
|
|
if (r < 0)
|
2015-02-03 15:44:50 +01:00
|
|
|
return log_error_errno(r, "Could not add udev monitor filter: %m");
|
2013-11-24 23:37:56 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = udev_monitor_enable_receiving(m->udev_monitor);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error("Could not enable udev monitor");
|
2013-11-24 23:37:56 +01:00
|
|
|
return r;
|
2014-04-19 20:49:06 +02:00
|
|
|
}
|
2014-04-15 14:21:44 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = sd_event_add_io(m->event,
|
|
|
|
&m->udev_event_source,
|
|
|
|
udev_monitor_get_fd(m->udev_monitor),
|
|
|
|
EPOLLIN, manager_dispatch_link_udev,
|
|
|
|
m);
|
|
|
|
if (r < 0)
|
2014-04-19 20:49:06 +02:00
|
|
|
return r;
|
2014-04-15 14:21:44 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = sd_event_source_set_description(m->udev_event_source, "networkd-udev");
|
2014-04-15 14:21:44 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-02-18 21:42:05 +01:00
|
|
|
|
2014-04-15 14:21:44 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
|
|
|
|
Manager *m = userdata;
|
|
|
|
Link *link = NULL;
|
|
|
|
uint16_t type;
|
|
|
|
uint32_t ifindex, priority = 0;
|
|
|
|
unsigned char protocol, scope, tos, table;
|
|
|
|
int family;
|
|
|
|
unsigned char dst_prefixlen, src_prefixlen;
|
|
|
|
union in_addr_union dst = {}, gw = {}, src = {}, prefsrc = {};
|
|
|
|
Route *route = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rtnl);
|
|
|
|
assert(message);
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (sd_netlink_message_is_error(message)) {
|
|
|
|
r = sd_netlink_message_get_errno(message);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(r, "rtnl: failed to receive route: %m");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_get_type(message, &type);
|
|
|
|
if (r < 0) {
|
|
|
|
log_warning_errno(r, "rtnl: could not get message type: %m");
|
|
|
|
return 0;
|
|
|
|
} else if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
|
|
|
|
log_warning("rtnl: received unexpected message type when processing route");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
|
|
|
|
if (r == -ENODATA) {
|
|
|
|
log_debug("rtnl: received route without ifindex, ignoring");
|
|
|
|
return 0;
|
|
|
|
} else if (r < 0) {
|
|
|
|
log_warning_errno(r, "rtnl: could not get ifindex from route, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
} else if (ifindex <= 0) {
|
|
|
|
log_warning("rtnl: received route message with invalid ifindex, ignoring: %d", ifindex);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
r = link_get(m, ifindex, &link);
|
|
|
|
if (r < 0 || !link) {
|
|
|
|
/* when enumerating we might be out of sync, but we will
|
|
|
|
* get the route again, so just ignore it */
|
|
|
|
if (!m->enumerating)
|
|
|
|
log_warning("rtnl: received route for nonexistent link (%d), ignoring", ifindex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_route_get_family(message, &family);
|
|
|
|
if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
|
|
|
|
log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_route_get_protocol(message, &protocol);
|
|
|
|
if (r < 0) {
|
|
|
|
log_warning_errno(r, "rtnl: could not get route protocol: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (family) {
|
|
|
|
case AF_INET:
|
|
|
|
r = sd_netlink_message_read_in_addr(message, RTA_DST, &dst.in);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &gw.in);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_read_in_addr(message, RTA_SRC, &src.in);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &prefsrc.in);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AF_INET6:
|
|
|
|
r = sd_netlink_message_read_in6_addr(message, RTA_DST, &dst.in6);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &gw.in6);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &src.in6);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &prefsrc.in6);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_route_get_dst_prefixlen(message, &dst_prefixlen);
|
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid destination prefixlen, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_route_get_src_prefixlen(message, &src_prefixlen);
|
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid source prefixlen, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_route_get_scope(message, &scope);
|
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid scope, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_route_get_tos(message, &tos);
|
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid tos, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_route_get_table(message, &table);
|
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid table, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &priority);
|
|
|
|
if (r < 0 && r != -ENODATA) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received route with invalid priority, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case RTM_NEWROUTE:
|
|
|
|
if (!route) {
|
|
|
|
/* A route appeared that we did not request */
|
|
|
|
r = route_add_foreign(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
|
|
|
|
if (r < 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RTM_DELROUTE:
|
2016-05-14 22:46:01 +02:00
|
|
|
route_free(route);
|
2015-10-25 14:46:21 +01:00
|
|
|
break;
|
2016-05-14 22:46:01 +02:00
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
default:
|
|
|
|
assert_not_reached("Received invalid RTNL message type");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-09-22 17:18:20 +02:00
|
|
|
int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
|
|
|
|
Manager *m = userdata;
|
|
|
|
Link *link = NULL;
|
|
|
|
uint16_t type;
|
|
|
|
unsigned char flags;
|
2015-09-24 01:22:05 +02:00
|
|
|
int family;
|
|
|
|
unsigned char prefixlen;
|
|
|
|
unsigned char scope;
|
|
|
|
union in_addr_union in_addr;
|
|
|
|
struct ifa_cacheinfo cinfo;
|
|
|
|
Address *address = NULL;
|
2015-09-22 17:18:20 +02:00
|
|
|
char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX];
|
|
|
|
const char *valid_str = NULL;
|
|
|
|
int r, ifindex;
|
|
|
|
|
|
|
|
assert(rtnl);
|
|
|
|
assert(message);
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (sd_netlink_message_is_error(message)) {
|
|
|
|
r = sd_netlink_message_get_errno(message);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(r, "rtnl: failed to receive address: %m");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_netlink_message_get_type(message, &type);
|
|
|
|
if (r < 0) {
|
|
|
|
log_warning_errno(r, "rtnl: could not get message type: %m");
|
|
|
|
return 0;
|
|
|
|
} else if (type != RTM_NEWADDR && type != RTM_DELADDR) {
|
|
|
|
log_warning("rtnl: received unexpected message type when processing address");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
|
|
|
|
if (r < 0) {
|
|
|
|
log_warning_errno(r, "rtnl: could not get ifindex from address: %m");
|
|
|
|
return 0;
|
|
|
|
} else if (ifindex <= 0) {
|
|
|
|
log_warning("rtnl: received address message with invalid ifindex: %d", ifindex);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
r = link_get(m, ifindex, &link);
|
|
|
|
if (r < 0 || !link) {
|
|
|
|
/* when enumerating we might be out of sync, but we will
|
|
|
|
* get the address again, so just ignore it */
|
|
|
|
if (!m->enumerating)
|
|
|
|
log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-24 01:22:05 +02:00
|
|
|
r = sd_rtnl_message_addr_get_family(message, &family);
|
|
|
|
if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
|
2015-09-22 17:18:20 +02:00
|
|
|
log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-24 01:22:05 +02:00
|
|
|
r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen);
|
2015-09-22 17:18:20 +02:00
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-24 01:22:05 +02:00
|
|
|
r = sd_rtnl_message_addr_get_scope(message, &scope);
|
2015-09-22 17:18:20 +02:00
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_rtnl_message_addr_get_flags(message, &flags);
|
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-24 01:22:05 +02:00
|
|
|
switch (family) {
|
2015-09-22 17:18:20 +02:00
|
|
|
case AF_INET:
|
2015-09-24 01:22:05 +02:00
|
|
|
r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in);
|
2015-09-22 17:18:20 +02:00
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AF_INET6:
|
2015-09-24 01:22:05 +02:00
|
|
|
r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6);
|
2015-09-22 17:18:20 +02:00
|
|
|
if (r < 0) {
|
|
|
|
log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2015-10-25 14:46:21 +01:00
|
|
|
log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
|
2015-09-22 17:18:20 +02:00
|
|
|
}
|
|
|
|
|
2015-09-24 01:22:05 +02:00
|
|
|
if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) {
|
2015-09-22 17:18:20 +02:00
|
|
|
log_link_warning(link, "Could not print address");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-24 01:22:05 +02:00
|
|
|
r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
|
2015-09-22 17:18:20 +02:00
|
|
|
if (r >= 0) {
|
2015-11-10 21:45:41 +01:00
|
|
|
if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
|
2015-09-22 17:18:20 +02:00
|
|
|
valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
|
2015-09-24 01:22:05 +02:00
|
|
|
cinfo.ifa_valid * USEC_PER_SEC,
|
2015-09-22 17:18:20 +02:00
|
|
|
USEC_PER_SEC);
|
|
|
|
}
|
|
|
|
|
2015-09-24 01:22:05 +02:00
|
|
|
address_get(link, family, &in_addr, prefixlen, &address);
|
2015-09-22 17:18:20 +02:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case RTM_NEWADDR:
|
2015-09-28 17:16:12 +02:00
|
|
|
if (address)
|
2015-11-10 21:45:41 +01:00
|
|
|
log_link_debug(link, "Updating address: %s/%u (valid %s%s)", buf, prefixlen,
|
|
|
|
valid_str ? "for " : "forever", valid_str ?: "");
|
2015-09-28 17:16:12 +02:00
|
|
|
else {
|
2015-09-30 14:01:44 +02:00
|
|
|
/* An address appeared that we did not request */
|
|
|
|
r = address_add_foreign(link, family, &in_addr, prefixlen, &address);
|
2015-09-23 01:53:29 +02:00
|
|
|
if (r < 0) {
|
2015-09-24 01:22:05 +02:00
|
|
|
log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen);
|
2015-09-23 01:53:29 +02:00
|
|
|
return 0;
|
|
|
|
} else
|
2015-11-10 21:45:41 +01:00
|
|
|
log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen,
|
|
|
|
valid_str ? "for " : "forever", valid_str ?: "");
|
2015-09-22 17:18:20 +02:00
|
|
|
}
|
|
|
|
|
2015-10-22 09:36:07 +02:00
|
|
|
address_update(address, flags, scope, &cinfo);
|
2015-09-28 17:16:12 +02:00
|
|
|
|
2015-09-22 17:18:20 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RTM_DELADDR:
|
|
|
|
|
2015-09-24 01:22:05 +02:00
|
|
|
if (address) {
|
2015-11-10 21:45:41 +01:00
|
|
|
log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen,
|
|
|
|
valid_str ? "for " : "forever", valid_str ?: "");
|
2015-09-24 15:25:20 +02:00
|
|
|
address_drop(address);
|
2015-09-22 17:18:20 +02:00
|
|
|
} else
|
2015-11-10 21:45:41 +01:00
|
|
|
log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen,
|
|
|
|
valid_str ? "for " : "forever", valid_str ?: "");
|
2015-09-22 17:18:20 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert_not_reached("Received invalid RTNL message type");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
|
2014-04-15 14:21:44 +02:00
|
|
|
Manager *m = userdata;
|
|
|
|
Link *link = NULL;
|
2014-05-09 17:59:20 +02:00
|
|
|
NetDev *netdev = NULL;
|
2014-05-08 18:55:11 +02:00
|
|
|
uint16_t type;
|
2014-07-18 02:35:16 +02:00
|
|
|
const char *name;
|
2014-04-15 14:21:44 +02:00
|
|
|
int r, ifindex;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2014-04-15 14:21:44 +02:00
|
|
|
assert(rtnl);
|
|
|
|
assert(message);
|
2013-10-17 03:18:36 +02:00
|
|
|
assert(m);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
if (sd_netlink_message_is_error(message)) {
|
|
|
|
r = sd_netlink_message_get_errno(message);
|
2014-12-08 19:54:06 +01:00
|
|
|
if (r < 0)
|
2015-08-26 20:43:28 +02:00
|
|
|
log_warning_errno(r, "rtnl: Could not receive link: %m");
|
2014-12-08 19:54:06 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_get_type(message, &type);
|
2014-05-08 18:55:11 +02:00
|
|
|
if (r < 0) {
|
2015-08-26 20:43:28 +02:00
|
|
|
log_warning_errno(r, "rtnl: Could not get message type: %m");
|
2014-05-08 18:55:11 +02:00
|
|
|
return 0;
|
2015-04-03 15:21:03 +02:00
|
|
|
} else if (type != RTM_NEWLINK && type != RTM_DELLINK) {
|
2015-08-26 20:43:28 +02:00
|
|
|
log_warning("rtnl: Received unexpected message type when processing link");
|
2015-04-03 15:21:03 +02:00
|
|
|
return 0;
|
2014-05-08 18:55:11 +02:00
|
|
|
}
|
|
|
|
|
2014-04-15 14:21:44 +02:00
|
|
|
r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
|
2014-12-08 19:54:06 +01:00
|
|
|
if (r < 0) {
|
2015-08-26 20:43:28 +02:00
|
|
|
log_warning_errno(r, "rtnl: Could not get ifindex from link: %m");
|
2014-12-08 19:54:06 +01:00
|
|
|
return 0;
|
|
|
|
} else if (ifindex <= 0) {
|
|
|
|
log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
|
2014-04-15 14:21:44 +02:00
|
|
|
return 0;
|
2016-01-20 01:25:45 +01:00
|
|
|
}
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
|
2014-12-08 19:54:06 +01:00
|
|
|
if (r < 0) {
|
2015-08-26 20:43:28 +02:00
|
|
|
log_warning_errno(r, "rtnl: Received link message without ifname: %m");
|
2014-05-09 17:59:20 +02:00
|
|
|
return 0;
|
2016-01-20 01:25:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) link_get(m, ifindex, &link);
|
|
|
|
(void) netdev_get(m, name, &netdev);
|
2014-05-09 17:59:20 +02:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case RTM_NEWLINK:
|
|
|
|
if (!link) {
|
|
|
|
/* link is new, so add it */
|
|
|
|
r = link_add(m, message, &link);
|
|
|
|
if (r < 0) {
|
2015-08-26 20:43:28 +02:00
|
|
|
log_warning_errno(r, "Could not add new link: %m");
|
2014-05-09 17:59:20 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netdev) {
|
|
|
|
/* netdev exists, so make sure the ifindex matches */
|
2014-04-15 14:21:44 +02:00
|
|
|
r = netdev_set_ifindex(netdev, message);
|
|
|
|
if (r < 0) {
|
2015-08-26 20:43:28 +02:00
|
|
|
log_warning_errno(r, "Could not set ifindex on netdev: %m");
|
2014-04-15 14:21:44 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2013-12-18 17:12:15 +01:00
|
|
|
|
2014-05-08 18:55:11 +02:00
|
|
|
r = link_update(link, message);
|
|
|
|
if (r < 0)
|
|
|
|
return 0;
|
2014-05-09 17:59:20 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RTM_DELLINK:
|
|
|
|
link_drop(link);
|
|
|
|
netdev_drop(netdev);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Received invalid RTNL message type.");
|
2014-05-08 18:55:11 +02:00
|
|
|
}
|
2014-04-15 14:21:44 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
static int systemd_netlink_fd(void) {
|
|
|
|
int n, fd, rtnl_fd = -EINVAL;
|
|
|
|
|
|
|
|
n = sd_listen_fds(true);
|
|
|
|
if (n <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
|
|
|
|
if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
|
|
|
|
if (rtnl_fd >= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_fd = fd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rtnl_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int manager_connect_rtnl(Manager *m) {
|
|
|
|
int fd, r;
|
2014-04-15 14:21:44 +02:00
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
fd = systemd_netlink_fd();
|
|
|
|
if (fd < 0)
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_open(&m->rtnl);
|
2015-02-03 15:44:50 +01:00
|
|
|
else
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_open_fd(&m->rtnl, fd);
|
2014-04-15 14:21:44 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE);
|
2013-10-17 03:18:36 +02:00
|
|
|
if (r < 0)
|
2013-12-18 03:37:26 +01:00
|
|
|
return r;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_attach_event(m->rtnl, m->event, 0);
|
2014-04-15 14:21:44 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
|
2015-02-03 15:44:50 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-04-15 14:21:44 +02:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, &manager_rtnl_process_link, m);
|
2015-02-03 15:44:50 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-12-08 19:54:06 +01:00
|
|
|
|
2015-09-22 17:18:20 +02:00
|
|
|
r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &manager_rtnl_process_address, m);
|
2015-02-03 15:44:50 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-09-22 17:18:20 +02:00
|
|
|
r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &manager_rtnl_process_address, m);
|
2015-02-03 15:44:50 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
r = sd_netlink_add_match(m->rtnl, RTM_NEWROUTE, &manager_rtnl_process_route, m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_netlink_add_match(m->rtnl, RTM_DELROUTE, &manager_rtnl_process_route, m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
return 0;
|
2014-12-08 19:54:06 +01:00
|
|
|
}
|
2014-04-15 14:21:44 +02:00
|
|
|
|
2016-11-18 17:04:26 +01:00
|
|
|
static int ordered_set_put_in_addr_data(OrderedSet *s, const struct in_addr_data *address) {
|
2015-09-30 18:17:43 +02:00
|
|
|
char *p;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(s);
|
2016-11-18 17:04:26 +01:00
|
|
|
assert(address);
|
|
|
|
|
|
|
|
r = in_addr_to_string(address->family, &address->address, &p);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = ordered_set_consume(s, p);
|
|
|
|
if (r == -EEXIST)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ordered_set_put_in_addr_datav(OrderedSet *s, const struct in_addr_data *addresses, unsigned n) {
|
|
|
|
int r, c = 0;
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(addresses || n == 0);
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
r = ordered_set_put_in_addr_data(s, addresses+i);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
c += r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address) {
|
|
|
|
char *p;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(address);
|
2015-09-30 18:17:43 +02:00
|
|
|
|
|
|
|
r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-01-25 20:31:11 +01:00
|
|
|
r = ordered_set_consume(s, p);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (r == -EEXIST)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2016-11-18 17:04:26 +01:00
|
|
|
static int ordered_set_put_in4_addrv(OrderedSet *s, const struct in_addr *addresses, unsigned n) {
|
|
|
|
int r, c = 0;
|
|
|
|
unsigned i;
|
2015-09-30 18:17:43 +02:00
|
|
|
|
|
|
|
assert(s);
|
2016-11-18 17:04:26 +01:00
|
|
|
assert(n == 0 || addresses);
|
2015-09-30 18:17:43 +02:00
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2016-11-18 17:04:26 +01:00
|
|
|
r = ordered_set_put_in4_addr(s, addresses+i);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
c += r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2016-01-25 20:31:11 +01:00
|
|
|
static void print_string_set(FILE *f, const char *field, OrderedSet *s) {
|
2015-09-30 18:17:43 +02:00
|
|
|
bool space = false;
|
|
|
|
Iterator i;
|
|
|
|
char *p;
|
|
|
|
|
2016-01-25 20:31:11 +01:00
|
|
|
if (ordered_set_isempty(s))
|
2015-09-30 18:17:43 +02:00
|
|
|
return;
|
|
|
|
|
2017-07-21 10:35:45 +02:00
|
|
|
fputs_unlocked(field, f);
|
2015-09-30 18:17:43 +02:00
|
|
|
|
2016-01-25 22:42:36 +01:00
|
|
|
ORDERED_SET_FOREACH(p, s, i)
|
|
|
|
fputs_with_space(f, p, NULL, &space);
|
|
|
|
|
2017-07-21 10:35:45 +02:00
|
|
|
fputc_unlocked('\n', f);
|
2015-09-30 18:17:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int manager_save(Manager *m) {
|
2016-01-25 20:31:11 +01:00
|
|
|
_cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL;
|
2015-09-30 18:17:43 +02:00
|
|
|
Link *link;
|
|
|
|
Iterator i;
|
|
|
|
_cleanup_free_ char *temp_path = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
LinkOperationalState operstate = LINK_OPERSTATE_OFF;
|
|
|
|
const char *operstate_str;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(m->state_file);
|
|
|
|
|
|
|
|
/* We add all NTP and DNS server to a set, to filter out duplicates */
|
2016-01-25 20:31:11 +01:00
|
|
|
dns = ordered_set_new(&string_hash_ops);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (!dns)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-01-25 20:31:11 +01:00
|
|
|
ntp = ordered_set_new(&string_hash_ops);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (!ntp)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-01-25 20:33:47 +01:00
|
|
|
search_domains = ordered_set_new(&dns_name_hash_ops);
|
networkd: rework Domains= setting
Previously, .network files only knew a vaguely defined "Domains=" concept, for which the documentation declared it was
the "DNS domain" for the network connection, without specifying what that means.
With this the Domains setting is reworked, so that there are now "routing" domains and "search" domains. The former are
to be used by resolved to route DNS request to specific network interfaces, the latter is to be used for searching
single-label hostnames with (in addition to being used for routing). Both settings are configured in the "Domains="
setting. Normal domain names listed in it are now considered search domains (for compatibility with existing setups),
while those prefixed with "~" are considered routing domains only. To route all lookups to a specific interface the
routing domain "." may be used, referring to the root domain. An alternative syntax for this is the "*", as was already
implemented before using the "wildcard" domain concept.
This commit adds proper parsers for this new logic, and exposes this via the sd-network API. This information is not
used by resolved yet, this will be added in a later commit.
2016-01-25 19:46:00 +01:00
|
|
|
if (!search_domains)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-01-25 20:33:47 +01:00
|
|
|
route_domains = ordered_set_new(&dns_name_hash_ops);
|
networkd: rework Domains= setting
Previously, .network files only knew a vaguely defined "Domains=" concept, for which the documentation declared it was
the "DNS domain" for the network connection, without specifying what that means.
With this the Domains setting is reworked, so that there are now "routing" domains and "search" domains. The former are
to be used by resolved to route DNS request to specific network interfaces, the latter is to be used for searching
single-label hostnames with (in addition to being used for routing). Both settings are configured in the "Domains="
setting. Normal domain names listed in it are now considered search domains (for compatibility with existing setups),
while those prefixed with "~" are considered routing domains only. To route all lookups to a specific interface the
routing domain "." may be used, referring to the root domain. An alternative syntax for this is the "*", as was already
implemented before using the "wildcard" domain concept.
This commit adds proper parsers for this new logic, and exposes this via the sd-network API. This information is not
used by resolved yet, this will be added in a later commit.
2016-01-25 19:46:00 +01:00
|
|
|
if (!route_domains)
|
2015-09-30 18:17:43 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(link, m->links, i) {
|
|
|
|
if (link->flags & IFF_LOOPBACK)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (link->operstate > operstate)
|
|
|
|
operstate = link->operstate;
|
|
|
|
|
|
|
|
if (!link->network)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* First add the static configured entries */
|
2016-11-18 17:04:26 +01:00
|
|
|
r = ordered_set_put_in_addr_datav(dns, link->network->dns, link->network->n_dns);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-01-25 20:31:11 +01:00
|
|
|
r = ordered_set_put_strdupv(ntp, link->network->ntp);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-01-25 20:31:11 +01:00
|
|
|
r = ordered_set_put_strdupv(search_domains, link->network->search_domains);
|
networkd: rework Domains= setting
Previously, .network files only knew a vaguely defined "Domains=" concept, for which the documentation declared it was
the "DNS domain" for the network connection, without specifying what that means.
With this the Domains setting is reworked, so that there are now "routing" domains and "search" domains. The former are
to be used by resolved to route DNS request to specific network interfaces, the latter is to be used for searching
single-label hostnames with (in addition to being used for routing). Both settings are configured in the "Domains="
setting. Normal domain names listed in it are now considered search domains (for compatibility with existing setups),
while those prefixed with "~" are considered routing domains only. To route all lookups to a specific interface the
routing domain "." may be used, referring to the root domain. An alternative syntax for this is the "*", as was already
implemented before using the "wildcard" domain concept.
This commit adds proper parsers for this new logic, and exposes this via the sd-network API. This information is not
used by resolved yet, this will be added in a later commit.
2016-01-25 19:46:00 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-01-25 20:31:11 +01:00
|
|
|
r = ordered_set_put_strdupv(route_domains, link->network->route_domains);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!link->dhcp_lease)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Secondly, add the entries acquired via DHCP */
|
2016-01-25 21:47:02 +01:00
|
|
|
if (link->network->dhcp_use_dns) {
|
2015-09-30 18:17:43 +02:00
|
|
|
const struct in_addr *addresses;
|
|
|
|
|
|
|
|
r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
|
|
|
|
if (r > 0) {
|
2016-11-18 17:04:26 +01:00
|
|
|
r = ordered_set_put_in4_addrv(dns, addresses, r);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
} else if (r < 0 && r != -ENODATA)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2016-01-25 21:47:02 +01:00
|
|
|
if (link->network->dhcp_use_ntp) {
|
2015-09-30 18:17:43 +02:00
|
|
|
const struct in_addr *addresses;
|
|
|
|
|
|
|
|
r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
|
|
|
|
if (r > 0) {
|
2016-11-18 17:04:26 +01:00
|
|
|
r = ordered_set_put_in4_addrv(ntp, addresses, r);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
} else if (r < 0 && r != -ENODATA)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2016-01-25 22:27:01 +01:00
|
|
|
if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
|
2015-09-30 18:17:43 +02:00
|
|
|
const char *domainname;
|
2017-05-13 16:19:32 +02:00
|
|
|
char **domains = NULL;
|
2015-09-30 18:17:43 +02:00
|
|
|
|
2017-05-13 16:19:32 +02:00
|
|
|
OrderedSet *target_domains = (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) ? search_domains : route_domains;
|
2015-09-30 18:17:43 +02:00
|
|
|
r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
|
|
|
|
if (r >= 0) {
|
2017-05-13 16:19:32 +02:00
|
|
|
r = ordered_set_put_strdup(target_domains, domainname);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
} else if (r != -ENODATA)
|
|
|
|
return r;
|
2016-01-25 22:27:01 +01:00
|
|
|
|
2017-05-13 16:19:32 +02:00
|
|
|
r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains);
|
|
|
|
if (r >= 0) {
|
|
|
|
r = ordered_set_put_strdupv(target_domains, domains);
|
2015-09-30 18:17:43 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
} else if (r != -ENODATA)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
operstate_str = link_operstate_to_string(operstate);
|
|
|
|
assert(operstate_str);
|
|
|
|
|
|
|
|
r = fopen_temporary(m->state_file, &f, &temp_path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-11-18 17:04:26 +01:00
|
|
|
(void) fchmod(fileno(f), 0644);
|
2015-09-30 18:17:43 +02:00
|
|
|
|
|
|
|
fprintf(f,
|
|
|
|
"# This is private data. Do not parse.\n"
|
|
|
|
"OPER_STATE=%s\n", operstate_str);
|
|
|
|
|
|
|
|
print_string_set(f, "DNS=", dns);
|
|
|
|
print_string_set(f, "NTP=", ntp);
|
networkd: rework Domains= setting
Previously, .network files only knew a vaguely defined "Domains=" concept, for which the documentation declared it was
the "DNS domain" for the network connection, without specifying what that means.
With this the Domains setting is reworked, so that there are now "routing" domains and "search" domains. The former are
to be used by resolved to route DNS request to specific network interfaces, the latter is to be used for searching
single-label hostnames with (in addition to being used for routing). Both settings are configured in the "Domains="
setting. Normal domain names listed in it are now considered search domains (for compatibility with existing setups),
while those prefixed with "~" are considered routing domains only. To route all lookups to a specific interface the
routing domain "." may be used, referring to the root domain. An alternative syntax for this is the "*", as was already
implemented before using the "wildcard" domain concept.
This commit adds proper parsers for this new logic, and exposes this via the sd-network API. This information is not
used by resolved yet, this will be added in a later commit.
2016-01-25 19:46:00 +01:00
|
|
|
print_string_set(f, "DOMAINS=", search_domains);
|
|
|
|
print_string_set(f, "ROUTE_DOMAINS=", route_domains);
|
2015-09-30 18:17:43 +02:00
|
|
|
|
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (rename(temp_path, m->state_file) < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->operational_state != operstate) {
|
|
|
|
m->operational_state = operstate;
|
|
|
|
r = manager_send_changed(m, "OperationalState", NULL);
|
|
|
|
if (r < 0)
|
|
|
|
log_error_errno(r, "Could not emit changed OperationalState: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
m->dirty = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
(void) unlink(m->state_file);
|
|
|
|
(void) unlink(temp_path);
|
|
|
|
|
|
|
|
return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int manager_dirty_handler(sd_event_source *s, void *userdata) {
|
|
|
|
Manager *m = userdata;
|
|
|
|
Link *link;
|
|
|
|
Iterator i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (m->dirty)
|
|
|
|
manager_save(m);
|
|
|
|
|
|
|
|
SET_FOREACH(link, m->dirty_links, i) {
|
|
|
|
r = link_save(link);
|
|
|
|
if (r >= 0)
|
|
|
|
link_clean(link);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-11-28 20:42:40 +01:00
|
|
|
int manager_new(Manager **ret, sd_event *event) {
|
2015-02-03 15:44:50 +01:00
|
|
|
_cleanup_manager_free_ Manager *m = NULL;
|
2014-12-08 19:54:06 +01:00
|
|
|
int r;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
m = new0(Manager, 1);
|
|
|
|
if (!m)
|
|
|
|
return -ENOMEM;
|
2014-12-08 19:54:06 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
m->state_file = strdup("/run/systemd/netif/state");
|
|
|
|
if (!m->state_file)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-11-28 20:42:40 +01:00
|
|
|
m->event = sd_event_ref(event);
|
2015-02-03 15:44:50 +01:00
|
|
|
|
2015-09-30 18:17:43 +02:00
|
|
|
r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = manager_connect_rtnl(m);
|
2014-12-08 19:54:06 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = manager_connect_udev(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-12-08 19:54:06 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
m->netdevs = hashmap_new(&string_hash_ops);
|
|
|
|
if (!m->netdevs)
|
|
|
|
return -ENOMEM;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
LIST_HEAD_INIT(m->networks);
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = setup_default_address_pool(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
networkd: rework duid_{type,duid_type,duid,duid_len} setting
Separate fields are replaced with a struct.
Second second duid type field is removed. The first field was used to carry
the result of DUIDType= configuration, and the second was either a copy of
this, or contained the type extracted from DuidRawData. The semantics are changed
so that the type specified in DUIDType is always used. DUIDRawData= no longer
overrides the type setting.
The networkd code is now more constrained than the sd-dhcp code:
DUIDRawData cannot have 0 length, length 0 is treated the same as unsetting.
Likewise, it is not possible to set a DUIDType=0. If it ever becomes necessary
to set type=0 or a zero-length duid, the code can be changed to support that.
Nevertheless, I think that's unlikely.
This addresses #3127 § 1 and 3.
v2:
- rename DUID.duid, DUID.duid_len to DUID.raw_data, DUID.raw_data_len
2016-04-29 05:23:45 +02:00
|
|
|
m->duid.type = DUID_TYPE_EN;
|
2016-03-31 01:33:55 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
*ret = m;
|
|
|
|
m = NULL;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
void manager_free(Manager *m) {
|
|
|
|
Network *network;
|
|
|
|
NetDev *netdev;
|
|
|
|
Link *link;
|
|
|
|
AddressPool *pool;
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
if (!m)
|
|
|
|
return;
|
2014-04-15 14:21:44 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
free(m->state_file);
|
2014-04-15 14:21:44 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
while ((link = hashmap_first(m->links)))
|
|
|
|
link_unref(link);
|
|
|
|
hashmap_free(m->links);
|
2013-10-17 03:18:36 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
while ((network = m->networks))
|
|
|
|
network_free(network);
|
|
|
|
|
2015-02-08 13:29:35 +01:00
|
|
|
hashmap_free(m->networks_by_name);
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
while ((netdev = hashmap_first(m->netdevs)))
|
|
|
|
netdev_unref(netdev);
|
|
|
|
hashmap_free(m->netdevs);
|
|
|
|
|
|
|
|
while ((pool = m->address_pools))
|
|
|
|
address_pool_free(pool);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_unref(m->rtnl);
|
2015-08-27 17:38:05 +02:00
|
|
|
sd_event_unref(m->event);
|
2015-02-03 15:44:50 +01:00
|
|
|
|
2015-10-09 21:36:04 +02:00
|
|
|
sd_event_source_unref(m->udev_event_source);
|
|
|
|
udev_monitor_unref(m->udev_monitor);
|
|
|
|
udev_unref(m->udev);
|
|
|
|
|
|
|
|
sd_bus_unref(m->bus);
|
|
|
|
sd_bus_slot_unref(m->prepare_for_sleep_slot);
|
|
|
|
sd_event_source_unref(m->bus_retry_event_source);
|
|
|
|
|
2016-11-22 08:36:20 +01:00
|
|
|
free(m->dynamic_timezone);
|
|
|
|
free(m->dynamic_hostname);
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
free(m);
|
|
|
|
}
|
|
|
|
|
2016-11-28 20:42:40 +01:00
|
|
|
int manager_start(Manager *m) {
|
2015-09-30 18:17:43 +02:00
|
|
|
Link *link;
|
|
|
|
Iterator i;
|
|
|
|
|
2015-02-04 15:00:20 +01:00
|
|
|
assert(m);
|
|
|
|
|
2015-09-30 18:17:43 +02:00
|
|
|
/* The dirty handler will deal with future serialization, but the first one
|
|
|
|
must be done explicitly. */
|
|
|
|
|
|
|
|
manager_save(m);
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(link, m->links, i)
|
|
|
|
link_save(link);
|
|
|
|
|
2016-11-28 20:42:40 +01:00
|
|
|
return 0;
|
2015-02-04 15:00:20 +01:00
|
|
|
}
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
int manager_load_config(Manager *m) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
/* update timestamp */
|
|
|
|
paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true);
|
|
|
|
|
|
|
|
r = netdev_load(m);
|
2013-10-17 03:18:36 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = network_load(m);
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-10-17 03:18:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2013-11-14 16:22:51 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
bool manager_should_reload(Manager *m) {
|
|
|
|
return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
int manager_rtnl_enumerate_links(Manager *m) {
|
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, *reply = NULL;
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_message *link;
|
2013-11-14 16:22:51 +01:00
|
|
|
int r;
|
|
|
|
|
2014-07-01 10:09:52 +02:00
|
|
|
assert(m);
|
2015-02-03 15:44:50 +01:00
|
|
|
assert(m->rtnl);
|
2014-07-01 10:09:52 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
|
2013-11-14 16:22:51 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_request_dump(req, true);
|
2013-12-03 18:48:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_call(m->rtnl, req, 0, &reply);
|
2014-05-08 18:55:11 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
for (link = reply; link; link = sd_netlink_message_next(link)) {
|
2015-02-03 15:44:50 +01:00
|
|
|
int k;
|
2014-05-10 19:39:03 +02:00
|
|
|
|
2015-02-04 10:08:12 +01:00
|
|
|
m->enumerating = true;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
k = manager_rtnl_process_link(m->rtnl, link, m);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
2015-02-04 10:08:12 +01:00
|
|
|
|
|
|
|
m->enumerating = false;
|
2015-02-03 15:44:50 +01:00
|
|
|
}
|
2014-05-10 19:39:03 +02:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
return r;
|
2013-11-14 16:22:51 +01:00
|
|
|
}
|
2014-01-05 23:01:10 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
int manager_rtnl_enumerate_addresses(Manager *m) {
|
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, *reply = NULL;
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_message *addr;
|
2014-01-13 23:48:28 +01:00
|
|
|
int r;
|
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
assert(m);
|
|
|
|
assert(m->rtnl);
|
2014-01-18 01:37:35 +01:00
|
|
|
|
2015-02-03 15:44:50 +01:00
|
|
|
r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-01-18 01:37:35 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_request_dump(req, true);
|
2014-01-13 23:48:28 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_call(m->rtnl, req, 0, &reply);
|
2015-02-03 15:44:50 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
for (addr = reply; addr; addr = sd_netlink_message_next(addr)) {
|
2015-02-03 15:44:50 +01:00
|
|
|
int k;
|
|
|
|
|
2015-02-04 10:08:12 +01:00
|
|
|
m->enumerating = true;
|
|
|
|
|
2015-09-22 17:18:20 +02:00
|
|
|
k = manager_rtnl_process_address(m->rtnl, addr, m);
|
2015-02-03 15:44:50 +01:00
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
2015-02-04 10:08:12 +01:00
|
|
|
|
|
|
|
m->enumerating = false;
|
2015-02-03 15:44:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
2014-01-13 23:48:28 +01:00
|
|
|
}
|
|
|
|
|
2015-10-25 14:46:21 +01:00
|
|
|
int manager_rtnl_enumerate_routes(Manager *m) {
|
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, *reply = NULL;
|
2015-10-25 14:46:21 +01:00
|
|
|
sd_netlink_message *route;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(m->rtnl);
|
|
|
|
|
|
|
|
r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_netlink_message_request_dump(req, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_netlink_call(m->rtnl, req, 0, &reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
for (route = reply; route; route = sd_netlink_message_next(route)) {
|
|
|
|
int k;
|
|
|
|
|
|
|
|
m->enumerating = true;
|
|
|
|
|
|
|
|
k = manager_rtnl_process_route(m->rtnl, route, m);
|
|
|
|
if (k < 0)
|
|
|
|
r = k;
|
|
|
|
|
|
|
|
m->enumerating = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-07-18 16:09:30 +02:00
|
|
|
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
|
2014-06-18 18:22:14 +02:00
|
|
|
AddressPool *p;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(prefixlen > 0);
|
|
|
|
assert(found);
|
|
|
|
|
|
|
|
LIST_FOREACH(address_pools, p, m->address_pools) {
|
|
|
|
if (p->family != family)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = address_pool_acquire(p, prefixlen, found);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-08-27 16:45:24 +02:00
|
|
|
|
|
|
|
Link* manager_find_uplink(Manager *m, Link *exclude) {
|
|
|
|
_cleanup_free_ struct local_address *gateways = NULL;
|
|
|
|
int n, i;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
/* Looks for a suitable "uplink", via black magic: an
|
|
|
|
* interface that is up and where the default route with the
|
|
|
|
* highest priority points to. */
|
|
|
|
|
|
|
|
n = local_gateways(m->rtnl, 0, AF_UNSPEC, &gateways);
|
|
|
|
if (n < 0) {
|
|
|
|
log_warning_errno(n, "Failed to determine list of default gateways: %m");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
Link *link;
|
|
|
|
|
|
|
|
link = hashmap_get(m->links, INT_TO_PTR(gateways[i].ifindex));
|
|
|
|
if (!link) {
|
2015-08-27 20:23:17 +02:00
|
|
|
log_debug("Weird, found a gateway for a link we don't know. Ignoring.");
|
2015-08-27 16:45:24 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (link == exclude)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (link->operstate < LINK_OPERSTATE_ROUTABLE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return link;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-09-30 18:17:43 +02:00
|
|
|
|
|
|
|
void manager_dirty(Manager *manager) {
|
|
|
|
assert(manager);
|
|
|
|
|
|
|
|
/* the serialized state in /run is no longer up-to-date */
|
|
|
|
manager->dirty = true;
|
|
|
|
}
|
2016-11-21 23:15:41 +01:00
|
|
|
|
|
|
|
static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
|
|
|
|
Manager *manager = userdata;
|
|
|
|
const sd_bus_error *e;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(manager);
|
|
|
|
|
|
|
|
e = sd_bus_message_get_error(m);
|
|
|
|
if (e)
|
|
|
|
log_warning_errno(sd_bus_error_get_errno(e), "Could not set hostname: %s", e->message);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int manager_set_hostname(Manager *m, const char *hostname) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
log_debug("Setting transient hostname: '%s'", strna(hostname));
|
2016-11-22 08:36:20 +01:00
|
|
|
if (free_and_strdup(&m->dynamic_hostname, hostname) < 0)
|
|
|
|
return log_oom();
|
2016-11-21 23:15:41 +01:00
|
|
|
|
|
|
|
if (!m->bus) {
|
|
|
|
/* TODO: replace by assert when we can rely on kdbus */
|
|
|
|
log_info("Not connected to system bus, ignoring transient hostname.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_call_method_async(
|
|
|
|
m->bus,
|
|
|
|
NULL,
|
|
|
|
"org.freedesktop.hostname1",
|
|
|
|
"/org/freedesktop/hostname1",
|
|
|
|
"org.freedesktop.hostname1",
|
|
|
|
"SetHostname",
|
|
|
|
set_hostname_handler,
|
|
|
|
m,
|
|
|
|
"sb",
|
|
|
|
hostname,
|
|
|
|
false);
|
|
|
|
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not set transient hostname: %m");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_timezone_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
|
|
|
|
Manager *manager = userdata;
|
|
|
|
const sd_bus_error *e;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(manager);
|
|
|
|
|
|
|
|
e = sd_bus_message_get_error(m);
|
|
|
|
if (e)
|
|
|
|
log_warning_errno(sd_bus_error_get_errno(e), "Could not set timezone: %s", e->message);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int manager_set_timezone(Manager *m, const char *tz) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(tz);
|
|
|
|
|
|
|
|
log_debug("Setting system timezone: '%s'", tz);
|
2016-11-22 08:36:20 +01:00
|
|
|
if (free_and_strdup(&m->dynamic_timezone, tz) < 0)
|
|
|
|
return log_oom();
|
2016-11-21 23:15:41 +01:00
|
|
|
|
|
|
|
if (!m->bus) {
|
|
|
|
log_info("Not connected to system bus, ignoring timezone.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_call_method_async(
|
|
|
|
m->bus,
|
|
|
|
NULL,
|
|
|
|
"org.freedesktop.timedate1",
|
|
|
|
"/org/freedesktop/timedate1",
|
|
|
|
"org.freedesktop.timedate1",
|
|
|
|
"SetTimezone",
|
|
|
|
set_timezone_handler,
|
|
|
|
m,
|
|
|
|
"sb",
|
|
|
|
tz,
|
|
|
|
false);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Could not set timezone: %m");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|