mirror of
https://github.com/clearlinux/systemd-stable.git
synced 2026-10-04 07:48:36 +00:00
sd-rtnl: handle empty multi-part message from the kernel
We strips out NLMSG_DONE piece from a multi-part message adding into the
receive queue only the messages containing actual data.
If we send a request to the kernel for getting the forwarding database table (just an example),
the response will be a multi-part message like below:
1. FDB entry 1;
2. FDB entry 2;
3. NLMSG_DONE;
We strip out "3. NLMSG_DONE;" part and places into the receive queue a pointer to
"1. FDB entry 1; 2. FDB entry 2".
But if the FDB table is empty, the respose from the kernel will look like below:
1. NLMSG_DONE;
We strip out "1. NLMSG_DONE;" part and since there is no actual data got, it continues
waiting until reaching timeout.
Therefore, a call to "sd_rtnl_call" to send and wait for a response from kernel will exit
with timeout which is interpreted as error in communication.
This patch puts the NLMSG_DONE message on the receive queue if it ends an empty multi-part
message. This situation is detected in sd_rtnl_call() and in the callback code and NULL is
returned to the caller instead.
[tomegun:
- added/reworded commit message
- extend the same support to sd_rtnl_call_async()
- drop debug logging from library, we only do this if something is really wrong, but an
empty multi-part message is perfectly normal
- modernize the code we touch whilst we are at it]
This commit is contained in:
committed by
Tom Gundersen
parent
eaa5251d91
commit
ea342a99fd
@@ -1554,7 +1554,9 @@ int socket_read_message(sd_rtnl *rtnl) {
|
||||
/* finished reading multi-part message */
|
||||
done = true;
|
||||
|
||||
continue;
|
||||
/* if first is not defined, put NLMSG_DONE into the receive queue. */
|
||||
if (first)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check that we support this message type */
|
||||
|
||||
@@ -411,6 +411,7 @@ static const NLTypeSystem rtnl_neigh_type_system = {
|
||||
};
|
||||
|
||||
static const NLType rtnl_types[RTM_MAX + 1] = {
|
||||
[NLMSG_DONE] = { .type = NLA_META, .size = 0 },
|
||||
[NLMSG_ERROR] = { .type = NLA_META, .size = sizeof(struct nlmsgerr) },
|
||||
[RTM_NEWLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
|
||||
[RTM_DELLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
|
||||
|
||||
@@ -421,8 +421,9 @@ static int process_timeout(sd_rtnl *rtnl) {
|
||||
}
|
||||
|
||||
static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
|
||||
struct reply_callback *c;
|
||||
_cleanup_free_ struct reply_callback *c = NULL;
|
||||
uint64_t serial;
|
||||
uint16_t type;
|
||||
int r;
|
||||
|
||||
assert(rtnl);
|
||||
@@ -436,12 +437,17 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
|
||||
if (c->timeout != 0)
|
||||
prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
|
||||
|
||||
r = sd_rtnl_message_get_type(m, &type);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
if (type == NLMSG_DONE)
|
||||
m = NULL;
|
||||
|
||||
r = c->callback(rtnl, m, c->userdata);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "sd-rtnl: callback failed: %m");
|
||||
|
||||
free(c);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -702,7 +708,6 @@ int sd_rtnl_call(sd_rtnl *rtnl,
|
||||
sd_rtnl_message **ret) {
|
||||
usec_t timeout;
|
||||
uint32_t serial;
|
||||
unsigned i = 0;
|
||||
int r;
|
||||
|
||||
assert_return(rtnl, -EINVAL);
|
||||
@@ -717,36 +722,44 @@ int sd_rtnl_call(sd_rtnl *rtnl,
|
||||
|
||||
for (;;) {
|
||||
usec_t left;
|
||||
unsigned i;
|
||||
|
||||
while (i < rtnl->rqueue_size) {
|
||||
sd_rtnl_message *incoming;
|
||||
for (i = 0; i < rtnl->rqueue_size; i++) {
|
||||
uint32_t received_serial;
|
||||
|
||||
incoming = rtnl->rqueue[i];
|
||||
received_serial = rtnl_message_get_serial(incoming);
|
||||
received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
|
||||
|
||||
if (received_serial == serial) {
|
||||
_cleanup_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
|
||||
uint16_t type;
|
||||
|
||||
incoming = rtnl->rqueue[i];
|
||||
|
||||
/* found a match, remove from rqueue and return it */
|
||||
memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
|
||||
sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1));
|
||||
rtnl->rqueue_size--;
|
||||
|
||||
r = sd_rtnl_message_get_errno(incoming);
|
||||
if (r < 0) {
|
||||
sd_rtnl_message_unref(incoming);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_rtnl_message_get_type(incoming, &type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (type == NLMSG_DONE) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
*ret = incoming;
|
||||
} else
|
||||
sd_rtnl_message_unref(incoming);
|
||||
incoming = NULL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Try to read more, right away */
|
||||
i ++;
|
||||
}
|
||||
|
||||
r = socket_read_message(rtnl);
|
||||
|
||||
Reference in New Issue
Block a user