strace/netlink_netfilter.c
2018-08-31 21:11:42 +02:00

606 lines
17 KiB
C

/*
* Copyright (c) 2018 Chen Jingpiao <chenjingpiao@gmail.com>
* Copyright (c) 2018 The strace developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "defs.h"
#ifdef HAVE_LINUX_NETFILTER_NFNETLINK_H
# include "print_fields.h"
# include "nlattr.h"
# include <netinet/in.h>
# include <arpa/inet.h>
# include "netlink.h"
# include <linux/netfilter/nfnetlink.h>
# include "xlat/icmp_codes_redirect.h"
# include "xlat/icmp_codes_time_exceeded.h"
# include "xlat/icmp_codes_unreach.h"
# include "xlat/icmp_types.h"
# include "xlat/icmpv6_codes_parameter.h"
# include "xlat/icmpv6_codes_time_exceeded.h"
# include "xlat/icmpv6_codes_unreach.h"
# include "xlat/icmpv6_types.h"
# include "xlat/netfilter_versions.h"
# include "xlat/nf_acct_attr_names.h"
# include "xlat/nf_cthelper_attr_names.h"
# include "xlat/nf_ctnetlink_attr_names.h"
# include "xlat/nf_ctnetlink_exp_attr_names.h"
# include "xlat/nf_ctnetlink_to_attr_names.h"
# include "xlat/nf_ipset_attr_names.h"
# include "xlat/nf_nft_compat_attr_names.h"
# include "xlat/nf_osf_attr_names.h"
# include "xlat/nf_queue_attr_names.h"
# include "xlat/nf_ulog_attr_names.h"
# include "xlat/nfnl_ct_ip_attrs.h"
# include "xlat/nfnl_ct_proto_attrs.h"
# include "xlat/nfnl_ct_protoinfo_attrs.h"
# include "xlat/nfnl_ct_protoinfo_dccp_attrs.h"
# include "xlat/nfnl_ct_protoinfo_sctp_attrs.h"
# include "xlat/nfnl_ct_protoinfo_tcp_attrs.h"
# include "xlat/nfnl_ct_status.h"
# include "xlat/nfnl_ct_tcp_flags.h"
# include "xlat/nfnl_ct_tcp_states.h"
# include "xlat/nfnl_ct_tuple_attrs.h"
# include "xlat/nfnl_zones.h"
# include "xlat/nft_chain_attr_names.h"
# include "xlat/nft_flow_attr_names.h"
# include "xlat/nft_gen_attr_names.h"
# include "xlat/nft_obj_attr_names.h"
# include "xlat/nft_rule_attr_names.h"
# include "xlat/nft_set_attr_names.h"
# include "xlat/nft_setelem_attr_names.h"
# include "xlat/nft_table_attr_names.h"
# include "xlat/nft_trace_attr_names.h"
# include "xlat/nl_netfilter_msg_types.h"
# include "xlat/nl_netfilter_subsys_ids.h"
# define XLAT_MACROS_ONLY
# include "xlat/nl_netfilter_subsys_ids.h"
# include "xlat/nf_nftables_msg_types.h"
# undef XLAT_MACROS_ONLY
struct nfnl_decoder {
const struct xlat *name_xlat;
const char *dflt;
const nla_decoder_t *decoders;
size_t decoders_sz;
const struct nfnl_decoder *subdecoder;
size_t subdecoder_sz;
};
static const struct nfnl_decoder nft_subsystem_decoders[] = {
[NFT_MSG_NEWTABLE] =
{ nft_table_attr_names, "NFTA_TABLE_???", },
[NFT_MSG_GETTABLE] =
{ nft_table_attr_names, "NFTA_TABLE_???", },
[NFT_MSG_DELTABLE] =
{ nft_table_attr_names, "NFTA_TABLE_???", },
[NFT_MSG_NEWCHAIN] =
{ nft_chain_attr_names, "NFTA_CHAIN_???", },
[NFT_MSG_GETCHAIN] =
{ nft_chain_attr_names, "NFTA_CHAIN_???", },
[NFT_MSG_DELCHAIN] =
{ nft_chain_attr_names, "NFTA_CHAIN_???", },
[NFT_MSG_NEWRULE] =
{ nft_rule_attr_names, "NFTA_RULE_???", },
[NFT_MSG_GETRULE] =
{ nft_rule_attr_names, "NFTA_RULE_???", },
[NFT_MSG_DELRULE] =
{ nft_rule_attr_names, "NFTA_RULE_???", },
[NFT_MSG_NEWSET] =
{ nft_set_attr_names, "NFTA_SET_???", },
[NFT_MSG_GETSET] =
{ nft_set_attr_names, "NFTA_SET_???", },
[NFT_MSG_DELSET] =
{ nft_set_attr_names, "NFTA_SET_???", },
[NFT_MSG_NEWSETELEM] =
{ nft_setelem_attr_names, "NFTA_SET_ELEM_???", },
[NFT_MSG_GETSETELEM] =
{ nft_setelem_attr_names, "NFTA_SET_ELEM_???", },
[NFT_MSG_DELSETELEM] =
{ nft_setelem_attr_names, "NFTA_SET_ELEM_???", },
[NFT_MSG_NEWGEN] =
{ nft_gen_attr_names, "NFTA_GEN_???", },
[NFT_MSG_GETGEN] =
{ nft_gen_attr_names, "NFTA_GEN_???", },
[NFT_MSG_TRACE] =
{ nft_trace_attr_names, "NFTA_TRACE_???", },
[NFT_MSG_NEWOBJ] =
{ nft_obj_attr_names, "NFTA_OBJ_???", },
[NFT_MSG_GETOBJ] =
{ nft_obj_attr_names, "NFTA_OBJ_???", },
[NFT_MSG_DELOBJ] =
{ nft_obj_attr_names, "NFTA_OBJ_???", },
[NFT_MSG_GETOBJ_RESET] =
{ nft_obj_attr_names, "NFTA_OBJ_???", },
[NFT_MSG_NEWFLOWTABLE] =
{ nft_flow_attr_names, "NFTA_FLOW_???", },
[NFT_MSG_GETFLOWTABLE] =
{ nft_flow_attr_names, "NFTA_FLOW_???", },
[NFT_MSG_DELFLOWTABLE] =
{ nft_flow_attr_names, "NFTA_FLOW_???", },
};
static const nla_decoder_t nfnl_ct_ip_decoders[] = {
[CTA_IP_V4_SRC] = decode_nla_in_addr,
[CTA_IP_V4_DST] = decode_nla_in_addr,
[CTA_IP_V6_SRC] = decode_nla_in6_addr,
[CTA_IP_V6_DST] = decode_nla_in6_addr,
};
static bool
decode_cta_ip(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
decode_nlattr(tcp, addr, len, nfnl_ct_ip_attrs, "CTA_IP_???",
ARRSZ_PAIR(nfnl_ct_ip_decoders), opaque_data);
return true;
}
struct cta_proto_ctx {
uint8_t icmp_type_seen :1,
icmpv6_type_seen :1;
uint8_t icmp_type;
uint8_t icmpv6_type;
};
static bool
decode_cta_icmp_type(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
struct cta_proto_ctx *ctx = (void *) opaque_data;
uint8_t type;
if (len < sizeof(type))
return false;
if (!umove_or_printaddr(tcp, addr, &type))
printxval(icmp_types, type, "ICMP_???");
if (ctx) {
ctx->icmp_type_seen = 1;
ctx->icmp_type = type;
}
return true;
}
static bool
decode_cta_icmp_code(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
static const struct {
uint16_t type;
const struct xlat *xlat;
const char *dflt;
} codes[] = {
{ ICMP_DEST_UNREACH, icmp_codes_unreach,
"ICMP_???" },
{ ICMP_REDIRECT, icmp_codes_redirect,
"ICMP_REDIR_???" },
{ ICMP_TIME_EXCEEDED, icmp_codes_time_exceeded,
"ICMP_EXC_???" },
};
struct cta_proto_ctx *ctx = (void *) opaque_data;
uint8_t code;
if (len < sizeof(code))
return false;
if (umove_or_printaddr(tcp, addr, &code))
return true;
if (!ctx || !ctx->icmp_type_seen)
goto type_not_found;
for (size_t i = 0; i < ARRAY_SIZE(codes); i++) {
if (codes[i].type == ctx->icmp_type) {
printxval(codes[i].xlat, code, codes[i].dflt);
return true;
}
}
type_not_found:
tprintf("%#hhx", code);
return true;
}
static bool
decode_cta_icmpv6_type(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
struct cta_proto_ctx *ctx = (void *) opaque_data;
uint8_t type;
if (len < sizeof(type))
return false;
if (!umove_or_printaddr(tcp, addr, &type))
printxval(icmpv6_types, type, "ICMPV6_???");
if (ctx) {
ctx->icmpv6_type_seen = 1;
ctx->icmpv6_type = type;
}
return true;
}
static bool
decode_cta_icmpv6_code(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
static const struct code_data {
const struct xlat *xlat;
const char *dflt;
} codes[] = {
[ICMPV6_DEST_UNREACH] =
{ icmpv6_codes_unreach, "ICMPV6_???" },
[ICMPV6_TIME_EXCEED] =
{ icmpv6_codes_time_exceeded, "ICMPV6_EXC_???" },
[ICMPV6_PARAMPROB] =
{ icmpv6_codes_parameter, "ICMPV6_???" },
};
struct cta_proto_ctx *ctx = (void *) opaque_data;
uint8_t code;
if (len < sizeof(code))
return false;
if (umove_or_printaddr(tcp, addr, &code))
return true;
if (!ctx || !ctx->icmpv6_type_seen
|| ctx->icmpv6_type >= ARRAY_SIZE(codes)
|| !codes[ctx->icmpv6_type].xlat) {
tprintf("%#hhx", code);
return true;
}
const struct code_data *c = codes + ctx->icmpv6_type;
printxval(c->xlat, code, c->dflt);
return true;
}
static const nla_decoder_t nfnl_ct_proto_decoders[] = {
[CTA_PROTO_NUM] = decode_nla_ip_proto,
[CTA_PROTO_SRC_PORT] = decode_nla_be16,
[CTA_PROTO_DST_PORT] = decode_nla_be16,
[CTA_PROTO_ICMP_ID] = decode_nla_be16,
[CTA_PROTO_ICMP_TYPE] = decode_cta_icmp_type,
[CTA_PROTO_ICMP_CODE] = decode_cta_icmp_code,
[CTA_PROTO_ICMPV6_ID] = decode_nla_be16,
[CTA_PROTO_ICMPV6_TYPE] = decode_cta_icmpv6_type,
[CTA_PROTO_ICMPV6_CODE] = decode_cta_icmpv6_code,
};
static bool
decode_cta_proto(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
struct cta_proto_ctx ctx = { 0 };
decode_nlattr(tcp, addr, len, nfnl_ct_proto_attrs, "CTA_PROTO_???",
ARRSZ_PAIR(nfnl_ct_proto_decoders), &ctx);
return true;
}
static const nla_decoder_t nfnl_ct_tuple_decoders[] = {
[CTA_TUPLE_IP] = decode_cta_ip,
[CTA_TUPLE_PROTO] = decode_cta_proto,
[CTA_TUPLE_ZONE] = decode_nla_be16,
};
static bool
decode_cta_tuple(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
decode_nlattr(tcp, addr, len, nfnl_ct_tuple_attrs, "CTA_TUPLE_???",
ARRSZ_PAIR(nfnl_ct_tuple_decoders), opaque_data);
return true;
}
static bool
decode_cta_status(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
uint32_t status;
if (len < sizeof(status))
return false;
if (umove_or_printaddr(tcp, addr, &status))
return true;
status = ntohs(status);
tprints("htons(");
printflags(nfnl_ct_status, status, "IPS_???");
tprints(")");
return true;
}
static bool
decode_cta_tcp_state(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
uint32_t state;
if (len < sizeof(state))
return false;
if (!umove_or_printaddr(tcp, addr, &state))
printxval(nfnl_ct_tcp_states, state, "TCP_CONNTRACK_???");
return true;
}
static bool
decode_cta_tcp_flags(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
uint8_t flags[2];
if (len < sizeof(flags))
return false;
if (umove_or_printaddr(tcp, addr, &flags))
return true;
tprints("{flags=");
printflags(nfnl_ct_tcp_flags, flags[0], "IP_CT_???");
tprints(", mask=");
printflags(nfnl_ct_tcp_flags, flags[1], "IP_CT_???");
tprints("}");
return true;
}
static const nla_decoder_t nfnl_ct_protoinfo_tcp_decoders[] = {
[CTA_PROTOINFO_TCP_STATE] = decode_cta_tcp_state,
[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = decode_nla_u8,
[CTA_PROTOINFO_TCP_WSCALE_REPLY] = decode_nla_u8,
[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = decode_cta_tcp_flags,
[CTA_PROTOINFO_TCP_FLAGS_REPLY] = decode_cta_tcp_flags,
};
static bool
decode_cta_protoinfo_tcp(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
decode_nlattr(tcp, addr, len, nfnl_ct_protoinfo_tcp_attrs,
"CTA_PROTOINFO_TCP_???",
ARRSZ_PAIR(nfnl_ct_protoinfo_tcp_decoders), opaque_data);
return true;
}
static const nla_decoder_t nfnl_ct_protoinfo_dccp_decoders[] = {
};
static bool
decode_cta_protoinfo_dccp(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
decode_nlattr(tcp, addr, len, nfnl_ct_protoinfo_dccp_attrs,
"CTA_PROTOINFO_DCCP_???",
ARRSZ_PAIR(nfnl_ct_protoinfo_dccp_decoders), opaque_data);
return true;
}
static const nla_decoder_t nfnl_ct_protoinfo_sctp_decoders[] = {
};
static bool
decode_cta_protoinfo_sctp(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
decode_nlattr(tcp, addr, len, nfnl_ct_protoinfo_sctp_attrs,
"CTA_PROTOINFO_SCTP_???",
ARRSZ_PAIR(nfnl_ct_protoinfo_sctp_decoders), opaque_data);
return true;
}
static const nla_decoder_t nfnl_ct_protoinfo_decoders[] = {
[CTA_PROTOINFO_TCP] = decode_cta_protoinfo_tcp,
[CTA_PROTOINFO_DCCP] = decode_cta_protoinfo_dccp,
[CTA_PROTOINFO_SCTP] = decode_cta_protoinfo_sctp,
};
static bool
decode_cta_protoinfo(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
decode_nlattr(tcp, addr, len, nfnl_ct_protoinfo_attrs,
"CTA_PROTOINFO_???",
ARRSZ_PAIR(nfnl_ct_protoinfo_decoders), opaque_data);
return true;
}
static const nla_decoder_t nf_ctnetlink_decoders[] = {
[CTA_TUPLE_ORIG] = decode_cta_tuple,
[CTA_TUPLE_REPLY] = decode_cta_tuple,
[CTA_STATUS] = decode_cta_status,
[CTA_PROTOINFO] = decode_cta_protoinfo,
[CTA_HELP] = decode_nla_u32,
[CTA_NAT_SRC] = decode_nla_u32,
[CTA_TIMEOUT] = decode_nla_u32,
[CTA_MARK] = decode_nla_u32,
[CTA_COUNTERS_ORIG] = decode_nla_u32,
[CTA_COUNTERS_REPLY] = decode_nla_u32,
[CTA_USE] = decode_nla_u32,
[CTA_ID] = decode_nla_u32,
[CTA_NAT_DST] = decode_nla_u32,
[CTA_TUPLE_MASTER] = decode_nla_u32,
[CTA_SEQ_ADJ_ORIG] = decode_nla_u32,
[CTA_SEQ_ADJ_REPLY] = decode_nla_u32,
[CTA_SECMARK] = decode_nla_u32,
[CTA_ZONE] = decode_nla_u32,
[CTA_SECCTX] = decode_nla_u32,
[CTA_TIMESTAMP] = decode_nla_u32,
[CTA_MARK_MASK] = decode_nla_u32,
[CTA_LABELS] = decode_nla_u32,
[CTA_LABELS_MASK] = decode_nla_u32,
[CTA_SYNPROXY] = decode_nla_u32,
};
static const struct nfnl_decoder nfnl_subsystems[] = {
[NFNL_SUBSYS_CTNETLINK] =
{ nf_ctnetlink_attr_names, "CTA_???",
ARRSZ_PAIR(nf_ctnetlink_decoders) },
[NFNL_SUBSYS_CTNETLINK_EXP] =
{ nf_ctnetlink_exp_attr_names, "CTA_EXPECT_???", },
[NFNL_SUBSYS_QUEUE] =
{ nf_queue_attr_names, "NFQA_???", },
[NFNL_SUBSYS_ULOG] =
{ nf_ulog_attr_names, "NFULA_???", },
[NFNL_SUBSYS_OSF] =
{ nf_osf_attr_names, "OSF_???", },
[NFNL_SUBSYS_IPSET] =
{ nf_ipset_attr_names, "IPSET_ATTR_???", },
[NFNL_SUBSYS_ACCT] =
{ nf_acct_attr_names, "NFACCT_???", },
[NFNL_SUBSYS_CTNETLINK_TIMEOUT] =
{ nf_ctnetlink_to_attr_names, "CTA_TIMEOUT_???", },
[NFNL_SUBSYS_CTHELPER] =
{ nf_cthelper_attr_names, "NFCTH_???" },
[NFNL_SUBSYS_NFTABLES] =
{ NULL, "NFT_???", NULL, 0,
ARRSZ_PAIR(nft_subsystem_decoders) },
[NFNL_SUBSYS_NFT_COMPAT] =
{ nf_nft_compat_attr_names, "NFTA_COMPAT_???" },
};
bool
decode_netlink_netfilter(struct tcb *const tcp,
const struct nlmsghdr *const nlmsghdr,
const kernel_ulong_t addr,
const unsigned int len)
{
if (nlmsghdr->nlmsg_type == NLMSG_DONE)
return false;
struct nfgenmsg nfmsg;
if (len < sizeof(nfmsg))
printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
else if (!umove_or_printaddr(tcp, addr, &nfmsg)) {
const uint8_t subsys_id = nlmsghdr->nlmsg_type >> 8;
const uint8_t msg_type = nlmsghdr->nlmsg_type;
uint16_t res_id = ntohs(nfmsg.res_id);
PRINT_FIELD_XVAL("{", nfmsg, nfgen_family, addrfams, "AF_???");
PRINT_FIELD_XVAL(", ", nfmsg, version, netfilter_versions,
"NFNETLINK_???");
/*
* Work around wrong endianness in res_id field,
* see linux commit v4.3-rc1~28^2~47^2~1
*/
tprints(", res_id=");
if (subsys_id == NFNL_SUBSYS_NFTABLES
&& res_id == NFNL_SUBSYS_NFTABLES) {
print_xlat_ex(nfmsg.res_id,
"htons(NFNL_SUBSYS_NFTABLES)",
XLAT_STYLE_DEFAULT);
} else if (subsys_id == NFNL_SUBSYS_NFTABLES
&& nfmsg.res_id == NFNL_SUBSYS_NFTABLES) {
print_xlat_ex(nfmsg.res_id, "NFNL_SUBSYS_NFTABLES",
XLAT_STYLE_DEFAULT);
} else {
tprintf("htons(%d)", res_id);
}
const size_t offset = NLMSG_ALIGN(sizeof(nfmsg));
if (len > offset) {
tprints(", ");
if ((nlmsghdr->nlmsg_type >= NFNL_MSG_BATCH_BEGIN
&& nlmsghdr->nlmsg_type <= NFNL_MSG_BATCH_END)
|| nlmsghdr->nlmsg_type < NLMSG_MIN_TYPE) {
printstr_ex(tcp, addr + offset,
len - offset, QUOTE_FORCE_HEX);
} else {
static const struct nfnl_decoder def;
const struct nfnl_decoder *subsys = &def;
if (subsys_id < ARRAY_SIZE(nfnl_subsystems))
subsys = nfnl_subsystems + subsys_id;
if (subsys->subdecoder
&& (msg_type < subsys->subdecoder_sz))
subsys = subsys->subdecoder + msg_type;
decode_nlattr(tcp, addr + offset, len - offset,
subsys->name_xlat, subsys->dflt,
subsys->decoders,
subsys->decoders_sz, NULL);
}
}
}
return true;
}
#endif /* HAVE_LINUX_NETFILTER_NFNETLINK_H */