platform: reimplement nl_recvmsg() in platform

Calling nl_recvmsgs_default() leads dirctly to recvmsgs() from
"nl.c". This functions reads messages (recvmsg) in a loop and
invokes the callbacks.

Later we want to merge nlh and nlh_event, meaning that we must
anticipate parsing unrelated messages while waiting for an ACK.
While that would be possible by registering different callbacks
and letting them interact, it is actually more complicated. Just
assume full control over the message parsing.

Basically, copy recvmsgs() to event_handler_recvmsg(). For now just
copy the function and do little adjustment (to show the similarity
to the original). Cleanup follows.
This commit is contained in:
Thomas Haller
2015-12-11 19:25:00 +01:00
parent be0f8e2854
commit 2f6b5d4125

View File

@@ -3307,14 +3307,13 @@ event_seq_check (struct nl_msg *msg, gpointer user_data)
return NL_OK;
}
static int
static void
event_err (struct sockaddr_nl *nla, struct nlmsgerr *nlerr, gpointer platform)
{
_LOGt ("event_err(): error from kernel: %s (%d) for request %d",
strerror (nlerr ? -nlerr->error : 0),
nlerr ? -nlerr->error : 0,
NM_LINUX_PLATFORM_GET_PRIVATE (platform)->nlh_seq_last);
return NL_OK;
}
/* This function does all the magic to avoid race conditions caused
@@ -5397,6 +5396,160 @@ event_handler (GIOChannel *channel,
return TRUE;
}
/*****************************************************************************/
#define NL_CB_CALL(cmd) \
do { \
err = (cmd); \
switch (err) { \
case NL_OK: \
err = 0; \
break; \
case NL_SKIP: \
goto skip; \
case NL_STOP: \
goto stop; \
default: \
goto out; \
} \
} while (0)
/* copied from libnl3's recvmsgs() */
static int
event_handler_recvmsgs (NMPlatform *platform)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
struct nl_sock *sk = priv->nlh_event;
int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0;
unsigned char *buf = NULL;
struct nlmsghdr *hdr;
/*
nla is passed on to not only to nl_recv() but may also be passed
to a function pointer provided by the caller which may or may not
initialize the variable. Thomas Graf.
*/
struct sockaddr_nl nla = {0};
struct nl_msg *msg = NULL;
struct ucred *creds = NULL;
continue_reading:
n = nl_recv(sk, &nla, &buf, &creds);
if (n <= 0)
return n;
hdr = (struct nlmsghdr *) buf;
while (nlmsg_ok(hdr, n)) {
nlmsg_free(msg);
msg = nlmsg_convert(hdr);
if (!msg) {
err = -NLE_NOMEM;
goto out;
}
nlmsg_set_proto(msg, NETLINK_ROUTE);
nlmsg_set_src(msg, &nla);
if (creds)
nlmsg_set_creds(msg, creds);
nrecv++;
/* NL_CB_MSG_IN */
NL_CB_CALL (verify_source (msg, platform));
NL_CB_CALL (event_seq_check (msg, platform));
if (hdr->nlmsg_flags & NLM_F_MULTI)
multipart = 1;
if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) {
/*
* We have to continue reading to clear
* all messages until a NLMSG_DONE is
* received and report the inconsistency.
*/
interrupted = 1;
}
/* Other side wishes to see an ack for this message */
if (hdr->nlmsg_flags & NLM_F_ACK) {
/* FIXME: implement */
}
if (hdr->nlmsg_type == NLMSG_DONE) {
/* messages terminates a multipart message, this is
* usually the end of a message and therefore we slip
* out of the loop by default. the user may overrule
* this action by skipping this packet. */
multipart = 0;
} else if (hdr->nlmsg_type == NLMSG_NOOP) {
/* Message to be ignored, the default action is to
* skip this message if no callback is specified. The
* user may overrule this action by returning
* NL_PROCEED. */
goto skip;
} else if (hdr->nlmsg_type == NLMSG_OVERRUN) {
/* Data got lost, report back to user. The default action is to
* quit parsing. The user may overrule this action by retuning
* NL_SKIP or NL_PROCEED (dangerous) */
err = -NLE_MSG_OVERFLOW;
goto out;
} else if (hdr->nlmsg_type == NLMSG_ERROR) {
/* Message carries a nlmsgerr */
struct nlmsgerr *e = nlmsg_data(hdr);
if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) {
/* Truncated error message, the default action
* is to stop parsing. The user may overrule
* this action by returning NL_SKIP or
* NL_PROCEED (dangerous) */
err = -NLE_MSG_TRUNC;
goto out;
} else if (e->error) {
/* Error message reported back from kernel. */
event_err (&nla, e, platform);
}
} else {
/* Valid message (not checking for MULTIPART bit to
* get along with broken kernels. NL_SKIP has no
* effect on this. */
event_notification (msg, platform);
}
skip:
err = 0;
hdr = nlmsg_next(hdr, &n);
}
nlmsg_free(msg);
free(buf);
free(creds);
buf = NULL;
msg = NULL;
creds = NULL;
if (multipart) {
/* Multipart message not yet complete, continue reading */
goto continue_reading;
}
stop:
err = 0;
out:
nlmsg_free(msg);
free(buf);
free(creds);
if (interrupted)
err = -NLE_DUMP_INTR;
if (!err)
err = nrecv;
return err;
}
/*****************************************************************************/
static gboolean
event_handler_read_netlink_one (NMPlatform *platform)
{
@@ -5404,7 +5557,7 @@ event_handler_read_netlink_one (NMPlatform *platform)
int nle;
errno = 0;
nle = nl_recvmsgs_default (priv->nlh_event);
nle = event_handler_recvmsgs (platform);
/* Work around a libnl bug fixed in 3.2.22 (375a6294) */
if (nle == 0 && errno == EAGAIN) {
@@ -5659,15 +5812,6 @@ constructed (GObject *_object)
priv->nlh_event = nl_socket_alloc ();
g_assert (priv->nlh_event);
nle = nl_socket_modify_cb (priv->nlh_event, NL_CB_MSG_IN, NL_CB_CUSTOM, (nl_recvmsg_msg_cb_t) verify_source, platform);
g_assert (!nle);
nle = nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_CUSTOM, event_notification, platform);
g_assert (!nle);
nle = nl_socket_modify_cb (priv->nlh_event, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, event_seq_check, platform);
g_assert (!nle);
nle = nl_socket_modify_err_cb (priv->nlh_event, NL_CB_CUSTOM, event_err, platform);
g_assert (!nle);
nle = nl_connect (priv->nlh_event, NETLINK_ROUTE);
g_assert (!nle);
nle = nl_socket_set_passcred (priv->nlh_event, 1);