Add dtrace SDT probe ipfw:::rule-matched.

It helps to reduce complexity with debugging of large ipfw rulesets.
Also define several constants and translators, that can by used by
dtrace scripts with this probe.

Reviewed by:	gnn
Obtained from:	Yandex LLC
MFC after:	2 weeks
Sponsored by:	Yandex LLC
Differential Revision:	https://reviews.freebsd.org/D26879
This commit is contained in:
Andrey V. Elsukov 2020-10-21 15:01:33 +00:00
parent 2f1c35053c
commit 7ec2f6bce5
3 changed files with 240 additions and 1 deletions

View File

@ -21,7 +21,7 @@ SCRIPTS= blocking \
SCRIPTSDIR= ${SHAREDIR}/dtrace SCRIPTSDIR= ${SHAREDIR}/dtrace
DSRCS= mbuf.d DSRCS= mbuf.d ipfw.d
FILES= ${DSRCS} FILES= ${DSRCS}
FILESDIR= /usr/lib/dtrace FILESDIR= /usr/lib/dtrace

219
share/dtrace/ipfw.d Normal file
View File

@ -0,0 +1,219 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Yandex LLC
* Copyright (c) 2020 Andrey V. Elsukov <ae@FreeBSD.org>
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``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 AUTHORS OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#pragma D depends_on provider ipfw
/* ipfw_chk() return values */
#pragma D binding "1.0" IP_FW_PASS
inline int IP_FW_PASS = 0;
#pragma D binding "1.0" IP_FW_DENY
inline int IP_FW_DENY = 1;
#pragma D binding "1.0" IP_FW_DIVERT
inline int IP_FW_DIVERT = 2;
#pragma D binding "1.0" IP_FW_TEE
inline int IP_FW_TEE = 3;
#pragma D binding "1.0" IP_FW_DUMMYNET
inline int IP_FW_DUMMYNET = 4;
#pragma D binding "1.0" IP_FW_NETGRAPH
inline int IP_FW_NETGRAPH = 5;
#pragma D binding "1.0" IP_FW_NGTEE
inline int IP_FW_NGTEE = 6;
#pragma D binding "1.0" IP_FW_NAT
inline int IP_FW_NAT = 7;
#pragma D binding "1.0" IP_FW_REASS
inline int IP_FW_REASS = 8;
#pragma D binding "1.0" IP_FW_NAT64
inline int IP_FW_NAT64 = 9;
#pragma D binding "1.0" ipfw_retcodes
inline string ipfw_retcodes[int ret] =
ret == IP_FW_PASS ? "PASS" :
ret == IP_FW_DENY ? "DENY" :
ret == IP_FW_DIVERT ? "DIVERT" :
ret == IP_FW_TEE ? "TEE" :
ret == IP_FW_DUMMYNET ? "DUMMYNET" :
ret == IP_FW_NETGRAPH ? "NETGRAPH" :
ret == IP_FW_NGTEE ? "NGTEE" :
ret == IP_FW_NAT ? "NAT" :
ret == IP_FW_REASS ? "REASS" :
ret == IP_FW_NAT64 ? "NAT64" :
"<unknown>";
/* ip_fw_args flags */
#pragma D binding "1.0" IPFW_ARGS_ETHER
inline int IPFW_ARGS_ETHER = 0x00010000; /* valid ethernet header */
#pragma D binding "1.0" IPFW_ARGS_NH4
inline int IPFW_ARGS_NH4 = 0x00020000; /* IPv4 next hop in hopstore */
#pragma D binding "1.0" IPFW_ARGS_NH6
inline int IPFW_ARGS_NH6 = 0x00040000; /* IPv6 next hop in hopstore */
#pragma D binding "1.0" IPFW_ARGS_NH4PTR
inline int IPFW_ARGS_NH4PTR = 0x00080000; /* IPv4 next hop in next_hop */
#pragma D binding "1.0" IPFW_ARGS_NH6PTR
inline int IPFW_ARGS_NH6PTR = 0x00100000; /* IPv6 next hop in next_hop6 */
#pragma D binding "1.0" IPFW_ARGS_REF
inline int IPFW_ARGS_REF = 0x00200000; /* valid ipfw_rule_ref */
#pragma D binding "1.0" IPFW_ARGS_IN
inline int IPFW_ARGS_IN = 0x00400000; /* called on input */
#pragma D binding "1.0" IPFW_ARGS_OUT
inline int IPFW_ARGS_OUT = 0x00800000; /* called on output */
#pragma D binding "1.0" IPFW_ARGS_IP4
inline int IPFW_ARGS_IP4 = 0x01000000; /* belongs to v4 ISR */
#pragma D binding "1.0" IPFW_ARGS_IP6
inline int IPFW_ARGS_IP6 = 0x02000000; /* belongs to v6 ISR */
#pragma D binding "1.0" IPFW_ARGS_DROP
inline int IPFW_ARGS_DROP = 0x04000000; /* drop it (dummynet) */
#pragma D binding "1.0" IPFW_ARGS_LENMASK
inline int IPFW_ARGS_LENMASK = 0x0000ffff; /* length of data in *mem */
/* ipfw_rule_ref.info */
#pragma D binding "1.0" IPFW_INFO_MASK
inline int IPFW_INFO_MASK = 0x0000ffff;
#pragma D binding "1.0" IPFW_INFO_OUT
inline int IPFW_INFO_OUT = 0x00000000;
#pragma D binding "1.0" IPFW_INFO_IN
inline int IPFW_INFO_IN = 0x80000000;
#pragma D binding "1.0" IPFW_ONEPASS
inline int IPFW_ONEPASS = 0x40000000;
#pragma D binding "1.0" IPFW_IS_MASK
inline int IPFW_IS_MASK = 0x30000000;
#pragma D binding "1.0" IPFW_IS_DIVERT
inline int IPFW_IS_DIVERT = 0x20000000;
#pragma D binding "1.0" IPFW_IS_DUMMYNET
inline int IPFW_IS_DUMMYNET = 0x10000000;
#pragma D binding "1.0" IPFW_IS_PIPE
inline int IPFW_IS_PIPE = 0x08000000;
typedef struct ipfw_match_info {
uint32_t flags;
struct mbuf *m;
void *mem;
struct inpcb *inp;
struct ifnet *ifp;
struct ip *ipp;
struct ip6_hdr *ip6p;
/* flow id */
uint8_t addr_type;
uint8_t proto;
uint8_t proto_flags;
uint16_t fib; /* XXX */
in_addr_t dst_ip; /* in network byte order */
in_addr_t src_ip; /* in network byte order */
struct in6_addr dst_ip6;
struct in6_addr src_ip6;
uint16_t dst_port; /* in host byte order */
uint16_t src_port; /* in host byte order */
uint32_t flowid; /* IPv6 flowid */
uint32_t extra;
/* ipfw_rule_ref */
uint32_t slot;
uint32_t rulenum;
uint32_t rule_id;
uint32_t chain_id;
uint32_t match_info;
} ipfw_match_info_t;
#pragma D binding "1.0" translator
translator ipfw_match_info_t < struct ip_fw_args *p > {
flags = p->flags;
m = (p->flags & IPFW_ARGS_LENMASK) ? NULL : p->m;
mem = (p->flags & IPFW_ARGS_LENMASK) ? p->mem : NULL;
inp = p->inp;
ifp = p->ifp;
/* Initialize IP pointer corresponding to addr_type */
ipp = (p->flags & IPFW_ARGS_IP4) ?
(p->flags & IPFW_ARGS_LENMASK) ? (struct ip *)p->mem :
(p->m != NULL) ? (struct ip *)p->m->m_data : NULL : NULL;
ip6p = (p->flags & IPFW_ARGS_IP6) ?
(p->flags & IPFW_ARGS_LENMASK) ? (struct ip6_hdr *)p->mem :
(p->m != NULL) ? (struct ip6_hdr *)p->m->m_data : NULL : NULL;
/* fill f_id fields */
addr_type = p->f_id.addr_type;
proto = p->f_id.proto;
proto_flags = p->f_id._flags;
/* f_id.fib keeps truncated fibnum, use mbuf's fibnum if possible */
fib = p->m != NULL ? p->m->m_pkthdr.fibnum : p->f_id.fib;
/*
* ipfw_chk() keeps IPv4 addresses in host byte order. But for
* dtrace script it is useful to have them in network byte order,
* because inet_ntoa() uses address in network byte order.
*/
dst_ip = htonl(p->f_id.dst_ip);
src_ip = htonl(p->f_id.src_ip);
dst_ip6 = p->f_id.dst_ip6;
src_ip6 = p->f_id.src_ip6;
dst_port = p->f_id.dst_port;
src_port = p->f_id.src_port;
flowid = p->f_id.flow_id6;
extra = p->f_id.extra;
/* ipfw_rule_ref */
slot = (p->flags & IPFW_ARGS_REF) ? p->rule.slot : 0;
rulenum = (p->flags & IPFW_ARGS_REF) ? p->rule.rulenum : 0;
rule_id = (p->flags & IPFW_ARGS_REF) ? p->rule.rule_id : 0;
chain_id = (p->flags & IPFW_ARGS_REF) ? p->rule.chain_id : 0;
match_info = (p->flags & IPFW_ARGS_REF) ? p->rule.info : 0;
};
typedef struct ipfw_rule_info {
uint16_t act_ofs;
uint16_t cmd_len;
uint32_t rulenum;
uint8_t flags;
uint8_t set;
uint32_t rule_id;
uint32_t cached_id;
uint32_t cached_pos;
uint32_t refcnt;
} ipfw_rule_info_t;
#pragma D binding "1.0" translator
translator ipfw_rule_info_t < struct ip_fw *r > {
act_ofs = r->act_ofs;
cmd_len = r->cmd_len;
rulenum = r->rulenum;
flags = r->flags;
set = r->set;
rule_id = r->id;
cached_id = r->cached_id;
cached_pos = r->cached_pos;
refcnt = r->refcnt;
};

View File

@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h> #include <sys/proc.h>
#include <sys/rwlock.h> #include <sys/rwlock.h>
#include <sys/rmlock.h> #include <sys/rmlock.h>
#include <sys/sdt.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/socketvar.h> #include <sys/socketvar.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
@ -106,6 +107,18 @@ __FBSDID("$FreeBSD$");
#include <security/mac/mac_framework.h> #include <security/mac/mac_framework.h>
#endif #endif
#define IPFW_PROBE(probe, arg0, arg1, arg2, arg3, arg4, arg5) \
SDT_PROBE6(ipfw, , , probe, arg0, arg1, arg2, arg3, arg4, arg5)
SDT_PROVIDER_DEFINE(ipfw);
SDT_PROBE_DEFINE6(ipfw, , , rule__matched,
"int", /* retval */
"int", /* af */
"void *", /* src addr */
"void *", /* dst addr */
"struct ip_fw_args *", /* args */
"struct ip_fw *" /* rule */);
/* /*
* static variables followed by global ones. * static variables followed by global ones.
* All ipfw global variables are here. * All ipfw global variables are here.
@ -3237,6 +3250,13 @@ do { \
struct ip_fw *rule = chain->map[f_pos]; struct ip_fw *rule = chain->map[f_pos];
/* Update statistics */ /* Update statistics */
IPFW_INC_RULE_COUNTER(rule, pktlen); IPFW_INC_RULE_COUNTER(rule, pktlen);
IPFW_PROBE(rule__matched, retval,
is_ipv4 ? AF_INET : AF_INET6,
is_ipv4 ? (uintptr_t)&src_ip :
(uintptr_t)&args->f_id.src_ip6,
is_ipv4 ? (uintptr_t)&dst_ip :
(uintptr_t)&args->f_id.dst_ip6,
args, rule);
} else { } else {
retval = IP_FW_DENY; retval = IP_FW_DENY;
printf("ipfw: ouch!, skip past end of rules, denying packet\n"); printf("ipfw: ouch!, skip past end of rules, denying packet\n");