diff options
-rw-r--r-- | src/network/networkd-address.c | 185 | ||||
-rw-r--r-- | src/network/networkd-address.h | 9 | ||||
-rw-r--r-- | src/network/networkd-dhcp4.c | 24 | ||||
-rw-r--r-- | src/network/networkd-dhcp6.c | 10 | ||||
-rw-r--r-- | src/network/networkd-ipv4ll.c | 13 | ||||
-rw-r--r-- | src/network/networkd-link.c | 194 | ||||
-rw-r--r-- | src/network/networkd-link.h | 6 | ||||
-rw-r--r-- | src/network/networkd-manager.c | 434 | ||||
-rw-r--r-- | src/network/networkd-network.c | 7 | ||||
-rw-r--r-- | src/network/networkd-route.c | 80 | ||||
-rw-r--r-- | src/network/networkd-route.h | 5 | ||||
-rw-r--r-- | src/network/networkd.h | 7 |
12 files changed, 579 insertions, 395 deletions
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 316ae2e4cb..5d443e9b9b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -95,8 +95,10 @@ void address_free(Address *address) { UINT_TO_PTR(address->section)); } - if (address->link) + if (address->link) { set_remove(address->link->addresses, address); + set_remove(address->link->addresses_foreign, address); + } free(address); } @@ -206,9 +208,9 @@ static 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) { @@ -225,13 +227,17 @@ static int address_establish(Address *address, Link *link) { return 0; } -int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { +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(link); + assert(addresses); assert(in_addr); - assert(ret); r = address_new(&address); if (r < 0) @@ -241,31 +247,46 @@ int address_add(Link *link, int family, const union in_addr_union *in_addr, unsi address->in_addr = *in_addr; address->prefixlen = prefixlen; - r = set_ensure_allocated(&link->addresses, &address_hash_ops); + r = set_ensure_allocated(addresses, &address_hash_ops); if (r < 0) return r; - r = set_put(link->addresses, address); + r = set_put(*addresses, address); if (r < 0) return r; address->link = link; - r = address_establish(address, 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; - *ret = address; - address = NULL; + link_update_operstate(link); + link_dirty(link); return 0; } -static int address_release(Address *address, Link *link) { +static int address_release(Address *address) { int r; assert(address); - assert(link); + assert(address->link); /* Remove masquerading firewall entry if it was added */ if (address->ip_masquerade_done) { @@ -274,7 +295,7 @@ static 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; } @@ -282,12 +303,42 @@ static int address_release(Address *address, Link *link) { return 0; } +int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) { + bool ready; + + assert(address); + assert(cinfo); + + ready = address_is_ready(address); + + address->added = true; + address->flags = flags; + address->scope = scope; + address->cinfo = *cinfo; + + if (!ready && address_is_ready(address) && address->link) + link_check_ready(address->link); + + return 0; +} + int address_drop(Address *address) { + Link *link; + bool ready; + assert(address); - address_release(address, address->link); + 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; } @@ -303,8 +354,11 @@ int address_get(Link *link, int family, const union in_addr_union *in_addr, unsi address.prefixlen = prefixlen; existing = set_get(link->addresses, &address); - if (!existing) - return -ENOENT; + if (!existing) { + existing = set_get(link->addresses_foreign, &address); + if (!existing) + return -ENOENT; + } *ret = existing; @@ -323,8 +377,6 @@ int address_remove(Address *address, Link *link, assert(link->manager); assert(link->manager->rtnl); - address_release(address, link); - r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR, link->ifindex, address->family); if (r < 0) @@ -350,74 +402,6 @@ int address_remove(Address *address, Link *link, return 0; } -int address_update(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->ifindex > 0); - assert(link->manager); - assert(link->manager->rtnl); - - r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, - link->ifindex, address->family); - if (r < 0) - return log_error_errno(r, "Could not allocate RTM_NEWADDR 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) - 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 (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"); - - link_ref(link); - - return 0; -} - static int address_acquire(Link *link, Address *original, Address **ret) { union in_addr_union in_addr = {}; struct in_addr broadcast = {}; @@ -477,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; @@ -493,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"); @@ -551,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; } @@ -742,5 +739,5 @@ int config_parse_label(const char *unit, bool address_is_ready(const Address *a) { assert(a); - return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); + return a->added && !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 425344fe48..fd309bebb6 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -52,7 +52,8 @@ struct Address { union in_addr_union in_addr; union in_addr_union in_addr_peer; - bool ip_masquerade_done; + bool added:1; + bool ip_masquerade_done:1; LIST_FIELDS(Address, addresses); }; @@ -60,11 +61,11 @@ struct Address { int address_new_static(Network *network, unsigned section, 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_add_foreign(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); -int address_update(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); 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); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 5d9bfcea7c..22594a9415 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -45,7 +45,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, if (!link->dhcp4_messages) { link->dhcp4_configured = true; - link_client_handler(link); + link_check_ready(link); } return 1; @@ -72,11 +72,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(&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(&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"); @@ -88,6 +90,7 @@ static int link_set_dhcp_routes(Link *link) { route_gw->dst_prefixlen = 32; route_gw->prefsrc_addr.in = address; route_gw->scope = RT_SCOPE_LINK; + route_gw->protocol = RTPROT_DHCP; route_gw->metrics = link->network->dhcp_route_metric; r = route_configure(route_gw, link, &dhcp4_route_handler); @@ -120,11 +123,12 @@ static int link_set_dhcp_routes(Link *link) { for (i = 0; i < n; i++) { _cleanup_route_free_ Route *route = NULL; - r = route_new(&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->protocol = RTPROT_DHCP; route->in_addr.in = static_routes[i].gw_addr; route->dst_addr.in = static_routes[i].dst_addr; route->dst_prefixlen = static_routes[i].dst_prefixlen; @@ -162,7 +166,7 @@ static int dhcp_lease_lost(Link *link) { for (i = 0; i < n; i++) { _cleanup_route_free_ Route *route = NULL; - r = route_new(&route, RTPROT_UNSPEC); + r = route_new(&route); if (r >= 0) { route->family = AF_INET; route->in_addr.in = routes[i].gw_addr; @@ -183,7 +187,7 @@ static int dhcp_lease_lost(Link *link) { _cleanup_route_free_ Route *route_gw = NULL; _cleanup_route_free_ Route *route = NULL; - r = route_new(&route_gw, RTPROT_UNSPEC); + r = route_new(&route_gw); if (r >= 0) { route_gw->family = AF_INET; route_gw->dst_addr.in = gateway; @@ -194,7 +198,7 @@ static int dhcp_lease_lost(Link *link) { &link_route_remove_handler); } - r = route_new(&route, RTPROT_UNSPEC); + r = route_new(&route); if (r >= 0) { route->family = AF_INET; route->in_addr.in = gateway; @@ -299,9 +303,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; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 2f9ecf7a89..fb93e6606e 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -63,7 +63,7 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, return 1; } -static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr, +static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr, uint8_t prefixlen, uint32_t lifetime_preferred, uint32_t lifetime_valid) { int r; @@ -87,7 +87,7 @@ static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr, SD_ICMP6_ND_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"); @@ -121,7 +121,7 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { if (r == -EADDRNOTAVAIL) prefixlen = 128; - r = dhcp6_address_update(link, &ip6_addr, prefixlen, + r = dhcp6_address_change(link, &ip6_addr, prefixlen, lifetime_preferred, lifetime_valid); if (r < 0) return r; @@ -176,7 +176,7 @@ 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) { @@ -300,7 +300,7 @@ static int dhcp6_prefix_expired(Link *link) { 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); + dhcp6_address_change(link, &ip6_addr, 128, lifetime_preferred, lifetime_valid); } return 0; diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 01ee9f9f4a..2fdb77ef6c 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -55,7 +55,7 @@ static int ipv4ll_address_lost(Link *link) { address_remove(address, link, &link_address_remove_handler); - r = route_new(&route, RTPROT_UNSPEC); + r = route_new(&route); if (r < 0) { log_link_error_errno(link, r, "Could not allocate route: %m"); return r; @@ -67,7 +67,7 @@ static int ipv4ll_address_lost(Link *link) { 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; } @@ -110,7 +110,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void link->ipv4ll_address = true; if (link->ipv4ll_route == true) - link_client_handler(link); + link_check_ready(link); return 1; } @@ -143,18 +143,19 @@ 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(&route, RTPROT_STATIC); + r = route_new(&route); if (r < 0) return r; route->family = AF_INET; route->scope = RT_SCOPE_LINK; + route->protocol = RTPROT_STATIC; route->metrics = IPV4LL_ROUTE_METRIC; r = route_configure(route, link, ipv4ll_route_handler); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 9d97089576..ec4b082542 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -130,6 +130,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)) \ @@ -202,7 +253,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; } @@ -297,6 +348,11 @@ static void link_free(Link *link) { set_free(link->addresses); + while (!set_isempty(link->addresses_foreign)) + address_free(set_first(link->addresses_foreign)); + + set_free(link->addresses_foreign); + while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); address_free(address); @@ -321,6 +377,7 @@ static void link_free(Link *link) { free(link->ifname); + (void)unlink(link->state_file); free(link->state_file); udev_device_unref(link->udev_device); @@ -399,7 +456,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) { @@ -457,7 +514,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) { @@ -498,14 +555,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; @@ -523,6 +585,10 @@ void link_client_handler(Link *link) { !link->dhcp4_configured && !link->dhcp6_configured)) return; + SET_FOREACH(a, link->addresses, i) + if (!address_is_ready(a)) + return; + if (link->state != LINK_STATE_CONFIGURED) link_enter_configured(link); @@ -550,7 +616,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata 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; @@ -579,7 +645,7 @@ 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"); @@ -736,7 +802,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); @@ -1446,14 +1512,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; @@ -1488,14 +1554,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; @@ -1531,7 +1597,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; @@ -1545,7 +1611,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); } } @@ -1569,7 +1635,7 @@ static void link_free_carrier_maps(Link *link) { } if (list_updated) - link_save(link); + link_dirty(link); return; } @@ -1584,6 +1650,7 @@ void link_drop(Link *link) { log_link_debug(link, "Link removed"); + (void)unlink(link->state_file); link_unref(link); return; @@ -1653,7 +1720,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 && @@ -2297,50 +2364,12 @@ 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; - 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; - } - - 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; + Iterator i; int r; assert(link); @@ -2348,12 +2377,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; @@ -2518,11 +2541,25 @@ int link_save(Link *link) { fprintf(f, "LLMNR=%s\n", resolve_support_to_string(link->network->llmnr)); + + fprintf(f, "ADDRESSES="); + 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; + } + + fputs("\n", f); } if (!hashmap_isempty(link->bound_to_links)) { Link *carrier; - Iterator i; bool space = false; fputs("CARRIER_BOUND_TO=", f); @@ -2538,7 +2575,6 @@ int link_save(Link *link) { if (!hashmap_isempty(link->bound_by_links)) { Link *carrier; - Iterator i; bool space = false; fputs("CARRIER_BOUND_BY=", f); @@ -2605,6 +2641,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 b81bae3830..af2ba11701 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -84,6 +84,7 @@ struct Link { unsigned enslaving; Set *addresses; + Set *addresses_foreign; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; @@ -126,10 +127,13 @@ int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *use 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); +void link_dirty(Link *link); +void link_clean(Link *link); int link_save(Link *link); int link_carrier_reset(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 07e47b668c..76d5aa5585 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -397,28 +397,20 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, switch (type) { case RTM_NEWADDR: - if (address) { + 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); + else { + /* An address appeared that we did not request */ + r = address_add_foreign(link, family, &in_addr, prefixlen, &address); if (r < 0) { log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen); return 0; } else log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, prefixlen, valid_str); - - address->scope = scope; - address->flags = flags; - address->cinfo = cinfo; - - link_save(link); } + address_update(address, scope, flags, &cinfo); + break; case RTM_DELADDR: @@ -581,6 +573,213 @@ static int manager_connect_rtnl(Manager *m) { 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; @@ -602,6 +801,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; @@ -696,8 +899,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, @@ -803,191 +1017,6 @@ 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 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); -} - -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"); - } - - 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); -} - int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) { AddressPool *p; int r; @@ -1044,3 +1073,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-network.c b/src/network/networkd-network.c index 5b8ca305bc..97ada56866 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -369,10 +369,9 @@ int network_apply(Manager *manager, Network *network, Link *link) { 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-route.c b/src/network/networkd-route.c index 1c8302ffaa..aa4ec230ab 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -19,14 +19,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "conf-parser.h" +#include "in-addr-util.h" #include "netlink-util.h" +#include "util.h" #include "networkd.h" #include "networkd-route.h" -int route_new(Route **ret, unsigned char rtm_protocol) { +int route_new(Route **ret) { _cleanup_route_free_ Route *route = NULL; route = new0(Route, 1); @@ -35,7 +36,8 @@ int route_new(Route **ret, unsigned char rtm_protocol) { route->family = AF_UNSPEC; route->scope = RT_SCOPE_UNIVERSE; - route->protocol = rtm_protocol; + route->protocol = RTPROT_UNSPEC; + route->table = RT_TABLE_DEFAULT; *ret = route; route = NULL; @@ -58,10 +60,11 @@ int route_new_static(Network *network, unsigned section, Route **ret) { } } - r = route_new(&route, RTPROT_STATIC); + r = route_new(&route); if (r < 0) return r; + route->protocol = RTPROT_STATIC; route->network = network; LIST_PREPEND(routes, network->static_routes, route); @@ -93,6 +96,75 @@ void route_free(Route *route) { free(route); } +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_addr, 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: + //TODO: check IPv6 routes + 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_addr, &b->dst_addr, 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_remove(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index e3ed1be866..c9972e4933 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -36,6 +36,9 @@ struct Route { unsigned char scope; uint32_t metrics; unsigned char protocol; /* RTPROT_* */ + unsigned char tos; + unsigned char priority; + unsigned char table; union in_addr_union in_addr; union in_addr_union dst_addr; @@ -46,7 +49,7 @@ struct Route { }; int route_new_static(Network *network, unsigned section, Route **ret); -int route_new(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_remove(Route *route, Link *link, sd_netlink_message_handler_t callback); diff --git a/src/network/networkd.h b/src/network/networkd.h index cbec6d5b7e..6c5a9939be 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; @@ -83,7 +86,7 @@ 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); +void manager_dirty(Manager *m); int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found); |