X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fnetwork%2Fnetworkd-link.c;h=437c59843ed9aaa4c971dfb28635c59427516fd0;hp=0665e1fcfa4aa1661b8c15d04990129a4f7a3fac;hb=851c9f82736c89d423b244a292e153ec7124d309;hpb=2eec67acbb00593e414549a7e5b35eb7dd776b1b diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 0665e1fcf..437c59843 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -272,6 +272,8 @@ static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) { static void link_free(Link *link) { Address *address; + Iterator i; + Link *carrier; if (!link) return; @@ -309,6 +311,14 @@ static void link_free(Link *link) { udev_device_unref(link->udev_device); + HASHMAP_FOREACH (carrier, link->bound_to_links, i) + hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex)); + hashmap_free(link->bound_to_links); + + HASHMAP_FOREACH (carrier, link->bound_by_links, i) + hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex)); + hashmap_free(link->bound_by_links); + free(link); } @@ -355,19 +365,6 @@ static void link_set_state(Link *link, LinkState state) { return; } -void link_drop(Link *link) { - if (!link || link->state == LINK_STATE_LINGER) - return; - - link_set_state(link, LINK_STATE_LINGER); - - log_link_debug(link, "link removed"); - - link_unref(link); - - return; -} - static void link_enter_unmanaged(Link *link) { assert(link); @@ -1148,13 +1145,319 @@ static int link_up(Link *link) { return 0; } +static int link_down_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_rtnl_message_get_errno(m); + if (r < 0) + log_link_warning_errno(link, -r, "%-*s: could not bring down interface: %m", IFNAMSIZ, link->ifname); + + return 1; +} + +static int link_down(Link *link) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + + log_link_debug(link, "bringing link down"); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, + RTM_SETLINK, link->ifindex); + if (r < 0) { + log_link_error(link, "Could not allocate RTM_SETLINK message"); + return r; + } + + r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP); + if (r < 0) { + log_link_error(link, "Could not set link flags: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_call_async(link->manager->rtnl, req, link_down_handler, link, + 0, NULL); + if (r < 0) { + log_link_error(link, + "Could not send rtnetlink message: %s", + strerror(-r)); + return r; + } + + link_ref(link); + + return 0; +} + +static int link_handle_bound_to_list(Link *link) { + Link *l; + Iterator i; + int r; + bool required_up = false; + bool link_is_up = false; + + assert(link); + + if (hashmap_isempty(link->bound_to_links)) + return 0; + + if (link->flags & IFF_UP) + link_is_up = true; + + HASHMAP_FOREACH (l, link->bound_to_links, i) + if (link_has_carrier(l)) { + required_up = true; + break; + } + + if (!required_up && link_is_up) { + r = link_down(link); + if (r < 0) + return r; + } else if (required_up && !link_is_up) { + r = link_up(link); + if (r < 0) + return r; + } + + return 0; +} + +static int link_handle_bound_by_list(Link *link) { + Iterator i; + Link *l; + int r; + + assert(link); + + if (hashmap_isempty(link->bound_by_links)) + return 0; + + HASHMAP_FOREACH (l, link->bound_by_links, i) { + r = link_handle_bound_to_list(l); + if (r < 0) + return r; + } + + return 0; +} + +static int link_put_carrier(Link *link, Link *carrier, Hashmap **h) { + int r; + + assert(link); + assert(carrier); + + if (link == carrier) + return 0; + + if (hashmap_get(*h, INT_TO_PTR(carrier->ifindex))) + return 0; + + r = hashmap_ensure_allocated(h, NULL); + if (r < 0) + return r; + + r = hashmap_put(*h, INT_TO_PTR(carrier->ifindex), carrier); + if (r < 0) + return r; + + return 0; +} + +static int link_new_bound_by_list(Link *link) { + Manager *m; + Link *carrier; + Iterator i; + int r; + bool list_updated = false; + + assert(link); + assert(link->manager); + + m = link->manager; + + HASHMAP_FOREACH (carrier, m->links, i) { + if (!carrier->network) + continue; + + if (strv_isempty(carrier->network->bind_carrier)) + continue; + + if (strv_fnmatch(carrier->network->bind_carrier, link->ifname, 0)) { + r = link_put_carrier(link, carrier, &link->bound_by_links); + if (r < 0) + return r; + + list_updated = true; + } + } + + if (list_updated) + link_save(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); + } + + return 0; +} + +static int link_new_bound_to_list(Link *link) { + Manager *m; + Link *carrier; + Iterator i; + int r; + bool list_updated = false; + + assert(link); + assert(link->manager); + + if (!link->network) + return 0; + + if (strv_isempty(link->network->bind_carrier)) + return 0; + + m = link->manager; + + HASHMAP_FOREACH (carrier, m->links, i) { + if (strv_fnmatch(link->network->bind_carrier, carrier->ifname, 0)) { + r = link_put_carrier(link, carrier, &link->bound_to_links); + if (r < 0) + return r; + + list_updated = true; + } + } + + if (list_updated) + link_save(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); + } + + return 0; +} + +static int link_new_carrier_maps(Link *link) { + int r; + + r = link_new_bound_by_list(link); + if (r < 0) + return r; + + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + + r = link_new_bound_to_list(link); + if (r < 0) + return r; + + r = link_handle_bound_to_list(link); + if (r < 0) + return r; + + return 0; +} + +static void link_free_bound_to_list(Link *link) { + Link *bound_to; + Iterator i; + + HASHMAP_FOREACH (bound_to, link->bound_to_links, i) { + 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); + } + + return; +} + +static void link_free_bound_by_list(Link *link) { + Link *bound_by; + Iterator i; + + HASHMAP_FOREACH (bound_by, link->bound_by_links, i) { + 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_handle_bound_to_list(bound_by); + } + } + + return; +} + +static void link_free_carrier_maps(Link *link) { + bool list_updated = false; + + assert(link); + + if (!hashmap_isempty(link->bound_to_links)) { + link_free_bound_to_list(link); + list_updated = true; + } + + if (!hashmap_isempty(link->bound_by_links)) { + link_free_bound_by_list(link); + list_updated = true; + } + + if (list_updated) + link_save(link); + + return; +} + +void link_drop(Link *link) { + if (!link || link->state == LINK_STATE_LINGER) + return; + + link_set_state(link, LINK_STATE_LINGER); + + link_free_carrier_maps(link); + + log_link_debug(link, "link removed"); + + link_unref(link); + + return; +} + static int link_joined(Link *link) { int r; assert(link); assert(link->network); - if (!(link->flags & IFF_UP)) { + if (!hashmap_isempty(link->bound_to_links)) { + r = link_handle_bound_to_list(link); + if (r < 0) + return r; + } else if (!(link->flags & IFF_UP)) { r = link_up(link); if (r < 0) { link_enter_failed(link); @@ -1429,6 +1732,14 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m, log_link_debug(link, "link state is up-to-date"); + r = link_new_bound_by_list(link); + if (r < 0) + return r; + + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + r = network_get(link->manager, link->udev_device, link->ifname, &link->mac, &network); if (r == -ENOENT) { @@ -1452,6 +1763,10 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m, if (r < 0) return r; + r = link_new_bound_to_list(link); + if (r < 0) + return r; + r = link_configure(link); if (r < 0) return r; @@ -1516,6 +1831,7 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use 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; @@ -1579,11 +1895,12 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use return 0; } - r = sd_rtnl_message_addr_get_flags(message, &address->flags); + r = sd_rtnl_message_addr_get_flags(message, &flags); if (r < 0) { log_link_warning(link, "rtnl: received address with invalid flags, ignoring"); return 0; } + address->flags = flags; switch (address->family) { case AF_INET: @@ -1729,6 +2046,10 @@ static int link_carrier_gained(Link *link) { } } + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + return 0; } @@ -1743,6 +2064,10 @@ static int link_carrier_lost(Link *link) { return r; } + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + return 0; } @@ -1782,16 +2107,26 @@ int link_update(Link *link, sd_rtnl_message *m) { link_ref(link); log_link_info(link, "link readded"); link_set_state(link, LINK_STATE_ENSLAVING); + + r = link_new_carrier_maps(link); + if (r < 0) + return r; } r = sd_rtnl_message_read_string(m, IFLA_IFNAME, &ifname); if (r >= 0 && !streq(ifname, link->ifname)) { log_link_info(link, "renamed to %s", ifname); + link_free_carrier_maps(link); + free(link->ifname); link->ifname = strdup(ifname); if (!link->ifname) return -ENOMEM; + + r = link_new_carrier_maps(link); + if (r < 0) + return r; } r = sd_rtnl_message_read_u32(m, IFLA_MTU, &mtu); @@ -2060,6 +2395,39 @@ int link_save(Link *link) { llmnr_support_to_string(link->network->llmnr)); } + if (!hashmap_isempty(link->bound_to_links)) { + Link *carrier; + Iterator i; + bool space = false; + + fputs("CARRIER_BOUND_TO=", f); + HASHMAP_FOREACH(carrier, link->bound_to_links, i) { + if (space) + fputc(' ', f); + fputs(carrier->ifname, f); + space = true; + } + + fputs("\n", f); + } + + if (!hashmap_isempty(link->bound_by_links)) { + Link *carrier; + Iterator i; + bool space = false; + + fputs("CARRIER_BOUND_BY=", f); + space = false; + HASHMAP_FOREACH(carrier, link->bound_by_links, i) { + if (space) + fputc(' ', f); + fputs(carrier->ifname, f); + space = true; + } + + fputs("\n", f); + } + if (link->dhcp_lease) { assert(link->network);