Dmitry V. Levin
b93d52fe3d
strace is now provided under the terms of the GNU Lesser General Public License version 2.1 or later, see COPYING for more details. strace test suite is now provided under the terms of the GNU General Public License version 2 or later, see tests/COPYING for more details.
668 lines
16 KiB
C
668 lines
16 KiB
C
/*
|
|
* Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr>
|
|
* Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
|
|
* Copyright (c) 2016-2018 The strace developers.
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include "netlink.h"
|
|
#include "nlattr.h"
|
|
#include <linux/audit.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/xfrm.h>
|
|
#include "xlat/netlink_ack_flags.h"
|
|
#include "xlat/netlink_delete_flags.h"
|
|
#include "xlat/netlink_flags.h"
|
|
#include "xlat/netlink_get_flags.h"
|
|
#include "xlat/netlink_new_flags.h"
|
|
#include "xlat/netlink_protocols.h"
|
|
#include "xlat/netlink_types.h"
|
|
#include "xlat/nf_acct_msg_types.h"
|
|
#include "xlat/nf_cthelper_msg_types.h"
|
|
#include "xlat/nf_ctnetlink_exp_msg_types.h"
|
|
#include "xlat/nf_ctnetlink_msg_types.h"
|
|
#include "xlat/nf_cttimeout_msg_types.h"
|
|
#include "xlat/nf_ipset_msg_types.h"
|
|
#include "xlat/nf_nft_compat_msg_types.h"
|
|
#include "xlat/nf_nftables_msg_types.h"
|
|
#include "xlat/nf_osf_msg_types.h"
|
|
#include "xlat/nf_queue_msg_types.h"
|
|
#include "xlat/nf_ulog_msg_types.h"
|
|
#include "xlat/nl_audit_types.h"
|
|
#include "xlat/nl_crypto_types.h"
|
|
#include "xlat/nl_netfilter_subsys_ids.h"
|
|
#include "xlat/nl_selinux_types.h"
|
|
#include "xlat/nl_sock_diag_types.h"
|
|
#include "xlat/nl_xfrm_types.h"
|
|
#include "xlat/nlmsgerr_attrs.h"
|
|
|
|
/*
|
|
* Fetch a struct nlmsghdr from the given address.
|
|
*/
|
|
static bool
|
|
fetch_nlmsghdr(struct tcb *const tcp, struct nlmsghdr *const nlmsghdr,
|
|
const kernel_ulong_t addr, const kernel_ulong_t len,
|
|
const bool in_array)
|
|
{
|
|
if (len < sizeof(struct nlmsghdr)) {
|
|
printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
|
|
return false;
|
|
}
|
|
|
|
if (tfetch_obj(tcp, addr, nlmsghdr))
|
|
return true;
|
|
|
|
if (in_array) {
|
|
tprints("...");
|
|
printaddr_comment(addr);
|
|
} else {
|
|
printaddr(addr);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int
|
|
get_fd_nl_family(struct tcb *const tcp, const int fd)
|
|
{
|
|
const unsigned long inode = getfdinode(tcp, fd);
|
|
if (!inode)
|
|
return -1;
|
|
|
|
const char *const details = get_sockaddr_by_inode(tcp, fd, inode);
|
|
if (!details)
|
|
return -1;
|
|
|
|
const char *const nl_details = STR_STRIP_PREFIX(details, "NETLINK:[");
|
|
if (nl_details == details)
|
|
return -1;
|
|
|
|
const struct xlat *xlats = netlink_protocols;
|
|
for (; xlats->str; ++xlats) {
|
|
const char *name = STR_STRIP_PREFIX(xlats->str, "NETLINK_");
|
|
if (!strncmp(nl_details, name, strlen(name)))
|
|
return xlats->val;
|
|
}
|
|
|
|
if (*nl_details >= '0' && *nl_details <= '9')
|
|
return atoi(nl_details);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
decode_nlmsg_type_default(struct tcb *tcp, const struct xlat *const xlat,
|
|
const uint16_t type,
|
|
const char *const dflt)
|
|
{
|
|
printxval(xlat, type, dflt);
|
|
}
|
|
|
|
static void
|
|
decode_nlmsg_type_generic(struct tcb *tcp, const struct xlat *const xlat,
|
|
const uint16_t type,
|
|
const char *const dflt)
|
|
{
|
|
printxval(genl_families_xlat(tcp), type, dflt);
|
|
}
|
|
|
|
static const struct {
|
|
const struct xlat *const xlat;
|
|
const char *const dflt;
|
|
} nf_nlmsg_types[] = {
|
|
[NFNL_SUBSYS_CTNETLINK] = {
|
|
nf_ctnetlink_msg_types,
|
|
"IPCTNL_MSG_CT_???"
|
|
},
|
|
[NFNL_SUBSYS_CTNETLINK_EXP] = {
|
|
nf_ctnetlink_exp_msg_types,
|
|
"IPCTNL_MSG_EXP_???"
|
|
},
|
|
[NFNL_SUBSYS_QUEUE] = { nf_queue_msg_types, "NFQNL_MSG_???" },
|
|
[NFNL_SUBSYS_ULOG] = { nf_ulog_msg_types, "NFULNL_MSG_???" },
|
|
[NFNL_SUBSYS_OSF] = { nf_osf_msg_types, "OSF_MSG_???" },
|
|
[NFNL_SUBSYS_IPSET] = { nf_ipset_msg_types, "IPSET_CMD_???" },
|
|
[NFNL_SUBSYS_ACCT] = { nf_acct_msg_types, "NFNL_MSG_ACCT_???" },
|
|
[NFNL_SUBSYS_CTNETLINK_TIMEOUT] = {
|
|
nf_cttimeout_msg_types,
|
|
"IPCTNL_MSG_TIMEOUT_???"
|
|
},
|
|
[NFNL_SUBSYS_CTHELPER] = {
|
|
nf_cthelper_msg_types,
|
|
"NFNL_MSG_CTHELPER_???"
|
|
},
|
|
[NFNL_SUBSYS_NFTABLES] = { nf_nftables_msg_types, "NFT_MSG_???" },
|
|
[NFNL_SUBSYS_NFT_COMPAT] = {
|
|
nf_nft_compat_msg_types,
|
|
"NFNL_MSG_COMPAT_???"
|
|
}
|
|
};
|
|
|
|
static void
|
|
decode_nlmsg_type_netfilter(struct tcb *tcp, const struct xlat *const xlat,
|
|
const uint16_t type,
|
|
const char *const dflt)
|
|
{
|
|
/* Reserved control nfnetlink messages first. */
|
|
const char *const text = xlookup(nl_netfilter_msg_types, type);
|
|
if (text) {
|
|
print_xlat_ex(type, text, XLAT_STYLE_DEFAULT);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Other netfilter message types are split
|
|
* in two pieces: 8 bits subsystem and 8 bits type.
|
|
*/
|
|
const uint8_t subsys_id = (uint8_t) (type >> 8);
|
|
const uint8_t msg_type = (uint8_t) type;
|
|
|
|
printxval(xlat, subsys_id, dflt);
|
|
|
|
tprints("<<8|");
|
|
if (subsys_id < ARRAY_SIZE(nf_nlmsg_types))
|
|
printxval(nf_nlmsg_types[subsys_id].xlat,
|
|
msg_type, nf_nlmsg_types[subsys_id].dflt);
|
|
else
|
|
tprintf("%#x", msg_type);
|
|
}
|
|
|
|
typedef void (*nlmsg_types_decoder_t)(struct tcb *, const struct xlat *,
|
|
uint16_t type,
|
|
const char *dflt);
|
|
|
|
static const struct {
|
|
const nlmsg_types_decoder_t decoder;
|
|
const struct xlat *const xlat;
|
|
const char *const dflt;
|
|
} nlmsg_types[] = {
|
|
[NETLINK_AUDIT] = { NULL, nl_audit_types, "AUDIT_???" },
|
|
[NETLINK_CRYPTO] = { NULL, nl_crypto_types, "CRYPTO_MSG_???" },
|
|
[NETLINK_GENERIC] = {
|
|
decode_nlmsg_type_generic,
|
|
NULL,
|
|
"GENERIC_FAMILY_???"
|
|
},
|
|
[NETLINK_NETFILTER] = {
|
|
decode_nlmsg_type_netfilter,
|
|
nl_netfilter_subsys_ids,
|
|
"NFNL_SUBSYS_???"
|
|
},
|
|
[NETLINK_ROUTE] = { NULL, nl_route_types, "RTM_???" },
|
|
[NETLINK_SELINUX] = { NULL, nl_selinux_types, "SELNL_MSG_???" },
|
|
[NETLINK_SOCK_DIAG] = { NULL, nl_sock_diag_types, "SOCK_DIAG_???" },
|
|
[NETLINK_XFRM] = { NULL, nl_xfrm_types, "XFRM_MSG_???" }
|
|
};
|
|
|
|
/*
|
|
* As all valid netlink families are positive integers, use unsigned int
|
|
* for family here to filter out -1.
|
|
*/
|
|
static void
|
|
decode_nlmsg_type(struct tcb *tcp, const uint16_t type,
|
|
const unsigned int family)
|
|
{
|
|
nlmsg_types_decoder_t decoder = decode_nlmsg_type_default;
|
|
const struct xlat *xlat = netlink_types;
|
|
const char *dflt = "NLMSG_???";
|
|
|
|
/*
|
|
* type < NLMSG_MIN_TYPE are reserved control messages
|
|
* that need no family-specific decoding.
|
|
*/
|
|
if (type >= NLMSG_MIN_TYPE && family < ARRAY_SIZE(nlmsg_types)) {
|
|
if (nlmsg_types[family].decoder)
|
|
decoder = nlmsg_types[family].decoder;
|
|
if (nlmsg_types[family].xlat)
|
|
xlat = nlmsg_types[family].xlat;
|
|
if (nlmsg_types[family].dflt)
|
|
dflt = nlmsg_types[family].dflt;
|
|
}
|
|
|
|
decoder(tcp, xlat, type, dflt);
|
|
}
|
|
|
|
static const struct xlat *
|
|
decode_nlmsg_flags_crypto(const uint16_t type)
|
|
{
|
|
switch (type) {
|
|
case CRYPTO_MSG_NEWALG:
|
|
return netlink_new_flags;
|
|
case CRYPTO_MSG_DELALG:
|
|
case CRYPTO_MSG_DELRNG:
|
|
return netlink_delete_flags;
|
|
case CRYPTO_MSG_GETALG:
|
|
return netlink_get_flags;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct xlat *
|
|
decode_nlmsg_flags_netfilter(const uint16_t type)
|
|
{
|
|
const uint8_t subsys_id = (uint8_t) (type >> 8);
|
|
const uint8_t msg_type = (uint8_t) type;
|
|
|
|
switch (subsys_id) {
|
|
case NFNL_SUBSYS_CTNETLINK:
|
|
switch (msg_type) {
|
|
case IPCTNL_MSG_CT_NEW:
|
|
return netlink_new_flags;
|
|
case IPCTNL_MSG_CT_GET:
|
|
case IPCTNL_MSG_CT_GET_CTRZERO:
|
|
case IPCTNL_MSG_CT_GET_STATS_CPU:
|
|
case IPCTNL_MSG_CT_GET_STATS:
|
|
case IPCTNL_MSG_CT_GET_DYING:
|
|
case IPCTNL_MSG_CT_GET_UNCONFIRMED:
|
|
return netlink_get_flags;
|
|
case IPCTNL_MSG_CT_DELETE:
|
|
return netlink_delete_flags;
|
|
}
|
|
break;
|
|
case NFNL_SUBSYS_CTNETLINK_EXP:
|
|
switch (msg_type) {
|
|
case IPCTNL_MSG_EXP_NEW:
|
|
return netlink_new_flags;
|
|
case IPCTNL_MSG_EXP_GET:
|
|
case IPCTNL_MSG_EXP_GET_STATS_CPU:
|
|
return netlink_get_flags;
|
|
case IPCTNL_MSG_EXP_DELETE:
|
|
return netlink_delete_flags;
|
|
}
|
|
break;
|
|
case NFNL_SUBSYS_ACCT:
|
|
switch (msg_type) {
|
|
case NFNL_MSG_ACCT_NEW:
|
|
return netlink_new_flags;
|
|
case NFNL_MSG_ACCT_GET:
|
|
case NFNL_MSG_ACCT_GET_CTRZERO:
|
|
return netlink_get_flags;
|
|
case NFNL_MSG_ACCT_DEL:
|
|
return netlink_delete_flags;
|
|
}
|
|
break;
|
|
case NFNL_SUBSYS_CTNETLINK_TIMEOUT:
|
|
switch (msg_type) {
|
|
case IPCTNL_MSG_TIMEOUT_NEW:
|
|
return netlink_new_flags;
|
|
case IPCTNL_MSG_TIMEOUT_GET:
|
|
return netlink_get_flags;
|
|
case IPCTNL_MSG_TIMEOUT_DELETE:
|
|
return netlink_delete_flags;
|
|
}
|
|
break;
|
|
case NFNL_SUBSYS_CTHELPER:
|
|
switch (msg_type) {
|
|
case NFNL_MSG_CTHELPER_NEW:
|
|
return netlink_new_flags;
|
|
case NFNL_MSG_CTHELPER_GET:
|
|
return netlink_get_flags;
|
|
case NFNL_MSG_CTHELPER_DEL:
|
|
return netlink_delete_flags;
|
|
}
|
|
break;
|
|
case NFNL_SUBSYS_NFTABLES:
|
|
switch (msg_type) {
|
|
case NFT_MSG_NEWTABLE:
|
|
case NFT_MSG_NEWCHAIN:
|
|
case NFT_MSG_NEWRULE:
|
|
case NFT_MSG_NEWSET:
|
|
case NFT_MSG_NEWSETELEM:
|
|
case NFT_MSG_NEWGEN:
|
|
case NFT_MSG_NEWOBJ:
|
|
return netlink_new_flags;
|
|
case NFT_MSG_GETTABLE:
|
|
case NFT_MSG_GETCHAIN:
|
|
case NFT_MSG_GETRULE:
|
|
case NFT_MSG_GETSET:
|
|
case NFT_MSG_GETSETELEM:
|
|
case NFT_MSG_GETGEN:
|
|
case NFT_MSG_GETOBJ:
|
|
case NFT_MSG_GETOBJ_RESET:
|
|
return netlink_get_flags;
|
|
case NFT_MSG_DELTABLE:
|
|
case NFT_MSG_DELCHAIN:
|
|
case NFT_MSG_DELRULE:
|
|
case NFT_MSG_DELSET:
|
|
case NFT_MSG_DELSETELEM:
|
|
case NFT_MSG_DELOBJ:
|
|
return netlink_delete_flags;
|
|
}
|
|
break;
|
|
case NFNL_SUBSYS_NFT_COMPAT:
|
|
switch (msg_type) {
|
|
case NFNL_MSG_COMPAT_GET:
|
|
return netlink_get_flags;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct xlat *
|
|
decode_nlmsg_flags_route(const uint16_t type)
|
|
{
|
|
/* RTM_DELACTION uses NLM_F_ROOT flags */
|
|
if (type == RTM_DELACTION)
|
|
return netlink_get_flags;
|
|
switch (type & 3) {
|
|
case 0:
|
|
return netlink_new_flags;
|
|
case 1:
|
|
return netlink_delete_flags;
|
|
case 2:
|
|
return netlink_get_flags;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct xlat *
|
|
decode_nlmsg_flags_sock_diag(const uint16_t type)
|
|
{
|
|
return netlink_get_flags;
|
|
}
|
|
|
|
static const struct xlat *
|
|
decode_nlmsg_flags_xfrm(const uint16_t type)
|
|
{
|
|
switch (type) {
|
|
case XFRM_MSG_NEWSA:
|
|
case XFRM_MSG_NEWPOLICY:
|
|
case XFRM_MSG_NEWAE:
|
|
case XFRM_MSG_NEWSADINFO:
|
|
case XFRM_MSG_NEWSPDINFO:
|
|
return netlink_new_flags;
|
|
case XFRM_MSG_DELSA:
|
|
case XFRM_MSG_DELPOLICY:
|
|
return netlink_delete_flags;
|
|
case XFRM_MSG_GETSA:
|
|
case XFRM_MSG_GETPOLICY:
|
|
case XFRM_MSG_GETAE:
|
|
case XFRM_MSG_GETSADINFO:
|
|
case XFRM_MSG_GETSPDINFO:
|
|
return netlink_get_flags;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
typedef const struct xlat *(*nlmsg_flags_decoder_t)(const uint16_t type);
|
|
|
|
static const nlmsg_flags_decoder_t nlmsg_flags[] = {
|
|
[NETLINK_CRYPTO] = decode_nlmsg_flags_crypto,
|
|
[NETLINK_NETFILTER] = decode_nlmsg_flags_netfilter,
|
|
[NETLINK_ROUTE] = decode_nlmsg_flags_route,
|
|
[NETLINK_SOCK_DIAG] = decode_nlmsg_flags_sock_diag,
|
|
[NETLINK_XFRM] = decode_nlmsg_flags_xfrm
|
|
};
|
|
|
|
/*
|
|
* As all valid netlink families are positive integers, use unsigned int
|
|
* for family here to filter out -1.
|
|
*/
|
|
static void
|
|
decode_nlmsg_flags(const uint16_t flags, const uint16_t type,
|
|
const unsigned int family)
|
|
{
|
|
const struct xlat *table = NULL;
|
|
|
|
if (type < NLMSG_MIN_TYPE) {
|
|
if (type == NLMSG_ERROR)
|
|
table = netlink_ack_flags;
|
|
} else if (family < ARRAY_SIZE(nlmsg_flags) && nlmsg_flags[family])
|
|
table = nlmsg_flags[family](type);
|
|
|
|
printflags_ex(flags, "NLM_F_???", XLAT_STYLE_DEFAULT,
|
|
netlink_flags, table, NULL);
|
|
}
|
|
|
|
static void
|
|
print_nlmsghdr(struct tcb *tcp,
|
|
const int fd,
|
|
const int family,
|
|
const struct nlmsghdr *const nlmsghdr)
|
|
{
|
|
/* print the whole structure regardless of its nlmsg_len */
|
|
|
|
tprintf("{len=%u, type=", nlmsghdr->nlmsg_len);
|
|
|
|
decode_nlmsg_type(tcp, nlmsghdr->nlmsg_type, family);
|
|
|
|
tprints(", flags=");
|
|
decode_nlmsg_flags(nlmsghdr->nlmsg_flags,
|
|
nlmsghdr->nlmsg_type, family);
|
|
|
|
tprintf(", seq=%u, pid=%u}", nlmsghdr->nlmsg_seq,
|
|
nlmsghdr->nlmsg_pid);
|
|
}
|
|
|
|
static bool
|
|
print_cookie(struct tcb *const tcp, void *const elem_buf,
|
|
const size_t elem_size, void *const opaque_data)
|
|
{
|
|
tprintf("%" PRIu8, *(uint8_t *) elem_buf);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
decode_nlmsgerr_attr_cookie(struct tcb *const tcp,
|
|
const kernel_ulong_t addr,
|
|
const unsigned int len,
|
|
const void *const opaque_data)
|
|
{
|
|
uint8_t cookie;
|
|
const size_t nmemb = len / sizeof(cookie);
|
|
|
|
print_array(tcp, addr, nmemb, &cookie, sizeof(cookie),
|
|
tfetch_mem, print_cookie, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
static const nla_decoder_t nlmsgerr_nla_decoders[] = {
|
|
[NLMSGERR_ATTR_MSG] = decode_nla_str,
|
|
[NLMSGERR_ATTR_OFFS] = decode_nla_u32,
|
|
[NLMSGERR_ATTR_COOKIE] = decode_nlmsgerr_attr_cookie
|
|
};
|
|
|
|
static void
|
|
decode_nlmsghdr_with_payload(struct tcb *const tcp,
|
|
const int fd,
|
|
const int family,
|
|
const struct nlmsghdr *const nlmsghdr,
|
|
const kernel_ulong_t addr,
|
|
const kernel_ulong_t len);
|
|
|
|
static void
|
|
decode_nlmsgerr(struct tcb *const tcp,
|
|
const int fd,
|
|
const int family,
|
|
kernel_ulong_t addr,
|
|
unsigned int len,
|
|
const bool capped)
|
|
{
|
|
struct nlmsgerr err;
|
|
|
|
if (len < sizeof(err.error)) {
|
|
printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
|
|
return;
|
|
}
|
|
|
|
if (umove_or_printaddr(tcp, addr, &err.error))
|
|
return;
|
|
|
|
tprints("{error=");
|
|
if (err.error < 0 && (unsigned) -err.error < nerrnos) {
|
|
tprintf("-%s", errnoent[-err.error]);
|
|
} else {
|
|
tprintf("%d", err.error);
|
|
}
|
|
|
|
addr += offsetof(struct nlmsgerr, msg);
|
|
len -= offsetof(struct nlmsgerr, msg);
|
|
|
|
if (len) {
|
|
tprints(", msg=");
|
|
if (fetch_nlmsghdr(tcp, &err.msg, addr, len, false)) {
|
|
unsigned int payload =
|
|
capped ? sizeof(err.msg) : err.msg.nlmsg_len;
|
|
if (payload > len)
|
|
payload = len;
|
|
|
|
decode_nlmsghdr_with_payload(tcp, fd, family,
|
|
&err.msg, addr, payload);
|
|
if (len > payload) {
|
|
tprints(", ");
|
|
decode_nlattr(tcp, addr + payload,
|
|
len - payload, nlmsgerr_attrs,
|
|
"NLMSGERR_ATTR_???",
|
|
nlmsgerr_nla_decoders,
|
|
ARRAY_SIZE(nlmsgerr_nla_decoders),
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
tprints("}");
|
|
}
|
|
|
|
static const netlink_decoder_t netlink_decoders[] = {
|
|
#ifdef HAVE_LINUX_CRYPTOUSER_H
|
|
[NETLINK_CRYPTO] = decode_netlink_crypto,
|
|
#endif
|
|
#ifdef HAVE_LINUX_NETFILTER_NFNETLINK_H
|
|
[NETLINK_NETFILTER] = decode_netlink_netfilter,
|
|
#endif
|
|
[NETLINK_ROUTE] = decode_netlink_route,
|
|
[NETLINK_SELINUX] = decode_netlink_selinux,
|
|
[NETLINK_SOCK_DIAG] = decode_netlink_sock_diag
|
|
};
|
|
|
|
static void
|
|
decode_payload(struct tcb *const tcp,
|
|
const int fd,
|
|
const int family,
|
|
const struct nlmsghdr *const nlmsghdr,
|
|
const kernel_ulong_t addr,
|
|
const unsigned int len)
|
|
{
|
|
if (nlmsghdr->nlmsg_type == NLMSG_ERROR) {
|
|
decode_nlmsgerr(tcp, fd, family, addr, len,
|
|
nlmsghdr->nlmsg_flags & NLM_F_CAPPED);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* While most of NLMSG_DONE messages indeed have payloads
|
|
* containing just a single integer, there are few exceptions,
|
|
* so pass payloads of NLMSG_DONE messages to family-specific
|
|
* netlink payload decoders.
|
|
*
|
|
* Other types of reserved control messages need no family-specific
|
|
* netlink payload decoding.
|
|
*/
|
|
if ((nlmsghdr->nlmsg_type >= NLMSG_MIN_TYPE
|
|
|| nlmsghdr->nlmsg_type == NLMSG_DONE)
|
|
&& (unsigned int) family < ARRAY_SIZE(netlink_decoders)
|
|
&& netlink_decoders[family]
|
|
&& netlink_decoders[family](tcp, nlmsghdr, addr, len)) {
|
|
return;
|
|
}
|
|
|
|
if (nlmsghdr->nlmsg_type == NLMSG_DONE && len == sizeof(int)) {
|
|
int num;
|
|
|
|
if (!umove_or_printaddr(tcp, addr, &num))
|
|
tprintf("%d", num);
|
|
return;
|
|
}
|
|
|
|
printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
|
|
}
|
|
|
|
static void
|
|
decode_nlmsghdr_with_payload(struct tcb *const tcp,
|
|
const int fd,
|
|
const int family,
|
|
const struct nlmsghdr *const nlmsghdr,
|
|
const kernel_ulong_t addr,
|
|
const kernel_ulong_t len)
|
|
{
|
|
const unsigned int nlmsg_len = MIN(nlmsghdr->nlmsg_len, len);
|
|
|
|
if (nlmsg_len > NLMSG_HDRLEN)
|
|
tprints("{");
|
|
|
|
print_nlmsghdr(tcp, fd, family, nlmsghdr);
|
|
|
|
if (nlmsg_len > NLMSG_HDRLEN) {
|
|
tprints(", ");
|
|
decode_payload(tcp, fd, family, nlmsghdr, addr + NLMSG_HDRLEN,
|
|
nlmsg_len - NLMSG_HDRLEN);
|
|
tprints("}");
|
|
}
|
|
}
|
|
|
|
void
|
|
decode_netlink(struct tcb *const tcp,
|
|
const int fd,
|
|
kernel_ulong_t addr,
|
|
kernel_ulong_t len)
|
|
{
|
|
const int family = get_fd_nl_family(tcp, fd);
|
|
|
|
if (family == NETLINK_KOBJECT_UEVENT) {
|
|
decode_netlink_kobject_uevent(tcp, addr, len);
|
|
return;
|
|
}
|
|
|
|
struct nlmsghdr nlmsghdr;
|
|
bool is_array = false;
|
|
unsigned int elt;
|
|
|
|
for (elt = 0; fetch_nlmsghdr(tcp, &nlmsghdr, addr, len, is_array);
|
|
elt++) {
|
|
if (abbrev(tcp) && elt == max_strlen) {
|
|
tprints("...");
|
|
break;
|
|
}
|
|
|
|
unsigned int nlmsg_len = NLMSG_ALIGN(nlmsghdr.nlmsg_len);
|
|
kernel_ulong_t next_addr = 0;
|
|
kernel_ulong_t next_len = 0;
|
|
|
|
if (nlmsghdr.nlmsg_len >= NLMSG_HDRLEN) {
|
|
next_len = (len >= nlmsg_len) ? len - nlmsg_len : 0;
|
|
|
|
if (next_len && addr + nlmsg_len > addr)
|
|
next_addr = addr + nlmsg_len;
|
|
}
|
|
|
|
if (!is_array && next_addr) {
|
|
tprints("[");
|
|
is_array = true;
|
|
}
|
|
|
|
decode_nlmsghdr_with_payload(tcp, fd, family,
|
|
&nlmsghdr, addr, len);
|
|
|
|
if (!next_addr)
|
|
break;
|
|
|
|
tprints(", ");
|
|
addr = next_addr;
|
|
len = next_len;
|
|
}
|
|
|
|
if (is_array) {
|
|
tprints("]");
|
|
}
|
|
}
|