ipfw2 logs to bpf (was Re: cvs commit: src/sbin/ipfw ipfw2.c...)

Luigi Rizzo rizzo at icir.org
Tue Dec 13 06:15:10 PST 2005


talking about ipfw2, a couple of years ago i posted some code for 4.x
to let ipfw2 "log" packets to a pseudo interface called /dev/ipfw0 so
that people in need of detailed logging could just get it from
there through tcpdump or whatever.

If someone is interested, here it is a copy of the message and patch.
It is very very trivial, so i don't expect to require a lot of
porting work to be adapted to HEAD and more recent versions of
FreeBSD.

	cheers
	luigi
--------------------------------------------------------

Basically, if net.inet.ip.fw.verbose=1 the log command works as
usual, 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;
_______________________________________________
freebsd-ipfw at freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-ipfw
To unsubscribe, send any mail to "freebsd-ipfw-unsubscribe at freebsd.org"


More information about the cvs-all mailing list