From f579559b3a14c1f1ef96c372e7626c4733e6ef7d Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Thu, 17 Oct 2013 03:18:36 +0200 Subject: [PATCH 1/1] networkd: add a basic network daemon This daemon listens for and configures network devices tagged with 'systemd-networkd'. By default, no devices are tagged so this daemon can safely run in parallel with existing network daemons/scripts. Networks are configured in /etc/systemd/network/*.network. The first .network file that matches a given link is applied. The matching logic is similar to the one for .link files, but additionally supports matching on interface name. The mid-term aim is to provide an alternative to ad-hoc scripts currently used in initrd's and for wired setups that don't change much (e.g., as seen on servers/and some embedded systems). Currently, static addresses and a gateway can be configured. Example .network file: [Match] Name=wlp2s0 [Network] Description=My Network Gateway=192.168.1.1 Address=192.168.1.23/24 Address=fe80::9aee:94ff:fe3f:c618/64 --- .gitignore | 2 + Makefile.am | 54 ++++++++ src/network/.gitignore | 1 + src/network/Makefile | 1 + src/network/networkd-address.c | 169 ++++++++++++++++++++++++ src/network/networkd-gperf.gperf | 25 ++++ src/network/networkd-link.c | 119 +++++++++++++++++ src/network/networkd-manager.c | 210 ++++++++++++++++++++++++++++++ src/network/networkd-network.c | 186 ++++++++++++++++++++++++++ src/network/networkd-route.c | 129 ++++++++++++++++++ src/network/networkd.c | 58 +++++++++ src/network/networkd.h | 170 ++++++++++++++++++++++++ src/network/test-network.c | 74 +++++++++++ units/.gitignore | 1 + units/systemd-networkd.service.in | 19 +++ 15 files changed, 1218 insertions(+) create mode 100644 src/network/.gitignore create mode 120000 src/network/Makefile create mode 100644 src/network/networkd-address.c create mode 100644 src/network/networkd-gperf.gperf create mode 100644 src/network/networkd-link.c create mode 100644 src/network/networkd-manager.c create mode 100644 src/network/networkd-network.c create mode 100644 src/network/networkd-route.c create mode 100644 src/network/networkd.c create mode 100644 src/network/networkd.h create mode 100644 src/network/test-network.c create mode 100644 units/systemd-networkd.service.in diff --git a/.gitignore b/.gitignore index 00d81ed3d..cf9bb6d7d 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ /systemd-machined /systemd-modules-load /systemd-multi-seat-x +/systemd-networkd /systemd-notify /systemd-nspawn /systemd-quotacheck @@ -133,6 +134,7 @@ /test-loopback /test-mmap-cache /test-namespace +/test-network /test-ns /test-path-util /test-prioq diff --git a/Makefile.am b/Makefile.am index dcc0a72d8..e6d572320 100644 --- a/Makefile.am +++ b/Makefile.am @@ -177,6 +177,7 @@ AM_CPPFLAGS = \ -DKEXEC=\"$(KEXEC)\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/shared \ + -I $(top_srcdir)/src/network \ -I $(top_srcdir)/src/login \ -I $(top_srcdir)/src/journal \ -I $(top_srcdir)/src/systemd \ @@ -3770,6 +3771,59 @@ EXTRA_DIST += \ endif +# ------------------------------------------------------------------------------ +rootlibexec_PROGRAMS += \ + systemd-networkd + +systemd_networkd_SOURCES = \ + src/network/networkd.h \ + src/network/networkd.c \ + src/network/networkd-link.c \ + src/network/networkd-network.c \ + src/network/networkd-address.c \ + src/network/networkd-route.c \ + src/network/networkd-manager.c + +nodist_systemd_networkd_SOURCES = \ + src/network/networkd-gperf.c + +systemd_networkd_LDADD = \ + libudev-internal.la \ + libsystemd-bus.la \ + libsystemd-rtnl.la \ + libsystemd-shared.la + +nodist_systemunit_DATA += \ + units/systemd-networkd.service + +MULTI_USER_TARGET_WANTS += \ + systemd-networkd.service + +test_network_SOURCES = \ + src/network/test-network.c \ + src/network/networkd.h \ + src/network/networkd-link.c \ + src/network/networkd-network.c \ + src/network/networkd-address.c \ + src/network/networkd-route.c \ + src/network/networkd-manager.c \ + src/network/networkd-gperf.c + +test_network_LDADD = \ + libudev-internal.la \ + libsystemd-bus.la \ + libsystemd-rtnl.la \ + libsystemd-shared.la + +tests += test-network + +EXTRA_DIST += \ + src/network/networkd-gperf.gperf \ + units/systemd-networkd.service.in + +CLEANFILES += \ + src/network/networkd-gperf.c + # ------------------------------------------------------------------------------ if ENABLE_LOGIND systemd_logind_SOURCES = \ diff --git a/src/network/.gitignore b/src/network/.gitignore new file mode 100644 index 000000000..b2a8c1ae1 --- /dev/null +++ b/src/network/.gitignore @@ -0,0 +1 @@ +/networkd-gperf.c diff --git a/src/network/Makefile b/src/network/Makefile new file mode 120000 index 000000000..d0b0e8e00 --- /dev/null +++ b/src/network/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c new file mode 100644 index 000000000..9a7106e83 --- /dev/null +++ b/src/network/networkd-address.c @@ -0,0 +1,169 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include + +#include "networkd.h" + +#include "utf8.h" +#include "util.h" +#include "conf-parser.h" +#include "net-util.h" + +int address_new(Network *network, Address **ret) { + _cleanup_address_free_ Address *address = NULL; + + address = new0(Address, 1); + if (!address) + return -ENOMEM; + + address->network = network; + + LIST_PREPEND(addresses, network->addresses, address); + + *ret = address; + address = NULL; + + return 0; +} + +void address_free(Address *address) { + if (!address) + return; + + LIST_REMOVE(addresses, address->network->addresses, address); + + free(address->label); + free(address); +} + +int address_configure(Manager *manager, Address *address, Link *link) { + _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL; + int r; + + r = sd_rtnl_message_addr_new(RTM_NEWADDR, link->ifindex, + address->family, address->prefixlen, + IFA_F_PERMANENT, RT_SCOPE_UNIVERSE, &req); + if (r < 0) { + log_error("Could not allocate RTM_NEWADDR message: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_append(req, IFA_LOCAL, &address->in_addr); + if (r < 0) { + log_error("Could not append IFA_LOCAL attribute: %s", + strerror(-r)); + return r; + } + + if (address->family == AF_INET) { + struct in_addr broadcast; + + broadcast.s_addr = address->in_addr.in.s_addr | + htonl(0xfffffffflu >> address->prefixlen); + + r = sd_rtnl_message_append(req, IFA_BROADCAST, &broadcast); + if (r < 0) { + log_error("Could not append IFA_BROADCAST attribute: %s", + strerror(-r)); + return r; + } + } + + if (address->label) { + r = sd_rtnl_message_append(req, IFA_LABEL, address->label); + if (r < 0) { + log_error("Could not append IFA_LABEL attribute: %s", + strerror(-r)); + return r; + } + } + + r = sd_rtnl_send_with_reply_and_block(manager->rtnl, req, 0, NULL); + if (r < 0) { + log_error("Could not configure address: %s", strerror(-r)); + return r != -EEXIST ? r : 0; + } + + log_info("Configured interface address"); + + return 0; +} + +int config_parse_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + _cleanup_address_free_ Address *n = NULL; + _cleanup_free_ char *address = NULL; + const char *e; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new(userdata, &n); + if (r < 0) + return r; + + /* Address=address/prefixlen */ + + /* prefixlen */ + e = strchr(rvalue, '/'); + if (e) { + unsigned i; + r = safe_atou(e + 1, &i); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Interface prefix length is invalid, " + "ignoring assignment: %s", e + 1); + return 0; + } + + n->prefixlen = (unsigned char) i; + address = strndup(rvalue, e - rvalue); + if (!address) + return log_oom(); + } else { + address = strdup(rvalue); + if (!address) + return log_oom(); + } + + r = net_parse_inaddr(address, &n->family, &n->in_addr); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Address is invalid, ignoring assignment: %s", address); + return 0; + } + + n = NULL; + + return 0; +} diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf new file mode 100644 index 000000000..a0ca6aa7a --- /dev/null +++ b/src/network/networkd-gperf.gperf @@ -0,0 +1,25 @@ +%{ +#include +#include "conf-parser.h" +#include "networkd.h" +#include "net-util.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name network_gperf_hash +%define lookup-function-name network_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Match.MACAddress, config_parse_hwaddr, 0, offsetof(Network, match_mac) +Match.Path, config_parse_string, 0, offsetof(Network, match_path) +Match.Driver, config_parse_string, 0, offsetof(Network, match_driver) +Match.Type, config_parse_string, 0, offsetof(Network, match_type) +Match.Name, config_parse_ifname, 0, offsetof(Network, match_name) +Network.Description, config_parse_string, 0, offsetof(Network, description) +Network.Address, config_parse_address, 0, 0 +Network.Gateway, config_parse_gateway, 0, 0 diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c new file mode 100644 index 000000000..486d4de2c --- /dev/null +++ b/src/network/networkd-link.c @@ -0,0 +1,119 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include +#include + +#include "networkd.h" +#include "libudev-private.h" +#include "util.h" + +int link_new(Manager *manager, struct udev_device *device, Link **ret) { + _cleanup_link_free_ Link *link = NULL; + uint64_t ifindex; + int r; + + assert(device); + assert(ret); + + link = new0(Link, 1); + if (!link) + return -ENOMEM; + + ifindex = udev_device_get_ifindex(device); + if (ifindex <= 0) + return -EINVAL; + + link->ifindex = ifindex; + link->manager = manager; + + r = hashmap_put(manager->links, &ifindex, link); + if (r < 0) + return r; + + *ret = link; + link = NULL; + + return 0; +} + +void link_free(Link *link) { + if (!link) + return; + + network_free(link->network); + + hashmap_remove(link->manager->links, link); + + free(link); +} + +int link_add(Manager *m, struct udev_device *device) { + Link *link; + Network *network; + int r; + uint64_t ifindex; + + assert(m); + assert(device); + + ifindex = udev_device_get_ifindex(device); + link = hashmap_get(m->links, &ifindex); + if (link) + return 0; + + r = link_new(m, device, &link); + if (r < 0) { + log_error("could not create link: %s", strerror(-r)); + return r; + } + + r = network_get(m, device, &network); + if (r < 0) + return r == -ENOENT ? 0 : r; + + r = network_apply(m, network, link); + if (r < 0) + return r; + + return 0; +} + +int link_up(Manager *manager, Link *link) { + _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL; + int r; + + r = sd_rtnl_message_link_new(RTM_NEWLINK, link->ifindex, 0, IFF_UP, &req); + if (r < 0) { + log_error("Could not allocate RTM_NEWLINK message"); + return r; + } + + r = sd_rtnl_send_with_reply_and_block(manager->rtnl, req, 0, NULL); + if (r < 0) { + log_error("Could not UP link: %s", strerror(-r)); + return r; + } + + log_info("Link is UP"); + + return 0; +} diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c new file mode 100644 index 000000000..6a57a8014 --- /dev/null +++ b/src/network/networkd-manager.c @@ -0,0 +1,210 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . + ***/ + +#include "path-util.h" +#include "networkd.h" +#include "libudev-private.h" + +int manager_new(Manager **ret) { + _cleanup_manager_free_ Manager *m = NULL; + int r; + + m = new0(Manager, 1); + if (!m) + return -ENOMEM; + + r = sd_event_new(&m->event); + if (r < 0) + return r; + + r = sd_rtnl_open(0, &m->rtnl); + if (r < 0) + return r; + + m->udev = udev_new(); + if (!m->udev) + return -ENOMEM; + + m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_monitor) + return -ENOMEM; + + m->links = hashmap_new(uint64_hash_func, uint64_compare_func); + if (!m->links) + return -ENOMEM; + + LIST_HEAD_INIT(m->networks); + + m->network_dirs = strv_new("/etc/systemd/network/", + "/run/systemd/network/", + "/usr/lib/systemd/network", +#ifdef HAVE_SPLIT_USER + "/lib/systemd/network", +#endif + NULL); + if (!m->network_dirs) + return -ENOMEM; + + if (!path_strv_canonicalize_uniq(m->network_dirs)) + return -ENOMEM; + + *ret = m; + m = NULL; + + return 0; +} + +void manager_free(Manager *m) { + udev_monitor_unref(m->udev_monitor); + udev_unref(m->udev); + sd_event_source_unref(m->udev_event_source); + sd_event_unref(m->event); + hashmap_free(m->links); + strv_free(m->network_dirs); + sd_rtnl_unref(m->rtnl); + + free(m); +} + +static int manager_process_link(Manager *m, struct udev_device *device) { + Link *link; + int r; + + if (streq_ptr(udev_device_get_action(device), "remove")) { + uint64_t ifindex; + + ifindex = udev_device_get_ifindex(device); + link = hashmap_get(m->links, &ifindex); + if (!link) + return 0; + + link_free(link); + } else { + r = link_add(m, device); + if (r < 0) { + log_error("Could not handle link %s: %s", + udev_device_get_sysname(device), + strerror(-r)); + } + } + + return 0; +} + +int manager_udev_enumerate_links(Manager *m) { + struct udev_list_entry *item = NULL, *first = NULL; + struct udev_enumerate *e; + int r; + + assert(m); + + e = udev_enumerate_new(m->udev); + if (!e) { + r = -ENOMEM; + goto finish; + } + + r = udev_enumerate_add_match_subsystem(e, "net"); + if (r < 0) + goto finish; + + r = udev_enumerate_add_match_tag(e, "systemd-networkd"); + if (r < 0) + goto finish; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + goto finish; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + struct udev_device *d; + int k; + + d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); + if (!d) { + r = -ENOMEM; + goto finish; + } + + k = manager_process_link(m, d); + udev_device_unref(d); + + if (k < 0) + r = k; + } + +finish: + if (e) + udev_enumerate_unref(e); + + return r; +} + +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; + struct udev_device *device; + int r; + + device = udev_monitor_receive_device(monitor); + if (!device) + return -ENOMEM; + + r = manager_process_link(m, device); + if (r < 0) + return r; + + udev_device_unref(device); + + return 0; +} + +int manager_udev_listen(Manager *m) { + int r; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL); + if (r < 0) { + log_error("Could not add udev monitor filter: %s", strerror(-r)); + return r; + } + + r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd-networkd"); + if (r < 0) { + log_error("Could not add udev monitor filter: %s", strerror(-r)); + return r; + } + + r = udev_monitor_enable_receiving(m->udev_monitor); + if (r < 0) { + log_error("Could not enable udev monitor"); + return r; + } + + r = sd_event_add_io(m->event, + udev_monitor_get_fd(m->udev_monitor), + EPOLLIN, manager_dispatch_link_udev, + m, &m->udev_event_source); + if (r < 0) + return r; + + return 0; +} diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c new file mode 100644 index 000000000..69c55edb8 --- /dev/null +++ b/src/network/networkd-network.c @@ -0,0 +1,186 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include "networkd.h" +#include "net-util.h" +#include "path-util.h" +#include "conf-files.h" +#include "conf-parser.h" + +static int network_load_one(Manager *manager, const char *filename) { + _cleanup_network_free_ Network *network = NULL; + _cleanup_fclose_ FILE *file = NULL; + int r; + + file = fopen(filename, "re"); + if (!file) { + if (errno == ENOENT) + return 0; + else + return errno; + } + + network = new0(Network, 1); + if (!network) + return log_oom(); + + LIST_HEAD_INIT(network->addresses); + + r = config_parse(NULL, filename, file, "Match\0Network\0", config_item_perf_lookup, + (void*) network_gperf_lookup, false, false, network); + if (r < 0) { + log_warning("Could not parse config file %s: %s", filename, strerror(-r)); + return r; + } else + log_debug("Parsed configuration file %s", filename); + + network->filename = strdup(filename); + if (!network->filename) + return log_oom(); + + LIST_PREPEND(networks, manager->networks, network); + network = NULL; + + return 0; +} + +int network_load(Manager *manager) { + Network *network; + char **files, **f; + int r; + + assert(manager); + + while ((network = manager->networks)) + network_free(network); + + /* update timestamp */ + paths_check_timestamp(manager->network_dirs, &manager->network_dirs_ts_usec, true); + + r = conf_files_list_strv(&files, ".network", NULL, (const char **)manager->network_dirs); + if (r < 0) { + log_error("failed to enumerate network files: %s", strerror(-r)); + return r; + } + + STRV_FOREACH_BACKWARDS(f, files) { + r = network_load_one(manager, *f); + if (r < 0) + return r; + } + + strv_free(files); + + return 0; +} + +bool network_should_reload(Manager *manager) { + return paths_check_timestamp(manager->network_dirs, &manager->network_dirs_ts_usec, false); +} + +void network_free(Network *network) { + Route *route; + Address *address; + + if (!network) + return; + + free(network->filename); + + free(network->match_mac); + free(network->match_path); + free(network->match_driver); + free(network->match_type); + free(network->match_name); + + free(network->description); + + while ((route = network->routes)) + route_free(route); + + while ((address = network->addresses)) + address_free(address); + + LIST_REMOVE(networks, network->manager->networks, network); + + free(network); +} + +int network_get(Manager *manager, struct udev_device *device, Network **ret) { + Network *network; + + assert(manager); + assert(device); + assert(ret); + + if (network_should_reload(manager)) + network_load(manager); + + LIST_FOREACH(networks, network, manager->networks) { + if (net_match_config(network->match_mac, network->match_path, + network->match_driver, network->match_type, + network->match_name, + udev_device_get_sysattr_value(device, "address"), + udev_device_get_property_value(device, "ID_PATH"), + udev_device_get_driver(device), + udev_device_get_devtype(device), + udev_device_get_sysname(device))) { + log_debug("Network file %s applies to link %s", + network->filename, + udev_device_get_sysname(device)); + *ret = network; + return 0; + } + } + + *ret = NULL; + + return -ENOENT; +} + +int network_apply(Manager *manager, Network *network, Link *link) { + Address *address; + Route *route; + int r; + + log_info("Network '%s' being applied to link '%u'", + network->description, (unsigned) link->ifindex); + + link->network = network; + + LIST_FOREACH(addresses, address, network->addresses) { + r = address_configure(manager, address, link); + if (r < 0) + return r; + } + + r = link_up(manager, link); + if (r < 0) + return r; + + LIST_FOREACH(routes, route, network->routes) { + r = route_configure(manager, route, link); + if (r < 0) + return r; + } + + return 0; +} diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c new file mode 100644 index 000000000..918a1d019 --- /dev/null +++ b/src/network/networkd-route.c @@ -0,0 +1,129 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include + +#include "networkd.h" + +#include "utf8.h" +#include "util.h" +#include "conf-parser.h" +#include "net-util.h" + +int route_new(Network *network, Route **ret) { + _cleanup_route_free_ Route *route = NULL; + + route = new0(Route, 1); + if (!route) + return -ENOMEM; + + route->network = network; + + LIST_PREPEND(routes, network->routes, route); + + *ret = route; + route = NULL; + + return 0; +} + +void route_free(Route *route) { + if (!route) + return; + + LIST_REMOVE(routes, route->network->routes, route); + + free(route); +} + +int route_configure(Manager *manager, Route *route, Link *link) { + _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL; + int r; + + assert(manager); + assert(link); + assert(link->ifindex > 0); + assert(route->family == AF_INET || route->family == AF_INET6); + + r = sd_rtnl_message_route_new(RTM_NEWROUTE, route->family, 0, 0, 0, + RT_TABLE_MAIN, RT_SCOPE_UNIVERSE, RTPROT_BOOT, + RTN_UNICAST, 0, &req); + if (r < 0) { + log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r)); + return r; + } + + r = sd_rtnl_message_append(req, RTA_GATEWAY, &route->in_addr); + if (r < 0) { + log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r)); + return r; + } + + r = sd_rtnl_message_append(req, RTA_OIF, &link->ifindex); + if (r < 0) { + log_error("Could not append RTA_OIF attribute: %s", strerror(-r)); + return r; + } + + r = sd_rtnl_send_with_reply_and_block(manager->rtnl, req, 0, NULL); + if (r < 0) { + log_error("Could not configure route: %s", strerror(-r)); + return r; + } + + log_info("Configured route"); + + return 0; +} + +int config_parse_gateway(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + _cleanup_route_free_ Route *n = NULL; + _cleanup_free_ char *route = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new(userdata, &n); + if (r < 0) + return r; + + r = net_parse_inaddr(rvalue, &n->family, &n->in_addr); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Route is invalid, ignoring assignment: %s", route); + return 0; + } + + n = NULL; + + return 0; +} diff --git a/src/network/networkd.c b/src/network/networkd.c new file mode 100644 index 000000000..ed93ffb3f --- /dev/null +++ b/src/network/networkd.c @@ -0,0 +1,58 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include "sd-event.h" + +#include "networkd.h" + +int main(int argc, char *argv[]) { + _cleanup_manager_free_ Manager *m; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc != 1) { + log_error("This program takes no arguments."); + return EXIT_FAILURE; + } + + r = manager_new(&m); + if (r < 0) + return EXIT_FAILURE; + + r = manager_udev_listen(m); + if (r < 0) + return EXIT_FAILURE; + + r = manager_udev_enumerate_links(m); + if (r < 0) + return EXIT_FAILURE; + + r = sd_event_loop(m->event); + if (r < 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/src/network/networkd.h b/src/network/networkd.h new file mode 100644 index 000000000..fd3b60a6c --- /dev/null +++ b/src/network/networkd.h @@ -0,0 +1,170 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#pragma once + +#include +#include + +#include "sd-event.h" +#include "sd-rtnl.h" +#include "udev.h" + +#include "rtnl-util.h" +#include "hashmap.h" +#include "list.h" + +typedef struct Network Network; +typedef struct Link Link; +typedef struct Address Address; +typedef struct Route Route; +typedef struct Manager Manager; + +struct Network { + Manager *manager; + + char *filename; + + struct ether_addr *match_mac; + char *match_path; + char *match_driver; + char *match_type; + char *match_name; + + char *description; + + LIST_HEAD(Address, addresses); + LIST_HEAD(Route, routes); + + LIST_FIELDS(Network, networks); +}; + +struct Address { + Network *network; + + unsigned char family; + unsigned char prefixlen; + char *label; + + union { + struct in_addr in; + struct in6_addr in6; + } in_addr; + + LIST_FIELDS(Address, addresses); +}; + +struct Route { + Network *network; + + unsigned char family; + + union { + struct in_addr in; + struct in6_addr in6; + } in_addr; + + LIST_FIELDS(Route, routes); +}; + +struct Link { + Manager *manager; + + int ifindex; + + unsigned flags; + + Network *network; +}; + +struct Manager { + sd_rtnl *rtnl; + sd_event *event; + struct udev *udev; + struct udev_monitor *udev_monitor; + sd_event_source *udev_event_source; + + Hashmap *links; + LIST_HEAD(Network, networks); + + char **network_dirs; + usec_t network_dirs_ts_usec; +}; + +/* Manager */ + +int manager_new(Manager **ret); +void manager_free(Manager *m); + +int manager_udev_enumerate_links(Manager *m); +int manager_udev_listen(Manager *m); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); +#define _cleanup_manager_free_ _cleanup_(manager_freep) + +/* Network */ + +int network_load(Manager *manager); +bool network_should_reload(Manager *manager); + +void network_free(Network *network); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free); +#define _cleanup_network_free_ _cleanup_(network_freep) + +int network_get(Manager *manager, struct udev_device *device, Network **ret); +int network_apply(Manager *manager, Network *network, Link *link); + +const struct ConfigPerfItem* network_gperf_lookup(const char *key, unsigned length); + +/* Route */ +int route_new(Network *network, Route **ret); +void route_free(Route *route); +int route_configure(Manager *manager, Route *route, Link *link); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free); +#define _cleanup_route_free_ _cleanup_(route_freep) + +int config_parse_gateway(const char *unit, const char *filename, unsigned line, + const char *section, const char *lvalue, int ltype, + const char *rvalue, void *data, void *userdata); + +/* Address */ +int address_new(Network *network, Address **ret); +void address_free(Address *address); +int address_configure(Manager *manager, Address *address, Link *link); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); +#define _cleanup_address_free_ _cleanup_(address_freep) + +int config_parse_address(const char *unit, const char *filename, unsigned line, + const char *section, const char *lvalue, int ltype, + const char *rvalue, void *data, void *userdata); + +/* Link */ + +int link_new(Manager *manager, struct udev_device *device, Link **ret); +void link_free(Link *link); +int link_add(Manager *manager, struct udev_device *device); +int link_up(Manager *manager, Link *link); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); +#define _cleanup_link_free_ _cleanup_(link_freep) diff --git a/src/network/test-network.c b/src/network/test-network.c new file mode 100644 index 000000000..299f0fa32 --- /dev/null +++ b/src/network/test-network.c @@ -0,0 +1,74 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + 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 . +***/ + +#include "networkd.h" + +static void test_link(struct udev_device *loopback) { + _cleanup_manager_free_ Manager *manager = NULL; + _cleanup_link_free_ Link *link = NULL; + + manager_new(&manager); + + assert(link_new(manager, loopback, &link) >= 0); + assert(link); +} + +static void test_network_load(Manager *manager) { + assert(network_should_reload(manager) == true); + assert(network_load(manager) >= 0); + assert(network_should_reload(manager) == false); +} + +static void test_network_get(Manager *manager, struct udev_device *loopback) { + Network *network; + + /* let's assume that the test machine does not have a .network file + that applies to the loopback device... */ + assert(network_get(manager, loopback, &network) == -ENOENT); + assert(!network); +} + +int main(void) { + _cleanup_manager_free_ Manager *manager = NULL; + struct udev *udev; + struct udev_device *loopback; + + assert(manager_new(&manager) >= 0); + + test_network_load(manager); + + udev = udev_new(); + assert(udev); + + loopback = udev_device_new_from_syspath(udev, "/sys/class/net/lo"); + assert(loopback); + assert(udev_device_get_ifindex(loopback) == 1); + + test_network_get(manager, loopback); + + test_link(loopback); + + assert(manager_udev_enumerate_links(manager) >= 0); + assert(manager_udev_listen(manager) >= 0); + + udev_device_unref(loopback); + udev_unref(udev); +} diff --git a/units/.gitignore b/units/.gitignore index 5fd9ccadb..c5ac8fe72 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -60,6 +60,7 @@ /initrd-parse-etc.service /initrd-switch-root.service /initrd-udevadm-cleanup-db.service +/systemd-networkd.service /systemd-nspawn@.service /systemd-machined.service /kmod-static-nodes.service diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in new file mode 100644 index 000000000..52c04ed1f --- /dev/null +++ b/units/systemd-networkd.service.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# 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. + +[Unit] +Description=Network Service +Documentation=man:systemd-networkd.service(8) +DefaultDependencies=no +After=sysinit.target +Before=network.target +Wants=network.target + +[Service] +Restart=always +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-networkd -- 2.30.2