diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 1f840db22b..ad39f690f6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -238,10 +238,8 @@ static Link *link_free(Link *link) { link->ndisc_routes = set_free(link->ndisc_routes); link->nexthops = set_free(link->nexthops); - link->nexthops_foreign = set_free(link->nexthops_foreign); link->neighbors = set_free(link->neighbors); - link->neighbors_foreign = set_free(link->neighbors_foreign); link->addresses = set_free(link->addresses); link->addresses_foreign = set_free(link->addresses_foreign); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 19e212766e..7190310a13 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -92,8 +92,6 @@ typedef struct Link { unsigned static_route_messages; unsigned static_routing_policy_rule_messages; unsigned address_remove_messages; - unsigned neighbor_remove_messages; - unsigned nexthop_remove_messages; unsigned route_remove_messages; unsigned tc_messages; unsigned sr_iov_messages; @@ -108,11 +106,9 @@ typedef struct Link { Set *pool_addresses; Set *static_addresses; Set *neighbors; - Set *neighbors_foreign; Set *routes; Set *routes_foreign; Set *nexthops; - Set *nexthops_foreign; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index e7aa804f09..9fb12a07f8 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -473,10 +473,7 @@ Manager* manager_free(Manager *m) { hashmap_free(m->route_table_names_by_number); hashmap_free(m->route_table_numbers_by_name); - /* routing_policy_rule_free() access m->rules and m->rules_foreign. - * So, it is necessary to set NULL after the sets are freed. */ - m->rules = set_free(m->rules); - m->rules_foreign = set_free(m->rules_foreign); + set_free(m->rules); sd_netlink_unref(m->rtnl); sd_netlink_unref(m->genl); @@ -490,7 +487,6 @@ Manager* manager_free(Manager *m) { m->routes_foreign = set_free(m->routes_foreign); m->nexthops = set_free(m->nexthops); - m->nexthops_foreign = set_free(m->nexthops_foreign); m->nexthops_by_id = hashmap_free(m->nexthops_by_id); sd_event_source_unref(m->speed_meter_event_source); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 4e78904538..c80095544a 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -64,17 +64,13 @@ struct Manager { char* dynamic_hostname; char* dynamic_timezone; - unsigned routing_policy_rule_remove_messages; Set *rules; - Set *rules_foreign; /* Manage nexthops by id. */ Hashmap *nexthops_by_id; /* Manager stores nexthops without RTA_OIF attribute. */ - unsigned nexthop_remove_messages; Set *nexthops; - Set *nexthops_foreign; /* Manager stores routes without RTA_OIF attribute. */ unsigned route_remove_messages; diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 70d1828c9d..b2b1484a2e 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -21,10 +21,8 @@ Neighbor *neighbor_free(Neighbor *neighbor) { network_config_section_free(neighbor->section); - if (neighbor->link) { + if (neighbor->link) set_remove(neighbor->link->neighbors, neighbor); - set_remove(neighbor->link->neighbors_foreign, neighbor); - } return mfree(neighbor); } @@ -59,6 +57,7 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned .network = network, .family = AF_UNSPEC, .section = TAKE_PTR(n), + .source = NETWORK_CONFIG_SOURCE_STATIC, }; r = hashmap_ensure_put(&network->neighbors_by_section, &network_config_hash_ops, neighbor->section, neighbor); @@ -69,6 +68,25 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned return 0; } +static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) { + _cleanup_(neighbor_freep) Neighbor *dest = NULL; + + assert(neighbor); + assert(ret); + + dest = newdup(Neighbor, neighbor, 1); + if (!dest) + return -ENOMEM; + + /* Unset all pointers */ + dest->link = NULL; + dest->network = NULL; + dest->section = NULL; + + *ret = TAKE_PTR(dest); + return 0; +} + void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) { assert(neighbor); @@ -120,100 +138,32 @@ static int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) { assert(in); existing = set_get(link->neighbors, in); - if (existing) { - if (ret) - *ret = existing; - return 1; - } + if (!existing) + return -ENOENT; - existing = set_get(link->neighbors_foreign, in); - if (existing) { - if (ret) - *ret = existing; - return 0; - } - - return -ENOENT; + if (ret) + *ret = existing; + return 0; } -static int neighbor_add_internal(Link *link, Set **neighbors, const Neighbor *in, Neighbor **ret) { - _cleanup_(neighbor_freep) Neighbor *neighbor = NULL; +static int neighbor_add(Link *link, Neighbor *neighbor) { int r; assert(link); - assert(neighbors); - assert(in); + assert(neighbor); - neighbor = new(Neighbor, 1); - if (!neighbor) - return -ENOMEM; - - *neighbor = (Neighbor) { - .family = in->family, - .in_addr = in->in_addr, - .lladdr = in->lladdr, - .lladdr_size = in->lladdr_size, - }; - - r = set_ensure_put(neighbors, &neighbor_hash_ops, neighbor); + r = set_ensure_put(&link->neighbors, &neighbor_hash_ops, neighbor); if (r < 0) return r; if (r == 0) return -EEXIST; neighbor->link = link; - - if (ret) - *ret = neighbor; - - TAKE_PTR(neighbor); return 0; } -static int neighbor_add(Link *link, const Neighbor *in, Neighbor **ret) { - Neighbor *neighbor; - int r; - - r = neighbor_get(link, in, &neighbor); - if (r == -ENOENT) { - /* Neighbor doesn't exist, make a new one */ - r = neighbor_add_internal(link, &link->neighbors, in, &neighbor); - if (r < 0) - return r; - } else if (r == 0) { - /* Neighbor is foreign, claim it as recognized */ - r = set_ensure_put(&link->neighbors, &neighbor_hash_ops, neighbor); - if (r < 0) - return r; - - set_remove(link->neighbors_foreign, neighbor); - } else if (r == 1) { - /* Neighbor already exists */ - ; - } else - return r; - - if (ret) - *ret = neighbor; - return 0; -} - -static int neighbor_add_foreign(Link *link, const Neighbor *in, Neighbor **ret) { - return neighbor_add_internal(link, &link->neighbors_foreign, in, ret); -} - -static bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) { - if (n1 == n2) - return true; - - if (!n1 || !n2) - return false; - - return neighbor_compare_func(n1, n2) == 0; -} - static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const Link *link) { - _cleanup_free_ char *lladdr = NULL, *dst = NULL; + _cleanup_free_ char *state = NULL, *lladdr = NULL, *dst = NULL; assert(neighbor); assert(str); @@ -221,6 +171,7 @@ static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const if (!DEBUG_LOGGING) return; + (void) network_config_state_to_string_alloc(neighbor->state, &state); if (neighbor->lladdr_size == sizeof(struct ether_addr)) (void) ether_addr_to_string_alloc(&neighbor->lladdr.mac, &lladdr); else if (neighbor->lladdr_size == sizeof(struct in_addr)) @@ -231,14 +182,15 @@ static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const (void) in_addr_to_string(neighbor->family, &neighbor->in_addr, &dst); log_link_debug(link, - "%s neighbor: lladdr: %s, dst: %s", - str, strna(lladdr), strna(dst)); + "%s %s neighbor (%s): lladdr: %s, dst: %s", + str, strna(network_config_source_to_string(neighbor->source)), strna(state), + strna(lladdr), strna(dst)); } + static int neighbor_configure( - const Neighbor *neighbor, + Neighbor *neighbor, Link *link, - link_netlink_message_handler_t callback, - Neighbor **ret) { + link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -269,10 +221,6 @@ static int neighbor_configure( if (r < 0) return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); - r = neighbor_add(link, neighbor, ret); - if (r < 0) - return log_link_error_errno(link, r, "Could not add neighbor: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link); if (r < 0) @@ -280,6 +228,7 @@ static int neighbor_configure( link_ref(link); + neighbor_enter_configuring(neighbor); return r; } @@ -313,18 +262,41 @@ static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_messag static int link_request_neighbor( Link *link, - Neighbor *neighbor, - bool consume_object, + const Neighbor *neighbor, unsigned *message_counter, link_netlink_message_handler_t netlink_handler, Request **ret) { + Neighbor *existing; + int r; + assert(link); assert(neighbor); + assert(neighbor->source != NETWORK_CONFIG_SOURCE_FOREIGN); - log_neighbor_debug(neighbor, "Requesting", link); - return link_queue_request(link, REQUEST_TYPE_NEIGHBOR, neighbor, consume_object, - message_counter, netlink_handler, ret); + if (neighbor_get(link, neighbor, &existing) < 0) { + _cleanup_(neighbor_freep) Neighbor *tmp = NULL; + + r = neighbor_dup(neighbor, &tmp); + if (r < 0) + return r; + + r = neighbor_add(link, tmp); + if (r < 0) + return r; + + existing = TAKE_PTR(tmp); + } else + existing->source = neighbor->source; + + log_neighbor_debug(existing, "Requesting", link); + r = link_queue_request(link, REQUEST_TYPE_NEIGHBOR, existing, false, + message_counter, netlink_handler, ret); + if (r <= 0) + return r; + + neighbor_enter_requesting(existing); + return 1; } int link_request_static_neighbors(Link *link) { @@ -338,7 +310,7 @@ int link_request_static_neighbors(Link *link) { link->static_neighbors_configured = false; HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) { - r = link_request_neighbor(link, neighbor, false, &link->static_neighbor_messages, + r = link_request_neighbor(link, neighbor, &link->static_neighbor_messages, static_neighbor_configure_handler, NULL); if (r < 0) return log_link_warning_errno(link, r, "Could not request neighbor: %m"); @@ -360,9 +332,6 @@ static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link assert(m); assert(link); - assert(link->neighbor_remove_messages > 0); - - link->neighbor_remove_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -375,15 +344,17 @@ static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link return 1; } -static int neighbor_remove(Neighbor *neighbor, Link *link) { +static int neighbor_remove(Neighbor *neighbor) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + Link *link; int r; assert(neighbor); - assert(link); - assert(link->ifindex > 0); - assert(link->manager); - assert(link->manager->rtnl); + assert(neighbor->link); + assert(neighbor->link->manager); + assert(neighbor->link->manager->rtnl); + + link = neighbor->link; log_neighbor_debug(neighbor, "Removing", link); @@ -402,45 +373,49 @@ static int neighbor_remove(Neighbor *neighbor, Link *link) { return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); - link->neighbor_remove_messages++; + neighbor_enter_removing(neighbor); return 0; } -static bool link_is_neighbor_configured(Link *link, Neighbor *neighbor) { - Neighbor *net_neighbor; - - assert(link); - assert(neighbor); - - if (!link->network) - return false; - - HASHMAP_FOREACH(net_neighbor, link->network->neighbors_by_section) - if (neighbor_equal(net_neighbor, neighbor)) - return true; - - return false; -} - int link_drop_foreign_neighbors(Link *link) { Neighbor *neighbor; - int r; + int k, r = 0; assert(link); + assert(link->network); - SET_FOREACH(neighbor, link->neighbors_foreign) - if (link_is_neighbor_configured(link, neighbor)) { - r = neighbor_add(link, neighbor, NULL); - if (r < 0) - return r; - } else { - r = neighbor_remove(neighbor, link); - if (r < 0) - return r; - } + /* First, mark all neighbors. */ + SET_FOREACH(neighbor, link->neighbors) { + /* Do not remove neighbors we configured. */ + if (neighbor->source != NETWORK_CONFIG_SOURCE_FOREIGN) + continue; - return 0; + /* Ignore neighbors not assigned yet or already removing. */ + if (!neighbor_exists(neighbor)) + continue; + + neighbor_mark(neighbor); + } + + /* Next, unmark requested neighbors. They will be configured later. */ + HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) { + Neighbor *existing; + + if (neighbor_get(link, neighbor, &existing) >= 0) + neighbor_unmark(existing); + } + + SET_FOREACH(neighbor, link->neighbors) { + if (!neighbor_is_marked(neighbor)) + continue; + + k = neighbor_remove(neighbor); + if (k < 0 && r >= 0) + r = k; + } + + return r; } int link_drop_neighbors(Link *link) { @@ -450,7 +425,11 @@ int link_drop_neighbors(Link *link) { assert(link); SET_FOREACH(neighbor, link->neighbors) { - k = neighbor_remove(neighbor, link); + /* Ignore neighbors not assigned yet or already removing. */ + if (!neighbor_exists(neighbor)) + continue; + + k = neighbor_remove(neighbor); if (k < 0 && r >= 0) r = k; } @@ -459,7 +438,6 @@ int link_drop_neighbors(Link *link) { } int request_process_neighbor(Request *req) { - Neighbor *ret; int r; assert(req); @@ -470,10 +448,7 @@ int request_process_neighbor(Request *req) { if (!link_is_ready_to_configure(req->link, false)) return 0; - if (req->link->neighbor_remove_messages > 0) - return 0; - - r = neighbor_configure(req->neighbor, req->link, req->netlink_handler, &ret); + r = neighbor_configure(req->neighbor, req->link, req->netlink_handler); if (r < 0) return r; @@ -566,22 +541,32 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, switch (type) { case RTM_NEWNEIGH: - if (neighbor) + if (neighbor) { + neighbor_enter_configured(neighbor); log_neighbor_debug(tmp, "Received remembered", link); - else { - log_neighbor_debug(tmp, "Remembering foreign", link); - r = neighbor_add_foreign(link, tmp, NULL); + } else { + neighbor_enter_configured(tmp); + log_neighbor_debug(tmp, "Remembering", link); + r = neighbor_add(link, tmp); if (r < 0) { log_link_warning_errno(link, r, "Failed to remember foreign neighbor, ignoring: %m"); return 0; } + TAKE_PTR(tmp); } break; case RTM_DELNEIGH: - log_neighbor_debug(tmp, neighbor ? "Forgetting" : "Kernel removed unknown", link); - neighbor_free(neighbor); + if (neighbor) { + neighbor_enter_removed(neighbor); + if (neighbor->state == 0) { + log_neighbor_debug(neighbor, "Forgetting", link); + neighbor_free(neighbor); + } else + log_neighbor_debug(neighbor, "Removed", link); + } else + log_neighbor_debug(tmp, "Kernel removed unknown", link); break; diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index 8feb515e05..9c122f341b 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -24,6 +24,8 @@ typedef struct Neighbor { Network *network; Link *link; NetworkConfigSection *section; + NetworkConfigSource source; + NetworkConfigState state; int family; union in_addr_union in_addr; @@ -46,6 +48,8 @@ int request_process_neighbor(Request *req); int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Neighbor, neighbor); + CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address); CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_lladdr); diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index 0015ab4c0c..6e87872031 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -31,7 +31,6 @@ NextHop *nexthop_free(NextHop *nexthop) { if (nexthop->link) { set_remove(nexthop->link->nexthops, nexthop); - set_remove(nexthop->link->nexthops_foreign, nexthop); if (nexthop->link->manager && nexthop->id > 0) hashmap_remove(nexthop->link->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); @@ -39,7 +38,6 @@ NextHop *nexthop_free(NextHop *nexthop) { if (nexthop->manager) { set_remove(nexthop->manager->nexthops, nexthop); - set_remove(nexthop->manager->nexthops_foreign, nexthop); if (nexthop->id > 0) hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); @@ -96,6 +94,7 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s nexthop->protocol = RTPROT_STATIC; nexthop->network = network; nexthop->section = TAKE_PTR(n); + nexthop->source = NETWORK_CONFIG_SOURCE_STATIC; r = hashmap_ensure_put(&network->nexthops_by_section, &network_config_hash_ops, nexthop->section, nexthop); if (r < 0) @@ -221,155 +220,133 @@ int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) { return 0; } -static int nexthop_get(Manager *manager, Link *link, const NextHop *in, NextHop **ret) { - NextHop *existing; +static bool nexthop_owned_by_link(const NextHop *nexthop) { + return !nexthop->blackhole && hashmap_isempty(nexthop->group); +} + +static int nexthop_get(Manager *manager, Link *link, NextHop *in, NextHop **ret) { + NextHop *nexthop; + Set *nexthops; - assert(manager || link); assert(in); - existing = set_get(link ? link->nexthops : manager->nexthops, in); - if (existing) { - if (ret) - *ret = existing; - return 1; + if (nexthop_owned_by_link(in)) { + if (!link) + return -ENOENT; + + nexthops = link->nexthops; + } else { + if (!manager) + return -ENOENT; + + nexthops = manager->nexthops; } - existing = set_get(link ? link->nexthops_foreign : manager->nexthops_foreign, in); - if (existing) { + nexthop = set_get(nexthops, in); + if (nexthop) { if (ret) - *ret = existing; + *ret = nexthop; + return 0; + } + + if (in->id > 0) + return -ENOENT; + + /* Also find nexthop configured without ID. */ + SET_FOREACH(nexthop, nexthops) { + uint32_t id; + bool found; + + id = nexthop->id; + nexthop->id = 0; + found = nexthop_equal(nexthop, in); + nexthop->id = id; + + if (!found) + continue; + + if (ret) + *ret = nexthop; return 0; } return -ENOENT; } -static int nexthop_add_internal(Manager *manager, Link *link, Set **nexthops, const NextHop *in, NextHop **ret) { - _cleanup_(nexthop_freep) NextHop *nexthop = NULL; +static int nexthop_add(Manager *manager, Link *link, NextHop *nexthop) { int r; - assert(manager || link); - assert(nexthops); - assert(in); - - r = nexthop_dup(in, &nexthop); - if (r < 0) - return r; - - r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop); - if (r < 0) - return r; - if (r == 0) - return -EEXIST; - - nexthop->link = link; - nexthop->manager = manager; - - if (ret) - *ret = nexthop; - - TAKE_PTR(nexthop); - return 0; -} - -static int nexthop_add_foreign(Manager *manager, Link *link, const NextHop *in, NextHop **ret) { - assert(manager || link); - return nexthop_add_internal(manager, link, link ? &link->nexthops_foreign : &manager->nexthops_foreign, in, ret); -} - -static bool nexthop_has_link(const NextHop *nexthop) { - return !nexthop->blackhole && hashmap_isempty(nexthop->group); -} - -static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) { - bool by_manager; - NextHop *nexthop; - int r; - - assert(link); - assert(in); - - by_manager = !nexthop_has_link(in); - - if (by_manager) - r = nexthop_get(link->manager, NULL, in, &nexthop); - else - r = nexthop_get(NULL, link, in, &nexthop); - if (r == -ENOENT) { - /* NextHop does not exist, create a new one */ - r = nexthop_add_internal(link->manager, - by_manager ? NULL : link, - by_manager ? &link->manager->nexthops : &link->nexthops, - in, &nexthop); - if (r < 0) - return r; - } else if (r == 0) { - /* Take over a foreign nexthop */ - r = set_ensure_put(by_manager ? &link->manager->nexthops : &link->nexthops, - &nexthop_hash_ops, nexthop); - if (r < 0) - return r; - - set_remove(by_manager ? link->manager->nexthops_foreign : link->nexthops_foreign, nexthop); - } else if (r == 1) { - /* NextHop exists, do nothing */ - ; - } else - return r; - - if (ret) - *ret = nexthop; - return 0; -} - -static int nexthop_update(Manager *manager, Link *link, NextHop *nexthop, const NextHop *in) { - Set *nexthops; - int r; - - /* link may be NULL. */ - - assert(manager); assert(nexthop); - assert(in); - assert(in->id > 0); + assert(nexthop->id > 0); - /* This updates nexthop ID if necessary, and register the nexthop to Manager. */ + if (nexthop_owned_by_link(nexthop)) { + assert(link); - if (nexthop->id > 0) { - if (nexthop->id == in->id) - goto set_manager; - return -EINVAL; + r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + + nexthop->link = link; + + manager = link->manager; + } else { + assert(manager); + + r = set_ensure_put(&manager->nexthops, &nexthop_hash_ops, nexthop); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + + nexthop->manager = manager; } - nexthops = link ? link->nexthops : manager->nexthops; - - nexthop = set_remove(nexthops, nexthop); - if (!nexthop) - return -ENOENT; - - nexthop->id = in->id; - - r = set_put(nexthops, nexthop); - if (r <= 0) { - int k; - - /* On failure, revert the change. */ - nexthop->id = 0; - k = set_put(nexthops, nexthop); - if (k <= 0) { - nexthop_free(nexthop); - return k < 0 ? k : -EEXIST; - } - - return r < 0 ? r : -EEXIST; - } - -set_manager: return hashmap_ensure_put(&manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop); } -static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *str, const Link *link) { - _cleanup_free_ char *gw = NULL, *group = NULL; +static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) { + _cleanup_set_free_ Set *ids = NULL; + Network *network; + uint32_t id; + int r; + + assert(manager); + assert(nexthop); + + if (nexthop->id > 0) + return 0; + + /* Find the lowest unused ID. */ + + ORDERED_HASHMAP_FOREACH(network, manager->networks) { + NextHop *tmp; + + HASHMAP_FOREACH(tmp, network->nexthops_by_section) { + if (tmp->id == 0) + continue; + + r = set_ensure_put(&ids, NULL, UINT32_TO_PTR(tmp->id)); + if (r < 0) + return r; + } + } + + for (id = 1; id < UINT32_MAX; id++) { + if (manager_get_nexthop_by_id(manager, id, NULL) >= 0) + continue; + if (set_contains(ids, UINT32_TO_PTR(id))) + continue; + break; + } + + nexthop->id = id; + return 0; +} + +static void log_nexthop_debug(const NextHop *nexthop, const char *str, const Link *link) { + _cleanup_free_ char *state = NULL, *gw = NULL, *group = NULL; struct nexthop_grp *nhg; assert(nexthop); @@ -380,29 +357,25 @@ static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *s if (!DEBUG_LOGGING) return; - char new_id[STRLEN("→") + DECIMAL_STR_MAX(uint32_t)] = ""; - if (nexthop->id != id) - xsprintf(new_id, "→%"PRIu32, id); - + (void) network_config_state_to_string_alloc(nexthop->state, &state); (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw); HASHMAP_FOREACH(nhg, nexthop->group) (void) strextendf_with_separator(&group, ",", "%"PRIu32":%"PRIu32, nhg->id, nhg->weight+1); - log_link_debug(link, "%s nexthop: id: %"PRIu32"%s, gw: %s, blackhole: %s, group: %s", - str, nexthop->id, new_id, strna(gw), yes_no(nexthop->blackhole), strna(group)); + log_link_debug(link, "%s %s nexthop (%s): id: %"PRIu32", gw: %s, blackhole: %s, group: %s", + str, strna(network_config_source_to_string(nexthop->source)), strna(state), + nexthop->id, strna(gw), yes_no(nexthop->blackhole), strna(group)); } -static int link_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(m); - assert(link); - assert(link->nexthop_remove_messages > 0); - link->nexthop_remove_messages--; + /* link may be NULL. */ - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; r = sd_netlink_message_get_errno(m); @@ -412,37 +385,25 @@ static int link_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, return 1; } -static int manager_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) { - int r; - - assert(m); - assert(manager); - assert(manager->nexthop_remove_messages > 0); - - manager->nexthop_remove_messages--; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -ENOENT) - log_message_warning_errno(m, r, "Could not drop nexthop, ignoring"); - - return 1; -} - -static int nexthop_remove(const NextHop *nexthop, Manager *manager, Link *link) { +static int nexthop_remove(NextHop *nexthop) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + Manager *manager; + Link *link; int r; assert(nexthop); - assert(manager); + assert(nexthop->manager || (nexthop->link && nexthop->link->manager)); /* link may be NULL. */ + link = nexthop->link; + manager = nexthop->manager ?: nexthop->link->manager; if (nexthop->id == 0) { log_link_debug(link, "Cannot remove nexthop without valid ID, ignoring."); return 0; } - log_nexthop_debug(nexthop, nexthop->id, "Removing", link); + log_nexthop_debug(nexthop, "Removing", link); r = sd_rtnl_message_new_nexthop(manager->rtnl, &req, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC); if (r < 0) @@ -452,30 +413,21 @@ static int nexthop_remove(const NextHop *nexthop, Manager *manager, Link *link) if (r < 0) return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m"); - if (link) - r = netlink_call_async(manager->rtnl, NULL, req, link_nexthop_remove_handler, - link_netlink_destroy_callback, link); - else - r = netlink_call_async(manager->rtnl, NULL, req, manager_nexthop_remove_handler, - NULL, manager); + r = netlink_call_async(manager->rtnl, NULL, req, nexthop_remove_handler, + link ? link_netlink_destroy_callback : NULL, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); /* link may be NULL, link_ref() is OK with that */ - if (link) - link->nexthop_remove_messages++; - else - manager->nexthop_remove_messages++; - + nexthop_enter_removing(nexthop); return 0; } static int nexthop_configure( - const NextHop *nexthop, + NextHop *nexthop, Link *link, - link_netlink_message_handler_t callback, - NextHop **ret) { + link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -487,7 +439,7 @@ static int nexthop_configure( assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6)); assert(callback); - log_nexthop_debug(nexthop, nexthop->id, "Configuring", link); + log_nexthop_debug(nexthop, "Configuring", link); r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req, RTM_NEWNEXTHOP, nexthop->family, @@ -539,10 +491,6 @@ static int nexthop_configure( } } - r = nexthop_add(link, nexthop, ret); - if (r < 0) - return log_link_error_errno(link, r, "Could not add nexthop: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link); if (r < 0) @@ -550,7 +498,8 @@ static int nexthop_configure( link_ref(link); - return r; + nexthop_enter_configuring(nexthop); + return 0; } static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -583,17 +532,44 @@ static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link static int link_request_nexthop( Link *link, NextHop *nexthop, - bool consume_object, unsigned *message_counter, link_netlink_message_handler_t netlink_handler, Request **ret) { + NextHop *existing; + int r; + assert(link); assert(nexthop); + assert(nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN); - log_nexthop_debug(nexthop, nexthop->id, "Requesting", link); - return link_queue_request(link, REQUEST_TYPE_NEXTHOP, nexthop, consume_object, - message_counter, netlink_handler, ret); + if (nexthop_get(link->manager, link, nexthop, &existing) < 0) { + _cleanup_(nexthop_freep) NextHop *tmp = NULL; + + r = nexthop_dup(nexthop, &tmp); + if (r < 0) + return r; + + r = nexthop_acquire_id(link->manager, tmp); + if (r < 0) + return r; + + r = nexthop_add(link->manager, link, tmp); + if (r < 0) + return r; + + existing = TAKE_PTR(tmp); + } else + existing->source = nexthop->source; + + log_nexthop_debug(existing, "Requesting", link); + r = link_queue_request(link, REQUEST_TYPE_NEXTHOP, existing, false, + message_counter, netlink_handler, ret); + if (r <= 0) + return r; + + nexthop_enter_requesting(existing); + return 1; } int link_request_static_nexthops(Link *link, bool only_ipv4) { @@ -609,7 +585,7 @@ int link_request_static_nexthops(Link *link, bool only_ipv4) { if (only_ipv4 && nh->family != AF_INET) continue; - r = link_request_nexthop(link, nh, false, &link->static_nexthop_messages, + r = link_request_nexthop(link, nh, &link->static_nexthop_messages, static_nexthop_handler, NULL); if (r < 0) return log_link_warning_errno(link, r, "Could not request nexthop: %m"); @@ -626,58 +602,52 @@ int link_request_static_nexthops(Link *link, bool only_ipv4) { return 0; } -static bool link_has_nexthop(const Link *link, const NextHop *nexthop) { - NextHop *net_nexthop; - - assert(link); - assert(nexthop); - - if (!link->network) - return false; - - HASHMAP_FOREACH(net_nexthop, link->network->nexthops_by_section) - if (nexthop_equal(net_nexthop, nexthop)) - return true; - - return false; -} - -static bool links_have_nexthop(const Manager *manager, const NextHop *nexthop, const Link *except) { - Link *link; - - assert(manager); - - HASHMAP_FOREACH(link, manager->links_by_index) { - if (link == except) - continue; - - if (link_has_nexthop(link, nexthop)) - return true; - } - - return false; -} - -static int manager_drop_nexthops_internal(Manager *manager, bool foreign, const Link *except) { +static int manager_drop_nexthops(Manager *manager, bool foreign, const Link *except) { NextHop *nexthop; - Set *nexthops; + Link *link; int k, r = 0; assert(manager); - nexthops = foreign ? manager->nexthops_foreign : manager->nexthops; - SET_FOREACH(nexthop, nexthops) { + /* First, mark all nexthops. */ + SET_FOREACH(nexthop, manager->nexthops) { /* do not touch nexthop created by the kernel */ if (nexthop->protocol == RTPROT_KERNEL) continue; - /* The nexthop will be configured later, or already configured by a link. */ - if (links_have_nexthop(manager, nexthop, except)) + /* When 'foreign' is true, do not remove nexthops we configured. */ + if (foreign && nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN) continue; - /* The existing links do not have the nexthop. Let's drop this now. It may be - * re-configured later. */ - k = nexthop_remove(nexthop, manager, NULL); + /* Ignore nexthops not assigned yet or already removed. */ + if (!nexthop_exists(nexthop)) + continue; + + nexthop_mark(nexthop); + } + + /* Then, unmark all nexthops requested by active links. */ + HASHMAP_FOREACH(link, manager->links_by_index) { + if (link == except) + continue; + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + continue; + + HASHMAP_FOREACH(nexthop, link->network->nexthops_by_section) { + NextHop *existing; + + if (nexthop_get(manager, NULL, nexthop, &existing) >= 0) + nexthop_unmark(existing); + } + } + + /* Finally, remove all marked nexthops. */ + SET_FOREACH(nexthop, manager->nexthops) { + if (!nexthop_is_marked(nexthop)) + continue; + + k = nexthop_remove(nexthop); if (k < 0 && r >= 0) r = k; } @@ -685,35 +655,50 @@ static int manager_drop_nexthops_internal(Manager *manager, bool foreign, const return r; } -static int manager_drop_foreign_nexthops(Manager *manager) { - return manager_drop_nexthops_internal(manager, true, NULL); -} - -static int manager_drop_nexthops(Manager *manager, const Link *except) { - return manager_drop_nexthops_internal(manager, false, except); -} - int link_drop_foreign_nexthops(Link *link) { NextHop *nexthop; int k, r = 0; assert(link); assert(link->manager); + assert(link->network); - SET_FOREACH(nexthop, link->nexthops_foreign) { + /* First, mark all nexthops. */ + SET_FOREACH(nexthop, link->nexthops) { /* do not touch nexthop created by the kernel */ if (nexthop->protocol == RTPROT_KERNEL) continue; - if (link_has_nexthop(link, nexthop)) - k = nexthop_add(link, nexthop, NULL); - else - k = nexthop_remove(nexthop, link->manager, link); + /* Do not remove nexthops we configured. */ + if (nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + + /* Ignore nexthops not assigned yet or already removed. */ + if (!nexthop_exists(nexthop)) + continue; + + nexthop_mark(nexthop); + } + + /* Then, unmark all nexthops requested by active links. */ + HASHMAP_FOREACH(nexthop, link->network->nexthops_by_section) { + NextHop *existing; + + if (nexthop_get(NULL, link, nexthop, &existing) >= 0) + nexthop_unmark(existing); + } + + /* Finally, remove all marked rules. */ + SET_FOREACH(nexthop, link->nexthops) { + if (!nexthop_is_marked(nexthop)) + continue; + + k = nexthop_remove(nexthop); if (k < 0 && r >= 0) r = k; } - k = manager_drop_foreign_nexthops(link->manager); + k = manager_drop_nexthops(link->manager, /* foreign = */ true, NULL); if (k < 0 && r >= 0) r = k; @@ -732,12 +717,16 @@ int link_drop_nexthops(Link *link) { if (nexthop->protocol == RTPROT_KERNEL) continue; - k = nexthop_remove(nexthop, link->manager, link); + /* Ignore nexthops not assigned yet or already removing. */ + if (!nexthop_exists(nexthop)) + continue; + + k = nexthop_remove(nexthop); if (k < 0 && r >= 0) r = k; } - k = manager_drop_nexthops(link->manager, link); + k = manager_drop_nexthops(link->manager, /* foreign = */ false, link); if (k < 0 && r >= 0) r = k; @@ -753,10 +742,7 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { if (!link_is_ready_to_configure(link, false)) return false; - if (!nexthop_has_link(nexthop)) { - if (link->manager->nexthop_remove_messages > 0) - return false; - } else { + if (nexthop_owned_by_link(nexthop)) { Link *l; /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated @@ -770,18 +756,22 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { HASHMAP_FOREACH(l, link->manager->links_by_index) { if (l->address_remove_messages > 0) return false; - if (l->nexthop_remove_messages > 0) - return false; if (l->route_remove_messages > 0) return false; } } /* All group members must be configured first. */ - HASHMAP_FOREACH(nhg, nexthop->group) - if (manager_get_nexthop_by_id(link->manager, nhg->id, NULL) < 0) + HASHMAP_FOREACH(nhg, nexthop->group) { + NextHop *g; + + if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0) return false; + if (!nexthop_exists(g)) + return false; + } + if (nexthop->id == 0) { Request *req; @@ -797,7 +787,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { } int request_process_nexthop(Request *req) { - NextHop *ret; int r; assert(req); @@ -808,7 +797,7 @@ int request_process_nexthop(Request *req) { if (!nexthop_is_ready_to_configure(req->link, req->nexthop)) return 0; - r = nexthop_configure(req->nexthop, req->link, req->netlink_handler, &ret); + r = nexthop_configure(req->nexthop, req->link, req->netlink_handler); if (r < 0) return r; @@ -956,45 +945,40 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */ - if (!nexthop_has_link(tmp)) + if (!nexthop_owned_by_link(tmp)) link = NULL; - r = nexthop_get(m, link, tmp, &nexthop); - if (r < 0) { - uint32_t id; - - /* The nexthop may be created without setting NHA_ID. */ - - id = tmp->id; - tmp->id = 0; - - (void) nexthop_get(m, link, tmp, &nexthop); - - tmp->id = id; - } + (void) nexthop_get(m, link, tmp, &nexthop); switch (type) { case RTM_NEWNEXTHOP: - if (nexthop) - log_nexthop_debug(nexthop, tmp->id, "Received remembered", link); - else { - log_nexthop_debug(tmp, tmp->id, "Remembering foreign", link); - r = nexthop_add_foreign(m, link, tmp, &nexthop); + if (nexthop) { + nexthop_enter_configured(nexthop); + log_nexthop_debug(tmp, "Received remembered", link); + } else { + nexthop_enter_configured(tmp); + log_nexthop_debug(tmp, "Remembering", link); + + r = nexthop_add(m, link, tmp); if (r < 0) { log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m"); return 0; } + + TAKE_PTR(tmp); } - r = nexthop_update(m, link, nexthop, tmp); - if (r < 0) { - log_link_warning_errno(link, r, "Could not update nexthop, ignoring: %m"); - return 0; - } break; case RTM_DELNEXTHOP: - log_nexthop_debug(tmp, tmp->id, nexthop ? "Forgetting" : "Kernel removed unknown", link); - nexthop_free(nexthop); + if (nexthop) { + nexthop_enter_removed(nexthop); + if (nexthop->state == 0) { + log_nexthop_debug(nexthop, "Forgetting", link); + nexthop_free(nexthop); + } else + log_nexthop_debug(nexthop, "Removed", link); + } else + log_nexthop_debug(tmp, "Kernel removed unknown", link); break; default: diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index 9904ab8ff1..a63ca56d26 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -20,10 +20,11 @@ typedef struct Request Request; typedef struct NextHop { Network *network; - NetworkConfigSection *section; - Manager *manager; Link *link; + NetworkConfigSection *section; + NetworkConfigSource source; + NetworkConfigState state; uint8_t protocol; @@ -51,6 +52,8 @@ int request_process_nexthop(Request *req); int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret); int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(NextHop, nexthop); + CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_family); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 7c89e66ab9..1f613b6e0b 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -1709,14 +1709,21 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0) return false; - HASHMAP_FOREACH(nhg, nh->group) - if (manager_get_nexthop_by_id(link->manager, nhg->id, NULL) < 0) + if (!nexthop_exists(nh)) + return false; + + HASHMAP_FOREACH(nhg, nh->group) { + NextHop *g; + + if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0) return false; + + if (!nexthop_exists(g)) + return false; + } } - if (route_type_is_reject(route) || (nh && nh->blackhole)) { - if (nh && link->manager->nexthop_remove_messages > 0) - return false; + if (route_type_is_reject(route)) { if (link->manager->route_remove_messages > 0) return false; } else { @@ -1725,8 +1732,6 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { HASHMAP_FOREACH(l, link->manager->links_by_index) { if (l->address_remove_messages > 0) return false; - if (l->nexthop_remove_messages > 0) - return false; if (l->route_remove_messages > 0) return false; } diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index e388881e0e..e3b57814f5 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -41,10 +41,8 @@ RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule) { hashmap_remove(rule->network->rules_by_section, rule->section); } - if (rule->manager) { + if (rule->manager) set_remove(rule->manager->rules, rule); - set_remove(rule->manager->rules_foreign, rule); - } network_config_section_free(rule->section); free(rule->iif); @@ -101,6 +99,7 @@ static int routing_policy_rule_new_static(Network *network, const char *filename rule->network = network; rule->section = TAKE_PTR(n); + rule->source = NETWORK_CONFIG_SOURCE_STATIC; rule->protocol = RTPROT_STATIC; r = hashmap_ensure_put(&network->rules_by_section, &network_config_hash_ops, rule->section, rule); @@ -163,9 +162,7 @@ void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash siphash24_compress(&rule->type, sizeof(rule->type), state); siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state); siphash24_compress(&rule->fwmask, sizeof(rule->fwmask), state); - siphash24_compress_boolean(rule->priority_set, state); - if (rule->priority_set) - siphash24_compress(&rule->priority, sizeof(rule->priority), state); + siphash24_compress(&rule->priority, sizeof(rule->priority), state); siphash24_compress(&rule->table, sizeof(rule->table), state); siphash24_compress(&rule->suppress_prefixlen, sizeof(rule->suppress_prefixlen), state); @@ -231,16 +228,10 @@ int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPo if (r != 0) return r; - r = CMP(a->priority_set, b->priority_set); + r = CMP(a->priority, b->priority); if (r != 0) return r; - if (a->priority_set) { - r = CMP(a->priority, b->priority); - if (r != 0) - return r; - } - r = CMP(a->table, b->table); if (r != 0) return r; @@ -301,130 +292,113 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( routing_policy_rule_compare_func, routing_policy_rule_free); -static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *rule, bool require_priority, RoutingPolicyRule **ret) { - RoutingPolicyRule *existing; - int r; +static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *in, RoutingPolicyRule **ret) { + RoutingPolicyRule *rule; assert(m); + assert(in); - existing = set_get(m->rules, rule); - if (existing) { + rule = set_get(m->rules, in); + if (rule) { if (ret) - *ret = existing; - return 1; - } - - existing = set_get(m->rules_foreign, rule); - if (existing) { - if (ret) - *ret = existing; + *ret = rule; return 0; } - if (!require_priority && rule->priority_set) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; + if (in->priority_set) + return -ENOENT; - r = routing_policy_rule_dup(rule, &tmp); - if (r < 0) - return r; + /* Also find rules configured without priority. */ + SET_FOREACH(rule, m->rules) { + uint32_t priority; + bool found; - tmp->priority_set = false; + if (rule->priority_set) + /* The rule is configured with priority. */ + continue; - existing = set_get(m->rules, tmp); - if (existing) { + priority = rule->priority; + rule->priority = 0; + found = routing_policy_rule_equal(rule, in); + rule->priority = priority; + + if (found) { if (ret) - *ret = existing; - return 1; + *ret = rule; + return 0; } } return -ENOENT; } -static int routing_policy_rule_add(Manager *m, const RoutingPolicyRule *in, RoutingPolicyRule **ret) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; - RoutingPolicyRule *existing; - int r; - - assert(m); - assert(in); - assert(IN_SET(in->family, AF_INET, AF_INET6)); - - r = routing_policy_rule_dup(in, &rule); - if (r < 0) - return r; - - r = routing_policy_rule_get(m, rule, true, &existing); - if (r == -ENOENT) { - /* Rule does not exist, use a new one. */ - r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule); - if (r < 0) - return r; - assert(r > 0); - - rule->manager = m; - existing = TAKE_PTR(rule); - } else if (r < 0) - return r; - else if (r == 0) { - /* Take over a foreign rule. */ - r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, existing); - if (r < 0) - return r; - assert(r > 0); - - set_remove(m->rules_foreign, existing); - } /* else r > 0: already exists, do nothing. */ - - if (ret) - *ret = existing; - return 0; -} - -static int routing_policy_rule_consume_foreign(Manager *m, RoutingPolicyRule *rule) { +static int routing_policy_rule_add(Manager *m, RoutingPolicyRule *rule) { int r; assert(m); assert(rule); assert(IN_SET(rule->family, AF_INET, AF_INET6)); - r = set_ensure_consume(&m->rules_foreign, &routing_policy_rule_hash_ops, rule); - if (r <= 0) + r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule); + if (r < 0) return r; + if (r == 0) + return -EEXIST; rule->manager = m; - - return 1; + return 0; } -static int routing_policy_rule_update_priority(RoutingPolicyRule *rule, uint32_t priority) { +static int routing_policy_rule_acquire_priority(Manager *manager, RoutingPolicyRule *rule) { + _cleanup_set_free_ Set *priorities = NULL; + RoutingPolicyRule *tmp; + uint32_t priority; + Network *network; int r; + assert(manager); assert(rule); - assert(rule->manager); + assert(IN_SET(rule->family, AF_INET, AF_INET6)); if (rule->priority_set) return 0; - if (!set_remove(rule->manager->rules, rule)) - return -ENOENT; + /* Find the highest unused priority. Note that 32766 is already used by kernel. + * See kernel_rules[] below. */ - rule->priority = priority; - rule->priority_set = true; - - r = set_put(rule->manager->rules, rule); - if (r <= 0) { - /* Undo */ - rule->priority_set = false; - assert_se(set_put(rule->manager->rules, rule) > 0); - return r == 0 ? -EEXIST : r; + SET_FOREACH(tmp, manager->rules) { + if (tmp->family != rule->family) + continue; + if (tmp->priority == 0 || tmp->priority > 32765) + continue; + r = set_ensure_put(&priorities, NULL, UINT32_TO_PTR(tmp->priority)); + if (r < 0) + return r; } - return 1; + ORDERED_HASHMAP_FOREACH(network, manager->networks) + HASHMAP_FOREACH(tmp, network->rules_by_section) { + if (tmp->family != AF_UNSPEC && tmp->family != rule->family) + continue; + if (!tmp->priority_set) + continue; + if (tmp->priority == 0 || tmp->priority > 32765) + continue; + r = set_ensure_put(&priorities, NULL, UINT32_TO_PTR(tmp->priority)); + if (r < 0) + return r; + } + + for (priority = 32765; priority > 0; priority--) + if (!set_contains(priorities, UINT32_TO_PTR(priority))) + break; + + rule->priority = priority; + return 0; } static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, const char *str, const Link *link, const Manager *m) { - _cleanup_free_ char *from = NULL, *to = NULL, *table = NULL; + _cleanup_free_ char *state = NULL, *from = NULL, *to = NULL, *table = NULL; assert(rule); assert(IN_SET(rule->family, AF_INET, AF_INET6)); @@ -436,13 +410,15 @@ static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, const c if (!DEBUG_LOGGING) return; + (void) network_config_state_to_string_alloc(rule->state, &state); (void) in_addr_prefix_to_string(rule->family, &rule->from, rule->from_prefixlen, &from); (void) in_addr_prefix_to_string(rule->family, &rule->to, rule->to_prefixlen, &to); (void) manager_get_route_table_to_string(m, rule->table, &table); log_link_debug(link, - "%s routing policy rule: priority: %"PRIu32", %s -> %s, iif: %s, oif: %s, table: %s", - str, rule->priority, strna(from), strna(to), + "%s %s routing policy rule (%s): priority: %"PRIu32", %s -> %s, iif: %s, oif: %s, table: %s", + str, strna(network_config_source_to_string(rule->source)), strna(state), + rule->priority, strna(from), strna(to), strna(rule->iif), strna(rule->oif), strna(table)); } @@ -474,11 +450,9 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule return log_link_error_errno(link, r, "Could not set destination prefix length: %m"); } - if (rule->priority_set) { - r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority); - if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m"); - } + r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority); + if (r < 0) + return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m"); if (rule->tos > 0) { r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos); @@ -567,14 +541,10 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule return 0; } -static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) { +static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { int r; assert(m); - assert(manager); - assert(manager->routing_policy_rule_remove_messages > 0); - - manager->routing_policy_rule_remove_messages--; r = sd_netlink_message_get_errno(m); if (r < 0) @@ -583,18 +553,18 @@ static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_messa return 1; } -static int routing_policy_rule_remove(const RoutingPolicyRule *rule, Manager *manager) { +static int routing_policy_rule_remove(RoutingPolicyRule *rule) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(rule); - assert(manager); - assert(manager->rtnl); + assert(rule->manager); + assert(rule->manager->rtnl); assert(IN_SET(rule->family, AF_INET, AF_INET6)); - log_routing_policy_rule_debug(rule, "Removing", NULL, manager); + log_routing_policy_rule_debug(rule, "Removing", NULL, rule->manager); - r = sd_rtnl_message_new_routing_policy_rule(manager->rtnl, &m, RTM_DELRULE, rule->family); + r = sd_rtnl_message_new_routing_policy_rule(rule->manager->rtnl, &m, RTM_DELRULE, rule->family); if (r < 0) return log_error_errno(r, "Could not allocate RTM_DELRULE message: %m"); @@ -602,22 +572,20 @@ static int routing_policy_rule_remove(const RoutingPolicyRule *rule, Manager *ma if (r < 0) return r; - r = netlink_call_async(manager->rtnl, NULL, m, + r = netlink_call_async(rule->manager->rtnl, NULL, m, routing_policy_rule_remove_handler, - NULL, manager); + NULL, NULL); if (r < 0) return log_error_errno(r, "Could not send rtnetlink message: %m"); - manager->routing_policy_rule_remove_messages++; - + routing_policy_rule_enter_removing(rule); return 0; } static int routing_policy_rule_configure( - const RoutingPolicyRule *rule, + RoutingPolicyRule *rule, Link *link, - link_netlink_message_handler_t callback, - RoutingPolicyRule **ret) { + link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; @@ -640,10 +608,6 @@ static int routing_policy_rule_configure( if (r < 0) return r; - r = routing_policy_rule_add(link->manager, rule, ret); - if (r < 0) - return log_link_error_errno(link, r, "Could not add rule: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, m, callback, link_netlink_destroy_callback, link); if (r < 0) @@ -651,94 +615,70 @@ static int routing_policy_rule_configure( link_ref(link); + routing_policy_rule_enter_configuring(rule); return r; } -static int links_have_routing_policy_rule(const Manager *m, const RoutingPolicyRule *rule, const Link *except) { - Link *link; - int r; - - assert(m); - assert(rule); - - HASHMAP_FOREACH(link, m->links_by_index) { - RoutingPolicyRule *link_rule; - - if (link == except) - continue; - - if (!link->network) - continue; - - HASHMAP_FOREACH(link_rule, link->network->rules_by_section) - if (IN_SET(link_rule->family, AF_INET, AF_INET6)) { - if (routing_policy_rule_equal(link_rule, rule)) - return true; - } else { - /* The case Family=both. */ - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; - - r = routing_policy_rule_dup(link_rule, &tmp); - if (r < 0) - return r; - - tmp->family = AF_INET; - if (routing_policy_rule_equal(tmp, rule)) - return true; - - tmp->family = AF_INET6; - if (routing_policy_rule_equal(tmp, rule)) - return true; - } - } - - return false; -} - int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except) { RoutingPolicyRule *rule; - int k, r = 0; - Set *rules; + Link *link; + int k, r; assert(m); - rules = foreign ? m->rules_foreign : m->rules; - SET_FOREACH(rule, rules) { + /* First, mark all existing rules. */ + SET_FOREACH(rule, m->rules) { /* Do not touch rules managed by kernel. */ if (rule->protocol == RTPROT_KERNEL) continue; - /* The rule will be configured later, or already configured by a link. */ - k = links_have_routing_policy_rule(m, rule, except); - if (k != 0) { - if (k < 0 && r >= 0) - r = k; + /* When 'foreign' is true, do not remove rules we configured. */ + if (foreign && rule->source != NETWORK_CONFIG_SOURCE_FOREIGN) continue; - } - if (!foreign) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; + /* Ignore rules not assigned yet or already removing. */ + if (!routing_policy_rule_exists(rule)) + continue; - /* The rule may be configured without priority. Try to find without priority. */ + routing_policy_rule_mark(rule); + } - k = routing_policy_rule_dup(rule, &tmp); - if (k < 0) { - if (r >= 0) - r = k; - continue; - } + /* Then, unmark all rules requested by active links. */ + HASHMAP_FOREACH(link, m->links_by_index) { + if (link == except) + continue; - tmp->priority_set = false; + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + continue; - k = links_have_routing_policy_rule(m, tmp, except); - if (k != 0) { - if (k < 0 && r >= 0) - r = k; - continue; + HASHMAP_FOREACH(rule, link->network->rules_by_section) { + RoutingPolicyRule *existing; + + if (IN_SET(rule->family, AF_INET, AF_INET6)) { + if (routing_policy_rule_get(m, rule, &existing) >= 0) + routing_policy_rule_unmark(existing); + } else { + /* The case Family=both. */ + rule->family = AF_INET; + if (routing_policy_rule_get(m, rule, &existing) >= 0) + routing_policy_rule_unmark(existing); + + rule->family = AF_INET6; + if (routing_policy_rule_get(m, rule, &existing) >= 0) + routing_policy_rule_unmark(existing); + + rule->family = AF_UNSPEC; } } + } - k = routing_policy_rule_remove(rule, m); + /* Finally, remove all marked rules. */ + r = 0; + SET_FOREACH(rule, m->rules) { + if (!routing_policy_rule_is_marked(rule)) + continue; + + k = routing_policy_rule_remove(rule); if (k < 0 && r >= 0) r = k; } @@ -779,64 +719,73 @@ static int static_routing_policy_rule_configure_handler(sd_netlink *rtnl, sd_net static int link_request_routing_policy_rule( Link *link, RoutingPolicyRule *rule, - bool consume_object, unsigned *message_counter, link_netlink_message_handler_t netlink_handler, Request **ret) { + RoutingPolicyRule *existing; + int r; + assert(link); assert(link->manager); assert(rule); + assert(rule->source != NETWORK_CONFIG_SOURCE_FOREIGN); - log_routing_policy_rule_debug(rule, "Requesting", link, link->manager); - return link_queue_request(link, REQUEST_TYPE_ROUTING_POLICY_RULE, rule, consume_object, - message_counter, netlink_handler, ret); + if (routing_policy_rule_get(link->manager, rule, &existing) < 0) { + _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; + + r = routing_policy_rule_dup(rule, &tmp); + if (r < 0) + return r; + + r = routing_policy_rule_acquire_priority(link->manager, tmp); + if (r < 0) + return r; + + r = routing_policy_rule_add(link->manager, tmp); + if (r < 0) + return r; + + existing = TAKE_PTR(tmp); + } else + existing->source = rule->source; + + log_routing_policy_rule_debug(existing, "Requesting", link, link->manager); + r = link_queue_request(link, REQUEST_TYPE_ROUTING_POLICY_RULE, existing, false, + message_counter, netlink_handler, ret); + if (r <= 0) + return r; + + routing_policy_rule_enter_requesting(existing); + return 1; } static int link_request_static_routing_policy_rule(Link *link, RoutingPolicyRule *rule) { int r; if (IN_SET(rule->family, AF_INET, AF_INET6)) - return link_request_routing_policy_rule(link, rule, false, + return link_request_routing_policy_rule(link, rule, &link->static_routing_policy_rule_messages, static_routing_policy_rule_configure_handler, NULL); - if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; - - r = routing_policy_rule_dup(rule, &tmp); - if (r < 0) - return r; - - tmp->family = AF_INET; - - r = link_request_routing_policy_rule(link, TAKE_PTR(tmp), true, - &link->static_routing_policy_rule_messages, - static_routing_policy_rule_configure_handler, - NULL); - if (r < 0) - return r; + rule->family = AF_INET; + r = link_request_routing_policy_rule(link, rule, + &link->static_routing_policy_rule_messages, + static_routing_policy_rule_configure_handler, + NULL); + if (r < 0) { + rule->family = AF_UNSPEC; + return r; } - if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; - - r = routing_policy_rule_dup(rule, &tmp); - if (r < 0) - return r; - - tmp->family = AF_INET6; - - r = link_request_routing_policy_rule(link, TAKE_PTR(tmp), true, - &link->static_routing_policy_rule_messages, - static_routing_policy_rule_configure_handler, - NULL); - if (r < 0) - return r; - } - - return 0; + rule->family = AF_INET6; + r = link_request_routing_policy_rule(link, rule, + &link->static_routing_policy_rule_messages, + static_routing_policy_rule_configure_handler, + NULL); + rule->family = AF_UNSPEC; + return r; } int link_request_static_routing_policy_rules(Link *link) { @@ -866,7 +815,6 @@ int link_request_static_routing_policy_rules(Link *link) { } int request_process_routing_policy_rule(Request *req) { - RoutingPolicyRule *ret = NULL; /* avoid false maybe-uninitialized warning */ int r; assert(req); @@ -877,10 +825,7 @@ int request_process_routing_policy_rule(Request *req) { if (!link_is_ready_to_configure(req->link, false)) return 0; - if (req->link->manager->routing_policy_rule_remove_messages > 0) - return 0; - - r = routing_policy_rule_configure(req->rule, req->link, req->netlink_handler, &ret); + r = routing_policy_rule_configure(req->rule, req->link, req->netlink_handler); if (r < 0) return r; @@ -1090,28 +1035,35 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man * protocol of the received rule is RTPROT_KERNEL or RTPROT_STATIC. */ tmp->protocol = routing_policy_rule_is_created_by_kernel(tmp) ? RTPROT_KERNEL : RTPROT_STATIC; - (void) routing_policy_rule_get(m, tmp, false, &rule); + (void) routing_policy_rule_get(m, tmp, &rule); switch (type) { case RTM_NEWRULE: if (rule) { - log_routing_policy_rule_debug(tmp, "Received remembered", NULL, m); - r = routing_policy_rule_update_priority(rule, tmp->priority); - if (r < 0) - log_warning_errno(r, "Failed to update priority of remembered routing policy rule, ignoring: %m"); - } else if (!m->manage_foreign_rules) - log_routing_policy_rule_debug(tmp, "Ignoring received foreign", NULL, m); - else { - log_routing_policy_rule_debug(tmp, "Remembering foreign", NULL, m); - r = routing_policy_rule_consume_foreign(m, TAKE_PTR(tmp)); - if (r < 0) + routing_policy_rule_enter_configured(rule); + log_routing_policy_rule_debug(rule, "Received remembered", NULL, m); + } else if (!m->manage_foreign_rules) { + routing_policy_rule_enter_configured(tmp); + log_routing_policy_rule_debug(tmp, "Ignoring received", NULL, m); + } else { + routing_policy_rule_enter_configured(tmp); + log_routing_policy_rule_debug(tmp, "Remembering", NULL, m); + r = routing_policy_rule_add(m, tmp); + if (r < 0) { log_warning_errno(r, "Could not remember foreign rule, ignoring: %m"); + return 0; + } + TAKE_PTR(tmp); } break; case RTM_DELRULE: if (rule) { - log_routing_policy_rule_debug(tmp, "Forgetting", NULL, m); - routing_policy_rule_free(rule); + routing_policy_rule_enter_removed(rule); + if (rule->state == 0) { + log_routing_policy_rule_debug(rule, "Forgetting", NULL, m); + routing_policy_rule_free(rule); + } else + log_routing_policy_rule_debug(rule, "Removed", NULL, m); } else log_routing_policy_rule_debug(tmp, "Kernel removed unknown", NULL, m); break; diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index 557048c3f4..757b17bcb6 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -18,6 +18,8 @@ typedef struct RoutingPolicyRule { Manager *manager; Network *network; NetworkConfigSection *section; + NetworkConfigSource source; + NetworkConfigState state; bool invert_rule; bool priority_set; @@ -70,6 +72,8 @@ static inline int manager_drop_routing_policy_rules(Manager *m, const Link *exce return manager_drop_routing_policy_rules_internal(m, false, except); } +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(RoutingPolicyRule, routing_policy_rule); + CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_tos); CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_table); CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_fwmark_mask); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index d7906e0cb5..7ac34c18f3 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -10,46 +10,84 @@ #include "string-util.h" #include "web-util.h" -static const char* const address_family_table[_ADDRESS_FAMILY_MAX] = { +/* This is used in log messages, and never used in parsing settings. So, upper cases are OK. */ +static const char * const network_config_source_table[_NETWORK_CONFIG_SOURCE_MAX] = { + [NETWORK_CONFIG_SOURCE_FOREIGN] = "foreign", + [NETWORK_CONFIG_SOURCE_STATIC] = "static", + [NETWORK_CONFIG_SOURCE_IPV4LL] = "IPv4LL", + [NETWORK_CONFIG_SOURCE_DHCP4] = "DHCPv4", + [NETWORK_CONFIG_SOURCE_DHCP6] = "DHCPv6", + [NETWORK_CONFIG_SOURCE_DHCP6PD] = "DHCPv6-PD", + [NETWORK_CONFIG_SOURCE_NDISC] = "NDisc", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(network_config_source, NetworkConfigSource); + +int network_config_state_to_string_alloc(NetworkConfigState s, char **ret) { + static const struct { + NetworkConfigState state; + const char *str; + } map[] = { + { .state = NETWORK_CONFIG_STATE_PROBING, .str = "probing", }, + { .state = NETWORK_CONFIG_STATE_REQUESTING, .str = "requesting", }, + { .state = NETWORK_CONFIG_STATE_CONFIGURING, .str = "configuring", }, + { .state = NETWORK_CONFIG_STATE_CONFIGURED, .str = "configured", }, + { .state = NETWORK_CONFIG_STATE_MARKED, .str = "marked", }, + { .state = NETWORK_CONFIG_STATE_REMOVING, .str = "removing", }, + }; + _cleanup_free_ char *buf = NULL; + + assert(ret); + + for (size_t i = 0; i < ELEMENTSOF(map); i++) + if (FLAGS_SET(s, map[i].state) && + !strextend_with_separator(&buf, ",", map[i].str)) + return -ENOMEM; + + *ret = TAKE_PTR(buf); + return 0; +} + +static const char * const address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_NO] = "no", [ADDRESS_FAMILY_YES] = "yes", [ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV6] = "ipv6", }; -static const char* const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char * const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_YES] = "both", [ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV6] = "ipv6", }; -static const char* const nexthop_address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char * const nexthop_address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV6] = "ipv6", }; -static const char* const duplicate_address_detection_address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char * const duplicate_address_detection_address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_NO] = "none", [ADDRESS_FAMILY_YES] = "both", [ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV6] = "ipv6", }; -static const char* const dhcp_deprecated_address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char * const dhcp_deprecated_address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_NO] = "none", [ADDRESS_FAMILY_YES] = "both", [ADDRESS_FAMILY_IPV4] = "v4", [ADDRESS_FAMILY_IPV6] = "v6", }; -static const char* const ip_masquerade_address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char * const ip_masquerade_address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_NO] = "no", [ADDRESS_FAMILY_YES] = "both", [ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV6] = "ipv6", }; -static const char* const dhcp_lease_server_type_table[_SD_DHCP_LEASE_SERVER_TYPE_MAX] = { +static const char * const dhcp_lease_server_type_table[_SD_DHCP_LEASE_SERVER_TYPE_MAX] = { [SD_DHCP_LEASE_DNS] = "DNS servers", [SD_DHCP_LEASE_NTP] = "NTP servers", [SD_DHCP_LEASE_SIP] = "SIP servers", diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index 997bcee4b4..39463cadbd 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -19,11 +19,108 @@ typedef struct NetworkConfigSection { char filename[]; } NetworkConfigSection; +typedef enum NetworkConfigSource { + NETWORK_CONFIG_SOURCE_FOREIGN, /* configured by kernel */ + NETWORK_CONFIG_SOURCE_STATIC, + NETWORK_CONFIG_SOURCE_IPV4LL, + NETWORK_CONFIG_SOURCE_DHCP4, + NETWORK_CONFIG_SOURCE_DHCP6, + NETWORK_CONFIG_SOURCE_DHCP6PD, + NETWORK_CONFIG_SOURCE_NDISC, + _NETWORK_CONFIG_SOURCE_MAX, + _NETWORK_CONFIG_SOURCE_INVALID = -EINVAL, +} NetworkConfigSource; + +typedef enum NetworkConfigState { + NETWORK_CONFIG_STATE_PROBING = 1 << 0, /* address is probing by IPv4ACD */ + NETWORK_CONFIG_STATE_REQUESTING = 1 << 1, /* request is queued */ + NETWORK_CONFIG_STATE_CONFIGURING = 1 << 2, /* e.g. address_configure() is called, but no responce is received yet */ + NETWORK_CONFIG_STATE_CONFIGURED = 1 << 3, /* e.g. address_configure() is called and received a response from kernel. + * Note that address may not be ready yet, so please use address_is_ready() + * to check whether the address can be usable or not. */ + NETWORK_CONFIG_STATE_MARKED = 1 << 4, /* used GC'ing the old config */ + NETWORK_CONFIG_STATE_REMOVING = 1 << 5, /* e.g. address_remove() is called, but no responce is received yet */ +} NetworkConfigState; + CONFIG_PARSER_PROTOTYPE(config_parse_link_local_address_family); CONFIG_PARSER_PROTOTYPE(config_parse_address_family_with_kernel); CONFIG_PARSER_PROTOTYPE(config_parse_ip_masquerade); CONFIG_PARSER_PROTOTYPE(config_parse_mud_url); +const char *network_config_source_to_string(NetworkConfigSource s) _const_; + +int network_config_state_to_string_alloc(NetworkConfigState s, char **ret); + +#define DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(type, name) \ + static inline void name##_update_state( \ + type *t, \ + NetworkConfigState mask, \ + NetworkConfigState value) { \ + \ + assert(t); \ + \ + t->state = (t->state & ~mask) | (value & mask); \ + } \ + static inline bool name##_exists(type *t) { \ + assert(t); \ + \ + if ((t->state & (NETWORK_CONFIG_STATE_CONFIGURING | \ + NETWORK_CONFIG_STATE_CONFIGURED)) == 0) \ + return false; /* Not assigned yet. */ \ + if (FLAGS_SET(t->state, NETWORK_CONFIG_STATE_REMOVING)) \ + return false; /* Already removing. */ \ + return true; \ + } \ + static inline void name##_enter_requesting(type *t) { \ + name##_update_state(t, \ + NETWORK_CONFIG_STATE_REQUESTING, \ + NETWORK_CONFIG_STATE_REQUESTING); \ + } \ + static inline void name##_cancel_requesting(type *t) { \ + name##_update_state(t, \ + NETWORK_CONFIG_STATE_REQUESTING, \ + 0); \ + } \ + static inline bool name##_is_requesting(type *t) { \ + return FLAGS_SET(t->state, NETWORK_CONFIG_STATE_REQUESTING); \ + } \ + static inline void name##_enter_configuring(type *t) { \ + name##_update_state(t, \ + NETWORK_CONFIG_STATE_REQUESTING | \ + NETWORK_CONFIG_STATE_CONFIGURING, \ + NETWORK_CONFIG_STATE_CONFIGURING); \ + } \ + static inline void name##_enter_configured(type *t) { \ + name##_update_state(t, \ + NETWORK_CONFIG_STATE_CONFIGURING | \ + NETWORK_CONFIG_STATE_CONFIGURED, \ + NETWORK_CONFIG_STATE_CONFIGURED); \ + } \ + static inline void name##_mark(type *t) { \ + name##_update_state(t, \ + NETWORK_CONFIG_STATE_MARKED, \ + NETWORK_CONFIG_STATE_MARKED); \ + } \ + static inline void name##_unmark(type *t) { \ + name##_update_state(t, NETWORK_CONFIG_STATE_MARKED, 0); \ + } \ + static inline bool name##_is_marked(type *t) { \ + assert(t); \ + return FLAGS_SET(t->state, NETWORK_CONFIG_STATE_MARKED); \ + } \ + static inline void name##_enter_removing(type *t) { \ + name##_update_state(t, \ + NETWORK_CONFIG_STATE_MARKED | \ + NETWORK_CONFIG_STATE_REMOVING, \ + NETWORK_CONFIG_STATE_REMOVING); \ + } \ + static inline void name##_enter_removed(type *t) { \ + name##_update_state(t, \ + NETWORK_CONFIG_STATE_CONFIGURED | \ + NETWORK_CONFIG_STATE_REMOVING, \ + 0); \ + } + const char *address_family_to_string(AddressFamily b) _const_; AddressFamily address_family_from_string(const char *s) _pure_;