ipfw3: logging feature
authorBill Yuan <bycn82@leaf.dragonflybsd.org>
Thu, 17 Dec 2015 09:06:54 +0000 (09:06 +0000)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 17 Dec 2015 17:16:42 +0000 (09:16 -0800)
ifconfig ipfw0 create
ipfw3 add allow log 0 icmp

support max=9 pseudo ipfw interfaces.
the logged packets header will be 0x41 & 0x42.
the logged frames can keep the original MAC.

sbin/ipfw3/ipfw3.c
sys/net/ipfw3/Makefile
sys/net/ipfw3/ip_fw3.c
sys/net/ipfw3/ip_fw3_log.c [new file with mode: 0644]
sys/net/ipfw3/ip_fw3_log.h [new file with mode: 0644]

index cad8f5d..8271e7f 100644 (file)
@@ -287,6 +287,14 @@ parse_accept(ipfw_insn **cmd, int *ac, char **av[])
        (*cmd)->module = MODULE_BASIC_ID;
        (*cmd)->len = (*cmd)->len|LEN_OF_IPFWINSN;
        NEXT_ARG1;
+       if (!strncmp(**av, "log", strlen(**av))) {
+               (*cmd)->arg3 = 1;
+               NEXT_ARG1;
+               if (isdigit(***av)) {
+                       (*cmd)->arg1 = strtoul(**av, NULL, 10);
+                       NEXT_ARG1;
+               }
+       }
 }
 
 void
@@ -296,18 +304,32 @@ parse_deny(ipfw_insn **cmd, int *ac, char **av[])
        (*cmd)->module = MODULE_BASIC_ID;
        (*cmd)->len = (*cmd)->len|LEN_OF_IPFWINSN;
        NEXT_ARG1;
+       if (!strncmp(**av, "log", strlen(**av))) {
+               (*cmd)->arg3 = 1;
+               NEXT_ARG1;
+               if (isdigit(***av)) {
+                       (*cmd)->arg1 = strtoul(**av, NULL, 10);
+                       NEXT_ARG1;
+               }
+       }
 }
 
 void
 show_accept(ipfw_insn *cmd, int show_or)
 {
        printf(" allow");
+       if (cmd->arg3) {
+               printf(" log %d", cmd->arg1);
+       }
 }
 
 void
 show_deny(ipfw_insn *cmd, int show_or)
 {
        printf(" deny");
+       if (cmd->arg3) {
+               printf(" log %d", cmd->arg1);
+       }
 }
 
 static void
index daa08e7..b3503d8 100644 (file)
@@ -3,6 +3,7 @@
 
 KMOD=  ipfw3
 SRCS=  ip_fw3.c
+SRCS+= ip_fw3_log.c ip_fw3_log.h
 SRCS+=  ip_fw3_table.c ip_fw3_table.h
 SRCS+= opt_ipfw.h opt_inet.h
 
@@ -17,7 +18,6 @@ opt_ipfw.h:
        #
        # If you want it verbose
        #echo '#define IPFIREWALL_VERBOSE 1' >> ${.OBJDIR}/${.TARGET}
-       #echo '#define IPFIREWALL_VERBOSE_LIMIT 100' >> ${.OBJDIR}/${.TARGET}
        #
        # If you want it to pass all packets by default
        #echo '#define IPFIREWALL_DEFAULT_TO_ACCEPT 1' >> ${.OBJDIR}/${.TARGET}
index 0772b8d..c48db19 100644 (file)
@@ -81,6 +81,7 @@
 #include <net/netmsg2.h>
 
 #include <net/ipfw3/ip_fw.h>
+#include <net/ipfw3/ip_fw3_log.h>
 #include <net/ipfw3/ip_fw3_table.h>
 #include <net/ipfw3_basic/ip_fw3_basic.h>
 #include <net/ipfw3_nat/ip_fw3_nat.h>
@@ -149,8 +150,7 @@ extern int ip_fw_loaded;
 static uint32_t static_count;  /* # of static rules */
 static uint32_t static_ioc_len;        /* bytes of static rules */
 static int ipfw_flushing;
-static int fw_verbose;
-static int verbose_limit;
+static int fw_verbose = 0;
 static int fw_debug;
 static int autoinc_step = IPFW_AUTOINC_STEP_DEF;
 
@@ -170,8 +170,6 @@ SYSCTL_INT(_net_inet_ip_fw3, OID_AUTO, debug, CTLFLAG_RW,
        &fw_debug, 0, "Enable printing of debug ip_fw statements");
 SYSCTL_INT(_net_inet_ip_fw3, OID_AUTO, verbose, CTLFLAG_RW,
        &fw_verbose, 0, "Log matches to ipfw rules");
-SYSCTL_INT(_net_inet_ip_fw3, OID_AUTO, verbose_limit, CTLFLAG_RW,
-       &verbose_limit, 0, "Set upper limit of matches of ipfw rules logged");
 SYSCTL_INT(_net_inet_ip_fw3, OID_AUTO, static_count, CTLFLAG_RD,
        &static_count, 0, "Number of static rules");
 
@@ -272,6 +270,9 @@ check_accept(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
 {
        *cmd_val = IP_FW_PASS;
        *cmd_ctl = IP_FW_CTL_DONE;
+       if (cmd->arg3) {
+               ipfw_log((*args)->m, (*args)->eh, cmd->arg1);
+       }
 }
 
 void
@@ -280,6 +281,9 @@ check_deny(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
 {
        *cmd_val = IP_FW_DENY;
        *cmd_ctl = IP_FW_CTL_DONE;
+       if (cmd->arg3) {
+               ipfw_log((*args)->m, (*args)->eh, cmd->arg1);
+       }
 }
 
 void
@@ -2025,17 +2029,9 @@ ipfw_init_dispatch(netmsg_t nmsg)
 
 #ifdef IPFIREWALL_VERBOSE
        fw_verbose = 1;
-#endif
-#ifdef IPFIREWALL_VERBOSE_LIMIT
-       verbose_limit = IPFIREWALL_VERBOSE_LIMIT;
 #endif
        if (fw_verbose == 0) {
                kprintf("disabled ");
-       } else if (verbose_limit == 0) {
-               kprintf("unlimited ");
-       } else {
-               kprintf("limited to %d packets/entry by default ",
-                               verbose_limit);
        }
        kprintf("\n");
        ip_fw3_loaded = 1;
@@ -2051,6 +2047,7 @@ ipfw3_init(void)
        struct netmsg_base smsg;
        int error;
 
+       ipfw3_log_modevent(MOD_LOAD);
        init_module();
        netmsg_init(&smsg, NULL, &curthread->td_msgport,
                        0, ipfw_init_dispatch);
@@ -2095,6 +2092,8 @@ static int
 ipfw3_fini(void)
 {
        struct netmsg_base smsg;
+
+       ipfw3_log_modevent(MOD_UNLOAD);
        netmsg_init(&smsg, NULL, &curthread->td_msgport,
                        0, ipfw_fini_dispatch);
        return lwkt_domsg(IPFW_CFGPORT, &smsg.lmsg, 0);
diff --git a/sys/net/ipfw3/ip_fw3_log.c b/sys/net/ipfw3/ip_fw3_log.c
new file mode 100644 (file)
index 0000000..d7bc46b
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
+ *
+ * Copyright (c) 2015 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Bill Yuan <bycn82@leaf.dragonflybsd.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.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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.
+ */
+
+#include "opt_inet.h"
+#ifndef INET
+#error IPFIREWALL3 requires INET.
+#endif
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/ucred.h>
+#include <sys/lock.h>
+#include <sys/lock.h>
+#include <net/ethernet.h>      /* for ETHERTYPE_IP */
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ifq_var.h>
+#include <net/if_clone.h>
+#include <net/if_types.h>      /* for IFT_PFLOG */
+#include <net/bpf.h>           /* for BPF */
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp_var.h>
+#include <netinet/udp.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet6/in6_var.h>  /* ip6_sprintf() */
+#endif
+
+#include <net/ipfw3/ip_fw.h>
+#include <net/ipfw3/ip_fw3_log.h>
+
+extern int fw_verbose;
+extern struct if_clone *if_clone_lookup(const char *, int *);
+
+static const char ipfw_log_ifname[] = "ipfw";
+static int log_if_count;
+struct ifnet *log_if_table[LOG_IF_MAX];
+struct lock log_if_lock;
+
+
+u_char fake_eh[14] = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+                       0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x08, 0x00};
+
+static const u_char ipfwbroadcastaddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+#define        LOGIF_LOCK_INIT(x) lockinit(&log_if_lock, "fw3log_lk", 0, LK_CANRECURSE)
+#define        LOGIF_LOCK_DESTROY(x) lockuninit(&log_if_lock)
+#define        LOGIF_RLOCK(x) lockmgr(&log_if_lock, LK_SHARED)
+#define        LOGIF_RUNLOCK(x) lockmgr(&log_if_lock, LK_RELEASE)
+#define        LOGIF_WLOCK(x) lockmgr(&log_if_lock, LK_EXCLUSIVE)
+#define        LOGIF_WUNLOCK(x) lockmgr(&log_if_lock, LK_RELEASE)
+
+
+/* we use this dummy function for all ifnet callbacks */
+static int
+log_dummy(struct ifnet *ifp, u_long cmd, caddr_t addr, struct ucred *uc)
+{
+       return EINVAL;
+}
+
+static int
+ipfw_log_output(struct ifnet *ifp, struct mbuf *m,
+               struct sockaddr *dst, struct rtentry *rtent)
+{
+       if (m != NULL) {
+               m_freem(m);
+       }
+       return EINVAL;
+}
+
+static void
+ipfw_log_start(struct ifnet* ifp, struct ifaltq_subque *subque)
+{
+}
+
+/*
+ * bpf_mtap into the ipfw interface.
+ * eh == NULL when mbuf is a packet, then use the fake_eh
+ * the ip_len need to be twisted before and after bpf copy.
+ */
+void
+ipfw_log(struct mbuf *m, struct ether_header *eh, uint16_t id)
+{
+       struct ifnet *the_if = NULL;
+
+       if (fw_verbose) {
+#ifndef WITHOUT_BPF
+               LOGIF_RLOCK();
+               the_if = log_if_table[id];
+               if (the_if == NULL || the_if->if_bpf == NULL) {
+                       LOGIF_RUNLOCK();
+                       return;
+               }
+               if (eh != NULL) {
+                       bpf_gettoken();
+                       bpf_mtap_hdr(the_if->if_bpf, (caddr_t)eh,
+                                       ETHER_HDR_LEN, m, 0);
+                       bpf_reltoken();
+
+               } else {
+                       struct ip *ip;
+                       ip = mtod(m, struct ip *);
+                       /* twist the ip_len for the bpf copy */
+                       ip->ip_len =htons(ip->ip_len);
+
+                       bpf_gettoken();
+                       bpf_mtap_hdr(the_if->if_bpf, (caddr_t)fake_eh,
+                                       ETHER_HDR_LEN, m, 0);
+                       bpf_reltoken();
+                       ip->ip_len =ntohs(ip->ip_len);
+
+               }
+               LOGIF_RUNLOCK();
+#endif /* !WITHOUT_BPF */
+       }
+}
+
+static int
+ipfw_log_clone_create(struct if_clone *ifc, int unit, caddr_t param __unused)
+{
+       struct ifnet *ifp;
+
+       if (unit < 0 || unit >= LOG_IF_MAX) {
+               return EINVAL;
+       }
+       if (log_if_table[unit] != NULL) {
+               return EINVAL;
+       }
+
+       ifp = if_alloc(IFT_PFLOG);
+       if_initname(ifp, ipfw_log_ifname, unit);
+       ifq_set_maxlen(&ifp->if_snd, ifqmaxlen);
+       ifq_set_ready(&ifp->if_snd);
+
+       ifp->if_mtu = 65536;
+       ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_init = (void *)log_dummy;
+       ifp->if_ioctl = log_dummy;
+       ifp->if_start = ipfw_log_start;
+       ifp->if_output = ipfw_log_output;
+       ifp->if_addrlen = 6;
+       ifp->if_hdrlen = 14;
+       ifp->if_broadcastaddr = ipfwbroadcastaddr;
+       ifp->if_baudrate = IF_Mbps(10);
+
+       LOGIF_WLOCK();
+       log_if_table[unit] = ifp;
+       log_if_count++;
+       if_attach(ifp, NULL);
+       bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN);
+       LOGIF_WUNLOCK();
+
+       return (0);
+}
+
+static int
+ipfw_log_clone_destroy(struct ifnet *ifp)
+{
+       int unit;
+
+       if (ifp == NULL)
+               return (0);
+
+       unit = ifp->if_dunit;
+       if (unit < 0 || unit >= LOG_IF_MAX) {
+               return EINVAL;
+       }
+       if (log_if_table[unit] == NULL) {
+               return EINVAL;
+       }
+       LOGIF_WLOCK();
+       log_if_table[unit] = NULL;
+       bpfdetach(ifp);
+       if_detach(ifp);
+       if_free(ifp);
+       log_if_count--;
+       LOGIF_WUNLOCK();
+
+       return (0);
+}
+
+static eventhandler_tag ipfw_log_ifdetach_cookie;
+static struct if_clone ipfw_log_cloner = IF_CLONE_INITIALIZER(ipfw_log_ifname,
+               ipfw_log_clone_create, ipfw_log_clone_destroy, 0, 9);
+
+
+void ipfw3_log_modevent(int type){
+       struct ifnet *tmpif;
+       int i;
+
+       switch (type) {
+       case MOD_LOAD:
+               LOGIF_LOCK_INIT();
+               log_if_count = 0;
+               if_clone_attach(&ipfw_log_cloner);
+               ipfw_log_ifdetach_cookie =
+                       EVENTHANDLER_REGISTER(ifnet_detach_event,
+                               ipfw_log_clone_destroy, &ipfw_log_cloner,
+                               EVENTHANDLER_PRI_ANY);
+               break;
+       case MOD_UNLOAD:
+               EVENTHANDLER_DEREGISTER(ifnet_detach_event,
+                                       ipfw_log_ifdetach_cookie);
+               if_clone_detach(&ipfw_log_cloner);
+               for(i = 0; log_if_count > 0 && i < LOG_IF_MAX; i++){
+                       tmpif = log_if_table[i];
+                       if (tmpif != NULL) {
+                               ipfw_log_clone_destroy(tmpif);
+                       }
+               }
+               LOGIF_LOCK_DESTROY();
+               break;
+
+       default:
+               break;
+       }
+}
+
+/* end of file */
diff --git a/sys/net/ipfw3/ip_fw3_log.h b/sys/net/ipfw3/ip_fw3_log.h
new file mode 100644 (file)
index 0000000..d7f6227
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
+ *
+ * Copyright (c) 2015 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Bill Yuan <bycn82@leaf.dragonflybsd.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.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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.
+ */
+#ifndef _IP_FW3_LOG_H_
+#define _IP_FW3_LOG_H_
+
+#define LOG_IF_MAX     10
+
+void ipfw3_log_modevent(int type);
+
+void
+ipfw_log(struct mbuf *m, struct ether_header *eh, uint16_t id);
+
+#endif