Merge pull request #20728 from yuwata/network-introduce-source-and-state-neighbor-rule-and-nexthops
network: introduce NetworkConfigSource/State
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
+129
-144
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+261
-277
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user