From d26c538b9a549082c1696221282c007692261a35 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 May 2018 01:15:10 +0200 Subject: xtables: add xtables-monitor This is a partial revert of commit 7462e4aa757dc28e74b4a731b3ee13079b04ef23 ("iptables-compat: Keep xtables-config and xtables-events out from tree") and re-adds xtables-events under a new name, with a few enhancements, this is --trace mode, which replaces printk-based tracing, and an imroved event mode which will now also display pid/name and new generation id at the end of a batch. Example output of xtables-monitor --event --trace PACKET: 10 fa6b77e1 IN=wlan0 MACSRC=51:14:31:51:XX:XX MACDST=1c:b6:b0:ac:XX:XX MACPROTO=86dd SRC=2a00:3a0:2::1 DST=2b00:bf0:c001::1 LEN=1440 TC=18 HOPLIMIT=61 FLOWLBL=1921 SPORT=22 DPORT=13024 ACK PSH TRACE: 10 fa6b77e1 raw:PREROUTING:return: TRACE: 10 fa6b77e1 raw:PREROUTING:policy:DROP EVENT: -6 -t mangle -A PREROUTING -j DNPT --src-pfx dead::/64 --dst-pfx 1c3::/64 NEWGEN: GENID=6581 PID=15601 NAME=xtables-multi Signed-off-by: Florian Westphal --- iptables/.gitignore | 1 + iptables/Makefile.am | 8 +- iptables/xtables-compat-multi.c | 1 + iptables/xtables-monitor.8.in | 93 ++++++ iptables/xtables-monitor.c | 684 ++++++++++++++++++++++++++++++++++++++++ iptables/xtables-multi.c | 1 - iptables/xtables-multi.h | 2 +- 7 files changed, 785 insertions(+), 5 deletions(-) create mode 100644 iptables/xtables-monitor.8.in create mode 100644 iptables/xtables-monitor.c (limited to 'iptables') diff --git a/iptables/.gitignore b/iptables/.gitignore index 7438ad12..5ca54adc 100644 --- a/iptables/.gitignore +++ b/iptables/.gitignore @@ -17,5 +17,6 @@ /xtables-config-parser.c /xtables-config-parser.h /xtables-config-syntax.c +/xtables-monitor.8 /xtables.pc diff --git a/iptables/Makefile.am b/iptables/Makefile.am index 2de14208..95e67b83 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -41,6 +41,7 @@ xtables_compat_multi_SOURCES += xtables-config-parser.y xtables-config-syntax.l xtables_compat_multi_SOURCES += xtables-save.c xtables-restore.c \ xtables-standalone.c xtables.c nft.c \ nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \ + xtables-monitor.c \ xtables-arp-standalone.c xtables-arp.c \ getethertype.c nft-bridge.c \ xtables-eb-standalone.c xtables-eb.c \ @@ -60,8 +61,9 @@ endif man_MANS = iptables.8 iptables-restore.8 iptables-save.8 \ iptables-xml.1 ip6tables.8 ip6tables-restore.8 \ ip6tables-save.8 iptables-extensions.8 \ - xtables-compat.8 xtables-translate.8 -CLEANFILES = iptables.8 \ + xtables-compat.8 xtables-translate.8 \ + xtables-monitor.8 +CLEANFILES = iptables.8 xtables-monitor.8 \ xtables-config-parser.c xtables-config-syntax.c vx_bin_links = iptables-xml @@ -76,7 +78,7 @@ x_sbin_links = iptables-compat iptables-compat-restore iptables-compat-save \ ip6tables-compat ip6tables-compat-restore ip6tables-compat-save \ iptables-translate ip6tables-translate \ iptables-restore-translate ip6tables-restore-translate \ - arptables-compat ebtables-compat + arptables-compat ebtables-compat xtables-monitor endif iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man diff --git a/iptables/xtables-compat-multi.c b/iptables/xtables-compat-multi.c index 0b05eade..014e5a4e 100644 --- a/iptables/xtables-compat-multi.c +++ b/iptables/xtables-compat-multi.c @@ -35,6 +35,7 @@ static const struct subcommand multi_subcommands[] = { {"ebtables-compat", xtables_eb_main}, {"ebtables-translate", xtables_eb_xlate_main}, {"ebtables", xtables_eb_main}, + {"xtables-monitor", xtables_monitor_main}, {NULL}, }; diff --git a/iptables/xtables-monitor.8.in b/iptables/xtables-monitor.8.in new file mode 100644 index 00000000..202842b9 --- /dev/null +++ b/iptables/xtables-monitor.8.in @@ -0,0 +1,93 @@ +.TH XTABLES\-MONITOR 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@" +.SH NAME +xtables-monitor \(em show changes to rule set and trace-events +.SH SYNOPSIS +\fBxtables\-monitor\fP [\fB\-t\fP] [\fB\-e\fP] [\fB\-4\fP|\fB|\-6\fB] +.PP +\ +.SH DESCRIPTION +.PP +.B xtables-monitor +is used to monitor changes to the ruleset or to show rule evaluation events +for packets tagged using the TRACE target. +.B xtables-monitor +will run until the user aborts execution, typically by using CTRL-C. +.RE +.SH OPTIONS +\fB\-e\fP, \fB\-\-event\fP +.TP +Watch for updates to the rule set. +Updates include creation of new tables, chains and rules and +the name of the program that caused the rule update. +.TP +\fB\-t\fP, \fB\-\-trace\fP +Watch for trace events generated by packets that have been tagged +using the TRACE target. +.TP +\fB\-4\fP +Restrict output to ipv4. +.TP +\fB\-6\fP +Restrict output to ipv6. +.SH EXAMPLE OUTPUT +.TP +.B xtables-monitor \-\-trace + + 1 TRACE: 2 fc475095 raw:PREROUTING:rule:0x3:CONTINUE \-4 \-t raw \-A PREROUTING \-p icmp \-j TRACE + 2 PACKET: 0 fc475095 IN=lo LL=0x304 0000000000000000000000000800 SRC=127.0.0.1 DST=127.0.0.1 LEN=84 TOS=0x0 TTL=64 ID=38349DF + 3 TRACE: 2 fc475095 raw:PREROUTING:return: + 4 TRACE: 2 fc475095 raw:PREROUTING:policy:ACCEPT + 5 TRACE: 2 fc475095 filter:INPUT:return: + 6 TRACE: 2 fc475095 filter:INPUT:policy:DROP + 7 TRACE: 2 0df9d3d8 raw:PREROUTING:rule:0x3:CONTINUE \-4 \-t raw \-A PREROUTING \-p icmp \-j TRACE +.PP +The first line shows a packet entering rule set evaluation. +The protocol number is shown (AF_INET in this case), then a packet +identifier number that allows to correlate messages coming from rule set evaluation of +this packet. After this, the rule that was matched by the packet is shown. +This is the TRACE rule that turns on tracing events for this packet. + +The second line dumps information about the packet. Incoming interface +and packet headers such as source and destination addresses are shown. + +The third line shows that the packet completed traversal of the raw table +PREROUTING chain, and is returning, followed by use the chain policy to make accept/drop +decision (the example shows accept being applied). +The fifth line shows that the packet leaves the filter INPUT chain, i.e., no rules in the filter tables +INPUT chain matched the packet. +It then got DROPPED by the policy of the INPUT table, as hown by line six. +The last line shows another packet arriving \-\- the packet id is different. + +When using the TRACE target, it is usally a good idea to only select packets +that are relevant, for example via +.nf +iptables \-t raw \-A PREROUTING \-p tcp \-\-dport 80 \-\-syn \-m limit \-\-limit 1/s \-j TRACE +.fi +.TP +.B xtables-monitor \-\-event + 1 EVENT: nft: NEW table: table filter ip flags 0 use 4 handle 444 + 2 EVENT: # nft: ip filter INPUT use 2 type filter hook input prio 0 policy drop packets 0 bytes 0 + 3 EVENT: # nft: ip filter FORWARD use 0 type filter hook forward prio 0 policy accept packets 0 bytes 0 + 4 EVENT: # nft: ip filter OUTPUT use 0 type filter hook output prio 0 policy accept packets 0 bytes 0 + 5 EVENT: \-4 \-t filter \-N TCP + 6 EVENT: \-4 \-t filter \-A TCP \-s 192.168.0.0/16 \-p tcp \-m tcp \-\-dport 22 \-j ACCEPT + 7 EVENT: \-4 \-t filter \-A TCP \-p tcp \-m multiport \-\-dports 80,443 \-j ACCEPT + 8 EVENT: \-4 \-t filter \-A INPUT \-p tcp \-j TCP + 9 EVENT: \-4 \-t filter \-A INPUT \-m conntrack \-\-ctstate RELATED,ESTABLISHED \-j ACCEPT + 10 NEWGEN: GENID=13904 PID=25167 NAME=iptables-nftables-restore +.PP +This example shows event monitoring. Line one shows creation of a table (filter in this case), followed +by three base hooks INPUT, FORWARD and OUTPUT. The iptables-nftables tools all create tables and base +chains automatically when needed, so this is expected when a table was not yet initialized or when it is +re-created from scratch by iptables-nftables-restore. Line five shows a new user-defined chain (TCP) +being added, followed by addition a few rules. the last line shows that a new ruleset generation has +become active, i.e., the rule set changes are now active. This also lists the process id and the programs name. +.SH LIMITATIONS +.B xtables-monitor +only works with rules added using iptables-nftables, rules added using +iptables-legacy cannot be monitored. +.SH BUGS +Should be reported or by sending email to netfilter-devel@vger.kernel.org or +by filing a report on https://bugzilla.netfilter.org/. +.SH SEE ALSO +\fBiptables\fP(8), \fBxtables\fP(8), \fBnft\fP(8) diff --git a/iptables/xtables-monitor.c b/iptables/xtables-monitor.c new file mode 100644 index 00000000..e0a2d98f --- /dev/null +++ b/iptables/xtables-monitor.c @@ -0,0 +1,684 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software has been sponsored by Sophos Astaro + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "iptables.h" /* for xtables_globals */ +#include "xtables-multi.h" +#include "nft.h" +#include "nft-arp.h" + +struct cb_arg { + uint32_t nfproto; + bool is_event; +}; + +static int table_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t type = nlh->nlmsg_type & 0xFF; + const struct cb_arg *arg = data; + struct nftnl_table *t; + char buf[4096]; + + t = nftnl_table_alloc(); + if (t == NULL) + goto err; + + if (nftnl_table_nlmsg_parse(nlh, t) < 0) + goto err_free; + + if (arg->nfproto && arg->nfproto != nftnl_table_get_u32(t, NFTNL_TABLE_FAMILY)) + goto err_free; + nftnl_table_snprintf(buf, sizeof(buf), t, NFTNL_OUTPUT_DEFAULT, 0); + printf(" EVENT: "); + printf("nft: %s table: %s\n", type == NFT_MSG_NEWTABLE ? "NEW" : "DEL", buf); + +err_free: + nftnl_table_free(t); +err: + return MNL_CB_OK; +} + +static bool counters; +static bool trace; +static bool events; + +static int rule_cb(const struct nlmsghdr *nlh, void *data) +{ + struct arptables_command_state cs_arp = {}; + struct iptables_command_state cs = {}; + uint32_t type = nlh->nlmsg_type & 0xFF; + const struct cb_arg *arg = data; + struct nftnl_rule *r; + void *fw = NULL; + uint8_t family; + + r = nftnl_rule_alloc(); + if (r == NULL) + goto err; + + if (nftnl_rule_nlmsg_parse(nlh, r) < 0) + goto err_free; + + family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY); + if (arg->nfproto && arg->nfproto != family) + goto err_free; + + if (arg->is_event) + printf(" EVENT: "); + switch (family) { + case AF_INET: + case AF_INET6: + printf("-%c ", family == AF_INET ? '4' : '6'); + nft_rule_to_iptables_command_state(r, &cs); + fw = &cs; + break; + case NFPROTO_ARP: + printf("-0 "); + nft_rule_to_arptables_command_state(r, &cs_arp); + fw = &cs_arp; + break; + default: + goto err_free; + } + + printf("-t %s ", nftnl_rule_get_str(r, NFTNL_RULE_TABLE)); + nft_rule_print_save(fw, r, + type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND : + NFT_RULE_DEL, + counters ? 0 : FMT_NOCOUNTS); +err_free: + nftnl_rule_free(r); +err: + return MNL_CB_OK; +} + +static int chain_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t type = nlh->nlmsg_type & 0xFF; + const struct cb_arg *arg = data; + struct nftnl_chain *c; + char buf[4096]; + int family; + + c = nftnl_chain_alloc(); + if (c == NULL) + goto err; + + if (nftnl_chain_nlmsg_parse(nlh, c) < 0) + goto err_free; + + family = nftnl_chain_get_u32(c, NFTNL_CHAIN_FAMILY); + if (arg->nfproto && arg->nfproto != family) + goto err_free; + + if (nftnl_chain_is_set(c, NFTNL_CHAIN_PRIO)) + family = -1; + + printf(" EVENT: "); + switch (family) { + case NFPROTO_IPV4: + family = 4; + break; + case NFPROTO_IPV6: + family = 6; + break; + default: + nftnl_chain_snprintf(buf, sizeof(buf), c, NFTNL_OUTPUT_DEFAULT, 0); + printf("# nft: %s\n", buf); + goto err_free; + } + + printf("-%d -t %s -%c %s\n", + family, + nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), + type == NFT_MSG_NEWCHAIN ? 'N' : 'X', + nftnl_chain_get_str(c, NFTNL_CHAIN_NAME)); +err_free: + nftnl_chain_free(c); +err: + return MNL_CB_OK; +} + +static int newgen_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t genid = 0, pid = 0; + const struct nlattr *attr; + const char *name = NULL; + + mnl_attr_for_each(attr, nlh, sizeof(struct nfgenmsg)) { + switch (mnl_attr_get_type(attr)) { + case NFTA_GEN_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + break; + genid = ntohl(mnl_attr_get_u32(attr)); + break; + case NFTA_GEN_PROC_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + break; + name = mnl_attr_get_str(attr); + break; + case NFTA_GEN_PROC_PID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + break; + pid = ntohl(mnl_attr_get_u32(attr)); + break; + } + } + + if (name) + printf("NEWGEN: GENID=%u PID=%u NAME=%s\n", genid, pid, name); + + return MNL_CB_OK; +} + +static void trace_print_return(const struct nftnl_trace *nlt) +{ + const char *chain = NULL; + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) { + chain = nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET); + printf("%s", chain); + } +} + +static void trace_print_rule(const struct nftnl_trace *nlt, struct cb_arg *args) +{ + uint64_t handle = nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE); + uint32_t family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY); + const char *table = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE); + const char *chain = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN); + struct nftnl_rule *r; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + uint32_t portid; + char buf[16536]; + int ret; + + r = nftnl_rule_alloc(); + if (r == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, NLM_F_DUMP, 0); + + nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); + nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); + nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle); + nftnl_rule_nlmsg_build_payload(nlh, r); + nftnl_rule_free(r); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + portid = mnl_socket_get_portid(nl); + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + args->is_event = false; + ret = mnl_cb_run(buf, ret, 0, portid, rule_cb, args); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + mnl_socket_close(nl); +} + +static void trace_print_packet(const struct nftnl_trace *nlt, struct cb_arg *args) +{ + struct list_head stmts = LIST_HEAD_INIT(stmts); + uint32_t nfproto, family; + uint16_t l4proto = 0; + uint32_t mark; + char name[IFNAMSIZ]; + + printf("PACKET: %d %08x ", args->nfproto, nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID)); + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_IIF)) + printf("IN=%s ", if_indextoname(nftnl_trace_get_u32(nlt, NFTNL_TRACE_IIF), name)); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_OIF)) + printf("OUT=%s ", if_indextoname(nftnl_trace_get_u32(nlt, NFTNL_TRACE_OIF), name)); + + family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY); + nfproto = family; + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) { + nfproto = nftnl_trace_get_u32(nlt, NFTNL_TRACE_NFPROTO); + + if (family != nfproto) + printf("NFPROTO=%d ", nfproto); + } + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER)) { + const struct ethhdr *eh; + const char *linklayer; + uint32_t i, len; + uint16_t type = nftnl_trace_get_u16(nlt, NFTNL_TRACE_IIFTYPE); + + linklayer = nftnl_trace_get_data(nlt, NFTNL_TRACE_LL_HEADER, &len); + switch (type) { + case ARPHRD_ETHER: + if (len < sizeof(*eh)) + break; + eh = (const void *)linklayer; + printf("MACSRC=%s ", ether_ntoa((const void *)eh->h_source)); + printf("MACDST=%s ", ether_ntoa((const void *)eh->h_dest)); + printf("MACPROTO=%04x ", ntohs(eh->h_proto)); + break; + default: + printf("LL=0x%x ", type); + for (i = 0 ; i < len; i++) + printf("%02x", linklayer[i]); + printf(" "); + break; + } + } + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER)) { + const struct ip6_hdr *ip6h; + const struct iphdr *iph; + uint32_t i, len; + const char *nh; + + ip6h = nftnl_trace_get_data(nlt, NFTNL_TRACE_NETWORK_HEADER, &len); + + switch (nfproto) { + case NFPROTO_IPV4: { + char addrbuf[INET_ADDRSTRLEN]; + + if (len < sizeof(*iph)) + break; + iph = (const void *)ip6h; + + + inet_ntop(AF_INET, &iph->saddr, addrbuf, sizeof(addrbuf)); + printf("SRC=%s ", addrbuf); + inet_ntop(AF_INET, &iph->daddr, addrbuf, sizeof(addrbuf)); + printf("DST=%s ", addrbuf); + + printf("LEN=%d TOS=0x%x TTL=%d ID=%d", ntohs(iph->tot_len), iph->tos, iph->ttl, ntohs(iph->id)); + if (iph->frag_off & htons(0x8000)) + printf("CE "); + if (iph->frag_off & htons(IP_DF)) + printf("DF "); + if (iph->frag_off & htons(IP_MF)) + printf("MF "); + + if (ntohs(iph->frag_off) & 0x1fff) + printf("FRAG:%u ", ntohs(iph->frag_off) & 0x1fff); + + l4proto = iph->protocol; + if (iph->ihl * 4 > sizeof(*iph)) { + unsigned int optsize; + const char *op; + + optsize = iph->ihl * 4 - sizeof(*iph); + op = (const char *)iph; + op += sizeof(*iph); + + printf("OPT ("); + for (i = 0; i < optsize; i++) + printf("%02X", op[i]); + printf(")"); + } + break; + } + case NFPROTO_IPV6: { + uint32_t flowlabel = ntohl(*(uint32_t *)ip6h); + char addrbuf[INET6_ADDRSTRLEN]; + + if (len < sizeof(*ip6h)) + break; + + inet_ntop(AF_INET6, &ip6h->ip6_src, addrbuf, sizeof(addrbuf)); + printf("SRC=%s ", addrbuf); + inet_ntop(AF_INET6, &ip6h->ip6_dst, addrbuf, sizeof(addrbuf)); + printf("DST=%s ", addrbuf); + + printf("LEN=%zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", + ntohs(ip6h->ip6_plen) + sizeof(*iph), + (flowlabel & 0x0ff00000) >> 20, + ip6h->ip6_hops, + flowlabel & 0x000fffff); + + l4proto = ip6h->ip6_nxt; + break; + } + default: + nh = (const char *)ip6h; + printf("NH="); + for (i = 0 ; i < len; i++) + printf("%02x", nh[i]); + printf(" "); + } + } + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_TRANSPORT_HEADER)) { + const struct tcphdr *tcph; + uint32_t len; + + tcph = nftnl_trace_get_data(nlt, NFTNL_TRACE_TRANSPORT_HEADER, &len); + + switch (l4proto) { + case IPPROTO_DCCP: + case IPPROTO_SCTP: + case IPPROTO_UDPLITE: + case IPPROTO_UDP: + if (len < 4) + break; + printf("SPORT=%d DPORT=%d ", ntohs(tcph->th_sport), ntohs(tcph->th_dport)); + break; + case IPPROTO_TCP: + if (len < sizeof(*tcph)) + break; + printf("SPORT=%d DPORT=%d ", ntohs(tcph->th_sport), ntohs(tcph->th_dport)); + if (tcph->th_flags & (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG)) { + if (tcph->th_flags & TH_SYN) + printf("SYN "); + if (tcph->th_flags & TH_ACK) + printf("ACK "); + if (tcph->th_flags & TH_FIN) + printf("FIN "); + if (tcph->th_flags & TH_RST) + printf("RST "); + if (tcph->th_flags & TH_PUSH) + printf("PSH "); + if (tcph->th_flags & TH_URG) + printf("URG "); + } + break; + default: + break; + } + } + + mark = nftnl_trace_get_u32(nlt, NFTNL_TRACE_MARK); + if (mark) + printf("MARK=0x%x ", mark); +} + +static void print_verdict(struct nftnl_trace *nlt, uint32_t verdict) +{ + const char *chain; + + switch (verdict) { + case NF_ACCEPT: + printf("ACCEPT"); + break; + case NF_DROP: + printf("DROP"); + break; + case NF_QUEUE: + printf("QUEUE"); + break; + case NF_STOLEN: + printf("STOLEN"); + break; + case NFT_BREAK: + printf("BREAK"); + break; + case NFT_CONTINUE: + printf("CONTINUE"); + break; + case NFT_GOTO: + printf("GOTO"); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) { + chain = nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET); + printf(":%s", chain); + } + break; + case NFT_JUMP: + printf("JUMP"); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) { + chain = nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET); + printf(":%s", chain); + } + break; + default: + printf("0x%x", verdict); + break; + } + + printf(" "); +} + +static int trace_cb(const struct nlmsghdr *nlh, struct cb_arg *arg) +{ + struct nftnl_trace *nlt; + uint32_t verdict; + + nlt = nftnl_trace_alloc(); + if (nlt == NULL) + goto err; + + if (nftnl_trace_nlmsg_parse(nlh, nlt) < 0) + goto err_free; + + if (arg->nfproto && + arg->nfproto != nftnl_trace_get_u32(nlt, NFTNL_TABLE_FAMILY)) + goto err_free; + + printf(" TRACE: %d %08x %s:%s", nftnl_trace_get_u32(nlt, NFTNL_TABLE_FAMILY), + nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID), + nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE), + nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN)); + + switch (nftnl_trace_get_u32(nlt, NFTNL_TRACE_TYPE)) { + case NFT_TRACETYPE_RULE: + verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_VERDICT); + printf(":rule:0x%llx:", (unsigned long long)nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE)); + print_verdict(nlt, verdict); + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE)) + trace_print_rule(nlt, arg); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) || + nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER)) + trace_print_packet(nlt, arg); + break; + case NFT_TRACETYPE_POLICY: + printf(":policy:"); + verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_POLICY); + + print_verdict(nlt, verdict); + break; + case NFT_TRACETYPE_RETURN: + printf(":return:"); + trace_print_return(nlt); + break; + } + puts(""); +err_free: + nftnl_trace_free(nlt); +err: + return MNL_CB_OK; +} + +static int monitor_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t type = nlh->nlmsg_type & 0xFF; + struct cb_arg *arg = data; + int ret = MNL_CB_OK; + + switch(type) { + case NFT_MSG_NEWTABLE: + case NFT_MSG_DELTABLE: + ret = table_cb(nlh, data); + break; + case NFT_MSG_NEWCHAIN: + case NFT_MSG_DELCHAIN: + ret = chain_cb(nlh, data); + break; + case NFT_MSG_NEWRULE: + case NFT_MSG_DELRULE: + arg->is_event = true; + ret = rule_cb(nlh, data); + break; + case NFT_MSG_NEWGEN: + ret = newgen_cb(nlh, data); + break; + case NFT_MSG_TRACE: + ret = trace_cb(nlh, data); + break; + } + + return ret; +} + +static const struct option options[] = { + {.name = "counters", .has_arg = false, .val = 'c'}, + {.name = "trace", .has_arg = false, .val = 't'}, + {.name = "event", .has_arg = false, .val = 'e'}, + {.name = "ipv4", .has_arg = false, .val = '4'}, + {.name = "ipv6", .has_arg = false, .val = '6'}, + {NULL}, +}; + +static void print_usage(void) +{ + printf("%s %s\n", xtables_globals.program_name, + xtables_globals.program_version); + printf("Usage: %s [ -t | -e ]\n" + " --trace -t trace ruleset traversal of packets tagged via -j TRACE rule\n" + " --event -e show events taht modify the ruleset\n" + "Optional arguments:\n" + " --ipv4 -4 only monitor ipv4\n" + " --ipv6 -6 only monitor ipv6\n" + " --counters -c show counters in rules\n" + + , xtables_globals.program_name); + exit(EXIT_FAILURE); +} + +int xtables_monitor_main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + uint32_t nfgroup = 0; + struct cb_arg cb_arg; + int ret, c; + + xtables_globals.program_name = "xtables-monitor"; + /* XXX xtables_init_all does several things we don't want */ + c = xtables_init_all(&xtables_globals, NFPROTO_IPV4); + if (c < 0) { + fprintf(stderr, "%s/%s Failed to initialize xtables\n", + xtables_globals.program_name, + xtables_globals.program_version); + exit(1); + } +#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) + init_extensions(); + init_extensions4(); +#endif + + memset(&cb_arg, 0, sizeof(cb_arg)); + opterr = 0; + while ((c = getopt_long(argc, argv, "ceht46", options, NULL)) != -1) { + switch (c) { + case 'c': + counters = true; + break; + case 't': + trace = true; + break; + case 'e': + events = true; + break; + case 'h': + print_usage(); + exit(0); + case '4': + cb_arg.nfproto = NFPROTO_IPV4; + break; + case '6': + cb_arg.nfproto = NFPROTO_IPV6; + break; + default: + fprintf(stderr, "xtables-monitor %s: Bad argument.\n", XTABLES_VERSION); + fprintf(stderr, "Try `xtables-monitor -h' for more information."); + exit(PARAMETER_PROBLEM); + } + } + + if (trace) + nfgroup |= 1 << (NFNLGRP_NFTRACE - 1); + if (events) + nfgroup |= 1 << (NFNLGRP_NFTABLES - 1); + + if (nfgroup == 0) { + print_usage(); + exit(EXIT_FAILURE); + } + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("cannot open nfnetlink socket"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, nfgroup, MNL_SOCKET_AUTOPID) < 0) { + perror("cannot bind to nfnetlink socket"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, 0, 0, monitor_cb, &cb_arg); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("cannot receive from nfnetlink socket"); + exit(EXIT_FAILURE); + } + mnl_socket_close(nl); + + return EXIT_SUCCESS; +} + diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c index 30391e7f..e90885dd 100644 --- a/iptables/xtables-multi.c +++ b/iptables/xtables-multi.c @@ -41,7 +41,6 @@ static const struct subcommand multi_subcommands[] = { {"xtables-save", xtables_save_main}, {"xtables-restore", xtables_restore_main}, {"xtables-config", xtables_config_main}, - {"xtables-events", xtables_events_main}, {"xtables-arp", xtables_arp_main}, {"xtables-ebtables", xtables_eb_main}, #endif diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h index f0c14ea4..82ee9c9d 100644 --- a/iptables/xtables-multi.h +++ b/iptables/xtables-multi.h @@ -17,7 +17,7 @@ extern int xtables_ip6_xlate_restore_main(int, char **); extern int xtables_arp_main(int, char **); extern int xtables_eb_main(int, char **); extern int xtables_config_main(int, char **); -extern int xtables_events_main(int, char **); +extern int xtables_monitor_main(int, char **); #endif #endif /* _XTABLES_MULTI_H */ -- cgit v1.2.3