diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2019-10-24 11:07:24 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-24 11:07:24 +0200 |
commit | 1048436869416d58071803e8c4fcc897b373af30 (patch) | |
tree | 4b85fd86ef0bce7ed38ebdfd2c94d4e613382d50 | |
parent | 235ecb6d75f00384b3f42f449c769340e13fbd0b (diff) | |
parent | dcd9f07ccfc010c9e4984124c70821bb81d6745a (diff) | |
download | systemd-1048436869416d58071803e8c4fcc897b373af30.tar.gz |
Merge pull request #13452 from yuwata/network-reload
network: add networkctl reload and reconfigure
-rw-r--r-- | man/networkctl.xml | 19 | ||||
-rw-r--r-- | src/basic/format-util.c | 17 | ||||
-rw-r--r-- | src/basic/format-util.h | 10 | ||||
-rw-r--r-- | src/network/fuzz-network-parser.c | 2 | ||||
-rw-r--r-- | src/network/netdev/netdev.c | 12 | ||||
-rw-r--r-- | src/network/netdev/netdev.h | 2 | ||||
-rw-r--r-- | src/network/networkctl.c | 128 | ||||
-rw-r--r-- | src/network/networkd-link-bus.c | 24 | ||||
-rw-r--r-- | src/network/networkd-link-bus.h | 1 | ||||
-rw-r--r-- | src/network/networkd-link.c | 6 | ||||
-rw-r--r-- | src/network/networkd-link.h | 2 | ||||
-rw-r--r-- | src/network/networkd-manager-bus.c | 41 | ||||
-rw-r--r-- | src/network/networkd-manager.c | 4 | ||||
-rw-r--r-- | src/network/networkd-network.c | 76 | ||||
-rw-r--r-- | src/network/networkd-network.h | 6 | ||||
-rw-r--r-- | src/network/org.freedesktop.network1.policy | 22 | ||||
-rwxr-xr-x | test/test-network/systemd-networkd-tests.py | 53 |
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() |