diff options
Diffstat (limited to 'src/network/networkd-address.c')
-rw-r--r-- | src/network/networkd-address.c | 428 |
1 files changed, 268 insertions, 160 deletions
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index b0d296941e..5d443e9b9b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -21,26 +21,37 @@ #include <net/if.h> -#include "utf8.h" -#include "util.h" #include "conf-parser.h" #include "firewall-util.h" #include "netlink-util.h" +#include "set.h" +#include "utf8.h" +#include "util.h" #include "networkd.h" #include "networkd-address.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 +63,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 +83,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 +95,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 +208,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 +227,66 @@ int address_establish(Address *address, Link *link) { return 0; } -int address_release(Address *address, Link *link) { +static int address_add_internal(Link *link, Set **addresses, + int family, + const union in_addr_union *in_addr, + unsigned char prefixlen, + Address **ret) { + _cleanup_address_free_ Address *address = NULL; int r; - assert(address); assert(link); + assert(addresses); + assert(in_addr); + + r = address_new(&address); + if (r < 0) + return r; + + address->family = family; + address->in_addr = *in_addr; + address->prefixlen = prefixlen; + + r = set_ensure_allocated(addresses, &address_hash_ops); + if (r < 0) + return r; + + r = set_put(*addresses, address); + if (r < 0) + return r; + + address->link = link; + + if (ret) + *ret = address; + + address = NULL; + + return 0; +} + +int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { + return address_add_internal(link, &link->addresses_foreign, family, in_addr, prefixlen, ret); +} + +static int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { + int r; + + r = address_add_internal(link, &link->addresses, family, in_addr, prefixlen, ret); + if (r < 0) + return r; + + link_update_operstate(link); + link_dirty(link); + + 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 +295,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 +303,89 @@ int address_release(Address *address, Link *link) { return 0; } -int address_drop(Address *address, Link *link, - sd_netlink_message_handler_t callback) { - _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; - int r; +int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) { + bool ready; assert(address); - assert(address->family == AF_INET || address->family == AF_INET6); - assert(link); - assert(link->ifindex > 0); - assert(link->manager); - assert(link->manager->rtnl); + assert(cinfo); - address_release(address, link); + ready = address_is_ready(address); - r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR, - link->ifindex, address->family); - if (r < 0) - return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m"); + address->added = true; + address->flags = flags; + address->scope = scope; + address->cinfo = *cinfo; - r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); - if (r < 0) - return log_error_errno(r, "Could not set prefixlen: %m"); + if (!ready && address_is_ready(address) && address->link) + link_check_ready(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"); + return 0; +} - 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"); +int address_drop(Address *address) { + Link *link; + bool ready; - link_ref(link); + 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_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) { + existing = set_get(link->addresses_foreign, &address); + if (!existing) + return -ENOENT; + } + + *ret = existing; return 0; } -int address_update(Address *address, Link *link, - sd_netlink_message_handler_t callback) { +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 +393,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 +435,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 +461,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 +476,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 +538,23 @@ int address_configure(Address *address, Link *link, if (r < 0) return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m"); - r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); + r = address_establish(address, link); if (r < 0) + return r; + + r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); + if (r < 0) { + address_release(address); return log_error_errno(r, "Could not send rtnetlink message: %m"); + } link_ref(link); - address_establish(address, link); + r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL); + if (r < 0) { + address_release(address); + return log_error_errno(r, "Could not add address: %m"); + } return 0; } @@ -430,15 +586,13 @@ int config_parse_broadcast( return r; if (n->family == AF_INET6) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue); return 0; } r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Broadcast is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Broadcast is invalid, ignoring assignment: %s", rvalue); return 0; } @@ -487,10 +641,10 @@ int config_parse_address(const char *unit, e = strchr(rvalue, '/'); if (e) { unsigned i; + r = safe_atou(e + 1, &i); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Prefix length is invalid, ignoring assignment: %s", e + 1); + log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1); return 0; } @@ -502,23 +656,20 @@ int config_parse_address(const char *unit, r = in_addr_from_string_auto(address, &f, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Address is invalid, ignoring assignment: %s", address); + log_syntax(unit, LOG_ERR, filename, line, r, "Address is invalid, ignoring assignment: %s", address); return 0; } if (!e && f == AF_INET) { r = in_addr_default_prefixlen(&buffer.in, &n->prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address); + log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address); return 0; } } if (n->family != AF_UNSPEC && f != n->family) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Address is incompatible, ignoring assignment: %s", address); + log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", address); return 0; } @@ -567,9 +718,7 @@ int config_parse_label(const char *unit, return log_oom(); if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Interface label is not ASCII clean or is too" - " long, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue); free(label); return 0; } @@ -587,49 +736,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; - - 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; +bool address_is_ready(const Address *a) { + assert(a); - b1 = (uint64_t*)&a1->in_addr.in6; - b2 = (uint64_t*)&a2->in_addr.in6; - - return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL); - } - - default: - assert_not_reached("Invalid address family"); - } + return a->added && !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } |