summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authordm <dm@cvs.openbsd.org>1996-01-07 02:34:41 +0000
committerdm <dm@cvs.openbsd.org>1996-01-07 02:34:41 +0000
commit01b9b71d86a5edcc543a88b2d407927fa52c042d (patch)
tree878168b4effcec4e50c243cfd1095656af14f4db /sbin
parent2defc765aa92d65e239f5b4d36582850fd58b7da (diff)
from beurton@fnet.fr: Darren Reed's IP filter
Diffstat (limited to 'sbin')
-rw-r--r--sbin/Makefile7
-rw-r--r--sbin/ipf/Makefile7
-rw-r--r--sbin/ipf/ipf.160
-rw-r--r--sbin/ipf/ipf.4154
-rw-r--r--sbin/ipf/ipf.5259
-rw-r--r--sbin/ipf/ipf.c289
-rw-r--r--sbin/ipf/ipf.h43
-rw-r--r--sbin/ipf/opt.c133
-rw-r--r--sbin/ipf/parse.c958
-rw-r--r--sbin/ipfstat/Makefile9
-rw-r--r--sbin/ipfstat/fils.c212
-rw-r--r--sbin/ipfstat/ipfstat.848
-rw-r--r--sbin/ipfstat/kmem.c66
-rw-r--r--sbin/ipfstat/kmem.h11
14 files changed, 2253 insertions, 3 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
index 0679404a971..a4bf43886ce 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -4,9 +4,10 @@
# Not ported: XNSrouted enpload scsiformat startslip
# Missing: icheck ncheck
-SUBDIR= badsect disklabel ccdconfig dmesg fastboot ifconfig init mknod \
- modload modunload mount mountd nfsd nfsiod nologin ping quotacheck \
- reboot route routed savecore shutdown slattach swapon ttyflags umount
+SUBDIR= badsect disklabel ccdconfig dmesg fastboot ifconfig init ipf \
+ ipfstat mknod modload modunload mount mountd nfsd nfsiod nologin \
+ ping quotacheck reboot route routed savecore shutdown slattach \
+ swapon ttyflags umount
# support for various file systems
SUBDIR+= mount_ados
diff --git a/sbin/ipf/Makefile b/sbin/ipf/Makefile
new file mode 100644
index 00000000000..32e77eb18fa
--- /dev/null
+++ b/sbin/ipf/Makefile
@@ -0,0 +1,7 @@
+PROG= ipf
+MAN= ipf.1 ipf.4 ipf.5
+SRCS= ipf.c parse.c opt.c
+CFLAGS+=-DIPL_NAME=\"/dev/ipl\"
+
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipf/ipf.1 b/sbin/ipf/ipf.1
new file mode 100644
index 00000000000..1a4abc8bf44
--- /dev/null
+++ b/sbin/ipf/ipf.1
@@ -0,0 +1,60 @@
+.TH IPF 1
+.SH NAME
+ipf - alters packet filtering lists for IP packet input and ouput
+.SH SYNOPSIS
+ipf [-AEDIsnovdr] [-F <i|o|a>] -f <\fIfilename\fP>
+[ -f <\fIfilename\fP> [...]]
+.SH DESCRIPTION
+.PP
+\fBipf\fP opens the filenames listed (treating "-" as stdin) and parses the
+file for a set of rules which are to be added or removed from the packet
+filter rule set.
+.PP
+Each rule processed by \fBipf\fP
+is added to the kernels internal lists if there are no parsing problems.
+Rules are added to the end of the internal lists, matching the order in
+which they appear when given to \fBipf\fP.
+.SH OPTIONS
+.IP -A
+set the list to make changes to the active list (default).
+.IP -E
+Enable the filter (if disabled). Not effective for loadable kernel versions.
+.IP -D
+Disable the filter (if enabled). Not effective for loadable kernel versions.
+.IP -F
+this option specifies which filter list to flush. The parameter should
+either be "i" (input), "o" (output) or "a" (remove all filter rules).
+Either a single letter or an entire word starting with the appropriate
+letter maybe used. This option maybe before, or after, any other with
+the order on the command line being that used to execute options.
+.IP -d
+turn debug mode on. Causes a hexdump of filter rules to be generated as
+it processes each one.
+.IP -f
+this option specifies which files
+\fBipf\fP should use to get input from for modifying the pack filter rule
+lists.
+.IP -I
+set the list to make changes to the inactive list.
+.IP -n
+This flag (no-change) prevents \fBipf\fP from actually making any ioctl
+calls or doing anything which would alter the currently running kernel.
+.IP -o
+Force rules by default to be added/deleted to/from the output list, rather
+than the (default) input list.
+.IP -s
+swap the active filter list in use to be the "other" one.
+.IP -r
+remove matching filter rules rather than add them to the internal lists
+.IP -v
+turn verbose mode on. Displays information relating to rule processing.
+.DT
+.SH SEE ALSO
+ipfstat(1), ipftest(1), ipf(5)
+.SH DIAGNOSTICS
+.PP
+Needs to be run as root for the packet filtering lists to actually
+be affected inside the kernel.
+.SH BUGS
+.PP
+If you find any, please send email to me at darrenr@arbld.unimelb.edu.au
diff --git a/sbin/ipf/ipf.4 b/sbin/ipf/ipf.4
new file mode 100644
index 00000000000..f9c65e77c32
--- /dev/null
+++ b/sbin/ipf/ipf.4
@@ -0,0 +1,154 @@
+.TH IPF 4
+.SH NAME
+ipf - packet filtering kernel interface
+.SH SYNOPSIS
+#include <sys/ip_fil.h>
+.SH IOCTLS
+.PP
+To add and delete rules to the filter list, three 'basic' ioctls are provided
+for use. The ioctl's are called as:
+.LP
+.nf
+ ioctl(fd, SIOCADDFR, struct frentry *)
+ ioctl(fd, SIOCDELFR, struct frentry *)
+ ioctl(fd, SIOCIPFFL, int *)
+.fi
+.PP
+However, the full complement is as follows:
+.LP
+.nf
+ ioctl(fd, SIOCADAFR, struct frentry *) (same as SUICADDFR)
+ ioctl(fd, SIOCRMAFR, struct frentry *) (same as SUICDELFR)
+ ioctl(fd, SIOCADIFR, struct frentry *)
+ ioctl(fd, SIOCRMIFR, struct frentry *)
+ ioctl(fd, SIOCINAFR, struct frentry *)
+ ioctl(fd, SIOCINIFR, struct frentry *)
+ ioctl(fd, SIOCIPFFL, int *)
+.fi
+.PP
+The variations, SIOCADAFR vs SIOCADIFR, allow operation on the two lists,
+active and inactive, respectively. All of these ioctl's are implemented
+as being routing ioctls and thus the same rules for the various routing
+ioctls and the file descriptor are employed, mainly being that the fd must
+be that of the device associated with the module (ie /dev/ipl). In addition
+to this, these ioctl's will only succeed if made as root.
+.LP
+.PP
+The three groups of ioctls above perform adding rules to the end of the
+list (SIOCAD*), deletion of rules from any place in the list (SIOCRM*)
+and insertion of a rule into the list (SIOCIN*). The rule place into
+which it is inserted is stored in the "fr_hits" field, below.
+.LP
+.nf
+
+typedef struct frentry {
+ struct frentry *fr_next;
+ struct ifnet *fr_ifa;
+ u_int fr_hits;
+
+ /*
+ * Fields after this may not change whilst in the kernel.
+ */
+ struct ip fr_ip;
+ struct ip fr_mip;
+
+ u_short fr_icmpm; /* data for ICMP packets (mask) */
+ u_short fr_icmp;
+
+ char fr_tcpfm; /* tcp flags mask */
+ char fr_tcpf; /* tcp flags */
+
+ u_char fr_scmp; /* data for port comparisons */
+ u_char fr_dcmp;
+ u_short fr_dport;
+ u_short fr_sport;
+ u_short fr_stop; /* top port for <> and >< */
+ u_short fr_dtop; /* top port for <> and >< */
+ u_short fr_flags; /* per-rule flags && options */
+ char fr_ifname[IFNAMSIZ];
+} frentry_t;
+.fi
+.PP
+Flags which are recognised in fr_pass:
+.nf
+
+ FR_BLOCK 0x0001 /* do not allow packet to pass */
+ FR_PASS 0x0002 /* allow packet to pass */
+ FR_OUTQUE 0x0004 /* outgoing packets */
+ FR_QUICK 0x0008 /* quick-match and return */
+ FR_LOGP 0x0010 /* Log-pass */
+ FR_INQUE 0x0020 /* ingoing packets */
+ FR_LOGB 0x0040 /* Log-fail */
+ FR_LOG 0x0080 /* Log */
+ FR_RETRST 0x0100 /* return a TCP RST packet if blocked */
+ FR_OPTFRAG 0x0200 /* filter packets which are fragments */
+ FR_OPTSHORT 0x0400 /* filter short TCP packets */
+ FR_RETICMP 0x0800 /* return an ICMP packet if blocked */
+ FR_TCPUDP 0x1000 /* TCP/UCP implied comparison involved */
+.fi
+.PP
+Values for fr_scomp and fr_dcomp (source and destination port value
+comparisons) :
+.LP
+.nf
+ FR_NONE 0
+ FR_EQUAL 1
+ FR_NEQUAL 2
+ FR_LESST 3
+ FR_GREATERT 4
+ FR_LESSTE 5
+ FR_GREATERTE 6
+ FR_OUTRANGE 7
+ FR_INRANGE 8
+.fi
+.PP
+The third ioctl, SIOCIPFFL, flushes either the input filter list, the
+output filter list or both and it returns the number of filters removed
+from the list(s). The values which it will take and recognise are FR_INQUE
+and FR_OUTQUE (see above).
+
+\fBGeneral Logging Flags\fP
+There are two flags which can be set to log packets independantly of the
+rules used. These allow for packets which are either passed or blocked
+to be logged. To set (and clear)/get these flags, two ioctls are
+provided:
+.IP SIOCSETFF 16
+Takes an unsigned integer as the parameter. The flags are then set to
+those provided (clearing/setting all in one).
+.nf
+
+ FF_LOGPASS 1
+ FF_LOGBLOCK 2
+.fi
+.IP SIOCGETFF 16
+Takes a pointer to an unsigned integer as the parameter. A copy of the
+fags currently in used is copied to user space.
+.LP
+\fBFilter statistics\fP
+Statistics on the various operations performed by this package on packets
+is kept inside the kernel. These statistics apply to packets traversing
+through the kernel. To retrieve this structure, use this ioctl:
+.nf
+
+ ioctl(fd, SIOCGETFS, struct friostat *)
+
+struct friostat {
+ struct filterstats f_st[2];
+ struct frentry *f_fin;
+ struct frentry *f_fout;
+};
+
+struct filterstats {
+ u_long fr_pass; /* packets allowed */
+ u_long fr_block; /* packets denied */
+ u_long fr_ppkl; /* packets allowed and logged */
+ u_long fr_bpkl; /* packets denied and logged */
+ u_long fr_pkl; /* packets logged */
+ u_long fr_skip; /* packets to be logged but buffer full */
+};
+.fi
+.SH BUGS
+It would be nice if there were more flexibility when adding and deleting
+filter rules.
+.SH SEE ALSO
+ipfstat(1), ipf(1), ipf(5)
diff --git a/sbin/ipf/ipf.5 b/sbin/ipf/ipf.5
new file mode 100644
index 00000000000..2e70be16e92
--- /dev/null
+++ b/sbin/ipf/ipf.5
@@ -0,0 +1,259 @@
+.LP
+.TH IPF 5
+.SH NAME
+ipf - IP packet filtering format.
+.SH DESCRIPTION
+.PP
+A rule file for \fBipf\fP may have any name or even be stdin. As
+\fBipfstat\fP produces parseable rules as output when displaying the internal
+kernel filter lists, it is quite plausible to use its output to feed back
+into \fBipf\fP. Thus, to remove all filters on input packets, the following
+could be done:
+.nf
+
+\fC# ipfstat -i | ipf -rf -\fP
+.fi
+.PP
+The format used by \fBipf\fP for construction of filtering rules can be
+described using the following grammar in BNF:
+\fC
+.nf
+filter-rule = [ insert ] action in-out [ options ] [ tos ] [ ttl ]
+ [ proto ] [ ip ] .
+
+insert = "@" decnumber
+action = block | "pass" | log .
+in-out = "in" | "out" .
+options = [ log ] [ "quick" ] [ "on" interface-name] .
+tos = "tos" decnumber | "tos" hexnumber .
+ttl = "ttl" decnumber .
+proto = "proto" protocol .
+ip = srcdst [ flags ] [ with withopt ] [ icmp ] .
+
+block = "block" [ "return-icmp" ] [ "return-rst" ].
+log = "log" [ "body" ] .
+protocol = "tcp/udp" | "udp" | "tcp" | "icmp" | decnumber .
+srcdst = "all" | fromto .
+fromto = "from" object "to" object .
+
+object = addr [ port-comp | port-range ] .
+addr = "any" | nummask | host-name [ "mask" ipaddr | hexnumber ] .
+port-comp = "port" compare port-num .
+port-range = "port" port-num range port-num .
+flags = "flags" flag { flag } [ "/" flag { flag } ] .
+with = "with" | "and" .
+icmp = "icmp-type" icmp-type .
+
+nummask = host-name [ "/" decnumber ] .
+host-name = ipaddr | hostname | "any" .
+ipaddr = host-num "." host-num "." host-num "." host-num .
+host-num = digit [ digit [ digit ] ] .
+port-num = service-name | decnumber .
+
+withopt = [ "not" | "no" ] opttype [ withopt ] .
+opttype = "ipopts" | "short" | "frag" | "opt" ipopts .
+ipopts = "nop" | "rr" | "ts" | "security" | "sec-class" [ "=" seclvl ] |
+ "lsrr" | "satid" | "rsrr" .
+seclvl = "unclass" | "confid" | "reserv-1" | "reserv-2" | "reserv-3" |
+ "reserv-4" | "secret" | "topsecret" .
+icmp-type = "unreach" | "echo" | "echorep" | "squench" | "redir" |
+ "timex" | "paramprob" | "timest" | "timestrep" | "inforeq" |
+ "inforep" | "maskreq" | "maskrep" | decnumber .
+
+hexnumber = "0" "x" hexstring .
+hexstring = hexdigit [ hexstring ] .
+decnumber = digit [ decnumber ] .
+
+compare = "=" | "!=" | "<" | ">" | "<=" | ">=" | "eq" | "ne" | "lt" | "gt" |
+ "le" | "ge" .
+range = "<>" | "><" .
+hexdigit = digit | "a" | "b" | "c" | "d" | "e" | "f" .
+digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" .
+flag = "F" | "S" | "R" | "P" | "A" | "U" .
+.fi
+.PP
+The "briefest" valid rule is of the form:
+.nf
+
+ block in
+ pass in
+ log in
+.fi
+.PP
+These can also be written like:
+.nf
+ block in all pass in from any to any
+.fi
+.PP
+The action, one of either block, log or pass, indicates what to do with
+the packet if it matches the rest of the filter rule. Block indicates that
+the packet should be dropped here and not let through, log write the packet
+header to the \fBipl\fP packet logging psuedo-device (and has no further
+effect on validity of packet to be allowed through the filter) and pass which
+will allow the packet through. Each rule MUST have one of these three
+keywords.
+.PP
+In response to blocking a packet, the filter may be instructed to send a
+reply packet, either an ICMP unreachable (\fBreturn-icmp\fP)or a TCP
+"reset" (\fBreturn-rst\fP). An ICMP packet may be generated in response
+to any IP packet but a TCP reset may only be used with a rule which is
+being applied to TCP packets.
+.PP
+When a packet header is logged with the \fBlog\fP keyword, the optional
+\fBbody\fP keyword indicates that the first 128 bytes of the packet contents
+will be logged to the \fBipl\fP packet logging psuedo-device after the
+headers.
+.PP
+The next word must be either \fBin\fP or \fBout\fP. As each packet moving
+through the kernel is either an inbound packet or outbound, there is a
+requirement that each filter rule be explicitly stated as to which side of
+the IO it is to be used on.
+.PP
+The list of options is brief, and indeed all are optional. The presence
+of the \fBlog\fP option indicates, that should this be the last matching
+rule, the packet header will be written to the \fBipl\fP log. The \fBquick\fP
+option allows "short-cut" rules in order to speed up the filter. If a
+packet header matches a filter rule which is marked as \fBquick\fP, it will
+result in a quick-match and stop processing at this point. This is good for
+rules such as "block in quick from any to any with ipopts" which will match
+any packet with a non-standard header length (IP options present) and abort
+further processing, recording a match and also that the packet should be
+blocked. If this command is missing, the rule is taken to be a
+"fall-through" rule, meaning that the result of the match is used
+(block/pass) and that it will continue processing to see if there are any
+more matches. This allows for effects such as this:
+.LP
+.nf
+ block in from any to any port < 6000
+ pass in from any to any port >= 6000
+ block in from any to port > 6003
+.fi
+.PP
+which sets up the range 6000-6003 as being permitted and all others being
+denied. Another (easier) way to do the same is:
+.LP
+.nf
+ block in from any to any port 6000 <> 6003
+ pass in from any to any port 5999 >< 6004
+.fi
+.PP
+Note that both the "block" and "pass" are needed here to affect a result
+as a failed "block" does not imply a pass, only that the rule hasn't taken
+effect. To then allow ports < 1024, a rule such as:
+.LP
+.nf
+ pass in quick from any to any port < 1024
+.fi
+.PP
+would be needed before the first block. Expect to see a "between" operator
+as soon as I can work out how to fit in in.
+.PP
+The \fBon\fP command allows an interface name to be incorporated into the
+matching procedure. That it is a match and not actually associated with
+the interface itself is a result of the way this was implemented. Indeed,
+there is nothing to stop you using this with every rule if you so wish.
+If it is absent, the rule is taken to be applied to a packet regardless of
+the interface it is present on.
+.PP
+The \fBall\fP command is essentially an alias for "from any to any" with
+no other commands.
+.PP
+Using \fBtos\fP, packets with different service capabilities can be filtered
+upon. Individual service levels or combinations can be filtered upon. The
+value for the TOS mask can either be represented as a hex number or a
+decimal integer value.
+.PP
+Packets may also be selected by their \fBttl\fP value. The value given in
+the filter rule must exactly match that in the packet for a match to occur.
+This value can only be given as a decimal integer value.
+.PP
+The \fBproto\fP command allows a specific protocol to be matched against.
+All protocol names found in \fB/etc/protocols\fP are recognised and maybe
+used. However, the protocol may also be given as a DECIMAL number, allowing
+for rules to match your own protocols, or new ones which would out-date any
+attempted listing.
+.PP
+To match against BOTH source and destination addresses, the \fBfrom\fP and
+\fBto\fP commands are used. They both support a large variety of valid
+syntaxes, including the "x/y" format. There is a special case for the
+hostname \fBany\fP which is taken to be 0.0.0.0/0 and matches all IP numbers.
+If a \fBport\fP match is included, then it is only applied to TCP/UDP
+packets. If the \fBproto\fP command is left out, packets from both protocols
+are compared. The hostname may either be a valid hostname, from either the
+hosts file or DNS (depending on your configuration and library) or of the
+dotted numeric form. There is no special designation for networks but
+network names are recognised.
+.PP
+"x/y" indicates that a mask of y consecutive bits set is generated, starting
+with the MSB, so a value of 16 would give 0xffff0000.
+.PP
+"x mask y" indicates that the mask y is in dotted IP notation or a hexadecimal
+number of the form 0x12345678.
+.PP
+Only the presence of "any" has an implied mask, in all other situations,
+a hostname MUST be accompanied by a mask. It is possible to give "any" a
+hostmask, but in the context of this language, it is non-sensical.
+.PP
+When composing
+\fBport\fP comparisons, either the service name may be used or an integer
+port number.
+.PP
+The \fBwith\fP command is used to nominate irregular attributes that some
+packets ma have associated with them. Alternatively, the keyword \fBand\fP
+maybe used in place of \fBwith\fP. This is provided to make the rules more
+readable and serves no other purpose. To filter IP options, in general,
+use \fBipopts\fP. For more specific filtering on IP options, individual
+options can be listed. When listed, all those listed must be found in a
+packet to cause a match.
+.PP
+Before any option used after the \fBwith\fP keyword, the word \fBnot\fp
+maybe inserted to cause the filter rule to only match if the option(s) is
+not present.
+.PP
+The \fBflags\fP command is only effective for TCP filtering. Each of the
+letters possible represents one of the possible flags that can be set in the
+TCP header. The association is as follows:
+.LP
+.nf
+ F - FIN
+ S - SYN
+ R - RST
+ P - PUSH
+ A - ACK
+ U - URG
+.fi
+.PP
+The various flag symbols maybe used in combination, so that "SA" would
+represent a SYN-ACK combination present in a packet. There is nothing
+preventing combinations, such as "SFR". However, to guard against weird
+abberations, it is necessary to state which flags you are filtering against.
+To allow this, it is possible to set a mask indicating which TCP flags you
+wich to compare (ie those you deem significant). This is done by appending
+"/<flags>" to the set of TCP flags you wish to match against. eg:
+.LP
+.nf
+ ... flags S
+ # becomes "flags S/AUPRFS" and will match a
+ # packet with ONLY the SYN flag set.
+
+ ... flags SA
+ # becomes "flags SA/AUPRFS" and will match any
+ # packet with only the SYN and ACK flags set.
+
+ ... flags S/SA
+ # will match any packet with just the SYN flag set
+ # out of the SYN-ACK pair; the common "establish"
+ # keyword action. "S/SA" will NOT match a packet
+ # with BOTH SYN and ACK set, but WILL match "SFP".
+.fi
+.PP
+The last parameter set for the filter rule is the optional \fBicmp-type\fP.
+It is only effective when used with \fB"proto icmp"\fP and must NOT be used
+in conjuction with \fBflags\fP. There are a number of types which can be
+refered to by an abbreviation recognised by this language or the numbers
+with which they are associated can be used.
+.SH FILES
+/etc/services
+/etc/hosts
+.SH SEE ALSO
+ipf(1), ipftest(1)
diff --git a/sbin/ipf/ipf.c b/sbin/ipf/ipf.c
new file mode 100644
index 00000000000..12e8ff2caef
--- /dev/null
+++ b/sbin/ipf/ipf.c
@@ -0,0 +1,289 @@
+/*
+ * (C)opyright 1993,1994,1995 by Darren Reed.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and due credit is given
+ * to the original author and the contributors.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#if !defined(__SVR4) && !defined(__GNUC__)
+#include <strings.h>
+#endif
+#if !defined(__SVR4) && defined(__GNUC__)
+extern char *index();
+#endif
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <net/if.h>
+#include <netinet/ip.h>
+#include <netinet/ip_fil.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "ipf.h"
+
+#ifndef lint
+static char sccsid[] = "@(#)ipf.c 1.18 11/11/95 (C) 1993-1995 Darren Reed";
+#endif
+
+extern char *optarg;
+
+int opts = 0;
+
+static int fd = -1;
+static void procfile(), flushfilter(), set_state();
+static void packetlogon(), swapactive();
+
+int main(argc,argv)
+int argc;
+char *argv[];
+{
+ char c;
+
+ if ((fd = open(IPL_NAME, O_RDONLY)) == -1)
+ perror("open device");
+
+ while ((c = getopt(argc, argv, "AsInovdryf:F:l:EDZ")) != -1)
+ switch (c)
+ {
+ case 'E' :
+ set_state(1);
+ break;
+ case 'D' :
+ set_state(0);
+ break;
+ case 'A' :
+ opts &= ~OPT_INACTIVE;
+ break;
+ case 'd' :
+ opts |= OPT_DEBUG;
+ break;
+ case 'f' :
+ procfile(optarg);
+ break;
+ case 'F' :
+ flushfilter(optarg);
+ break;
+ case 'I' :
+ opts |= OPT_INACTIVE;
+ break;
+ case 'l' :
+ packetlogon(optarg);
+ break;
+ case 'n' :
+ opts |= OPT_DONOTHING;
+ break;
+ case 'o' :
+ opts |= OPT_OUTQUE;
+ break;
+ case 'r' :
+ opts |= OPT_REMOVE;
+ break;
+ case 's' :
+ swapactive();
+ break;
+ case 'v' :
+ opts |= OPT_VERBOSE;
+ break;
+#if defined(sun) && (defined(__SVR4) || defined(__svr4__))
+ case 'y' :
+ frsync();
+ break;
+#endif
+ case 'Z' :
+ zerostats();
+ break;
+ }
+
+ if (fd != -1)
+ (void) close(fd);
+ return 0;
+}
+
+static void set_state(enable)
+u_int enable;
+{
+ if (ioctl(fd, SIOCFRENB, &enable) == -1)
+ perror("SIOCFRENB");
+ return;
+}
+
+static void procfile(file)
+char *file;
+{
+ FILE *fp;
+ char line[513], *s;
+ struct frentry *fr;
+ u_int add = SIOCADAFR, del = SIOCRMAFR;
+
+ if (opts & OPT_INACTIVE) {
+ add = SIOCADIFR;
+ del = SIOCRMIFR;
+ }
+ if (opts & OPT_DEBUG)
+ printf("add %x del %x\n", add, del);
+
+ if (!strcmp(file, "-"))
+ fp = stdin;
+ else if (!(fp = fopen(file, "r")))
+ return;
+
+ while (fgets(line, sizeof(line)-1, fp)) {
+ /*
+ * treat both CR and LF as EOL
+ */
+ if ((s = index(line, '\n')))
+ *s = '\0';
+ if ((s = index(line, '\r')))
+ *s = '\0';
+ /*
+ * # is comment marker, everything after is a ignored
+ */
+ if ((s = index(line, '#')))
+ *s = '\0';
+
+ if (!*line)
+ continue;
+
+ if (opts & OPT_VERBOSE)
+ (void)fprintf(stderr, "[%s]\n",line);
+
+ fr = parse(line);
+ (void)fflush(stdout);
+
+ if (fr) {
+ if (opts & OPT_INACTIVE)
+ add = fr->fr_hits ? SIOCINIFR : SIOCADIFR;
+ else
+ add = fr->fr_hits ? SIOCINAFR : SIOCADAFR;
+ if (fr->fr_hits)
+ fr->fr_hits--;
+ if (fr && (opts & OPT_VERBOSE))
+ printfr(fr);
+ if (fr && (opts & OPT_OUTQUE))
+ fr->fr_flags |= FR_OUTQUE;
+
+ if (opts & OPT_DEBUG)
+ binprint(fr);
+
+ if ((opts & OPT_REMOVE) && !(opts & OPT_DONOTHING)) {
+ if (ioctl(fd, del, fr) == -1)
+ perror("ioctl(SIOCDELFR)");
+ } else if (!(opts & OPT_DONOTHING)) {
+ if (ioctl(fd, add, fr) == -1)
+ perror("ioctl(SIOCADDFR)");
+ }
+ }
+ }
+ (void)fclose(fp);
+}
+
+
+static void packetlogon(opt)
+char *opt;
+{
+ int err, flag;
+
+ if (opts & OPT_VERBOSE) {
+ if ((err = ioctl(fd, SIOCGETFF, &flag)))
+ perror("ioctl(SIOCGETFF)");
+
+ printf("log flag is currently %#x\n", flag);
+ }
+
+ flag = 0;
+
+ if (index(opt, 'p')) {
+ flag |= FF_LOGPASS;
+ if (opts & OPT_VERBOSE)
+ printf("set log flag: pass\n");
+ }
+ if (index(opt, 'b') || index(opt, 'd')) {
+ flag |= FF_LOGBLOCK;
+ if (opts & OPT_VERBOSE)
+ printf("set log flag: block\n");
+ }
+
+ if (!(opts & OPT_DONOTHING) &&
+ (err = ioctl(fd, SIOCSETFF, &flag)))
+ perror("ioctl(SIOCSETFF)");
+
+ if (opts & OPT_VERBOSE) {
+ if ((err = ioctl(fd, SIOCGETFF, &flag)))
+ perror("ioctl(SIOCGETFF)");
+
+ printf("log flag is now %#x\n", flag);
+ }
+}
+
+
+static void flushfilter(arg)
+char *arg;
+{
+ int fl = 0, rem;
+
+ if (!arg || !*arg)
+ return;
+ if (*arg == 'i' || *arg == 'I')
+ fl = FR_INQUE;
+ else if (*arg == 'o' || *arg == 'O')
+ fl = FR_OUTQUE;
+ else if (*arg == 'a' || *arg == 'A')
+ fl = FR_OUTQUE|FR_INQUE;
+ fl |= (opts & FR_INACTIVE);
+ rem = fl;
+
+ if (!(opts & OPT_DONOTHING) && ioctl(fd, SIOCIPFFL, &fl) == -1)
+ perror("ioctl(SIOCIPFFL)");
+ if (opts & OPT_VERBOSE){
+ printf("remove flags %s%s (%d)\n", (rem & FR_INQUE) ? "I" : "",
+ (rem & FR_OUTQUE) ? "O" : "", rem);
+ printf("removed %d filter rules\n", fl);
+ }
+ return;
+}
+
+
+static void swapactive()
+{
+ int in = 2;
+
+ if (ioctl(fd, SIOCSWAPA, &in) == -1)
+ perror("ioctl(SIOCSWAPA)");
+ else
+ printf("Set %d now inactive\n", in);
+}
+
+
+#if defined(sun) && (defined(__SVR4) || defined(__svr4__))
+frsync()
+{
+ if (ioctl(fd, SIOCFRSYN, 0) == -1)
+ perror("SIOCFRSYN");
+ else
+ printf("filter sync'd\n");
+}
+#endif
+
+
+zerostats()
+{
+ struct friostat fio;
+
+ if (ioctl(fd, SIOCFRZST, &fio) == -1) {
+ perror("ioctl(SIOCFRZST)");
+ exit(-1);
+ }
+
+}
diff --git a/sbin/ipf/ipf.h b/sbin/ipf/ipf.h
new file mode 100644
index 00000000000..537c15b901d
--- /dev/null
+++ b/sbin/ipf/ipf.h
@@ -0,0 +1,43 @@
+/*
+ * (C)opyright 1993,1994,1995 by Darren Reed.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and due credit is given
+ * to the original author and the contributors.
+ *
+ * @(#)ipf.h 1.7 10/15/95
+ */
+
+#define OPT_INQUE FR_INQUE /* 0x0001 */
+#define OPT_REMOVE 0x0002
+#define OPT_OUTQUE FR_OUTQUE /* 0x0004 */
+#define OPT_DEBUG 0x0008
+#define OPT_SHOWLIST 0x0010
+#define OPT_VERBOSE 0x0020
+#define OPT_LOG FR_LOG /* 0x0040 */
+#define OPT_DONOTHING 0x0080
+#define OPT_INACTIVE FR_INACTIVE /* 0x0800 */
+#define OPT_HITS 0x10000
+#define OPT_BRIEF 0x20000
+
+extern struct frentry *parse();
+
+extern void printfr(), binprint();
+
+#if defined(__SVR4) || defined(__svr4__)
+#define index strchr
+#define bzero(a,b) memset(a, 0, b)
+#define bcopy(a,b,c) memmove(b,a,c)
+#endif
+
+struct ipopt_names {
+ int on_value;
+ int on_bit;
+ int on_siz;
+ char *on_name;
+};
+
+
+extern u_long hostnum(), optname();
+extern void printpacket();
+
diff --git a/sbin/ipf/opt.c b/sbin/ipf/opt.c
new file mode 100644
index 00000000000..5b7debfb2b4
--- /dev/null
+++ b/sbin/ipf/opt.c
@@ -0,0 +1,133 @@
+/*
+ * (C)opyright 1993,1994,1995 by Darren Reed.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and due credit is given
+ * to the original author and the contributors.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <net/if.h>
+#include <netinet/ip_fil.h>
+#include "ipf.h"
+
+#ifndef lint
+static char sccsid[] = "@(#)opt.c 1.6 11/11/95 (C) 1993-1995 Darren Reed";
+#endif
+
+extern int opts;
+
+struct ipopt_names ionames[] ={
+ { IPOPT_NOP, 0x000001, 1, "nop" },
+ { IPOPT_RR, 0x000002, 7, "rr" }, /* 1 route */
+ { IPOPT_ZSU, 0x000004, 3, "zsu" },
+ { IPOPT_MTUP, 0x000008, 3, "mtup" },
+ { IPOPT_MTUR, 0x000010, 3, "mtur" },
+ { IPOPT_ENCODE, 0x000020, 3, "encode" },
+ { IPOPT_TS, 0x000040, 8, "ts" }, /* 1 TS */
+ { IPOPT_TR, 0x000080, 3, "tr" },
+ { IPOPT_SECURITY,0x000100, 11, "sec" },
+ { IPOPT_SECURITY,0x000100, 11, "sec-class" },
+ { IPOPT_LSRR, 0x000200, 7, "lsrr" }, /* 1 route */
+ { IPOPT_E_SEC, 0x000400, 3, "e-sec" },
+ { IPOPT_CIPSO, 0x000800, 3, "cipso" },
+ { IPOPT_SATID, 0x001000, 4, "satid" },
+ { IPOPT_SSRR, 0x002000, 7, "ssrr" }, /* 1 route */
+ { IPOPT_ADDEXT, 0x004000, 3, "addext" },
+ { IPOPT_VISA, 0x008000, 3, "visa" },
+ { IPOPT_IMITD, 0x010000, 3, "imitd" },
+ { IPOPT_EIP, 0x020000, 3, "eip" },
+ { IPOPT_FINN, 0x040000, 3, "finn" },
+ { 0, 0, 0, (char *)NULL } /* must be last */
+};
+
+struct ipopt_names secclass[] = {
+ { IPSO_CLASS_RES4, 0x01, 0, "reserv-4" },
+ { IPSO_CLASS_TOPS, 0x02, 0, "topsecret" },
+ { IPSO_CLASS_SECR, 0x04, 0, "secret" },
+ { IPSO_CLASS_RES3, 0x08, 0, "reserv-3" },
+ { IPSO_CLASS_CONF, 0x10, 0, "confid" },
+ { IPSO_CLASS_UNCL, 0x20, 0, "unclass" },
+ { IPSO_CLASS_RES2, 0x40, 0, "reserv-2" },
+ { IPSO_CLASS_RES1, 0x40, 0, "reserv-1" },
+ { 0, 0, 0, NULL } /* must be last */
+};
+
+
+static u_char seclevel(slevel)
+char *slevel;
+{
+ struct ipopt_names *so;
+
+ for (so = secclass; so->on_name; so++)
+ if (!strcasecmp(slevel, so->on_name))
+ break;
+
+ if (!so->on_name) {
+ fprintf(stderr, "no such security level: %s\n", slevel);
+ return 0;
+ }
+ return (u_char)so->on_value;
+}
+
+
+u_long buildopts(cp, op)
+char *cp, *op;
+{
+ struct ipopt_names *io;
+ u_char lvl;
+ u_long msk = 0;
+ char *s, *t;
+ int len = 0;
+
+ for (s = strtok(cp, ","); s; s = strtok(NULL, ",")) {
+ if ((t = strchr(s, '=')))
+ *t++ = '\0';
+ for (io = ionames; io->on_name; io++) {
+ if (strcasecmp(s, io->on_name) || (msk & io->on_bit))
+ continue;
+ if ((len + io->on_siz) > 48) {
+ fprintf(stderr, "options too long\n");
+ return 0;
+ }
+ len += io->on_siz;
+ *op++ = io->on_value;
+ if (io->on_siz > 1) {
+ *op++ = io->on_siz;
+ *op++ = IPOPT_MINOFF;
+
+ if (t && !strcasecmp(s, "sec-class")) {
+ lvl = seclevel(t);
+ *op = lvl;
+ }
+ op += io->on_siz - 3;
+ if (len & 3) {
+ *op++ = IPOPT_NOP;
+ len++;
+ }
+ }
+ if (opts & OPT_DEBUG)
+ fprintf(stderr, "bo: %s %d %#x: %d\n",
+ io->on_name, io->on_value,
+ io->on_bit, len);
+ msk |= io->on_bit;
+ break;
+ }
+ if (!io->on_name) {
+ fprintf(stderr, "unknown IP option name %s\n", s);
+ return 0;
+ }
+ }
+ *op++ = IPOPT_EOL;
+ len++;
+ return len;
+}
diff --git a/sbin/ipf/parse.c b/sbin/ipf/parse.c
new file mode 100644
index 00000000000..56cf14d0afd
--- /dev/null
+++ b/sbin/ipf/parse.c
@@ -0,0 +1,958 @@
+/*
+ * (C)opyright 1993,1994,1995 by Darren Reed.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and due credit is given
+ * to the original author and the contributors.
+ */
+#include <stdio.h>
+#include <string.h>
+#if !defined(__SVR4) && !defined(__svr4__)
+#include <strings.h>
+#else
+#include <sys/byteorder.h>
+#endif
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <netinet/ip_fil.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include "ipf.h"
+#include <ctype.h>
+
+#ifndef lint
+static char sccsid[] ="@(#)parse.c 1.25 11/11/95 (C) 1993 Darren Reed";
+#endif
+
+extern struct ipopt_names ionames[], secclass[];
+extern int opts;
+
+u_long hostnum(), optname();
+u_short portnum();
+u_char tcp_flags();
+struct frentry *parse();
+void binprint(), printfr();
+int addicmp(), extras(), hostmask(), ports();
+
+char *proto = NULL;
+char flagset[] = "FSRPAU";
+u_char flags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, TH_ACK, TH_URG };
+
+/* parse()
+ *
+ * parse a line read from the input filter rule file
+ */
+struct frentry *parse(line)
+char *line;
+{
+ static struct frentry fil;
+ struct protoent *p = NULL;
+ char *cps[31], **cpp;
+ u_char ch;
+ int i, cnt = 1;
+
+ bzero((char *)&fil, sizeof(fil));
+ fil.fr_mip.fi_v = 0xf;
+ fil.fr_ip.fi_v = 4;
+ /*
+ * break line up into max of 20 segments
+ */
+ if (opts & OPT_DEBUG)
+ fprintf(stderr, "parse [%s]\n", line);
+ for (i = 0, *cps = strtok(line, " \b\t\r\n"); cps[i] && i < 30; cnt++)
+ cps[++i] = strtok(NULL, " \b\t\r\n");
+ cps[i] = NULL;
+
+ if (cnt < 3) {
+ (void)fprintf(stderr,"not enough segments in line\n");
+ return NULL;
+ }
+
+ cpp = cps;
+ if (**cpp == '@')
+ fil.fr_hits = atoi(*cpp++ + 1) + 1;
+ /*
+ * does it start with one of the two possible first words ?
+ */
+ if (strcasecmp("block",*cpp) && strcasecmp("pass",*cpp) &&
+ strcasecmp("log",*cpp)) {
+ (void)fprintf(stderr, "unknown keyword (%s)\n", *cpp);
+ return NULL;
+ }
+ if (**cpp == 'b') {
+ fil.fr_flags = FR_BLOCK;
+ if (!strncasecmp(*(cpp+1), "return-icmp", 11)) {
+ fil.fr_flags |= FR_RETICMP;
+ cpp++;
+ } else if (!strncasecmp(*(cpp+1), "return-rst", 10)) {
+ fil.fr_flags |= FR_RETRST;
+ cpp++;
+ }
+ } else if (**cpp == 'p')
+ fil.fr_flags = FR_PASS;
+ else if (**cpp == 'l') {
+ fil.fr_flags = FR_LOG;
+ if (!strcasecmp(*(cpp+1), "body")) {
+ fil.fr_flags |= FR_LOGBODY;
+ cpp++;
+ }
+ }
+ cpp++;
+
+ if (!strcasecmp("in", *cpp))
+ fil.fr_flags |= FR_INQUE;
+ else if (!strcasecmp("out", *cpp))
+ fil.fr_flags |= FR_OUTQUE;
+ else {
+ (void)fprintf(stderr, "missing 'in'/'out' keyword (%s)\n",
+ *cpp);
+ return NULL;
+ }
+
+ if (!*++cpp)
+ return NULL;
+ if (!strcasecmp("log", *cpp)) {
+ cpp++;
+ if (fil.fr_flags & FR_PASS)
+ fil.fr_flags |= FR_LOGP;
+ else if (fil.fr_flags & FR_BLOCK)
+ fil.fr_flags |= FR_LOGB;
+ if (!strcasecmp(*cpp, "body")) {
+ fil.fr_flags |= FR_LOGBODY;
+ cpp++;
+ }
+ }
+
+ if (!strcasecmp("quick", *cpp)) {
+ cpp++;
+ fil.fr_flags |= FR_QUICK;
+ }
+
+ *fil.fr_ifname = '\0';
+ if (!strcasecmp(*cpp, "on")) {
+ if (!*++cpp) {
+ (void)fprintf(stderr, "interface name missing\n");
+ return NULL;
+ }
+ (void)strncpy(fil.fr_ifname, *cpp, IFNAMSIZ-1);
+ fil.fr_ifname[IFNAMSIZ-1] = '\0';
+ cpp++;
+ if (!*cpp) {
+ if (fil.fr_flags & FR_RETRST) {
+ (void)fprintf(stderr,
+ "%s can only be used with TCP\n",
+ "return-rst");
+ return NULL;
+ }
+ return &fil;
+ }
+ }
+
+ if (!strcasecmp(*cpp, "tos")) {
+ if (!*++cpp) {
+ (void)fprintf(stderr, "tos missing value\n");
+ return NULL;
+ }
+ fil.fr_tos = strtol(*cpp, NULL, 0);
+ fil.fr_mip.fi_tos = 0xff;
+ cpp++;
+ }
+
+ if (!strcasecmp(*cpp, "ttl")) {
+ if (!*++cpp) {
+ (void)fprintf(stderr, "ttl missing hopcount value\n");
+ return NULL;
+ }
+ fil.fr_ttl = atoi(*cpp);
+ fil.fr_mip.fi_ttl = 0xff;
+ cpp++;
+ }
+
+ /*
+ * check for "proto <protoname>" only decode udp/tcp/icmp as protoname
+ */
+ proto = NULL;
+ if (!strcasecmp(*cpp, "proto")) {
+ if (!*++cpp) {
+ (void)fprintf(stderr, "protocol name missing\n");
+ return NULL;
+ }
+ if (!strcasecmp(*cpp, "tcp/udp")) {
+ fil.fr_ip.fi_fl |= FI_TCPUDP;
+ fil.fr_mip.fi_fl |= FI_TCPUDP;
+ } else {
+ if (!(p = getprotobyname(*cpp)) && !isdigit(**cpp)) {
+ (void)fprintf(stderr,
+ "unknown protocol (%s)\n", *cpp);
+ return NULL;
+ }
+ if (p)
+ fil.fr_proto = p->p_proto;
+ else if (isdigit(**cpp))
+ fil.fr_proto = atoi(*cpp);
+ fil.fr_mip.fi_p = 0xff;
+ }
+ proto = *cpp;
+ if (fil.fr_proto != IPPROTO_TCP && fil.fr_flags & FR_RETRST) {
+ (void)fprintf(stderr,
+ "%s can only be used with TCP\n",
+ "return-rst");
+ return NULL;
+ }
+ if (!*++cpp)
+ return &fil;
+ }
+ if (fil.fr_proto != IPPROTO_TCP && fil.fr_flags & FR_RETRST) {
+ (void)fprintf(stderr, "%s can only be used with TCP\n",
+ "return-rst");
+ return NULL;
+ }
+
+ /*
+ * get the from host and bit mask to use against packets
+ */
+
+ if (!strcasecmp(*cpp, "all")) {
+ cpp++;
+ if (!*cpp)
+ return &fil;
+ } else {
+ if (strcasecmp(*cpp, "from")) {
+ (void)fprintf(stderr,
+ "unexpected keyword (%s) - from\n", *cpp);
+ return NULL;
+ }
+ if (!*++cpp) {
+ (void)fprintf(stderr, "missing host after from\n");
+ return NULL;
+ }
+ ch = 0;
+ if (hostmask(&cpp, &fil.fr_src, &fil.fr_smsk,
+ &fil.fr_sport, &ch, &fil.fr_stop)) {
+ (void)fprintf(stderr, "bad host (%s)\n", *cpp);
+ return NULL;
+ }
+ fil.fr_scmp = ch;
+ if (!*cpp) {
+ (void)fprintf(stderr, "missing to fields\n");
+ return NULL;
+ }
+
+ /*
+ * do the same for the to field (destination host)
+ */
+ if (strcasecmp(*cpp, "to")) {
+ (void)fprintf(stderr,
+ "unexpected keyword (%s) - to\n", *cpp);
+ return NULL;
+ }
+ if (!*++cpp) {
+ (void)fprintf(stderr, "missing host after to\n");
+ return NULL;
+ }
+ ch = 0;
+ if (hostmask(&cpp, &fil.fr_dst, &fil.fr_dmsk,
+ &fil.fr_dport, &ch, &fil.fr_dtop)) {
+ (void)fprintf(stderr, "bad host (%s)\n", *cpp);
+ return NULL;
+ }
+ fil.fr_dcmp = ch;
+ }
+
+ /*
+ * check some sanity, make sure we don't have icmp checks with tcp
+ * or udp or visa versa.
+ */
+ if (fil.fr_proto && (fil.fr_dcmp || fil.fr_scmp) &&
+ fil.fr_proto != IPPROTO_TCP && fil.fr_proto != IPPROTO_UDP) {
+ (void)fprintf(stderr, "port operation on non tcp/udp\n");
+ return NULL;
+ }
+ if (fil.fr_icmp && fil.fr_proto != IPPROTO_ICMP) {
+ (void)fprintf(stderr, "icmp comparisons on wrong protocol\n");
+ return NULL;
+ }
+
+ if (!*cpp)
+ return &fil;
+
+ if (*cpp && !strcasecmp(*cpp, "flags")) {
+ if (!*++cpp) {
+ (void)fprintf(stderr, "no flags present\n");
+ return NULL;
+ }
+ fil.fr_tcpf = tcp_flags(*cpp, &fil.fr_tcpfm);
+ cpp++;
+ }
+
+ /*
+ * extras...
+ */
+ if (*cpp && (!strcasecmp(*cpp, "with") || !strcasecmp(*cpp, "and")))
+ if (extras(&cpp, &fil))
+ return NULL;
+
+ /*
+ * icmp types for use with the icmp protocol
+ */
+ if (*cpp && !strcasecmp(*cpp, "icmp-type")) {
+ if (fil.fr_proto != IPPROTO_ICMP) {
+ (void)fprintf(stderr,
+ "icmp with wrong protocol (%d)\n",
+ fil.fr_proto);
+ return NULL;
+ }
+ if (addicmp(&cpp, &fil))
+ return NULL;
+ fil.fr_icmp = htons(fil.fr_icmp);
+ fil.fr_icmpm = htons(fil.fr_icmpm);
+ }
+
+ /*
+ * leftovers...yuck
+ */
+ if (*cpp && **cpp) {
+ fprintf(stderr, "unknown words at end: [");
+ for (; *cpp; cpp++)
+ (void)fprintf(stderr, "%s ", *cpp);
+ (void)fprintf(stderr, "]\n");
+ return NULL;
+ }
+
+ /*
+ * lazy users...
+ */
+ if (!fil.fr_proto && (fil.fr_dcmp || fil.fr_scmp || fil.fr_tcpf)) {
+ (void)fprintf(stderr,
+ "no protocol given for TCP/UDP comparisons\n");
+ return NULL;
+ }
+
+ return &fil;
+}
+
+/*
+ * returns false if neither "hostmask/num" or "hostmask mask addr" are
+ * found in the line segments
+ */
+int hostmask(seg, sa, msk, pp, cp, tp)
+char ***seg;
+u_long *sa, *msk;
+u_short *pp, *tp;
+u_char *cp;
+{
+ char *s;
+ int bits = -1;
+
+ /*
+ * is it possibly hostname/num ?
+ */
+ if ((s = index(**seg, '/'))) {
+ *s++ = '\0';
+ if (!isdigit(*s))
+ return -1;
+ if (index(s, '.'))
+ *msk = inet_addr(s);
+ else {
+ /*
+ * set x most significant bits
+ */
+ for (bits = atoi(s); bits; bits--) {
+ *msk /= 2;
+ *msk |= ntohl(inet_addr("128.0.0.0"));
+ }
+ *msk = htonl(*msk);
+ }
+ *sa = hostnum(**seg) & *msk;
+ (*seg)++;
+ return ports(seg, pp, cp, tp);
+ }
+
+ /*
+ * look for extra segments if "mask" found in right spot
+ */
+ if (*(*seg+1) && *(*seg+2) && !strcasecmp(*(*seg+1), "mask")) {
+ *sa = hostnum(**seg);
+ (*seg)++;
+ (*seg)++;
+ *msk = inet_addr(**seg);
+ (*seg)++;
+ *sa &= *msk;
+ return ports(seg, pp, cp, tp);
+ }
+
+ if (**seg) {
+ *sa = hostnum(**seg);
+ (*seg)++;
+ *msk = (*sa ? inet_addr("255.255.255.255") : 0L);
+ *sa &= *msk;
+ return ports(seg, pp, cp, tp);
+ }
+ return -1;
+}
+
+/*
+ * returns an ip address as a long var as a result of either a DNS lookup or
+ * straight inet_addr() call
+ */
+u_long hostnum(host)
+char *host;
+{
+ struct hostent *hp;
+ struct netent *np;
+
+ if (!strcasecmp("any",host))
+ return 0L;
+ if (isdigit(*host))
+ return inet_addr(host);
+
+ if (!(hp = gethostbyname(host))) {
+ if (!(np = getnetbyname(host)))
+ return 0;
+ return np->n_net;
+ }
+ return *(u_long *)hp->h_addr;
+}
+
+/*
+ * check for possible presence of the port fields in the line
+ */
+int ports(seg, pp, cp, tp)
+char ***seg;
+u_short *pp, *tp;
+u_char *cp;
+{
+ int comp = -1;
+
+ if (!*seg || !**seg || !***seg)
+ return 0;
+ if (!strcasecmp(**seg, "port") && *(*seg + 1) && *(seg + 2)) {
+ (*seg)++;
+ if (isdigit(***seg) && *(seg + 2)) {
+ *pp = portnum(**seg);
+ (*seg)++;
+ if (!strcmp(**seg, "<>"))
+ comp = FR_OUTRANGE;
+ else if (!strcmp(**seg, "><"))
+ comp = FR_INRANGE;
+ (*seg)++;
+ *tp = portnum(**seg);
+ } else if (!strcmp(**seg, "=") || !strcasecmp(**seg, "eq"))
+ comp = FR_EQUAL;
+ else if (!strcmp(**seg, "!=") || !strcasecmp(**seg, "ne"))
+ comp = FR_NEQUAL;
+ else if (!strcmp(**seg, "<") || !strcasecmp(**seg, "lt"))
+ comp = FR_LESST;
+ else if (!strcmp(**seg, ">") || !strcasecmp(**seg, "gt"))
+ comp = FR_GREATERT;
+ else if (!strcmp(**seg, "<=") || !strcasecmp(**seg, "le"))
+ comp = FR_LESSTE;
+ else if (!strcmp(**seg, ">=") || !strcasecmp(**seg, "ge"))
+ comp = FR_GREATERTE;
+ else {
+ (void)fprintf(stderr,"unknown comparator (%s)\n",
+ **seg);
+ return -1;
+ }
+ if (comp != FR_OUTRANGE && comp != FR_INRANGE) {
+ (*seg)++;
+ *pp = portnum(**seg);
+ }
+ *cp = comp;
+ (*seg)++;
+ }
+ return 0;
+}
+
+/*
+ * find the port number given by the name, either from getservbyname() or
+ * straight atoi()
+ */
+u_short portnum(name)
+char *name;
+{
+ struct servent *sp, *sp2;
+ u_short p1 = 0;
+
+ if (isdigit(*name) || !proto)
+ return htons((u_short)atoi(name));
+ if (strcasecmp(proto, "tcp/udp")) {
+ sp = getservbyname(name, proto);
+ if (sp)
+ return sp->s_port;
+ (void) fprintf(stderr, "unknown service \"%s\".\n", name);
+ return 0;
+ }
+ sp = getservbyname(name, "tcp");
+ if (sp)
+ p1 = sp->s_port;
+ sp2 = getservbyname(name, "udp");
+ if (!sp || !sp2) {
+ (void) fprintf(stderr, "unknown tcp/udp service \"%s\".\n",
+ name);
+ return 0;
+ }
+ if (p1 != sp2->s_port) {
+ (void) fprintf(stderr, "%s %d/tcp is a different port to ",
+ name, p1);
+ (void) fprintf(stderr, "%s %d/udp\n", name, sp->s_port);
+ return 0;
+ }
+ return p1;
+}
+
+
+u_char tcp_flags(flgs, mask)
+char *flgs;
+u_char *mask;
+{
+ u_char tcpf = 0, tcpfm = 0, *fp = &tcpf;
+ char *s, *t;
+
+ for (s = flgs; *s; s++) {
+ if (*s == '/' && fp == &tcpf) {
+ fp = &tcpfm;
+ continue;
+ }
+ if (!(t = index(flagset, *s))) {
+ (void)fprintf(stderr, "unknown flag (%c)\n", *s);
+ return 0;
+ }
+ *fp |= flags[t - flagset];
+ }
+ if (!tcpfm)
+ tcpfm = 0xff;
+ *mask = tcpfm;
+ return tcpf;
+}
+
+
+/*
+ * deal with extra bits on end of the line
+ */
+int extras(cp, fr)
+char ***cp;
+struct frentry *fr;
+{
+ u_short secmsk;
+ u_long opts;
+ int notopt;
+ char oflags;
+
+ opts = 0;
+ secmsk = 0;
+ notopt = 0;
+ (*cp)++;
+ if (!**cp)
+ return -1;
+
+ while (**cp && (!strncasecmp(**cp, "ipopt", 5) ||
+ !strncasecmp(**cp, "not", 3) || !strncasecmp(**cp, "opt", 4) ||
+ !strncasecmp(**cp, "frag", 3) || !strncasecmp(**cp, "no", 2) ||
+ !strncasecmp(**cp, "short", 5))) {
+ if (***cp == 'n')
+ notopt = 1;
+ else if (***cp == 'i')
+ oflags = FI_OPTIONS;
+ else if (***cp == 'f')
+ oflags = FI_FRAG;
+ else if (***cp == 'o') {
+ if (!*(*cp + 1)) {
+ (void)fprintf(stderr,
+ "opt missing arguements\n");
+ return -1;
+ }
+ (*cp)++;
+ if (!(opts = optname(cp, &secmsk)))
+ return -1;
+ oflags = FI_OPTIONS;
+ } else if (***cp == 's') {
+ if (fr->fr_tcpf) {
+ (void) fprintf(stderr,
+ "short cannot be used with TCP flags\n");
+ return -1;
+ }
+ oflags = FI_SHORT;
+ } else
+ return -1;
+
+ fr->fr_mip.fi_fl |= oflags;
+ fr->fr_mip.fi_optmsk |= opts;
+ fr->fr_mip.fi_secmsk |= secmsk;
+
+ if (notopt) {
+ fr->fr_ip.fi_fl &= (~oflags & 0xf);
+ fr->fr_ip.fi_optmsk &= ~opts;
+ fr->fr_ip.fi_secmsk &= ~secmsk;
+ } else {
+ fr->fr_ip.fi_fl |= oflags;
+ fr->fr_ip.fi_optmsk |= opts;
+ fr->fr_ip.fi_secmsk |= secmsk;
+ }
+ opts = 0;
+ oflags = 0;
+ secmsk = 0;
+ (*cp)++;
+ }
+ return 0;
+}
+
+
+u_long optname(cp, sp)
+char ***cp;
+u_short *sp;
+{
+ struct ipopt_names *io, *so;
+ u_long msk = 0;
+ u_short smsk = 0;
+ char *s;
+ int sec = 0;
+
+ for (s = strtok(**cp, ","); s; s = strtok(NULL, ",")) {
+ for (io = ionames; io->on_name; io++)
+ if (!strcasecmp(s, io->on_name)) {
+ msk |= io->on_bit;
+ break;
+ }
+ if (!io->on_name) {
+ fprintf(stderr, "unknown IP option name %s\n", s);
+ return 0;
+ }
+ if (!strcasecmp(s, "sec-class"))
+ sec = 1;
+ }
+
+ if (sec && !*(*cp + 1)) {
+ fprintf(stderr, "missing security level after sec-class\n");
+ return 0;
+ }
+
+ if (sec) {
+ (*cp)++;
+ for (s = strtok(**cp, ","); s; s = strtok(NULL, ",")) {
+ for (so = secclass; so->on_name; so++)
+ if (!strcasecmp(s, so->on_name)) {
+ smsk |= so->on_bit;
+ break;
+ }
+ if (!so->on_name) {
+ fprintf(stderr, "no such security level: %s\n",
+ s);
+ return 0;
+ }
+ }
+ if (smsk)
+ *sp = smsk;
+ }
+ return msk;
+}
+
+
+void optprint(optmsk, secmsk)
+u_char optmsk, secmsk;
+{
+ struct ipopt_names *io, *so;
+ char *s = "", *t = "";
+
+ printf("opt ");
+ for (io = ionames; io->on_name; io++)
+ if (io->on_bit & optmsk) {
+ printf("%s%s", s, io->on_name);
+ s = ",";
+ }
+ if (secmsk) {
+ putchar(' ');
+ for (so = secclass; so->on_name; so++)
+ if (secmsk & so->on_bit) {
+ printf("%s%s", t, so->on_name);
+ t = ",";
+ }
+ }
+}
+
+char *icmptypes[] = {
+ "echorep", (char *)NULL, (char *)NULL, "unreach", "squench",
+ "redir", (char *)NULL, (char *)NULL, "echo", (char *)NULL,
+ (char *)NULL, "timex", "paramprob", "timest", "timestrep",
+ "inforeq", "inforep", "maskreq", "maskrep", "END"
+};
+
+/*
+ * set the icmp field to the correct type if "icmp" word is found
+ */
+int addicmp(cp, fp)
+char ***cp;
+struct frentry *fp;
+{
+ char **t;
+ int i;
+
+ (*cp)++;
+ if (!**cp)
+ return -1;
+ if (!fp->fr_proto) /* to catch lusers */
+ fp->fr_proto = IPPROTO_ICMP;
+ if (isdigit(***cp)) {
+ i = atoi(**cp);
+ (*cp)++;
+ } else {
+ for (t = icmptypes, i = 0; ; t++, i++) {
+ if (!*t)
+ continue;
+ if (!strcasecmp("END", *t)) {
+ i = -1;
+ break;
+ }
+ if (!strcasecmp(*t, **cp))
+ break;
+ }
+ if (i == -1)
+ return -1;
+ }
+ fp->fr_icmp = (u_short)(i << 8);
+ fp->fr_icmpm = (u_short)0xff00;
+ (*cp)++;
+ if (!**cp)
+ return 0;
+
+ if (**cp && strcasecmp("code", **cp))
+ return 0;
+ (*cp)++;
+ if (isdigit(***cp)) {
+ i = atoi(**cp);
+ fp->fr_icmp |= (u_short)i;
+ fp->fr_icmpm = (u_short)0xffff;
+ (*cp)++;
+ return 0;
+ }
+ return -1;
+}
+
+
+/*
+ * count consecutive 1's in bit mask. If the mask generated by counting
+ * consecutive 1's is different to that passed, return -1, else return #
+ * of bits.
+ */
+int countbits(ip)
+u_long ip;
+{
+ u_long ipn;
+ int cnt = 0, i, j;
+
+ ip = ipn = ntohl(ip);
+ for (i = 32; i; i--, ipn *= 2)
+ if (ipn & 0x80000000)
+ cnt++;
+ else
+ break;
+ ipn = 0;
+ for (i = 32, j = cnt; i; i--, j--) {
+ ipn *= 2;
+ if (j > 0)
+ ipn++;
+ }
+ if (ipn == ip)
+ return cnt;
+ return -1;
+}
+
+
+char *portname(pr, port)
+int pr, port;
+{
+ static char buf[32];
+ struct protoent *p = NULL;
+ struct servent *sv = NULL, *sv1 = NULL;
+
+ if (pr == -1) {
+ if ((sv = getservbyport(htons(port), "tcp"))) {
+ strncpy(buf, sv->s_name, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ sv1 = getservbyport(htons(port), "udp");
+ if (sv1 && !strcasecmp(buf, sv->s_name))
+ return buf;
+ }
+ } else if (pr && (p = getprotobynumber(pr))) {
+ if ((sv = getservbyport(htons(port), p->p_name))) {
+ strncpy(buf, sv->s_name, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ return buf;
+ }
+ }
+
+ (void) sprintf(buf, "%d", port);
+ return buf;
+}
+
+
+/*
+ * print the filter structure in a useful way
+ */
+void printfr(fp)
+struct frentry *fp;
+{
+ static char *pcmp1[] = { "*", "=", "!=", "<", ">", "<=", ">=",
+ "<>", "><"};
+ struct protoent *p;
+ int ones = 0, pr;
+ char *s;
+ u_char *t;
+
+ if (fp->fr_flags & FR_PASS)
+ (void)printf("pass");
+ else if (fp->fr_flags & FR_LOG) {
+ (void)printf("log");
+ if (fp->fr_flags & FR_LOGBODY)
+ (void)printf(" body");
+ } else if (fp->fr_flags & FR_BLOCK) {
+ (void)printf("block");
+ if (fp->fr_flags & FR_RETICMP)
+ (void)printf(" return-icmp");
+ if (fp->fr_flags & FR_RETRST)
+ (void)printf(" return-rst");
+ }
+
+ if (fp->fr_flags & FR_OUTQUE)
+ (void)printf(" out ");
+ else
+ (void)printf(" in ");
+
+ if (fp->fr_flags & (FR_LOGB|FR_LOGP)) {
+ (void)printf("log ");
+ if (fp->fr_flags & FR_LOGBODY)
+ (void)printf(" body");
+ }
+ if (fp->fr_flags & FR_QUICK)
+ (void)printf("quick ");
+
+ if (*fp->fr_ifname)
+ (void)printf("on %s%s ", fp->fr_ifname,
+ (fp->fr_ifa || (int)fp->fr_ifa == -1) ? "" : "(!)");
+ if (fp->fr_mip.fi_tos)
+ (void)printf("tos %#x ", fp->fr_tos);
+ if (fp->fr_mip.fi_ttl)
+ (void)printf("ttl %d ", fp->fr_ttl);
+ if (fp->fr_ip.fi_fl & FI_TCPUDP) {
+ (void)printf("proto tcp/udp ");
+ pr = -1;
+ } else if ((pr = fp->fr_proto)) {
+ if ((p = getprotobynumber(fp->fr_proto)))
+ (void)printf("proto %s ", p->p_name);
+ else
+ (void)printf("proto %d ", fp->fr_proto);
+ }
+
+ if (!fp->fr_src.s_addr & !fp->fr_smsk.s_addr)
+ (void)printf("from any ");
+ else {
+ (void)printf("from %s", inet_ntoa(fp->fr_src));
+ if ((ones = countbits(fp->fr_smsk.s_addr)) == -1)
+ (void)printf("/%s ", inet_ntoa(fp->fr_smsk));
+ else
+ (void)printf("/%d ", ones);
+ }
+ if (fp->fr_sport)
+ if (fp->fr_scmp == FR_INRANGE || fp->fr_scmp == FR_OUTRANGE)
+ (void)printf("port %d %s %d ", ntohs(fp->fr_sport),
+ pcmp1[fp->fr_scmp], ntohs(fp->fr_stop));
+ else
+ (void)printf("port %s %s ", pcmp1[fp->fr_scmp],
+ portname(pr, ntohs(fp->fr_sport)));
+ if (!fp->fr_dst.s_addr & !fp->fr_dmsk.s_addr)
+ (void)printf("to any ");
+ else {
+ (void)printf("to %s", inet_ntoa(fp->fr_dst));
+ if ((ones = countbits(fp->fr_dmsk.s_addr)) == -1)
+ (void)printf("/%s ", inet_ntoa(fp->fr_dmsk));
+ else
+ (void)printf("/%d ", ones);
+ }
+ if (fp->fr_dport) {
+ if (fp->fr_dcmp == FR_INRANGE || fp->fr_dcmp == FR_OUTRANGE)
+ (void)printf("port %d %s %d ", ntohs(fp->fr_dport),
+ pcmp1[fp->fr_dcmp], ntohs(fp->fr_dtop));
+ else
+ (void)printf("port %s %s ", pcmp1[fp->fr_dcmp],
+ portname(pr, ntohs(fp->fr_dport)));
+ }
+ if (fp->fr_mip.fi_fl & (FI_SHORT|FI_OPTIONS|FI_FRAG)) {
+ (void)printf("with ");
+ if (fp->fr_mip.fi_fl & FI_OPTIONS) {
+ if (fp->fr_ip.fi_optmsk)
+ optprint(fp->fr_ip.fi_optmsk,
+ fp->fr_ip.fi_secmsk);
+ else {
+ if (!(fp->fr_ip.fi_fl & FI_OPTIONS))
+ (void)printf("not ");
+ (void)printf("ipopt ");
+ }
+ }
+ if (fp->fr_mip.fi_fl & FI_SHORT) {
+ if (!(fp->fr_ip.fi_fl & FI_SHORT))
+ (void)printf("not ");
+ (void)printf("short ");
+ }
+ if (fp->fr_mip.fi_fl & FI_FRAG) {
+ if (!(fp->fr_ip.fi_fl & FI_FRAG))
+ (void)printf("not ");
+ (void)printf("frag ");
+ }
+ }
+ if (fp->fr_proto == IPPROTO_ICMP && fp->fr_icmpm) {
+ int type = fp->fr_icmp, code;
+
+ type = ntohs(fp->fr_icmp);
+ code = type & 0xff;
+ type /= 256;
+ if (type < (sizeof(icmptypes) / sizeof(char *)) &&
+ icmptypes[type])
+ (void)printf("icmp-type %s ", icmptypes[type]);
+ else
+ (void)printf("icmp-type %d ", type);
+ if (code)
+ (void)printf("code %d ", code);
+ }
+ if (fp->fr_proto == IPPROTO_TCP && fp->fr_tcpf) {
+ (void)printf("flags ");
+ for (s = flagset, t = flags; *s; s++, t++)
+ if (fp->fr_tcpf & *t)
+ (void)putchar(*s);
+ if (fp->fr_tcpfm) {
+ (void)putchar('/');
+ for (s = flagset, t = flags; *s; s++, t++)
+ if (fp->fr_tcpfm & *t)
+ (void)putchar(*s);
+ }
+ }
+ (void)putchar('\n');
+}
+
+void binprint(fp)
+struct frentry *fp;
+{
+ int i = sizeof(*fp), j = 0;
+ u_char *s;
+
+ for (s = (u_char *)fp; i; i--, s++) {
+ j++;
+ (void)printf("%02x ",*s);
+ if (j == 16) {
+ (void)printf("\n");
+ j = 0;
+ }
+ }
+ putchar('\n');
+ (void)fflush(stdout);
+}
diff --git a/sbin/ipfstat/Makefile b/sbin/ipfstat/Makefile
new file mode 100644
index 00000000000..bafd458b225
--- /dev/null
+++ b/sbin/ipfstat/Makefile
@@ -0,0 +1,9 @@
+PROG= ipfstat
+MAN= ipfstat.8
+SRCS= fils.c parse.c opt.c kmem.c
+.PATH: ${.CURDIR}/../../sbin/ipf
+CFLAGS+=-DIPL_NAME=\"/dev/ipl\" -I${.CURDIR}/../../sbin/ipf
+
+
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipfstat/fils.c b/sbin/ipfstat/fils.c
new file mode 100644
index 00000000000..7f440f6da39
--- /dev/null
+++ b/sbin/ipfstat/fils.c
@@ -0,0 +1,212 @@
+/*
+ * (C)opyright 1993,1994,1995 by Darren Reed.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and due credit is given
+ * to the original author and the contributors.
+ */
+#include <stdio.h>
+#include <string.h>
+#if !defined(__SVR4) && !defined(__svr4__)
+#include <strings.h>
+#endif
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <nlist.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <net/if.h>
+#include <netinet/ip_fil.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "ipf.h"
+#include "kmem.h"
+#ifdef __NetBSD__
+#include <paths.h>
+#endif
+
+#ifndef lint
+static char sccsid[] = "@(#)fils.c 1.15 11/11/95 (C) 1993 Darren Reed";
+#endif
+#ifdef _PATH_UNIX
+#define VMUNIX _PATH_UNIX
+#else
+#define VMUNIX "/vmunix"
+#endif
+
+extern char *optarg;
+#define F_ST 0
+#define F_IN 1
+#define F_OUT 2
+#define F_FL 3
+
+int opts = 0;
+
+static void showstats();
+static void showlist();
+
+int main(argc,argv)
+int argc;
+char *argv[];
+{
+ struct friostat fio;
+ char c, *name = NULL, *device = IPL_NAME;
+ int fd;
+
+ if (openkmem() == -1)
+ exit(-1);
+
+ (void)setuid(getuid());
+ (void)setgid(getgid());
+
+ while ((c = getopt(argc, argv, "hIiovd:")) != -1)
+ {
+ switch (c)
+ {
+ case 'd' :
+ device = optarg;
+ break;
+ case 'h' :
+ opts |= OPT_HITS;
+ break;
+ case 'i' :
+ opts |= OPT_INQUE|OPT_SHOWLIST;
+ break;
+ case 'I' :
+ opts |= OPT_INACTIVE;
+ break;
+ case 'o' :
+ opts |= OPT_OUTQUE|OPT_SHOWLIST;
+ break;
+ case 'v' :
+ opts |= OPT_VERBOSE;
+ break;
+ }
+ }
+
+ if ((fd = open(device, O_RDONLY)) < 0) {
+ perror("open");
+ exit(-1);
+ }
+ bzero((char *)&fio, sizeof(fio));
+ if (ioctl(fd, SIOCGETFS, &fio) == -1) {
+ perror("ioctl(SIOCGETFS)");
+ exit(-1);
+ }
+
+ if (opts & OPT_VERBOSE)
+ printf("opts %#x name %s\n", opts, name ? name : "<>");
+ if (opts & OPT_SHOWLIST){
+ showlist(&fio);
+ if((opts & OPT_OUTQUE) && (opts & OPT_INQUE)){
+ opts &= ~OPT_OUTQUE;
+ showlist(&fio);
+ }
+ }
+ else
+ showstats(fd, &fio);
+ return 0;
+}
+
+
+/*
+ * read the kernel stats for packets blocked and passed
+ */
+static void showstats(fd, fp)
+int fd;
+struct friostat *fp;
+{
+ int frf = 0;
+
+ if (ioctl(fd, SIOCGETFF, &frf) == -1)
+ perror("ioctl(SIOCGETFF)");
+
+#if SOLARIS
+ (void)printf("dropped packets:\tin %ld\tout %ld\n",
+ fp->f_st[0].fr_drop, fp->f_st[1].fr_drop);
+ (void)printf("non-ip packets:\t\tin %ld\tout %ld\n",
+ fp->f_st[0].fr_notip, fp->f_st[1].fr_notip);
+ (void)printf(" bad packets:\t\tin %ld\tout %ld\n",
+ fp->f_st[0].fr_bad, fp->f_st[1].fr_bad);
+#endif
+ (void)printf(" input packets:\t\tblocked %ld passed %ld nomatch %ld\n",
+ fp->f_st[0].fr_block, fp->f_st[0].fr_pass,
+ fp->f_st[0].fr_nom);
+ (void)printf("output packets:\t\tblocked %ld passed %ld nomatch %ld\n",
+ fp->f_st[1].fr_block, fp->f_st[1].fr_pass,
+ fp->f_st[1].fr_nom);
+ (void)printf(" input packets logged:\tblocked %ld passed %ld\n",
+ fp->f_st[0].fr_bpkl, fp->f_st[0].fr_ppkl);
+ (void)printf("output packets logged:\tblocked %ld passed %ld\n",
+ fp->f_st[1].fr_bpkl, fp->f_st[1].fr_ppkl);
+ (void)printf(" packets logged:\tinput %ld-%ld output %ld-%ld\n",
+ fp->f_st[0].fr_pkl, fp->f_st[0].fr_skip,
+ fp->f_st[1].fr_pkl, fp->f_st[1].fr_skip);
+ (void)printf("ICMP replies:\t%ld\tTCP RSTs sent:\t%ld\n",
+ fp->f_st[0].fr_ret, fp->f_st[1].fr_ret);
+
+ (void)printf("Packet log flags set: (%#x)\n", frf);
+ if (frf & FF_LOGPASS)
+ printf("\tpackets passed through filter\n");
+ if (frf & FF_LOGBLOCK)
+ printf("\tpackets blocked by filter\n");
+ if (!frf)
+ printf("\tnone\n");
+}
+
+/*
+ * print out filter rule list
+ */
+static void showlist(fiop)
+struct friostat *fiop;
+{
+ struct frentry fb;
+ struct frentry *fp = NULL;
+ int i, set;
+
+ if (opts & OPT_OUTQUE)
+ i = F_OUT;
+ else if (opts & OPT_INQUE)
+ i = F_IN;
+ else
+ return;
+ set = fiop->f_active;
+ if (opts & OPT_INACTIVE)
+ set = 1 - set;
+ fp = (i == F_IN) ? (struct frentry *)fiop->f_fin[set] :
+ (struct frentry *)fiop->f_fout[set];
+ if (opts & OPT_VERBOSE)
+ (void)fprintf(stderr, "showlist:opts %#x i %d\n", opts, i);
+
+ if (opts & OPT_VERBOSE)
+ printf("fp %#x set %d\n", (u_int)fp, set);
+ if (!fp) {
+ (void)fprintf(stderr, "empty list for filter%s\n",
+ (i == F_IN) ? "in" : "out");
+ return;
+ }
+ while (fp) {
+ if (kmemcpy((char *)&fb, (u_long)fp, sizeof(fb)) == -1) {
+ perror("kmemcpy");
+ return;
+ }
+ fp = &fb;
+ if (opts & OPT_OUTQUE)
+ fp->fr_flags |= FR_OUTQUE;
+ if (opts & (OPT_HITS|OPT_VERBOSE))
+ printf("%d ", fp->fr_hits);
+ printfr(fp);
+ if (opts & OPT_VERBOSE)
+ binprint(fp);
+ fp = fp->fr_next;
+ }
+}
diff --git a/sbin/ipfstat/ipfstat.8 b/sbin/ipfstat/ipfstat.8
new file mode 100644
index 00000000000..bfc8b2ae262
--- /dev/null
+++ b/sbin/ipfstat/ipfstat.8
@@ -0,0 +1,48 @@
+.LP
+.TH ipfstat 8
+.SH NAME
+ipfstat - reports on packet filter statistics and filter list
+.SH SYNOPSIS
+ipfstat [-hIiovd:]
+.SH DESCRIPTION
+.LP
+.PP
+\fBipfstat examines /dev/kmem using the symbols \fB_fr_flags\fP,
+\fB_frstats\fP, \fB_filterin\fP, and \fB_filterout\fP.
+To run and work, it needs to be able to read both /dev/kmem and the
+kernel itself. The kernel name defaults to \fB/vmunix\fP.
+.PP
+The default behaviour of \fBipfstat\fP
+is to retrieve and display the accumulated statistics which have been
+accumulated over time as the kernel has put packets through the filter.
+.SH OPTIONS
+.IP -o
+display the filter list used for the output side of the kernel IP processing.
+.IP -i
+display the filter list used for the input side of the kernel IP processing.
+.IP -v
+turn verbose mode on. Displays more debugging information.
+.IP -d <device>
+use a device other than \fB/dev/ipl\fP for interfacing with the kernel.
+.IP -I
+swap between retrieving "inactive"/"active" filter list details. For use
+in combination with \fB-i\fP.
+.IP -h
+show per-rule the number of times each one scores a "hit". For use in
+combination with \fB-i\fP.
+.SH SYNOPSIS
+The role of \fBipfstat\fP is to display current kernel statistics gathered
+as a result of applying the filters in place (if any) to packets going in and
+out of the kernel. This is the default operation when no command line
+parameters are present.
+.PP
+When supplied with either \fB-i\fP or \fB-o\fP, it will retrieve and display
+the appropriate list of filter rules currently installed and in use by the
+kernel.
+.SH FILES
+/dev/kmem
+/vmunix
+.SH SEE ALSO
+ipf(1), ipfstat(1)
+.SH BUGS
+none known.
diff --git a/sbin/ipfstat/kmem.c b/sbin/ipfstat/kmem.c
new file mode 100644
index 00000000000..4c0dbe3b520
--- /dev/null
+++ b/sbin/ipfstat/kmem.c
@@ -0,0 +1,66 @@
+/*
+ * (C)opyright 1993,1994,1995 by Darren Reed.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and due credit is given
+ * to the original author and the contributors.
+ */
+/*
+ * kmemcpy() - copies n bytes from kernel memory into user buffer.
+ * returns 0 on success, -1 on error.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
+#define KMEM "/dev/kmem"
+
+#ifndef lint
+static char sccsid[] = "@(#)kmem.c 1.3 10/15/95 (C) 1992 Darren Reed";
+#endif
+
+static int kmemfd = -1;
+
+int openkmem()
+{
+ if ((kmemfd = open(KMEM,O_RDONLY)) == -1)
+ {
+ perror("kmeminit:open");
+ return -1;
+ }
+ return kmemfd;
+}
+
+int kmemcpy(buf, pos, n)
+register char *buf;
+long pos;
+register int n;
+{
+ register int r;
+
+ if (!n)
+ return 0;
+ if (kmemfd == -1)
+ if (openkmem() == -1)
+ return -1;
+ if (lseek(kmemfd, pos, 0) == -1)
+ {
+ perror("kmemcpy:lseek");
+ return -1;
+ }
+ while ((r = read(kmemfd, buf, n)) < n)
+ if (r <= 0)
+ {
+ perror("kmemcpy:read");
+ return -1;
+ }
+ else
+ {
+ buf += r;
+ n -= r;
+ }
+ return 0;
+}
diff --git a/sbin/ipfstat/kmem.h b/sbin/ipfstat/kmem.h
new file mode 100644
index 00000000000..dd6af9db495
--- /dev/null
+++ b/sbin/ipfstat/kmem.h
@@ -0,0 +1,11 @@
+/*
+ * (C)opyright 1993,1994,1995 by Darren Reed.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and due credit is given
+ * to the original author and the contributors.
+ */
+
+extern int openkmem();
+extern int kmemcpy();
+