platform: extend netlink processing of messages for different protocols

Later, the same loop should also handle genl.
This commit is contained in:
Thomas Haller
2022-06-17 10:40:37 +02:00
parent ddbcd668ec
commit 2f8d8bba8f

View File

@@ -9241,11 +9241,11 @@ rtnl_event_handler(int fd, GIOCondition io_condition, gpointer user_data)
/*****************************************************************************/ /*****************************************************************************/
/* copied from libnl3's recvmsgs() */
static int static int
event_handler_recvmsgs(NMPlatform *platform, gboolean handle_events) _netlink_recv_handle(NMPlatform *platform, int netlink_protocol, gboolean handle_events)
{ {
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
struct nl_sock *sk;
int n; int n;
int err = 0; int err = 0;
gboolean multipart = 0; gboolean multipart = 0;
@@ -9255,10 +9255,15 @@ event_handler_recvmsgs(NMPlatform *platform, gboolean handle_events)
struct sockaddr_nl nla; struct sockaddr_nl nla;
struct ucred creds; struct ucred creds;
gboolean creds_has; gboolean creds_has;
const char *log_prefix;
nm_assert(netlink_protocol == NETLINK_ROUTE);
sk = priv->sk_rtnl;
log_prefix = "rtnl";
continue_reading: continue_reading:
n = _netlink_recv(platform, priv->sk_rtnl, &nla, &creds, &creds_has); n = _netlink_recv(platform, sk, &nla, &creds, &creds_has);
if (n < 0) { if (n < 0) {
if (n == -NME_NL_MSG_TRUNC && !handle_events) if (n == -NME_NL_MSG_TRUNC && !handle_events)
goto continue_reading; goto continue_reading;
@@ -9267,9 +9272,9 @@ continue_reading:
if (!creds_has || creds.pid) { if (!creds_has || creds.pid) {
if (!creds_has) if (!creds_has)
_LOGT("netlink: recvmsg: received message without credentials"); _LOGT("%s: recvmsg: received message without credentials", log_prefix);
else else
_LOGT("netlink: recvmsg: received non-kernel message (pid %d)", creds.pid); _LOGT("%s: recvmsg: received non-kernel message (pid %d)", log_prefix, creds.pid);
err = 0; err = 0;
goto stop; goto stop;
} }
@@ -9284,13 +9289,13 @@ continue_reading:
const char *extack_msg = NULL; const char *extack_msg = NULL;
msg = nlmsg_alloc_convert(hdr); msg = nlmsg_alloc_convert(hdr);
nlmsg_set_proto(msg, netlink_protocol);
nlmsg_set_proto(msg, NETLINK_ROUTE);
nlmsg_set_src(msg, &nla); nlmsg_set_src(msg, &nla);
nlmsg_set_creds(msg, &creds); nlmsg_set_creds(msg, &creds);
_LOGt("netlink: recvmsg: new message %s", _LOGt("%s: recvmsg: new message %s",
nl_nlmsghdr_to_str(NETLINK_ROUTE, hdr, buf_nlmsghdr, sizeof(buf_nlmsghdr))); log_prefix,
nl_nlmsghdr_to_str(netlink_protocol, hdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)));
if (hdr->nlmsg_flags & NLM_F_MULTI) if (hdr->nlmsg_flags & NLM_F_MULTI)
multipart = TRUE; multipart = TRUE;
@@ -9309,95 +9314,104 @@ continue_reading:
/* FIXME: implement */ /* FIXME: implement */
} }
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; switch (netlink_protocol) {
case NETLINK_ROUTE:
{
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN;
if (hdr->nlmsg_type == NLMSG_DONE) { if (hdr->nlmsg_type == NLMSG_DONE) {
/* messages terminates a multipart message, this is /* messages terminates a multipart message, this is
* usually the end of a message and therefore we slip * usually the end of a message and therefore we slip
* out of the loop by default. the user may overrule * out of the loop by default. the user may overrule
* this action by skipping this packet. */ * this action by skipping this packet. */
multipart = FALSE; multipart = FALSE;
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
} 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. */
} 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 returning
* NL_SKIP or NL_PROCEED (dangerous) */
err = -NME_NL_MSG_OVERFLOW;
abort_parsing = TRUE;
} 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 = -NME_NL_MSG_TRUNC;
abort_parsing = TRUE;
} else if (e->error) {
int errsv = nm_errno_native(e->error);
if (NM_FLAGS_HAS(hdr->nlmsg_flags, NLM_F_ACK_TLVS)
&& hdr->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) {
static const struct nla_policy policy[] = {
[NLMSGERR_ATTR_MSG] = {.type = NLA_STRING},
[NLMSGERR_ATTR_OFFS] = {.type = NLA_U32},
};
struct nlattr *tb[G_N_ELEMENTS(policy)];
struct nlattr *tlvs;
tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len
- NLMSG_HDRLEN);
if (nla_parse_arr(tb,
tlvs,
hdr->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len,
policy)
>= 0) {
if (tb[NLMSGERR_ATTR_MSG])
extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]);
}
}
/* Error message reported back from kernel. */
_LOGD("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d",
nm_strerror_native(errsv),
errsv,
NM_PRINT_FMT_QUOTED(extack_msg, " \"", extack_msg, "\"", ""),
nlmsg_hdr(msg)->nlmsg_seq);
seq_result = -NM_ERRNO_NATIVE(errsv);
} else
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
} else } else if (hdr->nlmsg_type == NLMSG_NOOP) {
process_valid_msg = TRUE; /* 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. */
} 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 returning
* NL_SKIP or NL_PROCEED (dangerous) */
err = -NME_NL_MSG_OVERFLOW;
abort_parsing = TRUE;
} else if (hdr->nlmsg_type == NLMSG_ERROR) {
/* Message carries a nlmsgerr */
struct nlmsgerr *e = nlmsg_data(hdr);
seq_number = nlmsg_hdr(msg)->nlmsg_seq; 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 = -NME_NL_MSG_TRUNC;
abort_parsing = TRUE;
} else if (e->error) {
int errsv = nm_errno_native(e->error);
/* check whether the seq number is different from before, and if (NM_FLAGS_HAS(hdr->nlmsg_flags, NLM_F_ACK_TLVS)
* whether the previous number (@nlh_seq_last_seen) is a pending && hdr->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) {
* refresh-all request. In that case, the pending request is thereby static const struct nla_policy policy[] = {
* completed. [NLMSGERR_ATTR_MSG] = {.type = NLA_STRING},
* [NLMSGERR_ATTR_OFFS] = {.type = NLA_U32},
* We must do that before processing the message with event_valid_msg(), };
* because we must track the completion of the pending request before that. */ struct nlattr *tb[G_N_ELEMENTS(policy)];
event_seq_check_refresh_all(platform, seq_number); struct nlattr *tlvs;
if (process_valid_msg) { tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len
/* Valid message (not checking for MULTIPART bit to - NLMSG_HDRLEN);
* get along with broken kernels. NL_SKIP has no if (nla_parse_arr(tb,
* effect on this. */ tlvs,
hdr->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len,
policy)
>= 0) {
if (tb[NLMSGERR_ATTR_MSG])
extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]);
}
}
event_valid_msg(platform, msg, handle_events); /* Error message reported back from kernel. */
_LOGD(
"netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d",
nm_strerror_native(errsv),
errsv,
NM_PRINT_FMT_QUOTED(extack_msg, " \"", extack_msg, "\"", ""),
nlmsg_hdr(msg)->nlmsg_seq);
seq_result = -NM_ERRNO_NATIVE(errsv);
} else
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
} else
process_valid_msg = TRUE;
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; seq_number = nlmsg_hdr(msg)->nlmsg_seq;
/* check whether the seq number is different from before, and
* whether the previous number (@nlh_seq_last_seen) is a pending
* refresh-all request. In that case, the pending request is thereby
* completed.
*
* We must do that before processing the message with event_valid_msg(),
* because we must track the completion of the pending request before that. */
event_seq_check_refresh_all(platform, seq_number);
if (process_valid_msg) {
/* Valid message (not checking for MULTIPART bit to
* get along with broken kernels. NL_SKIP has no
* effect on this. */
event_valid_msg(platform, msg, handle_events);
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
}
event_seq_check(platform, seq_number, seq_result, extack_msg);
break;
}
default:
nm_assert_not_reached();
} }
event_seq_check(platform, seq_number, seq_result, extack_msg);
if (abort_parsing) if (abort_parsing)
goto stop; goto stop;
@@ -9450,7 +9464,7 @@ event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks)
for (;;) { for (;;) {
int nle; int nle;
nle = event_handler_recvmsgs(platform, TRUE); nle = _netlink_recv_handle(platform, NETLINK_ROUTE, TRUE);
if (nle < 0) { if (nle < 0) {
switch (nle) { switch (nle) {
@@ -9475,7 +9489,7 @@ event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks)
} }
_reason; _reason;
})); }));
event_handler_recvmsgs(platform, FALSE); _netlink_recv_handle(platform, NETLINK_ROUTE, FALSE);
delayed_action_wait_for_nl_response_complete_all( delayed_action_wait_for_nl_response_complete_all(
platform, platform,
WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC);