mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 16:37:19 +09:00
Merge pull request #30895 from yuwata/network-drop-object-even-on-fail
network: remove Address object even when we failed to remove relevant address
This commit is contained in:
@@ -1111,18 +1111,35 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
assert(rreq);
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
Link *link = ASSERT_PTR(rreq->link);
|
||||
Address *address = ASSERT_PTR(rreq->userdata);
|
||||
|
||||
if (link->state == LINK_STATE_LINGER)
|
||||
return 0;
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0 && r != -EADDRNOTAVAIL)
|
||||
log_link_message_warning_errno(link, m, r, "Could not drop address");
|
||||
if (r < 0) {
|
||||
log_link_message_full_errno(link, m,
|
||||
(r == -EADDRNOTAVAIL || !address->link) ? LOG_DEBUG : LOG_WARNING,
|
||||
r, "Could not drop address");
|
||||
|
||||
if (address->link) {
|
||||
/* If the address cannot be removed, then assume the address is already removed. */
|
||||
log_address_debug(address, "Forgetting", link);
|
||||
|
||||
Request *req;
|
||||
if (address_get_request(link, address, &req) >= 0)
|
||||
address_enter_removed(req->userdata);
|
||||
|
||||
(void) address_drop(address);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1149,13 +1166,9 @@ int address_remove(Address *address, Link *link) {
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Could not set netlink attributes: %m");
|
||||
|
||||
r = netlink_call_async(link->manager->rtnl, NULL, m,
|
||||
address_remove_handler,
|
||||
link_netlink_destroy_callback, link);
|
||||
r = link_remove_request_add(link, address, address, link->manager->rtnl, m, address_remove_handler);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Could not send rtnetlink message: %m");
|
||||
|
||||
link_ref(link);
|
||||
return log_link_warning_errno(link, r, "Could not queue rtnetlink message: %m");
|
||||
|
||||
address_enter_removing(address);
|
||||
|
||||
|
||||
@@ -424,6 +424,7 @@ static int manager_connect_rtnl(Manager *m, int fd) {
|
||||
static int manager_post_handler(sd_event_source *s, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
|
||||
(void) manager_process_remove_requests(manager);
|
||||
(void) manager_process_requests(manager);
|
||||
(void) manager_clean_all(manager);
|
||||
return 0;
|
||||
@@ -594,6 +595,7 @@ Manager* manager_free(Manager *m) {
|
||||
(void) link_stop_engines(link, true);
|
||||
|
||||
m->request_queue = ordered_set_free(m->request_queue);
|
||||
m->remove_request_queue = ordered_set_free(m->remove_request_queue);
|
||||
|
||||
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
|
||||
m->new_wlan_ifindices = set_free(m->new_wlan_ifindices);
|
||||
|
||||
@@ -102,6 +102,7 @@ struct Manager {
|
||||
|
||||
bool request_queued;
|
||||
OrderedSet *request_queue;
|
||||
OrderedSet *remove_request_queue;
|
||||
|
||||
Hashmap *tuntap_fds_by_name;
|
||||
};
|
||||
|
||||
@@ -399,19 +399,36 @@ int link_request_static_neighbors(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
assert(rreq);
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
return 1;
|
||||
Link *link = ASSERT_PTR(rreq->link);
|
||||
Neighbor *neighbor = ASSERT_PTR(rreq->userdata);
|
||||
|
||||
if (link->state == LINK_STATE_LINGER)
|
||||
return 0;
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0 && r != -ESRCH)
|
||||
if (r < 0) {
|
||||
/* Neighbor may not exist because it already got deleted, ignore that. */
|
||||
log_link_message_warning_errno(link, m, r, "Could not remove neighbor");
|
||||
log_link_message_full_errno(link, m,
|
||||
(r == -ESRCH || !neighbor->link) ? LOG_DEBUG : LOG_WARNING,
|
||||
r, "Could not remove neighbor");
|
||||
|
||||
if (neighbor->link) {
|
||||
/* If the neighbor cannot be removed, then assume the neighbor is already removed. */
|
||||
log_neighbor_debug(neighbor, "Forgetting", link);
|
||||
|
||||
Request *req;
|
||||
if (neighbor_get_request(link, neighbor, &req) >= 0)
|
||||
neighbor_enter_removed(req->userdata);
|
||||
|
||||
neighbor_detach(neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -436,12 +453,9 @@ int neighbor_remove(Neighbor *neighbor, Link *link) {
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
|
||||
|
||||
r = netlink_call_async(link->manager->rtnl, NULL, m, neighbor_remove_handler,
|
||||
link_netlink_destroy_callback, link);
|
||||
r = link_remove_request_add(link, neighbor, neighbor, link->manager->rtnl, m, neighbor_remove_handler);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
|
||||
|
||||
link_ref(link);
|
||||
return log_link_error_errno(link, r, "Could not queue rtnetlink message: %m");
|
||||
|
||||
neighbor_enter_removing(neighbor);
|
||||
return 0;
|
||||
|
||||
@@ -436,19 +436,32 @@ static void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *
|
||||
yes_no(nexthop->blackhole), strna(group), strna(flags));
|
||||
}
|
||||
|
||||
static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(rreq);
|
||||
|
||||
/* link may be NULL. */
|
||||
|
||||
if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
return 1;
|
||||
Manager *manager = ASSERT_PTR(rreq->manager);
|
||||
NextHop *nexthop = ASSERT_PTR(rreq->userdata);
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_link_message_warning_errno(link, m, r, "Could not drop nexthop, ignoring");
|
||||
if (r < 0) {
|
||||
log_message_full_errno(m,
|
||||
(r == -ENOENT || !nexthop->manager) ? LOG_DEBUG : LOG_WARNING,
|
||||
r, "Could not drop nexthop, ignoring");
|
||||
|
||||
if (nexthop->manager) {
|
||||
/* If the nexthop cannot be removed, then assume the nexthop is already removed. */
|
||||
log_nexthop_debug(nexthop, "Forgetting", manager);
|
||||
|
||||
Request *req;
|
||||
if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0)
|
||||
nexthop_enter_removed(req->userdata);
|
||||
|
||||
nexthop_detach(nexthop);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -475,12 +488,9 @@ int nexthop_remove(NextHop *nexthop, Manager *manager) {
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
|
||||
|
||||
r = netlink_call_async(manager->rtnl, NULL, m, nexthop_remove_handler,
|
||||
link ? link_netlink_destroy_callback : NULL, link);
|
||||
r = manager_remove_request_add(manager, nexthop, nexthop, manager->rtnl, m, nexthop_remove_handler);
|
||||
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 */
|
||||
return log_link_error_errno(link, r, "Could not queue rtnetlink message: %m");
|
||||
|
||||
nexthop_enter_removing(nexthop);
|
||||
return 0;
|
||||
|
||||
@@ -224,6 +224,10 @@ int manager_process_requests(Manager *manager) {
|
||||
|
||||
assert(manager);
|
||||
|
||||
/* Process only when no remove request is queued. */
|
||||
if (!ordered_set_isempty(manager->remove_request_queue))
|
||||
return 0;
|
||||
|
||||
manager->request_queued = false;
|
||||
|
||||
ORDERED_SET_FOREACH(req, manager->request_queue) {
|
||||
@@ -339,3 +343,110 @@ static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
|
||||
|
||||
static RemoveRequest* remove_request_free(RemoveRequest *req) {
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
if (req->manager)
|
||||
ordered_set_remove(req->manager->remove_request_queue, req);
|
||||
|
||||
if (req->unref_func)
|
||||
req->unref_func(req->userdata);
|
||||
|
||||
link_unref(req->link);
|
||||
sd_netlink_unref(req->netlink);
|
||||
sd_netlink_message_unref(req->message);
|
||||
|
||||
return mfree(req);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
|
||||
DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
remove_request_hash_ops,
|
||||
void,
|
||||
trivial_hash_func,
|
||||
trivial_compare_func,
|
||||
remove_request_free);
|
||||
|
||||
int remove_request_add(
|
||||
Manager *manager,
|
||||
Link *link,
|
||||
void *userdata,
|
||||
mfree_func_t unref_func,
|
||||
sd_netlink *netlink,
|
||||
sd_netlink_message *message,
|
||||
remove_request_netlink_handler_t netlink_handler) {
|
||||
|
||||
_cleanup_(remove_request_freep) RemoveRequest *req = NULL;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(userdata);
|
||||
assert(netlink);
|
||||
assert(message);
|
||||
|
||||
req = new(RemoveRequest, 1);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
*req = (RemoveRequest) {
|
||||
.link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
|
||||
.userdata = userdata,
|
||||
.netlink = sd_netlink_ref(netlink),
|
||||
.message = sd_netlink_message_ref(message),
|
||||
.netlink_handler = netlink_handler,
|
||||
};
|
||||
|
||||
r = ordered_set_ensure_put(&manager->remove_request_queue, &remove_request_hash_ops, req);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(r > 0);
|
||||
|
||||
req->manager = manager;
|
||||
req->unref_func = unref_func;
|
||||
|
||||
TAKE_PTR(req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_process_remove_requests(Manager *manager) {
|
||||
RemoveRequest *req;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
while ((req = ordered_set_first(manager->remove_request_queue))) {
|
||||
|
||||
/* Do not make the reply callback queue in sd-netlink full. */
|
||||
if (netlink_get_reply_callback_count(req->netlink) >= REPLY_CALLBACK_COUNT_THRESHOLD)
|
||||
return 0;
|
||||
|
||||
r = netlink_call_async(
|
||||
req->netlink, NULL, req->message,
|
||||
req->netlink_handler,
|
||||
remove_request_destroy_callback,
|
||||
req);
|
||||
if (r < 0) {
|
||||
_cleanup_(link_unrefp) Link *link = link_ref(req->link);
|
||||
|
||||
log_link_warning_errno(link, r, "Failed to call netlink message: %m");
|
||||
|
||||
/* First free the request. */
|
||||
remove_request_free(req);
|
||||
|
||||
/* Then, make the link enter the failed state. */
|
||||
if (link)
|
||||
link_enter_failed(link);
|
||||
|
||||
} else {
|
||||
/* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
|
||||
* request may not freed on shutting down. */
|
||||
req->netlink = sd_netlink_unref(req->netlink);
|
||||
ordered_set_remove(manager->remove_request_queue, req);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -139,3 +139,52 @@ int manager_process_requests(Manager *manager);
|
||||
int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req);
|
||||
|
||||
const char* request_type_to_string(RequestType t) _const_;
|
||||
|
||||
typedef struct RemoveRequest RemoveRequest;
|
||||
typedef int (*remove_request_netlink_handler_t)(sd_netlink *nl, sd_netlink_message *m, RemoveRequest *req);
|
||||
|
||||
struct RemoveRequest {
|
||||
Manager *manager;
|
||||
Link *link;
|
||||
void *userdata; /* e.g. Address */
|
||||
mfree_func_t unref_func; /* e.g. address_unref() */
|
||||
sd_netlink *netlink;
|
||||
sd_netlink_message *message;
|
||||
remove_request_netlink_handler_t netlink_handler;
|
||||
};
|
||||
|
||||
int remove_request_add(
|
||||
Manager *manager,
|
||||
Link *link,
|
||||
void *userdata, /* This is unref()ed when the call failed. */
|
||||
mfree_func_t unref_func,
|
||||
sd_netlink *netlink,
|
||||
sd_netlink_message *message,
|
||||
remove_request_netlink_handler_t netlink_handler);
|
||||
|
||||
#define _remove_request_add(manager, link, data, name, nl, m, handler) \
|
||||
({ \
|
||||
typeof(*data) *_data = (data); \
|
||||
int _r; \
|
||||
\
|
||||
_r = remove_request_add(manager, link, _data, \
|
||||
(mfree_func_t) name##_unref, \
|
||||
nl, m, handler); \
|
||||
if (_r >= 0) \
|
||||
name##_ref(_data); \
|
||||
_r; \
|
||||
})
|
||||
|
||||
|
||||
#define link_remove_request_add(link, data, name, nl, m, handler) \
|
||||
({ \
|
||||
Link *_link = (link); \
|
||||
\
|
||||
_remove_request_add(_link->manager, _link, data, name, \
|
||||
nl, m, handler); \
|
||||
})
|
||||
|
||||
#define manager_remove_request_add(manager, data, name, nl, m, handler) \
|
||||
_remove_request_add(manager, NULL, data, name, nl, m, handler)
|
||||
|
||||
int manager_process_remove_requests(Manager *manager);
|
||||
|
||||
Reference in New Issue
Block a user