platform: merge branch 'th/platform-genl-2'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1270
This commit is contained in:
Thomas Haller 2022-06-24 11:09:09 +02:00
commit eb90ebc530
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
4 changed files with 384 additions and 336 deletions

View file

@ -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;
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;
msg = nlmsg_alloc_convert(hdr);
nlmsg_set_proto(msg, netlink_protocol);
nlmsg_set_src(msg, &nla);
nlmsg_set_creds(msg, &creds);
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;
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 (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 (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 (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(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;
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,
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]);
}
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,

View file

@ -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;
case NETLINK_GENERIC:
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;
}

View file

@ -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);
extern const struct nla_policy genl_ctrl_policy[8];
int genlmsg_parse(struct nlmsghdr *nlh,
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,

View file

@ -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,