summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2013-10-17 03:18:36 +0200
committerTom Gundersen <teg@jklm.no>2013-11-09 23:41:17 +0100
commitf579559b3a14c1f1ef96c372e7626c4733e6ef7d (patch)
treee3e3eb1079c9ab5cff88a3ee08c613d9b80a69ee
parentf52841825ad01e80465aa662358c57dc7addbb9a (diff)
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
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am54
-rw-r--r--src/network/.gitignore1
l---------src/network/Makefile1
-rw-r--r--src/network/networkd-address.c169
-rw-r--r--src/network/networkd-gperf.gperf25
-rw-r--r--src/network/networkd-link.c119
-rw-r--r--src/network/networkd-manager.c210
-rw-r--r--src/network/networkd-network.c186
-rw-r--r--src/network/networkd-route.c129
-rw-r--r--src/network/networkd.c58
-rw-r--r--src/network/networkd.h170
-rw-r--r--src/network/test-network.c74
-rw-r--r--units/.gitignore1
-rw-r--r--units/systemd-networkd.service.in19
15 files changed, 1218 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 00d81ed3db..cf9bb6d7dd 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 dcc0a72d81..e6d5723205 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 \
@@ -3771,6 +3772,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 = \
src/login/logind.c \
diff --git a/src/network/.gitignore b/src/network/.gitignore
new file mode 100644
index 0000000000..b2a8c1ae1a
--- /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 0000000000..d0b0e8e008
--- /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 0000000000..9a7106e834
--- /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 <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/>.
+***/
+
+#include <net/if.h>
+
+#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 0000000000..a0ca6aa7a5
--- /dev/null
+++ b/src/network/networkd-gperf.gperf
@@ -0,0 +1,25 @@
+%{
+#include <stddef.h>
+#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 0000000000..486d4de2c7
--- /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 <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/>.
+***/
+
+#include <netinet/ether.h>
+#include <linux/if.h>
+
+#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 0000000000..6a57a80147
--- /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 <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/>.
+ ***/
+
+#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 0000000000..69c55edb8f
--- /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 <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/>.
+***/
+
+#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 0000000000..918a1d0191
--- /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 <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/>.
+***/
+
+#include <net/if.h>
+
+#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 0000000000..ed93ffb3fc
--- /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 <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/>.
+***/
+
+#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 0000000000..fd3b60a6cb
--- /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 <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/>.
+***/
+
+#pragma once
+
+#include <arpa/inet.h>
+#include <linux/rtnetlink.h>
+
+#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 0000000000..299f0fa326
--- /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 <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/>.
+***/
+
+#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 5fd9ccadb1..c5ac8fe72a 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 0000000000..52c04ed1f1
--- /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