Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Sep 2003 07:18:28 -0700
From:      Luigi Rizzo <rizzo@icir.org>
To:        ipfw@freebsd.org
Subject:   ipfw2 logging through tcpdump (almost done!)
Message-ID:  <20030915071828.B79168@xorpc.icir.org>
In-Reply-To: <20030915041525.B77950@xorpc.icir.org>; from rizzo@icir.org on Mon, Sep 15, 2003 at 04:15:26AM -0700
References:  <20030915041525.B77950@xorpc.icir.org>

next in thread | previous in thread | raw e-mail | index | archive | help
well it was almost as easy as i expected...
a patch for netinet/ip_fw2.c is at the end of this message.

Basically, if net.inet.ip.fw.verbose=1 the log command works as
ususal, whereas if it is 0 you can run

	tcpdump -i ipfw0 ...

and catch packets that would normally go to the log output.
There are still some minor glitches in that "ipfw0" looks
weird, i cannot figure out what am i missing in the initialization
to make it look as an ethernet interface, and also what needs to be
done to assign an address to it so that it works ok with
things like trafshow. Suggestions welcome...

	cheers
	luigi


On Mon, Sep 15, 2003 at 04:15:26AM -0700, Luigi Rizzo wrote:
> having recently played a bit with largish ipfw configurations,
> i would have really liked to have a more flexible logging facility
> for ipfw.
> 
> It occurred to me that one way could be to extend the ipfw2
> "log" option to optionally pass to a bpf listener a copy of the packets
> selected by the ipfw rule (maybe with some tag showing the rule
> they come from) so that one can run a tcpdump on that stream when
> detailed analysis is required, and have essentially zero overhead in
> other cases.
> 
> What do people think ? Implementation should be almost trivial,
> amounting to creating a fake struct ifnet for ipfw2 (suitably
> initialized to give it a name and pretend it is up and running and
> has an address assigned), calling
> bpfattach() on it, and then implement an ipfw2 instruction
> which always succeeds and, if there is a listener, passes
> calls bpf_mtap on the mbuf.
> 
> Does this make sense ? And, any idea on how to tag the packet with
> a rule number in a way that tcpdump can filter (yes, i am looking
> for dirty hacks here...)
> 
> (and speaking of dirty hacks, of course layer3 packets can be
> easily prepended with a fake MAC header which includes the matching
> rule number in the ether src/dst addresses, but this trick won't
> work for layer2 packets, which we do care about).
> 
> 	cheers
> 	luigi


Index: netinet/ip_fw2.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_fw2.c,v
retrieving revision 1.6.2.16
diff -u -r1.6.2.16 ip_fw2.c
--- netinet/ip_fw2.c	17 Jul 2003 06:03:39 -0000	1.6.2.16
+++ netinet/ip_fw2.c	15 Sep 2003 14:49:30 -0000
@@ -55,6 +55,8 @@
 #include <sys/syslog.h>
 #include <sys/ucred.h>
 #include <net/if.h>
+#include <net/if_types.h>	/* for IFT_ETHER */
+#include <net/bpf.h>	/* for BPF */
 #include <net/route.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
@@ -234,6 +236,11 @@
 ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL;	/* hook into dummynet */
 
 /*
+ * hook to attach to bpf
+ */
+static struct ifnet ifn;
+
+/*
  * This macro maps an ip pointer into a layer3 header pointer of type T
  */
 #define	L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
@@ -1812,6 +1819,26 @@
 			case O_LOG:
 				if (fw_verbose)
 					ipfw_log(f, hlen, args->eh, m, oif);
+				else if (ifn.if_bpf != NULL) {
+                /* This kludge is OK; BPF treats the "mbuf" as read-only */
+				    struct m_hdr mh;
+				    mh.mh_next = m;
+				    mh.mh_len = ETHER_HDR_LEN;
+				    if (args->eh)	/* layer2, complete */
+					mh.mh_data = (char *)args->eh;
+				    else {
+					/* fake header and restore wire format*/
+					mh.mh_data = "DDDDDDSSSSSS\x08\x00";
+					ip->ip_off = ntohs(ip->ip_off);
+					ip->ip_len = ntohs(ip->ip_len);
+				    }
+				    bpf_mtap(&ifn, (struct mbuf *)&mh);
+				    if (args->eh == NULL) {
+					/* restore IP format */
+					ip->ip_off = htons(ip->ip_off);
+					ip->ip_len = htons(ip->ip_len);
+				    }
+				}
 				match = 1;
 				break;
 
@@ -2767,11 +2794,29 @@
 	ipfw_timeout_h = timeout(ipfw_tick, NULL, dyn_keepalive_period*hz);
 }
 
+static int
+ipfw_ifnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
+{
+	return 0;	/* always succeed... */
+}
+
 static void
 ipfw_init(void)
 {
 	struct ip_fw default_rule;
 
+/** bpf code **/
+	ifn.if_name = "ipfw";
+	ifn.if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST;
+	ifn.if_ioctl = ipfw_ifnet_ioctl;	/* getaddr */
+	ifn.if_type = IFT_ETHER;
+	ifn.if_addrlen = 6;
+	ifn.if_hdrlen = 14;
+	if_attach(&ifn);
+	bpfattach(&ifn, DLT_EN10MB, sizeof(struct ether_header));
+
+/** end bpf code **/
+
 	ip_fw_chk_ptr = ipfw_chk;
 	ip_fw_ctl_ptr = ipfw_ctl;
 	layer3_chain = NULL;
@@ -2844,6 +2889,7 @@
 		err = EBUSY;
 #else
                 s = splimp();
+		ether_ifdetach(&ifn, 1 /* we want bpf */);
 		untimeout(ipfw_tick, NULL, ipfw_timeout_h);
 		ip_fw_chk_ptr = NULL;
 		ip_fw_ctl_ptr = NULL;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20030915071828.B79168>