diff options
54 files changed, 9432 insertions, 6 deletions
diff --git a/etc/etc.i386/MAKEDEV b/etc/etc.i386/MAKEDEV index a5a4b3e3935..41bc67d4474 100644 --- a/etc/etc.i386/MAKEDEV +++ b/etc/etc.i386/MAKEDEV @@ -83,7 +83,7 @@ all) sh MAKEDEV std fd wt0 fd0 fd1 wd0 wd1 sd0 sd1 sd2 tty0 tty1 pty0 pty1 sh MAKEDEV st0 st1 ch0 cd0 cd1 mcd0 vnd0 vnd1 lpa0 lpa1 sh MAKEDEV ccd0 ccd1 ccd2 ccd3 - sh MAKEDEV lpt0 lpt1 lpt2 ttyv0 bpf0 bpf1 bpf2 bpf3 tun0 tun1 tun2 + sh MAKEDEV lpt0 lpt1 lpt2 ttyv0 bpf0 bpf1 bpf2 bpf3 ipl tun0 tun1 tun2 sh MAKEDEV speaker lkm mms0 lms0 pms0 audio local # MISSING: # sh MAKEDEV mouse-? @@ -283,6 +283,12 @@ bpf*|tun*) chown root.wheel $name$unit ;; +ipl) + rm -f ipl + mknod ipl c 44 0 + chown root.wheel ipl + ;; + speaker) # (XXX - installed) rm -f speaker mknod speaker c 27 0 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(); + diff --git a/sys/arch/i386/i386/conf.c b/sys/arch/i386/i386/conf.c index 1f97e5960b6..15aa1270a25 100644 --- a/sys/arch/i386/i386/conf.c +++ b/sys/arch/i386/i386/conf.c @@ -171,6 +171,19 @@ cdev_decl(audio); cdev_decl(svr4_net); cdev_decl(ccd); +/* open, close, read, ioctl */ +cdev_decl(ipl); +#define cdev_gen_ipf(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \ + (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ + (dev_type_stop((*))) nullop, 0, (dev_type_select((*))) enodev, \ + (dev_type_mmap((*))) enodev, 0 } +#ifdef IPFILTER +#define NIPF 1 +#else +#define NIPF 0 +#endif + struct cdevsw cdevsw[] = { cdev_cn_init(1,cn), /* 0: virtual console */ @@ -223,6 +236,7 @@ struct cdevsw cdevsw[] = #else cdev_notdef(), /* 43 */ #endif + cdev_gen_ipf(NIPF,ipl), /* 44 */ }; int nchrdev = sizeof(cdevsw) / sizeof(cdevsw[0]); diff --git a/sys/conf/files b/sys/conf/files index 6273730565b..543090df1d8 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -228,6 +228,8 @@ file netinet/tcp_subr.c inet file netinet/tcp_timer.c inet file netinet/tcp_usrreq.c inet file netinet/udp_usrreq.c inet +file netinet/ip_fil.c ipfilter +file netinet/fil.c ipfilter file netiso/clnp_debug.c iso file netiso/clnp_er.c iso file netiso/clnp_frag.c iso diff --git a/sys/conf/files.oldconf b/sys/conf/files.oldconf index d6eaa8a0318..d1e0f0fe919 100644 --- a/sys/conf/files.oldconf +++ b/sys/conf/files.oldconf @@ -181,6 +181,8 @@ netinet/tcp_subr.c optional inet netinet/tcp_timer.c optional inet netinet/tcp_usrreq.c optional inet netinet/udp_usrreq.c optional inet +netinet/ip_fil.c optional ipfilter requires inet +netinet/fil.c optional ipfilter requires inet netiso/clnp_debug.c optional iso netiso/clnp_er.c optional iso netiso/clnp_frag.c optional iso diff --git a/sys/netinet/fil.c b/sys/netinet/fil.c new file mode 100644 index 00000000000..b485678d5a7 --- /dev/null +++ b/sys/netinet/fil.c @@ -0,0 +1,534 @@ +/* + * (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. + */ +#ifndef lint +static char sccsid[] = "@(#)fil.c 1.18 10/24/95 (C) 1993-1995 Darren Reed"; +#endif + +#ifndef linux +# include <sys/errno.h> +# include <sys/types.h> +# include <sys/param.h> +# include <sys/file.h> +# include <sys/ioctl.h> +# if defined(_KERNEL) || defined(KERNEL) +# include <sys/systm.h> +# endif +# include <sys/uio.h> +# if !defined(__SVR4) && !defined(__svr4__) +# include <sys/dir.h> +# include <sys/mbuf.h> +# else +# include <sys/byteorder.h> +# include <sys/dditypes.h> +# include <sys/stream.h> +# endif +# include <sys/protosw.h> +# include <sys/socket.h> +# include <net/if.h> +# ifdef sun +# include <net/af.h> +# endif +# include <net/route.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/udp.h> +# include <netinet/tcpip.h> +# include <netinet/ip_icmp.h> +#endif +#include <netinet/ip_fil.h> +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef _KERNEL +#include "ipf.h" +extern int opts; +extern void debug(), verbose(); + +#define FR_IFVERBOSE(ex,second,verb_pr) if (ex) { verbose verb_pr; second; } +#define FR_IFDEBUG(ex,second,verb_pr) if (ex) { debug verb_pr; second; } +#define FR_VERBOSE(verb_pr) verbose verb_pr +#define FR_DEBUG(verb_pr) debug verb_pr +#else +#define FR_IFVERBOSE(ex,second,verb_pr) ; +#define FR_IFDEBUG(ex,second,verb_pr) ; +#define FR_VERBOSE(verb_pr) +#define FR_DEBUG(verb_pr) + +extern int ipl_unreach, ipllog(); +#endif + +struct filterstats frstats[2] = {{0,0,0,0,0},{0,0,0,0,0}}; +struct frentry *filterin[2] = { NULL, NULL }, + *filterout[2] = { NULL, NULL }; +int fr_flags = 0, fr_active = 0; +int fr_check(); + + +/* + * bit values for identifying presence of individual IP options + */ +struct optlist ipopts[20] = { + { IPOPT_NOP, 0x000001 }, + { IPOPT_RR, 0x000002 }, + { IPOPT_ZSU, 0x000004 }, + { IPOPT_MTUP, 0x000008 }, + { IPOPT_MTUR, 0x000010 }, + { IPOPT_ENCODE, 0x000020 }, + { IPOPT_TS, 0x000040 }, + { IPOPT_TR, 0x000080 }, + { IPOPT_SECURITY, 0x000100 }, + { IPOPT_LSRR, 0x000200 }, + { IPOPT_E_SEC, 0x000400 }, + { IPOPT_CIPSO, 0x000800 }, + { IPOPT_SATID, 0x001000 }, + { IPOPT_SSRR, 0x002000 }, + { IPOPT_ADDEXT, 0x004000 }, + { IPOPT_VISA, 0x008000 }, + { IPOPT_IMITD, 0x010000 }, + { IPOPT_EIP, 0x020000 }, + { IPOPT_FINN, 0x040000 }, + { 0, 0x000000 } +}; + +/* + * bit values for identifying presence of individual IP security options + */ +struct optlist secopt[8] = { + { IPSO_CLASS_RES4, 0x01 }, + { IPSO_CLASS_TOPS, 0x02 }, + { IPSO_CLASS_SECR, 0x04 }, + { IPSO_CLASS_RES3, 0x08 }, + { IPSO_CLASS_CONF, 0x10 }, + { IPSO_CLASS_UNCL, 0x20 }, + { IPSO_CLASS_RES2, 0x40 }, + { IPSO_CLASS_RES1, 0x80 } +}; + + +/* + * compact the IP header into a structure which contains just the info. + * which is useful for comparing IP headers with. + */ +struct fr_ip *fr_makefrip(hlen, ip) +int hlen; +ip_t *ip; +{ + static struct fr_ip fi; + struct optlist *op; + u_short optmsk = 0, secmsk = 0, auth = 0; + int i, mv, ol, off; + u_char *s, opt; + + fi.fi_fl = 0; + fi.fi_v = ip->ip_v; + fi.fi_tos = ip->ip_tos; + (*(((u_short *)&fi) + 1)) = (*(((u_short *)ip) + 4)); + (*(((u_long *)&fi) + 1)) = (*(((u_long *)ip) + 3)); + (*(((u_long *)&fi) + 2)) = (*(((u_long *)ip) + 4)); + + if (hlen > sizeof(struct ip)) + fi.fi_fl |= FI_OPTIONS; + off = (ip->ip_off & 0x1fff) << 3; + if (ip->ip_off & 0x3fff) + fi.fi_fl |= FI_FRAG; + switch (ip->ip_p) + { + case IPPROTO_ICMP : + if ((!IPMINLEN(ip, icmp) && !off) || + (off && off < sizeof(struct icmp))) + fi.fi_fl |= FI_SHORT; + break; + case IPPROTO_TCP : + fi.fi_fl |= FI_TCPUDP; + if ((!IPMINLEN(ip, tcphdr) && !off) || + (off && off < sizeof(struct tcphdr))) + fi.fi_fl |= FI_SHORT; + break; + case IPPROTO_UDP : + fi.fi_fl |= FI_TCPUDP; + if ((!IPMINLEN(ip, udphdr) && !off) || + (off && off < sizeof(struct udphdr))) + fi.fi_fl |= FI_SHORT; + break; + default : + break; + } + + for (s = (u_char *)(ip + 1), hlen -= sizeof(*ip); hlen; ) { + if (!(opt = *s)) + break; + ol = (opt == IPOPT_NOP) ? 1 : (int)*(s+1); + if (opt > 1 && (ol < 0 || ol > hlen)) + break; + for (i = 9, mv = 4; mv >= 0; ) { + op = ipopts + i; + if (opt == (u_char)op->ol_val) { + optmsk |= op->ol_bit; + if (opt == IPOPT_SECURITY) { + struct optlist *sp; + u_char sec; + int j, m; + + sec = *(s + 3); /* classification */ + for (j = 3, m = 2; m >= 0; ) { + sp = secopt + j; + if (sec == sp->ol_val) { + secmsk |= sp->ol_bit; + auth = *(s + 3); + auth *= 256; + auth += *(s + 4); + break; + } + if (sec < sp->ol_val) + j -= m--; + else + j += m--; + } + } + break; + } + if (opt < op->ol_val) + i -= mv--; + else + i += mv--; + } + hlen -= ol; + s += ol; + } + if (auth && !(auth & 0x0100)) + auth &= 0xff00; + fi.fi_optmsk = optmsk; + fi.fi_secmsk = secmsk; + fi.fi_auth = auth; + return &fi; +} + + +/* + * check an IP packet for TCP/UDP characteristics such as ports and flags. + */ +int fr_tcpudpchk(ip, tcp, fr) +ip_t *ip; +tcphdr_t *tcp; +struct frentry *fr; +{ + register u_short po, tup; + register char i; + int err = 1; + + /* + * Both ports should *always* be in the first fragment. + * So far, I cannot find any cases where they can not be. + * + * compare destination ports + */ + if ((i = (int)fr->fr_dcmp)) { + po = ntohs(fr->fr_dport); + tup = ntohs(tcp->th_dport); + /* + * Do opposite test to that required and + * continue if that succeeds. + */ + if (!--i && tup != po) /* EQUAL */ + err = 0; + else if (!--i && tup == po) /* NOTEQUAL */ + err = 0; + else if (!--i && tup >= po) /* LESSTHAN */ + err = 0; + else if (!--i && tup <= po) /* GREATERTHAN */ + err = 0; + else if (!--i && tup > po) /* LT or EQ */ + err = 0; + else if (!--i && tup < po) /* GT or EQ */ + err = 0; + else if (!--i && /* Out of range */ + (tup >= po && tup <= ntohs(fr->fr_dtop))) + err = 0; + else if (!--i && /* In range */ + (tup <= po || tup >= ntohs(fr->fr_dtop))) + err = 0; + } + /* + * compare source ports + */ + if (err && (i = (int)fr->fr_scmp)) { + po = ntohs(fr->fr_sport); + tup = ntohs(tcp->th_sport); + if (!--i && tup != po) + err = 0; + else if (!--i && tup == po) + err = 0; + else if (!--i && tup >= po) + err = 0; + else if (!--i && tup <= po) + err = 0; + else if (!--i && tup > po) + err = 0; + else if (!--i && tup < po) + err = 0; + else if (!--i && /* Out of range */ + (tup >= po && tup <= ntohs(fr->fr_stop))) + err = 0; + else if (!--i && /* In range */ + (tup <= po || tup >= ntohs(fr->fr_stop))) + err = 0; + } + + /* + * If we don't have all the TCP/UDP header, then how can we + * expect to do any sort of match on it ? If we were looking for + * TCP flags, then NO match. If not, then match (which should + * satisfy the "short" class too). + */ + if (err) + if (ip->ip_p == IPPROTO_TCP) { + if (!IPMINLEN(ip, tcphdr)) + return !(fr->fr_tcpf); + /* + * Match the flags ? If not, abort this match. + */ + if (fr->fr_tcpf && + fr->fr_tcpf != (tcp->th_flags & fr->fr_tcpfm)) { + FR_DEBUG(("f. %#x & %#x != %#x\n", + tcp->th_flags, fr->fr_tcpfm, + fr->fr_tcpf)); + err = 0; + } + } + else if (!IPMINLEN(ip, udphdr)) /* must be UDP */ + return 1; + return err; +} + +/* + * Check the input/output list of rules for a match and result. + * Could be per interface, but this gets real nasty when you don't have + * kernel sauce. + */ +int fr_scanlist(pass, ip, hlen, ifp, out, rule) +int pass; +ip_t *ip; +int hlen, out; +struct ifnet *ifp; +u_short *rule; +{ + register struct frentry *fr; + register struct fr_ip *fi; + tcphdr_t *tcp; + int rulen; + + *rule = 1; + tcp = (tcphdr_t *)((char *)ip + hlen); + fr = (out) ? filterout[fr_active] : filterin[fr_active]; + fi = fr_makefrip(hlen, ip); + + for (rulen = 0; fr; fr = fr->fr_next, rulen++) { + /* + * In all checks below, a null (zero) value in the + * filter struture is taken to mean a wildcard. + * + * check that we are working for the right interface + */ +#ifdef _KERNEL + if (fr->fr_ifa && fr->fr_ifa != ifp) + continue; +#else + if (opts & (OPT_VERBOSE|OPT_DEBUG)) + printf("\n"); + FR_VERBOSE(("%c", (pass & FR_PASS) ? 'p' : 'b')); + if (ifp && *fr->fr_ifname && strcasecmp(ifp->if_name, + fr->fr_ifname)) + continue; + FR_VERBOSE((":i")); +#endif + { + register u_long *ld, *lm, *lip; + register int i; + + lip = (u_long *)fi; + lm = (u_long *)&fr->fr_mip; + ld = (u_long *)&fr->fr_ip; + i = ((lip[0] & lm[0]) != ld[0]); + FR_IFDEBUG(i,continue,("0. %#08x & %#08x != %#08x\n", + lip[0], lm[0], ld[0])); + i |= ((lip[1] & lm[1]) != ld[1]); + FR_IFDEBUG(i,continue,("1. %#08x & %#08x != %#08x\n", + lip[1], lm[1], ld[1])); + i |= ((lip[2] & lm[2]) != ld[2]); + FR_IFDEBUG(i,continue,("2. %#08x & %#08x != %#08x\n", + lip[2], lm[2], ld[2])); + i |= ((lip[3] & lm[3]) != ld[3]); + FR_IFDEBUG(i,continue,("3. %#08x & %#08x != %#08x\n", + lip[3], lm[3], ld[3])); + i |= ((lip[4] & lm[4]) != ld[4]); + FR_IFDEBUG(i,continue,("4. %#08x & %#08x != %#08x\n", + lip[4], lm[4], ld[4])); + if (i) + continue; + } + + /* + * If a fragment, then only the first has what we're looking + * for here... + */ + if (!(ip->ip_off & 0x1fff)) { + if ((fi->fi_fl & FI_TCPUDP) && + !fr_tcpudpchk(ip, tcp, fr)) + continue; + else if (ip->ip_p == IPPROTO_ICMP && + (*(u_short *)((char *)ip + hlen) & + fr->fr_icmpm) != fr->fr_icmp) { + FR_DEBUG(("i. %#x & %#x != %#x\n", + *(u_short *)((char *)ip + hlen), + fr->fr_icmpm, fr->fr_icmp)); + continue; + } + } else if (fr->fr_dcmp || fr->fr_scmp || fr->fr_icmpm || + fr->fr_tcpfm) + continue; + FR_VERBOSE(("*")); + /* + * Just log this packet... + */ + if (fr->fr_flags & FR_LOG) { +#ifdef IPFILTER_LOG + if (!ipllog(hlen, fr->fr_flags, ip, ifp, *rule)) + frstats[out].fr_skip++; + frstats[out].fr_pkl++; +#endif /* IPFILTER_LOG */ + } else + pass = fr->fr_flags; + FR_DEBUG(("pass %#x\n", pass)); + fr->fr_hits++; + *rule = rulen; + if (pass & FR_QUICK) + break; + } + return pass; +} + + +/* + * frcheck - filter check + * check using source and destination addresses/pors in a packet whether + * or not to pass it on or not. + */ +int fr_check(ip, hlen, ifp, out +#if SOLARIS && defined(_KERNEL) +, qif, q) +qif_t *qif; +queue_t *q; +#else +) +#endif +ip_t *ip; +int hlen; +struct ifnet *ifp; +int out; +{ + int pass = FR_NOMATCH; + int sl; + u_short rule; + + SPLNET(sl); + + pass = fr_scanlist(pass, ip, hlen, ifp, out, &rule); + if (pass == FR_NOMATCH) { + frstats[out].fr_nom++; +#ifdef NOMATCH + pass |= NOMATCH; +#endif + } + +#ifdef IPFILTER_LOG + if ((pass & FR_LOGP) || + ((pass & FR_PASS) && (fr_flags & FF_LOGPASS))) { + if (!(pass & FR_LOGP)) + pass |= FF_LOGPASS << 8; + if (!ipllog(hlen, pass, ip, ifp, rule)) + frstats[out].fr_skip++; + frstats[out].fr_ppkl++; + } else if ((pass & FR_LOGB) || + ((pass & FR_BLOCK) && (fr_flags & FF_LOGBLOCK))) { + if (!(pass & FR_LOGB)) + pass |= FF_LOGBLOCK << 8; + if (!ipllog(hlen, pass, ip, ifp, rule)) + frstats[out].fr_skip++; + frstats[out].fr_bpkl++; + } +#endif /* IPFILTER_LOG */ + SPLX(sl); + if (pass & FR_PASS) + frstats[out].fr_pass++; + else if (pass & FR_BLOCK) { + frstats[out].fr_block++; + /* + * Should we return an ICMP packet to indicate error + * status passing through the packet filter ? + * XXX - copy mbuf as icmp_error() calls mfree() - fix this + * later, but preserve backward compatibility for now. + */ +#ifdef _KERNEL + if (pass & FR_RETICMP) { +# if SOLARIS + icmp_error(q, ip, ICMP_UNREACH, ipl_unreach, qif, + ip->ip_src); +# else + struct mbuf *copy; + + copy = m_copy(dtom(ip), 0, imin((int)ip->ip_len, 64)); +# if BSD < 199103 + icmp_error(mtod(copy, struct ip *), + ICMP_UNREACH, ipl_unreach, ifp, ip->ip_src); +# else + icmp_error(copy, ICMP_UNREACH, ipl_unreach, + ip->ip_src.s_addr, ifp); +# endif +# endif + frstats[0].fr_ret++; + } else if (pass & FR_RETRST && IPMINLEN(ip, tcphdr)) { +# if SOLARIS + if (send_reset(ip, qif, q) == 0) +# else + if (send_reset(ip) == 0) +# endif + frstats[1].fr_ret++; + } +#else + if (pass & FR_RETICMP) { + verbose("- ICMP unreachable sent\n"); + frstats[0].fr_ret++; + } else if (pass & FR_RETRST && IPMINLEN(ip, tcphdr)) { + verbose("- TCP RST sent\n"); + frstats[1].fr_ret++; + } +#endif + } +#ifdef _KERNEL + return (pass & FR_PASS) ? 0 : -1; +#else + if (pass & FR_NOMATCH) + return 1; + if (pass & FR_PASS) + return 0; + return -1; +#endif +} + + +#ifndef _KERNEL +int ipllog() +{ + verbose("l"); + return 1; +} +#endif diff --git a/sys/netinet/ip_fil.c b/sys/netinet/ip_fil.c new file mode 100644 index 00000000000..f234291a923 --- /dev/null +++ b/sys/netinet/ip_fil.c @@ -0,0 +1,585 @@ +/* + * (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. + */ +#ifndef lint +static char sccsid[] = "@(#)ip_fil.c 2.26 11/8/95 (C) 1993-1995 Darren Reed"; +#endif + +#ifndef linux +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/dir.h> +#include <sys/ioctl.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> + +#include <net/if.h> +#ifdef sun +#include <net/af.h> +#endif +#include <net/route.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/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include <syslog.h> +#endif +#include "ip_fil.h" +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +extern fr_flags, fr_active; +extern int fr_check(), (*fr_checkp)(); +#if BSD < 199306 +extern int tcp_ttl; +#else +extern int ip_defttl; +#endif + +int ipl_inited = 0; +int ipl_unreach = ICMP_UNREACH_FILTER; +int send_reset(); + +#ifdef IPFILTER_LOG +#define LOGSIZE 8192 +int ipllog(); +static char iplbuf[LOGSIZE]; +static caddr_t iplh = iplbuf, iplt = iplbuf; +static int iplused = 0; +#endif /* IPFILTER_LOG */ +static void frflush(); +static int frrequest(); +static int iplbusy = 0; +static int (*fr_savep)(); + + +#ifdef IPFILTER_LKM +int iplidentify(s) +char *s; +{ + if (strcmp(s, "ipl") == 0) + return 1; + return 0; +} + + +int iplattach() +{ + int s; + + SPLNET(s); + if (ipl_inited || (fr_checkp == fr_check)) { + printf("ipl: already initialized (%d)\n", iplbusy); + SPLX(s); + return EBUSY; + } + ipl_inited = 1; + fr_savep = fr_checkp; + fr_checkp = fr_check; + + SPLX(s); + return 0; +} + + +int ipldetach() +{ + int s, i = FR_INQUE|FR_OUTQUE; + + if (iplbusy) { + printf("iplbusy: %d\n", iplbusy); + return EBUSY; + } + SPLNET(s); + if (!ipl_inited) + { + printf("ipl: not initialized\n"); + SPLX(s); + return EBUSY; + } + + fr_checkp = fr_savep; + frflush((caddr_t)&i); + ipl_inited = 0; + + SPLX(s); + return 0; +} +#endif /* IPFILTER_LKM */ + + +static void frzerostats(data) +caddr_t data; +{ + struct friostat fio; + + bcopy((char *)frstats, (char *)fio.f_st, + sizeof(struct filterstats) * 2); + fio.f_fin[0] = filterin[0]; + fio.f_fin[1] = filterin[1]; + fio.f_fout[0] = filterout[0]; + fio.f_fout[1] = filterout[1]; + fio.f_active = fr_active; + IWCOPY((caddr_t)&fio, data, sizeof(fio)); + bzero((char *)frstats, sizeof(*frstats)); +} + + +static void frflush(data) +caddr_t data; +{ + struct frentry *f, **fp; + int flags = *(int *)data, flushed = 0, set = fr_active; + + if (flags & FR_INACTIVE) + set = 1 - set; + if (flags & FR_OUTQUE) + for (fp = &filterout[set]; (f = *fp); ) { + *fp = f->fr_next; + KFREE(f); + flushed++; + } + if (flags & FR_INQUE) + for (fp = &filterin[set]; (f = *fp); ) { + *fp = f->fr_next; + KFREE(f); + flushed++; + } + + *(int *)data = flushed; +} + + +/* + * Filter ioctl interface. + */ +int iplioctl(dev, cmd, data, mode) +dev_t dev; +int cmd; +caddr_t data; +int mode; +{ + int error = 0, s, unit; + + unit = minor(dev); + if (unit != 0) + return ENXIO; + + SPLNET(s); + switch (cmd) { +#ifndef IPFILTER_LKM + case SIOCFRENB : + { + u_int enable; + + IRCOPY(data, (caddr_t)&enable, sizeof(enable)); + if (enable) { + if (fr_checkp != fr_check) { + fr_savep = fr_checkp; + fr_checkp = fr_check; + } + } else + fr_checkp = fr_savep; + break; + } +#endif + case SIOCSETFF : + IRCOPY(data, (caddr_t)&fr_flags, sizeof(fr_flags)); + break; + case SIOCGETFF : + IWCOPY((caddr_t)&fr_flags, data, sizeof(fr_flags)); + break; + case SIOCINAFR : + case SIOCRMAFR : + case SIOCADAFR : + error = frrequest(cmd, (struct frentry *)data, fr_active); + break; + case SIOCINIFR : + case SIOCRMIFR : + case SIOCADIFR : + error = frrequest(cmd, (struct frentry *)data, 1-fr_active); + break; + case SIOCSWAPA : + *(u_int *)data = fr_active; + fr_active = 1 - fr_active; + break; + case SIOCGETFS : + { + struct friostat fio; + + bcopy((char *)frstats, (char *)fio.f_st, + sizeof(struct filterstats) * 2); + fio.f_fin[0] = filterin[0]; + fio.f_fin[1] = filterin[1]; + fio.f_fout[0] = filterout[0]; + fio.f_fout[1] = filterout[1]; + fio.f_active = fr_active; + IWCOPY((caddr_t)&fio, data, sizeof(fio)); + break; + } + case SIOCFRZST : + frzerostats(data); + break; + case SIOCIPFFL : + frflush(data); + break; +#ifdef IPFILTER_LOG + case SIOCIPFFB : + *(int *)data = iplused; + iplh = iplt = iplbuf; + iplused = 0; + break; +#endif /* IPFILTER_LOG */ + default : + error = -EINVAL; + break; + } + SPLX(s); + return error; +} + + +static int frrequest(req, fp, set) +int req, set; +register struct frentry *fp; +{ + register struct frentry *f, **fprev; + register struct frentry **ftail; + struct frentry frd; + int error = 0; + + if (fp->fr_flags & FR_OUTQUE) + ftail = fprev = &filterout[set]; + else if (fp->fr_flags & FR_INQUE) + ftail = fprev = &filterin[set]; + else + return ESRCH; + + IRCOPY((char *)fp, (char *)&frd, sizeof(frd)); + fp = &frd; + if (*fp->fr_ifname) { + fp->fr_ifa = GETUNIT(fp->fr_ifname); + if (!fp->fr_ifa) + fp->fr_ifa = (struct ifnet *)-1; + } + /* + * Look for a matching filter rule, but don't include the next or + * interface pointer in the comparison (fr_next, fr_ifa). + */ + for (; f = *ftail; ftail = &f->fr_next) + if (bcmp((char *)&f->fr_ip, (char *)&fp->fr_ip, + FR_CMPSIZ) == 0) + break; + if (!f) { + ftail = fprev; + if (req != SIOCINAFR && req != SIOCINIFR) + while ((f = *ftail)) + ftail = &f->fr_next; + else if (fp->fr_hits) + while (--fp->fr_hits && (f = *ftail)) + ftail = &f->fr_next; + f = NULL; + } + + if (req == SIOCDELFR || req == SIOCRMIFR) { + if (!f) + error = ESRCH; + else { + *ftail = f->fr_next; + (void) KFREE(f); + } + } else { + if (f) + error = EEXIST; + else { + if ((f = (struct frentry *)KMALLOC(sizeof(*f)))) { + bcopy((char *)fp, (char *)f, sizeof(*f)); + f->fr_hits = 0; + f->fr_next = *ftail; + *ftail = f; + } else + error = ENOMEM; + } + } + return (error); +} + + +#if !defined(linux) +/* + * routines below for saving IP headers to buffer + */ +int iplopen(dev, flags) +dev_t dev; +int flags; +{ + u_int min = minor(dev); + + if ((flags & FWRITE) || min) + min = ENXIO; + else + iplbusy++; + return min; +} + + +int iplclose(dev, flags) +dev_t dev; +int flags; +{ + u_int min = minor(dev); + + if (min) + min = ENXIO; + else if (iplbusy > 0) + iplbusy--; + return min; +} + +# ifdef IPFILTER_LOG +/* + * iplread/ipllog + * both of these must operate with at least splnet() lest they be + * called during packet processing and cause an inconsistancy to appear in + * the filter lists. + */ +# if BSD >= 199306 +int iplread(dev, uio, ioflag) +int ioflag; +# else +int iplread(dev, uio) +# endif +dev_t dev; +register struct uio *uio; +{ + register int ret, s; + register size_t sz, sx; + int error; + + if (!uio->uio_resid) + return 0; + while (!iplused) { + error = SLEEP(iplbuf, "ipl sleep"); + if (error) + return error; + } + + SPLNET(s); + + sx = sz = MIN(uio->uio_resid, iplused); + if (iplh < iplt) + sz = MIN(sz, LOGSIZE - (iplt - iplbuf)); + sx -= sz; + +# if BSD >= 199306 || defined(__FreeBSD__) + uio->uio_rw = UIO_READ; +# endif + if (!(ret = UIOMOVE(iplt, sz, UIO_READ, uio))) { + iplt += sz; + iplused -= sz; + if ((iplh < iplt) && (iplt == iplbuf + LOGSIZE)) + iplt = iplbuf; + + if (sx && !(ret = UIOMOVE(iplt, sx, UIO_READ, uio))) { + iplt += sx; + iplused -= sx; + if ((iplh < iplt) && (iplt == iplbuf + LOGSIZE)) + iplt = iplbuf; + } + if (!iplused) /* minimise wrapping around the end */ + iplh = iplt = iplbuf; + } + SPLX(s); + return ret; +} +# endif /* IPFILTER_LOG */ +#endif /* linux */ + + +#ifdef IPFILTER_LOG +int ipllog(hlen, flags, ip, ifp, rule) +register int hlen; +u_int flags; +ip_t *ip; +struct ifnet *ifp; +u_short rule; +{ + struct ipl_ci iplci; + register size_t tail = 0; + register int len, mlen; + register struct mbuf *m = dtom(ip); + + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + hlen += sizeof(tcphdr_t); + else if (ip->ip_p == IPPROTO_ICMP) { + struct icmp *icmp = (struct icmp *)((char *)ip + hlen); + + switch (icmp->icmp_type) { + case ICMP_UNREACH : + case ICMP_SOURCEQUENCH : + case ICMP_REDIRECT : + case ICMP_TIMXCEED : + case ICMP_PARAMPROB : + hlen += 8; + default : + hlen += sizeof(struct icmp); + } + } + + mlen = (flags & FR_LOGBODY) ? (MIN(m->m_len, 128) & 0xfa) : 0; + len = hlen + sizeof(iplci) + mlen; + if (iplused + len > LOGSIZE) + return 0; + iplused += len; + +# ifdef sun + uniqtime(&iplci); +# endif +# if BSD >= 199306 || defined(__FreeBSD__) + microtime((struct timeval *)&iplci); +# endif + iplci.flags = flags; + iplci.hlen = (u_char)hlen; + iplci.plen = (flags & FR_LOGBODY) ? (u_char)mlen : 0 ; + iplci.rule = rule; + iplci.unit = (u_char)ifp->if_unit; + iplci.ifname[0] = ifp->if_name[0]; + iplci.ifname[1] = ifp->if_name[1]; + iplci.ifname[2] = ifp->if_name[2]; + iplci.ifname[3] = ifp->if_name[3]; + + if (iplh == iplbuf + LOGSIZE) + iplh = iplbuf; + tail = (iplh >= iplt) ? (iplbuf + LOGSIZE - iplh) : (iplt - iplh); + + len = MIN(tail, sizeof(iplci)); + + /* + * check in both cases where we add stuff to the buffer to see if we + * are going to wrap around at the end. + */ + bcopy((char *)&iplci, iplh, len); + iplh += len; + if (len < sizeof(iplci)) { + bcopy((char *)&iplci + len, iplbuf, sizeof(iplci) - len); + iplh = iplbuf + sizeof(iplci) - len; + tail = iplt - iplh; + } else + tail -= len; + + len = MIN(tail, hlen); + bcopy((char *)ip, iplh, len); + iplh += len; + if (len < hlen) { + iplh = iplbuf; + bcopy((char *)ip + len, iplh, hlen - len); + iplh += hlen - len; + tail = iplt - iplh; + } else + tail -= len; + + if (mlen) { + len = MIN(tail, mlen); +#if BSD < 199103 + bcopy((char *)m->m_un.mun_dat, iplh, len); +#else + bcopy((char *)m->M_dat.M_databuf, iplh, len); +#endif + iplh += len; + if (len < mlen) { + iplh = iplbuf; +#if BSD < 199103 + bcopy((char *)m->m_un.mun_dat + len, iplh, + mlen - len); +#else + bcopy((char *)m->M_dat.M_databuf + len, iplh, + mlen - len); +#endif + iplh += mlen - len; + } + } + wakeup(iplbuf); + return 1; +} +#endif /* IPFILTER_LOG */ + +/* + * send_reset - this could conceivably be a call to tcp_respond(), but that + * requires a large amount of setting up and isn't any more efficient. + */ +int send_reset(ti) +struct tcpiphdr *ti; +{ + struct tcpiphdr *tp; + struct ip *ip; + struct tcphdr *tcp; + struct mbuf *m; + int tlen = 0; + + if (ti->ti_flags & TH_RST) + return -1; /* feedback loop */ +#if BSD < 199306 + m = m_get(M_DONTWAIT, MT_HEADER); +#else + m = m_gethdr(M_DONTWAIT, MT_HEADER); + m->m_data += max_linkhdr; +#endif + if (m == NULL) + return -1; + + if (ti->ti_flags & TH_SYN) + tlen = 1; + m->m_len = sizeof (struct tcpiphdr); +#if BSD >= 199306 + m->m_pkthdr.len = sizeof (struct tcpiphdr); + m->m_pkthdr.rcvif = (struct ifnet *)0; +#endif + bzero(mtod(m, char *), sizeof(struct tcpiphdr)); + ip = mtod(m, struct ip *); + tp = mtod(m, struct tcpiphdr *); + tcp = (struct tcphdr *)((char *)ip + sizeof(struct ip)); + + ip->ip_src.s_addr = ti->ti_dst.s_addr; + ip->ip_dst.s_addr = ti->ti_src.s_addr; + tcp->th_dport = ti->ti_sport; + tcp->th_sport = ti->ti_dport; + tcp->th_ack = htonl(ntohl(ti->ti_seq) + tlen); + tcp->th_off = sizeof(struct tcphdr) >> 2; + tcp->th_flags = TH_RST|TH_ACK; + tp->ti_pr = ((struct ip *)ti)->ip_p; + tp->ti_len = htons(sizeof(struct tcphdr)); + tcp->th_sum = in_cksum(m, sizeof(struct tcpiphdr)); + + ip->ip_hl = sizeof(struct ip) >> 2; + ip->ip_v = IPVERSION; + ip->ip_tos = ((struct ip *)ti)->ip_tos; + ip->ip_id = ((struct ip *)ti)->ip_id; + ip->ip_off = ((struct ip *)ti)->ip_off; + ip->ip_p = ((struct ip *)ti)->ip_p; + ip->ip_len = sizeof (struct tcpiphdr); +#if BSD < 199306 + ip->ip_ttl = tcp_ttl; +#else + ip->ip_ttl = ip_defttl; +#endif + + /* + * extra 0 in case of multicast + */ + (void) ip_output(m, (struct mbuf *)0, 0, IP_FORWARDING, 0); + return 0; +} diff --git a/sys/netinet/ip_fil.h b/sys/netinet/ip_fil.h new file mode 100644 index 00000000000..8d4380dd4cc --- /dev/null +++ b/sys/netinet/ip_fil.h @@ -0,0 +1,463 @@ +/* + * (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. + * + * @(#)ip_fil.h 1.23 11/11/95 + */ + +#ifndef __IP_FIL_H_ +#define __IP_FIL_H__ + +#ifndef IPFILTER_LOG +#define IPFILTER_LOG 1 +#endif + +#define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) +#if defined(KERNEL) && !defined(_KERNEL) +#define _KERNEL +#endif +#if SOLARIS +# include <sys/ioccom.h> +# include <sys/sysmacros.h> +# ifdef _KERNEL +# include <inet/common.h> +/* + * because Solaris 2 defines these in two places :-/ + */ +#undef IPOPT_EOL +#undef IPOPT_NOP +#undef IPOPT_LSRR +#undef IPOPT_RR +#undef IPOPT_SSRR +# include <inet/ip.h> +# endif +#endif + +#if defined(__STDC__) || defined(__GNUC__) +#define SIOCADAFR _IOW('r', 60, struct frentry) +#define SIOCRMAFR _IOW('r', 61, struct frentry) +#define SIOCSETFF _IOW('r', 62, u_int) +#define SIOCGETFF _IOR('r', 63, u_int) +#define SIOCGETFS _IOR('r', 64, struct friostat) +#define SIOCIPFFL _IOWR('r', 65, int) +#define SIOCIPFFB _IOR('r', 66, int) +#define SIOCADIFR _IOW('r', 67, struct frentry) +#define SIOCRMIFR _IOW('r', 68, struct frentry) +#define SIOCSWAPA _IOR('r', 69, u_int) +#define SIOCINAFR _IOW('r', 70, struct frentry) +#define SIOCINIFR _IOW('r', 71, struct frentry) +#define SIOCFRENB _IOW('r', 72, u_int) +#define SIOCFRSYN _IOW('r', 73, u_int) +#define SIOCFRZST _IOWR('r', 74, struct friostat) +#else +#define SIOCADAFR _IOW(r, 60, struct frentry) +#define SIOCRMAFR _IOW(r, 61, struct frentry) +#define SIOCSETFF _IOW(r, 62, u_int) +#define SIOCGETFF _IOR(r, 63, u_int) +#define SIOCGETFS _IOR(r, 64, struct friostat) +#define SIOCIPFFL _IOWR(r, 65, int) +#define SIOCIPFFB _IOR(r, 66, int) +#define SIOCADIFR _IOW(r, 67, struct frentry) +#define SIOCRMIFR _IOW(r, 68, struct frentry) +#define SIOCSWAPA _IOR(r, 69, u_int) +#define SIOCINAFR _IOW(r, 70, struct frentry) +#define SIOCINIFR _IOW(r, 71, struct frentry) +#define SIOCFRENB _IOW(r, 72, u_int) +#define SIOCFRSYN _IOW(r, 73, u_int) +#define SIOCFRZST _IOWR(r, 74, struct friostat) +#endif +#define SIOCADDFR SIOCADAFR +#define SIOCDELFR SIOCRMAFR +#define SIOCINSFR SIOCINAFR + +typedef struct fr_ip { + u_char fi_v:4; + u_char fi_fl:4; + u_char fi_tos; + u_char fi_ttl; + u_char fi_p; + struct in_addr fi_src; + struct in_addr fi_dst; + u_long fi_optmsk; + u_short fi_secmsk; + u_short fi_auth; +} fr_ip_t; + +#define FI_SHORT 0x01 +#define FI_OPTIONS 0x02 +#define FI_FRAG 0x04 +#define FI_TCPUDP 0x08 /* TCP/UCP implied comparison involved */ + +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 fr_ip fr_ip; + struct fr_ip fr_mip; + + u_char fr_tcpfm; /* tcp flags mask */ + u_char fr_tcpf; /* tcp flags */ + + u_short fr_icmpm; /* data for ICMP packets (mask) */ + u_short fr_icmp; + + 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 (see below) */ + char fr_ifname[IFNAMSIZ]; +} frentry_t; + +#define fr_proto fr_ip.fi_p +#define fr_ttl fr_ip.fi_ttl +#define fr_tos fr_ip.fi_tos +#define fr_dst fr_ip.fi_dst +#define fr_src fr_ip.fi_src +#define fr_dmsk fr_mip.fi_dst +#define fr_smsk fr_mip.fi_src + +#ifndef offsetof +#define offsetof(t,m) (int)((&((t *)0L)->m)) +#endif +#define FR_CMPSIZ (sizeof(struct frentry) - offsetof(frentry_t, fr_ip)) + +/* + * fr_flags + */ +#define FR_BLOCK 0x0001 +#define FR_PASS 0x0002 +#define FR_OUTQUE 0x0004 +#define FR_INQUE 0x0008 +#define FR_LOGP 0x0010 /* Log-pass */ +#define FR_LOGB 0x0020 /* Log-fail */ +#define FR_LOG 0x0040 /* Log */ +#define FR_LOGBODY 0x0080 /* Log the body */ +#define FR_QUICK 0x0100 +#define FR_RETRST 0x0200 +#define FR_RETICMP 0x0400 +#define FR_INACTIVE 0x0800 +#define FR_NOMATCH 0x1000 + +#define FR_NONE 0 +#define FR_EQUAL 1 +#define FR_NEQUAL 2 +#define FR_LESST 3 +#define FR_GREATERT 4 +#define FR_LESSTE 5 +#define FR_GREATERTE 6 +#define FR_OUTRANGE 7 +#define FR_INRANGE 8 + +typedef struct filterstats { + u_long fr_pass; /* packets allowed */ + u_long fr_block; /* packets denied */ + u_long fr_nom; /* packets which don't match any rule */ + 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 */ + u_long fr_ret; /* packets for which a return is sent */ +#if SOLARIS + u_long fr_bad; /* bad IP packets to the filter */ + u_long fr_notip; /* packets passed through no on ip queue */ + u_long fr_drop; /* packets dropped - no info for them! */ +#endif +} filterstats_t; + +/* + * recognized flags for SIOCGETFF and SIOCSETFF + */ +#define FF_LOGPASS 1 +#define FF_LOGBLOCK 2 + +/* + * For SIOCGETFS + */ +typedef struct friostat { + struct filterstats f_st[2]; + struct frentry *f_fin[2]; + struct frentry *f_fout[2]; + int f_active; +} friostat_t; + +typedef struct optlist { + u_short ol_val; + int ol_bit; +} optlist_t; + +#ifdef _KERNEL +extern struct frentry *filterin[], *filterout[]; +extern struct filterstats frstats[]; +#endif + +typedef struct ipl_ci { + u_long sec; + u_long usec; + u_char hlen; + u_char plen; + u_short rule; + u_long flags:24; + u_long unit:8; + u_char ifname[4]; +} ipl_ci_t; + +#ifdef _KERNEL + +typedef struct ipfr { + struct ipfr *ipfr_next, *ipfr_prev; + struct in_addr ipfr_src; + struct in_addr ipfr_dst; + u_short ipfr_id; + u_short ipfr_age; + u_char ipfr_p; + u_char ipfr_tos; + u_char ipfr_pass; +} ipfr_t; + +#define IPFR_CMPSZ (4 + 4 + 2 + 1 + 1) + +# if defined(sun) && !defined(linux) +# define UIOMOVE(a,b,c,d) uiomove(a,b,c,d) +# define SLEEP(id, n) sleep((id), PZERO+1) +# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) +# if SOLARIS +# ifdef sparc +# define ntohs(x) (x) +# define ntohl(x) (x) +# define htons(x) (x) +# define htonl(x) (x) +# endif +# define KMALLOC(x) kmem_alloc((x), KM_SLEEP) +# define GET_MINOR(x) getminor(x) +# else +# define KMALLOC(x) new_kmem_alloc((x), KMEM_SLEEP) +# endif /* __svr4__ */ +# endif /* sun && !linux */ +# ifndef GET_MINOR +# define GET_MINOR(x) minor(x) +# endif +# if BSD >= 199306 || defined(__FreeBSD__) +# include <vm/vm.h> +# if !defined(__FreeBSD__) +# include <vm/vm_extern.h> +# include <sys/proc.h> +extern vm_map_t kmem_map; +# else +# include <vm/vm_kern.h> +# endif /* __FreeBSD__ */ +# define KMALLOC(x) kmem_alloc(kmem_map, (x)) +# define KFREE(x) kmem_free(kmem_map, (vm_offset_t)(x), \ + sizeof(*(x))) +# define UIOMOVE(a,b,c,d) uiomove(a,b,d) +# define SLEEP(id, n) tsleep((id), PPAUSE|PCATCH, n, 0) +# else +# endif /* BSD */ +#endif /* _KERNEL */ + +#ifdef linux +# define ICMP_UNREACH ICMP_DEST_UNREACH +# define ICMP_SOURCEQUENCH ICMP_SOURCE_QUENCH +# define ICMP_TIMXCEED ICMP_TIME_EXCEEDED +# define ICMP_PARAMPROB ICMP_PARAMETERPROB +# define icmp icmphdr +# define icmp_type type +# define icmp_code code + +# define TH_FIN 0x01 +# define TH_SYN 0x02 +# define TH_RST 0x04 +# define TH_PUSH 0x08 +# define TH_ACK 0x10 +# define TH_URG 0x20 + +typedef struct { + __u16 th_sport; + __u16 th_dport; + __u32 th_seq; + __u32 th_ack; + __u8 th_x; + __u8 th_flags; + __u16 th_win; + __u16 th_sum; + __u16 th_urp; +} tcphdr_t; + +typedef struct { +# if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ + defined(vax) + __u8 ip_hl:4; + __u8 ip_v:4; +# else + __u8 ip_hl:4; + __u8 ip_v:4; +# endif + __u8 ip_tos; + __u16 ip_len; + __u16 ip_id; + __u16 ip_off; + __u8 ip_ttl; + __u8 ip_p; + __u16 ip_sum; + __u32 ip_src; + __u32 ip_dst; +} ip_t; + +# define SPLX(x) ; +# define SPLNET(x) ; + +# define bcopy(a,b,c) memmove(b,a,c) +# define bcmp(a,b,c) memcmp(a,b,c) + +# define UNITNAME(n) dev_get((n)) +# define ifnet device + +# define KMALLOC(x) kmalloc((x), GFP_ATOMIC) +# define KFREE(x) kfree_s((x), sizeof(*(x))) +# define IRCOPY(a,b,c) { \ + error = verify_area(VERIFY_READ, \ + (b) ,sizeof((b))); \ + if (!error) \ + memcpy_fromfs((b), (a), (c)); \ + } +# define IWCOPY(a,b,c) { \ + error = verify_area(VERIFY_WRITE, \ + (b) ,sizeof((b))); \ + if (!error) \ + memcpy_tofs((b), (a), (c)); \ + } +#else + +typedef struct tcphdr tcphdr_t; +typedef struct ip ip_t; + +# if SOLARIS +# define MTOD(m,t) (t)((m)->b_rptr) +# define IRCOPY(a,b,c) copyin((a), (b), (c)) +# define IWCOPY(a,b,c) copyout((a), (b), (c)) +# ifdef _KERNEL +typedef struct qif { + struct qif *qf_next; + ill_t *qf_ill; + kmutex_t qf_lock; + void *qf_iptr; + void *qf_optr; + queue_t *qf_in; + queue_t *qf_out; + void *qf_wqinfo; + void *qf_rqinfo; + char qf_name[8]; + int (*qf_inp)(); + int (*qf_outp)(); + /* + * in case the ILL has disappeared... + */ + int qf_hl; /* header length */ +} qif_t; +# endif /* _KERNEL */ +# else +# define MTOD(m,t) mtod(m,t) +# define IRCOPY(a,b,c) bcopy((a), (b), (c)) +# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +# endif /* SOLARIS */ +# ifdef _KERNEL +# if defined(NetBSD1_0) && (NetBSD1_0 > 1) +# define SPLNET(x) x = splsoftnet() +# else +# if SOLARIS +# define SPLNET(x) ; +# else +# define SPLNET(x) x = splnet() +# endif +# endif +# ifdef SPLX +# undef SPLX +# endif +# if SOLARIS +# define SPLX(x) ; +# else +# define SPLX(x) (void) splx(x) +# endif +# else +# define SPLNET(x) ; +# define SPLX(x) ; +# endif /* KERNEL */ + +# ifdef sun +# if !defined(__sysv__) && !defined(__SVR4) +# define GETUNIT(n) ifunit((n), IFNAMSIZ) +# endif +# else +# define GETUNIT(n) ifunit((n)) +# endif /* sun */ +extern struct ifnet *ifunit(); +#endif /* linux */ + +#define IPMINLEN(i, h) ((i)->ip_len >= ((i)->ip_hl * 4 + sizeof(struct h))) + +/*#define IPOPT_RR 7 */ +#define IPOPT_ZSU 10 /* ZSU */ +#define IPOPT_MTUP 11 /* MTUP */ +#define IPOPT_MTUR 12 /* MTUR */ +#define IPOPT_ENCODE 15 /* ENCODE */ +/*#define IPOPT_TS 68 */ +#define IPOPT_TR 82 /* TR */ +/*#define IPOPT_SECURITY 130 */ +/*#define IPOPT_LSRR 131 */ +#define IPOPT_E_SEC 133 /* E-SEC */ +#define IPOPT_CIPSO 134 /* CIPSO */ +/*#define IPOPT_SATID 136 */ +#ifndef IPOPT_SID +# define IPOPT_SID IPOPT_SATID +#endif +/*#define IPOPT_SSRR 137 */ +#define IPOPT_ADDEXT 147 /* ADDEXT */ +#define IPOPT_VISA 142 /* VISA */ +#define IPOPT_IMITD 144 /* IMITD */ +#define IPOPT_EIP 145 /* EIP */ +#define IPOPT_FINN 205 /* FINN */ + +#ifndef ICMP_UNREACH_FILTER +#define ICMP_UNREACH_FILTER 13 +#endif +/* + * Security Options for Intenet Protocol (IPSO) as defined in RFC 1108. + * + * Basic Option + * + * 00000001 - (Reserved 4) + * 00111101 - Top Secret + * 01011010 - Secret + * 10010110 - Confidential + * 01100110 - (Reserved 3) + * 11001100 - (Reserved 2) + * 10101011 - Unclassified + * 11110001 - (Reserved 1) + */ +#define IPSO_CLASS_RES4 0x01 +#define IPSO_CLASS_TOPS 0x3d +#define IPSO_CLASS_SECR 0x5a +#define IPSO_CLASS_CONF 0x96 +#define IPSO_CLASS_RES3 0x66 +#define IPSO_CLASS_RES2 0xcc +#define IPSO_CLASS_UNCL 0xab +#define IPSO_CLASS_RES1 0xf1 + +#define IPSO_AUTH_GENSER 0x80 +#define IPSO_AUTH_ESI 0x40 +#define IPSO_AUTH_SCI 0x20 +#define IPSO_AUTH_NSA 0x10 +#define IPSO_AUTH_DOE 0x08 +#define IPSO_AUTH_UN 0x06 +#define IPSO_AUTH_FTE 0x01 + +#endif /* __IP_FIL_H__ */ diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index f7797d04a28..897583fcfb4 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -82,6 +82,10 @@ u_char ip_protox[IPPROTO_MAX]; int ipqmaxlen = IFQ_MAXLEN; struct in_ifaddrhead in_ifaddr; struct ifqueue ipintrq; +#if defined(IPFILTER) || defined(IPFILTER_LKM) +int fr_nullcheck(); +int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int)) = fr_nullcheck; +#endif char * inet_ntoa(ina) @@ -231,6 +235,14 @@ next: m_adj(m, ip->ip_len - m->m_pkthdr.len); } +#if defined(IPFILTER) || defined(IPFILTER_LKM) + /* + * Check if we want to allow this packet to be processed. + * Consider it to be bad if not. + */ + if ((*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0)) + goto bad; +#endif /* * Process options and, if not destined for us, * ship it on. ip_dooptions returns 1 when an @@ -1173,3 +1185,10 @@ ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen) } /* NOTREACHED */ } + +#if defined(IPFILTER) || defined(IPFILTER_LKM) +int fr_nullcheck() +{ + return 0; +} +#endif diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 2362ebe3f07..7e7d7097b37 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -60,6 +60,9 @@ static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); static void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *)); +#if defined(IPFILTER) || defined(IPFILTER_LKM) +extern int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int)); +#endif /* * IP output. The packet in mbuf chain m contains a skeletal IP @@ -276,6 +279,16 @@ ip_output(m0, opt, ro, flags, imo) } else m->m_flags &= ~M_BCAST; +#if defined(IPFILTER) || defined(IPFILTER_LKM) + /* + * looks like most checking has been done now...do a filter check + */ + if ((*fr_checkp)(ip, hlen, ifp, 1)) + { + error = EHOSTUNREACH; + goto bad; + } +#endif sendit: /* * If small enough for interface, can just send directly. diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 62e74f69f3b..27cce19e3d4 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,11 +1,12 @@ # from: @(#)Makefile 5.6.1.2 (Berkeley) 5/8/91 -# $Id: Makefile,v 1.6 1995/12/17 13:40:26 deraadt Exp $ +# $Id: Makefile,v 1.7 1996/01/07 02:34:20 dm Exp $ # not yet done: catman SUBDIR= ac accton arp bootpd bootpgw bootpef bootptest \ chown chroot config cron dev_mkdb \ - diskpart edquota gettable gspa htable inetd iostat kgmon \ + diskpart edquota gettable gspa htable inetd iostat \ + ipftest ipmon ipsend kgmon \ kvm_mkdb lpr map-mbone mrinfo mrouted mtrace mtree named \ netgroup_mkdb portmap pppd pstat pwd_mkdb quot quotaon \ rarpd rbootd rdconfig rdate repquota rmt \ diff --git a/usr.sbin/ipftest/Makefile b/usr.sbin/ipftest/Makefile new file mode 100644 index 00000000000..723b31469d7 --- /dev/null +++ b/usr.sbin/ipftest/Makefile @@ -0,0 +1,8 @@ +PROG= ipftest +MAN= ipftest.1 +SRCS= ipt.c fil.c ipft_sn.c ipft_ef.c ipft_td.c ipft_pc.c ipft_tx.c misc.c parse.c opt.c +.PATH: ${.CURDIR}/../../sbin/ipf ${.CURDIR}/../../sbin/ipfstat +CFLAGS+=-DIPL_NAME=\"/dev/ipl\" -I${.CURDIR}/../../sbin/ipf + + +.include <bsd.prog.mk> diff --git a/usr.sbin/ipftest/fil.c b/usr.sbin/ipftest/fil.c new file mode 100644 index 00000000000..b485678d5a7 --- /dev/null +++ b/usr.sbin/ipftest/fil.c @@ -0,0 +1,534 @@ +/* + * (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. + */ +#ifndef lint +static char sccsid[] = "@(#)fil.c 1.18 10/24/95 (C) 1993-1995 Darren Reed"; +#endif + +#ifndef linux +# include <sys/errno.h> +# include <sys/types.h> +# include <sys/param.h> +# include <sys/file.h> +# include <sys/ioctl.h> +# if defined(_KERNEL) || defined(KERNEL) +# include <sys/systm.h> +# endif +# include <sys/uio.h> +# if !defined(__SVR4) && !defined(__svr4__) +# include <sys/dir.h> +# include <sys/mbuf.h> +# else +# include <sys/byteorder.h> +# include <sys/dditypes.h> +# include <sys/stream.h> +# endif +# include <sys/protosw.h> +# include <sys/socket.h> +# include <net/if.h> +# ifdef sun +# include <net/af.h> +# endif +# include <net/route.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/udp.h> +# include <netinet/tcpip.h> +# include <netinet/ip_icmp.h> +#endif +#include <netinet/ip_fil.h> +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef _KERNEL +#include "ipf.h" +extern int opts; +extern void debug(), verbose(); + +#define FR_IFVERBOSE(ex,second,verb_pr) if (ex) { verbose verb_pr; second; } +#define FR_IFDEBUG(ex,second,verb_pr) if (ex) { debug verb_pr; second; } +#define FR_VERBOSE(verb_pr) verbose verb_pr +#define FR_DEBUG(verb_pr) debug verb_pr +#else +#define FR_IFVERBOSE(ex,second,verb_pr) ; +#define FR_IFDEBUG(ex,second,verb_pr) ; +#define FR_VERBOSE(verb_pr) +#define FR_DEBUG(verb_pr) + +extern int ipl_unreach, ipllog(); +#endif + +struct filterstats frstats[2] = {{0,0,0,0,0},{0,0,0,0,0}}; +struct frentry *filterin[2] = { NULL, NULL }, + *filterout[2] = { NULL, NULL }; +int fr_flags = 0, fr_active = 0; +int fr_check(); + + +/* + * bit values for identifying presence of individual IP options + */ +struct optlist ipopts[20] = { + { IPOPT_NOP, 0x000001 }, + { IPOPT_RR, 0x000002 }, + { IPOPT_ZSU, 0x000004 }, + { IPOPT_MTUP, 0x000008 }, + { IPOPT_MTUR, 0x000010 }, + { IPOPT_ENCODE, 0x000020 }, + { IPOPT_TS, 0x000040 }, + { IPOPT_TR, 0x000080 }, + { IPOPT_SECURITY, 0x000100 }, + { IPOPT_LSRR, 0x000200 }, + { IPOPT_E_SEC, 0x000400 }, + { IPOPT_CIPSO, 0x000800 }, + { IPOPT_SATID, 0x001000 }, + { IPOPT_SSRR, 0x002000 }, + { IPOPT_ADDEXT, 0x004000 }, + { IPOPT_VISA, 0x008000 }, + { IPOPT_IMITD, 0x010000 }, + { IPOPT_EIP, 0x020000 }, + { IPOPT_FINN, 0x040000 }, + { 0, 0x000000 } +}; + +/* + * bit values for identifying presence of individual IP security options + */ +struct optlist secopt[8] = { + { IPSO_CLASS_RES4, 0x01 }, + { IPSO_CLASS_TOPS, 0x02 }, + { IPSO_CLASS_SECR, 0x04 }, + { IPSO_CLASS_RES3, 0x08 }, + { IPSO_CLASS_CONF, 0x10 }, + { IPSO_CLASS_UNCL, 0x20 }, + { IPSO_CLASS_RES2, 0x40 }, + { IPSO_CLASS_RES1, 0x80 } +}; + + +/* + * compact the IP header into a structure which contains just the info. + * which is useful for comparing IP headers with. + */ +struct fr_ip *fr_makefrip(hlen, ip) +int hlen; +ip_t *ip; +{ + static struct fr_ip fi; + struct optlist *op; + u_short optmsk = 0, secmsk = 0, auth = 0; + int i, mv, ol, off; + u_char *s, opt; + + fi.fi_fl = 0; + fi.fi_v = ip->ip_v; + fi.fi_tos = ip->ip_tos; + (*(((u_short *)&fi) + 1)) = (*(((u_short *)ip) + 4)); + (*(((u_long *)&fi) + 1)) = (*(((u_long *)ip) + 3)); + (*(((u_long *)&fi) + 2)) = (*(((u_long *)ip) + 4)); + + if (hlen > sizeof(struct ip)) + fi.fi_fl |= FI_OPTIONS; + off = (ip->ip_off & 0x1fff) << 3; + if (ip->ip_off & 0x3fff) + fi.fi_fl |= FI_FRAG; + switch (ip->ip_p) + { + case IPPROTO_ICMP : + if ((!IPMINLEN(ip, icmp) && !off) || + (off && off < sizeof(struct icmp))) + fi.fi_fl |= FI_SHORT; + break; + case IPPROTO_TCP : + fi.fi_fl |= FI_TCPUDP; + if ((!IPMINLEN(ip, tcphdr) && !off) || + (off && off < sizeof(struct tcphdr))) + fi.fi_fl |= FI_SHORT; + break; + case IPPROTO_UDP : + fi.fi_fl |= FI_TCPUDP; + if ((!IPMINLEN(ip, udphdr) && !off) || + (off && off < sizeof(struct udphdr))) + fi.fi_fl |= FI_SHORT; + break; + default : + break; + } + + for (s = (u_char *)(ip + 1), hlen -= sizeof(*ip); hlen; ) { + if (!(opt = *s)) + break; + ol = (opt == IPOPT_NOP) ? 1 : (int)*(s+1); + if (opt > 1 && (ol < 0 || ol > hlen)) + break; + for (i = 9, mv = 4; mv >= 0; ) { + op = ipopts + i; + if (opt == (u_char)op->ol_val) { + optmsk |= op->ol_bit; + if (opt == IPOPT_SECURITY) { + struct optlist *sp; + u_char sec; + int j, m; + + sec = *(s + 3); /* classification */ + for (j = 3, m = 2; m >= 0; ) { + sp = secopt + j; + if (sec == sp->ol_val) { + secmsk |= sp->ol_bit; + auth = *(s + 3); + auth *= 256; + auth += *(s + 4); + break; + } + if (sec < sp->ol_val) + j -= m--; + else + j += m--; + } + } + break; + } + if (opt < op->ol_val) + i -= mv--; + else + i += mv--; + } + hlen -= ol; + s += ol; + } + if (auth && !(auth & 0x0100)) + auth &= 0xff00; + fi.fi_optmsk = optmsk; + fi.fi_secmsk = secmsk; + fi.fi_auth = auth; + return &fi; +} + + +/* + * check an IP packet for TCP/UDP characteristics such as ports and flags. + */ +int fr_tcpudpchk(ip, tcp, fr) +ip_t *ip; +tcphdr_t *tcp; +struct frentry *fr; +{ + register u_short po, tup; + register char i; + int err = 1; + + /* + * Both ports should *always* be in the first fragment. + * So far, I cannot find any cases where they can not be. + * + * compare destination ports + */ + if ((i = (int)fr->fr_dcmp)) { + po = ntohs(fr->fr_dport); + tup = ntohs(tcp->th_dport); + /* + * Do opposite test to that required and + * continue if that succeeds. + */ + if (!--i && tup != po) /* EQUAL */ + err = 0; + else if (!--i && tup == po) /* NOTEQUAL */ + err = 0; + else if (!--i && tup >= po) /* LESSTHAN */ + err = 0; + else if (!--i && tup <= po) /* GREATERTHAN */ + err = 0; + else if (!--i && tup > po) /* LT or EQ */ + err = 0; + else if (!--i && tup < po) /* GT or EQ */ + err = 0; + else if (!--i && /* Out of range */ + (tup >= po && tup <= ntohs(fr->fr_dtop))) + err = 0; + else if (!--i && /* In range */ + (tup <= po || tup >= ntohs(fr->fr_dtop))) + err = 0; + } + /* + * compare source ports + */ + if (err && (i = (int)fr->fr_scmp)) { + po = ntohs(fr->fr_sport); + tup = ntohs(tcp->th_sport); + if (!--i && tup != po) + err = 0; + else if (!--i && tup == po) + err = 0; + else if (!--i && tup >= po) + err = 0; + else if (!--i && tup <= po) + err = 0; + else if (!--i && tup > po) + err = 0; + else if (!--i && tup < po) + err = 0; + else if (!--i && /* Out of range */ + (tup >= po && tup <= ntohs(fr->fr_stop))) + err = 0; + else if (!--i && /* In range */ + (tup <= po || tup >= ntohs(fr->fr_stop))) + err = 0; + } + + /* + * If we don't have all the TCP/UDP header, then how can we + * expect to do any sort of match on it ? If we were looking for + * TCP flags, then NO match. If not, then match (which should + * satisfy the "short" class too). + */ + if (err) + if (ip->ip_p == IPPROTO_TCP) { + if (!IPMINLEN(ip, tcphdr)) + return !(fr->fr_tcpf); + /* + * Match the flags ? If not, abort this match. + */ + if (fr->fr_tcpf && + fr->fr_tcpf != (tcp->th_flags & fr->fr_tcpfm)) { + FR_DEBUG(("f. %#x & %#x != %#x\n", + tcp->th_flags, fr->fr_tcpfm, + fr->fr_tcpf)); + err = 0; + } + } + else if (!IPMINLEN(ip, udphdr)) /* must be UDP */ + return 1; + return err; +} + +/* + * Check the input/output list of rules for a match and result. + * Could be per interface, but this gets real nasty when you don't have + * kernel sauce. + */ +int fr_scanlist(pass, ip, hlen, ifp, out, rule) +int pass; +ip_t *ip; +int hlen, out; +struct ifnet *ifp; +u_short *rule; +{ + register struct frentry *fr; + register struct fr_ip *fi; + tcphdr_t *tcp; + int rulen; + + *rule = 1; + tcp = (tcphdr_t *)((char *)ip + hlen); + fr = (out) ? filterout[fr_active] : filterin[fr_active]; + fi = fr_makefrip(hlen, ip); + + for (rulen = 0; fr; fr = fr->fr_next, rulen++) { + /* + * In all checks below, a null (zero) value in the + * filter struture is taken to mean a wildcard. + * + * check that we are working for the right interface + */ +#ifdef _KERNEL + if (fr->fr_ifa && fr->fr_ifa != ifp) + continue; +#else + if (opts & (OPT_VERBOSE|OPT_DEBUG)) + printf("\n"); + FR_VERBOSE(("%c", (pass & FR_PASS) ? 'p' : 'b')); + if (ifp && *fr->fr_ifname && strcasecmp(ifp->if_name, + fr->fr_ifname)) + continue; + FR_VERBOSE((":i")); +#endif + { + register u_long *ld, *lm, *lip; + register int i; + + lip = (u_long *)fi; + lm = (u_long *)&fr->fr_mip; + ld = (u_long *)&fr->fr_ip; + i = ((lip[0] & lm[0]) != ld[0]); + FR_IFDEBUG(i,continue,("0. %#08x & %#08x != %#08x\n", + lip[0], lm[0], ld[0])); + i |= ((lip[1] & lm[1]) != ld[1]); + FR_IFDEBUG(i,continue,("1. %#08x & %#08x != %#08x\n", + lip[1], lm[1], ld[1])); + i |= ((lip[2] & lm[2]) != ld[2]); + FR_IFDEBUG(i,continue,("2. %#08x & %#08x != %#08x\n", + lip[2], lm[2], ld[2])); + i |= ((lip[3] & lm[3]) != ld[3]); + FR_IFDEBUG(i,continue,("3. %#08x & %#08x != %#08x\n", + lip[3], lm[3], ld[3])); + i |= ((lip[4] & lm[4]) != ld[4]); + FR_IFDEBUG(i,continue,("4. %#08x & %#08x != %#08x\n", + lip[4], lm[4], ld[4])); + if (i) + continue; + } + + /* + * If a fragment, then only the first has what we're looking + * for here... + */ + if (!(ip->ip_off & 0x1fff)) { + if ((fi->fi_fl & FI_TCPUDP) && + !fr_tcpudpchk(ip, tcp, fr)) + continue; + else if (ip->ip_p == IPPROTO_ICMP && + (*(u_short *)((char *)ip + hlen) & + fr->fr_icmpm) != fr->fr_icmp) { + FR_DEBUG(("i. %#x & %#x != %#x\n", + *(u_short *)((char *)ip + hlen), + fr->fr_icmpm, fr->fr_icmp)); + continue; + } + } else if (fr->fr_dcmp || fr->fr_scmp || fr->fr_icmpm || + fr->fr_tcpfm) + continue; + FR_VERBOSE(("*")); + /* + * Just log this packet... + */ + if (fr->fr_flags & FR_LOG) { +#ifdef IPFILTER_LOG + if (!ipllog(hlen, fr->fr_flags, ip, ifp, *rule)) + frstats[out].fr_skip++; + frstats[out].fr_pkl++; +#endif /* IPFILTER_LOG */ + } else + pass = fr->fr_flags; + FR_DEBUG(("pass %#x\n", pass)); + fr->fr_hits++; + *rule = rulen; + if (pass & FR_QUICK) + break; + } + return pass; +} + + +/* + * frcheck - filter check + * check using source and destination addresses/pors in a packet whether + * or not to pass it on or not. + */ +int fr_check(ip, hlen, ifp, out +#if SOLARIS && defined(_KERNEL) +, qif, q) +qif_t *qif; +queue_t *q; +#else +) +#endif +ip_t *ip; +int hlen; +struct ifnet *ifp; +int out; +{ + int pass = FR_NOMATCH; + int sl; + u_short rule; + + SPLNET(sl); + + pass = fr_scanlist(pass, ip, hlen, ifp, out, &rule); + if (pass == FR_NOMATCH) { + frstats[out].fr_nom++; +#ifdef NOMATCH + pass |= NOMATCH; +#endif + } + +#ifdef IPFILTER_LOG + if ((pass & FR_LOGP) || + ((pass & FR_PASS) && (fr_flags & FF_LOGPASS))) { + if (!(pass & FR_LOGP)) + pass |= FF_LOGPASS << 8; + if (!ipllog(hlen, pass, ip, ifp, rule)) + frstats[out].fr_skip++; + frstats[out].fr_ppkl++; + } else if ((pass & FR_LOGB) || + ((pass & FR_BLOCK) && (fr_flags & FF_LOGBLOCK))) { + if (!(pass & FR_LOGB)) + pass |= FF_LOGBLOCK << 8; + if (!ipllog(hlen, pass, ip, ifp, rule)) + frstats[out].fr_skip++; + frstats[out].fr_bpkl++; + } +#endif /* IPFILTER_LOG */ + SPLX(sl); + if (pass & FR_PASS) + frstats[out].fr_pass++; + else if (pass & FR_BLOCK) { + frstats[out].fr_block++; + /* + * Should we return an ICMP packet to indicate error + * status passing through the packet filter ? + * XXX - copy mbuf as icmp_error() calls mfree() - fix this + * later, but preserve backward compatibility for now. + */ +#ifdef _KERNEL + if (pass & FR_RETICMP) { +# if SOLARIS + icmp_error(q, ip, ICMP_UNREACH, ipl_unreach, qif, + ip->ip_src); +# else + struct mbuf *copy; + + copy = m_copy(dtom(ip), 0, imin((int)ip->ip_len, 64)); +# if BSD < 199103 + icmp_error(mtod(copy, struct ip *), + ICMP_UNREACH, ipl_unreach, ifp, ip->ip_src); +# else + icmp_error(copy, ICMP_UNREACH, ipl_unreach, + ip->ip_src.s_addr, ifp); +# endif +# endif + frstats[0].fr_ret++; + } else if (pass & FR_RETRST && IPMINLEN(ip, tcphdr)) { +# if SOLARIS + if (send_reset(ip, qif, q) == 0) +# else + if (send_reset(ip) == 0) +# endif + frstats[1].fr_ret++; + } +#else + if (pass & FR_RETICMP) { + verbose("- ICMP unreachable sent\n"); + frstats[0].fr_ret++; + } else if (pass & FR_RETRST && IPMINLEN(ip, tcphdr)) { + verbose("- TCP RST sent\n"); + frstats[1].fr_ret++; + } +#endif + } +#ifdef _KERNEL + return (pass & FR_PASS) ? 0 : -1; +#else + if (pass & FR_NOMATCH) + return 1; + if (pass & FR_PASS) + return 0; + return -1; +#endif +} + + +#ifndef _KERNEL +int ipllog() +{ + verbose("l"); + return 1; +} +#endif diff --git a/usr.sbin/ipftest/ipft_ef.c b/usr.sbin/ipftest/ipft_ef.c new file mode 100644 index 00000000000..fb8f67215b8 --- /dev/null +++ b/usr.sbin/ipftest/ipft_ef.c @@ -0,0 +1,157 @@ +/* + * (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. + */ + +/* + icmp type + lnth proto source destination src port dst port + +etherfind -n + + 60 tcp 128.250.20.20 128.250.133.13 2419 telnet + +etherfind -n -t + + 0.32 91 04 131.170.1.10 128.250.133.13 + 0.33 566 udp 128.250.37.155 128.250.133.3 901 901 +*/ +#include <stdio.h> +#include <string.h> +#if !defined(__SVR4) && !defined(__GNUC__) +#include <strings.h> +#endif +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcpip.h> +#include <net/if.h> +#include <netdb.h> +#include <netinet/ip_fil.h> +#include "ipf.h" +#include "ipt.h" + +#ifndef lint +static char sccsid[] = "@(#)ipft_ef.c 1.5 10/15/95 (C)1995 Darren Reed"; +#endif + +static int etherf_open(), etherf_close(), etherf_readip(); + +struct ipread etherf = { etherf_open, etherf_close, etherf_readip }; + +static FILE *efp = NULL; +static int efd = -1; + +#ifdef NEED_INET_ATON +extern u_long inet_aton(); +#else +#include <arpa/inet.h> +#endif + +static int etherf_open(fname) +char *fname; +{ + if (efd != -1) + return efd; + + if (!strcmp(fname, "-")) { + efd = 0; + efp = stdin; + } else { + efd = open(fname, O_RDONLY); + efp = fdopen(efd, "r"); + } + return efd; +} + + +static int etherf_close() +{ + return close(efd); +} + + +static int etherf_readip(buf, cnt, ifn, dir) +char *buf, **ifn; +int cnt, *dir; +{ + struct tcpiphdr pkt; + struct ip *ip = (struct ip *)&pkt; + struct protoent *p = NULL; + char src[16], dst[16], sprt[16], dprt[16]; + char lbuf[128], len[8], prot[8], time[8], *s; + int slen, extra = 0, i, n; + + if (!fgets(lbuf, sizeof(lbuf) - 1, efp)) + return 0; + + if ((s = strchr(lbuf, '\n'))) + *s = '\0'; + lbuf[sizeof(lbuf)-1] = '\0'; + + bzero(&pkt, sizeof(pkt)); + + if ((n = sscanf(lbuf, "%s %s %s %s %s %s", len, prot, src, dst, + sprt, dprt)) != 6) + if ((n = sscanf(lbuf, "%s %s %s %s %s %s %s", time, + len, prot, src, dst, sprt, dprt)) != 7) + return -1; + + ip->ip_p = atoi(prot); + if (ip->ip_p == 0) { + if (!(p = getprotobyname(prot))) + return -1; + ip->ip_p = p->p_proto; + } + + switch (ip->ip_p) { + case IPPROTO_TCP : + case IPPROTO_UDP : + s = strtok(NULL, " :"); + ip->ip_len += atoi(s); + if (p->p_proto == IPPROTO_TCP) + extra = sizeof(struct tcphdr); + else if (p->p_proto == IPPROTO_UDP) + extra = sizeof(struct udphdr); + break; +#ifdef IGMP + case IPPROTO_IGMP : + extra = sizeof(struct igmp); + break; +#endif + case IPPROTO_ICMP : + extra = sizeof(struct icmp); + break; + default : + break; + } + +#ifdef NEED_INET_ATON + ip->ip_src.s_addr = inet_aton(src); + ip->ip_dst.s_addr = inet_aton(dst); +#else + (void) inet_aton(src, &ip->ip_src); + (void) inet_aton(dst, &ip->ip_dst); +#endif + ip->ip_len = atoi(len); + ip->ip_hl = sizeof(struct ip); + + slen = ip->ip_hl + extra; + i = MIN(cnt, slen); + bcopy((char *)&pkt, buf, i); + return i; +} diff --git a/usr.sbin/ipftest/ipft_pc.c b/usr.sbin/ipftest/ipft_pc.c new file mode 100644 index 00000000000..06af91f4897 --- /dev/null +++ b/usr.sbin/ipftest/ipft_pc.c @@ -0,0 +1,189 @@ +/* + * (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(__GNUC__) +#include <strings.h> +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> +#include <net/if.h> +#include <netinet/ip_fil.h> +#include "ipf.h" +#include "ipt.h" +#include "pcap.h" + +struct llc { + int lc_sz; /* LLC header length */ + int lc_to; /* LLC Type offset */ + int lc_tl; /* LLC Type length */ +}; + +/* + * While many of these maybe the same, some do have different header formats + * which make this useful. + */ +#define DLT_MAX 10 + +static struct llc llcs[DLT_MAX+1] = { + { 0, 0, 0 }, /* DLT_NULL */ + { 14, 12, 2 }, /* DLT_E10MB */ + { 0, 0, 0 }, /* DLT_EN3MB */ + { 0, 0, 0 }, /* DLT_AX25 */ + { 0, 0, 0 }, /* DLT_PRONET */ + { 0, 0, 0 }, /* DLT_CHAOS */ + { 0, 0, 0 }, /* DLT_IEEE802 */ + { 0, 0, 0 }, /* DLT_ARCNET */ + { 0, 0, 0 }, /* DLT_SLIP */ + { 0, 0, 0 }, /* DLT_PPP */ + { 0, 0, 0 } /* DLT_FDDI */ +}; + +static int pcap_open(), pcap_close(), pcap_readip(); + +static int pfd = -1, s_type = -1; + +struct ipread pcap = { pcap_open, pcap_close, pcap_readip }; + + +static int pcap_open(fname) +char *fname; +{ + pcaphdr_t ph; + int fd; + + if (pfd != -1) + return pfd; + + if (!strcmp(fname, "-")) + fd = 0; + else if ((fd = open(fname, O_RDONLY)) == -1) + return -1; + + if (read(fd, (char *)&ph, sizeof(ph)) != sizeof(ph)) + return -2; + + if (ph.pc_v_maj != PCAP_VERSION_MAJ || ph.pc_type > DLT_MAX) { + (void) close(fd); + return -2; + } + + pfd = fd; + s_type = ph.pc_type; + printf("opened pcap file %s:\n", fname); + printf("\tid: %08x version: %d.%d type: %d snap %d\n", + ph.pc_id, ph.pc_v_maj, ph.pc_v_min, ph.pc_type, ph.pc_slen); + + return fd; +} + + +static int pcap_close() +{ + return close(pfd); +} + + +/* + * read in the header (and validate) which should be the first record + * in a pcap file. + */ +static int pcap_read_rec(rec) +struct pcap_pkthdr *rec; +{ + int n, p; + + if (read(pfd, (char *)rec, sizeof(*rec)) != sizeof(*rec)) + return -2; + + p = rec->ph_clen; + n = MIN(p, rec->ph_len); + if (!n || n < 0) + return -3; + + return p; +} + + +/* + * read an entire pcap packet record. only the data part is copied into + * the available buffer, with the number of bytes copied returned. + */ +static int pcap_read(buf, cnt) +char *buf; +int cnt; +{ + struct pcap_pkthdr rec; + static char *bufp = NULL; + int i, n; + + if ((i = pcap_read_rec(&rec)) <= 0) + return i; + + if (!bufp) + bufp = malloc(i); + else + bufp = realloc(bufp, i); + + if (read(pfd, bufp, i) != i) + return -2; + + n = MIN(i, cnt); + bcopy(bufp, buf, n); + return n; +} + + +/* + * return only an IP packet read into buf + */ +static int pcap_readip(buf, cnt, ifn, dir) +char *buf, **ifn; +int cnt, *dir; +{ + static char *bufp = NULL; + struct pcap_pkthdr rec; + struct llc *l; + char *s, ty[4]; + int i, n; + + do { + if ((i = pcap_read_rec(&rec)) <= 0) + return i; + + if (!bufp) + bufp = malloc(i); + else + bufp = realloc(bufp, i); + s = bufp; + + if (read(pfd, s, i) != i) + return -2; + + l = &llcs[s_type]; + i -= l->lc_sz; + s += l->lc_to; + bcopy(s, ty, l->lc_tl); + s += l->lc_tl; + } while (ty[0] != 0x8 && ty[1] != 0); + n = MIN(i, cnt); + bcopy(s, buf, n); + return n; +} diff --git a/usr.sbin/ipftest/ipft_sn.c b/usr.sbin/ipftest/ipft_sn.c new file mode 100644 index 00000000000..b94a477e61b --- /dev/null +++ b/usr.sbin/ipftest/ipft_sn.c @@ -0,0 +1,201 @@ +/* + * (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. + */ + +/* + * Written to comply with the recent RFC 1761 from Sun. + */ +#include <stdio.h> +#include <string.h> +#if !defined(__SVR4) && !defined(__GNUC__) +#include <strings.h> +#endif +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> +#include <net/if.h> +#include <netinet/ip_fil.h> +#include "ipf.h" +#include "ipt.h" +#include "snoop.h" + +struct llc { + int lc_sz; /* LLC header length */ + int lc_to; /* LLC Type offset */ + int lc_tl; /* LLC Type length */ +}; + +/* + * While many of these maybe the same, some do have different header formats + * which make this useful. + */ +static struct llc llcs[SDL_MAX+1] = { + { 0, 0, 0 }, /* SDL_8023 */ + { 0, 0, 0 }, /* SDL_8024 */ + { 0, 0, 0 }, /* SDL_8025 */ + { 0, 0, 0 }, /* SDL_8026 */ + { 14, 12, 2 }, /* SDL_ETHER */ + { 0, 0, 0 }, /* SDL_HDLC */ + { 0, 0, 0 }, /* SDL_CHSYNC */ + { 0, 0, 0 }, /* SDL_IBMCC */ + { 0, 0, 0 }, /* SDL_FDDI */ + { 0, 0, 0 }, /* SDL_OTHER */ +}; + +static int snoop_open(), snoop_close(), snoop_readip(); + +static int sfd = -1, s_type = -1; + +struct ipread snoop = { snoop_open, snoop_close, snoop_readip }; + + +static int snoop_open(fname) +char *fname; +{ + struct snoophdr sh; + int fd; + + if (sfd != -1) + return sfd; + + if (!strcmp(fname, "-")) + fd = 0; + else if ((fd = open(fname, O_RDONLY)) == -1) + return -1; + + if (read(fd, (char *)&sh, sizeof(sh)) != sizeof(sh)) + return -2; + + if (sh.s_v != SNOOP_VERSION || + sh.s_type < 0 || sh.s_type > SDL_MAX) { + (void) close(fd); + return -2; + } + + sfd = fd; + s_type = sh.s_type; + printf("opened snoop file %s:\n", fname); + printf("\tid: %8.8s version: %d type: %d\n", sh.s_id, sh.s_v, s_type); + + return fd; +} + + +static int snoop_close() +{ + return close(sfd); +} + + +/* + * read in the header (and validate) which should be the first record + * in a snoop file. + */ +static int snoop_read_rec(rec) +struct snooppkt *rec; +{ + int n, p; + + if (read(sfd, (char *)rec, sizeof(*rec)) != sizeof(*rec)) + return -2; + + if (rec->sp_ilen > rec->sp_plen || rec->sp_plen < sizeof(*rec)) + return -2; + + p = rec->sp_plen - sizeof(*rec); + n = MIN(p, rec->sp_ilen); + if (!n || n < 0) + return -3; + + return p; +} + + +/* + * read an entire snoop packet record. only the data part is copied into + * the available buffer, with the number of bytes copied returned. + */ +static int snoop_read(buf, cnt) +char *buf; +int cnt; +{ + struct snooppkt rec; + static char *bufp = NULL; + int i, n; + + if ((i = snoop_read_rec(&rec)) <= 0) + return i; + + if (!bufp) + bufp = malloc(i); + else + bufp = realloc(bufp, i); + + if (read(sfd, bufp, i) != i) + return -2; + + n = MIN(i, cnt); + bcopy(bufp, buf, n); + return n; +} + + +/* + * return only an IP packet read into buf + */ +static int snoop_readip(buf, cnt, ifn, dir) +char *buf, **ifn; +int cnt, *dir; +{ + static char *bufp = NULL; + struct snooppkt rec; + struct llc *l; + char ty[4], *s; + int i, n; + + do { + if ((i = snoop_read_rec(&rec)) <= 0) + return i; + + if (!bufp) + bufp = malloc(i); + else + bufp = realloc(bufp, i); + s = bufp; + + if (read(sfd, s, i) != i) + return -2; + + l = &llcs[s_type]; + i -= l->lc_to; + s += l->lc_to; + /* + * XXX - bogus assumption here on the part of the time field + * that it won't be greater than 4 bytes and the 1st two will + * have the values 8 and 0 for IP. Should be a table of + * these too somewhere. Really only works for SDL_ETHER. + */ + bcopy(s, ty, l->lc_tl); + } while (ty[0] != 0x8 && ty[1] != 0); + + i -= l->lc_tl; + s += l->lc_tl; + n = MIN(i, cnt); + bcopy(s, buf, n); + + return n; +} diff --git a/usr.sbin/ipftest/ipft_td.c b/usr.sbin/ipftest/ipft_td.c new file mode 100644 index 00000000000..3ddee115192 --- /dev/null +++ b/usr.sbin/ipftest/ipft_td.c @@ -0,0 +1,201 @@ +/* + * (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. + */ + +/* +tcpdump -n + +00:05:47.816843 128.231.76.76.3291 > 224.2.252.231.36573: udp 36 (encap) + +tcpdump -nq + +00:33:48.410771 192.73.213.11.1463 > 224.2.248.153.59360: udp 31 (encap) + +tcpdump -nqt + +128.250.133.13.23 > 128.250.20.20.2419: tcp 27 + +tcpdump -nqtt + +123456789.1234567 128.250.133.13.23 > 128.250.20.20.2419: tcp 27 + +tcpdump -nqte + +8:0:20:f:65:f7 0:0:c:1:8a:c5 81: 128.250.133.13.23 > 128.250.20.20.2419: tcp 27 + +*/ +#include <stdio.h> +#include <string.h> +#if !defined(__SVR4) && !defined(__GNUC__) +#include <strings.h> +#endif +#include <sys/types.h> +#include <sys/param.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 <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcpip.h> +#include <net/if.h> +#include <netdb.h> +#include <netinet/ip_fil.h> +#include "ipf.h" +#include "ipt.h" + +#ifndef lint +static char sccsid[] = "@(#)ipft_td.c 1.6 10/15/95 (C)1995 Darren Reed"; +#endif + +static int tcpd_open(), tcpd_close(), tcpd_readip(); +#ifdef NEED_INET_ATON +static u_long inet_aton(); +#else +#include <arpa/inet.h> +#endif + +struct ipread tcpd = { tcpd_open, tcpd_close, tcpd_readip }; + +static FILE *tfp = NULL; +static int tfd = -1; + + +static int tcpd_open(fname) +char *fname; +{ + if (tfd != -1) + return tfd; + + if (!strcmp(fname, "-")) { + tfd = 0; + tfp = stdin; + } else { + tfd = open(fname, O_RDONLY); + tfp = fdopen(tfd, "r"); + } + return tfd; +} + + +static int tcpd_close() +{ + (void) fclose(tfp); + return close(tfd); +} + + +static int count_dots(str) +char *str; +{ + int i = 0; + + while (*str) + if (*str++ == '.') + i++; + return i; +} + + +static int tcpd_readip(buf, cnt, ifn, dir) +char *buf, **ifn; +int cnt, *dir; +{ + struct tcpiphdr pkt; + struct ip *ip = (struct ip *)&pkt; + struct protoent *p; + char src[32], dst[32], misc[256], time[32], link1[32], link2[32]; + char lbuf[160], *s; + int n, dots, slen, extra = 0; + + if (!fgets(lbuf, sizeof(lbuf) - 1, tfp)) + return 0; + + if ((s = strchr(lbuf, '\n'))) + *s = '\0'; + lbuf[sizeof(lbuf)-1] = '\0'; + + bzero(&pkt, sizeof(pkt)); + + if ((n = sscanf(lbuf, "%s > %s: %s", src, dst, misc)) != 3) + if ((n = sscanf(lbuf, "%s %s > %s: %s", + time, src, dst, misc)) != 4) + if ((n = sscanf(lbuf, "%s %s: %s > %s: %s", + link1, link2, src, dst, misc)) != 5) { + n = sscanf(lbuf, "%s %s %s: %s > %s: %s", + time, link1, link2, src, dst, misc); + if (n != 6) + return -1; + } + + if ((dots = count_dots(dst)) == 4) { + s = strrchr(src, '.'); + *s++ = '\0'; +#ifdef NEED_INET_ATON + ip->ip_src.s_addr = inet_aton(src); +#else + (void) inet_aton(src, &ip->ip_src); +#endif + pkt.ti_sport = htons(atoi(s)); + *--s = '.'; + s = strrchr(dst, '.'); + + *s++ = '\0'; +#ifdef NEED_INET_ATON + ip->ip_dst.s_addr = inet_aton(dst); +#else + (void) inet_aton(src, &ip->ip_dst); +#endif + pkt.ti_dport = htons(atoi(s)); + *--s = '.'; + + } else { +#ifdef NEED_INET_ATON + ip->ip_src.s_addr = inet_aton(src); + ip->ip_dst.s_addr = inet_aton(dst); +#else + (void) inet_aton(src, &ip->ip_src); + (void) inet_aton(src, &ip->ip_dst); +#endif + } + ip->ip_len = ip->ip_hl = sizeof(struct ip); + + s = strtok(misc, " :"); + if ((p = getprotobyname(s))) { + ip->ip_p = p->p_proto; + + switch (p->p_proto) { + case IPPROTO_TCP : + case IPPROTO_UDP : + s = strtok(NULL, " :"); + ip->ip_len += atoi(s); + if (p->p_proto == IPPROTO_TCP) + extra = sizeof(struct tcphdr); + else if (p->p_proto == IPPROTO_UDP) + extra = sizeof(struct udphdr); + break; +#ifdef IGMP + case IPPROTO_IGMP : + extra = sizeof(struct igmp); + break; +#endif + case IPPROTO_ICMP : + extra = sizeof(struct icmp); + break; + default : + break; + } + } + slen = ip->ip_hl + extra + ip->ip_len; + return slen; +} diff --git a/usr.sbin/ipftest/ipft_tx.c b/usr.sbin/ipftest/ipft_tx.c new file mode 100644 index 00000000000..4ca44a8380e --- /dev/null +++ b/usr.sbin/ipftest/ipft_tx.c @@ -0,0 +1,248 @@ +/* + * (C)opyright 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 <assert.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 <sys/ioctl.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcpip.h> +#include <net/if.h> +#include <netinet/ip_fil.h> +#include <netdb.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include "ipf.h" +#include "ipt.h" + +#ifndef lint +static char sccsid[] = "@(#)ipft_tx.c 1.2 10/17/95 (C) 1993 Darren Reed"; +#endif + +extern int opts; + +static int text_open(), text_close(), text_readip(), parseline(); + +struct ipread iptext = { text_open, text_close, text_readip }; +static FILE *tfp = NULL; +static int tfd = -1; + +static int text_open(fname) +char *fname; +{ + if (tfp && tfd != -1) { + rewind(tfp); + return tfd; + } + + if (!strcmp(fname, "-")) { + tfd = 0; + tfp = stdin; + } else { + tfd = open(fname, O_RDONLY); + if (tfd != -1) + tfp = fdopen(tfd, "r"); + } + return tfd; +} + + +static int text_close() +{ + int cfd = tfd; + + tfd = -1; + return close(cfd); +} + + +static int text_readip(buf, cnt, ifn, dir) +char *buf, **ifn; +int cnt, *dir; +{ + register char *s; + struct ip *ip; + char line[513]; + + ip = (struct ip *)buf; + *ifn = NULL; + while (fgets(line, sizeof(line)-1, tfp)) { + if ((s = index(line, '\n'))) + *s = '\0'; + if ((s = index(line, '\r'))) + *s = '\0'; + if ((s = index(line, '#'))) + *s = '\0'; + if (!*line) + continue; + if (!(opts & OPT_BRIEF)) + printf("input: %s\n", line); + *ifn = NULL; + *dir = 0; + if (!parseline(line, buf, ifn, dir)) +#if 0 + return sizeof(struct tcpiphdr); +#else + return sizeof(struct ip); +#endif + } + return -1; +} + +static int parseline(line, ip, ifn, out) +char *line; +struct ip *ip; +char **ifn; +int *out; +{ + extern char *proto; + tcphdr_t th, *tcp = &th; + struct icmp icmp, *ic = &icmp; + char *cps[20], **cpp, c, opts[68]; + int i; + + bzero((char *)ip, MAX(sizeof(*tcp), sizeof(*ic)) + sizeof(*ip)); + bzero((char *)tcp, sizeof(*tcp)); + bzero((char *)ic, sizeof(*ic)); + bzero(opts, sizeof(opts)); + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_v = IPVERSION; + for (i = 0, cps[0] = strtok(line, " \b\t\r\n"); cps[i] && i < 19; ) + cps[++i] = strtok(NULL, " \b\t\r\n"); + if (i < 2) + return 1; + + cpp = cps; + + c = **cpp; + if (!isalpha(c) || (tolower(c) != 'o' && tolower(c) != 'i')) { + fprintf(stderr, "bad direction \"%s\"\n", *cpp); + return 1; + } + *out = (tolower(c) == 'o') ? 1 : 0; + cpp++; + + if (!strcasecmp(*cpp, "on")) { + cpp++; + if (!*cpp) + return 1; + *ifn = *cpp++; + } + + c = **cpp; + ip->ip_len = sizeof(struct ip); + if (!strcasecmp(*cpp, "tcp") || !strcasecmp(*cpp, "udp") || + !strcasecmp(*cpp, "icmp")) { + if (c == 't') { + ip->ip_p = IPPROTO_TCP; + ip->ip_len += sizeof(struct tcphdr); + proto = "tcp"; + } else if (c == 'u') { + ip->ip_p = IPPROTO_UDP; + ip->ip_len += sizeof(struct udphdr); + proto = "udp"; + } else { + ip->ip_p = IPPROTO_ICMP; + ip->ip_len += sizeof(struct icmp); + proto = "icmp"; + } + cpp++; + } else + ip->ip_p = IPPROTO_IP; + + if (!*cpp) + return 1; + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) { + char *last; + + last = index(*cpp, ','); + if (!last) { + fprintf(stderr, "tcp/udp with no source port\n"); + return 1; + } + *last++ = '\0'; + tcp->th_sport = portnum(last); + } + ip->ip_src.s_addr = hostnum(*cpp); + cpp++; + if (!*cpp) + return 1; + + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) { + char *last; + + last = index(*cpp, ','); + if (!last) { + fprintf(stderr, "tcp/udp with no destination port\n"); + return 1; + } + *last++ = '\0'; + tcp->th_dport = portnum(last); + } + ip->ip_dst.s_addr = hostnum(*cpp); + cpp++; + if (*cpp && ip->ip_p == IPPROTO_TCP) { + extern char flagset[]; + extern u_char flags[]; + char *s, *t; + + for (s = *cpp; *s; s++) + if ((t = index(flagset, *s))) + tcp->th_flags |= flags[t - flagset]; + if (tcp->th_flags) + cpp++; + assert(tcp->th_flags != 0); + } else if (*cpp && ip->ip_p == IPPROTO_ICMP) { + extern char *icmptypes[]; + char **s, *t; + int i; + + for (s = icmptypes, i = 0; !*s || strcmp(*s, "END"); s++, i++) + if (*s && !strncasecmp(*cpp, *s, strlen(*s))) { + ic->icmp_type = i; + if ((t = index(*cpp, ','))) + ic->icmp_code = atoi(t+1); + cpp++; + break; + } + } + + if (*cpp && !strcasecmp(*cpp, "opt")) { + u_long olen; + + cpp++; + olen = buildopts(*cpp, opts); + if (olen) { + bcopy(opts, (char *)(ip + 1), olen); + ip->ip_hl += olen >> 2; + } + } + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + bcopy((char *)tcp, ((char *)ip) + (ip->ip_hl << 2), + sizeof(*tcp)); + else if (ip->ip_p == IPPROTO_ICMP) + bcopy((char *)ic, ((char *)ip) + (ip->ip_hl << 2), + sizeof(*ic)); + return 0; +} diff --git a/usr.sbin/ipftest/ipftest.1 b/usr.sbin/ipftest/ipftest.1 new file mode 100644 index 00000000000..2e6991a0d09 --- /dev/null +++ b/usr.sbin/ipftest/ipftest.1 @@ -0,0 +1,99 @@ +.LP +.TH ipftest 8 +.SH NAME +ipftest - test packet filter rules with arbitary input. +.SH SYNOPSIS +ipftest [-vbdPSTE] [-I interface] -r <filename> [-i <filename>] +.SH DESCRIPTION +.LP +.PP +\fBipftest\fP is provided for the purpose of being able to test a set of +filter rules without having to put them in place, in operation and procede +to test their effectiveness. The hope is that this minimises disruptions +in providing a secure IP environment. +.PP +\fBipftest\fP will parse any standard ruleset for use with \fBipf\fP +and apply input, returning output as to the result. However, \fBipftest\fP +will return one of three values for packets passed through the filter: +pass, block or nomatch. This is intended to give the operator a better +idea of what is happening with packets passing through their filter +ruleset. +.PP +When used without eiether of \fB-S\fP, \fB-T\fP or \fB-E\fP, +\fBipftest\fP uses its own text input format to generate "fake" IP packets. +The format used is as follows: +.nf + "in"|"out" "on" if ["tcp"|"udp"|"icmp"] + srchost[,srcport] dsthost[,destport] [FSRPAU] +.fi +.PP +This allows for a packet going "in" or "out" of an interface (if) to be +generated, being one of the three main protocols (optionally), and if +either TCP or UDP, a port parameter is also expected. If TCP is selected, +it is possible to (optionally) supply TCP flags at the end. Some examples +are: +.nf + # a UDP packet coming in on le0 + in on le0 udp 10.1.1.1,2210 10.2.1.5,23 + # an IP packet coming in on le0 from localhost - hmm :) + in on le0 localhost 10.4.12.1 + # a TCP packet going out of le0 with the SYN flag set. + out on le0 tcp 10.4.12.1,2245 10.1.1.1,23 S +.fi +.SH OPTIONS +.IP -v +Verbose mode. This provides more information about which parts of rule +matching the input packet passes and fails. +.IP -d +Turn on filter rule debugging. Currently, this only shows you what caused +the rule to not match in the IP header checking (addresses/netmasks, etc). +.IP -b +Cause the output to be a brief summary (one-word) of the result of passing +the packet through the filter; either "pass", "block" or "nomatch". +This is used in the regression testing. +.IP -I <interface> +Set the interface name (used in rule matching) to be the name supplied. +This is useful with the \fB-P, -S, -T\fP and \fB-E\fP options, where it is +not otherwise possible to associate a packet with an interface. Normal +"text packets" can override this setting. +.IP -P +The input file specified by \fB-i\fP is a binary file produced using libpcap +(ie tcpdump version 3). Packets are read from this file as being input +(for rule purposes). An interface maybe specified using \fB-I\fP. +.IP -S +The input file is to be in "snoop" format (see RFC 1761). Packets are read +from this file and used as input from any interface. This is perhaps the +most useful input type, currently. +.IP -T +The input file is to be text output from tcpdump. The text formats which +are currently supported are those which result from the following tcpdump +option combinations: +.PP +.nf + tcpdump -n + tcpdump -nq + tcpdump -nqt + tcpdump -nqtt + tcpdump -nqte +.fi +.LP +.IP -E +The input file is to be text output from etherfind. The text formats which +are currently supported are those which result from the following etherfind +option combinations: +.PP +.nf + etherfind -n + etherfind -n -t +.fi +.LP +.IP -i <filename> +Specify the filename to take input from. Default is stdin. +.IP -r <filename> +Specify the filename from which to read filter rules. +.SH FILES +.SH SEE ALSO +ipf(1), ipf(5), snoop(1m), tcpdump(8), etherfind(8c) +.SH BUGS +Not all of the input formats are sufficiently capable of introducing a +wide enough variety of packets for them to be all useful in testing. diff --git a/usr.sbin/ipftest/ipt.c b/usr.sbin/ipftest/ipt.c new file mode 100644 index 00000000000..bbb5d2a3e5f --- /dev/null +++ b/usr.sbin/ipftest/ipt.c @@ -0,0 +1,197 @@ +/* + * (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 <assert.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 <sys/ioctl.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcpip.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 "ipt.h" +#include <ctype.h> + +#ifndef lint +static char sccsid[] = "@(#)ipt.c 1.13 11/11/95 (C) 1993 Darren Reed"; +#endif + +extern int fr_check(); +extern char *optarg; +extern struct frentry *filterin[], *filterout[]; +extern struct ipread snoop, etherf, tcpd, pcap, iptext; +extern void debug(), verbose(); + +struct frentry *ft_in = NULL, *ft_out = NULL; +struct ipread *readers[] = { &iptext, ðerf, &tcpd, &snoop, &pcap, NULL }; + +int opts = 0; + +int main(argc,argv) +int argc; +char *argv[]; +{ + struct ipread **r = readers; + struct frentry *f; + struct ip *ip; + u_long buf[64]; + char c; + char *rules = NULL, *datain = NULL, *iface = NULL; + int fd, i, dir = 0; + + while ((c = getopt(argc, argv, "I:PSTEbdi:r:v")) != -1) + switch (c) + { + case 'b' : + opts |= OPT_BRIEF; + break; + case 'd' : + opts |= OPT_DEBUG; + break; + case 'i' : + datain = optarg; + break; + case 'I' : + iface = optarg; + break; + case 'r' : + rules = optarg; + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + case 'E' : + for (i = 0, r = readers; *r; i++, r++) + if (*r == ðerf) + break; + break; + case 'P' : + for (i = 0, r = readers; *r; i++, r++) + if (*r == &pcap) + break; + break; + case 'S' : + for (i = 0, r = readers; *r; i++, r++) + if (*r == &snoop) + break; + break; + case 'T' : + for (i = 0, r = readers; *r; i++, r++) + if (*r == &tcpd) + break; + break; + } + + if (!rules) { + (void)fprintf(stderr,"no rule file present\n"); + exit(-1); + } + + if (rules) { + struct frentry *fr; + char line[513], *s; + FILE *fp; + + if (!strcmp(rules, "-")) + fp = stdin; + else if (!(fp = fopen(rules, "r"))) { + (void)fprintf(stderr, "couldn't open %s\n", rules); + exit(-1); + } + if (!(opts & OPT_BRIEF)) + (void)printf("opening rule file \"%s\"\n", rules); + 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 (!(fr = parse(line))) + continue; + f = (struct frentry *)malloc(sizeof(*f)); + if (fr->fr_flags & FR_INQUE) { + if (!ft_in) + ft_in = filterin[0] = f; + else + ft_in->fr_next = f, ft_in = f; + } else if (fr->fr_flags & FR_OUTQUE) { + if (!ft_out) + ft_out = filterout[0] = f; + else + ft_out->fr_next = f, ft_out = f; + } + bcopy((char *)fr, (char *)f, sizeof(*fr)); + } + (void)fclose(fp); + } + + if (datain) + fd = (*(*r)->r_open)(datain); + else + fd = (*(*r)->r_open)("-"); + + if (fd < 0) + exit(-1); + + ip = (struct ip *)buf; + while ((i = (*(*r)->r_readip)(buf, sizeof(buf), &iface, &dir)) > 0) { + switch (fr_check(ip, ip->ip_hl << 2, iface, dir)) + { + case -1 : + (void)printf("block"); + break; + case 0 : + (void)printf("pass"); + break; + case 1 : + (void)printf("nomatch"); + break; + } + if (!(opts & OPT_BRIEF)) { + putchar(' '); + printpacket(buf); + printf("--------------"); + } + putchar('\n'); + dir = 0; + } + (*(*r)->r_close)(); + return 0; +} diff --git a/usr.sbin/ipftest/ipt.h b/usr.sbin/ipftest/ipt.h new file mode 100644 index 00000000000..fd0e5acf3e0 --- /dev/null +++ b/usr.sbin/ipftest/ipt.h @@ -0,0 +1,15 @@ +/* + * (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 <fcntl.h> + +struct ipread { + int (*r_open)(); + int (*r_close)(); + int (*r_readip)(); +}; diff --git a/usr.sbin/ipftest/misc.c b/usr.sbin/ipftest/misc.c new file mode 100644 index 00000000000..89a9883c4d7 --- /dev/null +++ b/usr.sbin/ipftest/misc.c @@ -0,0 +1,103 @@ +/* + * (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 <assert.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 <sys/ioctl.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcpip.h> +#include <net/if.h> +#include <netinet/ip_fil.h> +#include <netdb.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include "ipf.h" +#include "ipt.h" + +#ifndef lint +static char sccsid[] = "@(#)misc.c 1.1 10/15/95 (C) 1995 Darren Reed"; +#endif + +void debug(), verbose(); + +extern int opts; + + +void printpacket(ip) +struct ip *ip; +{ + struct tcphdr *tcp; + + tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); + printf("ip %d(%d) %d ", ip->ip_len, ip->ip_hl << 2, ip->ip_p); + if (ip->ip_off & 0x1fff) + printf("@%d", ip->ip_off << 3); + (void)printf(" %s", inet_ntoa(ip->ip_src)); + if (!(ip->ip_off & 0x1fff)) + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + (void)printf(",%d", ntohs(tcp->th_sport)); + (void)printf(" > "); + (void)printf("%s", inet_ntoa(ip->ip_dst)); + if (!(ip->ip_off & 0x1fff)) + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + (void)printf(",%d", ntohs(tcp->th_dport)); + putchar('\n'); +} + + +#ifdef NEED_INET_ATON +u_long inet_aton(buf) +char *buf; +{ + u_long n; + char *s = (char *)&n; + int a, b, c, d; + + if (sscanf(buf, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) + return -1; + + *s++ = (u_char)a; + *s++ = (u_char)b; + *s++ = (u_char)c; + *s++ = (u_char)d; + return n; +} +#endif + + +void verbose(fmt, p1, p2, p3, p4, p5, p6, p7, p8, p9) +char *fmt, *p1, *p2, *p3, *p4, *p5, *p6, *p7,*p8,*p9; +{ + if (opts & OPT_VERBOSE) + printf(fmt, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + + +void debug(fmt, p1, p2, p3, p4, p5, p6, p7, p8, p9) +char *fmt, *p1, *p2, *p3, *p4, *p5, *p6, *p7,*p8,*p9; +{ + if (opts & OPT_DEBUG) + printf(fmt, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} diff --git a/usr.sbin/ipftest/pcap.h b/usr.sbin/ipftest/pcap.h new file mode 100644 index 00000000000..f7efe142c6e --- /dev/null +++ b/usr.sbin/ipftest/pcap.h @@ -0,0 +1,32 @@ +/* + * (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. + */ +/* + * This header file is constructed to match the version described by + * PCAP_VERSION_MAJ. + * + * The structure largely derives from libpcap which wouldn't include + * nicely without bpf. + */ +typedef struct pcap_filehdr { + u_int pc_id; + u_short pc_v_maj; + u_short pc_v_min; + u_int pc_zone; + u_int pc_sigfigs; + u_int pc_slen; + u_int pc_type; +} pcaphdr_t; + +#define PCAP_VERSION_MAJ 2 + +typedef struct pcap_pkthdr { + struct timeval ph_ts; + u_int ph_clen; + u_int ph_len; +} pcappkt_t; + diff --git a/usr.sbin/ipftest/snoop.h b/usr.sbin/ipftest/snoop.h new file mode 100644 index 00000000000..37503ea29f8 --- /dev/null +++ b/usr.sbin/ipftest/snoop.h @@ -0,0 +1,41 @@ +/* + * (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. + */ + +/* + * written to comply with the RFC (1761) from Sun. + */ +struct snoophdr { + char s_id[8]; + int s_v; + int s_type; +}; + +#define SNOOP_VERSION 2 + +#define SDL_8023 0 +#define SDL_8024 1 +#define SDL_8025 2 +#define SDL_8026 3 +#define SDL_ETHER 4 +#define SDL_HDLC 5 +#define SDL_CHSYNC 6 +#define SDL_IBMCC 7 +#define SDL_FDDI 8 +#define SDL_OTHER 9 + +#define SDL_MAX 9 + + +struct snooppkt { + int sp_olen; + int sp_ilen; + int sp_plen; + int sp_drop; + int sp_sec; + int sp_usec; +}; diff --git a/usr.sbin/ipmon/Makefile b/usr.sbin/ipmon/Makefile new file mode 100644 index 00000000000..1a743162356 --- /dev/null +++ b/usr.sbin/ipmon/Makefile @@ -0,0 +1,6 @@ +PROG= ipmon +MAN= ipmon.8 +CFLAGS+=-DIPL_NAME=\"/dev/ipl\" -DLOGFAC=LOG_LOCAL0 + + +.include <bsd.prog.mk> diff --git a/usr.sbin/ipmon/ipmon.8 b/usr.sbin/ipmon/ipmon.8 new file mode 100644 index 00000000000..b6398c6bd6e --- /dev/null +++ b/usr.sbin/ipmon/ipmon.8 @@ -0,0 +1,51 @@ +.LP +.TH ipmon 8 +.SH NAME +ipmon - monitors /dev/ipl for logged packets +.SH SYNOPSIS +ipmon [-sfN] [<filename>] +.SH DESCRIPTION +.LP +\fBipmon\fP opens \fB/dev/ipl\fP for reading and awaits data to be saved from +the packet filter. The binary data read from the device is reprinted in +human readable for, however, IP#'s are not mapped back to hostnames, nor are +ports mapped back to service names. The output goes to standard output by +default or a filename, if given on the command line. Should the \fB-s\fP +option be used, output is instead sent to \fBsyslogd(8)\fP. Messages sent +via syslog have the day, month and year removed from the message, but the +time (including microseconds), as recorded in the log, is still included. +.SH OPTIONS +.TP +.B -s +Packet information read in will be sent through syslogd rather than +saved to a file. The following levels are used: +.TP +.IP +.RS +.B LOG_INFO - packets logged using the "log" keyword as the action rather +than pass or block. +.TP 3 +.B LOG_NOTICE - packets logged which are also passed +.TP 3 +.B LOG_WARNING - packets logged which are also blocked +.TP 3 +.B LOG_ERR - packets which have been logged and which can be considered +"short". +.RE +.TP +.B -f +Flush the current packet log buffer. The number of bytes flushed is displayed, +even should the result be zero. +.TP +.B -N +IP addresses and port numbers will be mapped, where possible, back into +hostnames and service names. +.SH DIAGNOSTICS +\fBipmon\fP expects data that it reads to be consistant with how it should be +saved and will abort if it fails an assertion which detects an anomoly in the +recorded data. +.SH FILES +/dev/ipl +.SH SEE ALSO +ipf(1), ipfstat(1) +.SH BUGS diff --git a/usr.sbin/ipmon/ipmon.c b/usr.sbin/ipmon/ipmon.c new file mode 100644 index 00000000000..0ba67d2f252 --- /dev/null +++ b/usr.sbin/ipmon/ipmon.c @@ -0,0 +1,284 @@ +/* + * (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 <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/syslog.h> +#include <sys/errno.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/uio.h> +#if !defined(__SVR4) && !defined(__svr4__) +#include <sys/dir.h> +#include <sys/mbuf.h> +#else +#include <sys/byteorder.h> +#endif +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/user.h> + +#include <net/if.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 <netinet/ip_icmp.h> +#include <netdb.h> +#include <arpa/inet.h> + +#ifndef lint +static char sccsid[] = "@(#)ipmon.c 1.13 11/11/95 (C)1995 Darren Reed"; +#endif + +#include <netinet/ip_fil.h> + +struct flags { + int value; + char flag; +}; + +struct flags tcpfl[] = { + { TH_ACK, 'A' }, + { TH_RST, 'R' }, + { TH_SYN, 'S' }, + { TH_FIN, 'F' }, + { TH_URG, 'U' }, + { TH_PUSH,'P' }, + { 0, '\0' } +}; + +static char line[2048]; +static void printpacket(); + + +char *hostname(res, ip) +int res; +struct in_addr ip; +{ + struct hostent *hp; + + if (!res) + return inet_ntoa(ip); + hp = gethostbyaddr((char *)&ip, sizeof(ip), AF_INET); + if (!hp) + return inet_ntoa(ip); + return hp->h_name; +} + + +char *portname(res, proto, port) +int res; +char *proto; +u_short port; +{ + static char pname[8]; + struct servent *serv; + + (void) sprintf(pname, "%hu", htons(port)); + if (!res) + return pname; + serv = getservbyport((int)port, proto); + if (!serv) + return pname; + return serv->s_name; +} + + +static void printpacket(log, ip, lp, opts) +FILE *log; +struct ip *ip; +struct ipl_ci *lp; +int opts; +{ + struct protoent *pr; + struct tcphdr *tp; + struct icmp *ic; + struct ip *ipc; + struct tm *tm; + char c[3], pname[8], *t, *proto; + u_short hl, p; + int i, lvl, res; + + res = (opts & 2) ? 1 : 0; + t = line; + *t = '\0'; + hl = (ip->ip_hl << 2); + p = (u_short)ip->ip_p; + tm = localtime((time_t *)&lp->sec); + if (!(opts & 1)) { + (void) sprintf(t, "%2d/%02d/%4d ", + tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900); + t += strlen(t); + } + (void) sprintf(t, "%02d:%02d:%02d.%-.6d %c%c%d @%d ", + tm->tm_hour, tm->tm_min, tm->tm_sec, lp->usec, + lp->ifname[0], lp->ifname[1], lp->unit, lp->rule); + pr = getprotobynumber((int)p); + if (!pr) { + proto = pname; + sprintf(proto, "%d", (u_int)p); + } else + proto = pr->p_name; + + if (lp->flags & (FI_SHORT << 16)) { + c[0] = 'S'; + lvl = LOG_ERR; + } else if (lp->flags & FR_PASS) { + if (lp->flags & FR_LOGP) + c[0] = 'p'; + else + c[0] = 'P'; + lvl = LOG_NOTICE; + } else if (lp->flags & FR_BLOCK) { + if (lp->flags & FR_LOGB) + c[0] = 'b'; + else + c[0] = 'B'; + lvl = LOG_WARNING; + } else { + c[0] = 'L'; + lvl = LOG_INFO; + } + c[1] = ' '; + c[2] = '\0'; + (void) strcat(line, c); + t = line + strlen(line); + + if ((p == IPPROTO_TCP || p == IPPROTO_UDP) && !(ip->ip_off & 0x1fff)) { + tp = (struct tcphdr *)((char *)ip + hl); + if (!(lp->flags & (FI_SHORT << 16))) { + (void) sprintf(t, "%s,%s -> ", + hostname(res, ip->ip_src), + portname(res, proto, tp->th_sport)); + t += strlen(t); + (void) sprintf(t, "%s,%s PR %s len %hu (%hu) ", + hostname(res, ip->ip_dst), + portname(res, proto, tp->th_dport), + proto, hl, ip->ip_len); + t += strlen(t); + + if (p == IPPROTO_TCP) + for (i = 0; tcpfl[i].value; i++) + if (tp->th_flags & tcpfl[i].value) + *t++ = tcpfl[i].flag; + *t = '\0'; + } else { + (void) sprintf(t, "%s -> ", hostname(res, ip->ip_src)); + t += strlen(t); + (void) sprintf(t, "%s PR %s len %hu (%hu)", + hostname(res, ip->ip_dst), proto, + hl, ip->ip_len); + } + } else if (p == IPPROTO_ICMP) { + ic = (struct icmp *)((char *)ip + hl); + (void) sprintf(t, "%s -> ", hostname(res, ip->ip_src)); + t += strlen(t); + (void) sprintf(t, "%s PR icmp len %hu (%hu) icmp %d/%d", + hostname(res, ip->ip_dst), hl, + ip->ip_len, ic->icmp_type, ic->icmp_code); + if (ic->icmp_type == ICMP_UNREACH || + ic->icmp_type == ICMP_SOURCEQUENCH || + ic->icmp_type == ICMP_PARAMPROB || + ic->icmp_type == ICMP_REDIRECT || + ic->icmp_type == ICMP_TIMXCEED) { + ipc = &ic->icmp_ip; + tp = (struct tcphdr *)((char *)ipc + hl); + + p = (u_short)ipc->ip_p; + pr = getprotobynumber((int)p); + if (!pr) { + proto = pname; + (void) sprintf(proto, "%d", (int)p); + } else + proto = pr->p_name; + + t += strlen(t); + (void) sprintf(t, " for %s,%s -", + hostname(res, ipc->ip_src), + portname(res, proto, tp->th_sport)); + t += strlen(t); + (void) sprintf(t, " %s,%s PR %s len %hu (%hu)", + hostname(res, ipc->ip_dst), + portname(res, proto, tp->th_dport), + proto, ipc->ip_hl << 2, ipc->ip_len); + } + } else { + (void) sprintf(t, "%s -> ", hostname(res, ip->ip_src)); + t += strlen(t); + (void) sprintf(t, "%s PR %s len %hu (%hu)", + hostname(res, ip->ip_dst), proto, hl, ip->ip_len); + t += strlen(t); + if (ip->ip_off & 0x1fff) + (void) sprintf(t, " frag %s%hu@%hu", + ip->ip_off & IP_MF ? "+" : "", + ip->ip_len - hl, (ip->ip_off & 0x1fff) << 3); + } + t += strlen(t); + *t++ = '\n'; + *t++ = '\0'; + if (opts & 1) + syslog(lvl, "%s", line); + else + (void) fprintf(log, "%s", line); + fflush(log); +} + +main(argc, argv) +int argc; +char *argv[]; +{ + FILE *log; + int fd, flushed = 0, opts = 0; + char buf[512], c; + struct ipl_ci iplci; + extern int optind; + + if ((fd = open(IPL_NAME, O_RDONLY)) == -1) { + (void) fprintf(stderr, "%s: ", IPL_NAME); + perror("open"); + exit(-1); + } + + while ((c = getopt(argc, argv, "Nfs")) != -1) + switch (c) + { + case 'f' : + (void) ioctl(fd, SIOCIPFFB, &flushed); + break; + case 'N' : + opts |= 2; + break; + case 's' : + openlog(argv[0], LOG_NDELAY|LOG_PID, LOGFAC); + opts |= 1; + break; + } + + log = argv[optind] ? fopen(argv[1], "a") : stdout; + setvbuf(log, NULL, _IONBF, 0); + if (flushed) + fprintf(log, "%d bytes flushed from log\n", flushed); + + while (1) { + assert(read(fd, &iplci, sizeof(struct ipl_ci)) == + sizeof(struct ipl_ci)); + assert(iplci.hlen > 0 && iplci.hlen <= 92); + assert((u_char)iplci.plen <= 128); + assert(read(fd, buf, iplci.hlen + iplci.plen) == + (iplci.hlen + iplci.plen)); + printpacket(log, buf, &iplci, opts); + } + return 0; +} diff --git a/usr.sbin/ipsend/44arp.c b/usr.sbin/ipsend/44arp.c new file mode 100644 index 00000000000..04afb619f24 --- /dev/null +++ b/usr.sbin/ipsend/44arp.c @@ -0,0 +1,92 @@ +/* + * Based upon 4.4BSD's /usr/sbin/arp + */ +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include <nlist.h> +#include <stdio.h> + +/* + * lookup host and return + * its IP address in address + * (4 bytes) + */ +int resolve(host, address) +char *host, *address; +{ + struct hostent *hp; + u_long add; + + add = inet_addr(host); + if (add == -1) + { + if (!(hp = gethostbyname(host))) + { + fprintf(stderr, "unknown host: %s\n", host); + return -1; + } + bcopy((char *)hp->h_addr, (char *)address, 4); + return 0; + } + bcopy((char*)&add, address, 4); + return 0; +} + + +int arp(addr, eaddr) +char *addr, *eaddr; +{ + int mib[6]; + size_t needed; + char *host, *malloc(), *lim, *buf, *next; + struct rt_msghdr *rtm; + struct sockaddr_inarp *sin; + struct sockaddr_dl *sdl; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_FLAGS; + mib[5] = RTF_LLINFO; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) + { + perror("route-sysctl-estimate"); + exit(-1); + } + if ((buf = malloc(needed)) == NULL) + { + perror("malloc"); + exit(-1); + } + if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) + { + perror("actual retrieval of routing table"); + exit(-1); + } + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) + { + rtm = (struct rt_msghdr *)next; + sin = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(sin + 1); + if (addr && !bcmp(addr, (char *)&sin->sin_addr, + sizeof(struct in_addr))) + { + bcopy(LLADDR(sdl), eaddr, sdl->sdl_alen); + return 0; + } + } + return -1; +} diff --git a/usr.sbin/ipsend/Makefile b/usr.sbin/ipsend/Makefile new file mode 100644 index 00000000000..7555119420f --- /dev/null +++ b/usr.sbin/ipsend/Makefile @@ -0,0 +1,9 @@ +PROG= ipsend +NOMAN= +SRCS= iptests.c ip.c ipsend.c ipsopt.c resend.c sbpf.c sock.c 44arp.c ipft_sn.c ipft_pc.c +.PATH: ${.CURDIR}/../../usr.sbin/ipftest +CFLAGS+= -DDOSOCKET -I${.CURDIR}/../../usr.sbin/ipftest -I${.CURDIR}/../../sbin/ipf +LDADD+= -lpcap + + +.include <bsd.prog.mk> diff --git a/usr.sbin/ipsend/dltest.h b/usr.sbin/ipsend/dltest.h new file mode 100644 index 00000000000..4c32c30eb1b --- /dev/null +++ b/usr.sbin/ipsend/dltest.h @@ -0,0 +1,32 @@ +/* + * Common DLPI Test Suite header file + * + */ + +/* + * Maximum control/data buffer size (in long's !!) for getmsg(). + */ +#define MAXDLBUF 8192 + +/* + * Maximum number of seconds we'll wait for any + * particular DLPI acknowledgment from the provider + * after issuing a request. + */ +#define MAXWAIT 15 + +/* + * Maximum address buffer length. + */ +#define MAXDLADDR 1024 + + +/* + * Handy macro. + */ +#define OFFADDR(s, n) (u_char*)((char*)(s) + (int)(n)) + +/* + * externs go here + */ +extern void sigalrm(); diff --git a/usr.sbin/ipsend/in_var.h b/usr.sbin/ipsend/in_var.h new file mode 100644 index 00000000000..63980ef304e --- /dev/null +++ b/usr.sbin/ipsend/in_var.h @@ -0,0 +1,177 @@ +/* @(#)in_var.h 1.3 88/08/19 SMI; from UCB 7.1 6/5/86 */ + +/* + * Copyright (c) 1985, 1986 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +/* + * Interface address, Internet version. One of these structures + * is allocated for each interface with an Internet address. + * The ifaddr structure contains the protocol-independent part + * of the structure and is assumed to be first. + */ + +#ifndef _netinet_in_var_h +#define _netinet_in_var_h + +struct in_ifaddr { + struct ifaddr ia_ifa; /* protocol-independent info */ +#define ia_addr ia_ifa.ifa_addr +#define ia_broadaddr ia_ifa.ifa_broadaddr +#define ia_dstaddr ia_ifa.ifa_dstaddr +#define ia_ifp ia_ifa.ifa_ifp + u_long ia_net; /* network number of interface */ + u_long ia_netmask; /* mask of net part */ + u_long ia_subnet; /* subnet number, including net */ + u_long ia_subnetmask; /* mask of net + subnet */ + struct in_addr ia_netbroadcast; /* broadcast addr for (logical) net */ + int ia_flags; + struct in_ifaddr *ia_next; /* next in list of internet addresses */ + struct in_multi *ia_multiaddrs;/* list of multicast addresses */ +}; +/* + * Given a pointer to an in_ifaddr (ifaddr), + * return a pointer to the addr as a sockadd_in. + */ +#define IA_SIN(ia) ((struct sockaddr_in *)(&((struct in_ifaddr *)ia)->ia_addr)) +/* + * ia_flags + */ +#define IFA_ROUTE 0x01 /* routing entry installed */ + +#ifdef KERNEL +struct in_ifaddr *in_ifaddr; +struct in_ifaddr *in_iaonnetof(); +struct ifqueue ipintrq; /* ip packet input queue */ +#endif + +#ifdef KERNEL +/* + * Macro for finding the interface (ifnet structure) corresponding to one + * of our IP addresses. + */ +#define INADDR_TO_IFP(addr, ifp) \ + /* struct in_addr addr; */ \ + /* struct ifnet *ifp; */ \ +{ \ + register struct in_ifaddr *ia; \ + \ + for (ia = in_ifaddr; \ + ia != NULL && IA_SIN(ia)->sin_addr.s_addr != (addr).s_addr; \ + ia = ia->ia_next); \ + (ifp) = (ia == NULL) ? NULL : ia->ia_ifp; \ +} + +/* + * Macro for finding the internet address structure (in_ifaddr) corresponding + * to a given interface (ifnet structure). + */ +#define IFP_TO_IA(ifp, ia) \ + /* struct ifnet *ifp; */ \ + /* struct in_ifaddr *ia; */ \ +{ \ + for ((ia) = in_ifaddr; \ + (ia) != NULL && (ia)->ia_ifp != (ifp); \ + (ia) = (ia)->ia_next); \ +} +#endif KERNEL + +/* + * Per-interface router version information is kept in this list. + * This information should be part of the ifnet structure but we don't wish + * to change that - as it might break a number of things + */ + +struct router_info { + struct ifnet *ifp; + int type; /* type of router which is querier on this interface */ + int time; /* # of slow timeouts since last old query */ + struct router_info *next; +}; + +/* + * Internet multicast address structure. There is one of these for each IP + * multicast group to which this host belongs on a given network interface. + * They are kept in a linked list, rooted in the interface's in_ifaddr + * structure. + */ + +struct in_multi { + struct in_addr inm_addr; /* IP multicast address */ + struct ifnet *inm_ifp; /* back pointer to ifnet */ + struct in_ifaddr *inm_ia; /* back pointer to in_ifaddr */ + u_int inm_refcount;/* no. membership claims by sockets */ + u_int inm_timer; /* IGMP membership report timer */ + struct in_multi *inm_next; /* ptr to next multicast address */ + u_int inm_state; /* state of the membership */ + struct router_info *inm_rti; /* router info*/ +}; + +#ifdef KERNEL +/* + * Structure used by macros below to remember position when stepping through + * all of the in_multi records. + */ +struct in_multistep { + struct in_ifaddr *i_ia; + struct in_multi *i_inm; +}; + +/* + * Macro for looking up the in_multi record for a given IP multicast address + * on a given interface. If no matching record is found, "inm" returns NULL. + */ +#define IN_LOOKUP_MULTI(addr, ifp, inm) \ + /* struct in_addr addr; */ \ + /* struct ifnet *ifp; */ \ + /* struct in_multi *inm; */ \ +{ \ + register struct in_ifaddr *ia; \ + \ + IFP_TO_IA((ifp), ia); \ + if (ia == NULL) \ + (inm) = NULL; \ + else \ + for ((inm) = ia->ia_multiaddrs; \ + (inm) != NULL && (inm)->inm_addr.s_addr != (addr).s_addr; \ + (inm) = inm->inm_next); \ +} + +/* + * Macro to step through all of the in_multi records, one at a time. + * The current position is remembered in "step", which the caller must + * provide. IN_FIRST_MULTI(), below, must be called to initialize "step" + * and get the first record. Both macros return a NULL "inm" when there + * are no remaining records. + */ +#define IN_NEXT_MULTI(step, inm) \ + /* struct in_multistep step; */ \ + /* struct in_multi *inm; */ \ +{ \ + if (((inm) = (step).i_inm) != NULL) { \ + (step).i_inm = (inm)->inm_next; \ + } \ + else while ((step).i_ia != NULL) { \ + (inm) = (step).i_ia->ia_multiaddrs; \ + (step).i_ia = (step).i_ia->ia_next; \ + if ((inm) != NULL) { \ + (step).i_inm = (inm)->inm_next; \ + break; \ + } \ + } \ +} + +#define IN_FIRST_MULTI(step, inm) \ + /* struct in_multistep step; */ \ + /* struct in_multi *inm; */ \ +{ \ + (step).i_ia = in_ifaddr; \ + (step).i_inm = NULL; \ + IN_NEXT_MULTI((step), (inm)); \ +} + +struct in_multi *in_addmulti(); +#endif KERNEL +#endif /*!_netinet_in_var_h*/ diff --git a/usr.sbin/ipsend/ip.c b/usr.sbin/ipsend/ip.c new file mode 100644 index 00000000000..e71eaabdba0 --- /dev/null +++ b/usr.sbin/ipsend/ip.c @@ -0,0 +1,342 @@ +/* + * ip.c (C) 1995 Darren Reed + * + * The author provides this program as-is, with no gaurantee for its + * suitability for any specific purpose. The author takes no responsibility + * for the misuse/abuse of this program and provides it for the sole purpose + * of testing packet filter policies. This file maybe distributed freely + * providing it is not modified and that this notice remains in tact. + */ +#ifndef lint +static char sccsid[] = "%W% %G% (C)1995"; +#endif +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#ifndef linux +#include <netinet/if_ether.h> +#include <netinet/ip_var.h> +#include <netinet/tcpip.h> +#endif +#include "ip_compat.h" +#ifdef linux +#include "tcpip.h" +#endif + + +static char *ipbuf = NULL, *ethbuf = NULL; + + +u_short chksum(buf,len) +u_short *buf; +int len; +{ + u_long sum = 0; + int nwords = len >> 1; + + for(; nwords > 0; nwords--) + sum += *buf++; + sum = (sum>>16) + (sum & 0xffff); + sum += (sum >>16); + return (~sum); +} + + +int send_ether(nfd, buf, len, gwip) +int nfd, len; +char *buf; +struct in_addr gwip; +{ + static struct in_addr last_gw; + static char last_arp[6] = { 0, 0, 0, 0, 0, 0}; + ether_header_t *eh; + char *s; + int err; + + if (!ethbuf) + ethbuf = (char *)calloc(1, 65536+1024); + s = ethbuf; + eh = (ether_header_t *)s; + + bcopy((char *)buf, s + sizeof(*eh), len); + if (gwip.s_addr == last_gw.s_addr) + bcopy(last_arp, eh->ether_dhost, 6); + else if (arp((char *)&gwip, &eh->ether_dhost) == -1) + { + perror("arp"); + free(buf); + return -2; + } + eh->ether_type = htons((u_short)ETHERTYPE_IP); + last_gw.s_addr = gwip.s_addr; + err = sendip(nfd, s, sizeof(*eh) + len); + free(buf); + return err; +} + + +/* + */ +int send_ip(nfd, mtu, ip, gwip) +int nfd, mtu; +ip_t *ip; +struct in_addr gwip; +{ + static struct in_addr last_gw; + static char last_arp[6] = { 0, 0, 0, 0, 0, 0}; + static u_short id = 0; + ether_header_t *eh; + ip_t ipsv; + int err; + + if (!ipbuf) + ipbuf = (char *)malloc(65536); + eh = (ether_header_t *)ipbuf; + + bzero(&eh->ether_shost, sizeof(eh->ether_shost)); + if (gwip.s_addr == last_gw.s_addr) + bcopy(last_arp, eh->ether_dhost, 6); + else if (arp((char *)&gwip, &eh->ether_dhost) == -1) + { + perror("arp"); + return -2; + } + eh->ether_type = htons((u_short)ETHERTYPE_IP); + + bcopy((char *)ip, (char *)&ipsv, sizeof(*ip)); + last_gw.s_addr = gwip.s_addr; + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + if (!ip->ip_v) + ip->ip_v = IPVERSION; + if (!ip->ip_id) + ip->ip_id = htons(id++); + if (!ip->ip_ttl) + ip->ip_ttl = 60; + + if (sizeof(*eh) + ntohs(ip->ip_len) < mtu) + { + ip->ip_sum = 0; + ip->ip_sum = chksum(ip, ip->ip_hl << 2); + + bcopy((char *)ip, ipbuf + sizeof(*eh), ntohs(ip->ip_len)); + err = sendip(nfd, ipbuf, sizeof(*eh) + ntohs(ip->ip_len)); + } + else + { + /* + * Actually, this is bogus because we're putting all IP + * options in every packet, which isn't always what should be + * done. Will do for now. + */ + ether_header_t eth; + char optcpy[48], ol; + char *s; + int i, iplen, sent = 0, ts, hlen, olen; + + hlen = ip->ip_hl << 2; + if (mtu < (hlen + 8)) { + fprintf(stderr, "mtu (%d) < ip header size (%d) + 8\n", + mtu, hlen); + fprintf(stderr, "can't fragment data\n"); + return -2; + } + ol = (ip->ip_hl << 2) - sizeof(*ip); + for (i = 0, s = (char*)(ip + 1); ol > 0; ) + if (*s == IPOPT_EOL) { + optcpy[i++] = *s; + break; + } else if (*s == IPOPT_NOP) { + s++; + ol--; + } else + { + olen = (int)(*(u_char *)(s + 1)); + ol -= olen; + if (IPOPT_COPIED(*s)) + { + bcopy(s, optcpy + i, olen); + i += olen; + s += olen; + } + } + if (i) + { + /* + * pad out + */ + while ((i & 3) && (i & 3) != 3) + optcpy[i++] = IPOPT_NOP; + if (i & 3 == 3) + optcpy[i++] = IPOPT_EOL; + } + + bcopy((char *)eh, (char *)ð, sizeof(eth)); + s = (char *)ip + hlen; + iplen = ntohs(ip->ip_len) - hlen; + ip->ip_off |= htons(IP_MF); + + while (1) + { + if ((sent + (mtu - hlen)) >= iplen) + { + ip->ip_off ^= htons(IP_MF); + ts = iplen - sent; + } + else + ts = (mtu - hlen); + ip->ip_off &= htons(0xe000); + ip->ip_off |= htons(sent >> 3); + ts += hlen; + ip->ip_len = htons(ts); + ip->ip_sum = 0; + ip->ip_sum = chksum(ip, hlen); + bcopy((char *)ip, ipbuf + sizeof(*eh), hlen); + bcopy(s + sent, ipbuf + sizeof(*eh) + hlen, ts - hlen); + err = sendip(nfd, ipbuf, sizeof(*eh) + ts); + + bcopy((char *)ð, ipbuf, sizeof(eth)); + sent += (ts - hlen); + if (!(ntohs(ip->ip_off) & IP_MF)) + break; + else if (!(ip->ip_off & htons(0x1fff))) + { + hlen = i + sizeof(*ip); + ip->ip_hl = (sizeof(*ip) + i) >> 2; + bcopy(optcpy, (char *)(ip + 1), i); + } + } + } + + bcopy((char *)&ipsv, (char *)ip, sizeof(*ip)); + return err; +} + + +/* + * send a tcp packet. + */ +int send_tcp(nfd, mtu, ip, gwip) +int nfd, mtu; +ip_t *ip; +struct in_addr gwip; +{ + static tcp_seq iss = 2; + struct tcpiphdr *ti; + int thlen, i; + u_long lbuf[20]; + + ti = (struct tcpiphdr *)lbuf; + bzero((char *)ti, sizeof(*ti)); + thlen = sizeof(tcphdr_t); + ip->ip_p = IPPROTO_TCP; + ti->ti_pr = ip->ip_p; + ti->ti_src = ip->ip_src; + ti->ti_dst = ip->ip_dst; + bcopy((char *)ip + (ip->ip_hl << 2), + (char *)&ti->ti_sport, sizeof(tcphdr_t)); + + if (!ti->ti_win) + ti->ti_win = htons(4096); + if (!ti->ti_seq) + ti->ti_seq = htonl(iss); + iss += 64; + + if ((ti->ti_flags == TH_SYN) && !ip->ip_off) + { + ip = (ip_t *)realloc((char *)ip, ntohs(ip->ip_len) + 4); + i = sizeof(struct tcpiphdr) / sizeof(long); + lbuf[i] = htonl(0x020405b4); + bcopy((char *)(lbuf + i), (char*)ip + ntohs(ip->ip_len), + sizeof(u_long)); + thlen += 4; + } + if (!ti->ti_off) + ti->ti_off = thlen >> 2; + ti->ti_len = htons(thlen); + ip->ip_len = (ip->ip_hl << 2) + thlen; + ti->ti_sum = 0; + ti->ti_sum = chksum(ti, thlen + sizeof(ip_t)); + + bcopy((char *)&ti->ti_sport, + (char *)ip + (ip->ip_hl << 2), thlen); + return send_ip(nfd, mtu, ip, gwip); +} + + +/* + * send a udp packet. + */ +int send_udp(nfd, mtu, ip, gwip) +int nfd, mtu; +ip_t *ip; +struct in_addr gwip; +{ + struct tcpiphdr *ti; + int thlen, i; + u_long lbuf[20]; + + ti = (struct tcpiphdr *)lbuf; + bzero((char *)ti, sizeof(*ti)); + thlen = sizeof(udphdr_t); + ti->ti_pr = ip->ip_p; + ti->ti_src = ip->ip_src; + ti->ti_dst = ip->ip_dst; + bcopy((char *)ip + (ip->ip_hl << 2), + (char *)&ti->ti_sport, sizeof(udphdr_t)); + + ti->ti_len = htons(thlen); + ip->ip_len = (ip->ip_hl << 2) + thlen; + ti->ti_sum = 0; + ti->ti_sum = chksum(ti, thlen + sizeof(ip_t)); + + bcopy((char *)&ti->ti_sport, + (char *)ip + (ip->ip_hl << 2), sizeof(udphdr_t)); + return send_ip(nfd, mtu, ip, gwip); +} + + +/* + * send an icmp packet. + */ +int send_icmp(nfd, mtu, ip, gwip) +int nfd, mtu; +ip_t *ip; +struct in_addr gwip; +{ + struct icmp *ic; + + ic = (struct icmp *)((char *)ip + (ip->ip_hl << 2)); + + ic->icmp_cksum = 0; + ic->icmp_cksum = chksum((char *)ic, sizeof(struct icmp)); + + return send_ip(nfd, mtu, ip, gwip); +} + + +int send_packet(nfd, mtu, ip, gwip) +int nfd, mtu; +ip_t *ip; +struct in_addr gwip; +{ + switch (ip->ip_p) + { + case IPPROTO_TCP : + return send_tcp(nfd, mtu, ip, gwip); + case IPPROTO_UDP : + return send_udp(nfd, mtu, ip, gwip); + case IPPROTO_ICMP : + return send_icmp(nfd, mtu, ip, gwip); + default : + return send_ip(nfd, mtu, ip, gwip); + } +} diff --git a/usr.sbin/ipsend/ip_compat.h b/usr.sbin/ipsend/ip_compat.h new file mode 100644 index 00000000000..a911fd83c3f --- /dev/null +++ b/usr.sbin/ipsend/ip_compat.h @@ -0,0 +1,201 @@ +/* + * (C)opyright 1995 by Darren Reed. + * + * This code may be freely distributed as long as it retains this notice + * and is not changed in any way. The author accepts no responsibility + * for the use of this software. I hate legaleese, don't you ? + * + * @(#)ip_compat.h 1.1 9/14/95 + */ + +/* + * These #ifdef's are here mainly for linux, but who knows, they may + * not be in other places or maybe one day linux will grow up and some + * of these will turn up there too. + */ +#ifndef ICMP_UNREACH +# define ICMP_UNREACH ICMP_DEST_UNREACH +#endif +#ifndef ICMP_SOURCEQUENCH +# define ICMP_SOURCEQUENCH ICMP_SOURCE_QUENCH +#endif +#ifndef ICMP_TIMXCEED +# define ICMP_TIMXCEED ICMP_TIME_EXCEEDED +#endif +#ifndef ICMP_PARAMPROB +# define ICMP_PARAMPROB ICMP_PARAMETERPROB +#endif +#ifndef IPVERSION +# define IPVERSION 4 +#endif +#ifndef IPOPT_MINOFF +# define IPOPT_MINOFF 4 +#endif +#ifndef IPOPT_COPIED +# define IPOPT_COPIED(x) ((x)&0x80) +#endif +#ifndef IPOPT_EOL +# define IPOPT_EOL 0 +#endif +#ifndef IPOPT_NOP +# define IPOPT_NOP 1 +#endif +#ifndef IP_MF +# define IP_MF ((u_short)0x2000) +#endif +#ifndef ETHERTYPE_IP +# define ETHERTYPE_IP ((u_short)0x0800) +#endif +#ifndef TH_FIN +# define TH_FIN 0x01 +#endif +#ifndef TH_SYN +# define TH_SYN 0x02 +#endif +#ifndef TH_RST +# define TH_RST 0x04 +#endif +#ifndef TH_PUSH +# define TH_PUSH 0x08 +#endif +#ifndef TH_ACK +# define TH_ACK 0x10 +#endif +#ifndef TH_URG +# define TH_URG 0x20 +#endif +#ifndef IPOPT_EOL +# define IPOPT_EOL 0 +#endif +#ifndef IPOPT_NOP +# define IPOPT_NOP 1 +#endif +#ifndef IPOPT_RR +# define IPOPT_RR 7 +#endif +#ifndef IPOPT_TS +# define IPOPT_TS 68 +#endif +#ifndef IPOPT_SECURITY +# define IPOPT_SECURITY 130 +#endif +#ifndef IPOPT_LSRR +# define IPOPT_LSRR 131 +#endif +#ifndef IPOPT_SATID +# define IPOPT_SATID 136 +#endif +#ifndef IPOPT_SSRR +# define IPOPT_SSRR 137 +#endif +#ifndef IPOPT_SECUR_UNCLASS +# define IPOPT_SECUR_UNCLASS ((u_short)0x0000) +#endif +#ifndef IPOPT_SECUR_CONFID +# define IPOPT_SECUR_CONFID ((u_short)0xf135) +#endif +#ifndef IPOPT_SECUR_EFTO +# define IPOPT_SECUR_EFTO ((u_short)0x789a) +#endif +#ifndef IPOPT_SECUR_MMMM +# define IPOPT_SECUR_MMMM ((u_short)0xbc4d) +#endif +#ifndef IPOPT_SECUR_RESTR +# define IPOPT_SECUR_RESTR ((u_short)0xaf13) +#endif +#ifndef IPOPT_SECUR_SECRET +# define IPOPT_SECUR_SECRET ((u_short)0xd788) +#endif +#ifndef IPOPT_SECUR_TOPSECRET +# define IPOPT_SECUR_TOPSECRET ((u_short)0x6bc5) +#endif + +#ifdef linux +# define icmp icmphdr +# define icmp_type type +# define icmp_code code + +/* + * From /usr/include/netinet/ip_var.h + * !%@#!$@# linux... + */ +struct ipovly { + caddr_t ih_next, ih_prev; /* for protocol sequence q's */ + u_char ih_x1; /* (unused) */ + u_char ih_pr; /* protocol */ + short ih_len; /* protocol length */ + struct in_addr ih_src; /* source internet address */ + struct in_addr ih_dst; /* destination internet address */ +}; + +typedef struct { + __u16 th_sport; + __u16 th_dport; + __u32 th_seq; + __u32 th_ack; +# if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ + defined(vax) + __u8 th_res:4; + __u8 th_off:4; +#else + __u8 th_off:4; + __u8 th_res:4; +#endif + __u8 th_flags; + __u16 th_win; + __u16 th_sum; + __u16 th_urp; +} tcphdr_t; + +typedef struct { + __u16 uh_sport; + __u16 uh_dport; + __s16 uh_ulen; + __u16 uh_sum; +} udphdr_t; + +typedef struct { +# if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ + defined(vax) + __u8 ip_hl:4; + __u8 ip_v:4; +# else + __u8 ip_hl:4; + __u8 ip_v:4; +# endif + __u8 ip_tos; + __u16 ip_len; + __u16 ip_id; + __u16 ip_off; + __u8 ip_ttl; + __u8 ip_p; + __u16 ip_sum; + struct in_addr ip_src; + struct in_addr ip_dst; +} ip_t; + +typedef struct { + __u8 ether_dhost[6]; + __u8 ether_shost[6]; + __u16 ether_type; +} ether_header_t; + +# define bcopy(a,b,c) memmove(b,a,c) +# define bcmp(a,b,c) memcmp(a,b,c) + +# define ifnet device + +#else + +typedef struct udphdr udphdr_t; +typedef struct tcphdr tcphdr_t; +typedef struct ip ip_t; +typedef struct ether_header ether_header_t; + +#endif + +#ifdef solaris +# define bcopy(a,b,c) memmove(b,a,c) +# define bcmp(a,b,c) memcmp(a,b,c) +# define bzero(a,b) memset(a,0,b) +#endif diff --git a/usr.sbin/ipsend/ip_var.h b/usr.sbin/ipsend/ip_var.h new file mode 100644 index 00000000000..92eb38a0bef --- /dev/null +++ b/usr.sbin/ipsend/ip_var.h @@ -0,0 +1,123 @@ +/* @(#)ip_var.h 1.11 88/08/19 SMI; from UCB 7.1 6/5/86 */ + +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +/* + * Overlay for ip header used by other protocols (tcp, udp). + */ + +#ifndef _netinet_ip_var_h +#define _netinet_ip_var_h + +struct ipovly { + caddr_t ih_next, ih_prev; /* for protocol sequence q's */ + u_char ih_x1; /* (unused) */ + u_char ih_pr; /* protocol */ + short ih_len; /* protocol length */ + struct in_addr ih_src; /* source internet address */ + struct in_addr ih_dst; /* destination internet address */ +}; + +/* + * Ip reassembly queue structure. Each fragment + * being reassembled is attached to one of these structures. + * They are timed out after ipq_ttl drops to 0, and may also + * be reclaimed if memory becomes tight. + */ +struct ipq { + struct ipq *next,*prev; /* to other reass headers */ + u_char ipq_ttl; /* time for reass q to live */ + u_char ipq_p; /* protocol of this fragment */ + u_short ipq_id; /* sequence id for reassembly */ + struct ipasfrag *ipq_next,*ipq_prev; + /* to ip headers of fragments */ + struct in_addr ipq_src,ipq_dst; +}; + +/* + * Ip header, when holding a fragment. + * + * Note: ipf_next must be at same offset as ipq_next above + */ +struct ipasfrag { +#if defined(vax) || defined(i386) + u_char ip_hl:4, + ip_v:4; +#endif +#if defined(mc68000) || defined(sparc) + u_char ip_v:4, + ip_hl:4; +#endif + u_char ipf_mff; /* copied from (ip_off&IP_MF) */ + short ip_len; + u_short ip_id; + short ip_off; + u_char ip_ttl; + u_char ip_p; + u_short ip_sum; + struct ipasfrag *ipf_next; /* next fragment */ + struct ipasfrag *ipf_prev; /* previous fragment */ +}; + +/* + * Structure stored in mbuf in inpcb.ip_options + * and passed to ip_output when ip options are in use. + * The actual length of the options (including ipopt_dst) + * is in m_len. + */ +#define MAX_IPOPTLEN 40 + +struct ipoption { + struct in_addr ipopt_dst; /* first-hop dst if source routed */ + char ipopt_list[MAX_IPOPTLEN]; /* options proper */ +}; + +/* + * Structure stored in an mbuf attached to inpcb.ip_moptions and + * passed to ip_output when IP multicast options are in use. + */ +struct ip_moptions { + struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */ + u_char imo_multicast_ttl; /* TTL for outgoing multicasts */ + u_char imo_multicast_loop; /* 1 => hear sends if a member */ + u_short imo_num_memberships;/* no. memberships this socket */ + struct in_multi *imo_membership[IP_MAX_MEMBERSHIPS]; +#ifdef RSVP_ISI + long imo_multicast_vif; /* vif for outgoing multicasts */ +#endif /* RSVP_ISI */ +}; + +struct ipstat { + long ips_total; /* total packets received */ + long ips_badsum; /* checksum bad */ + long ips_tooshort; /* packet too short */ + long ips_toosmall; /* not enough data */ + long ips_badhlen; /* ip header length < data size */ + long ips_badlen; /* ip length < ip header length */ + long ips_fragments; /* fragments received */ + long ips_fragdropped; /* frags dropped (dups, out of space) */ + long ips_fragtimeout; /* fragments timed out */ + long ips_forward; /* packets forwarded */ + long ips_cantforward; /* packets rcvd for unreachable dest */ + long ips_redirectsent; /* packets forwarded on same net */ +}; + +#ifdef KERNEL +/* flags passed to ip_output as last parameter */ +#define IP_FORWARDING 0x1 /* most of ip header exists */ +#define IP_MULTICASTOPTS 0x2 /* multicast opts present */ +#define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */ +#define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */ + +struct ipstat ipstat; +struct ipq ipq; /* ip reass. queue */ +u_short ip_id; /* ip packet ctr, for ids */ + +struct mbuf *ip_srcroute(); +#endif + +#endif /*!_netinet_ip_var_h*/ diff --git a/usr.sbin/ipsend/ipsend.c b/usr.sbin/ipsend/ipsend.c new file mode 100644 index 00000000000..0ffaa0915c8 --- /dev/null +++ b/usr.sbin/ipsend/ipsend.c @@ -0,0 +1,423 @@ +/* + * ipsend.c (C) 1995 Darren Reed + * + * This was written to test what size TCP fragments would get through + * various TCP/IP packet filters, as used in IP firewalls. In certain + * conditions, enough of the TCP header is missing for unpredictable + * results unless the filter is aware that this can happen. + * + * The author provides this program as-is, with no gaurantee for its + * suitability for any specific purpose. The author takes no responsibility + * for the misuse/abuse of this program and provides it for the sole purpose + * of testing packet filter policies. This file maybe distributed freely + * providing it is not modified and that this notice remains in tact. + * + * This was written and tested (successfully) on SunOS 4.1.x. + */ +#ifndef lint +static char sccsid[] = "%W% %G% (C)1995 Darren Reed"; +#endif +#include <stdio.h> +#include <netdb.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/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#ifndef linux +#include <netinet/ip_var.h> +#include <netinet/tcpip.h> +#endif +#include "ip_compat.h" +#ifdef linux +#include <linux/sockios.h> +#include "tcpip.h" +#endif +#include "ipt.h" + +#if defined (__OpenBSD__) || defined (__NetBSD__) +/* XXX - ipftest already has a pcap.h file, so just declare this here */ +char *pcap_lookupdev (char *); +#endif + +extern char *optarg; +extern int optind; +extern struct ipread snoop, pcap; + +struct ipread *readers[] = { &snoop, &pcap, NULL }; +char options[68]; +#ifdef linux +char default_device[] = "eth0"; +#else +# ifdef sun +char default_device[] = "le0"; +# else +# ifdef ultrix +char default_device[] = "ln0"; +# else +# ifdef __bsdi__ +char default_device[] = "ef0"; +# else +char default_device[] = "le0"; +# endif +# endif +# endif +#endif + + +void usage(prog) +char *prog; +{ + fprintf(stderr, "Usage: %s [options] dest [flags]\n\ +\toptions:\n\ +\t\t-d device\tSend out on this device\n\ +\t\t-f fragflags\tcan set IP_MF or IP_DF\n\ +\t\t-g gateway\tIP gateway to use if non-local dest.\n\ +\t\t-I code,type[,gw[,dst[,src]]]\tSet ICMP protocol\n\ +\t\t-m mtu\t\tfake MTU to use when sending out\n\ +\t\t-P protocol\tSet protocol by name\n\ +\t\t-s src\t\tsource address for IP packet\n\ +\t\t-T\t\tSet TCP protocol\n\ +\t\t-t port\t\tdestination port\n\ +\t\t-U\t\tSet UDP protocol\n\ +", prog); + fprintf(stderr, "Usage: %s [options] -g gateway\n\ +\toptions:\n\ +\t\t-r filename\tsnoop data file to resend\n\ +\t\t-R filename\tlibpcap data file to resend\n\ +", prog); + fprintf(stderr, "Usage: %s [options] destination\n\ +\toptions:\n\ +\t\t-d device\tSend out on this device\n\ +\t\t-m mtu\t\tfake MTU to use when sending out\n\ +\t\t-s src\t\tsource address for IP packet\n\ +\t\t-1 \t\tPerform test 1 (IP header)\n\ +\t\t-2 \t\tPerform test 2 (IP options)\n\ +\t\t-3 \t\tPerform test 3 (ICMP)\n\ +\t\t-4 \t\tPerform test 4 (UDP)\n\ +\t\t-5 \t\tPerform test 5 (TCP)\n\ +", prog); + exit(1); +} + + +void do_icmp(ip, args) +ip_t *ip; +char *args; +{ + struct icmp *ic; + char *s; + + ip->ip_p = IPPROTO_ICMP; + ip->ip_len += sizeof(*ic); + ic = (struct icmp *)(ip + 1); + bzero((char *)ic, sizeof(*ic)); + if (!(s = strchr(args, ','))) + { + fprintf(stderr, "ICMP args missing: ,\n"); + return; + } + *s++ = '\0'; + ic->icmp_type = atoi(args); + ic->icmp_code = atoi(s); + if (ic->icmp_type == ICMP_REDIRECT && strchr(s, ',')) + { + char *t; + + t = strtok(s, ","); + t = strtok(NULL, ","); + if (resolve(t, (char *)&ic->icmp_gwaddr) == -1) + { + fprintf(stderr,"Cant resolve %s\n", t); + exit(2); + } + if ((t = strtok(NULL, ","))) + { + if (resolve(t, (char *)&ic->icmp_ip.ip_dst) == -1) + { + fprintf(stderr,"Cant resolve %s\n", t); + exit(2); + } + if ((t = strtok(NULL, ","))) + { + if (resolve(t, + (char *)&ic->icmp_ip.ip_src) == -1) + { + fprintf(stderr,"Cant resolve %s\n", t); + exit(2); + } + } + } + } +} + + +int send_packets(dev, mtu, ip, gwip) +char *dev; +int mtu; +ip_t *ip; +struct in_addr gwip; +{ + u_short sport = 0; + int wfd; + + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + sport = ((struct tcpiphdr *)ip)->ti_sport; + wfd = initdevice(dev, sport, 5); + + return send_packet(wfd, mtu, ip, gwip); +} + + +main(argc, argv) +int argc; +char **argv; +{ + struct tcpiphdr *ti; + struct in_addr gwip; + struct ipread *ipr = NULL; + tcphdr_t *tcp; + ip_t *ip; + char *name = argv[0], host[64], *gateway = NULL, *dev = NULL; + char *src = NULL, *dst, c, *s, *resend = NULL; + int mtu = 1500, olen = 0, tests = 0, pointtest = 0; + + /* + * 65535 is maximum packet size...you never know... + */ + ip = (ip_t *)calloc(1, 65536); + ti = (struct tcpiphdr *)ip; + tcp = (tcphdr_t *)&ti->ti_sport; + ip->ip_len = sizeof(*ip); + ip->ip_hl = sizeof(*ip) >> 2; + + while ((c = getopt(argc, argv, "12345IP:R:TUd:f:g:m:o:p:r:s:t:")) != -1) + switch (c) + { + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + tests = c - '0'; + break; + case 'I' : + if (ip->ip_p) + { + fprintf(stderr, "Protocol already set: %d\n", + ip->ip_p); + break; + } + do_icmp(ip, optarg); + break; + case 'P' : + { + struct protoent *p; + + if (ip->ip_p) + { + fprintf(stderr, "Protocol already set: %d\n", + ip->ip_p); + break; + } + if ((p = getprotobyname(optarg))) + ip->ip_p = p->p_proto; + else + fprintf(stderr, "Unknown protocol: %s\n", + optarg); + break; + } + case 'R' : + resend = optarg; + ipr = &pcap; + break; + case 'T' : + if (ip->ip_p) + { + fprintf(stderr, "Protocol already set: %d\n", + ip->ip_p); + break; + } + ip->ip_p = IPPROTO_TCP; + ip->ip_len += sizeof(tcphdr_t); + break; + case 'U' : + if (ip->ip_p) + { + fprintf(stderr, "Protocol already set: %d\n", + ip->ip_p); + break; + } + ip->ip_p = IPPROTO_UDP; + ip->ip_len += sizeof(udphdr_t); + break; + case 'd' : + dev = optarg; + break; + case 'f' : + ip->ip_off = strtol(optarg, NULL, 0); + break; + case 'g' : + gateway = optarg; + break; + case 'm' : + mtu = atoi(optarg); + if (mtu < 28) + { + fprintf(stderr, "mtu must be > 28\n"); + exit(1); + } + break; + case 'o' : + olen = optname(optarg, options); + break; + case 'p' : + pointtest = atoi(optarg); + break; + case 'r' : + resend = optarg; + ipr = &pcap; + break; + case 's' : + src = optarg; + break; + case 't' : + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + tcp->th_dport = htons(atoi(optarg)); + break; + case 'w' : + if (ip->ip_p == IPPROTO_TCP) + tcp->th_win = atoi(optarg); + else + fprintf(stderr, "set protocol to TCP first\n"); + break; + default : + fprintf(stderr, "Unknown option \"%c\"\n", c); + usage(name); + } + + if (ipr) + { + if (!gateway) + usage(name); + dst = gateway; + } + else + { + if (argc - optind < 2 && !tests) + usage(name); + dst = argv[optind++]; + } + + if (!src) + { + gethostname(host, sizeof(host)); + src = host; + } + + if (resolve(src, (char *)&ip->ip_src) == -1) + { + fprintf(stderr,"Cant resolve %s\n", src); + exit(2); + } + + if (resolve(dst, (char *)&ip->ip_dst) == -1) + { + fprintf(stderr,"Cant resolve %s\n", dst); + exit(2); + } + + if (!gateway) + gwip = ip->ip_dst; + else if (resolve(gateway, (char *)&gwip) == -1) + { + fprintf(stderr,"Cant resolve %s\n", src); + exit(2); + } + + if (!ipr && ip->ip_p == IPPROTO_TCP) + for (s = argv[optind]; c = *s; s++) + switch(c) + { + case 'S' : case 's' : + tcp->th_flags |= TH_SYN; + break; + case 'A' : case 'a' : + tcp->th_flags |= TH_ACK; + break; + case 'F' : case 'f' : + tcp->th_flags |= TH_FIN; + break; + case 'R' : case 'r' : + tcp->th_flags |= TH_RST; + break; + case 'P' : case 'p' : + tcp->th_flags |= TH_PUSH; + break; + case 'U' : case 'u' : + tcp->th_flags |= TH_URG; + break; + } + + if (!dev) { +#if defined (__OpenBSD__) || defined (__NetBSD__) + char errbuf[160]; + if (! (dev = pcap_lookupdev(errbuf))) { + fprintf (stderr, "%s", errbuf); + dev = default_device; + } +#else + dev = default_device; +#endif + } + printf("Device: %s\n", dev); + printf("Source: %s\n", inet_ntoa(ip->ip_src)); + printf("Dest: %s\n", inet_ntoa(ip->ip_dst)); + printf("Gateway: %s\n", inet_ntoa(gwip)); + if (ip->ip_p == IPPROTO_TCP && tcp->th_flags) + printf("Flags: %#x\n", tcp->th_flags); + printf("mtu: %d\n", mtu); + + if (olen) + { + printf("Options: %d\n", olen); + ti = (struct tcpiphdr *)malloc(olen + ip->ip_len); + bcopy((char *)ip, (char *)ti, sizeof(*ip)); + ip = (ip_t *)ti; + ip->ip_hl += (olen >> 2); + bcopy(options, (char *)(ip + 1), olen); + bcopy((char *)tcp, (char *)(ip + 1) + olen, sizeof(*tcp)); + tcp = (tcphdr_t *)((char *)(ip + 1) + olen); + ip->ip_len += olen; + } + + switch (tests) + { + case 1 : + return ip_test1(dev, mtu, ti, gwip, pointtest); + case 2 : + return ip_test2(dev, mtu, ti, gwip, pointtest); + case 3 : + return ip_test3(dev, mtu, ti, gwip, pointtest); + case 4 : + return ip_test4(dev, mtu, ti, gwip, pointtest); + case 5 : + return ip_test5(dev, mtu, ti, gwip, pointtest); + default : + break; + } + + if (ipr) + return ip_resend(dev, mtu, ipr, gwip, resend); + +#ifdef DOSOCKET + if (tcp->th_dport) + return do_socket(dev, mtu, ti, gwip); +#endif + return send_packets(dev, mtu, ti, gwip); +} diff --git a/usr.sbin/ipsend/ipsopt.c b/usr.sbin/ipsend/ipsopt.c new file mode 100644 index 00000000000..8088bed3819 --- /dev/null +++ b/usr.sbin/ipsend/ipsopt.c @@ -0,0 +1,112 @@ +/* + * (C)opyright 1995 by Darren Reed. + * + * This code may be freely distributed as long as it retains this notice + * and is not changed in any way. The author accepts no responsibility + * for the use of this software. I hate legaleese, don't you ? + */ +#ifndef lint +static char sccsid[] = "@(#)opt.c 1.1 8/19/95 (C)1995 Darren Reed"; +#endif +#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 "ip_compat.h" + +struct ipopt_names { + int on_value; + int on_bit; + int on_siz; + char *on_name; +}; + +struct ipopt_names ionames[] = { + { IPOPT_EOL, 0x01, 1, "eol" }, + { IPOPT_NOP, 0x02, 1, "nop" }, + { IPOPT_RR, 0x04, 7, "rr" }, /* 1 route */ + { IPOPT_TS, 0x08, 8, "ts" }, /* 1 TS */ + { IPOPT_SECURITY, 0x08, 11, "sec-level" }, + { IPOPT_LSRR, 0x10, 7, "lsrr" }, /* 1 route */ + { IPOPT_SATID, 0x20, 4, "satid" }, + { IPOPT_SSRR, 0x40, 7, "ssrr" }, /* 1 route */ + { 0, NULL } /* must be last */ +}; + +struct ipopt_names secnames[] = { + { IPOPT_SECUR_UNCLASS, 0x0100, 0, "unclass" }, + { IPOPT_SECUR_CONFID, 0x0200, 0, "confid" }, + { IPOPT_SECUR_EFTO, 0x0400, 0, "efto" }, + { IPOPT_SECUR_MMMM, 0x0800, 0, "mmmm" }, + { IPOPT_SECUR_RESTR, 0x1000, 0, "restr" }, + { IPOPT_SECUR_SECRET, 0x2000, 0, "secret" }, + { IPOPT_SECUR_TOPSECRET, 0x4000,0, "topsecret" }, + { 0, 0, 0, NULL } /* must be last */ +}; + + +u_short seclevel(slevel) +char *slevel; +{ + struct ipopt_names *so; + u_short level = 0; + + for (so = secnames; 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 so->on_value; +} + + +u_long optname(cp, op) +char *cp, *op; +{ + struct ipopt_names *io, *so; + u_short lvl; + u_long msk = 0; + char *s, *sec, *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-level")) { + lvl = seclevel(t); + bcopy(&lvl, op, sizeof(lvl)); + } + op += io->on_siz - 3; + } + 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/usr.sbin/ipsend/iptests.c b/usr.sbin/ipsend/iptests.c new file mode 100644 index 00000000000..57752b306d8 --- /dev/null +++ b/usr.sbin/ipsend/iptests.c @@ -0,0 +1,1004 @@ +/* + * (C)opyright 1993, 1994, 1995 by Darren Reed. + * + * This code may be freely distributed as long as it retains this notice + * and is not changed in any way. The author accepts no responsibility + * for the use of this software. I hate legaleese, don't you ? + */ +#ifndef lint +static char sccsid[] = "%W% %G% (C)1995 Darren Reed"; +#endif +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <netinet/in_systm.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#include <netinet/if_ether.h> +#include <netinet/ip_var.h> +#include <netinet/tcpip.h> +#include "ip_compat.h" +#ifndef sun +#if !defined (__OpenBSD__) && !defined (__NetBSD__) +#include "tcpip.h" +#endif +#else +# if defined(__SVR4) || defined(__svr4__) +#include <sys/sysmacros.h> +# endif +#endif + +#define PAUSE() tv.tv_sec = 0; tv.tv_usec = 10000; \ + (void) select(0, NULL, NULL, NULL, &tv) + +void ip_test1(dev, mtu, ip, gwip, ptest) +char *dev; +int mtu; +ip_t *ip; +struct in_addr gwip; +int ptest; +{ + struct timeval tv; + udphdr_t *u; + int nfd, i, len, id = getpid(); + + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_v = IPVERSION; + ip->ip_tos = 0; + ip->ip_off = 0; + ip->ip_ttl = 60; + ip->ip_p = IPPROTO_UDP; + ip->ip_sum = 0; + u = (udphdr_t *)(ip + 1); + u->uh_sport = 1; + u->uh_dport = 9; + u->uh_sum = 0; + u->uh_ulen = sizeof(*u) + 4; + ip->ip_len = sizeof(*ip) + u->uh_ulen; + len = ip->ip_len; + nfd = initdevice(dev, u->uh_sport, 1); + + u->uh_sport = htons(u->uh_sport); + u->uh_dport = htons(u->uh_dport); + u->uh_ulen = htons(u->uh_ulen); + if (!ptest || (ptest == 1)) { + /* + * Part1: hl < len + */ + ip->ip_id = 0; + printf("1.1. sending packets with ip_hl < ip_len\n"); + for (i = 0; i < ((sizeof(*ip) + u->uh_ulen) >> 2); i++) { + ip->ip_hl = i >> 2; + (void) send_ip(nfd, 1500, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 2)) { + /* + * Part2: hl > len + */ + ip->ip_id = 0; + printf("1.2. sending packets with ip_hl > ip_len\n"); + for (; i < ((sizeof(*ip) * 2 + u->uh_ulen) >> 2); i++) { + ip->ip_hl = i >> 2; + (void) send_ip(nfd, 1500, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 3)) { + /* + * Part3: v < 4 + */ + ip->ip_id = 0; + printf("1.3. ip_v < 4\n"); + ip->ip_hl = sizeof(*ip) >> 2; + for (i = 0; i < 4; i++) { + ip->ip_v = i; + (void) send_ip(nfd, 1500, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 4)) { + /* + * Part4: v > 4 + */ + ip->ip_id = 0; + printf("1.4. ip_v > 4\n"); + for (i = 5; i < 16; i++) { + ip->ip_v = i; + (void) send_ip(nfd, 1500, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 5)) { + /* + * Part5: len < packet + */ + ip->ip_id = 0; + ip->ip_v = IPVERSION; + i = ip->ip_len + 1; + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + printf("1.5.0 ip_len < packet size (size++, long packets)\n"); + for (; i < (ip->ip_len * 2); i++) { + ip->ip_id = htons(id++); + ip->ip_sum = 0; + ip->ip_sum = chksum(ip, ip->ip_hl << 2); + (void) send_ether(nfd, ip, i, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + printf("1.5.1 ip_len < packet size (ip_len-, short packets)\n"); + for (i = len; i > 0; i--) { + ip->ip_id = htons(id++); + ip->ip_len = htons(i); + ip->ip_sum = 0; + ip->ip_sum = chksum(ip, ip->ip_hl << 2); + (void) send_ether(nfd, ip, len, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 6)) { + /* + * Part6: len > packet + */ + ip->ip_id = 0; + printf("1.6.0 ip_len > packet size (increase ip_len)\n"); + for (i = len + 1; i < (len * 2); i++) { + ip->ip_id = htons(id++); + ip->ip_len = htons(i); + ip->ip_sum = 0; + ip->ip_sum = chksum(ip, ip->ip_hl << 2); + (void) send_ether(nfd, ip, len, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + ip->ip_len = htons(len); + printf("1.6.1 ip_len > packet size (size--, short packets)\n"); + for (i = len; i > 0; i--) { + ip->ip_id = htons(id++); + ip->ip_sum = 0; + ip->ip_sum = chksum(ip, ip->ip_hl << 2); + (void) send_ether(nfd, ip, i, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 7)) { + /* + * Part7: 0 length fragment + */ + printf("1.7.0 Zero length fragments (ip_off = 0x2000)\n"); + ip->ip_id = 0; + ip->ip_len = sizeof(*ip); + ip->ip_off = htons(IP_MF); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("1.7.1 Zero length fragments (ip_off = 0x3000)\n"); + ip->ip_id = 0; + ip->ip_len = sizeof(*ip); + ip->ip_off = htons(IP_MF); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("1.7.2 Zero length fragments (ip_off = 0xa000)\n"); + ip->ip_id = 0; + ip->ip_len = sizeof(*ip); + ip->ip_off = htons(0xa000); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("1.7.3 Zero length fragments (ip_off = 0x0100)\n"); + ip->ip_id = 0; + ip->ip_len = sizeof(*ip); + ip->ip_off = htons(0x0100); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 8)) { + struct timeval tv; + + gettimeofday(&tv, NULL); + srand(tv.tv_sec ^ getpid() ^ tv.tv_usec); + /* + * Part8: 63k packet + 1k fragment at offset 0x1ffe + */ + ip->ip_off = IP_MF; + u->uh_dport = htons(9); + ip->ip_id = htons(id++); + printf("1.8. 63k packet + 1k fragment at offset 0x1ffe\n"); + ip->ip_len = 768 + 20 + 8; + if ((rand() & 0x1f) != 0) { + (void) send_ip(nfd, mtu, ip, gwip); + printf("%d\r", i); + } else + printf("skip 0\n"); + + ip->ip_len = MIN(768 + 20, mtu - 68); + i = 512; + for (; i < (63 * 1024 + 768); i += 768) { + ip->ip_off = IP_MF | (i >> 3); + if ((rand() & 0x1f) != 0) { + (void) send_ip(nfd, mtu, ip, gwip); + printf("%d\r", i); + } else + printf("skip %d\n", i); + fflush(stdout); + PAUSE(); + } + ip->ip_len = 896 + 20; + ip->ip_off = IP_MF | (i >> 3); + if ((rand() & 0x1f) != 0) { + (void) send_ip(nfd, mtu, ip, gwip); + printf("%d\r", i); + } else + printf("skip\n"); + putchar('\n'); + fflush(stdout); + } + + ip->ip_len = len; + ip->ip_off = 0; + if (!ptest || (ptest == 9)) { + /* + * Part9: off & 0x8000 == 0x8000 + */ + ip->ip_id = 0; + ip->ip_off = 0x8000; + printf("1.9. ip_off & 0x8000 == 0x8000\n"); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + ip->ip_off = 0; + + if (!ptest || (ptest == 10)) { + /* + * Part10: ttl = 255 + */ + ip->ip_id = 0; + ip->ip_ttl = 255; + printf("1.10.0 ip_ttl = 255\n"); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + ip->ip_ttl = 128; + printf("1.10.1 ip_ttl = 128\n"); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + ip->ip_ttl = 0; + printf("1.10.2 ip_ttl = 0\n"); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + (void) close(nfd); +} + + +void ip_test2(dev, mtu, ip, gwip, ptest) +char *dev; +int mtu; +ip_t *ip; +struct in_addr gwip; +int ptest; +{ + struct timeval tv; + udphdr_t *u; + int nfd, i, len, id = getpid(); + u_char *s; + + s = (u_char *)(ip + 1); + nfd = initdevice(dev, 1, 1); + + ip->ip_hl = 6; + ip->ip_len = ip->ip_hl << 2; + s[IPOPT_OPTVAL] = IPOPT_NOP; + s++; + if (!ptest || (ptest == 1)) { + /* + * Test 1: option length > packet length, + * header length == packet length + */ + s[IPOPT_OPTVAL] = IPOPT_TS; + s[IPOPT_OLEN] = 4; + s[IPOPT_OFFSET] = IPOPT_MINOFF; + ip->ip_p = IPPROTO_IP; + printf("2.1 option length > packet length\n"); + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + ip->ip_hl = 7; + ip->ip_len = ip->ip_hl << 2; + if (!ptest || (ptest == 1)) { + /* + * Test 2: options have length = 0 + */ + printf("2.2.1 option length = 0, RR\n"); + s[IPOPT_OPTVAL] = IPOPT_RR; + s[IPOPT_OLEN] = 0; + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("2.2.2 option length = 0, TS\n"); + s[IPOPT_OPTVAL] = IPOPT_TS; + s[IPOPT_OLEN] = 0; + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("2.2.3 option length = 0, SECURITY\n"); + s[IPOPT_OPTVAL] = IPOPT_SECURITY; + s[IPOPT_OLEN] = 0; + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("2.2.4 option length = 0, LSRR\n"); + s[IPOPT_OPTVAL] = IPOPT_LSRR; + s[IPOPT_OLEN] = 0; + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("2.2.5 option length = 0, SATID\n"); + s[IPOPT_OPTVAL] = IPOPT_SATID; + s[IPOPT_OLEN] = 0; + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("2.2.6 option length = 0, SSRR\n"); + s[IPOPT_OPTVAL] = IPOPT_SSRR; + s[IPOPT_OLEN] = 0; + (void) send_ip(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + (void) close(nfd); +} + + +/* + * test 3 (ICMP) + */ +void ip_test3(dev, mtu, ip, gwip, ptest) +char *dev; +int mtu; +ip_t *ip; +struct in_addr gwip; +int ptest; +{ + static int ict1[10] = { 8, 9, 10, 13, 14, 15, 16, 17, 18, 0 }; + static int ict2[8] = { 3, 9, 10, 13, 14, 17, 18, 0 }; + struct timeval tv; + struct icmp *icp; + int nfd, i, len, id = getpid(); + u_char *s; + + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_v = IPVERSION; + ip->ip_tos = 0; + ip->ip_off = 0; + ip->ip_ttl = 60; + ip->ip_p = IPPROTO_ICMP; + ip->ip_sum = 0; + ip->ip_len = sizeof(*ip) + sizeof(*icp); + icp = (struct icmp *)((char *)ip + (ip->ip_hl << 2)); + nfd = initdevice(dev, 1, 1); + + if (!ptest || (ptest == 1)) { + /* + * Type 0 - 31, 255, code = 0 + */ + bzero((char *)icp, sizeof(*icp)); + for (i = 0; i < 32; i++) { + icp->icmp_type = i; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.1.%d ICMP type %d code 0 (all 0's)\r", i, i); + } + icp->icmp_type = 255; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.1.%d ICMP type %d code 0 (all 0's)\r", i, 255); + putchar('\n'); + } + + if (!ptest || (ptest == 2)) { + /* + * Type 3, code = 0 - 31 + */ + icp->icmp_type = 3; + for (i = 0; i < 32; i++) { + icp->icmp_code = i; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.2.%d ICMP type 3 code %d (all 0's)\r", i, i); + } + } + + if (!ptest || (ptest == 3)) { + /* + * Type 4, code = 0,127,128,255 + */ + icp->icmp_type = 4; + icp->icmp_code = 0; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.3.1 ICMP type 4 code 0 (all 0's)\r"); + icp->icmp_code = 127; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.3.2 ICMP type 4 code 127 (all 0's)\r"); + icp->icmp_code = 128; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.3.3 ICMP type 4 code 128 (all 0's)\r"); + icp->icmp_code = 255; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.3.4 ICMP type 4 code 255 (all 0's)\r"); + } + + if (!ptest || (ptest == 4)) { + /* + * Type 5, code = 0,127,128,255 + */ + icp->icmp_type = 5; + icp->icmp_code = 0; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.4.1 ICMP type 5 code 0 (all 0's)\r"); + icp->icmp_code = 127; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.4.2 ICMP type 5 code 127 (all 0's)\r"); + icp->icmp_code = 128; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.4.3 ICMP type 5 code 128 (all 0's)\r"); + icp->icmp_code = 255; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.4.4 ICMP type 5 code 255 (all 0's)\r"); + } + + if (!ptest || (ptest == 5)) { + /* + * Type 8-10;13-18, code - 0,127,128,255 + */ + for (i = 0; ict1[i]; i++) { + icp->icmp_type = ict1[i]; + icp->icmp_code = 0; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.5.%d ICMP type 5 code 0 (all 0's)\r", + i * 4); + icp->icmp_code = 127; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.5.%d ICMP type 5 code 127 (all 0's)\r", + i * 4 + 1); + icp->icmp_code = 128; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.5.%d ICMP type 5 code 128 (all 0's)\r", + i * 4 + 2); + icp->icmp_code = 255; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.5.%d ICMP type 5 code 255 (all 0's)\r", + i * 4 + 3); + } + putchar('\n'); + } + + if (!ptest || (ptest == 6)) { + /* + * Type 12, code - 0,127,128,129,255 + */ + icp->icmp_type = 12; + icp->icmp_code = 0; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.6.1 ICMP type 12 code 0 (all 0's)\r"); + icp->icmp_code = 127; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.6.2 ICMP type 12 code 127 (all 0's)\r"); + icp->icmp_code = 128; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.6.3 ICMP type 12 code 128 (all 0's)\r"); + icp->icmp_code = 129; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.6.4 ICMP type 12 code 129 (all 0's)\r"); + icp->icmp_code = 255; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.6.5 ICMP type 12 code 255 (all 0's)\r"); + putchar('\n'); + } + + if (!ptest || (ptest == 7)) { + /* + * Type 3;9-10;13-14;17-18 - shorter packets + */ + ip->ip_len = sizeof(*ip) + sizeof(*icp) / 2; + for (i = 0; ict2[i]; i++) { + icp->icmp_type = ict1[i]; + icp->icmp_code = 0; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.5.%d ICMP type %d code 0 (all 0's)\r", + i * 4, icp->icmp_type); + icp->icmp_code = 127; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.5.%d ICMP type %d code 127 (all 0's)\r", + i * 4 + 1, icp->icmp_type); + icp->icmp_code = 128; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.5.%d ICMP type %d code 128 (all 0's)\r", + i * 4 + 2, icp->icmp_type); + icp->icmp_code = 255; + (void) send_icmp(nfd, mtu, ip, gwip); + PAUSE(); + printf("3.5.%d ICMP type %d code 127 (all 0's)\r", + i * 4 + 3, icp->icmp_type); + } + putchar('\n'); + } +} + + +/* Perform test 4 (UDP) */ + +void ip_test4(dev, mtu, ip, gwip, ptest) +char *dev; +int mtu; +ip_t *ip; +struct in_addr gwip; +int ptest; +{ + struct timeval tv; + struct udphdr *u; + int nfd, i, len, id = getpid(); + + + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_v = IPVERSION; + ip->ip_tos = 0; + ip->ip_off = 0; + ip->ip_ttl = 60; + ip->ip_p = IPPROTO_UDP; + ip->ip_sum = 0; + u = (udphdr_t *)((char *)ip + (ip->ip_hl << 2)); + u->uh_sport = 1; + u->uh_dport = 1; + u->uh_ulen = sizeof(*u) + 4; + nfd = initdevice(dev, u->uh_sport, 1); + + if (!ptest || (ptest == 1)) { + /* + * Test 1. ulen > packet + */ + u->uh_ulen = sizeof(*u) + 4; + ip->ip_len = (ip->ip_hl << 2) + u->uh_ulen; + printf("4.1 UDP uh_ulen > packet size - short packets\n"); + for (i = u->uh_ulen * 2; i > sizeof(*u) + 4; i--) { + u->uh_ulen = i; + (void) send_udp(nfd, 1500, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 2)) { + /* + * Test 2. ulen < packet + */ + u->uh_ulen = sizeof(*u) + 4; + ip->ip_len = (ip->ip_hl << 2) + u->uh_ulen; + printf("4.2 UDP uh_ulen < packet size - short packets\n"); + for (i = u->uh_ulen * 2; i > sizeof(*u) + 4; i--) { + ip->ip_len = i; + (void) send_udp(nfd, 1500, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 3)) { + /* + * Test 3: sport = 0, sport = 1, sport = 32767 + * sport = 32768, sport = 65535 + */ + u->uh_ulen = sizeof(*u) + 4; + ip->ip_len = (ip->ip_hl << 2) + u->uh_ulen; + printf("4.3.1 UDP sport = 0\n"); + u->uh_sport = 0; + (void) send_udp(nfd, 1500, ip, gwip); + printf("0\n"); + fflush(stdout); + PAUSE(); + printf("4.3.2 UDP sport = 1\n"); + u->uh_sport = 1; + (void) send_udp(nfd, 1500, ip, gwip); + printf("1\n"); + fflush(stdout); + PAUSE(); + printf("4.3.3 UDP sport = 32767\n"); + u->uh_sport = 32767; + (void) send_udp(nfd, 1500, ip, gwip); + printf("32767\n"); + fflush(stdout); + PAUSE(); + printf("4.3.4 UDP sport = 32768\n"); + u->uh_sport = 32768; + (void) send_udp(nfd, 1500, ip, gwip); + printf("32768\n"); + putchar('\n'); + fflush(stdout); + PAUSE(); + printf("4.3.5 UDP sport = 65535\n"); + u->uh_sport = 65535; + (void) send_udp(nfd, 1500, ip, gwip); + printf("65535\n"); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 4)) { + /* + * Test 4: dport = 0, dport = 1, dport = 32767 + * dport = 32768, dport = 65535 + */ + u->uh_ulen = sizeof(*u) + 4; + u->uh_sport = 1; + ip->ip_len = (ip->ip_hl << 2) + u->uh_ulen; + printf("4.4.1 UDP dport = 0\n"); + u->uh_dport = 0; + (void) send_udp(nfd, 1500, ip, gwip); + printf("0\n"); + fflush(stdout); + PAUSE(); + printf("4.4.2 UDP dport = 1\n"); + u->uh_dport = 1; + (void) send_udp(nfd, 1500, ip, gwip); + printf("1\n"); + fflush(stdout); + PAUSE(); + printf("4.4.3 UDP dport = 32767\n"); + u->uh_dport = 32767; + (void) send_udp(nfd, 1500, ip, gwip); + printf("32767\n"); + fflush(stdout); + PAUSE(); + printf("4.4.4 UDP dport = 32768\n"); + u->uh_dport = 32768; + (void) send_udp(nfd, 1500, ip, gwip); + printf("32768\n"); + fflush(stdout); + PAUSE(); + printf("4.4.5 UDP dport = 65535\n"); + u->uh_dport = 65535; + (void) send_udp(nfd, 1500, ip, gwip); + printf("65535\n"); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 4)) { + /* + * Test 5: sizeof(struct ip) <= MTU <= sizeof(struct udphdr) + + * sizeof(struct ip) + */ + printf("4.5 UDP 20 <= MTU <= 32\n"); + for (i = sizeof(*ip); i <= u->uh_ulen; i++) { + (void) send_udp(nfd, i, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } +} + + +/* Perform test 5 (TCP) */ + +void ip_test5(dev, mtu, ip, gwip, ptest) +char *dev; +int mtu; +ip_t *ip; +struct in_addr gwip; +int ptest; +{ + struct timeval tv; + tcphdr_t *t; + int nfd, i, len, id = getpid(); + + t = (tcphdr_t *)((char *)ip + (ip->ip_hl << 2)); + t->th_x2 = 0; + t->th_off = 0; + t->th_sport = 1; + t->th_dport = 1; + t->th_win = 4096; + t->th_urp = 0; + t->th_sum = 0; + t->th_seq = 1; + t->th_ack = 0; + nfd = initdevice(dev, t->th_sport, 1); + + if (!ptest || (ptest == 1)) { + /* + * Test 1: flags variations, 0 - 3f + */ + t->th_off = sizeof(*t) >> 2; + printf("5.1 Test TCP flag combinations\n"); + for (i = 0; i <= (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN); + i++) { + t->th_flags = i; + (void) send_tcp(nfd, mtu, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 2)) { + t->th_flags = TH_SYN; + /* + * Test 2: seq = 0, seq = 1, seq = 0x7fffffff, seq=0x80000000, + * seq = 0xa000000, seq = 0xffffffff + */ + printf("5.2.1 TCP seq = 0\n"); + t->th_seq = 0; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.2 TCP seq = 1\n"); + t->th_seq = 1; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.3 TCP seq = 0x7fffffff\n"); + t->th_seq = 0x7fffffff; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.4 TCP seq = 0x80000000\n"); + t->th_seq = 0x80000000; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.5 TCP seq = 0xc0000000\n"); + t->th_seq = 0xc0000000; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.6 TCP seq = 0xffffffff\n"); + t->th_seq = 0xffffffff; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 3)) { + t->th_flags = TH_ACK; + /* + * Test 3: ack = 0, ack = 1, ack = 0x7fffffff, ack = 0x8000000 + * ack = 0xa000000, ack = 0xffffffff + */ + printf("5.3.1 TCP ack = 0\n"); + t->th_ack = 0; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.2 TCP ack = 1\n"); + t->th_ack = 1; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.3 TCP ack = 0x7fffffff\n"); + t->th_ack = 0x7fffffff; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.4 TCP ack = 0x80000000\n"); + t->th_ack = 0x80000000; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.5 TCP ack = 0xc0000000\n"); + t->th_ack = 0xc0000000; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.6 TCP ack = 0xffffffff\n"); + t->th_ack = 0xffffffff; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 4)) { + t->th_flags = TH_SYN; + /* + * Test 4: win = 0, win = 32768, win = 65535 + */ + printf("5.4.1 TCP win = 0\n"); + t->th_seq = 0; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.4.2 TCP win = 32768\n"); + t->th_seq = 0x7fff; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.4.3 TCP win = 65535\n"); + t->th_win = 0xffff; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 5)) { + t->th_win = 4096; + /* + * Test 5: urp, urp = 0, urp > packetlen, urp < header + */ + } + + if (!ptest || (ptest == 6)) { + /* + * Test 6: data offset, off = 0, off is inside, off is outside + */ + ; + } + + if (!ptest || (ptest == 7)) { + t->th_off = 0; + t->th_flags = TH_SYN; + /* + * Test 7: sport = 0, sport = 1, sport = 32767 + * sport = 32768, sport = 65535 + */ + printf("5.7.1 TCP sport = 0\n"); + t->th_sport = 0; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.7.2 TCP sport = 1\n"); + t->th_sport = 1; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.7.3 TCP sport = 32767\n"); + t->th_sport = 32767; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.7.4 TCP sport = 32768\n"); + t->th_sport = 32768; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.7.5 TCP sport = 65535\n"); + t->th_sport = 65535; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 8)) { + t->th_sport = 1; + /* + * Test 8: dport = 0, dport = 1, dport = 32767 + * dport = 32768, dport = 65535 + */ + printf("5.8.1 TCP dport = 0\n"); + t->th_dport = 0; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.8.2 TCP dport = 1\n"); + t->th_dport = 1; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.8.3 TCP dport = 32767\n"); + t->th_dport = 32767; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.8.4 TCP dport = 32768\n"); + t->th_dport = 32768; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.8.5 TCP dport = 65535\n"); + t->th_dport = 65535; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } +} diff --git a/usr.sbin/ipsend/resend.c b/usr.sbin/ipsend/resend.c new file mode 100644 index 00000000000..c89db44ac84 --- /dev/null +++ b/usr.sbin/ipsend/resend.c @@ -0,0 +1,117 @@ +/* + * resend.c (C) 1995 Darren Reed + * + * This was written to test what size TCP fragments would get through + * various TCP/IP packet filters, as used in IP firewalls. In certain + * conditions, enough of the TCP header is missing for unpredictable + * results unless the filter is aware that this can happen. + * + */ +#ifndef lint +static char sccsid[] = "@(#)resend.c 1.2 8/25/95 (C)1995 Darren Reed"; +#endif +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#ifndef linux +#include <netinet/ip_var.h> +#include <netinet/tcpip.h> +#include <netinet/if_ether.h> +#endif +#include "ip_compat.h" +#ifdef linux +#include <linux/sockios.h> +#include "tcpip.h" +#endif +#include "ipt.h" + + +static u_char buf[65536]; /* 1 big packet */ + + +printpacket(ip) +ip_t *ip; +{ + tcphdr_t *t; + int i, j; + + t = (tcphdr_t *)((char *)ip + (ip->ip_hl << 2)); + if (ip->ip_tos) + printf("tos %#x ", ip->ip_tos); + if (ip->ip_off & 0x3fff) + printf("frag @%#x ", (ip->ip_off & 0x1fff) << 3); + printf("len %d id %d ", ip->ip_len, ip->ip_id); + printf("ttl %d p %d src %s", ip->ip_ttl, ip->ip_p, + inet_ntoa(ip->ip_src)); + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + printf(",%d", t->th_sport); + printf(" dst %s", inet_ntoa(ip->ip_dst)); + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + printf(",%d", t->th_sport); + if (ip->ip_p == IPPROTO_TCP) { + printf(" seq %u:%u flags ", t->th_seq, t->th_ack); + for (j = 0, i = 1; i < 256; i *= 2, j++) + if (t->th_flags & i) + printf("%c", "FSRPAU--"[j]); + } + putchar('\n'); +} + + +int ip_resend(dev, mtu, r, gwip, datain) +char *dev; +struct in_addr gwip; +struct ipread *r; +char *datain; +{ + ether_header_t *eh; + char dhost[6]; + ip_t *ip; + int fd, wfd = initdevice(dev, 0, 5), len, i; + + if (datain) + fd = (*r->r_open)(datain); + else + fd = (*r->r_open)("-"); + + if (fd < 0) + exit(-1); + + ip = (struct ip *)buf; + eh = (ether_header_t *)malloc(sizeof(*eh)); + + bzero(&eh->ether_shost, sizeof(eh->ether_shost)); + if (arp((char *)&gwip, dhost) == -1) + { + perror("arp"); + return -2; + } + + while ((i = (*r->r_readip)(buf, sizeof(buf), NULL, NULL)) > 0) + { + len = ntohs(ip->ip_len); + eh = (ether_header_t *)realloc((char *)eh, sizeof(*eh) + len); + eh->ether_type = htons((u_short)ETHERTYPE_IP); + bcopy(dhost, (char *)&eh->ether_dhost, sizeof(dhost)); + bcopy(ip, (char *)(eh + 1), len); + printpacket(ip); + + if (sendip(wfd, eh, sizeof(*eh) + len) == -1) + { + perror("send_packet"); + break; + } + } + (*r->r_close)(); + return 0; +} diff --git a/usr.sbin/ipsend/sbpf.c b/usr.sbin/ipsend/sbpf.c new file mode 100644 index 00000000000..b6a6d36f6c1 --- /dev/null +++ b/usr.sbin/ipsend/sbpf.c @@ -0,0 +1,134 @@ +/* + * (C)opyright October 1995 Darren Reed. (from tcplog) + * + * This software may be freely distributed as long as it is not altered + * in any way and that this messagge always accompanies it. + * + */ +#include <stdio.h> +#include <netdb.h> +#include <ctype.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/time.h> +#include <sys/timeb.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#if BSD < 199103 +#include <sys/fcntlcom.h> +#endif +#include <sys/dir.h> +#include <net/bpf.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <netinet/ip_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> + +#ifndef lint +static char sbpf[] = "@(#)sbpf.c 1.3 8/25/95 (C)1995 Darren Reed"; +#endif + +/* + * the code herein is dervied from libpcap. + */ +static u_char *buf = NULL; +static int bufsize = 0, timeout = 1; + + +int initdevice(device, sport, tout) +char *device; +int sport, tout; +{ + struct bpf_program prog; + struct bpf_version bv; + struct timeval to; + struct ifreq ifr; + char bpfname[16]; + int fd, i; + + for (i = 0; i < 16; i++) + { + (void) sprintf(bpfname, "/dev/bpf%d", i); + if ((fd = open(bpfname, O_RDWR)) >= 0) + break; + } + if (i == 16) + { + fprintf(stderr, "no bpf devices available as /dev/bpfxx\n"); + return -1; + } + + if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) + { + perror("BIOCVERSION"); + return -1; + } + if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) + { + fprintf(stderr, "kernel bpf (v%d.%d) filter out of date:\n", + bv.bv_major, bv.bv_minor); + fprintf(stderr, "current version: %d.%d\n", + BPF_MAJOR_VERSION, BPF_MINOR_VERSION); + return -1; + } + + (void) strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, &ifr) == -1) + { + fprintf(stderr, "%s(%d):", ifr.ifr_name, fd); + perror("BIOCSETIF"); + exit(1); + } + /* + * get kernel buffer size + */ + if (ioctl(fd, BIOCGBLEN, &bufsize) == -1) + { + perror("BIOCSBLEN"); + exit(-1); + } + buf = (u_char*)malloc(bufsize); + /* + * set the timeout + */ + timeout = tout; + to.tv_sec = 1; + to.tv_usec = 0; + if (ioctl(fd, BIOCSRTIMEOUT, (caddr_t)&to) == -1) + { + perror("BIOCSRTIMEOUT"); + exit(-1); + } + + (void) ioctl(fd, BIOCFLUSH, 0); + return fd; +} + + +/* + * output an IP packet onto a fd opened for /dev/bpf + */ +int sendip(fd, pkt, len) +int fd, len; +char *pkt; +{ + if (write(fd, pkt, len) == -1) + { + perror("send"); + return -1; + } + + return len; +} diff --git a/usr.sbin/ipsend/sock.c b/usr.sbin/ipsend/sock.c new file mode 100644 index 00000000000..a35ffc64d48 --- /dev/null +++ b/usr.sbin/ipsend/sock.c @@ -0,0 +1,367 @@ +/* + * sock.c (C) 1995 Darren Reed + * + * The author provides this program as-is, with no gaurantee for its + * suitability for any specific purpose. The author takes no responsibility + * for the misuse/abuse of this program and provides it for the sole purpose + * of testing packet filter policies. This file maybe distributed freely + * providing it is not modified and that this notice remains in tact. + */ +#ifndef lint +static char sccsid[] = "@(#)sock.c 1.1 8/19/95 (C)1995 Darren Reed"; +#endif +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/dir.h> +#define _KERNEL +#define KERNEL +#include <sys/file.h> +#undef _KERNEL +#undef KERNEL +#include <nlist.h> +#include <sys/user.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/proc.h> +#include <kvm.h> +#ifdef sun +#include <sys/systm.h> +#include <sys/session.h> +#endif +#if BSD >= 199103 +#include <sys/sysctl.h> +#include <sys/filedesc.h> +#include <paths.h> +#endif +#include <math.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <net/if.h> +#include <net/route.h> +#include <netinet/ip_var.h> +#include <netinet/in_pcb.h> +#include <netinet/tcp_timer.h> +#include <netinet/tcp_var.h> +#include <netinet/tcpip.h> + +int nproc; +struct proc *proc; + +#ifndef KMEM +# ifdef _PATH_KMEM +# define KMEM _PATH_KMEM +# endif +#endif +#ifndef KERNEL +# ifdef _PATH_UNIX +# define KERNEL _PATH_UNIX +# endif +#endif +#ifndef KMEM +# define KMEM "/dev/kmem" +#endif +#ifndef KERNEL +# define KERNEL "/vmunix" +#endif + +int kmemcpy(buf, pos, n) +char *buf; +off_t pos; +int n; +{ + static int kfd = -1; + + if (kfd == -1) + kfd = open(KMEM, O_RDONLY); + + if (lseek(kfd, pos, SEEK_SET) == -1) + { + perror("lseek"); + return -1; + } + if (read(kfd, buf, n) == -1) + { + perror("read"); + return -1; + } + return n; +} + +struct nlist names[3] = { + { "_proc" }, + { "_nproc" }, + { NULL } + }; + +#if BSD < 199103 +struct proc *getproc() +{ + struct proc *p; + pid_t pid = getpid(); + int siz, n; + + n = nlist(KERNEL, names); + if (n != 0) + { + fprintf(stderr, "nlist(%#x) == %d\n", names, n); + return NULL; + } + if (kmemcpy((char *)&nproc, (off_t)names[1].n_value, + sizeof(nproc)) == -1) + { + fprintf(stderr, "read nproc (%#x)\n", names[1].n_value); + return NULL; + } + siz = nproc * sizeof(struct proc); + if (kmemcpy((char *)&p, (off_t)names[0].n_value, sizeof(p)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) proc\n", + names[0].n_value, &p, sizeof(p)); + return NULL; + } + proc = (struct proc *)malloc(siz); + if (kmemcpy((char *)proc, (off_t)p, siz) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) proc\n", + p, proc, siz); + return NULL; + } + + p = proc; + + for (n = nproc; n; n--, p++) + if (p->p_pid == pid) + break; + if (!n) + return NULL; + + return p; +} + + +struct tcpcb *find_tcp(fd, ti) +int fd; +struct tcpiphdr *ti; +{ + struct tcpcb *t; + struct inpcb *i; + struct socket *s; + struct user *up; + struct proc *p; + struct file *f, **o; + + if (!(p = getproc())) + return NULL; + + up = (struct user *)malloc(sizeof(*up)); + if (kmemcpy((char *)up, (off_t)p->p_uarea, sizeof(*up)) == -1) + { + fprintf(stderr, "read(%#x,%#x) failed\n", p, p->p_uarea); + return NULL; + } + + o = (struct file **)calloc(1, sizeof(*o) * (up->u_lastfile + 1)); + if (kmemcpy((char *)o, (off_t)up->u_ofile, + (up->u_lastfile + 1) * sizeof(*o)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) - u_ofile - failed\n", + up->u_ofile_arr, o, sizeof(*o)); + return NULL; + } + f = (struct file *)calloc(1, sizeof(*f)); + if (kmemcpy((char *)f, (off_t)o[fd], sizeof(*f)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) - o[fd] - failed\n", + up->u_ofile_arr[fd], f, sizeof(*f)); + return NULL; + } + + s = (struct socket *)calloc(1, sizeof(*s)); + if (kmemcpy((char *)s, (off_t)f->f_data, sizeof(*s)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) - f_data - failed\n", + o[fd], s, sizeof(*s)); + return NULL; + } + + i = (struct inpcb *)calloc(1, sizeof(*i)); + if (kmemcpy((char *)i, (off_t)s->so_pcb, sizeof(*i)) == -1) + { + fprintf(stderr, "kvm_read(%#x,%#x,%d) - so_pcb - failed\n", + s->so_pcb, i, sizeof(*i)); + return NULL; + } + + t = (struct tcpcb *)calloc(1, sizeof(*t)); + if (kmemcpy((char *)t, (off_t)i->inp_ppcb, sizeof(*t)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) - inp_ppcb - failed\n", + i->inp_ppcb, t, sizeof(*t)); + return NULL; + } + return (struct tcpcb *)i->inp_ppcb; +} +#else +struct kinfo_proc *getproc() +{ + static struct kinfo_proc kp; + pid_t pid = getpid(); + int siz, n, mib[4]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + n = 1; + if (sysctl(mib, 4, &kp, &n, NULL, 0) == -1) + { + perror("sysctl"); + return NULL; + } + return &kp; +} + + +struct tcpcb *find_tcp(tfd, ti) +int tfd; +struct tcpiphdr *ti; +{ + struct tcpcb *t; + struct inpcb *i; + struct socket *s; + struct filedesc *fd; + struct kinfo_proc *p; + struct file *f, **o; + + if (!(p = getproc())) + return NULL; + + fd = (struct filedesc *)malloc(sizeof(*fd)); + if (kmemcpy((char *)fd, (off_t)p->kp_proc.p_fd, sizeof(*fd)) == -1) + { + fprintf(stderr, "read(%#x,%#x) failed\n", p, p->kp_proc.p_fd); + return NULL; + } + + o = (struct file **)calloc(1, sizeof(*o) * (fd->fd_lastfile + 1)); + if (kmemcpy((char *)o, (off_t)fd->fd_ofiles, + (fd->fd_lastfile + 1) * sizeof(*o)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) - u_ofile - failed\n", + fd->fd_ofiles, o, sizeof(*o)); + return NULL; + } + f = (struct file *)calloc(1, sizeof(*f)); + if (kmemcpy((char *)f, (off_t)o[tfd], sizeof(*f)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) - o[tfd] - failed\n", + o[tfd], f, sizeof(*f)); + return NULL; + } + + s = (struct socket *)calloc(1, sizeof(*s)); + if (kmemcpy((char *)s, (off_t)f->f_data, sizeof(*s)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) - f_data - failed\n", + f->f_data, s, sizeof(*s)); + return NULL; + } + + i = (struct inpcb *)calloc(1, sizeof(*i)); + if (kmemcpy((char *)i, (off_t)s->so_pcb, sizeof(*i)) == -1) + { + fprintf(stderr, "kvm_read(%#x,%#x,%d) - so_pcb - failed\n", + s->so_pcb, i, sizeof(*i)); + return NULL; + } + + t = (struct tcpcb *)calloc(1, sizeof(*t)); + if (kmemcpy((char *)t, (off_t)i->inp_ppcb, sizeof(*t)) == -1) + { + fprintf(stderr, "read(%#x,%#x,%d) - inp_ppcb - failed\n", + i->inp_ppcb, t, sizeof(*t)); + return NULL; + } + return (struct tcpcb *)i->inp_ppcb; +} +#endif /* BSD < 199301 */ + +int do_socket(dev, mtu, ti, gwip, flags) +char *dev; +int mtu; +struct tcpiphdr *ti; +struct in_addr gwip; +int flags; +{ + struct sockaddr_in rsin, lsin; + struct tcpcb *t, tcb; + int fd, nfd, len; + + printf("Dest. Port: %d\n", ti->ti_dport); + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) + { + perror("socket"); + return -1; + } + + if (fcntl(fd, F_SETFL, FNDELAY) == -1) + { + perror("fcntl"); + return -1; + } + + bzero((char *)&lsin, sizeof(lsin)); + lsin.sin_family = AF_INET; + bcopy((char *)&ti->ti_src, (char *)&lsin.sin_addr, + sizeof(struct in_addr)); + if (bind(fd, (struct sockaddr *)&lsin, sizeof(lsin)) == -1) + { + perror("bind"); + return -1; + } + len = sizeof(lsin); + (void) getsockname(fd, (struct sockaddr *)&lsin, &len); + ti->ti_sport = lsin.sin_port; + printf("sport %d\n", ntohs(lsin.sin_port)); + nfd = initdevice(dev, ntohs(lsin.sin_port)); + + if (!(t = find_tcp(fd, ti))) + return -1; + + bzero((char *)&rsin, sizeof(rsin)); + rsin.sin_family = AF_INET; + bcopy((char *)&ti->ti_dst, (char *)&rsin.sin_addr, + sizeof(struct in_addr)); + rsin.sin_port = ti->ti_dport; + if (connect(fd, (struct sockaddr *)&rsin, sizeof(rsin)) == -1 && + errno != EINPROGRESS) + { + perror("connect"); + return -1; + } + kmemcpy((char*)&tcb, (off_t)t, sizeof(tcb)); + ti->ti_win = tcb.rcv_adv; + ti->ti_seq = tcb.snd_nxt - 1; + ti->ti_ack = tcb.rcv_nxt; + + if (send_tcp(nfd, mtu, ti, gwip, TH_SYN) == -1) + return -1; + (void)write(fd, "Hello World\n", 12); + sleep(2); + close(fd); + return 0; +} diff --git a/usr.sbin/ipsend/tcpip.h b/usr.sbin/ipsend/tcpip.h new file mode 100644 index 00000000000..78f274f0066 --- /dev/null +++ b/usr.sbin/ipsend/tcpip.h @@ -0,0 +1,38 @@ +/* @(#)tcpip.h 1.7 88/08/19 SMI; from UCB 7.1 6/5/85 */ + +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +/* + * Tcp+ip header, after ip options removed. + */ + +#ifndef _netinet_tcpip_h +#define _netinet_tcpip_h + +struct tcpiphdr { + struct ipovly ti_i; /* overlaid ip structure */ + tcphdr_t ti_t; /* tcp header */ +}; +#define ti_next ti_i.ih_next +#define ti_prev ti_i.ih_prev +#define ti_x1 ti_i.ih_x1 +#define ti_pr ti_i.ih_pr +#define ti_len ti_i.ih_len +#define ti_src ti_i.ih_src +#define ti_dst ti_i.ih_dst +#define ti_sport ti_t.th_sport +#define ti_dport ti_t.th_dport +#define ti_seq ti_t.th_seq +#define ti_ack ti_t.th_ack +#define ti_x2 ti_t.th_x2 +#define ti_off ti_t.th_off +#define ti_flags ti_t.th_flags +#define ti_win ti_t.th_win +#define ti_sum ti_t.th_sum +#define ti_urp ti_t.th_urp + +#endif /*!_netinet_tcpip_h*/ |