diff options
Diffstat (limited to 'src/network')
38 files changed, 2543 insertions, 1091 deletions
diff --git a/src/network/networkctl.c b/src/network/networkctl.c index c78b9444b6..446b048ef1 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -28,15 +28,21 @@ #include "sd-netlink.h" #include "sd-network.h" +#include "alloc-util.h" #include "arphrd-list.h" #include "device-util.h" #include "ether-addr-util.h" #include "hwdb-util.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 "string-table.h" +#include "string-util.h" #include "strv.h" #include "terminal-util.h" #include "util.h" @@ -515,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); @@ -904,12 +910,10 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL; _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; _cleanup_free_ LinkInfo *links = NULL; - const char *state, *word; - double ttl = -1; uint32_t capability; int i, r, c, j; - size_t ll; + const char *p; char **s; pager_open_if_enabled(); @@ -950,14 +954,19 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) { return -ENOMEM; STRV_FOREACH(s, l) { - FOREACH_WORD_QUOTED(word, ll, *s, state) { - _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL; - t = strndup(word, ll); - if (!t) - return -ENOMEM; + p = *s; + for (;;) { + _cleanup_free_ char *a = NULL, *b = NULL, *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to parse LLDP syntax \"%s\": %m", *s); + + if (r == 0) + break; - r = split_pair(t, "=", &a, &b); + r = split_pair(word, "=", &a, &b); if (r < 0) continue; 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 388beb5d4c..c0562e5788 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,88 @@ 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; + /* Consider address tentative until we get the real flags from the kernel */ + address->flags = IFA_F_TENTATIVE; + + 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 +319,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 +327,106 @@ 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 address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) { + bool ready; 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); + 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->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"); + if (address->family == AF_INET6 && + in_addr_is_link_local(AF_INET6, &address->in_addr) && + !address->link->ipv6ll_address) { + r = link_ipv6ll_gained(address->link); + if (r < 0) + return r; + } + } + } - link_ref(link); + return 0; +} + +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 +434,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 +476,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 +502,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 +517,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 +579,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; } @@ -580,49 +777,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->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 39789a2382..4049a23bdc 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,23 @@ struct Address { union in_addr_union in_addr; union in_addr_union in_addr_peer; - bool ip_masquerade_done; + 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..b9c60a3c77 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) { @@ -33,7 +34,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, int r; assert(link); - assert(link->dhcp4_messages); + assert(link->dhcp4_messages > 0); link->dhcp4_messages --; @@ -43,9 +44,9 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, link_enter_failed(link); } - if (!link->dhcp4_messages) { + if (link->dhcp4_messages == 0) { 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 3cb7b8d9ca..d407b31b78 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, @@ -58,18 +57,17 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *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; @@ -77,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"); @@ -99,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) @@ -111,18 +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_errno(link, r, "Could not get prefix information: %m"); - 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; } @@ -145,7 +131,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { case SD_DHCP6_CLIENT_EVENT_STOP: case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: - log_link_warning(link, "DHCPv6 lease lost"); + if (sd_dhcp6_client_get_lease(client, NULL) >= 0) + log_link_warning(link, "DHCPv6 lease lost"); link->dhcp6_configured = false; break; @@ -176,197 +163,85 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { return; } - link_client_handler(link); + link_check_ready(link); } -static int dhcp6_configure(Link *link, int event) { - int r; - bool 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); - if (r < 0) { - 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) { - r = sd_dhcp6_client_stop(link->dhcp6_client); - if (r < 0) { - 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); - if (r < 0) { - log_link_warning_errno(link, r, "Could not unset DHCPv6 Information request: %m"); - goto error; - } - - } - - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0 && r != -EALREADY) { - log_link_warning_errno(link, r, "Could not restart DHCPv6: %m"); - goto error; - } - - if (r == -EALREADY) - link->dhcp6_configured = true; - - return r; - } - - r = sd_dhcp6_client_new(&link->dhcp6_client); - if (r < 0) - goto error; +int dhcp6_request_address(Link *link) { + int r, inf_req; + bool running; - r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); - if (r < 0) - goto error; + assert(link); + assert(link->dhcp6_client); - r = sd_dhcp6_client_set_mac(link->dhcp6_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), ARPHRD_ETHER); + r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); if (r < 0) - goto error; + return r; - r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); - if (r < 0) - goto error; + if (!inf_req) + return 0; - r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler, - link); + r = sd_dhcp6_client_is_running(link->dhcp6_client); if (r < 0) - goto error; + return r; + else + running = !!r; - if (event == SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER) { - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, - true); + if (running) { + r = sd_dhcp6_client_stop(link->dhcp6_client); if (r < 0) - goto error; + return r; } - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - goto error; - - return r; - - error: - 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); + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false); 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 (running) { + r = sd_dhcp6_client_start(link->dhcp6_client); 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 r; } return 0; } -static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) { - Link *link = userdata; +int dhcp6_configure(Link *link) { + sd_dhcp6_client *client = NULL; + int r; 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_errno(link, event, "ICMPv6 error: %m"); - else - log_link_warning(link, "ICMPv6 unknown event: %d", event); - - break; - } - -} - -int icmp6_configure(Link *link) { - int r; + r = sd_dhcp6_client_new(&client); + if (r < 0) + return r; - assert_return(link, -EINVAL); + r = sd_dhcp6_client_attach_event(client, NULL, 0); + if (r < 0) + goto error; - r = sd_icmp6_nd_new(&link->icmp6_router_discovery); + r = sd_dhcp6_client_set_information_request(client, true); if (r < 0) return r; - r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery, NULL, 0); + r = sd_dhcp6_client_set_mac(client, + (const uint8_t *) &link->mac, + sizeof (link->mac), ARPHRD_ETHER); if (r < 0) - return r; + goto error; - r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery, &link->mac); + r = sd_dhcp6_client_set_index(client, link->ifindex); if (r < 0) - return r; + goto error; - r = sd_icmp6_nd_set_index(link->icmp6_router_discovery, link->ifindex); + r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); if (r < 0) - return r; + goto error; + + link->dhcp6_client = client; - r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery, - icmp6_router_handler, link); + return 0; +error: + sd_dhcp6_client_unref(client); return r; } diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index 9cb63cb79f..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, diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 1902b3d23a..ed0d861e7a 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -42,7 +42,7 @@ 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_errno(link, r, "Could not allocate address: %m"); return r; @@ -53,9 +53,9 @@ 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_errno(link, r, "Could not allocate route: %m"); return r; @@ -63,11 +63,11 @@ static int ipv4ll_address_lost(Link *link) { 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; } @@ -88,7 +88,7 @@ static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *u link->ipv4ll_route = true; if (link->ipv4ll_address == true) - link_client_handler(link); + link_check_ready(link); return 1; } @@ -105,12 +105,12 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void 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; } @@ -133,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; @@ -143,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) @@ -207,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 ffc9578e86..a415035887 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) @@ -106,20 +111,56 @@ static bool link_ipv4_forward_enabled(Link *link) { if (!link->network) return false; + if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) + return false; + return link->network->ip_forward & ADDRESS_FAMILY_IPV4; } static bool link_ipv6_forward_enabled(Link *link) { + + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; if (!link->network) return false; + if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) + return false; + return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } +bool link_ipv6_accept_ra_enabled(Link *link) { + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + /* If unset use system default (enabled if local forwarding is disabled. + * disabled if local forwarding is enabled). + * If set, ignore or enforce RA independent of local forwarding state. + */ + if (link->network->ipv6_accept_ra < 0) + /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ + return !link_ipv6_forward_enabled(link); + else if (link->network->ipv6_accept_ra > 0) + /* accept RA even if ip_forward is enabled */ + return true; + else + /* ignore RA */ + return false; +} + static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { + + if (!socket_ipv6_is_supported()) + return _IPV6_PRIVACY_EXTENSIONS_INVALID; + if (link->flags & IFF_LOOPBACK) return _IPV6_PRIVACY_EXTENSIONS_INVALID; @@ -129,6 +170,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 +293,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 +383,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 +410,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 +434,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 +496,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) { @@ -395,9 +506,6 @@ static int link_stop_clients(Link *link) { assert(link->manager); assert(link->manager->event); - if (!link->network) - return 0; - if (link->dhcp_client) { k = sd_dhcp_client_stop(link->dhcp_client); if (k < 0) @@ -410,16 +518,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->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"); - } + 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); + if (link->ndisc_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 +551,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 +592,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,6 +614,10 @@ void link_client_handler(Link *link) { !link->ipv4ll_route) return; + if (link_ipv6ll_enabled(link)) + if (!link->ipv6ll_address) + return; + if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && !link->dhcp4_configured) || (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && @@ -509,6 +626,13 @@ void link_client_handler(Link *link) { !link->dhcp4_configured && !link->dhcp6_configured)) return; + if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) + return; + + SET_FOREACH(a, link->addresses, i) + if (!address_is_ready(a)) + return; + if (link->state != LINK_STATE_CONFIGURED) link_enter_configured(link); @@ -531,12 +655,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; @@ -565,14 +689,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; @@ -585,7 +709,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; } @@ -609,9 +733,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"); @@ -722,7 +846,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); @@ -854,7 +978,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; @@ -867,7 +991,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; } @@ -1019,7 +1143,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; } @@ -1129,6 +1253,34 @@ static void lldp_handler(sd_lldp *lldp, int event, void *userdata) { } } +static int link_acquire_ipv6_conf(Link *link) { + int r; + + assert(link); + + if (link_dhcp6_enabled(link)) { + assert(link->dhcp6_client); + + log_link_debug(link, "Acquiring DHCPv6 lease"); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0) + return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); + } + + if (link_ipv6_accept_ra_enabled(link)) { + assert(link->ndisc_router_discovery); + + log_link_debug(link, "Discovering IPv6 routers"); + + 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 0; +} + static int link_acquire_conf(Link *link) { int r; @@ -1157,16 +1309,6 @@ static int link_acquire_conf(Link *link) { return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); } - if (link_dhcp6_enabled(link)) { - assert(link->icmp6_router_discovery); - - log_link_debug(link, "Discovering IPv6 routers"); - - r = sd_icmp6_router_solicitation_start(link->icmp6_router_discovery); - if (r < 0) - return log_link_warning_errno(link, r, "Could not start IPv6 router discovery: %m"); - } - if (link_lldp_enabled(link)) { assert(link->lldp); @@ -1207,7 +1349,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; } @@ -1294,7 +1436,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; } @@ -1432,14 +1574,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; @@ -1474,14 +1616,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; @@ -1517,7 +1659,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; @@ -1531,7 +1673,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); } } @@ -1555,7 +1697,7 @@ static void link_free_carrier_maps(Link *link) { } if (list_updated) - link_save(link); + link_dirty(link); return; } @@ -1570,6 +1712,7 @@ void link_drop(Link *link) { log_link_debug(link, "Link removed"); + (void)unlink(link->state_file); link_unref(link); return; @@ -1616,7 +1759,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 @@ -1639,7 +1782,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 && @@ -1715,32 +1858,69 @@ static int link_enter_join_netdev(Link *link) { } static int link_set_ipv4_forward(Link *link) { - const char *p = NULL, *v; int r; - if (link->flags & IFF_LOOPBACK) + if (!link_ipv4_forward_enabled(link)) return 0; - if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) + /* We propagate the forwarding flag from one interface to the + * global setting one way. This means: as long as at least one + * interface was configured at any time that had IP forwarding + * enabled the setting will stay on for good. We do this + * primarily to keep IPv4 and IPv6 packet forwarding behaviour + * somewhat in sync (see below). */ + + r = write_string_file("/proc/sys/net/ipv4/ip_forward", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m"); + + return 0; +} + +static int link_set_ipv6_forward(Link *link) { + int r; + + if (!link_ipv6_forward_enabled(link)) return 0; - p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/forwarding"); - v = one_zero(link_ipv4_forward_enabled(link)); + /* On Linux, the IPv6 stack does not not know a per-interface + * packet forwarding setting: either packet forwarding is on + * for all, or off for all. We hence don't bother with a + * per-interface setting, but simply propagate the interface + * flag, if it is set, to the global flag, one-way. Note that + * while IPv4 would allow a per-interface flag, we expose the + * same behaviour there and also propagate the setting from + * one to all, to keep things simple (see above). */ - r = write_string_file(p, v, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, v) > 0) - return 0; + r = write_string_file("/proc/sys/net/ipv6/conf/all/forwarding", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m"); - log_link_warning_errno(link, r, "Cannot configure IPv4 forwarding for interface %s: %m", link->ifname); - } + return 0; +} + +static int link_set_ipv6_privacy_extensions(Link *link) { + char buf[DECIMAL_STR_MAX(unsigned) + 1]; + IPv6PrivacyExtensions s; + const char *p = NULL; + int r; + + s = link_ipv6_privacy_extensions(link); + if (s < 0) + return 0; + + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr"); + xsprintf(buf, "%u", (unsigned) link->network->ipv6_privacy_extensions); + + r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m"); return 0; } -static int link_set_ipv6_forward(Link *link) { - const char *p = NULL, *v = NULL; +static int link_set_ipv6_accept_ra(Link *link) { + const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -1750,27 +1930,21 @@ static int link_set_ipv6_forward(Link *link) { if (link->flags & IFF_LOOPBACK) return 0; - if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) + if (!link->network) return 0; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/forwarding"); - v = one_zero(link_ipv6_forward_enabled(link)); - - r = write_string_file(p, v, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, v) > 0) - return 0; + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); - log_link_warning_errno(link, r, "Cannot configure IPv6 forwarding for interface: %m"); - } + /* We handle router advertisments ourselves, tell the kernel to GTFO */ + r = write_string_file(p, "0", WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m"); return 0; } -static int link_set_ipv6_privacy_extensions(Link *link) { - char buf[DECIMAL_STR_MAX(unsigned) + 1]; - IPv6PrivacyExtensions s; +static int link_set_ipv6_dad_transmits(Link *link) { + char buf[DECIMAL_STR_MAX(int) + 1]; const char *p = NULL; int r; @@ -1778,27 +1952,28 @@ static int link_set_ipv6_privacy_extensions(Link *link) { if (!socket_ipv6_is_supported()) return 0; - s = link_ipv6_privacy_extensions(link); - if (s == _IPV6_PRIVACY_EXTENSIONS_INVALID) + if (link->flags & IFF_LOOPBACK) return 0; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr"); - xsprintf(buf, "%u", link->network->ipv6_privacy_extensions); + if (!link->network) + return 0; - 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; + if (link->network->ipv6_dad_transmits < 0) + return 0; - log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m"); - } + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/dad_transmits"); + xsprintf(buf, "%i", link->network->ipv6_dad_transmits); + + r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m"); return 0; } -static int link_set_ipv6_accept_ra(Link *link) { - const char *p = NULL, *v = NULL; +static int link_set_ipv6_hop_limit(Link *link) { + char buf[DECIMAL_STR_MAX(int) + 1]; + const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -1808,29 +1983,46 @@ static int link_set_ipv6_accept_ra(Link *link) { if (link->flags & IFF_LOOPBACK) return 0; - /* If unset use system default (enabled if local forwarding is disabled. - * disabled if local forwarding is enabled). - * If set, ignore or enforce RA independent of local forwarding state. - */ - 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) { - /* "2" means accept RA even if ip_forward is enabled */ - v = "2"; - } else { - /* "0" means ignore RA */ - v = "0"; + if (!link->network) + return 0; + + if (link->network->ipv6_hop_limit < 0) + return 0; + + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/hop_limit"); + xsprintf(buf, "%i", link->network->ipv6_hop_limit); + + r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m"); + + return 0; +} + +static int link_drop_foreign_config(Link *link) { + Address *address; + Route *route; + Iterator i; + int r; + + SET_FOREACH(address, link->addresses_foreign, i) { + /* we consider IPv6LL addresses to be managed by the kernel */ + if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1) + continue; + + r = address_remove(address, link, link_address_remove_handler); + if (r < 0) + return r; } - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); - r = write_string_file(p, v, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, v) > 0) - return 0; + SET_FOREACH(route, link->routes_foreign, i) { + /* do not touch routes managed by the kernel */ + if (route->protocol == RTPROT_KERNEL) + continue; - log_link_warning_errno(link, r, "Cannot configure IPv6 accept_ra for interface: %m"); + r = route_remove(route, link, link_address_remove_handler); + if (r < 0) + return r; } return 0; @@ -1843,6 +2035,10 @@ static int link_configure(Link *link) { assert(link->network); assert(link->state == LINK_STATE_PENDING); + r = link_drop_foreign_config(link); + if (r < 0) + return r; + r = link_set_bridge_fdb(link); if (r < 0) return r; @@ -1863,6 +2059,14 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_set_ipv6_dad_transmits(link); + if (r < 0) + return r; + + r = link_set_ipv6_hop_limit(link); + if (r < 0) + return r; + if (link_ipv4ll_enabled(link)) { r = ipv4ll_configure(link); if (r < 0) @@ -1886,7 +2090,13 @@ static int link_configure(Link *link) { } if (link_dhcp6_enabled(link)) { - r = icmp6_configure(link); + r = dhcp6_configure(link); + if (r < 0) + return r; + } + + if (link_ipv6_accept_ra_enabled(link)) { + r = ndisc_configure(link); if (r < 0) return r; } @@ -1910,6 +2120,12 @@ static int link_configure(Link *link) { r = link_acquire_conf(link); if (r < 0) return r; + + if (link->ipv6ll_address) { + r = link_acquire_ipv6_conf(link); + if (r < 0) + return r; + } } return link_enter_join_netdev(link); @@ -1938,28 +2154,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) @@ -2011,177 +2229,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"); - case RTM_DELADDR: + 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); + } - 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); +dhcp4_address_fail: - break; - default: - assert_not_reached("Received invalid RTNL message type"); + 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; + } + + 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) { @@ -2203,12 +2435,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 */ @@ -2218,14 +2456,38 @@ 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) + goto failed; + } + + return 0; +failed: + link_enter_failed(link); + return r; +} + +int link_ipv6ll_gained(Link *link) { + int r; + + assert(link); + + log_link_info(link, "Gained IPv6LL"); + + link->ipv6ll_address = true; + link_check_ready(link); + + if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) { + r = link_acquire_ipv6_conf(link); + if (r < 0) { + link_enter_failed(link); return r; + } } return 0; @@ -2236,7 +2498,7 @@ static int link_carrier_gained(Link *link) { assert(link); - if (link->network) { + if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) { r = link_acquire_conf(link); if (r < 0) { link_enter_failed(link); @@ -2416,49 +2678,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); @@ -2466,12 +2692,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; @@ -2501,9 +2721,8 @@ int link_save(Link *link) { sd_dhcp6_lease *dhcp6_lease = NULL; if (link->dhcp6_client) { - r = sd_dhcp6_client_get_lease(link->dhcp6_client, - &dhcp6_lease); - if (r < 0) + r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); + if (r < 0 && r != -ENOMSG) log_link_debug(link, "No DHCPv6 lease"); } @@ -2542,9 +2761,9 @@ int link_save(Link *link) { } } - fputs("\n", f); + fputc('\n', f); - fprintf(f, "NTP="); + fputs("NTP=", f); space = false; STRV_FOREACH(address, link->network->ntp) { if (space) @@ -2591,9 +2810,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) @@ -2629,18 +2848,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); @@ -2651,12 +2900,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); @@ -2667,19 +2915,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) @@ -2691,6 +2945,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); @@ -2723,6 +2988,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..b564bcbca0 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; @@ -92,10 +95,13 @@ struct Link { unsigned dhcp4_messages; bool dhcp4_configured; bool dhcp6_configured; + unsigned ndisc_messages; + bool ndisc_configured; sd_ipv4ll *ipv4ll; - bool ipv4ll_address; - bool ipv4ll_route; + bool ipv4ll_address:1; + bool ipv4ll_route:1; + bool ipv6ll_address:1; bool static_configured; @@ -103,7 +109,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,29 +126,35 @@ 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); bool link_has_carrier(Link *link); +int link_ipv6ll_gained(Link *link); + int link_set_mtu(Link *link, uint32_t mtu); int link_set_hostname(Link *link, const char *hostname); 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); +int dhcp6_request_address(Link *link); +int ndisc_configure(Link *link); bool link_lldp_enabled(Link *link); bool link_ipv4ll_enabled(Link *link); @@ -150,6 +162,7 @@ bool link_ipv6ll_enabled(Link *link); bool link_dhcp4_server_enabled(Link *link); bool link_dhcp4_enabled(Link *link); bool link_dhcp6_enabled(Link *link); +bool link_ipv6_accept_ra_enabled(Link *link); const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; 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..42f58fed19 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,350 @@ 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 = 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 %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", 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 %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); + } + + address_update(address, flags, scope, &cinfo); + + break; + + case RTM_DELADDR: + + if (address) { + log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); + address_drop(address); + } else + log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", 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 +756,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 +1003,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 +1042,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 +1061,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); } @@ -528,7 +1093,8 @@ static bool manager_check_idle(void *userdata) { link_ipv4ll_enabled(link) || link_dhcp4_server_enabled(link) || link_dhcp4_enabled(link) || - link_dhcp6_enabled(link)) + link_dhcp6_enabled(link) || + link_ipv6_accept_ra_enabled(link)) return false; } @@ -536,8 +1102,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 +1210,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 +1220,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 +1311,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..126f9c0fe9 --- /dev/null +++ b/src/network/networkd-ndisc.c @@ -0,0 +1,247 @@ +/*-*- 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 <netinet/icmp6.h> +#include <linux/if.h> + +#include "sd-ndisc.h" + +#include "networkd-link.h" + +static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + assert(link->ndisc_messages > 0); + + link->ndisc_messages --; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Could not set NDisc route or address: %m"); + link_enter_failed(link); + } + + if (link->ndisc_messages == 0) { + link->ndisc_configured = true; + link_check_ready(link); + } + + return 1; +} + +static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime_preferred, unsigned lifetime_valid, void *userdata) { + _cleanup_address_free_ Address *address = NULL; + Link *link = userdata; + usec_t time_now; + int r; + + assert(nd); + assert(link); + assert(link->network); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + r = address_new(&address); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate address: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + address->family = AF_INET6; + address->in_addr.in6 = *prefix; + if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0) + memcpy(&address->in_addr.in6 + 8, &link->network->ipv6_token + 8, 8); + else { + /* see RFC4291 section 2.5.1 */ + address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0]; + address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1; + address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1]; + address->in_addr.in6.__in6_u.__u6_addr8[10] = link->mac.ether_addr_octet[2]; + address->in_addr.in6.__in6_u.__u6_addr8[11] = 0xff; + address->in_addr.in6.__in6_u.__u6_addr8[12] = 0xfe; + address->in_addr.in6.__in6_u.__u6_addr8[13] = link->mac.ether_addr_octet[3]; + address->in_addr.in6.__in6_u.__u6_addr8[14] = link->mac.ether_addr_octet[4]; + address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5]; + } + address->prefixlen = prefixlen; + address->flags = IFA_F_NOPREFIXROUTE; + address->cinfo.ifa_prefered = lifetime_preferred; + address->cinfo.ifa_valid = lifetime_valid; + + r = address_configure(address, link, ndisc_netlink_handler, true); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set SLAAC address: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) { + _cleanup_route_free_ Route *route = NULL; + Link *link = userdata; + usec_t time_now; + int r; + + assert(nd); + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->flags = RTM_F_PREFIX; + route->dst.in6 = *prefix; + route->dst_prefixlen = prefixlen; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set prefix route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { + _cleanup_route_free_ Route *route = NULL; + Link *link = userdata; + usec_t time_now; + int r; + + assert(link); + assert(link->network); + assert(link->manager); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) { + if (flags & ND_RA_FLAG_MANAGED) + dhcp6_request_address(link); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EALREADY) + log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); + } + + if (!gateway) + return; + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->pref = pref; + route->gw.in6 = *gateway; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set default route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { + Link *link = userdata; + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch (event) { + case SD_NDISC_EVENT_TIMEOUT: + dhcp6_request_address(link); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EALREADY) + log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m"); + break; + case SD_NDISC_EVENT_STOP: + break; + default: + log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); + } +} + +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, + ndisc_prefix_onlink_handler, + ndisc_prefix_autonomous_handler, + ndisc_handler, + link); + + return r; +} diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c index bcaba57937..4e4755f86f 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)); @@ -333,8 +340,6 @@ int config_parse_arp_ip_target_address(const char *unit, void *data, void *userdata) { Bond *b = userdata; - const char *word, *state; - size_t l; int r; assert(filename); @@ -342,14 +347,19 @@ int config_parse_arp_ip_target_address(const char *unit, assert(rvalue); assert(data); - FOREACH_WORD_QUOTED(word, l, rvalue, state) { + for (;;) { _cleanup_free_ ArpIpTarget *buffer = NULL; _cleanup_free_ char *n = NULL; int f; - n = strndup(word, l); - if (!n) - return -ENOMEM; + r = extract_first_word(&rvalue, &n, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Bond ARP ip target address, ignoring assignment: %s", rvalue); + return 0; + } + + if (r == 0) + break; buffer = new0(ArpIpTarget, 1); if (!buffer) @@ -373,16 +383,21 @@ 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, 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); + 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 +407,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 2eeb86a683..57c58d83b4 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -72,20 +72,21 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess 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, b->forward_delay / USEC_PER_SEC); + 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, b->hello_time / USEC_PER_SEC ); + 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, b->max_age / USEC_PER_SEC); + 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"); } diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 4aac239850..4a4b400e41 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -56,6 +56,7 @@ VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) +VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) 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 c9b7fa96e2..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) 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 03a599c0d4..755ad2f934 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); @@ -89,6 +91,12 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_AGEING attribute: %m"); } + if (v->max_fdb) { + r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LIMIT, v->max_fdb); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LIMIT attribute: %m"); + } + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_CSUM, v->udpcsum); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_CSUM attribute: %m"); @@ -162,9 +170,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-vxlan.h b/src/network/networkd-netdev-vxlan.h index 4ec33946cc..d21f355f5d 100644 --- a/src/network/networkd-netdev-vxlan.h +++ b/src/network/networkd-netdev-vxlan.h @@ -39,6 +39,7 @@ struct VxLan { unsigned tos; unsigned ttl; + unsigned max_fdb; usec_t fdb_ageing; diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index 3d4865a780..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] = { 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 b6f70e191d..de2c66d153 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -51,6 +51,8 @@ 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.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit) Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) Address.Address, config_parse_address, 0, 0 Address.Peer, config_parse_address, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 5d22598fc0..29723a852f 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,8 @@ 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; + network->ipv6_hop_limit = -1; r = config_parse(NULL, filename, file, "Match\0" @@ -326,12 +333,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 +353,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 +366,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 +375,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; diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 2a43b6b347..a27c67eea5 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -121,6 +121,8 @@ struct Network { bool ip_masquerade; int ipv6_accept_ra; + int ipv6_dad_transmits; + int ipv6_hop_limit; 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 ee1ddd81fe..ed06c21160 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,10 +494,18 @@ 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_rtnl_message_route_set_flags(req, route->flags); + if (r < 0) + return log_error_errno(r, "Colud not set flags: %m"); + + 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"); + r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref); + if (r < 0) + return log_error_errno(r, "Could not append RTA_PREF attribute: %m"); + r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); @@ -257,6 +516,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; } @@ -299,7 +578,7 @@ int config_parse_gateway(const char *unit, } n->family = f; - n->in_addr = buffer; + n->gw = buffer; n = NULL; return 0; @@ -339,7 +618,7 @@ int config_parse_preferred_src(const char *unit, } n->family = f; - n->prefsrc_addr = buffer; + n->prefsrc = buffer; n = NULL; return 0; @@ -413,10 +692,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); @@ -450,9 +729,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; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 11e94d44fb..b276756674 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -30,26 +30,43 @@ 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; + unsigned char pref; + unsigned flags; + + 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) diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index dde6b327ed..2545621a93 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 || @@ -77,12 +79,20 @@ int config_parse_address_family_boolean_with_kernel( assert(rvalue); assert(data); + /* This function is mostly obsolete now. It simply redirects + * "kernel" to "no". In older networkd versions we used to + * distuingish IPForward=off from IPForward=kernel, where the + * former would explicitly turn off forwarding while the + * latter would simply not touch the setting. But that logic + * is gone, hence silently accept the old setting, but turn it + * to "no". */ + s = address_family_boolean_from_string(rvalue); if (s < 0) { if (streq(rvalue, "kernel")) - s = _ADDRESS_FAMILY_BOOLEAN_INVALID; + s = ADDRESS_FAMILY_NO; else { - log_syntax(unit, LOG_ERR, filename, line, 0, "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; } } 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.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)); |