summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2022-06-24 11:09:09 +0200
committerThomas Haller <thaller@redhat.com>2022-06-24 11:09:09 +0200
commiteb90ebc5307e6feb81703d81858164b899637215 (patch)
treea54f12a525d29d1209d7a5fd33e22d711405f4cd
parent6b0f67b7368d361cee2cfbfe3866264714556a5b (diff)
parentf8e061d7d675bdf0c973aa3e78a77eab3b39a19f (diff)
downloadNetworkManager-eb90ebc5307e6feb81703d81858164b899637215.tar.gz
platform: merge branch 'th/platform-genl-2'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1270
-rw-r--r--src/libnm-platform/nm-linux-platform.c265
-rw-r--r--src/libnm-platform/nm-netlink.c370
-rw-r--r--src/libnm-platform/nm-netlink.h93
-rw-r--r--src/libnm-platform/tests/test-nm-platform.c2
4 files changed, 389 insertions, 341 deletions
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index b997ca2e0a..a3d3b50c6e 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -2924,10 +2924,10 @@ _nmp_link_address_set(NMPLinkAddress *dst, const struct nlattr *nla)
/* Copied and heavily modified from libnl3's link_msg_parser(). */
static NMPObject *
-_new_from_nl_link(NMPlatform *platform,
- const NMPCache *cache,
- struct nlmsghdr *nlh,
- gboolean id_only)
+_new_from_nl_link(NMPlatform *platform,
+ const NMPCache *cache,
+ const struct nlmsghdr *nlh,
+ gboolean id_only)
{
static const struct nla_policy policy[] = {
[IFLA_IFNAME] = {.type = NLA_STRING, .maxlen = IFNAMSIZ},
@@ -3275,7 +3275,7 @@ _new_from_nl_link(NMPlatform *platform,
/* Copied and heavily modified from libnl3's addr_msg_parser(). */
static NMPObject *
-_new_from_nl_addr(struct nlmsghdr *nlh, gboolean id_only)
+_new_from_nl_addr(const struct nlmsghdr *nlh, gboolean id_only)
{
static const struct nla_policy policy[] = {
[IFA_LABEL] = {.type = NLA_STRING, .maxlen = IFNAMSIZ},
@@ -3390,7 +3390,7 @@ _new_from_nl_addr(struct nlmsghdr *nlh, gboolean id_only)
/* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */
static NMPObject *
-_new_from_nl_route(struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse_nlmsg_iter)
+_new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse_nlmsg_iter)
{
static const struct nla_policy policy[] = {
[RTA_TABLE] = {.type = NLA_U32},
@@ -3698,7 +3698,7 @@ rta_multipath_done:
}
static NMPObject *
-_new_from_nl_routing_rule(struct nlmsghdr *nlh, gboolean id_only)
+_new_from_nl_routing_rule(const struct nlmsghdr *nlh, gboolean id_only)
{
static const struct nla_policy policy[] = {
[FRA_UNSPEC] = {},
@@ -3974,7 +3974,7 @@ psched_tick_to_time(NMPlatform *platform, guint32 tick)
}
static NMPObject *
-_new_from_nl_qdisc(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only)
+_new_from_nl_qdisc(NMPlatform *platform, const struct nlmsghdr *nlh, gboolean id_only)
{
static const struct nla_policy policy[] = {
[TCA_KIND] = {.type = NLA_STRING},
@@ -4087,7 +4087,7 @@ _new_from_nl_qdisc(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only)
}
static NMPObject *
-_new_from_nl_tfilter(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only)
+_new_from_nl_tfilter(NMPlatform *platform, const struct nlmsghdr *nlh, gboolean id_only)
{
static const struct nla_policy policy[] = {
[TCA_KIND] = {.type = NLA_STRING},
@@ -4132,18 +4132,17 @@ _new_from_nl_tfilter(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_onl
* Returns: %NULL or a newly created NMPObject instance.
**/
static NMPObject *
-nmp_object_new_from_nl(NMPlatform *platform,
- const NMPCache *cache,
- struct nl_msg *msg,
- gboolean id_only,
- ParseNlmsgIter *parse_nlmsg_iter)
+nmp_object_new_from_nl(NMPlatform *platform,
+ const NMPCache *cache,
+ const struct nl_msg_lite *msg,
+ gboolean id_only,
+ ParseNlmsgIter *parse_nlmsg_iter)
{
- struct nlmsghdr *msghdr;
+ const struct nlmsghdr *msghdr;
- if (nlmsg_get_proto(msg) != NETLINK_ROUTE)
- return NULL;
+ nm_assert(msg->nm_protocol == NETLINK_ROUTE);
- msghdr = nlmsg_hdr(msg);
+ msghdr = msg->nm_nlh;
switch (msghdr->nlmsg_type) {
case RTM_NEWLINK:
@@ -7016,13 +7015,13 @@ event_seq_check(NMPlatform *platform,
}
static void
-event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events)
+_rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg, gboolean handle_events)
{
char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE];
NMLinuxPlatformPrivate *priv;
nm_auto_nmpobj NMPObject *obj = NULL;
NMPCacheOpsType cache_op;
- struct nlmsghdr *msghdr;
+ const struct nlmsghdr *msghdr;
char buf_nlmsghdr[400];
gboolean is_del = FALSE;
gboolean is_dump = FALSE;
@@ -7032,7 +7031,7 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events
if (!handle_events)
return;
- msghdr = nlmsg_hdr(msg);
+ msghdr = msg->nm_nlh;
if (NM_IN_SET(msghdr->nlmsg_type,
RTM_DELLINK,
@@ -7053,7 +7052,7 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events
obj = nmp_object_new_from_nl(platform, cache, msg, is_del, &parse_nlmsg_iter);
if (!obj) {
_LOGT("event-notification: %s: ignore",
- nl_nlmsghdr_to_str(NETLINK_ROUTE, msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)));
+ nl_nlmsghdr_to_str(NETLINK_ROUTE, 0, msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)));
return;
}
@@ -7071,7 +7070,7 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events
}
_LOGT("event-notification: %s%s: %s",
- nl_nlmsghdr_to_str(NETLINK_ROUTE, msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)),
+ nl_nlmsghdr_to_str(NETLINK_ROUTE, 0, msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)),
is_dump ? ", in-dump" : "",
nmp_object_to_string(obj,
is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC,
@@ -7123,7 +7122,7 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events
if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET
&& data->response.out_route_get) {
nm_assert(!*data->response.out_route_get);
- if (data->seq_number == nlmsg_hdr(msg)->nlmsg_seq) {
+ if (data->seq_number == msg->nm_nlh->nlmsg_seq) {
*data->response.out_route_get = nmp_object_clone(obj, FALSE);
data->response.out_route_get = NULL;
break;
@@ -9212,7 +9211,9 @@ _netlink_recv(NMPlatform *platform,
nla,
&buf,
out_creds,
- out_creds_has);
+ out_creds_has,
+ NULL,
+ NULL);
nm_assert((n <= 0 && !buf)
|| (n > 0 && n <= priv->netlink_recv_buf.len && buf == priv->netlink_recv_buf.buf));
@@ -9247,11 +9248,10 @@ _netlink_recv_handle(NMPlatform *platform, int netlink_protocol, gboolean handle
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
struct nl_sock *sk;
int n;
- int err = 0;
+ int retval = 0;
gboolean multipart = 0;
gboolean interrupted = FALSE;
struct nlmsghdr *hdr;
- WaitForNlResponseResult seq_result;
struct sockaddr_nl nla;
struct ucred creds;
gboolean creds_has;
@@ -9275,32 +9275,38 @@ continue_reading:
_LOGT("%s: recvmsg: received message without credentials", log_prefix);
else
_LOGT("%s: recvmsg: received non-kernel message (pid %d)", log_prefix, creds.pid);
- err = 0;
goto stop;
}
hdr = (struct nlmsghdr *) priv->netlink_recv_buf.buf;
while (nlmsg_ok(hdr, n)) {
- nm_auto_nlmsg struct nl_msg *msg = NULL;
- gboolean abort_parsing = FALSE;
- gboolean process_valid_msg = FALSE;
- guint32 seq_number;
- char buf_nlmsghdr[400];
- const char *extack_msg = NULL;
-
- msg = nlmsg_alloc_convert(hdr);
- nlmsg_set_proto(msg, netlink_protocol);
- nlmsg_set_src(msg, &nla);
- nlmsg_set_creds(msg, &creds);
+ WaitForNlResponseResult seq_result;
+ gboolean process_valid_msg = FALSE;
+ char buf_nlmsghdr[400];
+ const char *extack_msg = NULL;
+ const struct nl_msg_lite msg = {
+ .nm_protocol = netlink_protocol,
+ .nm_src = &nla,
+ .nm_creds = &creds,
+ .nm_size = NLMSG_ALIGN(hdr->nlmsg_len),
+ .nm_nlh = hdr,
+ };
+ const guint32 seq_number = msg.nm_nlh->nlmsg_seq;
+
+ nm_assert((((uintptr_t) (const void *) msg.nm_nlh) % NLMSG_ALIGNTO) == 0);
_LOGt("%s: recvmsg: new message %s",
log_prefix,
- nl_nlmsghdr_to_str(netlink_protocol, hdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)));
+ nl_nlmsghdr_to_str(netlink_protocol,
+ 0,
+ msg.nm_nlh,
+ buf_nlmsghdr,
+ sizeof(buf_nlmsghdr)));
- if (hdr->nlmsg_flags & NLM_F_MULTI)
+ if (msg.nm_nlh->nlmsg_flags & NLM_F_MULTI)
multipart = TRUE;
- if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) {
+ if (msg.nm_nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
/*
* We have to continue reading to clear
* all messages until a NLMSG_DONE is
@@ -9310,89 +9316,86 @@ continue_reading:
}
/* Other side wishes to see an ack for this message */
- if (hdr->nlmsg_flags & NLM_F_ACK) {
+ if (msg.nm_nlh->nlmsg_flags & NLM_F_ACK) {
/* FIXME: implement */
}
- switch (netlink_protocol) {
- case NETLINK_ROUTE:
- {
- seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN;
-
- 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 = 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]);
- }
+ seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN;
+
+ if (msg.nm_nlh->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 = FALSE;
+ seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
+ } else if (msg.nm_nlh->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 (msg.nm_nlh->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) */
+ retval = -NME_NL_MSG_OVERFLOW;
+ } else if (msg.nm_nlh->nlmsg_type == NLMSG_ERROR) {
+ /* Message carries a nlmsgerr */
+ struct nlmsgerr *e = nlmsg_data(msg.nm_nlh);
+
+ if (msg.nm_nlh->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) */
+ retval = -NME_NL_MSG_TRUNC;
+ } else if (e->error) {
+ int errsv = nm_errno_native(e->error);
+
+ if (NM_FLAGS_HAS(msg.nm_nlh->nlmsg_flags, NLM_F_ACK_TLVS)
+ && msg.nm_nlh->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,
+ msg.nm_nlh->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;
+ /* 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, "\"", ""),
+ msg.nm_nlh->nlmsg_seq);
+ seq_result = -NM_ERRNO_NATIVE(errsv);
} else
- process_valid_msg = TRUE;
-
- seq_number = nlmsg_hdr(msg)->nlmsg_seq;
+ seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
+ } else
+ process_valid_msg = TRUE;
+ switch (netlink_protocol) {
+ default:
+ nm_assert_not_reached();
+ /* fall-through */
+ case NETLINK_ROUTE:
/* 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(),
+ * We must do that before processing the message with _rtnl_handle_msg(),
* because we must track the completion of the pending request before that. */
event_seq_check_refresh_all(platform, seq_number);
@@ -9401,7 +9404,7 @@ continue_reading:
* get along with broken kernels. NL_SKIP has no
* effect on this. */
- event_valid_msg(platform, msg, handle_events);
+ _rtnl_handle_msg(platform, &msg, handle_events);
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
}
@@ -9409,14 +9412,10 @@ continue_reading:
event_seq_check(platform, seq_number, seq_result, extack_msg);
break;
}
- default:
- nm_assert_not_reached();
- }
- if (abort_parsing)
+ if (retval != 0)
goto stop;
- err = 0;
hdr = nlmsg_next(hdr, &n);
}
@@ -9424,6 +9423,7 @@ continue_reading:
/* Multipart message not yet complete, continue reading */
goto continue_reading;
}
+
stop:
if (!handle_events) {
/* when we don't handle events, we want to drain all messages from the socket
@@ -9434,7 +9434,7 @@ stop:
if (interrupted)
return -NME_NL_DUMP_INTR;
- return err;
+ return retval;
}
/*****************************************************************************/
@@ -9747,7 +9747,7 @@ constructed(GObject *_object)
/*************************************************************************/
- nle = nl_socket_new(&priv->sk_genl_sync, NETLINK_GENERIC);
+ nle = nl_socket_new(&priv->sk_genl_sync, NETLINK_GENERIC, NL_SOCKET_FLAGS_NONE, 0, 0);
g_assert(!nle);
_LOGD("genl: generic netlink socket for sync operations created: port=%u, fd=%d",
@@ -9756,24 +9756,15 @@ constructed(GObject *_object)
/*************************************************************************/
- nle = nl_socket_new(&priv->sk_rtnl, NETLINK_ROUTE);
+ /* disable MSG_PEEK, we will handle lost messages ourselves. */
+ nle = nl_socket_new(&priv->sk_rtnl,
+ NETLINK_ROUTE,
+ NL_SOCKET_FLAGS_NONBLOCK | NL_SOCKET_FLAGS_PASSCRED
+ | NL_SOCKET_FLAGS_DISABLE_MSG_PEEK,
+ 8 * 1024 * 1024,
+ 0);
g_assert(!nle);
- nle = nl_socket_set_passcred(priv->sk_rtnl, 1);
- g_assert(!nle);
-
- nle = nl_socket_set_nonblocking(priv->sk_rtnl);
- g_assert(!nle);
-
- nle = nl_socket_set_buffer_size(priv->sk_rtnl, 8 * 1024 * 1024, 0);
- g_assert(!nle);
-
- /* explicitly set the msg buffer size and disable MSG_PEEK.
- * We use our own receive buffer priv->netlink_recv_buf.
- * If we encounter NME_NL_MSG_TRUNC, we will increase the buffer
- * and resync (as we would have lost the message without NL_MSG_PEEK). */
- nl_socket_disable_msg_peek(priv->sk_rtnl);
-
nle = nl_socket_add_memberships(priv->sk_rtnl,
RTNLGRP_IPV4_IFADDR,
RTNLGRP_IPV4_ROUTE,
diff --git a/src/libnm-platform/nm-netlink.c b/src/libnm-platform/nm-netlink.c
index 05790cb054..267a5bc51e 100644
--- a/src/libnm-platform/nm-netlink.c
+++ b/src/libnm-platform/nm-netlink.c
@@ -28,11 +28,6 @@
} \
G_STMT_END
-#define NL_SOCK_PASSCRED (1 << 1)
-#define NL_MSG_PEEK (1 << 3)
-#define NL_MSG_PEEK_EXPLICIT (1 << 4)
-#define NL_NO_AUTO_ACK (1 << 5)
-
#ifndef NETLINK_EXT_ACK
#define NETLINK_EXT_ACK 11
#endif
@@ -50,12 +45,13 @@ struct nl_msg {
struct nl_sock {
struct sockaddr_nl s_local;
struct sockaddr_nl s_peer;
+ size_t s_bufsize;
int s_fd;
int s_proto;
unsigned int s_seq_next;
unsigned int s_seq_expect;
- int s_flags;
- size_t s_bufsize;
+ bool s_msg_peek : 1;
+ bool s_auto_ack : 1;
};
/*****************************************************************************/
@@ -81,10 +77,57 @@ NM_UTILS_FLAGS2STR_DEFINE(nl_nlmsg_flags2str,
NM_UTILS_FLAGS2STR(NLM_F_CREATE, "CREATE"),
NM_UTILS_FLAGS2STR(NLM_F_APPEND, "APPEND"), );
+static NM_UTILS_LOOKUP_STR_DEFINE(_rtnl_type_to_str,
+ guint16,
+ NM_UTILS_LOOKUP_DEFAULT(NULL),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_GETLINK, "RTM_GETLINK"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWLINK, "RTM_NEWLINK"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_DELLINK, "RTM_DELLINK"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_SETLINK, "RTM_SETLINK"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_GETADDR, "RTM_GETADDR"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWADDR, "RTM_NEWADDR"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_DELADDR, "RTM_DELADDR"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_GETROUTE, "RTM_GETROUTE"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWROUTE, "RTM_NEWROUTE"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_DELROUTE, "RTM_DELROUTE"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_GETRULE, "RTM_GETRULE"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWRULE, "RTM_NEWRULE"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_DELRULE, "RTM_DELRULE"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_GETQDISC, "RTM_GETQDISC"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWQDISC, "RTM_NEWQDISC"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_DELQDISC, "RTM_DELQDISC"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_GETTFILTER, "RTM_GETTFILTER"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWTFILTER, "RTM_NEWTFILTER"),
+ NM_UTILS_LOOKUP_STR_ITEM(RTM_DELTFILTER, "RTM_DELTFILTER"),
+ NM_UTILS_LOOKUP_STR_ITEM(NLMSG_NOOP, "NLMSG_NOOP"),
+ NM_UTILS_LOOKUP_STR_ITEM(NLMSG_ERROR, "NLMSG_ERROR"),
+ NM_UTILS_LOOKUP_STR_ITEM(NLMSG_DONE, "NLMSG_DONE"),
+ NM_UTILS_LOOKUP_STR_ITEM(NLMSG_OVERRUN, "NLMSG_OVERRUN"), );
+
+static NM_UTILS_LOOKUP_STR_DEFINE(
+ _genl_ctrl_cmd_to_str,
+ guint8,
+ NM_UTILS_LOOKUP_DEFAULT(NULL),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_UNSPEC, "CTRL_CMD_UNSPEC"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_NEWFAMILY, "CTRL_CMD_NEWFAMILY"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_DELFAMILY, "CTRL_CMD_DELFAMILY"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_GETFAMILY, "CTRL_CMD_GETFAMILY"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_NEWOPS, "CTRL_CMD_NEWOPS"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_DELOPS, "CTRL_CMD_DELOPS"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_GETOPS, "CTRL_CMD_GETOPS"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_NEWMCAST_GRP, "CTRL_CMD_NEWMCAST_GRP"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_DELMCAST_GRP, "CTRL_CMD_DELMCAST_GRP"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_GETMCAST_GRP, "CTRL_CMD_GETMCAST_GRP"),
+ NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_GETPOLICY, "CTRL_CMD_GETPOLICY"), );
+
/*****************************************************************************/
const char *
-nl_nlmsghdr_to_str(int netlink_protocol, const struct nlmsghdr *hdr, char *buf, gsize len)
+nl_nlmsghdr_to_str(int netlink_protocol,
+ guint32 pktinfo_group,
+ const struct nlmsghdr *hdr,
+ char *buf,
+ gsize len)
{
const char *b;
const char *s = NULL;
@@ -98,87 +141,44 @@ nl_nlmsghdr_to_str(int netlink_protocol, const struct nlmsghdr *hdr, char *buf,
switch (netlink_protocol) {
case NETLINK_ROUTE:
- switch (hdr->nlmsg_type) {
- case RTM_GETLINK:
- s = "RTM_GETLINK";
- break;
- case RTM_NEWLINK:
- s = "RTM_NEWLINK";
- break;
- case RTM_DELLINK:
- s = "RTM_DELLINK";
- break;
- case RTM_SETLINK:
- s = "RTM_SETLINK";
- break;
- case RTM_GETADDR:
- s = "RTM_GETADDR";
- break;
- case RTM_NEWADDR:
- s = "RTM_NEWADDR";
- break;
- case RTM_DELADDR:
- s = "RTM_DELADDR";
- break;
- case RTM_GETROUTE:
- s = "RTM_GETROUTE";
- break;
- case RTM_NEWROUTE:
- s = "RTM_NEWROUTE";
- break;
- case RTM_DELROUTE:
- s = "RTM_DELROUTE";
- break;
- case RTM_GETRULE:
- s = "RTM_GETRULE";
- break;
- case RTM_NEWRULE:
- s = "RTM_NEWRULE";
- break;
- case RTM_DELRULE:
- s = "RTM_DELRULE";
- break;
- case RTM_GETQDISC:
- s = "RTM_GETQDISC";
- break;
- case RTM_NEWQDISC:
- s = "RTM_NEWQDISC";
- break;
- case RTM_DELQDISC:
- s = "RTM_DELQDISC";
- break;
- case RTM_GETTFILTER:
- s = "RTM_GETTFILTER";
- break;
- case RTM_NEWTFILTER:
- s = "RTM_NEWTFILTER";
- break;
- case RTM_DELTFILTER:
- s = "RTM_DELTFILTER";
- break;
- case NLMSG_NOOP:
- s = "NLMSG_NOOP";
- break;
- case NLMSG_ERROR:
- s = "NLMSG_ERROR";
- break;
- case NLMSG_DONE:
- s = "NLMSG_DONE";
- break;
- case NLMSG_OVERRUN:
- s = "NLMSG_OVERRUN";
- break;
- }
+ s = _rtnl_type_to_str(hdr->nlmsg_type);
+ if (s)
+ nm_strbuf_append_str(&buf, &len, s);
+ else
+ nm_strbuf_append(&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type);
break;
+ default:
+ nm_assert_not_reached();
+ /* fall-through */
case NETLINK_GENERIC:
+ if (pktinfo_group == 0)
+ nm_strbuf_append(&buf, &len, "group:unicast");
+ else
+ nm_strbuf_append(&buf, &len, "group:multicast(%u)", (unsigned) pktinfo_group);
+
+ s = NULL;
+ if (hdr->nlmsg_type == GENL_ID_CTRL)
+ s = "GENL_ID_CTRL";
+ if (s)
+ nm_strbuf_append(&buf, &len, ", msg-type:%s", s);
+ else
+ nm_strbuf_append(&buf, &len, ", msg-type:(%u)", (unsigned) hdr->nlmsg_type);
+
+ if (genlmsg_valid_hdr(hdr, 0)) {
+ const struct genlmsghdr *ghdr;
+
+ ghdr = nlmsg_data(hdr);
+ s = NULL;
+ if (hdr->nlmsg_type == GENL_ID_CTRL)
+ s = _genl_ctrl_cmd_to_str(ghdr->cmd);
+ if (s)
+ nm_strbuf_append(&buf, &len, ", cmd:%s", s);
+ else
+ nm_strbuf_append(&buf, &len, ", cmd:(%u)", (unsigned) ghdr->cmd);
+ }
break;
}
- if (s)
- nm_strbuf_append_str(&buf, &len, s);
- else
- nm_strbuf_append(&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type);
-
flags = hdr->nlmsg_flags;
if (!flags) {
@@ -402,7 +402,7 @@ nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad)
/*****************************************************************************/
int
-nlmsg_parse(struct nlmsghdr *nlh,
+nlmsg_parse(const struct nlmsghdr *nlh,
int hdrlen,
struct nlattr *tb[],
int maxtype,
@@ -761,8 +761,8 @@ genlmsg_user_hdr(const struct genlmsghdr *gnlh)
return genlmsg_data(gnlh);
}
-struct genlmsghdr *
-genlmsg_hdr(struct nlmsghdr *nlh)
+const struct genlmsghdr *
+genlmsg_hdr(const struct nlmsghdr *nlh)
{
return nlmsg_data(nlh);
}
@@ -795,7 +795,7 @@ genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
}
int
-genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen)
+genlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen)
{
struct genlmsghdr *ghdr;
@@ -810,13 +810,13 @@ genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen)
}
int
-genlmsg_parse(struct nlmsghdr *nlh,
+genlmsg_parse(const struct nlmsghdr *nlh,
int hdrlen,
struct nlattr *tb[],
int maxtype,
const struct nla_policy *policy)
{
- struct genlmsghdr *ghdr;
+ const struct genlmsghdr *ghdr;
if (!genlmsg_valid_hdr(nlh, hdrlen))
return -NME_NL_MSG_TOOSHORT;
@@ -829,23 +829,24 @@ genlmsg_parse(struct nlmsghdr *nlh,
policy);
}
+const struct nla_policy genl_ctrl_policy[CTRL_ATTR_MCAST_GROUPS + 1] = {
+ [CTRL_ATTR_FAMILY_ID] = {.type = NLA_U16},
+ [CTRL_ATTR_FAMILY_NAME] = {.type = NLA_STRING, .maxlen = GENL_NAMSIZ},
+ [CTRL_ATTR_VERSION] = {.type = NLA_U32},
+ [CTRL_ATTR_HDRSIZE] = {.type = NLA_U32},
+ [CTRL_ATTR_MAXATTR] = {.type = NLA_U32},
+ [CTRL_ATTR_OPS] = {.type = NLA_NESTED},
+ [CTRL_ATTR_MCAST_GROUPS] = {.type = NLA_NESTED},
+};
+
static int
_genl_parse_getfamily(struct nl_msg *msg, void *arg)
{
- static const struct nla_policy ctrl_policy[] = {
- [CTRL_ATTR_FAMILY_ID] = {.type = NLA_U16},
- [CTRL_ATTR_FAMILY_NAME] = {.type = NLA_STRING, .maxlen = GENL_NAMSIZ},
- [CTRL_ATTR_VERSION] = {.type = NLA_U32},
- [CTRL_ATTR_HDRSIZE] = {.type = NLA_U32},
- [CTRL_ATTR_MAXATTR] = {.type = NLA_U32},
- [CTRL_ATTR_OPS] = {.type = NLA_NESTED},
- [CTRL_ATTR_MCAST_GROUPS] = {.type = NLA_NESTED},
- };
- struct nlattr *tb[G_N_ELEMENTS(ctrl_policy)];
+ struct nlattr *tb[G_N_ELEMENTS(genl_ctrl_policy)];
struct nlmsghdr *nlh = nlmsg_hdr(msg);
gint32 *response_data = arg;
- if (genlmsg_parse_arr(nlh, 0, tb, ctrl_policy) < 0)
+ if (genlmsg_parse_arr(nlh, 0, tb, genl_ctrl_policy) < 0)
return NL_SKIP;
if (tb[CTRL_ATTR_FAMILY_ID])
@@ -933,12 +934,19 @@ nl_socket_set_passcred(struct nl_sock *sk, int state)
err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof(state));
if (err < 0)
return -nm_errno_from_native(errno);
+ return 0;
+}
- if (state)
- sk->s_flags |= NL_SOCK_PASSCRED;
- else
- sk->s_flags &= ~NL_SOCK_PASSCRED;
+int
+nl_socket_set_pktinfo(struct nl_sock *sk, int state)
+{
+ int err;
+
+ nm_assert_sk(sk);
+ err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO, &state, sizeof(state));
+ if (err < 0)
+ return -nm_errno_from_native(errno);
return 0;
}
@@ -1025,33 +1033,14 @@ nl_socket_add_memberships(struct nl_sock *sk, int group, ...)
return 0;
}
-int
-nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable)
-{
- int err;
- int val;
-
- nm_assert_sk(sk);
-
- val = !!enable;
- err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof(val));
- if (err < 0)
- return -nm_errno_from_native(errno);
-
- return 0;
-}
-
-void
-nl_socket_disable_msg_peek(struct nl_sock *sk)
-{
- sk->s_flags |= NL_MSG_PEEK_EXPLICIT;
- sk->s_flags &= ~NL_MSG_PEEK;
-}
-
/*****************************************************************************/
int
-nl_socket_new(struct nl_sock **out_sk, int protocol)
+nl_socket_new(struct nl_sock **out_sk,
+ int protocol,
+ NLSocketFlags flags,
+ int bufsize_rx,
+ int bufsize_tx)
{
nm_auto_nlsock struct nl_sock *sk = NULL;
nm_auto_close int fd = -1;
@@ -1060,10 +1049,14 @@ nl_socket_new(struct nl_sock **out_sk, int protocol)
int nmerr;
socklen_t addrlen;
struct sockaddr_nl local = {0};
+ int i_val;
nm_assert(out_sk && !*out_sk);
- fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
+ fd = socket(AF_NETLINK,
+ SOCK_RAW | SOCK_CLOEXEC
+ | (NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_NONBLOCK) ? SOCK_NONBLOCK : 0),
+ protocol);
if (fd < 0)
return -nm_errno_from_native(errno);
@@ -1086,12 +1079,30 @@ nl_socket_new(struct nl_sock **out_sk, int protocol)
},
.s_seq_expect = t,
.s_seq_next = t,
+ .s_bufsize = 0,
+ .s_msg_peek = !NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK),
+ .s_auto_ack = TRUE,
};
- nmerr = nl_socket_set_buffer_size(sk, 0, 0);
+ nmerr = nl_socket_set_buffer_size(sk, bufsize_rx, bufsize_tx);
if (nmerr < 0)
return nmerr;
+ i_val = 1;
+ (void) setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &i_val, sizeof(i_val));
+
+ if (NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_PASSCRED)) {
+ err = nl_socket_set_passcred(sk, 1);
+ if (err < 0)
+ return err;
+ }
+
+ if (NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_PKTINFO)) {
+ err = nl_socket_set_pktinfo(sk, 1);
+ if (err < 0)
+ return err;
+ }
+
err = bind(sk->s_fd, (struct sockaddr *) &sk->s_local, sizeof(sk->s_local));
if (err != 0)
return -nm_errno_from_native(errno);
@@ -1107,8 +1118,6 @@ nl_socket_new(struct nl_sock **out_sk, int protocol)
if (local.nl_family != AF_NETLINK)
return -NME_UNSPEC;
- (void) nl_socket_set_ext_ack(sk, TRUE);
-
sk->s_local = local;
sk->s_proto = protocol;
@@ -1182,7 +1191,7 @@ nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb)
gboolean creds_has;
continue_reading:
- n = nl_recv(sk, NULL, 0, &nla, &buf, &creds, &creds_has);
+ n = nl_recv(sk, NULL, 0, &nla, &buf, &creds, &creds_has, NULL, NULL);
if (n <= 0)
return n;
@@ -1199,7 +1208,7 @@ continue_reading:
nrecv++;
/* Only do sequence checking if auto-ack mode is enabled */
- if (!(sk->s_flags & NL_NO_AUTO_ACK)) {
+ if (sk->s_auto_ack) {
if (hdr->nlmsg_seq != sk->s_seq_expect) {
nmerr = -NME_NL_SEQ_MISMATCH;
goto out;
@@ -1384,7 +1393,7 @@ nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg)
nlh->nlmsg_flags |= NLM_F_REQUEST;
- if (!(sk->s_flags & NL_NO_AUTO_ACK))
+ if (sk->s_auto_ack)
nlh->nlmsg_flags |= NLM_F_ACK;
}
@@ -1419,6 +1428,10 @@ nl_send_auto(struct nl_sock *sk, struct nl_msg *msg)
* on success.
* @out_creds_has: (out) (allow-none): result indicating whether
* @out_creds was filled.
+* @out_pktinfo_group: (out) (allow-none): optional out buffer for NETLINK_PKTINFO
+* group on success.
+ * @out_pktinfo_has: (out) (allow-none): result indicating whether
+ * @out_pktinfo_group was filled.
*
* If @buf0_len is zero, the function will g_malloc() a new receive buffer of size
* nl_socket_get_msg_buf_size(). If @buf0_len is larger than zero, then @buf0
@@ -1441,18 +1454,20 @@ nl_recv(struct nl_sock *sk,
struct sockaddr_nl *nla,
unsigned char **buf,
struct ucred *out_creds,
- gboolean *out_creds_has)
+ gboolean *out_creds_has,
+ uint32_t *out_pktinfo_group,
+ gboolean *out_pktinfo_has)
{
- /* We really expect msg_contol_buf to be large enough and MSG_CTRUNC not
- * happening. We nm_assert() against that. However, in release builds
- * we don't assert, so add some extra safety space for the unexpected
- * case where we might need more than CMSG_SPACE(sizeof(struct ucred)).
- * It should not hurt and should not be necessary. It's just some
- * extra defensive space. */
-#define _MSG_CONTROL_BUF_EXTRA_SPACE (NM_MORE_ASSERTS ? 512u : 0u)
union {
- struct cmsghdr cmsghdr;
- char buf[CMSG_SPACE(sizeof(struct ucred)) + _MSG_CONTROL_BUF_EXTRA_SPACE];
+ struct cmsghdr _dummy_for_alignment;
+ struct {
+ char buf[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(struct nl_pktinfo))];
+
+ /* We really expect that "buf" is large enough end even assert against
+ * that. We don't expect and don't want to handle MSG_CTRUNC error.
+ * Still, add some extra safety. This is on the stack and essentially for free. */
+ char _extra[512];
+ };
} msg_contol_buf;
ssize_t n;
int flags = 0;
@@ -1465,17 +1480,16 @@ nl_recv(struct nl_sock *sk,
.msg_controllen = 0,
.msg_control = NULL,
};
- struct ucred tmpcreds;
- gboolean tmpcreds_has = FALSE;
- int retval;
- int errsv;
+ struct cmsghdr *cmsg;
+ int retval;
+ int errsv;
nm_assert(nla);
nm_assert(buf && !*buf);
- nm_assert(!out_creds_has == !out_creds);
+ nm_assert(!out_creds_has || out_creds);
+ nm_assert(!out_pktinfo_has || out_pktinfo_group);
- if ((sk->s_flags & NL_MSG_PEEK)
- || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0))
+ if (sk->s_msg_peek)
flags |= MSG_PEEK | MSG_TRUNC;
if (buf0_len > 0) {
@@ -1486,7 +1500,7 @@ nl_recv(struct nl_sock *sk,
iov.iov_base = g_malloc(iov.iov_len);
}
- if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) {
+ if (out_creds_has || out_pktinfo_has) {
msg.msg_controllen = sizeof(msg_contol_buf);
msg.msg_control = msg_contol_buf.buf;
}
@@ -1506,11 +1520,14 @@ retry:
goto abort;
}
+ nm_assert((gsize) n <= G_MAXINT);
+
/* We really don't expect truncation of ancillary data. We provided a large
* enough buffer, so this is likely a bug. In the worst case, we might lack
* the requested credentials and the caller likely will reject the message
* later. */
nm_assert(!(msg.msg_flags & MSG_CTRUNC));
+ nm_assert(msg.msg_controllen <= G_STRUCT_OFFSET(typeof(msg_contol_buf), _extra));
if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) {
/* respond with error to an incomplete message */
@@ -1539,32 +1556,35 @@ retry:
goto abort;
}
- if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) {
- struct cmsghdr *cmsg;
-
+ if (out_creds_has || out_pktinfo_has) {
+ NM_SET_OUT(out_creds_has, FALSE);
+ NM_SET_OUT(out_pktinfo_has, FALSE);
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level != SOL_SOCKET)
- continue;
- if (cmsg->cmsg_type != SCM_CREDENTIALS)
- continue;
- memcpy(&tmpcreds, CMSG_DATA(cmsg), sizeof(tmpcreds));
- tmpcreds_has = TRUE;
- break;
+ switch (cmsg->cmsg_level) {
+ case SOL_SOCKET:
+ if (cmsg->cmsg_type == SCM_CREDENTIALS && out_creds_has) {
+ memcpy(out_creds, CMSG_DATA(cmsg), sizeof(*out_creds));
+ *out_creds_has = TRUE;
+ }
+ break;
+ case SOL_NETLINK:
+ if (cmsg->cmsg_type == NETLINK_PKTINFO && out_pktinfo_has) {
+ struct nl_pktinfo p;
+
+ memcpy(&p, CMSG_DATA(cmsg), sizeof(p));
+ *out_pktinfo_group = p.group;
+ *out_pktinfo_has = TRUE;
+ }
+ break;
+ }
}
}
- retval = n;
+ *buf = iov.iov_base;
+ return (int) n;
abort:
- if (retval <= 0) {
- if (iov.iov_base != buf0)
- g_free(iov.iov_base);
- return retval;
- }
-
- *buf = iov.iov_base;
- if (out_creds && tmpcreds_has)
- *out_creds = tmpcreds;
- NM_SET_OUT(out_creds_has, tmpcreds_has);
+ if (iov.iov_base != buf0)
+ g_free(iov.iov_base);
return retval;
}
diff --git a/src/libnm-platform/nm-netlink.h b/src/libnm-platform/nm-netlink.h
index 11dd559766..efc9585313 100644
--- a/src/libnm-platform/nm-netlink.h
+++ b/src/libnm-platform/nm-netlink.h
@@ -43,14 +43,37 @@ enum {
struct nl_msg;
+/* This is similar to "struct nl_msg", in that it contains a
+ * netlink message including additional information like the
+ * src, creds, protocol.
+ *
+ * The difference is that "struct nl_msg" is an opaque type and
+ * contains a copy of the message (requiring two heap allocations).
+ * "struct nl_msg_lite" can be on the stack and it can directly
+ * point to the receive buffer, without need to copy the message.
+ * That can be useful, if you don't need to clone the message and
+ * just need to pass it "down the stack" for somebody to parse
+ * the message. */
+struct nl_msg_lite {
+ int nm_protocol;
+ const struct sockaddr_nl *nm_src;
+ const struct sockaddr_nl *nm_dst;
+ const struct ucred *nm_creds;
+ const struct nlmsghdr *nm_nlh;
+ size_t nm_size;
+};
+
/*****************************************************************************/
const char *nl_nlmsgtype2str(int type, char *buf, size_t size);
const char *nl_nlmsg_flags2str(int flags, char *buf, size_t len);
-const char *
-nl_nlmsghdr_to_str(int netlink_protocol, const struct nlmsghdr *hdr, char *buf, gsize len);
+const char *nl_nlmsghdr_to_str(int netlink_protocol,
+ guint32 pktinfo_group,
+ const struct nlmsghdr *hdr,
+ char *buf,
+ gsize len);
/*****************************************************************************/
@@ -465,7 +488,7 @@ nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype)
return nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), attrtype);
}
-int nlmsg_parse(struct nlmsghdr *nlh,
+int nlmsg_parse(const struct nlmsghdr *nlh,
int hdrlen,
struct nlattr *tb[],
int maxtype,
@@ -484,12 +507,26 @@ nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, i
/*****************************************************************************/
+typedef enum {
+ NL_SOCKET_FLAGS_NONE = 0,
+ NL_SOCKET_FLAGS_NONBLOCK = 0x1,
+ NL_SOCKET_FLAGS_PASSCRED = 0x2,
+ NL_SOCKET_FLAGS_PKTINFO = 0x4,
+ NL_SOCKET_FLAGS_DISABLE_MSG_PEEK = 0x8,
+
+ _NL_SOCKET_FLAGS_ALL = (NL_SOCKET_FLAGS_DISABLE_MSG_PEEK << 1) - 1,
+} NLSocketFlags;
+
#define NL_AUTO_PORT 0
#define NL_AUTO_SEQ 0
struct nl_sock;
-int nl_socket_new(struct nl_sock **out_sk, int protocol);
+int nl_socket_new(struct nl_sock **out_sk,
+ int protocol,
+ NLSocketFlags flags,
+ int bufsize_rx,
+ int bufsize_tx);
void nl_socket_free(struct nl_sock *sk);
@@ -507,9 +544,9 @@ int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf);
int nl_socket_set_passcred(struct nl_sock *sk, int state);
-int nl_socket_set_nonblocking(const struct nl_sock *sk);
+int nl_socket_set_pktinfo(struct nl_sock *sk, int state);
-void nl_socket_disable_msg_peek(struct nl_sock *sk);
+int nl_socket_set_nonblocking(const struct nl_sock *sk);
uint32_t nl_socket_get_local_port(const struct nl_sock *sk);
@@ -523,7 +560,9 @@ int nl_recv(struct nl_sock *sk,
struct sockaddr_nl *nla,
unsigned char **buf,
struct ucred *out_creds,
- gboolean *out_creds_has);
+ gboolean *out_creds_has,
+ uint32_t *out_pktinfo_group,
+ gboolean *out_pktinfo_has);
int nl_send(struct nl_sock *sk, struct nl_msg *msg);
@@ -568,28 +607,28 @@ int nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb);
int nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb);
-int nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable);
-
/*****************************************************************************/
-void *genlmsg_put(struct nl_msg *msg,
- uint32_t port,
- uint32_t seq,
- int family,
- int hdrlen,
- int flags,
- uint8_t cmd,
- uint8_t version);
-void *genlmsg_data(const struct genlmsghdr *gnlh);
-void *genlmsg_user_hdr(const struct genlmsghdr *gnlh);
-struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh);
-void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen);
-struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen);
-int genlmsg_len(const struct genlmsghdr *gnlh);
-int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen);
-int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen);
-
-int genlmsg_parse(struct nlmsghdr *nlh,
+extern const struct nla_policy genl_ctrl_policy[8];
+
+void *genlmsg_put(struct nl_msg *msg,
+ uint32_t port,
+ uint32_t seq,
+ int family,
+ int hdrlen,
+ int flags,
+ uint8_t cmd,
+ uint8_t version);
+void *genlmsg_data(const struct genlmsghdr *gnlh);
+void *genlmsg_user_hdr(const struct genlmsghdr *gnlh);
+const struct genlmsghdr *genlmsg_hdr(const struct nlmsghdr *nlh);
+void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen);
+struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen);
+int genlmsg_len(const struct genlmsghdr *gnlh);
+int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen);
+int genlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen);
+
+int genlmsg_parse(const struct nlmsghdr *nlh,
int hdrlen,
struct nlattr *tb[],
int maxtype,
diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c
index 9263a6e7d7..c351f014e1 100644
--- a/src/libnm-platform/tests/test-nm-platform.c
+++ b/src/libnm-platform/tests/test-nm-platform.c
@@ -70,8 +70,6 @@ test_use_symbols(void)
(void (*)(void)) nl_socket_set_nonblocking,
(void (*)(void)) nl_socket_set_buffer_size,
(void (*)(void)) nl_socket_add_memberships,
- (void (*)(void)) nl_socket_set_ext_ack,
- (void (*)(void)) nl_socket_disable_msg_peek,
(void (*)(void)) nl_wait_for_ack,
(void (*)(void)) nl_recvmsgs,
(void (*)(void)) nl_sendmsg,