summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/networkctl.c37
-rw-r--r--src/network/networkd-address-pool.c8
-rw-r--r--src/network/networkd-address.c462
-rw-r--r--src/network/networkd-address.h20
-rw-r--r--src/network/networkd-dhcp4.c83
-rw-r--r--src/network/networkd-dhcp6.c189
-rw-r--r--src/network/networkd-fdb.c8
-rw-r--r--src/network/networkd-ipv4ll.c44
-rw-r--r--src/network/networkd-link-bus.c6
-rw-r--r--src/network/networkd-link.c728
-rw-r--r--src/network/networkd-link.h24
-rw-r--r--src/network/networkd-manager-bus.c2
-rw-r--r--src/network/networkd-manager.c807
-rw-r--r--src/network/networkd-ndisc.c88
-rw-r--r--src/network/networkd-netdev-bond.c31
-rw-r--r--src/network/networkd-netdev-bridge.c87
-rw-r--r--src/network/networkd-netdev-bridge.h4
-rw-r--r--src/network/networkd-netdev-gperf.gperf3
-rw-r--r--src/network/networkd-netdev-ipvlan.c15
-rw-r--r--src/network/networkd-netdev-macvlan.c3
-rw-r--r--src/network/networkd-netdev-tunnel.c34
-rw-r--r--src/network/networkd-netdev-tuntap.c3
-rw-r--r--src/network/networkd-netdev-veth.c19
-rw-r--r--src/network/networkd-netdev-vlan.c14
-rw-r--r--src/network/networkd-netdev-vxlan.c19
-rw-r--r--src/network/networkd-netdev.c13
-rw-r--r--src/network/networkd-netdev.h3
-rw-r--r--src/network/networkd-network-bus.c5
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.c72
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--src/network/networkd-route.c434
-rw-r--r--src/network/networkd-route.h30
-rw-r--r--src/network/networkd-util.c10
-rw-r--r--src/network/networkd-wait-online-link.c3
-rw-r--r--src/network/networkd-wait-online-manager.c5
-rw-r--r--src/network/networkd-wait-online.c10
-rw-r--r--src/network/networkd.c12
-rw-r--r--src/network/networkd.h11
-rw-r--r--src/network/test-network.c14
40 files changed, 2294 insertions, 1069 deletions
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 75572b6388..ba7e3ba74a 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -19,29 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
#include <getopt.h>
#include <net/if.h>
+#include <stdbool.h>
-#include "sd-network.h"
-#include "sd-netlink.h"
-#include "sd-hwdb.h"
#include "sd-device.h"
+#include "sd-hwdb.h"
+#include "sd-netlink.h"
+#include "sd-network.h"
-#include "strv.h"
-#include "build.h"
-#include "util.h"
-#include "pager.h"
-#include "lldp.h"
-#include "netlink-util.h"
+#include "alloc-util.h"
+#include "arphrd-list.h"
#include "device-util.h"
+#include "ether-addr-util.h"
#include "hwdb-util.h"
-#include "arphrd-list.h"
+#include "lldp.h"
#include "local-addresses.h"
+#include "locale-util.h"
+#include "locale-util.h"
+#include "netlink-util.h"
+#include "pager.h"
+#include "parse-util.h"
#include "socket-util.h"
-#include "ether-addr-util.h"
-#include "verbs.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "util.h"
+#include "verbs.h"
static bool arg_no_pager = false;
static bool arg_legend = true;
@@ -516,7 +521,7 @@ static int link_status_one(
assert(rtnl);
assert(name);
- if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
+ if (parse_ifindex(name, &ifindex) >= 0)
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
else {
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
@@ -1063,9 +1068,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_NO_PAGER:
arg_no_pager = true;
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
index d609daafde..889fe1e30d 100644
--- a/src/network/networkd-address-pool.c
+++ b/src/network/networkd-address-pool.c
@@ -19,8 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "networkd.h"
+#include "alloc-util.h"
#include "networkd-address-pool.h"
+#include "networkd.h"
+#include "set.h"
+#include "string-util.h"
int address_pool_new(
Manager *m,
@@ -96,9 +99,10 @@ static bool address_pool_prefix_is_taken(
HASHMAP_FOREACH(l, p->manager->links, i) {
Address *a;
+ Iterator j;
/* Don't clash with assigned addresses */
- LIST_FOREACH(addresses, a, l->addresses) {
+ SET_FOREACH(a, l->addresses, j) {
if (a->family != p->family)
continue;
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index b0d296941e..8b6acf2e1d 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -21,26 +21,39 @@
#include <net/if.h>
-#include "utf8.h"
-#include "util.h"
+#include "alloc-util.h"
#include "conf-parser.h"
#include "firewall-util.h"
#include "netlink-util.h"
-
-#include "networkd.h"
#include "networkd-address.h"
+#include "networkd.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "utf8.h"
+#include "util.h"
-static void address_init(Address *address) {
- assert(address);
+int address_new(Address **ret) {
+ _cleanup_address_free_ Address *address = NULL;
+
+ address = new0(Address, 1);
+ if (!address)
+ return -ENOMEM;
address->family = AF_UNSPEC;
address->scope = RT_SCOPE_UNIVERSE;
address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME;
+
+ *ret = address;
+ address = NULL;
+
+ return 0;
}
int address_new_static(Network *network, unsigned section, Address **ret) {
_cleanup_address_free_ Address *address = NULL;
+ int r;
if (section) {
address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section));
@@ -52,11 +65,9 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
}
}
- address = new0(Address, 1);
- if (!address)
- return -ENOMEM;
-
- address_init(address);
+ r = address_new(&address);
+ if (r < 0)
+ return r;
address->network = network;
@@ -74,21 +85,6 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
return 0;
}
-int address_new_dynamic(Address **ret) {
- _cleanup_address_free_ Address *address = NULL;
-
- address = new0(Address, 1);
- if (!address)
- return -ENOMEM;
-
- address_init(address);
-
- *ret = address;
- address = NULL;
-
- return 0;
-}
-
void address_free(Address *address) {
if (!address)
return;
@@ -101,10 +97,112 @@ void address_free(Address *address) {
UINT_TO_PTR(address->section));
}
+ if (address->link) {
+ set_remove(address->link->addresses, address);
+ set_remove(address->link->addresses_foreign, address);
+ }
+
free(address);
}
-int address_establish(Address *address, Link *link) {
+static void address_hash_func(const void *b, struct siphash *state) {
+ const Address *a = b;
+
+ assert(a);
+
+ siphash24_compress(&a->family, sizeof(a->family), state);
+
+ switch (a->family) {
+ case AF_INET:
+ siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
+
+ /* peer prefix */
+ if (a->prefixlen != 0) {
+ uint32_t prefix;
+
+ if (a->in_addr_peer.in.s_addr != 0)
+ prefix = be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
+ else
+ prefix = be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
+
+ siphash24_compress(&prefix, sizeof(prefix), state);
+ }
+
+ /* fallthrough */
+ case AF_INET6:
+ /* local address */
+ siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
+
+ break;
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ break;
+ }
+}
+
+static int address_compare_func(const void *c1, const void *c2) {
+ const Address *a1 = c1, *a2 = c2;
+
+ if (a1->family < a2->family)
+ return -1;
+ if (a1->family > a2->family)
+ return 1;
+
+ switch (a1->family) {
+ /* use the same notion of equality as the kernel does */
+ case AF_INET:
+ if (a1->prefixlen < a2->prefixlen)
+ return -1;
+ if (a1->prefixlen > a2->prefixlen)
+ return 1;
+
+ /* compare the peer prefixes */
+ if (a1->prefixlen != 0) {
+ /* make sure we don't try to shift by 32.
+ * See ISO/IEC 9899:TC3 ยง 6.5.7.3. */
+ uint32_t b1, b2;
+
+ if (a1->in_addr_peer.in.s_addr != 0)
+ b1 = be32toh(a1->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
+ else
+ b1 = be32toh(a1->in_addr.in.s_addr) >> (32 - a1->prefixlen);
+
+ if (a2->in_addr_peer.in.s_addr != 0)
+ b2 = be32toh(a2->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
+ else
+ b2 = be32toh(a2->in_addr.in.s_addr) >> (32 - a1->prefixlen);
+
+ if (b1 < b2)
+ return -1;
+ if (b1 > b2)
+ return 1;
+ }
+
+ /* fall-through */
+ case AF_INET6:
+ return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ return 0;
+ }
+}
+
+static const struct hash_ops address_hash_ops = {
+ .hash = address_hash_func,
+ .compare = address_compare_func
+};
+
+bool address_equal(Address *a1, Address *a2) {
+ if (a1 == a2)
+ return true;
+
+ if (!a1 || !a2)
+ return false;
+
+ return address_compare_func(a1, a2) == 0;
+}
+
+static int address_establish(Address *address, Link *link) {
bool masq;
int r;
@@ -112,9 +210,9 @@ int address_establish(Address *address, Link *link) {
assert(link);
masq = link->network &&
- link->network->ip_masquerade &&
- address->family == AF_INET &&
- address->scope < RT_SCOPE_LINK;
+ link->network->ip_masquerade &&
+ address->family == AF_INET &&
+ address->scope < RT_SCOPE_LINK;
/* Add firewall entry if this is requested */
if (address->ip_masquerade_done != masq) {
@@ -131,11 +229,86 @@ int address_establish(Address *address, Link *link) {
return 0;
}
-int address_release(Address *address, Link *link) {
+static int address_add_internal(Link *link, Set **addresses,
+ int family,
+ const union in_addr_union *in_addr,
+ unsigned char prefixlen,
+ Address **ret) {
+ _cleanup_address_free_ Address *address = NULL;
int r;
- assert(address);
assert(link);
+ assert(addresses);
+ assert(in_addr);
+
+ r = address_new(&address);
+ if (r < 0)
+ return r;
+
+ address->family = family;
+ address->in_addr = *in_addr;
+ address->prefixlen = prefixlen;
+
+ r = set_ensure_allocated(addresses, &address_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(*addresses, address);
+ if (r < 0)
+ return r;
+
+ address->link = link;
+
+ if (ret)
+ *ret = address;
+
+ address = NULL;
+
+ return 0;
+}
+
+int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
+ return address_add_internal(link, &link->addresses_foreign, family, in_addr, prefixlen, ret);
+}
+
+int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
+ Address *address;
+ int r;
+
+ r = address_get(link, family, in_addr, prefixlen, &address);
+ if (r == -ENOENT) {
+ /* Address does not exist, create a new one */
+ r = address_add_internal(link, &link->addresses, family, in_addr, prefixlen, &address);
+ if (r < 0)
+ return r;
+ } else if (r == 0) {
+ /* Take over a foreign address */
+ r = set_ensure_allocated(&link->addresses, &address_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(link->addresses, address);
+ if (r < 0)
+ return r;
+
+ set_remove(link->addresses_foreign, address);
+ } else if (r == 1) {
+ /* Already exists, do nothing */
+ ;
+ } else
+ return r;
+
+ if (ret)
+ *ret = address;
+
+ return 0;
+}
+
+static int address_release(Address *address) {
+ int r;
+
+ assert(address);
+ assert(address->link);
/* Remove masquerading firewall entry if it was added */
if (address->ip_masquerade_done) {
@@ -144,7 +317,7 @@ int address_release(Address *address, Link *link) {
r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
if (r < 0)
- log_link_warning_errno(link, r, "Failed to disable IP masquerading: %m");
+ log_link_warning_errno(address->link, r, "Failed to disable IP masquerading: %m");
address->ip_masquerade_done = false;
}
@@ -152,81 +325,97 @@ int address_release(Address *address, Link *link) {
return 0;
}
-int address_drop(Address *address, Link *link,
- sd_netlink_message_handler_t callback) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
- int r;
+int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) {
+ bool ready;
assert(address);
- assert(address->family == AF_INET || address->family == AF_INET6);
- assert(link);
- assert(link->ifindex > 0);
- assert(link->manager);
- assert(link->manager->rtnl);
+ assert(cinfo);
- address_release(address, link);
+ ready = address_is_ready(address);
- r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
- link->ifindex, address->family);
- if (r < 0)
- return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m");
+ address->added = true;
+ address->flags = flags;
+ address->scope = scope;
+ address->cinfo = *cinfo;
- r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
- if (r < 0)
- return log_error_errno(r, "Could not set prefixlen: %m");
+ if (address->link) {
+ link_update_operstate(address->link);
- if (address->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
- else if (address->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
- if (r < 0)
- return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
+ if (!ready && address_is_ready(address))
+ link_check_ready(address->link);
+ }
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
- if (r < 0)
- return log_error_errno(r, "Could not send rtnetlink message: %m");
+ return 0;
+}
- link_ref(link);
+int address_drop(Address *address) {
+ Link *link;
+ bool ready;
+
+ assert(address);
+
+ ready = address_is_ready(address);
+ link = address->link;
+
+ address_release(address);
+ address_free(address);
+
+ link_update_operstate(link);
+
+ if (link && !ready)
+ link_check_ready(link);
return 0;
}
-int address_update(Address *address, Link *link,
- sd_netlink_message_handler_t callback) {
+int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
+ Address address = {}, *existing;
+
+ assert(link);
+ assert(in_addr);
+ assert(ret);
+
+ address.family = family;
+ address.in_addr = *in_addr;
+ address.prefixlen = prefixlen;
+
+ existing = set_get(link->addresses, &address);
+ if (existing) {
+ *ret = existing;
+
+ return 1;
+ } else {
+ existing = set_get(link->addresses_foreign, &address);
+ if (!existing)
+ return -ENOENT;
+ }
+
+ *ret = existing;
+
+ return 0;
+}
+
+int address_remove(Address *address, Link *link,
+ sd_netlink_message_handler_t callback) {
_cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
int r;
assert(address);
assert(address->family == AF_INET || address->family == AF_INET6);
+ assert(link);
assert(link->ifindex > 0);
assert(link->manager);
assert(link->manager->rtnl);
- r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
+ r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
link->ifindex, address->family);
if (r < 0)
- return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
+ return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m");
r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
if (r < 0)
return log_error_errno(r, "Could not set prefixlen: %m");
- address->flags |= IFA_F_PERMANENT;
-
- r = sd_rtnl_message_addr_set_flags(req, address->flags & 0xff);
- if (r < 0)
- return log_error_errno(r, "Could not set flags: %m");
-
- if (address->flags & ~0xff && link->rtnl_extended_attrs) {
- r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags);
- if (r < 0)
- return log_error_errno(r, "Could not set extended flags: %m");
- }
-
- r = sd_rtnl_message_addr_set_scope(req, address->scope);
- if (r < 0)
- return log_error_errno(r, "Could not set scope: %m");
-
if (address->family == AF_INET)
r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
else if (address->family == AF_INET6)
@@ -234,22 +423,6 @@ int address_update(Address *address, Link *link,
if (r < 0)
return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
- if (address->family == AF_INET) {
- r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
- if (r < 0)
- return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m");
- }
-
- if (address->label) {
- r = sd_netlink_message_append_string(req, IFA_LABEL, address->label);
- if (r < 0)
- return log_error_errno(r, "Could not append IFA_LABEL attribute: %m");
- }
-
- r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo);
- if (r < 0)
- return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m");
-
r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
@@ -292,7 +465,7 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
} else if (original->family == AF_INET6)
in_addr.in6.s6_addr[15] |= 1;
- r = address_new_dynamic(&na);
+ r = address_new(&na);
if (r < 0)
return r;
@@ -318,8 +491,7 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
return 0;
}
-int address_configure(Address *address, Link *link,
- sd_netlink_message_handler_t callback) {
+int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback, bool update) {
_cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
int r;
@@ -334,8 +506,12 @@ int address_configure(Address *address, Link *link,
if (r < 0)
return r;
- r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
- link->ifindex, address->family);
+ if (update)
+ r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
+ link->ifindex, address->family);
+ else
+ r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
+ link->ifindex, address->family);
if (r < 0)
return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
@@ -392,13 +568,23 @@ int address_configure(Address *address, Link *link,
if (r < 0)
return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ r = address_establish(address, link);
if (r < 0)
+ return r;
+
+ r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ if (r < 0) {
+ address_release(address);
return log_error_errno(r, "Could not send rtnetlink message: %m");
+ }
link_ref(link);
- address_establish(address, link);
+ r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
+ if (r < 0) {
+ address_release(address);
+ return log_error_errno(r, "Could not add address: %m");
+ }
return 0;
}
@@ -430,15 +616,13 @@ int config_parse_broadcast(
return r;
if (n->family == AF_INET6) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
return 0;
}
r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Broadcast is invalid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Broadcast is invalid, ignoring assignment: %s", rvalue);
return 0;
}
@@ -487,10 +671,10 @@ int config_parse_address(const char *unit,
e = strchr(rvalue, '/');
if (e) {
unsigned i;
+
r = safe_atou(e + 1, &i);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Prefix length is invalid, ignoring assignment: %s", e + 1);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1);
return 0;
}
@@ -502,23 +686,20 @@ int config_parse_address(const char *unit,
r = in_addr_from_string_auto(address, &f, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Address is invalid, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Address is invalid, ignoring assignment: %s", address);
return 0;
}
if (!e && f == AF_INET) {
r = in_addr_default_prefixlen(&buffer.in, &n->prefixlen);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address);
return 0;
}
}
if (n->family != AF_UNSPEC && f != n->family) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Address is incompatible, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", address);
return 0;
}
@@ -567,9 +748,7 @@ int config_parse_label(const char *unit,
return log_oom();
if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface label is not ASCII clean or is too"
- " long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
free(label);
return 0;
}
@@ -587,49 +766,8 @@ int config_parse_label(const char *unit,
return 0;
}
-bool address_equal(Address *a1, Address *a2) {
- /* same object */
- if (a1 == a2)
- return true;
-
- /* one, but not both, is NULL */
- if (!a1 || !a2)
- return false;
-
- if (a1->family != a2->family)
- return false;
-
- switch (a1->family) {
- /* use the same notion of equality as the kernel does */
- case AF_UNSPEC:
- return true;
+bool address_is_ready(const Address *a) {
+ assert(a);
- case AF_INET:
- if (a1->prefixlen != a2->prefixlen)
- return false;
- else if (a1->prefixlen == 0)
- /* make sure we don't try to shift by 32.
- * See ISO/IEC 9899:TC3 ยง 6.5.7.3. */
- return true;
- else {
- uint32_t b1, b2;
-
- b1 = be32toh(a1->in_addr.in.s_addr);
- b2 = be32toh(a2->in_addr.in.s_addr);
-
- return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen));
- }
-
- case AF_INET6: {
- uint64_t *b1, *b2;
-
- b1 = (uint64_t*)&a1->in_addr.in6;
- b2 = (uint64_t*)&a2->in_addr.in6;
-
- return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL);
- }
-
- default:
- assert_not_reached("Invalid address family");
- }
+ return a->added && !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
}
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 39789a2382..0b1f3b688b 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -38,6 +38,8 @@ struct Address {
Network *network;
unsigned section;
+ Link *link;
+
int family;
unsigned char prefixlen;
unsigned char scope;
@@ -50,20 +52,24 @@ struct Address {
union in_addr_union in_addr;
union in_addr_union in_addr_peer;
- bool ip_masquerade_done;
+ bool added:1;
+ bool ip_masquerade_done:1;
LIST_FIELDS(Address, addresses);
};
int address_new_static(Network *network, unsigned section, Address **ret);
-int address_new_dynamic(Address **ret);
+int address_new(Address **ret);
void address_free(Address *address);
-int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_update(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_drop(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_establish(Address *address, Link *link);
-int address_release(Address *address, Link *link);
+int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo);
+int address_drop(Address *address);
+int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback, bool update);
+int address_remove(Address *address, Link *link, sd_netlink_message_handler_t callback);
bool address_equal(Address *a1, Address *a2);
+bool address_is_ready(const Address *a);
DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
#define _cleanup_address_free_ _cleanup_(address_freep)
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 04f04df117..b58fc5808c 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -22,10 +22,11 @@
#include <netinet/ether.h>
#include <linux/if.h>
+#include "alloc-util.h"
+#include "dhcp-lease-internal.h"
#include "hostname-util.h"
-#include "networkd-link.h"
#include "network-internal.h"
-#include "dhcp-lease-internal.h"
+#include "networkd-link.h"
static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
void *userdata) {
@@ -45,7 +46,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
if (!link->dhcp4_messages) {
link->dhcp4_configured = true;
- link_client_handler(link);
+ link_check_ready(link);
}
return 1;
@@ -72,11 +73,13 @@ static int link_set_dhcp_routes(Link *link) {
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
- r = route_new_dynamic(&route, RTPROT_DHCP);
+ r = route_new(&route);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate route: %m");
- r = route_new_dynamic(&route_gw, RTPROT_DHCP);
+ route->protocol = RTPROT_DHCP;
+
+ r = route_new(&route_gw);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate route: %m");
@@ -84,11 +87,12 @@ static int link_set_dhcp_routes(Link *link) {
* route for the gw host so that we can route no matter the
* netmask or existing kernel route tables. */
route_gw->family = AF_INET;
- route_gw->dst_addr.in = gateway;
+ route_gw->dst.in = gateway;
route_gw->dst_prefixlen = 32;
- route_gw->prefsrc_addr.in = address;
+ route_gw->prefsrc.in = address;
route_gw->scope = RT_SCOPE_LINK;
- route_gw->metrics = link->network->dhcp_route_metric;
+ route_gw->protocol = RTPROT_DHCP;
+ route_gw->priority = link->network->dhcp_route_metric;
r = route_configure(route_gw, link, &dhcp4_route_handler);
if (r < 0)
@@ -97,9 +101,9 @@ static int link_set_dhcp_routes(Link *link) {
link->dhcp4_messages ++;
route->family = AF_INET;
- route->in_addr.in = gateway;
- route->prefsrc_addr.in = address;
- route->metrics = link->network->dhcp_route_metric;
+ route->gw.in = gateway;
+ route->prefsrc.in = address;
+ route->priority = link->network->dhcp_route_metric;
r = route_configure(route, link, &dhcp4_route_handler);
if (r < 0) {
@@ -120,15 +124,16 @@ static int link_set_dhcp_routes(Link *link) {
for (i = 0; i < n; i++) {
_cleanup_route_free_ Route *route = NULL;
- r = route_new_dynamic(&route, RTPROT_DHCP);
+ r = route_new(&route);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate route: %m");
route->family = AF_INET;
- route->in_addr.in = static_routes[i].gw_addr;
- route->dst_addr.in = static_routes[i].dst_addr;
+ route->protocol = RTPROT_DHCP;
+ route->gw.in = static_routes[i].gw_addr;
+ route->dst.in = static_routes[i].dst_addr;
route->dst_prefixlen = static_routes[i].dst_prefixlen;
- route->metrics = link->network->dhcp_route_metric;
+ route->priority = link->network->dhcp_route_metric;
r = route_configure(route, link, &dhcp4_route_handler);
if (r < 0)
@@ -162,45 +167,45 @@ static int dhcp_lease_lost(Link *link) {
for (i = 0; i < n; i++) {
_cleanup_route_free_ Route *route = NULL;
- r = route_new_dynamic(&route, RTPROT_UNSPEC);
+ r = route_new(&route);
if (r >= 0) {
route->family = AF_INET;
- route->in_addr.in = routes[i].gw_addr;
- route->dst_addr.in = routes[i].dst_addr;
+ route->gw.in = routes[i].gw_addr;
+ route->dst.in = routes[i].dst_addr;
route->dst_prefixlen = routes[i].dst_prefixlen;
- route_drop(route, link,
- &link_route_drop_handler);
+ route_remove(route, link,
+ &link_route_remove_handler);
}
}
}
}
- r = address_new_dynamic(&address);
+ r = address_new(&address);
if (r >= 0) {
r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
if (r >= 0) {
_cleanup_route_free_ Route *route_gw = NULL;
_cleanup_route_free_ Route *route = NULL;
- r = route_new_dynamic(&route_gw, RTPROT_UNSPEC);
+ r = route_new(&route_gw);
if (r >= 0) {
route_gw->family = AF_INET;
- route_gw->dst_addr.in = gateway;
+ route_gw->dst.in = gateway;
route_gw->dst_prefixlen = 32;
route_gw->scope = RT_SCOPE_LINK;
- route_drop(route_gw, link,
- &link_route_drop_handler);
+ route_remove(route_gw, link,
+ &link_route_remove_handler);
}
- r = route_new_dynamic(&route, RTPROT_UNSPEC);
+ r = route_new(&route);
if (r >= 0) {
route->family = AF_INET;
- route->in_addr.in = gateway;
+ route->gw.in = gateway;
- route_drop(route, link,
- &link_route_drop_handler);
+ route_remove(route, link,
+ &link_route_remove_handler);
}
}
@@ -214,7 +219,7 @@ static int dhcp_lease_lost(Link *link) {
address->in_addr.in = addr;
address->prefixlen = prefixlen;
- address_drop(address, link, &link_address_drop_handler);
+ address_remove(address, link, &link_address_remove_handler);
}
}
@@ -267,7 +272,7 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
link_enter_failed(link);
} else if (r >= 0)
- link_rtnl_process_address(rtnl, m, link->manager);
+ manager_rtnl_process_address(rtnl, m, link->manager);
link_set_dhcp_routes(link);
@@ -288,7 +293,7 @@ static int dhcp4_update_address(Link *link,
prefixlen = in_addr_netmask_to_prefixlen(netmask);
- r = address_new_dynamic(&addr);
+ r = address_new(&addr);
if (r < 0)
return r;
@@ -299,9 +304,9 @@ static int dhcp4_update_address(Link *link,
addr->prefixlen = prefixlen;
addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
- /* use update rather than configure so that we will update the
- * lifetime of an existing address if it has already been configured */
- r = address_update(addr, link, &dhcp4_address_handler);
+ /* allow reusing an existing address and simply update its lifetime
+ * in case it already exists */
+ r = address_configure(addr, link, &dhcp4_address_handler, true);
if (r < 0)
return r;
@@ -528,9 +533,11 @@ int dhcp4_configure(Link *link) {
assert(link->network);
assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
- r = sd_dhcp_client_new(&link->dhcp_client);
- if (r < 0)
- return r;
+ if (!link->dhcp_client) {
+ r = sd_dhcp_client_new(&link->dhcp_client);
+ if (r < 0)
+ return r;
+ }
r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
if (r < 0)
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 13105c7865..c3332bb1ac 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -22,12 +22,11 @@
#include <netinet/ether.h>
#include <linux/if.h>
-#include "networkd-link.h"
-#include "network-internal.h"
-
-#include "sd-icmp6-nd.h"
#include "sd-dhcp6-client.h"
+#include "network-internal.h"
+#include "networkd-link.h"
+
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
@@ -53,24 +52,22 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
return 1;
}
- log_link_error(link, "Could not set DHCPv6 address: %s",
- strerror(-r));
+ log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
link_enter_failed(link);
} else if (r >= 0)
- link_rtnl_process_address(rtnl, m, link->manager);
+ manager_rtnl_process_address(rtnl, m, link->manager);
return 1;
}
-static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
- uint8_t prefixlen, uint32_t lifetime_preferred,
- uint32_t lifetime_valid) {
+static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr,
+ uint32_t lifetime_preferred, uint32_t lifetime_valid) {
int r;
_cleanup_address_free_ Address *addr = NULL;
- r = address_new_dynamic(&addr);
+ r = address_new(&addr);
if (r < 0)
return r;
@@ -78,17 +75,17 @@ static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
addr->flags = IFA_F_NOPREFIXROUTE;
- addr->prefixlen = prefixlen;
+ addr->prefixlen = 128;
addr->cinfo.ifa_prefered = lifetime_preferred;
addr->cinfo.ifa_valid = lifetime_valid;
log_link_info(link,
- "DHCPv6 address "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
- SD_ICMP6_ND_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
+ "DHCPv6 address "SD_NDISC_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
+ SD_NDISC_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
addr->prefixlen, lifetime_preferred, lifetime_valid);
- r = address_update(addr, link, dhcp6_address_handler);
+ r = address_configure(addr, link, dhcp6_address_handler, true);
if (r < 0)
log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
@@ -100,7 +97,6 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
sd_dhcp6_lease *lease;
struct in6_addr ip6_addr;
uint32_t lifetime_preferred, lifetime_valid;
- uint8_t prefixlen;
r = sd_dhcp6_client_get_lease(client, &lease);
if (r < 0)
@@ -112,19 +108,7 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
&lifetime_preferred,
&lifetime_valid) >= 0) {
- r = sd_icmp6_ra_get_prefixlen(link->icmp6_router_discovery,
- &ip6_addr, &prefixlen);
- if (r < 0 && r != -EADDRNOTAVAIL) {
- log_link_warning(link, "Could not get prefix information: %s",
- strerror(-r));
- return r;
- }
-
- if (r == -EADDRNOTAVAIL)
- prefixlen = 128;
-
- r = dhcp6_address_update(link, &ip6_addr, prefixlen,
- lifetime_preferred, lifetime_valid);
+ r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
if (r < 0)
return r;
}
@@ -172,50 +156,39 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
default:
if (event < 0)
- log_link_warning(link, "DHCPv6 error: %s",
- strerror(-event));
+ log_link_warning_errno(link, event, "DHCPv6 error: %m");
else
- log_link_warning(link, "DHCPv6 unknown event: %d",
- event);
+ log_link_warning(link, "DHCPv6 unknown event: %d", event);
return;
}
- link_client_handler(link);
+ link_check_ready(link);
}
-static int dhcp6_configure(Link *link, int event) {
- int r;
- bool information_request;
+int dhcp6_configure(Link *link, bool inf_req) {
+ int r, information_request;
assert_return(link, -EINVAL);
- assert_return(IN_SET(event, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED), -EINVAL);
link->dhcp6_configured = false;
if (link->dhcp6_client) {
- r = sd_dhcp6_client_get_information_request(link->dhcp6_client,
- &information_request);
+ r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &information_request);
if (r < 0) {
- log_link_warning(link, "Could not get DHCPv6 Information request setting: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not get DHCPv6 Information request setting: %m");
goto error;
}
- if (information_request && event != SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER) {
+ if (information_request && !inf_req) {
r = sd_dhcp6_client_stop(link->dhcp6_client);
if (r < 0) {
- log_link_warning(link, "Could not stop DHCPv6 while setting Managed mode %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not stop DHCPv6 while setting Managed mode: %m");
goto error;
}
- r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
- false);
+ r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false);
if (r < 0) {
- log_link_warning(link, "Could not unset DHCPv6 Information request: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not unset DHCPv6 Information request: %m");
goto error;
}
@@ -223,8 +196,7 @@ static int dhcp6_configure(Link *link, int event) {
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0 && r != -EALREADY) {
- log_link_warning(link, "Could not restart DHCPv6: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not restart DHCPv6: %m");
goto error;
}
@@ -257,9 +229,8 @@ static int dhcp6_configure(Link *link, int event) {
if (r < 0)
goto error;
- if (event == SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER) {
- r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
- true);
+ if (inf_req) {
+ r = sd_dhcp6_client_set_information_request(link->dhcp6_client, true);
if (r < 0)
goto error;
}
@@ -274,109 +245,3 @@ static int dhcp6_configure(Link *link, int event) {
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
-
-static int dhcp6_prefix_expired(Link *link) {
- int r;
- sd_dhcp6_lease *lease;
- struct in6_addr *expired_prefix, ip6_addr;
- uint8_t expired_prefixlen;
- uint32_t lifetime_preferred, lifetime_valid;
-
- r = sd_icmp6_ra_get_expired_prefix(link->icmp6_router_discovery,
- &expired_prefix, &expired_prefixlen);
- if (r < 0)
- return r;
-
- r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease);
- if (r < 0)
- return r;
-
- log_link_info(link, "IPv6 prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d expired",
- SD_ICMP6_ND_ADDRESS_FORMAT_VAL(*expired_prefix),
- expired_prefixlen);
-
- sd_dhcp6_lease_reset_address_iter(lease);
-
- while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
- &lifetime_preferred,
- &lifetime_valid) >= 0) {
-
- r = sd_icmp6_prefix_match(expired_prefix, expired_prefixlen,
- &ip6_addr);
- if (r < 0)
- continue;
-
- log_link_info(link, "IPv6 prefix length updated "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d", SD_ICMP6_ND_ADDRESS_FORMAT_VAL(ip6_addr), 128);
-
- dhcp6_address_update(link, &ip6_addr, 128, lifetime_preferred, lifetime_valid);
- }
-
- return 0;
-}
-
-static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
- Link *link = userdata;
-
- assert(link);
- assert(link->network);
- assert(link->manager);
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return;
-
- switch(event) {
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE:
- return;
-
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER:
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED:
- dhcp6_configure(link, event);
-
- break;
-
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED:
- if (!link->rtnl_extended_attrs)
- dhcp6_prefix_expired(link);
-
- break;
-
- default:
- if (event < 0)
- log_link_warning(link, "ICMPv6 error: %s",
- strerror(-event));
- else
- log_link_warning(link, "ICMPv6 unknown event: %d",
- event);
-
- break;
- }
-
-}
-
-int icmp6_configure(Link *link) {
- int r;
-
- assert_return(link, -EINVAL);
-
- r = sd_icmp6_nd_new(&link->icmp6_router_discovery);
- if (r < 0)
- return r;
-
- r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery, NULL, 0);
- if (r < 0)
- return r;
-
- r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery, &link->mac);
- if (r < 0)
- return r;
-
- r = sd_icmp6_nd_set_index(link->icmp6_router_discovery, link->ifindex);
- if (r < 0)
- return r;
-
- r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery,
- icmp6_router_handler, link);
-
- return r;
-}
diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c
index 22efadb843..c9222b8cb8 100644
--- a/src/network/networkd-fdb.c
+++ b/src/network/networkd-fdb.c
@@ -22,12 +22,12 @@
#include <net/if.h>
#include <net/ethernet.h>
+#include "alloc-util.h"
#include "conf-parser.h"
-#include "util.h"
#include "netlink-util.h"
-
-#include "networkd.h"
#include "networkd-fdb.h"
+#include "networkd.h"
+#include "util.h"
/* create a new FDB entry or get an existing one. */
int fdb_entry_new_static(Network *const network,
@@ -197,7 +197,7 @@ int config_parse_fdb_hwaddr(
&fdb_entry->mac_addr->ether_addr_octet[5]);
if (ETHER_ADDR_LEN != r) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not a valid MAC address, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
return 0;
}
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index af3e3884e6..ed0d861e7a 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -42,9 +42,9 @@ static int ipv4ll_address_lost(Link *link) {
log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
- r = address_new_dynamic(&address);
+ r = address_new(&address);
if (r < 0) {
- log_link_error(link, "Could not allocate address: %s", strerror(-r));
+ log_link_error_errno(link, r, "Could not allocate address: %m");
return r;
}
@@ -53,22 +53,21 @@ static int ipv4ll_address_lost(Link *link) {
address->prefixlen = 16;
address->scope = RT_SCOPE_LINK;
- address_drop(address, link, &link_address_drop_handler);
+ address_remove(address, link, &link_address_remove_handler);
- r = route_new_dynamic(&route, RTPROT_UNSPEC);
+ r = route_new(&route);
if (r < 0) {
- log_link_error(link, "Could not allocate route: %s",
- strerror(-r));
+ log_link_error_errno(link, r, "Could not allocate route: %m");
return r;
}
route->family = AF_INET;
route->scope = RT_SCOPE_LINK;
- route->metrics = IPV4LL_ROUTE_METRIC;
+ route->priority = IPV4LL_ROUTE_METRIC;
- route_drop(route, link, &link_route_drop_handler);
+ route_remove(route, link, &link_route_remove_handler);
- link_client_handler(link);
+ link_check_ready(link);
return 0;
}
@@ -82,14 +81,14 @@ static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *u
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_error(link, "could not set ipv4ll route: %s", strerror(-r));
+ log_link_error_errno(link, r, "could not set ipv4ll route: %m");
link_enter_failed(link);
}
link->ipv4ll_route = true;
if (link->ipv4ll_address == true)
- link_client_handler(link);
+ link_check_ready(link);
return 1;
}
@@ -103,15 +102,15 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_error(link, "could not set ipv4ll address: %s", strerror(-r));
+ log_link_error_errno(link, r, "could not set ipv4ll address: %m");
link_enter_failed(link);
} else if (r >= 0)
- link_rtnl_process_address(rtnl, m, link->manager);
+ manager_rtnl_process_address(rtnl, m, link->manager);
link->ipv4ll_address = true;
if (link->ipv4ll_route == true)
- link_client_handler(link);
+ link_check_ready(link);
return 1;
}
@@ -134,7 +133,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u",
ADDRESS_FMT_VAL(address));
- r = address_new_dynamic(&ll_addr);
+ r = address_new(&ll_addr);
if (r < 0)
return r;
@@ -144,19 +143,20 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htonl(0xfffffffflu >> ll_addr->prefixlen);
ll_addr->scope = RT_SCOPE_LINK;
- r = address_configure(ll_addr, link, ipv4ll_address_handler);
+ r = address_configure(ll_addr, link, ipv4ll_address_handler, false);
if (r < 0)
return r;
link->ipv4ll_address = false;
- r = route_new_dynamic(&route, RTPROT_STATIC);
+ r = route_new(&route);
if (r < 0)
return r;
route->family = AF_INET;
route->scope = RT_SCOPE_LINK;
- route->metrics = IPV4LL_ROUTE_METRIC;
+ route->protocol = RTPROT_STATIC;
+ route->priority = IPV4LL_ROUTE_METRIC;
r = route_configure(route, link, ipv4ll_route_handler);
if (r < 0)
@@ -208,9 +208,11 @@ int ipv4ll_configure(Link *link) {
assert(link->network);
assert(link->network->link_local & ADDRESS_FAMILY_IPV4);
- r = sd_ipv4ll_new(&link->ipv4ll);
- if (r < 0)
- return r;
+ if (!link->ipv4ll) {
+ r = sd_ipv4ll_new(&link->ipv4ll);
+ if (r < 0)
+ return r;
+ }
if (link->udev_device) {
r = net_get_unique_predictable_data(link->udev_device, seed);
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index 1a1524dfb4..11b35d6cf8 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -22,8 +22,10 @@
#include "bus-util.h"
#include "strv.h"
-#include "networkd.h"
+#include "alloc-util.h"
#include "networkd-link.h"
+#include "networkd.h"
+#include "parse-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_administrative_state, link_state, LinkState);
@@ -101,7 +103,7 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
if (r < 0)
return 0;
- r = safe_atoi(identifier, &ifindex);
+ r = parse_ifindex(identifier, &ifindex);
if (r < 0)
return 0;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 5dd14b1104..46979ffa12 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -23,18 +23,23 @@
#include <linux/if.h>
#include <unistd.h>
-#include "util.h"
-#include "virt.h"
-#include "fileio.h"
-#include "socket-util.h"
+#include "alloc-util.h"
#include "bus-util.h"
-#include "udev-util.h"
-#include "netlink-util.h"
#include "dhcp-lease-internal.h"
+#include "event-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "netlink-util.h"
#include "network-internal.h"
-
#include "networkd-link.h"
#include "networkd-netdev.h"
+#include "set.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "udev-util.h"
+#include "util.h"
+#include "virt.h"
bool link_dhcp6_enabled(Link *link) {
if (link->flags & IFF_LOOPBACK)
@@ -129,6 +134,57 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
return link->network->ipv6_privacy_extensions;
}
+void link_update_operstate(Link *link) {
+ LinkOperationalState operstate;
+ assert(link);
+
+ if (link->kernel_operstate == IF_OPER_DORMANT)
+ operstate = LINK_OPERSTATE_DORMANT;
+ else if (link_has_carrier(link)) {
+ Address *address;
+ uint8_t scope = RT_SCOPE_NOWHERE;
+ Iterator i;
+
+ /* if we have carrier, check what addresses we have */
+ SET_FOREACH(address, link->addresses, i) {
+ if (!address_is_ready(address))
+ continue;
+
+ if (address->scope < scope)
+ scope = address->scope;
+ }
+
+ /* for operstate we also take foreign addresses into account */
+ SET_FOREACH(address, link->addresses_foreign, i) {
+ if (!address_is_ready(address))
+ continue;
+
+ if (address->scope < scope)
+ scope = address->scope;
+ }
+
+ if (scope < RT_SCOPE_SITE)
+ /* universally accessible addresses found */
+ operstate = LINK_OPERSTATE_ROUTABLE;
+ else if (scope < RT_SCOPE_HOST)
+ /* only link or site local addresses found */
+ operstate = LINK_OPERSTATE_DEGRADED;
+ else
+ /* no useful addresses found */
+ operstate = LINK_OPERSTATE_CARRIER;
+ } else if (link->flags & IFF_UP)
+ operstate = LINK_OPERSTATE_NO_CARRIER;
+ else
+ operstate = LINK_OPERSTATE_OFF;
+
+ if (link->operstate != operstate) {
+ link->operstate = operstate;
+ link_send_changed(link, "OperationalState", NULL);
+ link_dirty(link);
+ manager_dirty(link->manager);
+ }
+}
+
#define FLAG_STRING(string, flag, old, new) \
(((old ^ new) & flag) \
? ((old & flag) ? (" -" string) : (" +" string)) \
@@ -201,7 +257,7 @@ static int link_update_flags(Link *link, sd_netlink_message *m) {
link->flags = flags;
link->kernel_operstate = operstate;
- link_save(link);
+ link_update_operstate(link);
return 0;
}
@@ -291,10 +347,15 @@ static void link_free(Link *link) {
if (!link)
return;
- while ((address = link->addresses)) {
- LIST_REMOVE(addresses, link->addresses, address);
- address_free(address);
- }
+ while (!set_isempty(link->addresses))
+ address_free(set_first(link->addresses));
+
+ while (!set_isempty(link->addresses_foreign))
+ address_free(set_first(link->addresses_foreign));
+
+ link->addresses = set_free(link->addresses);
+
+ link->addresses_foreign = set_free(link->addresses_foreign);
while ((address = link->pool_addresses)) {
LIST_REMOVE(addresses, link->pool_addresses, address);
@@ -313,13 +374,14 @@ static void link_free(Link *link) {
sd_ipv4ll_unref(link->ipv4ll);
sd_dhcp6_client_unref(link->dhcp6_client);
- sd_icmp6_nd_unref(link->icmp6_router_discovery);
+ sd_ndisc_unref(link->ndisc_router_discovery);
if (link->manager)
hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
free(link->ifname);
+ (void)unlink(link->state_file);
free(link->state_file);
udev_device_unref(link->udev_device);
@@ -336,15 +398,28 @@ static void link_free(Link *link) {
}
Link *link_unref(Link *link) {
- if (link && (-- link->n_ref <= 0))
- link_free(link);
+ if (!link)
+ return NULL;
+
+ assert(link->n_ref > 0);
+
+ link->n_ref --;
+
+ if (link->n_ref > 0)
+ return NULL;
+
+ link_free(link);
return NULL;
}
Link *link_ref(Link *link) {
- if (link)
- assert_se(++ link->n_ref >= 2);
+ if (!link)
+ return NULL;
+
+ assert(link->n_ref > 0);
+
+ link->n_ref ++;
return link;
}
@@ -385,7 +460,7 @@ static void link_enter_unmanaged(Link *link) {
link_set_state(link, LINK_STATE_UNMANAGED);
- link_save(link);
+ link_dirty(link);
}
static int link_stop_clients(Link *link) {
@@ -410,16 +485,16 @@ static int link_stop_clients(Link *link) {
r = log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m");
}
- if(link->icmp6_router_discovery) {
+ if(link->ndisc_router_discovery) {
if (link->dhcp6_client) {
k = sd_dhcp6_client_stop(link->dhcp6_client);
if (k < 0)
r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
}
- k = sd_icmp6_nd_stop(link->icmp6_router_discovery);
+ k = sd_ndisc_stop(link->ndisc_router_discovery);
if (k < 0)
- r = log_link_warning_errno(link, r, "Could not stop ICMPv6 router discovery: %m");
+ r = log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m");
}
if (link->lldp) {
@@ -443,7 +518,7 @@ void link_enter_failed(Link *link) {
link_stop_clients(link);
- link_save(link);
+ link_dirty(link);
}
static Address* link_find_dhcp_server_address(Link *link) {
@@ -484,14 +559,19 @@ static int link_enter_configured(Link *link) {
link_set_state(link, LINK_STATE_CONFIGURED);
- link_save(link);
+ link_dirty(link);
return 0;
}
-void link_client_handler(Link *link) {
+void link_check_ready(Link *link) {
+ Address *a;
+ Iterator i;
+
assert(link);
- assert(link->network);
+
+ if (!link->network)
+ return;
if (!link->static_configured)
return;
@@ -501,12 +581,18 @@ void link_client_handler(Link *link) {
!link->ipv4ll_route)
return;
- if (link_dhcp4_enabled(link) && !link->dhcp4_configured)
- return;
-
- if (link_dhcp6_enabled(link) && !link->dhcp6_configured)
+ if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) &&
+ !link->dhcp4_configured) ||
+ (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) &&
+ !link->dhcp6_configured) ||
+ (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) &&
+ !link->dhcp4_configured && !link->dhcp6_configured))
return;
+ SET_FOREACH(a, link->addresses, i)
+ if (!address_is_ready(a))
+ return;
+
if (link->state != LINK_STATE_CONFIGURED)
link_enter_configured(link);
@@ -529,12 +615,12 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST)
- log_link_warning_errno(link, r, "%-*s: could not set route: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "Could not set route: %m");
if (link->link_messages == 0) {
log_link_debug(link, "Routes set");
link->static_configured = true;
- link_client_handler(link);
+ link_check_ready(link);
}
return 1;
@@ -563,14 +649,14 @@ static int link_enter_set_routes(Link *link) {
if (link->link_messages == 0) {
link->static_configured = true;
- link_client_handler(link);
+ link_check_ready(link);
} else
log_link_debug(link, "Setting routes");
return 0;
}
-int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
@@ -583,7 +669,7 @@ int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userd
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -ESRCH)
- log_link_warning_errno(link, r, "%-*s: could not drop route: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "Could not drop route: %m");
return 1;
}
@@ -607,9 +693,9 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST)
- log_link_warning_errno(link, r, "%-*s: could not set address: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "could not set address: %m");
else if (r >= 0)
- link_rtnl_process_address(rtnl, m, link->manager);
+ manager_rtnl_process_address(rtnl, m, link->manager);
if (link->link_messages == 0) {
log_link_debug(link, "Addresses set");
@@ -720,7 +806,7 @@ static int link_enter_set_addresses(Link *link) {
link_set_state(link, LINK_STATE_SETTING_ADDRESSES);
LIST_FOREACH(addresses, ad, link->network->static_addresses) {
- r = address_configure(ad, link, &address_handler);
+ r = address_configure(ad, link, &address_handler, false);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set addresses: %m");
link_enter_failed(link);
@@ -852,7 +938,7 @@ static int link_enter_set_addresses(Link *link) {
return 0;
}
-int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
@@ -865,7 +951,7 @@ int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *use
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EADDRNOTAVAIL)
- log_link_warning_errno(link, r, "%-*s: could not drop address: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "Could not drop address: %m");
return 1;
}
@@ -1017,7 +1103,7 @@ static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
r = sd_netlink_message_get_errno(m);
if (r < 0)
- log_link_warning_errno(link, r, "%-*s: could not set MTU: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "Could not set MTU: %m");
return 1;
}
@@ -1156,13 +1242,13 @@ static int link_acquire_conf(Link *link) {
}
if (link_dhcp6_enabled(link)) {
- assert(link->icmp6_router_discovery);
+ assert(link->ndisc_router_discovery);
log_link_debug(link, "Discovering IPv6 routers");
- r = sd_icmp6_router_solicitation_start(link->icmp6_router_discovery);
+ r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not start IPv6 router discovery: %m");
+ return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
}
if (link_lldp_enabled(link)) {
@@ -1205,7 +1291,7 @@ static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
if (r < 0)
/* we warn but don't fail the link, as it may be
brought up later */
- log_link_warning_errno(link, r, "%-*s: could not bring up interface: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "Could not bring up interface: %m");
return 1;
}
@@ -1292,7 +1378,7 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *user
r = sd_netlink_message_get_errno(m);
if (r < 0)
- log_link_warning_errno(link, r, "%-*s: could not bring down interface: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "Could not bring down interface: %m");
return 1;
}
@@ -1430,14 +1516,14 @@ static int link_new_bound_by_list(Link *link) {
}
if (list_updated)
- link_save(link);
+ link_dirty(link);
HASHMAP_FOREACH (carrier, link->bound_by_links, i) {
r = link_put_carrier(carrier, link, &carrier->bound_to_links);
if (r < 0)
return r;
- link_save(carrier);
+ link_dirty(carrier);
}
return 0;
@@ -1472,14 +1558,14 @@ static int link_new_bound_to_list(Link *link) {
}
if (list_updated)
- link_save(link);
+ link_dirty(link);
HASHMAP_FOREACH (carrier, link->bound_to_links, i) {
r = link_put_carrier(carrier, link, &carrier->bound_by_links);
if (r < 0)
return r;
- link_save(carrier);
+ link_dirty(carrier);
}
return 0;
@@ -1515,7 +1601,7 @@ static void link_free_bound_to_list(Link *link) {
hashmap_remove(link->bound_to_links, INT_TO_PTR(bound_to->ifindex));
if (hashmap_remove(bound_to->bound_by_links, INT_TO_PTR(link->ifindex)))
- link_save(bound_to);
+ link_dirty(bound_to);
}
return;
@@ -1529,7 +1615,7 @@ static void link_free_bound_by_list(Link *link) {
hashmap_remove(link->bound_by_links, INT_TO_PTR(bound_by->ifindex));
if (hashmap_remove(bound_by->bound_to_links, INT_TO_PTR(link->ifindex))) {
- link_save(bound_by);
+ link_dirty(bound_by);
link_handle_bound_to_list(bound_by);
}
}
@@ -1553,7 +1639,7 @@ static void link_free_carrier_maps(Link *link) {
}
if (list_updated)
- link_save(link);
+ link_dirty(link);
return;
}
@@ -1568,6 +1654,7 @@ void link_drop(Link *link) {
log_link_debug(link, "Link removed");
+ (void)unlink(link->state_file);
link_unref(link);
return;
@@ -1614,7 +1701,7 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *us
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_error_errno(link, r, "%-*s: could not join netdev: %m", IFNAMSIZ, link->ifname);
+ log_link_error_errno(link, r, "Could not join netdev: %m");
link_enter_failed(link);
return 1;
} else
@@ -1637,7 +1724,7 @@ static int link_enter_join_netdev(Link *link) {
link_set_state(link, LINK_STATE_ENSLAVING);
- link_save(link);
+ link_dirty(link);
if (!link->network->bridge &&
!link->network->bond &&
@@ -1810,16 +1897,16 @@ static int link_set_ipv6_accept_ra(Link *link) {
* disabled if local forwarding is enabled).
* If set, ignore or enforce RA independent of local forwarding state.
*/
- if (link->network->ipv6_accept_ra < 0) {
+ if (link->network->ipv6_accept_ra < 0)
/* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
v = "1";
- } else if (link->network->ipv6_accept_ra > 0) {
+ else if (link->network->ipv6_accept_ra > 0)
/* "2" means accept RA even if ip_forward is enabled */
v = "2";
- } else {
+ else
/* "0" means ignore RA */
v = "0";
- }
+
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra");
r = write_string_file(p, v, 0);
@@ -1834,6 +1921,37 @@ static int link_set_ipv6_accept_ra(Link *link) {
return 0;
}
+static int link_set_ipv6_dad_transmits(Link *link) {
+ char buf[DECIMAL_STR_MAX(unsigned) + 1];
+ const char *p = NULL;
+ int r;
+
+ /* Make this a NOP if IPv6 is not available */
+ if (!socket_ipv6_is_supported())
+ return 0;
+
+ if (link->flags & IFF_LOOPBACK)
+ return 0;
+
+ if (link->network->ipv6_dad_transmits < 0)
+ return 0;
+
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/dad_transmits");
+
+ xsprintf(buf, "%u", link->network->ipv6_dad_transmits);
+
+ r = write_string_file(p, buf, 0);
+ if (r < 0) {
+ /* If the right value is set anyway, don't complain */
+ if (verify_one_line_file(p, buf) > 0)
+ return 0;
+
+ log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m");
+ }
+
+ return 0;
+}
+
static int link_configure(Link *link) {
int r;
@@ -1861,6 +1979,10 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
+ r = link_set_ipv6_dad_transmits(link);
+ if (r < 0)
+ return r;
+
if (link_ipv4ll_enabled(link)) {
r = ipv4ll_configure(link);
if (r < 0)
@@ -1884,7 +2006,7 @@ static int link_configure(Link *link) {
}
if (link_dhcp6_enabled(link)) {
- r = icmp6_configure(link);
+ r = ndisc_configure(link);
if (r < 0)
return r;
}
@@ -1936,28 +2058,30 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
if (r < 0)
return r;
- r = network_get(link->manager, link->udev_device, link->ifname,
- &link->mac, &network);
- if (r == -ENOENT) {
- link_enter_unmanaged(link);
- return 1;
- } else if (r < 0)
- return r;
+ if (!link->network) {
+ r = network_get(link->manager, link->udev_device, link->ifname,
+ &link->mac, &network);
+ if (r == -ENOENT) {
+ link_enter_unmanaged(link);
+ return 1;
+ } else if (r < 0)
+ return r;
- if (link->flags & IFF_LOOPBACK) {
- if (network->link_local != ADDRESS_FAMILY_NO)
- log_link_debug(link, "Ignoring link-local autoconfiguration for loopback link");
+ if (link->flags & IFF_LOOPBACK) {
+ if (network->link_local != ADDRESS_FAMILY_NO)
+ log_link_debug(link, "Ignoring link-local autoconfiguration for loopback link");
- if (network->dhcp != ADDRESS_FAMILY_NO)
- log_link_debug(link, "Ignoring DHCP clients for loopback link");
+ if (network->dhcp != ADDRESS_FAMILY_NO)
+ log_link_debug(link, "Ignoring DHCP clients for loopback link");
- if (network->dhcp_server)
- log_link_debug(link, "Ignoring DHCP server for loopback link");
- }
+ if (network->dhcp_server)
+ log_link_debug(link, "Ignoring DHCP server for loopback link");
+ }
- r = network_apply(link->manager, network, link);
- if (r < 0)
- return r;
+ r = network_apply(link->manager, network, link);
+ if (r < 0)
+ return r;
+ }
r = link_new_bound_to_list(link);
if (r < 0)
@@ -2009,177 +2133,191 @@ int link_initialized(Link *link, struct udev_device *device) {
return 0;
}
-static Address* link_get_equal_address(Link *link, Address *needle) {
- Address *i;
+static int link_load(Link *link) {
+ _cleanup_free_ char *network_file = NULL,
+ *addresses = NULL,
+ *routes = NULL,
+ *dhcp4_address = NULL,
+ *ipv4ll_address = NULL;
+ union in_addr_union address;
+ union in_addr_union route_dst;
+ const char *p;
+ int r;
assert(link);
- assert(needle);
- LIST_FOREACH(addresses, i, link->addresses)
- if (address_equal(i, needle))
- return i;
-
- return NULL;
-}
-
-int link_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
- Manager *m = userdata;
- Link *link = NULL;
- uint16_t type;
- _cleanup_address_free_ Address *address = NULL;
- unsigned char flags;
- Address *existing;
- char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX];
- const char *valid_str = NULL;
- int r, ifindex;
+ r = parse_env_file(link->state_file, NEWLINE,
+ "NETWORK_FILE", &network_file,
+ "ADDRESSES", &addresses,
+ "ROUTES", &routes,
+ "DHCP4_ADDRESS", &dhcp4_address,
+ "IPV4LL_ADDRESS", &ipv4ll_address,
+ NULL);
+ if (r < 0 && r != -ENOENT)
+ return log_link_error_errno(link, r, "Failed to read %s: %m", link->state_file);
+
+ if (network_file) {
+ Network *network;
+ char *suffix;
+
+ /* drop suffix */
+ suffix = strrchr(network_file, '.');
+ if (!suffix) {
+ log_link_debug(link, "Failed to get network name from %s", network_file);
+ goto network_file_fail;
+ }
+ *suffix = '\0';
- assert(rtnl);
- assert(message);
- assert(m);
+ r = network_get_by_name(link->manager, basename(network_file), &network);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to get network %s: %m", basename(network_file));
+ goto network_file_fail;
+ }
- if (sd_netlink_message_is_error(message)) {
- r = sd_netlink_message_get_errno(message);
+ r = network_apply(link->manager, network, link);
if (r < 0)
- log_warning_errno(r, "rtnl: failed to receive address: %m");
-
- return 0;
+ return log_link_error_errno(link, r, "Failed to apply network %s: %m", basename(network_file));
}
- r = sd_netlink_message_get_type(message, &type);
- if (r < 0) {
- log_warning_errno(r, "rtnl: could not get message type: %m");
- return 0;
- } else if (type != RTM_NEWADDR && type != RTM_DELADDR) {
- log_warning("rtnl: received unexpected message type when processing address");
- return 0;
- }
-
- r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
- if (r < 0) {
- log_warning_errno(r, "rtnl: could not get ifindex from address: %m");
- return 0;
- } else if (ifindex <= 0) {
- log_warning("rtnl: received address message with invalid ifindex: %d", ifindex);
- return 0;
- } else {
- r = link_get(m, ifindex, &link);
- if (r < 0 || !link) {
- /* when enumerating we might be out of sync, but we will
- * get the address again, so just ignore it */
- if (!m->enumerating)
- log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex);
- return 0;
- }
- }
+network_file_fail:
- r = address_new_dynamic(&address);
- if (r < 0)
- return r;
+ if (addresses) {
+ p = addresses;
- r = sd_rtnl_message_addr_get_family(message, &address->family);
- if (r < 0 || !IN_SET(address->family, AF_INET, AF_INET6)) {
- log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
- return 0;
- }
+ for (;;) {
+ _cleanup_free_ char *address_str = NULL;
+ char *prefixlen_str;
+ int family;
+ unsigned char prefixlen;
- r = sd_rtnl_message_addr_get_prefixlen(message, &address->prefixlen);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m");
- return 0;
- }
+ r = extract_first_word(&p, &address_str, NULL, 0);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to extract next address string: %m");
+ continue;
+ } if (r == 0)
+ break;
- r = sd_rtnl_message_addr_get_scope(message, &address->scope);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m");
- return 0;
- }
+ prefixlen_str = strchr(address_str, '/');
+ if (!prefixlen_str) {
+ log_link_debug(link, "Failed to parse address and prefix length %s", address_str);
+ continue;
+ }
- r = sd_rtnl_message_addr_get_flags(message, &flags);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m");
- return 0;
- }
- address->flags = flags;
+ *prefixlen_str ++ = '\0';
- switch (address->family) {
- case AF_INET:
- r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &address->in_addr.in);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
- return 0;
- }
+ r = sscanf(prefixlen_str, "%hhu", &prefixlen);
+ if (r != 1) {
+ log_link_error(link, "Failed to parse prefixlen %s", prefixlen_str);
+ continue;
+ }
- break;
+ r = in_addr_from_string_auto(address_str, &family, &address);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to parse address %s: %m", address_str);
+ continue;
+ }
- case AF_INET6:
- r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &address->in_addr.in6);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
- return 0;
+ r = address_add(link, family, &address, prefixlen, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to add address: %m");
}
-
- break;
-
- default:
- assert_not_reached("invalid address family");
}
- if (!inet_ntop(address->family, &address->in_addr, buf, INET6_ADDRSTRLEN)) {
- log_link_warning(link, "Could not print address");
- return 0;
- }
+ if (routes) {
+ for (;;) {
+ Route *route;
+ _cleanup_free_ char *route_str = NULL;
+ _cleanup_event_source_unref_ sd_event_source *expire = NULL;
+ usec_t lifetime;
+ char *prefixlen_str;
+ int family;
+ unsigned char prefixlen, tos, table;
+ uint32_t priority;
- r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &address->cinfo);
- if (r >= 0) {
- if (address->cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME)
- valid_str = "ever";
- else
- valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
- address->cinfo.ifa_valid * USEC_PER_SEC,
- USEC_PER_SEC);
- }
+ r = extract_first_word(&p, &route_str, NULL, 0);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to extract next route string: %m");
+ continue;
+ } if (r == 0)
+ break;
- existing = link_get_equal_address(link, address);
+ prefixlen_str = strchr(route_str, '/');
+ if (!prefixlen_str) {
+ log_link_debug(link, "Failed to parse route %s", route_str);
+ continue;
+ }
- switch (type) {
- case RTM_NEWADDR:
- if (existing) {
- log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+ *prefixlen_str ++ = '\0';
+ r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%hhu/"USEC_FMT, &prefixlen, &tos, &priority, &table, &lifetime);
+ if (r != 5) {
+ log_link_debug(link,
+ "Failed to parse destination prefix length, tos, priority, table or expiration %s",
+ prefixlen_str);
+ continue;
+ }
- existing->scope = address->scope;
- existing->flags = address->flags;
- existing->cinfo = address->cinfo;
+ r = in_addr_from_string_auto(route_str, &family, &route_dst);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to parse route destination %s: %m", route_str);
+ continue;
+ }
- } else {
- log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+ r = route_add(link, family, &route_dst, prefixlen, tos, priority, table, &route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to add route: %m");
- LIST_PREPEND(addresses, link->addresses, address);
- address_establish(address, link);
+ if (lifetime != USEC_INFINITY) {
+ r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), lifetime,
+ 0, route_expire_handler, route);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Could not arm route expiration handler: %m");
+ }
- address = NULL;
+ route->lifetime = lifetime;
+ sd_event_source_unref(route->expire);
+ route->expire = expire;
+ expire = NULL;
+ }
+ }
- link_save(link);
+ if (dhcp4_address) {
+ r = in_addr_from_string(AF_INET, dhcp4_address, &address);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Falied to parse DHCPv4 address %s: %m", dhcp4_address);
+ goto dhcp4_address_fail;
}
- break;
+ r = sd_dhcp_client_new(&link->dhcp_client);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Falied to create DHCPv4 client: %m");
+
+ r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Falied to set inital DHCPv4 address %s: %m", dhcp4_address);
+ }
- case RTM_DELADDR:
+dhcp4_address_fail:
- if (existing) {
- log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
- address_release(existing, link);
- LIST_REMOVE(addresses, link->addresses, existing);
- address_free(existing);
- } else
- log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+ if (ipv4ll_address) {
+ r = in_addr_from_string(AF_INET, ipv4ll_address, &address);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Falied to parse IPv4LL address %s: %m", ipv4ll_address);
+ goto ipv4ll_address_fail;
+ }
- break;
- default:
- assert_not_reached("Received invalid RTNL message type");
+ r = sd_ipv4ll_new(&link->ipv4ll);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Falied to create IPv4LL client: %m");
+
+ r = sd_ipv4ll_set_address(link->ipv4ll, &address.in);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Falied to set inital IPv4LL address %s: %m", ipv4ll_address);
}
- return 1;
+ipv4ll_address_fail:
+
+ return 0;
}
int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
@@ -2201,12 +2339,18 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
log_link_debug(link, "Link %d added", link->ifindex);
+ r = link_load(link);
+ if (r < 0)
+ return r;
+
if (detect_container() <= 0) {
/* not in a container, udev will be around */
sprintf(ifindex_str, "n%d", link->ifindex);
device = udev_device_new_from_device_id(m->udev, ifindex_str);
- if (!device)
- return log_link_warning_errno(link, errno, "Could not find udev device: %m");
+ if (!device) {
+ r = log_link_warning_errno(link, errno, "Could not find udev device: %m");
+ goto failed;
+ }
if (udev_device_get_is_initialized(device) <= 0) {
/* not yet ready */
@@ -2216,17 +2360,20 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
r = link_initialized(link, device);
if (r < 0)
- return r;
+ goto failed;
} else {
/* we are calling a callback directly, so must take a ref */
link_ref(link);
r = link_initialized_and_synced(m->rtnl, NULL, link);
if (r < 0)
- return r;
+ goto failed;
}
return 0;
+failed:
+ link_enter_failed(link);
+ return r;
}
static int link_carrier_gained(Link *link) {
@@ -2414,49 +2561,13 @@ int link_update(Link *link, sd_netlink_message *m) {
return 0;
}
-static void link_update_operstate(Link *link) {
- LinkOperationalState operstate;
- assert(link);
-
- if (link->kernel_operstate == IF_OPER_DORMANT)
- operstate = LINK_OPERSTATE_DORMANT;
- else if (link_has_carrier(link)) {
- Address *address;
- uint8_t scope = RT_SCOPE_NOWHERE;
-
- /* if we have carrier, check what addresses we have */
- LIST_FOREACH(addresses, address, link->addresses) {
- if (address->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
- continue;
-
- if (address->scope < scope)
- scope = address->scope;
- }
-
- if (scope < RT_SCOPE_SITE)
- /* universally accessible addresses found */
- operstate = LINK_OPERSTATE_ROUTABLE;
- else if (scope < RT_SCOPE_HOST)
- /* only link or site local addresses found */
- operstate = LINK_OPERSTATE_DEGRADED;
- else
- /* no useful addresses found */
- operstate = LINK_OPERSTATE_CARRIER;
- } else if (link->flags & IFF_UP)
- operstate = LINK_OPERSTATE_NO_CARRIER;
- else
- operstate = LINK_OPERSTATE_OFF;
-
- if (link->operstate != operstate) {
- link->operstate = operstate;
- link_send_changed(link, "OperationalState", NULL);
- }
-}
-
int link_save(Link *link) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *admin_state, *oper_state;
+ Address *a;
+ Route *route;
+ Iterator i;
int r;
assert(link);
@@ -2464,12 +2575,6 @@ int link_save(Link *link) {
assert(link->lease_file);
assert(link->manager);
- link_update_operstate(link);
-
- r = manager_save(link->manager);
- if (r < 0)
- return r;
-
if (link->state == LINK_STATE_LINGER) {
unlink(link->state_file);
return 0;
@@ -2537,13 +2642,12 @@ int link_save(Link *link) {
if (space)
fputc(' ', f);
serialize_in6_addrs(f, in6_addrs, r);
- space = true;
}
}
- fputs("\n", f);
+ fputc('\n', f);
- fprintf(f, "NTP=");
+ fputs("NTP=", f);
space = false;
STRV_FOREACH(address, link->network->ntp) {
if (space)
@@ -2590,9 +2694,9 @@ int link_save(Link *link) {
}
}
- fputs("\n", f);
+ fputc('\n', f);
- fprintf(f, "DOMAINS=");
+ fputs("DOMAINS=", f);
space = false;
STRV_FOREACH(domain, link->network->domains) {
if (space)
@@ -2628,18 +2732,48 @@ int link_save(Link *link) {
}
}
- fputs("\n", f);
+ fputc('\n', f);
fprintf(f, "WILDCARD_DOMAIN=%s\n",
yes_no(link->network->wildcard_domain));
fprintf(f, "LLMNR=%s\n",
resolve_support_to_string(link->network->llmnr));
+
+ fputs("ADDRESSES=", f);
+ space = false;
+ SET_FOREACH(a, link->addresses, i) {
+ _cleanup_free_ char *address_str = NULL;
+
+ r = in_addr_to_string(a->family, &a->in_addr, &address_str);
+ if (r < 0)
+ goto fail;
+
+ fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen);
+ space = true;
+ }
+
+ fputc('\n', f);
+
+ fputs("ROUTES=", f);
+ space = false;
+ SET_FOREACH(route, link->routes, i) {
+ _cleanup_free_ char *route_str = NULL;
+
+ r = in_addr_to_string(route->family, &route->dst, &route_str);
+ if (r < 0)
+ goto fail;
+
+ fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%hhu/"USEC_FMT, space ? " " : "", route_str,
+ route->dst_prefixlen, route->tos, route->priority, route->table, route->lifetime);
+ space = true;
+ }
+
+ fputc('\n', f);
}
if (!hashmap_isempty(link->bound_to_links)) {
Link *carrier;
- Iterator i;
bool space = false;
fputs("CARRIER_BOUND_TO=", f);
@@ -2650,12 +2784,11 @@ int link_save(Link *link) {
space = true;
}
- fputs("\n", f);
+ fputc('\n', f);
}
if (!hashmap_isempty(link->bound_by_links)) {
Link *carrier;
- Iterator i;
bool space = false;
fputs("CARRIER_BOUND_BY=", f);
@@ -2666,19 +2799,25 @@ int link_save(Link *link) {
space = true;
}
- fputs("\n", f);
+ fputc('\n', f);
}
if (link->dhcp_lease) {
+ struct in_addr address;
const char *tz = NULL;
+ assert(link->network);
+
r = sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
if (r >= 0)
fprintf(f, "TIMEZONE=%s\n", tz);
- }
- if (link->dhcp_lease) {
- assert(link->network);
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ if (r >= 0) {
+ fputs("DHCP4_ADDRESS=", f);
+ serialize_in_addrs(f, &address, 1);
+ fputc('\n', f);
+ }
r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
if (r < 0)
@@ -2690,6 +2829,17 @@ int link_save(Link *link) {
} else
unlink(link->lease_file);
+ if (link->ipv4ll) {
+ struct in_addr address;
+
+ r = sd_ipv4ll_get_address(link->ipv4ll, &address);
+ if (r >= 0) {
+ fputs("IPV4LL_ADDRESS=", f);
+ serialize_in_addrs(f, &address, 1);
+ fputc('\n', f);
+ }
+ }
+
if (link->lldp) {
assert(link->network);
@@ -2722,6 +2872,34 @@ fail:
return log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file);
}
+/* The serialized state in /run is no longer up-to-date. */
+void link_dirty(Link *link) {
+ int r;
+
+ assert(link);
+
+ r = set_ensure_allocated(&link->manager->dirty_links, NULL);
+ if (r < 0)
+ /* allocation errors are ignored */
+ return;
+
+ r = set_put(link->manager->dirty_links, link);
+ if (r < 0)
+ /* allocation errors are ignored */
+ return;
+
+ link_ref(link);
+}
+
+/* The serialized state in /run is up-to-date */
+void link_clean(Link *link) {
+ assert(link);
+ assert(link->manager);
+
+ set_remove(link->manager->dirty_links, link);
+ link_unref(link);
+}
+
static const char* const link_state_table[_LINK_STATE_MAX] = {
[LINK_STATE_PENDING] = "pending",
[LINK_STATE_ENSLAVING] = "configuring",
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 7b219c6854..a22041870e 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -25,10 +25,10 @@
#include "sd-dhcp-client.h"
#include "sd-dhcp-server.h"
-#include "sd-ipv4ll.h"
-#include "sd-icmp6-nd.h"
#include "sd-dhcp6-client.h"
+#include "sd-ipv4ll.h"
#include "sd-lldp.h"
+#include "sd-ndisc.h"
typedef struct Link Link;
@@ -83,7 +83,10 @@ struct Link {
unsigned link_messages;
unsigned enslaving;
- LIST_HEAD(Address, addresses);
+ Set *addresses;
+ Set *addresses_foreign;
+ Set *routes;
+ Set *routes_foreign;
sd_dhcp_client *dhcp_client;
sd_dhcp_lease *dhcp_lease;
@@ -103,7 +106,7 @@ struct Link {
sd_dhcp_server *dhcp_server;
- sd_icmp6_nd *icmp6_router_discovery;
+ sd_ndisc *ndisc_router_discovery;
sd_dhcp6_client *dhcp6_client;
bool rtnl_extended_attrs;
@@ -120,17 +123,19 @@ int link_get(Manager *m, int ifindex, Link **ret);
int link_add(Manager *manager, sd_netlink_message *message, Link **ret);
void link_drop(Link *link);
-int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
-int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
+int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
+int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
void link_enter_failed(Link *link);
int link_initialized(Link *link, struct udev_device *device);
-void link_client_handler(Link *link);
+void link_check_ready(Link *link);
+void link_update_operstate(Link *link);
int link_update(Link *link, sd_netlink_message *message);
-int link_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata);
+void link_dirty(Link *link);
+void link_clean(Link *link);
int link_save(Link *link);
int link_carrier_reset(Link *link);
@@ -142,7 +147,8 @@ int link_set_timezone(Link *link, const char *timezone);
int ipv4ll_configure(Link *link);
int dhcp4_configure(Link *link);
-int icmp6_configure(Link *link);
+int dhcp6_configure(Link *link, bool information_request);
+int ndisc_configure(Link *link);
bool link_lldp_enabled(Link *link);
bool link_ipv4ll_enabled(Link *link);
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index b281f4fdb6..dafaf2daea 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-util.h"
-
#include "networkd.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index b4259cafef..a5701001c1 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -22,21 +22,23 @@
#include <sys/socket.h>
#include <linux/if.h>
-#include "sd-netlink.h"
#include "sd-daemon.h"
+#include "sd-netlink.h"
-#include "conf-parser.h"
-#include "path-util.h"
-#include "libudev-private.h"
-#include "udev-util.h"
-#include "netlink-util.h"
+#include "alloc-util.h"
#include "bus-util.h"
+#include "conf-parser.h"
#include "def.h"
-#include "virt.h"
-#include "set.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "libudev-private.h"
#include "local-addresses.h"
-
+#include "netlink-util.h"
#include "networkd.h"
+#include "path-util.h"
+#include "set.h"
+#include "udev-util.h"
+#include "virt.h"
/* use 8 MB for receive socket kernel queue. */
#define RCVBUF_SIZE (8*1024*1024)
@@ -277,6 +279,348 @@ static int manager_connect_udev(Manager *m) {
return 0;
}
+int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+ Manager *m = userdata;
+ Link *link = NULL;
+ uint16_t type;
+ uint32_t ifindex, priority = 0;
+ unsigned char protocol, scope, tos, table;
+ int family;
+ unsigned char dst_prefixlen, src_prefixlen;
+ union in_addr_union dst = {}, gw = {}, src = {}, prefsrc = {};
+ Route *route = NULL;
+ int r;
+
+ assert(rtnl);
+ assert(message);
+ assert(m);
+
+ if (sd_netlink_message_is_error(message)) {
+ r = sd_netlink_message_get_errno(message);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to receive route: %m");
+
+ return 0;
+ }
+
+ r = sd_netlink_message_get_type(message, &type);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get message type: %m");
+ return 0;
+ } else if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
+ log_warning("rtnl: received unexpected message type when processing route");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
+ if (r == -ENODATA) {
+ log_debug("rtnl: received route without ifindex, ignoring");
+ return 0;
+ } else if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get ifindex from route, ignoring: %m");
+ return 0;
+ } else if (ifindex <= 0) {
+ log_warning("rtnl: received route message with invalid ifindex, ignoring: %d", ifindex);
+ return 0;
+ } else {
+ r = link_get(m, ifindex, &link);
+ if (r < 0 || !link) {
+ /* when enumerating we might be out of sync, but we will
+ * get the route again, so just ignore it */
+ if (!m->enumerating)
+ log_warning("rtnl: received route for nonexistent link (%d), ignoring", ifindex);
+ return 0;
+ }
+ }
+
+ r = sd_rtnl_message_route_get_family(message, &family);
+ if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
+ log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_protocol(message, &protocol);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get route protocol: %m");
+ return 0;
+ }
+
+ switch (family) {
+ case AF_INET:
+ r = sd_netlink_message_read_in_addr(message, RTA_DST, &dst.in);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &gw.in);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in_addr(message, RTA_SRC, &src.in);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &prefsrc.in);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
+ return 0;
+ }
+
+ break;
+
+ case AF_INET6:
+ r = sd_netlink_message_read_in6_addr(message, RTA_DST, &dst.in6);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &gw.in6);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &src.in6);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &prefsrc.in6);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
+ return 0;
+ }
+
+ break;
+
+ default:
+ log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_dst_prefixlen(message, &dst_prefixlen);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid destination prefixlen, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_src_prefixlen(message, &src_prefixlen);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid source prefixlen, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_scope(message, &scope);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid scope, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_tos(message, &tos);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid tos, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_table(message, &table);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid table, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &priority);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid priority, ignoring: %m");
+ return 0;
+ }
+
+ route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
+
+ switch (type) {
+ case RTM_NEWROUTE:
+ if (!route) {
+ /* A route appeared that we did not request */
+ r = route_add_foreign(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
+ if (r < 0)
+ return 0;
+ }
+
+ route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol);
+
+ break;
+
+ case RTM_DELROUTE:
+
+ if (route)
+ route_drop(route);
+
+ break;
+ default:
+ assert_not_reached("Received invalid RTNL message type");
+ }
+
+ return 1;
+}
+
+int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+ Manager *m = userdata;
+ Link *link = NULL;
+ uint16_t type;
+ unsigned char flags;
+ int family;
+ unsigned char prefixlen;
+ unsigned char scope;
+ union in_addr_union in_addr;
+ struct ifa_cacheinfo cinfo;
+ Address *address = NULL;
+ char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX];
+ const char *valid_str = NULL;
+ int r, ifindex;
+
+ assert(rtnl);
+ assert(message);
+ assert(m);
+
+ if (sd_netlink_message_is_error(message)) {
+ r = sd_netlink_message_get_errno(message);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to receive address: %m");
+
+ return 0;
+ }
+
+ r = sd_netlink_message_get_type(message, &type);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get message type: %m");
+ return 0;
+ } else if (type != RTM_NEWADDR && type != RTM_DELADDR) {
+ log_warning("rtnl: received unexpected message type when processing address");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get ifindex from address: %m");
+ return 0;
+ } else if (ifindex <= 0) {
+ log_warning("rtnl: received address message with invalid ifindex: %d", ifindex);
+ return 0;
+ } else {
+ r = link_get(m, ifindex, &link);
+ if (r < 0 || !link) {
+ /* when enumerating we might be out of sync, but we will
+ * get the address again, so just ignore it */
+ if (!m->enumerating)
+ log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex);
+ return 0;
+ }
+ }
+
+ r = sd_rtnl_message_addr_get_family(message, &family);
+ if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
+ log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_scope(message, &scope);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_flags(message, &flags);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m");
+ return 0;
+ }
+
+ switch (family) {
+ case AF_INET:
+ r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
+ return 0;
+ }
+
+ break;
+
+ case AF_INET6:
+ r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
+ return 0;
+ }
+
+ break;
+
+ default:
+ log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
+ }
+
+ if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) {
+ log_link_warning(link, "Could not print address");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
+ if (r >= 0) {
+ if (cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME)
+ valid_str = "ever";
+ else
+ valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
+ cinfo.ifa_valid * USEC_PER_SEC,
+ USEC_PER_SEC);
+ }
+
+ address_get(link, family, &in_addr, prefixlen, &address);
+
+ switch (type) {
+ case RTM_NEWADDR:
+ if (address)
+ log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, prefixlen, valid_str);
+ else {
+ /* An address appeared that we did not request */
+ r = address_add_foreign(link, family, &in_addr, prefixlen, &address);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen);
+ return 0;
+ } else
+ log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, prefixlen, valid_str);
+ }
+
+ address_update(address, flags, scope, &cinfo);
+
+ break;
+
+ case RTM_DELADDR:
+
+ if (address) {
+ log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, prefixlen, valid_str);
+ address_drop(address);
+ } else
+ log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, prefixlen, valid_str);
+
+ break;
+ default:
+ assert_not_reached("Received invalid RTNL message type");
+ }
+
+ return 1;
+}
+
static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
Manager *m = userdata;
Link *link = NULL;
@@ -410,17 +754,232 @@ static int manager_connect_rtnl(Manager *m) {
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &link_rtnl_process_address, m);
+ r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &manager_rtnl_process_address, m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &manager_rtnl_process_address, m);
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &link_rtnl_process_address, m);
+ r = sd_netlink_add_match(m->rtnl, RTM_NEWROUTE, &manager_rtnl_process_route, m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_add_match(m->rtnl, RTM_DELROUTE, &manager_rtnl_process_route, m);
if (r < 0)
return r;
return 0;
}
+static int set_put_in_addr(Set *s, const struct in_addr *address) {
+ char *p;
+ int r;
+
+ assert(s);
+
+ r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
+ if (r < 0)
+ return r;
+
+ r = set_consume(s, p);
+ if (r == -EEXIST)
+ return 0;
+
+ return r;
+}
+
+static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
+ int r, i, c = 0;
+
+ assert(s);
+ assert(n <= 0 || addresses);
+
+ for (i = 0; i < n; i++) {
+ r = set_put_in_addr(s, addresses+i);
+ if (r < 0)
+ return r;
+
+ c += r;
+ }
+
+ return c;
+}
+
+static void print_string_set(FILE *f, const char *field, Set *s) {
+ bool space = false;
+ Iterator i;
+ char *p;
+
+ if (set_isempty(s))
+ return;
+
+ fputs(field, f);
+
+ SET_FOREACH(p, s, i) {
+ if (space)
+ fputc(' ', f);
+ fputs(p, f);
+ space = true;
+ }
+ fputc('\n', f);
+}
+
+static int manager_save(Manager *m) {
+ _cleanup_set_free_free_ Set *dns = NULL, *ntp = NULL, *domains = NULL;
+ Link *link;
+ Iterator i;
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ LinkOperationalState operstate = LINK_OPERSTATE_OFF;
+ const char *operstate_str;
+ int r;
+
+ assert(m);
+ assert(m->state_file);
+
+ /* We add all NTP and DNS server to a set, to filter out duplicates */
+ dns = set_new(&string_hash_ops);
+ if (!dns)
+ return -ENOMEM;
+
+ ntp = set_new(&string_hash_ops);
+ if (!ntp)
+ return -ENOMEM;
+
+ domains = set_new(&string_hash_ops);
+ if (!domains)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH(link, m->links, i) {
+ if (link->flags & IFF_LOOPBACK)
+ continue;
+
+ if (link->operstate > operstate)
+ operstate = link->operstate;
+
+ if (!link->network)
+ continue;
+
+ /* First add the static configured entries */
+ r = set_put_strdupv(dns, link->network->dns);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdupv(ntp, link->network->ntp);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdupv(domains, link->network->domains);
+ if (r < 0)
+ return r;
+
+ if (!link->dhcp_lease)
+ continue;
+
+ /* Secondly, add the entries acquired via DHCP */
+ if (link->network->dhcp_dns) {
+ const struct in_addr *addresses;
+
+ r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
+ if (r > 0) {
+ r = set_put_in_addrv(dns, addresses, r);
+ if (r < 0)
+ return r;
+ } else if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (link->network->dhcp_ntp) {
+ const struct in_addr *addresses;
+
+ r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
+ if (r > 0) {
+ r = set_put_in_addrv(ntp, addresses, r);
+ if (r < 0)
+ return r;
+ } else if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (link->network->dhcp_domains) {
+ const char *domainname;
+
+ r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
+ if (r >= 0) {
+ r = set_put_strdup(domains, domainname);
+ if (r < 0)
+ return r;
+ } else if (r != -ENODATA)
+ return r;
+ }
+ }
+
+ operstate_str = link_operstate_to_string(operstate);
+ assert(operstate_str);
+
+ r = fopen_temporary(m->state_file, &f, &temp_path);
+ if (r < 0)
+ return r;
+
+ fchmod(fileno(f), 0644);
+
+ fprintf(f,
+ "# This is private data. Do not parse.\n"
+ "OPER_STATE=%s\n", operstate_str);
+
+ print_string_set(f, "DNS=", dns);
+ print_string_set(f, "NTP=", ntp);
+ print_string_set(f, "DOMAINS=", domains);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
+
+ if (rename(temp_path, m->state_file) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (m->operational_state != operstate) {
+ m->operational_state = operstate;
+ r = manager_send_changed(m, "OperationalState", NULL);
+ if (r < 0)
+ log_error_errno(r, "Could not emit changed OperationalState: %m");
+ }
+
+ m->dirty = false;
+
+ return 0;
+
+fail:
+ (void) unlink(m->state_file);
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
+}
+
+static int manager_dirty_handler(sd_event_source *s, void *userdata) {
+ Manager *m = userdata;
+ Link *link;
+ Iterator i;
+ int r;
+
+ assert(m);
+
+ if (m->dirty)
+ manager_save(m);
+
+ SET_FOREACH(link, m->dirty_links, i) {
+ r = link_save(link);
+ if (r >= 0)
+ link_clean(link);
+ }
+
+ return 1;
+}
+
int manager_new(Manager **ret) {
_cleanup_manager_free_ Manager *m = NULL;
int r;
@@ -442,6 +1001,10 @@ int manager_new(Manager **ret) {
sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m);
+ if (r < 0)
+ return r;
+
r = manager_connect_rtnl(m);
if (r < 0)
return r;
@@ -477,14 +1040,6 @@ void manager_free(Manager *m) {
free(m->state_file);
- sd_event_source_unref(m->udev_event_source);
- udev_monitor_unref(m->udev_monitor);
- udev_unref(m->udev);
-
- sd_bus_unref(m->bus);
- sd_bus_slot_unref(m->prepare_for_sleep_slot);
- sd_event_source_unref(m->bus_retry_event_source);
-
while ((link = hashmap_first(m->links)))
link_unref(link);
hashmap_free(m->links);
@@ -504,6 +1059,14 @@ void manager_free(Manager *m) {
sd_netlink_unref(m->rtnl);
sd_event_unref(m->event);
+ sd_event_source_unref(m->udev_event_source);
+ udev_monitor_unref(m->udev_monitor);
+ udev_unref(m->udev);
+
+ sd_bus_unref(m->bus);
+ sd_bus_slot_unref(m->prepare_for_sleep_slot);
+ sd_event_source_unref(m->bus_retry_event_source);
+
free(m);
}
@@ -536,8 +1099,19 @@ static bool manager_check_idle(void *userdata) {
}
int manager_run(Manager *m) {
+ Link *link;
+ Iterator i;
+
assert(m);
+ /* The dirty handler will deal with future serialization, but the first one
+ must be done explicitly. */
+
+ manager_save(m);
+
+ HASHMAP_FOREACH(link, m->links, i)
+ link_save(link);
+
if (m->bus)
return bus_event_loop_with_idle(
m->event,
@@ -633,7 +1207,7 @@ int manager_rtnl_enumerate_addresses(Manager *m) {
m->enumerating = true;
- k = link_rtnl_process_address(m->rtnl, addr, m);
+ k = manager_rtnl_process_address(m->rtnl, addr, m);
if (k < 0)
r = k;
@@ -643,189 +1217,39 @@ int manager_rtnl_enumerate_addresses(Manager *m) {
return r;
}
-static int set_put_in_addr(Set *s, const struct in_addr *address) {
- char *p;
+int manager_rtnl_enumerate_routes(Manager *m) {
+ _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ sd_netlink_message *route;
int r;
- assert(s);
+ assert(m);
+ assert(m->rtnl);
- r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
+ r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0);
if (r < 0)
return r;
- r = set_consume(s, p);
- if (r == -EEXIST)
- return 0;
-
- return r;
-}
-
-static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
- int r, i, c = 0;
-
- assert(s);
- assert(n <= 0 || addresses);
-
- for (i = 0; i < n; i++) {
- r = set_put_in_addr(s, addresses+i);
- if (r < 0)
- return r;
-
- c += r;
- }
-
- return c;
-}
-
-static void print_string_set(FILE *f, const char *field, Set *s) {
- bool space = false;
- Iterator i;
- char *p;
-
- if (set_isempty(s))
- return;
-
- fputs(field, f);
-
- SET_FOREACH(p, s, i) {
- if (space)
- fputc(' ', f);
- fputs(p, f);
- space = true;
- }
- fputc('\n', f);
-}
-
-int manager_save(Manager *m) {
- _cleanup_set_free_free_ Set *dns = NULL, *ntp = NULL, *domains = NULL;
- Link *link;
- Iterator i;
- _cleanup_free_ char *temp_path = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- LinkOperationalState operstate = LINK_OPERSTATE_OFF;
- const char *operstate_str;
- int r;
-
- assert(m);
- assert(m->state_file);
-
- /* We add all NTP and DNS server to a set, to filter out duplicates */
- dns = set_new(&string_hash_ops);
- if (!dns)
- return -ENOMEM;
-
- ntp = set_new(&string_hash_ops);
- if (!ntp)
- return -ENOMEM;
-
- domains = set_new(&string_hash_ops);
- if (!domains)
- return -ENOMEM;
-
- HASHMAP_FOREACH(link, m->links, i) {
- if (link->flags & IFF_LOOPBACK)
- continue;
-
- if (link->operstate > operstate)
- operstate = link->operstate;
-
- if (!link->network)
- continue;
-
- /* First add the static configured entries */
- r = set_put_strdupv(dns, link->network->dns);
- if (r < 0)
- return r;
-
- r = set_put_strdupv(ntp, link->network->ntp);
- if (r < 0)
- return r;
-
- r = set_put_strdupv(domains, link->network->domains);
- if (r < 0)
- return r;
-
- if (!link->dhcp_lease)
- continue;
-
- /* Secondly, add the entries acquired via DHCP */
- if (link->network->dhcp_dns) {
- const struct in_addr *addresses;
-
- r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
- if (r > 0) {
- r = set_put_in_addrv(dns, addresses, r);
- if (r < 0)
- return r;
- } else if (r < 0 && r != -ENODATA)
- return r;
- }
-
- if (link->network->dhcp_ntp) {
- const struct in_addr *addresses;
-
- r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
- if (r > 0) {
- r = set_put_in_addrv(ntp, addresses, r);
- if (r < 0)
- return r;
- } else if (r < 0 && r != -ENODATA)
- return r;
- }
-
- if (link->network->dhcp_domains) {
- const char *domainname;
-
- r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
- if (r >= 0) {
- r = set_put_strdup(domains, domainname);
- if (r < 0)
- return r;
- } else if (r != -ENODATA)
- return r;
- }
- }
-
- operstate_str = link_operstate_to_string(operstate);
- assert(operstate_str);
-
- r = fopen_temporary(m->state_file, &f, &temp_path);
+ r = sd_netlink_message_request_dump(req, true);
if (r < 0)
return r;
- fchmod(fileno(f), 0644);
-
- fprintf(f,
- "# This is private data. Do not parse.\n"
- "OPER_STATE=%s\n", operstate_str);
-
- print_string_set(f, "DNS=", dns);
- print_string_set(f, "NTP=", ntp);
- print_string_set(f, "DOMAINS=", domains);
-
- r = fflush_and_check(f);
+ r = sd_netlink_call(m->rtnl, req, 0, &reply);
if (r < 0)
- goto fail;
+ return r;
- if (rename(temp_path, m->state_file) < 0) {
- r = -errno;
- goto fail;
- }
+ for (route = reply; route; route = sd_netlink_message_next(route)) {
+ int k;
- if (m->operational_state != operstate) {
- m->operational_state = operstate;
- r = manager_send_changed(m, "OperationalState", NULL);
- if (r < 0)
- log_error_errno(r, "Could not emit changed OperationalState: %m");
- }
+ m->enumerating = true;
- return 0;
+ k = manager_rtnl_process_route(m->rtnl, route, m);
+ if (k < 0)
+ r = k;
-fail:
- (void) unlink(m->state_file);
- (void) unlink(temp_path);
+ m->enumerating = false;
+ }
- return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
+ return r;
}
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
@@ -884,3 +1308,10 @@ Link* manager_find_uplink(Manager *m, Link *exclude) {
return NULL;
}
+
+void manager_dirty(Manager *manager) {
+ assert(manager);
+
+ /* the serialized state in /run is no longer up-to-date */
+ manager->dirty = true;
+}
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
new file mode 100644
index 0000000000..33e692f97f
--- /dev/null
+++ b/src/network/networkd-ndisc.c
@@ -0,0 +1,88 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ 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 "sd-ndisc.h"
+
+#include "networkd-link.h"
+
+static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) {
+ Link *link = userdata;
+
+ assert(link);
+ assert(link->network);
+ assert(link->manager);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return;
+
+ switch(event) {
+ case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE:
+ return;
+
+ case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER:
+ dhcp6_configure(link, true);
+
+ break;
+ case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
+ case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED:
+ dhcp6_configure(link, false);
+
+ break;
+
+ default:
+ if (event < 0)
+ log_link_warning_errno(link, event, "IPv6 Neighbor Discover error: %m");
+ else
+ log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
+
+ break;
+ }
+}
+
+int ndisc_configure(Link *link) {
+ int r;
+
+ assert_return(link, -EINVAL);
+
+ r = sd_ndisc_new(&link->ndisc_router_discovery);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_attach_event(link->ndisc_router_discovery, NULL, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_set_mac(link->ndisc_router_discovery, &link->mac);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_set_index(link->ndisc_router_discovery, link->ifindex);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_set_callback(link->ndisc_router_discovery,
+ ndisc_router_handler, link);
+
+ return r;
+}
diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c
index 12e2321674..70105b8aa0 100644
--- a/src/network/networkd-netdev-bond.c
+++ b/src/network/networkd-netdev-bond.c
@@ -23,10 +23,14 @@
#include <netinet/ether.h>
#include <linux/if_bonding.h>
-#include "conf-parser.h"
#include "sd-netlink.h"
-#include "networkd-netdev-bond.h"
+
+#include "alloc-util.h"
+#include "conf-parser.h"
#include "missing.h"
+#include "networkd-netdev-bond.h"
+#include "string-util.h"
+#include "string-table.h"
/*
* Number of seconds between instances where the bonding
@@ -178,15 +182,18 @@ static uint8_t bond_xmit_hash_policy_to_kernel(BondXmitHashPolicy policy) {
}
static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
- Bond *b = BOND(netdev);
+ Bond *b;
ArpIpTarget *target = NULL;
int r, i = 0;
assert(netdev);
assert(!link);
- assert(b);
assert(m);
+ b = BOND(netdev);
+
+ assert(b);
+
if (b->mode != _NETDEV_BOND_MODE_INVALID) {
r = sd_netlink_message_append_u8(m, IFLA_BOND_MODE,
bond_mode_to_kernel(b->mode));
@@ -357,12 +364,12 @@ int config_parse_arp_ip_target_address(const char *unit,
r = in_addr_from_string_auto(n, &f, &buffer->ip);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Bond ARP ip target address is invalid, ignoring assignment: %s", n);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Bond ARP ip target address is invalid, ignoring assignment: %s", n);
return 0;
}
if (f != AF_INET) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Bond ARP ip target address is invalid, ignoring assignment: %s", n);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Bond ARP ip target address is invalid, ignoring assignment: %s", n);
return 0;
}
@@ -373,16 +380,19 @@ int config_parse_arp_ip_target_address(const char *unit,
}
if (b->n_arp_ip_targets > NETDEV_BOND_ARP_TARGETS_MAX)
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "More than the maximum number of kernel-supported ARP ip targets specified: %d > %d", b->n_arp_ip_targets, NETDEV_BOND_ARP_TARGETS_MAX);
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "More than the maximum number of kernel-supported ARP ip targets specified: %d > %d", b->n_arp_ip_targets, NETDEV_BOND_ARP_TARGETS_MAX);
return 0;
}
static void bond_done(NetDev *netdev) {
ArpIpTarget *t = NULL, *n = NULL;
- Bond *b = BOND(netdev);
+ Bond *b;
assert(netdev);
+
+ b = BOND(netdev);
+
assert(b);
LIST_FOREACH_SAFE(arp_ip_target, t, n, b->arp_ip_targets)
@@ -392,9 +402,12 @@ static void bond_done(NetDev *netdev) {
}
static void bond_init(NetDev *netdev) {
- Bond *b = BOND(netdev);
+ Bond *b;
assert(netdev);
+
+ b = BOND(netdev);
+
assert(b);
b->mode = _NETDEV_BOND_MODE_INVALID;
diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c
index fd6af7e99b..57c58d83b4 100644
--- a/src/network/networkd-netdev-bridge.c
+++ b/src/network/networkd-netdev-bridge.c
@@ -20,12 +20,97 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <net/if.h>
#include "networkd-netdev-bridge.h"
#include "missing.h"
+#include "netlink-util.h"
+
+/* callback for brige netdev's parameter set */
+static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ _cleanup_netdev_unref_ NetDev *netdev = userdata;
+ int r;
+
+ assert(netdev);
+ assert(m);
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0) {
+ log_netdev_warning_errno(netdev, r, "Bridge parameters could not be set: %m");
+ return 1;
+ }
+
+ log_netdev_debug(netdev, "Bridge parametres set success");
+
+ return 1;
+}
+
+static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+ _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ Bridge *b;
+ int r;
+
+ assert(netdev);
+
+ b = BRIDGE(netdev);
+
+ assert(b);
+
+ r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, netdev->ifindex);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m");
+
+ r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set netlink flags: %m");
+
+ r = sd_netlink_message_open_container(req, IFLA_LINKINFO);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_PROTINFO attribute: %m");
+
+ r = sd_netlink_message_open_container_union(req, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+ /* convert to jiffes */
+ if (b->forward_delay > 0) {
+ r = sd_netlink_message_append_u32(req, IFLA_BR_FORWARD_DELAY, usec_to_jiffies(b->forward_delay));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_FORWARD_DELAY attribute: %m");
+ }
+
+ if (b->hello_time > 0) {
+ r = sd_netlink_message_append_u32(req, IFLA_BR_HELLO_TIME, usec_to_jiffies(b->hello_time));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_HELLO_TIME attribute: %m");
+ }
+
+ if (b->max_age > 0) {
+ r = sd_netlink_message_append_u32(req, IFLA_BR_MAX_AGE, usec_to_jiffies(b->max_age));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MAX_AGE attribute: %m");
+ }
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+ r = sd_netlink_call_async(netdev->manager->rtnl, req, netdev_bridge_set_handler, netdev, 0, NULL);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
+
+ netdev_ref(netdev);
+
+ return r;
+}
const NetDevVTable bridge_vtable = {
.object_size = sizeof(Bridge),
- .sections = "Match\0NetDev\0",
+ .sections = "Match\0NetDev\0Bridge\0",
+ .post_create = netdev_bridge_post_create,
.create_type = NETDEV_CREATE_MASTER,
};
diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h
index a7d02b1c91..d3bd15e0d6 100644
--- a/src/network/networkd-netdev-bridge.h
+++ b/src/network/networkd-netdev-bridge.h
@@ -27,6 +27,10 @@ typedef struct Bridge Bridge;
struct Bridge {
NetDev meta;
+
+ usec_t forward_delay;
+ usec_t hello_time;
+ usec_t max_age;
};
extern const NetDevVTable bridge_vtable;
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index e0bd0e024a..4aac239850 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -86,3 +86,6 @@ Bond.UpDelaySec, config_parse_sec, 0,
Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
+Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
+Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
+Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
diff --git a/src/network/networkd-netdev-ipvlan.c b/src/network/networkd-netdev-ipvlan.c
index 5eb4a1eb36..27cb7d1bf0 100644
--- a/src/network/networkd-netdev-ipvlan.c
+++ b/src/network/networkd-netdev-ipvlan.c
@@ -21,8 +21,9 @@
#include <net/if.h>
-#include "networkd-netdev-ipvlan.h"
#include "conf-parser.h"
+#include "networkd-netdev-ipvlan.h"
+#include "string-table.h"
static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = {
[NETDEV_IPVLAN_MODE_L2] = "L2",
@@ -33,14 +34,17 @@ DEFINE_STRING_TABLE_LOOKUP(ipvlan_mode, IPVlanMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_mode, ipvlan_mode, IPVlanMode, "Failed to parse ipvlan mode");
static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
- IPVlan *m = IPVLAN(netdev);
+ IPVlan *m;
int r;
assert(netdev);
- assert(m);
assert(link);
assert(netdev->ifname);
+ m = IPVLAN(netdev);
+
+ assert(m);
+
if (m->mode != _NETDEV_IPVLAN_MODE_INVALID) {
r = sd_netlink_message_append_u16(req, IFLA_IPVLAN_MODE, m->mode);
if (r < 0)
@@ -51,9 +55,12 @@ static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netl
}
static void ipvlan_init(NetDev *n) {
- IPVlan *m = IPVLAN(n);
+ IPVlan *m;
assert(n);
+
+ m = IPVLAN(n);
+
assert(m);
m->mode = _NETDEV_IPVLAN_MODE_INVALID;
diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c
index e17de793ce..7144823b2d 100644
--- a/src/network/networkd-netdev-macvlan.c
+++ b/src/network/networkd-netdev-macvlan.c
@@ -21,8 +21,9 @@
#include <net/if.h>
-#include "networkd-netdev-macvlan.h"
#include "conf-parser.h"
+#include "networkd-netdev-macvlan.h"
+#include "string-table.h"
static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
[NETDEV_MACVLAN_MODE_PRIVATE] = "private",
diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c
index a906e473b6..385338849f 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/networkd-netdev-tunnel.c
@@ -26,11 +26,15 @@
#include <linux/ip6_tunnel.h>
#include "sd-netlink.h"
-#include "networkd-netdev-tunnel.h"
+
+#include "conf-parser.h"
+#include "missing.h"
#include "networkd-link.h"
+#include "networkd-netdev-tunnel.h"
+#include "parse-util.h"
+#include "string-table.h"
+#include "string-util.h"
#include "util.h"
-#include "missing.h"
-#include "conf-parser.h"
#define DEFAULT_TNL_HOP_LIMIT 64
#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF)
@@ -395,12 +399,12 @@ int config_parse_tunnel_address(const char *unit,
r = in_addr_from_string_auto(rvalue, &f, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Tunnel address is invalid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel address is invalid, ignoring assignment: %s", rvalue);
return 0;
}
if (t->family != AF_UNSPEC && t->family != f) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
return 0;
}
@@ -435,13 +439,14 @@ int config_parse_ipv6_flowlabel(const char* unit,
t->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
} else {
r = config_parse_int(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &k, userdata);
- if (r >= 0) {
- if (k > 0xFFFFF)
- log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue);
- else {
- *ipv6_flowlabel = htonl(k) & IP6_FLOWINFO_FLOWLABEL;
- t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
- }
+ if (r < 0)
+ return r;
+
+ if (k > 0xFFFFF)
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue);
+ else {
+ *ipv6_flowlabel = htonl(k) & IP6_FLOWINFO_FLOWLABEL;
+ t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
}
}
@@ -471,13 +476,12 @@ int config_parse_encap_limit(const char* unit,
else {
r = safe_atoi(rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue);
return 0;
}
if (k > 255 || k < 0)
- log_syntax(unit, LOG_ERR, filename, line, k, "Invalid Tunnel Encapsulation value, ignoring: %d", k);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid Tunnel Encapsulation value, ignoring: %d", k);
else {
t->encap_limit = k;
t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c
index 6a808b6205..851e83537e 100644
--- a/src/network/networkd-netdev-tuntap.c
+++ b/src/network/networkd-netdev-tuntap.c
@@ -23,7 +23,10 @@
#include <net/if.h>
#include <linux/if_tun.h>
+#include "alloc-util.h"
+#include "fd-util.h"
#include "networkd-netdev-tuntap.h"
+#include "user-util.h"
#define TUN_DEV "/dev/net/tun"
diff --git a/src/network/networkd-netdev-veth.c b/src/network/networkd-netdev-veth.c
index e20f9f74e2..bee1a16726 100644
--- a/src/network/networkd-netdev-veth.c
+++ b/src/network/networkd-netdev-veth.c
@@ -26,14 +26,17 @@
#include "networkd-netdev-veth.h"
static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
- Veth *v = VETH(netdev);
+ Veth *v;
int r;
assert(netdev);
assert(!link);
- assert(v);
assert(m);
+ v = VETH(netdev);
+
+ assert(v);
+
r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append VETH_INFO_PEER attribute: %m");
@@ -58,13 +61,16 @@ static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlin
}
static int netdev_veth_verify(NetDev *netdev, const char *filename) {
- Veth *v = VETH(netdev);
+ Veth *v;
int r;
assert(netdev);
- assert(v);
assert(filename);
+ v = VETH(netdev);
+
+ assert(v);
+
if (!v->ifname_peer) {
log_warning("Veth NetDev without peer name configured in %s. Ignoring",
filename);
@@ -84,9 +90,12 @@ static int netdev_veth_verify(NetDev *netdev, const char *filename) {
}
static void veth_done(NetDev *n) {
- Veth *v = VETH(n);
+ Veth *v;
assert(n);
+
+ v = VETH(n);
+
assert(v);
free(v->ifname_peer);
diff --git a/src/network/networkd-netdev-vlan.c b/src/network/networkd-netdev-vlan.c
index 195d1a944e..75fbdd355e 100644
--- a/src/network/networkd-netdev-vlan.c
+++ b/src/network/networkd-netdev-vlan.c
@@ -24,14 +24,17 @@
#include "networkd-netdev-vlan.h"
static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
- VLan *v = VLAN(netdev);
+ VLan *v;
int r;
assert(netdev);
- assert(v);
assert(link);
assert(req);
+ v = VLAN(netdev);
+
+ assert(v);
+
if (v->id <= VLANID_MAX) {
r = sd_netlink_message_append_u16(req, IFLA_VLAN_ID, v->id);
if (r < 0)
@@ -42,12 +45,15 @@ static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlin
}
static int netdev_vlan_verify(NetDev *netdev, const char *filename) {
- VLan *v = VLAN(netdev);
+ VLan *v;
assert(netdev);
- assert(v);
assert(filename);
+ v = VLAN(netdev);
+
+ assert(v);
+
if (v->id > VLANID_MAX) {
log_warning("VLAN without valid Id (%"PRIu64") configured in %s. Ignoring", v->id, filename);
return -EINVAL;
diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c
index 2518e2732b..baf6dd6623 100644
--- a/src/network/networkd-netdev-vxlan.c
+++ b/src/network/networkd-netdev-vxlan.c
@@ -28,14 +28,16 @@
#include "missing.h"
static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
- VxLan *v = VXLAN(netdev);
+ VxLan *v;
int r;
assert(netdev);
- assert(v);
assert(link);
assert(m);
+ v = VXLAN(netdev);
+
+ assert(v);
if (v->id <= VXLAN_VID_MAX) {
r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->id);
@@ -131,14 +133,12 @@ int config_parse_vxlan_group_address(const char *unit,
r = in_addr_from_string_auto(rvalue, &f, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "vxlan multicast group address is invalid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "vxlan multicast group address is invalid, ignoring assignment: %s", rvalue);
return 0;
}
- if(v->family != AF_UNSPEC && v->family != f) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "vxlan multicast group incompatible, ignoring assignment: %s", rvalue);
+ if (v->family != AF_UNSPEC && v->family != f) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan multicast group incompatible, ignoring assignment: %s", rvalue);
return 0;
}
@@ -164,9 +164,12 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) {
}
static void vxlan_init(NetDev *netdev) {
- VxLan *v = VXLAN(netdev);
+ VxLan *v;
assert(netdev);
+
+ v = VXLAN(netdev);
+
assert(v);
v->id = VXLAN_VID_MAX + 1;
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index ff1edf2c39..dd0b400c6a 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -21,15 +21,19 @@
#include <net/if.h>
+#include "alloc-util.h"
#include "conf-files.h"
#include "conf-parser.h"
+#include "fd-util.h"
#include "list.h"
-#include "siphash24.h"
#include "netlink-util.h"
#include "network-internal.h"
-
-#include "networkd.h"
#include "networkd-netdev.h"
+#include "networkd.h"
+#include "siphash24.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
@@ -245,6 +249,9 @@ static int netdev_enter_ready(NetDev *netdev) {
free(callback);
}
+ if (NETDEV_VTABLE(netdev)->post_create)
+ NETDEV_VTABLE(netdev)->post_create(netdev, NULL, NULL);
+
return 0;
}
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index 1f8510c4f7..3b9ab27b67 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -141,6 +141,9 @@ struct NetDevVTable {
/* create netdev, if not done via rtnl */
int (*create)(NetDev *netdev);
+ /* perform additional configuration after netdev has been createad */
+ int (*post_create)(NetDev *netdev, Link *link, sd_netlink_message *message);
+
/* verify that compulsory configuration options were specified */
int (*config_verify)(NetDev *netdev, const char *filename);
};
diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c
index 5717a15327..120760a986 100644
--- a/src/network/networkd-network-bus.c
+++ b/src/network/networkd-network-bus.c
@@ -19,9 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "strv.h"
-
+#include "alloc-util.h"
#include "networkd.h"
+#include "string-util.h"
+#include "strv.h"
static int property_get_ether_addrs(
sd_bus *bus,
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 8257ab45da..cc01dc24c9 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -51,6 +51,7 @@ Network.IPForward, config_parse_address_family_boolean_with
Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
+Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits)
Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Address.Address, config_parse_address, 0, 0
Address.Peer, config_parse_address, 0, 0
@@ -61,6 +62,7 @@ Route.Destination, config_parse_destination,
Route.Source, config_parse_destination, 0, 0
Route.Metric, config_parse_route_priority, 0, 0
Route.Scope, config_parse_route_scope, 0, 0
+Route.PreferredSource, config_parse_preferred_src, 0, 0
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_ntp)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 57495b58e0..0188cb6fe5 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -22,15 +22,20 @@
#include <ctype.h>
#include <net/if.h>
+#include "alloc-util.h"
#include "conf-files.h"
#include "conf-parser.h"
-#include "util.h"
-#include "hostname-util.h"
#include "dns-domain.h"
+#include "fd-util.h"
+#include "hostname-util.h"
#include "network-internal.h"
-
-#include "networkd.h"
#include "networkd-network.h"
+#include "networkd.h"
+#include "parse-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
@@ -121,6 +126,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
network->ipv6_accept_ra = -1;
+ network->ipv6_dad_transmits = -1;
r = config_parse(NULL, filename, file,
"Match\0"
@@ -326,12 +332,12 @@ int network_get(Manager *manager, struct udev_device *device,
(void) safe_atou8(attr, &name_assign_type);
if (name_assign_type == NET_NAME_ENUM)
- log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
- IFNAMSIZ, ifname, network->filename);
+ log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
+ ifname, network->filename);
else
- log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
+ log_debug("%s: found matching network '%s'", ifname, network->filename);
} else
- log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
+ log_debug("%s: found matching network '%s'", ifname, network->filename);
*ret = network;
return 0;
@@ -346,6 +352,10 @@ int network_get(Manager *manager, struct udev_device *device,
int network_apply(Manager *manager, Network *network, Link *link) {
int r;
+ assert(manager);
+ assert(network);
+ assert(link);
+
link->network = network;
if (network->ipv4ll_route) {
@@ -355,7 +365,7 @@ int network_apply(Manager *manager, Network *network, Link *link) {
if (r < 0)
return r;
- r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
+ r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
if (r == 0)
return -EINVAL;
if (r < 0)
@@ -364,14 +374,13 @@ int network_apply(Manager *manager, Network *network, Link *link) {
route->family = AF_INET;
route->dst_prefixlen = 16;
route->scope = RT_SCOPE_LINK;
- route->metrics = IPV4LL_ROUTE_METRIC;
+ route->priority = IPV4LL_ROUTE_METRIC;
route->protocol = RTPROT_STATIC;
}
- if (network->dns || network->ntp) {
- r = link_save(link);
- if (r < 0)
- return r;
+ if (network->dns || network->ntp || network->domains) {
+ manager_dirty(manager);
+ link_dirty(link);
}
return 0;
@@ -409,21 +418,18 @@ int config_parse_netdev(const char *unit,
kind = netdev_kind_from_string(kind_string);
if (kind == _NETDEV_KIND_INVALID) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid NetDev kind: %s", lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue);
return 0;
}
r = netdev_get(network->manager, rvalue, &netdev);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
if (netdev->kind != kind) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
@@ -443,9 +449,7 @@ int config_parse_netdev(const char *unit,
case NETDEV_KIND_VXLAN:
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Can not add VLAN '%s' to network: %m",
- rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Can not add VLAN '%s' to network: %m", rvalue);
return 0;
}
@@ -484,7 +488,7 @@ int config_parse_domains(const char *unit,
STRV_FOREACH(domain, *domains) {
if (is_localhost(*domain))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
else {
r = dns_name_is_valid(*domain);
if (r <= 0 && !streq(*domain, "*")) {
@@ -540,7 +544,7 @@ int config_parse_tunnel(const char *unit,
netdev->kind != NETDEV_KIND_VTI6 &&
netdev->kind != NETDEV_KIND_IP6TNL
) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ log_syntax(unit, LOG_ERR, filename, line, 0,
"NetDev is not a tunnel, ignoring assignment: %s", rvalue);
return 0;
}
@@ -625,7 +629,7 @@ int config_parse_dhcp(
else if (streq(rvalue, "both"))
s = ADDRESS_FAMILY_YES;
else {
- log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue);
return 0;
}
}
@@ -670,13 +674,13 @@ int config_parse_ipv6token(
}
r = in_addr_is_null(AF_INET6, &buffer);
- if (r < 0) {
+ if (r != 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
return 0;
}
if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
return 0;
}
@@ -730,7 +734,7 @@ int config_parse_ipv6_privacy_extensions(
if (streq(rvalue, "kernel"))
s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
else {
- log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
return 0;
}
}
@@ -765,7 +769,7 @@ int config_parse_hostname(
return r;
if (!hostname_is_valid(hn, false)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Hostname is not valid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue);
free(hn);
return 0;
}
@@ -799,7 +803,7 @@ int config_parse_timezone(
return r;
if (!timezone_is_valid(tz)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Timezone is not valid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue);
free(tz);
return 0;
}
@@ -844,7 +848,7 @@ int config_parse_dhcp_server_dns(
return 0;
if (inet_pton(AF_INET, w, &a) <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server address, ignoring: %s", w);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
continue;
}
@@ -883,7 +887,7 @@ int config_parse_dhcp_server_ntp(
r = extract_first_word(&p, &w, NULL, 0);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, r, line, "Failed to extract word, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
return 0;
}
@@ -891,7 +895,7 @@ int config_parse_dhcp_server_ntp(
return 0;
if (inet_pton(AF_INET, w, &a) <= 0) {
- log_syntax(unit, LOG_ERR, filename, r, line, "Failed to parse NTP server address, ignoring: %s", w);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w);
continue;
}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 2a43b6b347..c2872908b5 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -121,6 +121,7 @@ struct Network {
bool ip_masquerade;
int ipv6_accept_ra;
+ int ipv6_dad_transmits;
union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index fbaad40579..f4bbd06af1 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -19,15 +19,40 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include "alloc-util.h"
#include "conf-parser.h"
+#include "event-util.h"
+#include "in-addr-util.h"
#include "netlink-util.h"
-
-#include "networkd.h"
#include "networkd-route.h"
+#include "networkd.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "util.h"
+
+int route_new(Route **ret) {
+ _cleanup_route_free_ Route *route = NULL;
+
+ route = new0(Route, 1);
+ if (!route)
+ return -ENOMEM;
+
+ route->family = AF_UNSPEC;
+ route->scope = RT_SCOPE_UNIVERSE;
+ route->protocol = RTPROT_UNSPEC;
+ route->table = RT_TABLE_DEFAULT;
+ route->lifetime = USEC_INFINITY;
+
+ *ret = route;
+ route = NULL;
+
+ return 0;
+}
int route_new_static(Network *network, unsigned section, Route **ret) {
_cleanup_route_free_ Route *route = NULL;
+ int r;
if (section) {
route = hashmap_get(network->routes_by_section,
@@ -40,14 +65,11 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
}
}
- route = new0(Route, 1);
- if (!route)
- return -ENOMEM;
+ r = route_new(&route);
+ if (r < 0)
+ return r;
- route->family = AF_UNSPEC;
- route->scope = RT_SCOPE_UNIVERSE;
route->protocol = RTPROT_STATIC;
-
route->network = network;
LIST_PREPEND(routes, network->static_routes, route);
@@ -64,23 +86,6 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
return 0;
}
-int route_new_dynamic(Route **ret, unsigned char rtm_protocol) {
- _cleanup_route_free_ Route *route = NULL;
-
- route = new0(Route, 1);
- if (!route)
- return -ENOMEM;
-
- route->family = AF_UNSPEC;
- route->scope = RT_SCOPE_UNIVERSE;
- route->protocol = rtm_protocol;
-
- *ret = route;
- route = NULL;
-
- return 0;
-}
-
void route_free(Route *route) {
if (!route)
return;
@@ -93,10 +98,241 @@ void route_free(Route *route) {
UINT_TO_PTR(route->section));
}
+ if (route->link) {
+ set_remove(route->link->routes, route);
+ set_remove(route->link->routes_foreign, route);
+ }
+
+ sd_event_source_unref(route->expire);
+
free(route);
}
-int route_drop(Route *route, Link *link,
+static void route_hash_func(const void *b, struct siphash *state) {
+ const Route *route = b;
+
+ assert(route);
+
+ siphash24_compress(&route->family, sizeof(route->family), state);
+
+ switch (route->family) {
+ case AF_INET:
+ case AF_INET6:
+ /* Equality of routes are given by the 4-touple
+ (dst_prefix,dst_prefixlen,tos,priority,table) */
+ siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
+ siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
+ siphash24_compress(&route->tos, sizeof(route->tos), state);
+ siphash24_compress(&route->priority, sizeof(route->priority), state);
+ siphash24_compress(&route->table, sizeof(route->table), state);
+
+ break;
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ break;
+ }
+}
+
+static int route_compare_func(const void *_a, const void *_b) {
+ const Route *a = _a, *b = _b;
+
+ if (a->family < b->family)
+ return -1;
+ if (a->family > b->family)
+ return 1;
+
+ switch (a->family) {
+ case AF_INET:
+ case AF_INET6:
+ if (a->dst_prefixlen < b->dst_prefixlen)
+ return -1;
+ if (a->dst_prefixlen > b->dst_prefixlen)
+ return 1;
+
+ if (a->tos < b->tos)
+ return -1;
+ if (a->tos > b->tos)
+ return 1;
+
+ if (a->priority < b->priority)
+ return -1;
+ if (a->priority > b->priority)
+ return 1;
+
+ if (a->table < b->table)
+ return -1;
+ if (a->table > b->table)
+ return 1;
+
+ return memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ return 0;
+ }
+}
+
+static const struct hash_ops route_hash_ops = {
+ .hash = route_hash_func,
+ .compare = route_compare_func
+};
+
+int route_get(Link *link,
+ int family,
+ union in_addr_union *dst,
+ unsigned char dst_prefixlen,
+ unsigned char tos,
+ uint32_t priority,
+ unsigned char table,
+ Route **ret) {
+ Route route = {
+ .family = family,
+ .dst_prefixlen = dst_prefixlen,
+ .tos = tos,
+ .priority = priority,
+ .table = table,
+ }, *existing;
+
+ assert(link);
+ assert(dst);
+ assert(ret);
+
+ route.dst = *dst;
+
+ existing = set_get(link->routes, &route);
+ if (existing) {
+ *ret = existing;
+ return 1;
+ } else {
+ existing = set_get(link->routes_foreign, &route);
+ if (!existing)
+ return -ENOENT;
+ }
+
+ *ret = existing;
+
+ return 0;
+}
+
+static int route_add_internal(Link *link, Set **routes,
+ int family,
+ union in_addr_union *dst,
+ unsigned char dst_prefixlen,
+ unsigned char tos,
+ uint32_t priority,
+ unsigned char table, Route **ret) {
+ _cleanup_route_free_ Route *route = NULL;
+ int r;
+
+ assert(link);
+ assert(routes);
+ assert(dst);
+
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = family;
+ route->dst = *dst;
+ route->dst_prefixlen = dst_prefixlen;
+ route->tos = tos;
+ route->priority = priority;
+ route->table = table;
+
+ r = set_ensure_allocated(routes, &route_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(*routes, route);
+ if (r < 0)
+ return r;
+
+ route->link = link;
+
+ if (ret)
+ *ret = route;
+
+ route = NULL;
+
+ return 0;
+}
+
+int route_add_foreign(Link *link,
+ int family,
+ union in_addr_union *dst,
+ unsigned char dst_prefixlen,
+ unsigned char tos,
+ uint32_t priority,
+ unsigned char table, Route **ret) {
+ return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, tos, priority, table, ret);
+}
+
+int route_add(Link *link,
+ int family,
+ union in_addr_union *dst,
+ unsigned char dst_prefixlen,
+ unsigned char tos,
+ uint32_t priority,
+ unsigned char table, Route **ret) {
+ Route *route;
+ int r;
+
+ r = route_get(link, family, dst, dst_prefixlen, tos, priority, table, &route);
+ if (r == -ENOENT) {
+ /* Route does not exist, create a new one */
+ r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, tos, priority, table, &route);
+ if (r < 0)
+ return r;
+ } else if (r == 0) {
+ /* Take over a foreign route */
+ r = set_ensure_allocated(&link->routes, &route_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(link->routes, route);
+ if (r < 0)
+ return r;
+
+ set_remove(link->routes_foreign, route);
+ } else if (r == 1) {
+ /* Route exists, do nothing */
+ ;
+ } else
+ return r;
+
+ *ret = route;
+
+ return 0;
+}
+
+int route_update(Route *route,
+ union in_addr_union *src,
+ unsigned char src_prefixlen,
+ union in_addr_union *gw,
+ union in_addr_union *prefsrc,
+ unsigned char scope,
+ unsigned char protocol) {
+ assert(route);
+ assert(src);
+ assert(gw);
+ assert(prefsrc);
+
+ route->src = *src;
+ route->src_prefixlen = src_prefixlen;
+ route->gw = *gw;
+ route->prefsrc = *prefsrc;
+ route->scope = scope;
+ route->protocol = protocol;
+
+ return 0;
+}
+
+void route_drop(Route *route) {
+ assert(route);
+
+ route_free(route);
+}
+
+int route_remove(Route *route, Link *link,
sd_netlink_message_handler_t callback) {
_cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
int r;
@@ -113,20 +349,20 @@ int route_drop(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
- if (!in_addr_is_null(route->family, &route->in_addr)) {
+ if (!in_addr_is_null(route->family, &route->gw)) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
}
if (route->dst_prefixlen) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_DST attribute: %m");
@@ -137,9 +373,9 @@ int route_drop(Route *route, Link *link,
if (route->src_prefixlen) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_DST attribute: %m");
@@ -148,11 +384,11 @@ int route_drop(Route *route, Link *link,
return log_error_errno(r, "Could not set source prefix length: %m");
}
- if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
+ if (!in_addr_is_null(route->family, &route->prefsrc)) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
}
@@ -161,7 +397,7 @@ int route_drop(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not set scope: %m");
- r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
+ r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
@@ -178,9 +414,24 @@ int route_drop(Route *route, Link *link,
return 0;
}
+int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+ Route *route = userdata;
+ int r;
+
+ assert(route);
+
+ r = route_remove(route, route->link, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Could not remove route: %m");
+
+ return 1;
+}
+
int route_configure(Route *route, Link *link,
sd_netlink_message_handler_t callback) {
_cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_event_source_unref_ sd_event_source *expire = NULL;
+ usec_t lifetime;
int r;
assert(link);
@@ -195,20 +446,20 @@ int route_configure(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
- if (!in_addr_is_null(route->family, &route->in_addr)) {
+ if (!in_addr_is_null(route->family, &route->gw)) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
}
if (route->dst_prefixlen) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_DST attribute: %m");
@@ -219,9 +470,9 @@ int route_configure(Route *route, Link *link,
if (route->src_prefixlen) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
@@ -230,11 +481,11 @@ int route_configure(Route *route, Link *link,
return log_error_errno(r, "Could not set source prefix length: %m");
}
- if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
+ if (!in_addr_is_null(route->family, &route->prefsrc)) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
}
@@ -243,7 +494,7 @@ int route_configure(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not set scope: %m");
- r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
+ r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
@@ -257,6 +508,26 @@ int route_configure(Route *route, Link *link,
link_ref(link);
+ lifetime = route->lifetime;
+
+ r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route);
+ if (r < 0)
+ return log_error_errno(r, "Could not add route: %m");
+
+ /* TODO: drop expiration handling once it can be pushed into the kernel */
+ route->lifetime = lifetime;
+
+ if (route->lifetime != USEC_INFINITY) {
+ r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
+ route->lifetime, 0, route_expire_handler, route);
+ if (r < 0)
+ return log_error_errno(r, "Could not arm expiration timer: %m");
+ }
+
+ sd_event_source_unref(route->expire);
+ route->expire = expire;
+ expire = NULL;
+
return 0;
}
@@ -294,13 +565,52 @@ int config_parse_gateway(const char *unit,
r = in_addr_from_string_auto(rvalue, &f, &buffer);
if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ n->family = f;
+ n->gw = buffer;
+ n = NULL;
+
+ return 0;
+}
+
+int config_parse_preferred_src(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ _cleanup_route_free_ Route *n = NULL;
+ union in_addr_union buffer;
+ int r, f;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_new_static(network, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = in_addr_from_string_auto(rvalue, &f, &buffer);
+ if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Route is invalid, ignoring assignment: %s", rvalue);
+ "Preferred source is invalid, ignoring assignment: %s", rvalue);
return 0;
}
n->family = f;
- n->in_addr = buffer;
+ n->prefsrc = buffer;
n = NULL;
return 0;
@@ -345,14 +655,12 @@ int config_parse_destination(const char *unit,
r = in_addr_from_string_auto(address, &f, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Destination is invalid, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address);
return 0;
}
if (f != AF_INET && f != AF_INET6) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unknown address family, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address);
return 0;
}
@@ -360,8 +668,7 @@ int config_parse_destination(const char *unit,
if (e) {
r = safe_atou8(e + 1, &prefixlen);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
return 0;
}
} else {
@@ -377,10 +684,10 @@ int config_parse_destination(const char *unit,
n->family = f;
if (streq(lvalue, "Destination")) {
- n->dst_addr = buffer;
+ n->dst = buffer;
n->dst_prefixlen = prefixlen;
} else if (streq(lvalue, "Source")) {
- n->src_addr = buffer;
+ n->src = buffer;
n->src_prefixlen = prefixlen;
} else
assert_not_reached(lvalue);
@@ -414,9 +721,9 @@ int config_parse_route_priority(const char *unit,
if (r < 0)
return r;
- r = config_parse_unsigned(unit, filename, line, section,
- section_line, lvalue, ltype,
- rvalue, &n->metrics, userdata);
+ r = config_parse_uint32(unit, filename, line, section,
+ section_line, lvalue, ltype,
+ rvalue, &n->priority, userdata);
if (r < 0)
return r;
@@ -456,8 +763,7 @@ int config_parse_route_scope(const char *unit,
else if (streq(rvalue, "global"))
n->scope = RT_SCOPE_UNIVERSE;
else {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unknown route scope: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
return 0;
}
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index d090b9c91e..d0a51838ed 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -30,31 +30,47 @@ struct Route {
Network *network;
unsigned section;
+ Link *link;
+
int family;
unsigned char dst_prefixlen;
unsigned char src_prefixlen;
unsigned char scope;
- uint32_t metrics;
unsigned char protocol; /* RTPROT_* */
+ unsigned char tos;
+ uint32_t priority; /* note that ip(8) calls this 'metric' */
+ unsigned char table;
+
+ union in_addr_union gw;
+ union in_addr_union dst;
+ union in_addr_union src;
+ union in_addr_union prefsrc;
- union in_addr_union in_addr;
- union in_addr_union dst_addr;
- union in_addr_union src_addr;
- union in_addr_union prefsrc_addr;
+ usec_t lifetime;
+ sd_event_source *expire;
LIST_FIELDS(Route, routes);
};
int route_new_static(Network *network, unsigned section, Route **ret);
-int route_new_dynamic(Route **ret, unsigned char rtm_protocol);
+int route_new(Route **ret);
void route_free(Route *route);
int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback);
-int route_drop(Route *route, Link *link, sd_netlink_message_handler_t callback);
+int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback);
+
+int route_get(Link *link, int family, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret);
+int route_add(Link *link, int family, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret);
+int route_add_foreign(Link *link, int family, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret);
+int route_update(Route *route, union in_addr_union *src, unsigned char src_prefixlen, union in_addr_union *gw, union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol);
+void route_drop(Route *route);
+
+int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata);
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, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_preferred_src(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_destination(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_route_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_route_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c
index a41cd86239..df091393f6 100644
--- a/src/network/networkd-util.c
+++ b/src/network/networkd-util.c
@@ -19,10 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "conf-parser.h"
-
#include "networkd-util.h"
+#include "parse-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
const char *address_family_boolean_to_string(AddressFamilyBoolean b) {
if (b == ADDRESS_FAMILY_YES ||
@@ -82,7 +84,7 @@ int config_parse_address_family_boolean_with_kernel(
if (streq(rvalue, "kernel"))
s = _ADDRESS_FAMILY_BOOLEAN_INVALID;
else {
- log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding= option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue);
return 0;
}
}
@@ -133,7 +135,7 @@ int config_parse_resolve(
s = resolve_support_from_string(rvalue);
if (s < 0){
- log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s= option, ignoring: %s", lvalue, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse %s= option, ignoring: %s", lvalue, rvalue);
return 0;
}
diff --git a/src/network/networkd-wait-online-link.c b/src/network/networkd-wait-online-link.c
index cacb4c257e..c2779ff773 100644
--- a/src/network/networkd-wait-online-link.c
+++ b/src/network/networkd-wait-online-link.c
@@ -20,10 +20,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
#include "sd-network.h"
+#include "alloc-util.h"
#include "networkd-wait-online-link.h"
+#include "string-util.h"
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
_cleanup_(link_freep) Link *l = NULL;
diff --git a/src/network/networkd-wait-online-manager.c b/src/network/networkd-wait-online-manager.c
index 112d92a568..0c40ab2bb8 100644
--- a/src/network/networkd-wait-online-manager.c
+++ b/src/network/networkd-wait-online-manager.c
@@ -23,14 +23,13 @@
#include <linux/if.h>
#include <fnmatch.h>
+#include "alloc-util.h"
#include "netlink-util.h"
-
#include "network-internal.h"
#include "networkd-wait-online-link.h"
#include "networkd-wait-online.h"
-
-#include "util.h"
#include "time-util.h"
+#include "util.h"
bool manager_ignore_link(Manager *m, Link *link) {
char **ignore;
diff --git a/src/network/networkd-wait-online.c b/src/network/networkd-wait-online.c
index d958b48771..3220c4b7ef 100644
--- a/src/network/networkd-wait-online.c
+++ b/src/network/networkd-wait-online.c
@@ -21,10 +21,10 @@
#include <getopt.h>
#include "sd-daemon.h"
-#include "strv.h"
-#include "build.h"
-#include "signal-util.h"
+
#include "networkd-wait-online.h"
+#include "signal-util.h"
+#include "strv.h"
static bool arg_quiet = false;
static usec_t arg_timeout = 120 * USEC_PER_SEC;
@@ -79,9 +79,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 'i':
if (strv_extend(&arg_interfaces, optarg) < 0)
diff --git a/src/network/networkd.c b/src/network/networkd.c
index e6259043fa..ef394e0c04 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -20,9 +20,11 @@
***/
#include "sd-daemon.h"
-#include "capability.h"
-#include "signal-util.h"
+
+#include "capability-util.h"
#include "networkd.h"
+#include "signal-util.h"
+#include "user-util.h"
int main(int argc, char *argv[]) {
_cleanup_manager_free_ Manager *m = NULL;
@@ -107,6 +109,12 @@ int main(int argc, char *argv[]) {
goto out;
}
+ r = manager_rtnl_enumerate_routes(m);
+ if (r < 0) {
+ log_error_errno(r, "Could not enumerate routes: %m");
+ goto out;
+ }
+
log_info("Enumeration completed");
sd_notify(false,
diff --git a/src/network/networkd.h b/src/network/networkd.h
index eea57ac158..97665fac7a 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -48,7 +48,10 @@ struct Manager {
struct udev_monitor *udev_monitor;
sd_event_source *udev_event_source;
- bool enumerating;
+ bool enumerating:1;
+ bool dirty:1;
+
+ Set *dirty_links;
char *state_file;
LinkOperationalState operational_state;
@@ -79,9 +82,13 @@ bool manager_should_reload(Manager *m);
int manager_rtnl_enumerate_links(Manager *m);
int manager_rtnl_enumerate_addresses(Manager *m);
+int manager_rtnl_enumerate_routes(Manager *m);
+
+int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata);
+int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_send_changed(Manager *m, const char *property, ...) _sentinel_;
-int manager_save(Manager *m);
+void manager_dirty(Manager *m);
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found);
diff --git a/src/network/test-network.c b/src/network/test-network.c
index 5909cc790e..dbed3795e3 100644
--- a/src/network/test-network.c
+++ b/src/network/test-network.c
@@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "networkd.h"
#include "network-internal.h"
#include "dhcp-lease-internal.h"
@@ -143,8 +144,8 @@ static void test_network_get(Manager *manager, struct udev_device *loopback) {
static void test_address_equality(void) {
_cleanup_address_free_ Address *a1 = NULL, *a2 = NULL;
- assert_se(address_new_dynamic(&a1) >= 0);
- assert_se(address_new_dynamic(&a2) >= 0);
+ assert_se(address_new(&a1) >= 0);
+ assert_se(address_new(&a2) >= 0);
assert_se(address_equal(NULL, NULL));
assert_se(!address_equal(a1, NULL));
@@ -158,17 +159,18 @@ static void test_address_equality(void) {
assert_se(address_equal(a1, a2));
assert_se(inet_pton(AF_INET, "192.168.3.9", &a1->in_addr.in));
- assert_se(address_equal(a1, a2));
+ assert_se(!address_equal(a1, a2));
assert_se(inet_pton(AF_INET, "192.168.3.9", &a2->in_addr.in));
assert_se(address_equal(a1, a2));
+ assert_se(inet_pton(AF_INET, "192.168.3.10", &a1->in_addr_peer.in));
+ assert_se(address_equal(a1, a2));
+ assert_se(inet_pton(AF_INET, "192.168.3.11", &a2->in_addr_peer.in));
+ assert_se(address_equal(a1, a2));
a1->prefixlen = 10;
assert_se(!address_equal(a1, a2));
a2->prefixlen = 10;
assert_se(address_equal(a1, a2));
- assert_se(inet_pton(AF_INET, "192.168.3.10", &a2->in_addr.in));
- assert_se(address_equal(a1, a2));
-
a1->family = AF_INET6;
assert_se(!address_equal(a1, a2));