diff options
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/networkd-address-pool.c | 4 | ||||
-rw-r--r-- | src/network/networkd-address.c | 262 | ||||
-rw-r--r-- | src/network/networkd-address.h | 12 | ||||
-rw-r--r-- | src/network/networkd-dhcp4.c | 32 | ||||
-rw-r--r-- | src/network/networkd-dhcp6.c | 4 | ||||
-rw-r--r-- | src/network/networkd-ipv4ll.c | 14 | ||||
-rw-r--r-- | src/network/networkd-link.c | 255 | ||||
-rw-r--r-- | src/network/networkd-link.h | 7 | ||||
-rw-r--r-- | src/network/networkd-manager.c | 182 | ||||
-rw-r--r-- | src/network/networkd-network.c | 8 | ||||
-rw-r--r-- | src/network/networkd-route.c | 47 | ||||
-rw-r--r-- | src/network/networkd-route.h | 4 | ||||
-rw-r--r-- | src/network/networkd.h | 2 | ||||
-rw-r--r-- | src/network/test-network.c | 13 |
14 files changed, 488 insertions, 358 deletions
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index d609daafde..b3450c1456 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -21,6 +21,7 @@ #include "networkd.h" #include "networkd-address-pool.h" +#include "set.h" int address_pool_new( Manager *m, @@ -96,9 +97,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..316ae2e4cb 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,110 @@ void address_free(Address *address) { UINT_TO_PTR(address->section)); } + if (address->link) + set_remove(address->link->addresses, 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; @@ -131,7 +225,43 @@ int address_establish(Address *address, Link *link) { return 0; } -int address_release(Address *address, Link *link) { +int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { + _cleanup_address_free_ Address *address = NULL; + int r; + + assert(link); + assert(in_addr); + assert(ret); + + r = address_new(&address); + if (r < 0) + return r; + + address->family = family; + address->in_addr = *in_addr; + address->prefixlen = prefixlen; + + 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; + + address->link = link; + + r = address_establish(address, link); + if (r < 0) + return r; + + *ret = address; + address = NULL; + + return 0; +} + +static int address_release(Address *address, Link *link) { int r; assert(address); @@ -152,7 +282,36 @@ int address_release(Address *address, Link *link) { return 0; } -int address_drop(Address *address, Link *link, +int address_drop(Address *address) { + assert(address); + + address_release(address, address->link); + address_free(address); + + 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) + 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; @@ -292,7 +451,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; @@ -580,49 +739,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; - - b1 = (uint64_t*)&a1->in_addr.in6; - b2 = (uint64_t*)&a2->in_addr.in6; +bool address_is_ready(const Address *a) { + assert(a); - 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..425344fe48 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; @@ -56,14 +58,16 @@ struct Address { }; 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_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_drop(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_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..5d9bfcea7c 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -72,11 +72,11 @@ 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, RTPROT_DHCP); if (r < 0) return log_link_error_errno(link, r, "Could not allocate route: %m"); - r = route_new_dynamic(&route_gw, RTPROT_DHCP); + r = route_new(&route_gw, RTPROT_DHCP); if (r < 0) return log_link_error_errno(link, r, "Could not allocate route: %m"); @@ -120,7 +120,7 @@ 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, RTPROT_DHCP); if (r < 0) return log_link_error_errno(link, r, "Could not allocate route: %m"); @@ -162,45 +162,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, RTPROT_UNSPEC); if (r >= 0) { route->family = AF_INET; route->in_addr.in = routes[i].gw_addr; route->dst_addr.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, RTPROT_UNSPEC); if (r >= 0) { route_gw->family = AF_INET; route_gw->dst_addr.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, RTPROT_UNSPEC); if (r >= 0) { route->family = AF_INET; route->in_addr.in = gateway; - route_drop(route, link, - &link_route_drop_handler); + route_remove(route, link, + &link_route_remove_handler); } } @@ -214,7 +214,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 +267,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 +288,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; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 3cb7b8d9ca..2f9ecf7a89 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -58,7 +58,7 @@ 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; } @@ -69,7 +69,7 @@ static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr, int r; _cleanup_address_free_ Address *addr = NULL; - r = address_new_dynamic(&addr); + r = address_new(&addr); if (r < 0) return r; diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 1902b3d23a..01ee9f9f4a 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, RTPROT_UNSPEC); if (r < 0) { log_link_error_errno(link, r, "Could not allocate route: %m"); return r; @@ -65,7 +65,7 @@ static int ipv4ll_address_lost(Link *link) { route->scope = RT_SCOPE_LINK; route->metrics = IPV4LL_ROUTE_METRIC; - route_drop(route, link, &link_route_drop_handler); + route_remove(route, link, &link_route_remove_handler); link_client_handler(link); @@ -105,7 +105,7 @@ 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; @@ -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; @@ -149,7 +149,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { link->ipv4ll_address = false; - r = route_new_dynamic(&route, RTPROT_STATIC); + r = route_new(&route, RTPROT_STATIC); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index aa09065cf5..f6cc1f8ee2 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -23,15 +23,16 @@ #include <linux/if.h> #include <unistd.h> -#include "util.h" -#include "virt.h" -#include "fileio.h" -#include "socket-util.h" #include "bus-util.h" -#include "udev-util.h" -#include "netlink-util.h" #include "dhcp-lease-internal.h" +#include "fileio.h" +#include "netlink-util.h" #include "network-internal.h" +#include "set.h" +#include "socket-util.h" +#include "udev-util.h" +#include "util.h" +#include "virt.h" #include "networkd-link.h" #include "networkd-netdev.h" @@ -291,10 +292,10 @@ 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)); + + set_free(link->addresses); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); @@ -336,15 +337,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; } @@ -531,7 +545,7 @@ 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"); @@ -572,7 +586,7 @@ static int link_enter_set_routes(Link *link) { 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 +599,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 +623,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"); @@ -854,7 +868,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 +881,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 +1033,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; } @@ -1207,7 +1221,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 +1308,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; } @@ -1616,7 +1630,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 @@ -2011,179 +2025,6 @@ int link_initialized(Link *link, struct udev_device *device) { return 0; } -static Address* link_get_equal_address(Link *link, Address *needle) { - Address *i; - - 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; - - 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 = address_new_dynamic(&address); - if (r < 0) - return r; - - 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; - } - - 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 = 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; - } - - 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; - - 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; - } - - break; - - 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; - } - - 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; - } - - 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); - } - - existing = link_get_equal_address(link, address); - - switch (type) { - case RTM_NEWADDR: - if (existing) { - log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); - - - existing->scope = address->scope; - existing->flags = address->flags; - existing->cinfo = address->cinfo; - - } else { - log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); - - LIST_PREPEND(addresses, link->addresses, address); - address_establish(address, link); - - address = NULL; - - link_save(link); - } - - break; - - case RTM_DELADDR: - - 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); - - break; - default: - assert_not_reached("Received invalid RTNL message type"); - } - - return 1; -} - int link_add(Manager *m, sd_netlink_message *message, Link **ret) { Link *link; _cleanup_udev_device_unref_ struct udev_device *device = NULL; @@ -2207,8 +2048,10 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) { /* 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,17 +2061,20 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) { r = link_initialized(link, device); if (r < 0) - return r; + goto failed; } else { /* we are calling a callback directly, so must take a ref */ link_ref(link); r = link_initialized_and_synced(m->rtnl, NULL, link); if (r < 0) - return r; + goto failed; } return 0; +failed: + link_enter_failed(link); + return r; } static int link_carrier_gained(Link *link) { @@ -2425,10 +2271,11 @@ static void link_update_operstate(Link *link) { 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 */ - LIST_FOREACH(addresses, address, link->addresses) { - if (address->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)) + SET_FOREACH(address, link->addresses, i) { + if (!address_is_ready(address)) continue; if (address->scope < scope) diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 7b219c6854..b81bae3830 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -83,7 +83,7 @@ struct Link { unsigned link_messages; unsigned enslaving; - LIST_HEAD(Address, addresses); + Set *addresses; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; @@ -120,8 +120,8 @@ 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); @@ -129,7 +129,6 @@ int link_initialized(Link *link, struct udev_device *device); void link_client_handler(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); int link_save(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index b4259cafef..07e47b668c 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -277,6 +277,166 @@ static int manager_connect_udev(Manager *m) { return 0; } +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: + assert_not_reached("invalid address family"); + } + + if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) { + log_link_warning(link, "Could not print address"); + return 0; + } + + r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); + if (r >= 0) { + if (cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME) + valid_str = "ever"; + else + valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, + cinfo.ifa_valid * USEC_PER_SEC, + USEC_PER_SEC); + } + + address_get(link, family, &in_addr, prefixlen, &address); + + switch (type) { + case RTM_NEWADDR: + if (address) { + log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + + address->scope = scope; + address->flags = flags; + address->cinfo = cinfo; + + } else { + r = address_add(link, family, &in_addr, prefixlen, &address); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen); + return 0; + } else + log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + + address->scope = scope; + address->flags = flags; + address->cinfo = cinfo; + + link_save(link); + } + + break; + + case RTM_DELADDR: + + if (address) { + log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + address_drop(address); + } else + log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + + break; + default: + assert_not_reached("Received invalid RTNL message type"); + } + + return 1; +} + static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { Manager *m = userdata; Link *link = NULL; @@ -410,11 +570,11 @@ 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, &link_rtnl_process_address, m); + r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &manager_rtnl_process_address, m); if (r < 0) return r; @@ -477,14 +637,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 +656,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); } @@ -633,7 +793,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; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 5d22598fc0..bdee7f1923 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -326,12 +326,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; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index ee1ddd81fe..1c8302ffaa 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -26,8 +26,26 @@ #include "networkd.h" #include "networkd-route.h" +int route_new(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; +} + 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,13 +58,9 @@ int route_new_static(Network *network, unsigned section, Route **ret) { } } - route = new0(Route, 1); - if (!route) - return -ENOMEM; - - route->family = AF_UNSPEC; - route->scope = RT_SCOPE_UNIVERSE; - route->protocol = RTPROT_STATIC; + r = route_new(&route, RTPROT_STATIC); + if (r < 0) + return r; route->network = network; @@ -64,23 +78,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; @@ -96,7 +93,7 @@ void route_free(Route *route) { free(route); } -int route_drop(Route *route, Link *link, +int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; int r; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 11e94d44fb..e3ed1be866 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -46,10 +46,10 @@ struct Route { }; 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, unsigned char rtm_protocol); 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); DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free); #define _cleanup_route_free_ _cleanup_(route_freep) diff --git a/src/network/networkd.h b/src/network/networkd.h index eea57ac158..cbec6d5b7e 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -80,6 +80,8 @@ bool manager_should_reload(Manager *m); int manager_rtnl_enumerate_links(Manager *m); int manager_rtnl_enumerate_addresses(Manager *m); +int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata); + int manager_send_changed(Manager *m, const char *property, ...) _sentinel_; int manager_save(Manager *m); diff --git a/src/network/test-network.c b/src/network/test-network.c index 5909cc790e..bac1d6781d 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -143,8 +143,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 +158,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)); |