summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-10-24 11:07:24 +0200
committerGitHub <noreply@github.com>2019-10-24 11:07:24 +0200
commit1048436869416d58071803e8c4fcc897b373af30 (patch)
tree4b85fd86ef0bce7ed38ebdfd2c94d4e613382d50
parent235ecb6d75f00384b3f42f449c769340e13fbd0b (diff)
parentdcd9f07ccfc010c9e4984124c70821bb81d6745a (diff)
downloadsystemd-1048436869416d58071803e8c4fcc897b373af30.tar.gz
Merge pull request #13452 from yuwata/network-reload
network: add networkctl reload and reconfigure
-rw-r--r--man/networkctl.xml19
-rw-r--r--src/basic/format-util.c17
-rw-r--r--src/basic/format-util.h10
-rw-r--r--src/network/fuzz-network-parser.c2
-rw-r--r--src/network/netdev/netdev.c12
-rw-r--r--src/network/netdev/netdev.h2
-rw-r--r--src/network/networkctl.c128
-rw-r--r--src/network/networkd-link-bus.c24
-rw-r--r--src/network/networkd-link-bus.h1
-rw-r--r--src/network/networkd-link.c6
-rw-r--r--src/network/networkd-link.h2
-rw-r--r--src/network/networkd-manager-bus.c41
-rw-r--r--src/network/networkd-manager.c4
-rw-r--r--src/network/networkd-network.c76
-rw-r--r--src/network/networkd-network.h6
-rw-r--r--src/network/org.freedesktop.network1.policy22
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py53
17 files changed, 360 insertions, 65 deletions
diff --git a/man/networkctl.xml b/man/networkctl.xml
index 14277a75f0..0502ab752c 100644
--- a/man/networkctl.xml
+++ b/man/networkctl.xml
@@ -261,6 +261,25 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
Takes interface name or index number.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <command>reconfigure</command>
+ </term>
+ <listitem><para>Reconfigure network interfaces. Takes interface name or index number.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>reload</command>
+ </term>
+ <listitem><para>Reload <filename>.netdev</filename> and <filename>.network</filename> files.
+ If a new <filename>.netdev</filename> file is found, then the corresponding netdev is created.
+ Note that even if an existing <filename>.netdev</filename> is modified or removed,
+ <command>systemd-networkd</command> does not update or remove the netdev.
+ If a new, modified or removed <filename>.network</filename> file is found, then all interfaces
+ which match the file are reconfigured.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/src/basic/format-util.c b/src/basic/format-util.c
index aec929a06a..9fea2e0690 100644
--- a/src/basic/format-util.c
+++ b/src/basic/format-util.c
@@ -4,11 +4,24 @@
#include "format-util.h"
#include "memory-util.h"
+#include "stdio-util.h"
-char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
+assert_cc(DECIMAL_STR_MAX(int) + 1 <= IF_NAMESIZE + 1);
+char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag) {
/* Buffer is always cleared */
memzero(buf, IF_NAMESIZE + 1);
- return if_indextoname(ifindex, buf);
+ if (if_indextoname(ifindex, buf))
+ return buf;
+
+ if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX))
+ return NULL;
+
+ if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
+ snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex);
+ else
+ snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex);
+
+ return buf;
}
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
diff --git a/src/basic/format-util.h b/src/basic/format-util.h
index e0d184a541..59622508a3 100644
--- a/src/basic/format-util.h
+++ b/src/basic/format-util.h
@@ -68,7 +68,15 @@
# error Unknown ino_t size
#endif
-char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]);
+typedef enum {
+ FORMAT_IFNAME_IFINDEX = 1 << 0,
+ FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX,
+} FormatIfnameFlag;
+
+char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag);
+static inline char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
+ return format_ifname_full(ifindex, buf, 0);
+}
typedef enum {
FORMAT_BYTES_USE_IEC = 1 << 0,
diff --git a/src/network/fuzz-network-parser.c b/src/network/fuzz-network-parser.c
index b05626751e..732b5b21f4 100644
--- a/src/network/fuzz-network-parser.c
+++ b/src/network/fuzz-network-parser.c
@@ -23,6 +23,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fflush(f);
assert_se(manager_new(&manager) >= 0);
- (void) network_load_one(manager, network_config);
+ (void) network_load_one(manager, &manager->networks, network_config);
return 0;
}
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index 7853e197f1..9b5d3c0df3 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -757,9 +757,10 @@ int netdev_load_one(Manager *manager, const char *filename) {
NetDev *n = hashmap_get(netdev->manager->netdevs, netdev->ifname);
assert(n);
- log_netdev_warning_errno(netdev, r,
- "The setting Name=%s in %s conflicts with the one in %s, ignoring",
- netdev->ifname, netdev->filename, n->filename);
+ if (!streq(netdev->filename, n->filename))
+ log_netdev_warning_errno(netdev, r,
+ "The setting Name=%s in %s conflicts with the one in %s, ignoring",
+ netdev->ifname, netdev->filename, n->filename);
/* Clear ifname before netdev_free() is called. Otherwise, the NetDev object 'n' is
* removed from the hashmap 'manager->netdevs'. */
@@ -828,14 +829,15 @@ int netdev_load_one(Manager *manager, const char *filename) {
return 0;
}
-int netdev_load(Manager *manager) {
+int netdev_load(Manager *manager, bool reload) {
_cleanup_strv_free_ char **files = NULL;
char **f;
int r;
assert(manager);
- hashmap_clear_with_destructor(manager->netdevs, netdev_unref);
+ if (!reload)
+ hashmap_clear_with_destructor(manager->netdevs, netdev_unref);
r = conf_files_list_strv(&files, ".netdev", NULL, 0, NETWORK_DIRS);
if (r < 0)
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index 989dd64ffc..77cd15d8bf 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -156,7 +156,7 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
/* For casting the various netdev kinds into a netdev */
#define NETDEV(n) (&(n)->meta)
-int netdev_load(Manager *manager);
+int netdev_load(Manager *manager, bool reload);
int netdev_load_one(Manager *manager, const char *filename);
void netdev_drop(NetDev *netdev);
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 60d6a3f75f..d83dc611a5 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -719,10 +719,8 @@ static int dump_gateways(
if (ifindex <= 0) {
char name[IF_NAMESIZE+1];
- if (format_ifname(local[i].ifindex, name))
- r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway, name);
- else
- r = table_add_cell_stringf(table, NULL, "%s on %%%i", with_description ?: gateway, local[i].ifindex);
+ r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway,
+ format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT));
} else
r = table_add_cell(table, NULL, TABLE_STRING, with_description ?: gateway);
if (r < 0)
@@ -775,10 +773,8 @@ static int dump_addresses(
if (ifindex <= 0) {
char name[IF_NAMESIZE+1];
- if (format_ifname(local[i].ifindex, name))
- r = table_add_cell_stringf(table, NULL, "%s on %s", pretty, name);
- else
- r = table_add_cell_stringf(table, NULL, "%s on %%%i", pretty, local[i].ifindex);
+ r = table_add_cell_stringf(table, NULL, "%s on %s", pretty,
+ format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT));
} else
r = table_add_cell(table, NULL, TABLE_STRING, pretty);
if (r < 0)
@@ -1759,14 +1755,13 @@ static int link_delete(int argc, char *argv[], void *userdata) {
}
SET_FOREACH(p, indexes, j) {
- r = link_delete_send_message(rtnl, PTR_TO_INT(p));
+ index = PTR_TO_INT(p);
+ r = link_delete_send_message(rtnl, index);
if (r < 0) {
char ifname[IF_NAMESIZE + 1];
- if (format_ifname(index, ifname))
- return log_error_errno(r, "Failed to delete interface %s: %m", ifname);
- else
- return log_error_errno(r, "Failed to delete interface %d: %m", index);
+ return log_error_errno(r, "Failed to delete interface %s: %m",
+ format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
}
}
@@ -1815,6 +1810,73 @@ static int link_renew(int argc, char *argv[], void *userdata) {
}
+static int verb_reload(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect system bus: %m");
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "Reload",
+ &error, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reload network settings: %m");
+
+ return 0;
+}
+
+static int verb_reconfigure(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_set_free_ Set *indexes = NULL;
+ int index, i, r;
+ Iterator j;
+ void *p;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect system bus: %m");
+
+ indexes = set_new(NULL);
+ if (!indexes)
+ return log_oom();
+
+ for (i = 1; i < argc; i++) {
+ r = parse_ifindex_or_ifname(argv[i], &index);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve interface %s", argv[i]);
+
+ r = set_put(indexes, INT_TO_PTR(index));
+ if (r < 0)
+ return log_oom();
+ }
+
+ SET_FOREACH(p, indexes, j) {
+ index = PTR_TO_INT(p);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "ReconfigureLink",
+ &error, NULL, "i", index);
+ if (r < 0) {
+ char ifname[IF_NAMESIZE + 1];
+
+ return log_error_errno(r, "Failed to reconfigure network interface %s: %m", format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
+ }
+ }
+
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
@@ -1826,19 +1888,21 @@ static int help(void) {
printf("%s%s [OPTIONS...]\n\n"
"Query and control the networking subsystem.%s\n"
"\nCommands:\n"
- " list [PATTERN...] List links\n"
- " status [PATTERN...] Show link status\n"
- " lldp [PATTERN...] Show LLDP neighbors\n"
- " label Show current address label entries in the kernel\n"
- " delete DEVICES... Delete virtual netdevs\n"
- " renew DEVICES... Renew dynamic configurations\n"
+ " list [PATTERN...] List links\n"
+ " status [PATTERN...] Show link status\n"
+ " lldp [PATTERN...] Show LLDP neighbors\n"
+ " label Show current address label entries in the kernel\n"
+ " delete DEVICES... Delete virtual netdevs\n"
+ " renew DEVICES... Renew dynamic configurations\n"
+ " reconfigure DEVICES... Reconfigure interfaces\n"
+ " reload Reload .network and .netdev files\n"
"\nOptions\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-pager Do not pipe output into a pager\n"
- " --no-legend Do not show the headers and footers\n"
- " -a --all Show status for all links\n"
- " -s --stats Show detailed link statics\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-legend Do not show the headers and footers\n"
+ " -a --all Show status for all links\n"
+ " -s --stats Show detailed link statics\n"
"\nSee the %s for details.\n"
, ansi_highlight()
, program_invocation_short_name
@@ -1911,12 +1975,14 @@ static int parse_argv(int argc, char *argv[]) {
static int networkctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
- { "status", VERB_ANY, VERB_ANY, 0, link_status },
- { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
- { "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
- { "delete", 2, VERB_ANY, 0, link_delete },
- { "renew", 2, VERB_ANY, 0, link_renew },
+ { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
+ { "status", VERB_ANY, VERB_ANY, 0, link_status },
+ { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
+ { "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
+ { "delete", 2, VERB_ANY, 0, link_delete },
+ { "renew", 2, VERB_ANY, 0, link_renew },
+ { "reconfigure", 2, VERB_ANY, 0, verb_reconfigure },
+ { "reload", 1, 1, 0, verb_reload },
{}
};
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index 9ef9146bcd..b0abc0a216 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -604,6 +604,29 @@ int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error
return sd_bus_reply_method_return(message, NULL);
}
+int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.reconfigure",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ r = link_reconfigure(l, true);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable link_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -625,6 +648,7 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_METHOD("RevertNTP", NULL, NULL, bus_link_method_revert_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RevertDNS", NULL, NULL, bus_link_method_revert_dns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Renew", NULL, NULL, bus_link_method_renew, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Reconfigure", NULL, NULL, bus_link_method_reconfigure, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/network/networkd-link-bus.h b/src/network/networkd-link-bus.h
index 1bea0b0453..09e4ad68a1 100644
--- a/src/network/networkd-link-bus.h
+++ b/src/network/networkd-link-bus.h
@@ -31,3 +31,4 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 5fedd3765c..179408cbcc 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2857,7 +2857,7 @@ static int link_configure_duid(Link *link) {
return 0;
}
-int link_reconfigure(Link *link) {
+int link_reconfigure(Link *link, bool force) {
Network *network;
int r;
@@ -2875,7 +2875,7 @@ int link_reconfigure(Link *link) {
} else if (r < 0)
return r;
- if (link->network == network)
+ if (link->network == network && !force)
return 0;
log_link_info(link, "Re-configuring with %s", network->filename);
@@ -3337,7 +3337,7 @@ static int link_carrier_gained(Link *link) {
if (r < 0)
return r;
if (r > 0) {
- r = link_reconfigure(link);
+ r = link_reconfigure(link, false);
if (r < 0)
return r;
}
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 8a96da90b2..b47557181a 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -208,7 +208,7 @@ uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
int link_request_set_routes(Link *link);
int link_request_set_nexthop(Link *link);
-int link_reconfigure(Link *link);
+int link_reconfigure(Link *link, bool force);
#define ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index 7484fcfa12..773dcf0b4d 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/capability.h>
#include "alloc-util.h"
#include "bus-common-errors.h"
@@ -11,6 +13,7 @@
#include "networkd-manager.h"
#include "path-util.h"
#include "strv.h"
+#include "user-util.h"
static int method_list_links(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -187,6 +190,42 @@ static int bus_method_renew_link(sd_bus_message *message, void *userdata, sd_bus
return call_link_method(userdata, message, bus_link_method_renew, error);
}
+static int bus_method_reconfigure_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_reconfigure, error);
+}
+
+static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *manager = userdata;
+ Iterator i;
+ Link *link;
+ int r;
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.reload",
+ NULL, true, UID_INVALID,
+ &manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ r = netdev_load(manager, true);
+ if (r < 0)
+ return r;
+
+ r = network_reload(manager);
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(link, manager->links, i) {
+ r = link_reconfigure(link, false);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -209,6 +248,8 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("RevertLinkNTP", "i", NULL, bus_method_revert_link_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RevertLinkDNS", "i", NULL, bus_method_revert_link_dns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RenewLink", "i", NULL, bus_method_renew_link, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ReconfigureLink", "i", NULL, bus_method_reconfigure_link, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Reload", NULL, NULL, bus_method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index c70194f1f8..40fbf14743 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -1856,11 +1856,11 @@ int manager_load_config(Manager *m) {
/* update timestamp */
paths_check_timestamp(NETWORK_DIRS, &m->network_dirs_ts_usec, true);
- r = netdev_load(m);
+ r = netdev_load(m, false);
if (r < 0)
return r;
- r = network_load(m);
+ r = network_load(m, &m->networks);
if (r < 0)
return r;
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 24d6556c76..85d6150312 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -314,7 +314,7 @@ int network_verify(Network *network) {
return 0;
}
-int network_load_one(Manager *manager, const char *filename) {
+int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) {
_cleanup_free_ char *fname = NULL, *name = NULL;
_cleanup_(network_unrefp) Network *network = NULL;
_cleanup_fclose_ FILE *file = NULL;
@@ -488,36 +488,42 @@ int network_load_one(Manager *manager, const char *filename) {
log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
network->filename);
- r = ordered_hashmap_ensure_allocated(&manager->networks, &string_hash_ops);
+ struct stat stats;
+ if (stat(filename, &stats) < 0)
+ return -errno;
+ network->timestamp = timespec_load(&stats.st_mtim);
+
+ if (network_verify(network) < 0)
+ /* Ignore .network files that do not match the conditions. */
+ return 0;
+
+ r = ordered_hashmap_ensure_allocated(networks, &string_hash_ops);
if (r < 0)
return r;
- r = ordered_hashmap_put(manager->networks, network->name, network);
+ r = ordered_hashmap_put(*networks, network->name, network);
if (r < 0)
return r;
- if (network_verify(network) < 0)
- return 0;
-
network = NULL;
return 0;
}
-int network_load(Manager *manager) {
+int network_load(Manager *manager, OrderedHashmap **networks) {
_cleanup_strv_free_ char **files = NULL;
char **f;
int r;
assert(manager);
- ordered_hashmap_clear_with_destructor(manager->networks, network_unref);
+ ordered_hashmap_clear_with_destructor(*networks, network_unref);
r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
if (r < 0)
return log_error_errno(r, "Failed to enumerate network files: %m");
STRV_FOREACH(f, files) {
- r = network_load_one(manager, *f);
+ r = network_load_one(manager, networks, *f);
if (r < 0)
log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
}
@@ -525,6 +531,48 @@ int network_load(Manager *manager) {
return 0;
}
+int network_reload(Manager *manager) {
+ OrderedHashmap *new_networks = NULL;
+ Network *n, *old;
+ Iterator i;
+ int r;
+
+ assert(manager);
+
+ r = network_load(manager, &new_networks);
+ if (r < 0)
+ goto failure;
+
+ ORDERED_HASHMAP_FOREACH(n, new_networks, i) {
+ r = network_get_by_name(manager, n->name, &old);
+ if (r < 0)
+ continue; /* The .network file is new. */
+
+ if (n->timestamp != old->timestamp)
+ continue; /* The .network file is modified. */
+
+ if (!streq(n->filename, old->filename))
+ continue;
+
+ r = ordered_hashmap_replace(new_networks, old->name, old);
+ if (r < 0)
+ goto failure;
+
+ network_ref(old);
+ network_unref(n);
+ }
+
+ ordered_hashmap_free_with_destructor(manager->networks, network_unref);
+ manager->networks = new_networks;
+
+ return 0;
+
+failure:
+ ordered_hashmap_free_with_destructor(new_networks, network_unref);
+
+ return r;
+}
+
static Network *network_free(Network *network) {
IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
RoutingPolicyRule *rule;
@@ -615,13 +663,9 @@ static Network *network_free(Network *network) {
hashmap_free(network->prefixes_by_section);
hashmap_free(network->rules_by_section);
- if (network->manager) {
- if (network->manager->networks && network->name)
- ordered_hashmap_remove(network->manager->networks, network->name);
-
- if (network->manager->duids_requesting_uuid)
- set_remove(network->manager->duids_requesting_uuid, &network->duid);
- }
+ if (network->manager &&
+ network->manager->duids_requesting_uuid)
+ set_remove(network->manager->duids_requesting_uuid, &network->duid);
free(network->name);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index d169481cd8..1ae560e994 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -54,6 +54,7 @@ struct Network {
char *filename;
char *name;
+ usec_t timestamp;
unsigned n_ref;
@@ -283,8 +284,9 @@ Network *network_ref(Network *network);
Network *network_unref(Network *network);
DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_unref);
-int network_load(Manager *manager);
-int network_load_one(Manager *manager, const char *filename);
+int network_load(Manager *manager, OrderedHashmap **networks);
+int network_reload(Manager *manager);
+int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename);
int network_verify(Network *network);
int network_get_by_name(Manager *manager, const char *name, Network **ret);
diff --git a/src/network/org.freedesktop.network1.policy b/src/network/org.freedesktop.network1.policy
index 4a33f5a8aa..9b1895e657 100644
--- a/src/network/org.freedesktop.network1.policy
+++ b/src/network/org.freedesktop.network1.policy
@@ -150,4 +150,26 @@
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
</action>
+ <action id="org.freedesktop.network1.reload">
+ <description gettext-domain="systemd">Reload network settings</description>
+ <message gettext-domain="systemd">Authentication is required to reload network settings.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
+ <action id="org.freedesktop.network1.reconfigure">
+ <description gettext-domain="systemd">Reconfigure network interface</description>
+ <message gettext-domain="systemd">Authentication is required to reconfigure network interface.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
</policyconfig>
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index d100921709..1cd623482f 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -380,6 +380,7 @@ class Utilities():
class NetworkctlTests(unittest.TestCase, Utilities):
links = [
+ 'dummy98',
'test1',
'veth99',
]
@@ -388,6 +389,8 @@ class NetworkctlTests(unittest.TestCase, Utilities):
'11-dummy.netdev',
'11-dummy-mtu.netdev',
'11-dummy.network',
+ '12-dummy.netdev',
+ '25-address-static.network',
'25-veth.netdev',
'netdev-link-local-addressing-yes.network',
]
@@ -401,6 +404,56 @@ class NetworkctlTests(unittest.TestCase, Utilities):
remove_unit_from_networkd_path(self.units)
stop_networkd(show_logs=True)
+ def test_reconfigure(self):
+ copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
+
+ output = check_output('ip -4 address show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
+ self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
+ self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
+
+ check_output('ip address del 10.1.2.3/16 dev dummy98')
+ check_output('ip address del 10.1.2.4/16 dev dummy98')
+ check_output('ip address del 10.2.2.4/16 dev dummy98')
+
+ check_output(*networkctl_cmd, 'reconfigure', 'dummy98', env=env)
+ self.wait_online(['dummy98:routable'])
+
+ output = check_output('ip -4 address show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
+ self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
+ self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
+
+ def test_reload(self):
+ start_networkd(3)
+
+ copy_unit_to_networkd_unit_path('11-dummy.netdev')
+ check_output(*networkctl_cmd, 'reload', env=env)
+ time.sleep(3)
+ self.check_link_exists('test1')
+ self.check_operstate('test1', 'off', setup_state='unmanaged')
+
+ copy_unit_to_networkd_unit_path('11-dummy.network')
+ check_output(*networkctl_cmd, 'reload', env=env)
+ self.wait_online(['test1:degraded'])
+
+ remove_unit_from_networkd_path(['11-dummy.network'])
+ check_output(*networkctl_cmd, 'reload', env=env)
+ time.sleep(1)
+ self.check_operstate('test1', 'degraded', setup_state='unmanaged')
+
+ remove_unit_from_networkd_path(['11-dummy.netdev'])
+ check_output(*networkctl_cmd, 'reload', env=env)
+ self.check_operstate('test1', 'degraded', setup_state='unmanaged')
+
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
+ check_output(*networkctl_cmd, 'reload', env=env)
+ self.check_operstate('test1', 'degraded')
+
def test_glob(self):
copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
start_networkd()