diff options
author | Cy Schubert <cy@FreeBSD.org> | 2021-12-15 21:45:47 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2021-12-20 14:16:33 +0000 |
commit | 41edb306f05651fcaf6c74f9e3557f59f80292e1 (patch) | |
tree | 27e88c81aa6f5219d79ccd2c24e11d5cddac6d29 /sbin | |
parent | 3b9b51fe464ebb91e894742a6a0e6417e256f03a (diff) | |
download | src-41edb306f05651fcaf6c74f9e3557f59f80292e1.tar.gz src-41edb306f05651fcaf6c74f9e3557f59f80292e1.zip |
Diffstat (limited to 'sbin')
222 files changed, 46167 insertions, 9 deletions
diff --git a/sbin/ipf/Makefile b/sbin/ipf/Makefile index 6126564e9821..075119abd542 100644 --- a/sbin/ipf/Makefile +++ b/sbin/ipf/Makefile @@ -3,7 +3,7 @@ SUBDIR= libipf .WAIT SUBDIR+= ipf ipfs ipfstat ipmon ipnat ippool # XXX Temporarily disconnected. -# SUBDIR+= ipftest ipresend +# SUBDIR+= ipftest ipresend ipsend SUBDIR_PARALLEL= .include <bsd.subdir.mk> diff --git a/sbin/ipf/Makefile.inc b/sbin/ipf/Makefile.inc index 19742d3bd7c8..1f256a343b9a 100644 --- a/sbin/ipf/Makefile.inc +++ b/sbin/ipf/Makefile.inc @@ -6,10 +6,9 @@ WARNS?= 2 NO_WFORMAT= NO_WARRAY_BOUNDS= -CFLAGS+= -I${SRCTOP}/contrib/ipfilter -CFLAGS+= -I${SRCTOP}/contrib/ipfilter/tools CFLAGS+= -I${SRCTOP}/sys CFLAGS+= -I${SRCTOP}/sys/netpfil/ipfilter +CFLAGS+= -I${SRCTOP}/sbin/ipf/common CFLAGS+= -DSTATETOP -D__UIO_EXPOSE .if ${MK_INET6_SUPPORT} != "no" @@ -24,9 +23,7 @@ LIBADD+= ipf CLEANFILES+= y.tab.c y.tab.h -.PATH: ${SRCTOP}/contrib/ipfilter \ - ${SRCTOP}/contrib/ipfilter/lib \ - ${SRCTOP}/contrib/ipfilter/tools \ - ${SRCTOP}/contrib/ipfilter/man +.PATH: ${SRCTOP}/sbin/ipf/libipf \ + ${SRCTOP}/sbin/ipf/common .include "../Makefile.inc" diff --git a/sbin/ipf/common/genmask.c b/sbin/ipf/common/genmask.c new file mode 100644 index 000000000000..75193e3ea398 --- /dev/null +++ b/sbin/ipf/common/genmask.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +int genmask(family, msk, mskp) + int family; + char *msk; + i6addr_t *mskp; +{ + char *endptr = 0L; + u_32_t addr; + int bits; + + if (strchr(msk, '.') || strchr(msk, 'x') || strchr(msk, ':')) { + /* possibly of the form xxx.xxx.xxx.xxx + * or 0xYYYYYYYY */ + switch (family) + { +#ifdef USE_INET6 + case AF_INET6 : + if (inet_pton(AF_INET6, msk, &mskp->in4) != 1) + return -1; + break; +#endif + case AF_INET : + if (inet_aton(msk, &mskp->in4) == 0) + return -1; + break; + default : + return -1; + /*NOTREACHED*/ + } + } else { + /* + * set x most significant bits + */ + bits = (int)strtol(msk, &endptr, 0); + + switch (family) + { + case AF_INET6 : + if ((*endptr != '\0') || (bits < 0) || (bits > 128)) + return -1; + fill6bits(bits, mskp->i6); + break; + case AF_INET : + if (*endptr != '\0' || bits > 32 || bits < 0) + return -1; + if (bits == 0) + addr = 0; + else + addr = htonl(0xffffffff << (32 - bits)); + mskp->in4.s_addr = addr; + break; + default : + return -1; + /*NOTREACHED*/ + } + } + return 0; +} diff --git a/sbin/ipf/common/ipf.h b/sbin/ipf/common/ipf.h new file mode 100644 index 000000000000..e178cfc1676e --- /dev/null +++ b/sbin/ipf/common/ipf.h @@ -0,0 +1,377 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * @(#)ipf.h 1.12 6/5/96 + * $Id$ + */ + +#ifndef __IPF_H__ +#define __IPF_H__ + + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/file.h> +/* + * This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD. + * Needed here because on some systems <sys/uio.h> gets included by things + * like <sys/socket.h> + */ +#ifndef _KERNEL +# define ADD_KERNEL +# define _KERNEL +# define KERNEL +#endif +#include <sys/uio.h> +#ifdef ADD_KERNEL +# undef _KERNEL +# undef KERNEL +#endif +#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/ip_icmp.h> +# include <netinet/tcp.h> +#include <netinet/udp.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#if !defined(__SVR4) && !defined(__svr4__) && defined(sun) +# include <strings.h> +#endif +#include <string.h> +#include <unistd.h> + +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_auth.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_pool.h" +#include "netinet/ip_scan.h" +#include "netinet/ip_htable.h" +#include "netinet/ip_sync.h" +#include "netinet/ip_dstlist.h" + +#include "opts.h" + +#ifndef __P +# define __P(x) x +#endif + +#ifndef U_32_T +# define U_32_T 1 +# if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \ + defined(__sgi) +typedef u_int32_t u_32_t; +# else +# if defined(__alpha__) || defined(__alpha) || defined(_LP64) +typedef unsigned int u_32_t; +# else +# if SOLARIS2 >= 6 +typedef uint32_t u_32_t; +# else +typedef unsigned int u_32_t; +# endif +# endif +# endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */ +#endif /* U_32_T */ + +#ifndef MAXHOSTNAMELEN +# define MAXHOSTNAMELEN 256 +#endif + +#define MAX_ICMPCODE 16 +#define MAX_ICMPTYPE 19 + +#define PRINTF (void)printf +#define FPRINTF (void)fprintf + + +struct ipopt_names { + int on_value; + int on_bit; + int on_siz; + char *on_name; +}; + + +typedef struct alist_s { + struct alist_s *al_next; + int al_not; + int al_family; + i6addr_t al_i6addr; + i6addr_t al_i6mask; +} alist_t; + +#define al_addr al_i6addr.in4_addr +#define al_mask al_i6mask.in4_addr +#define al_1 al_addr +#define al_2 al_mask + + +typedef struct plist_s { + struct plist_s *pl_next; + int pl_compare; + u_short pl_port1; + u_short pl_port2; +} plist_t; + + +typedef struct { + u_short fb_c; + u_char fb_t; + u_char fb_f; + u_32_t fb_k; +} fakebpf_t; + + +typedef struct { + char *it_name; + int it_v4; + int it_v6; +} icmptype_t; + + +typedef struct wordtab { + char *w_word; + int w_value; +} wordtab_t; + + +typedef struct namelist { + struct namelist *na_next; + char *na_name; + int na_value; +} namelist_t; + + +typedef struct proxyrule { + struct proxyrule *pr_next; + char *pr_proxy; + char *pr_conf; + namelist_t *pr_names; + int pr_proto; +} proxyrule_t; + + +#if defined(__NetBSD__) || defined(__FreeBSD__) || \ + SOLARIS +# include <stdarg.h> +typedef int (* ioctlfunc_t)(int, ioctlcmd_t, ...); +#else +typedef int (* ioctlfunc_t)(dev_t, ioctlcmd_t, void *); +#endif +typedef int (* addfunc_t)(int, ioctlfunc_t, void *); +typedef int (* copyfunc_t)(void *, void *, size_t); + + +extern char thishost[MAXHOSTNAMELEN]; +extern char flagset[]; +extern u_char flags[]; +extern struct ipopt_names ionames[]; +extern struct ipopt_names secclass[]; +extern char *icmpcodes[MAX_ICMPCODE + 1]; +extern char *icmptypes[MAX_ICMPTYPE + 1]; +extern int use_inet6; +extern int lineNum; +extern int debuglevel; +extern struct ipopt_names v6ionames[]; +extern icmptype_t icmptypelist[]; +extern wordtab_t statefields[]; +extern wordtab_t natfields[]; +extern wordtab_t poolfields[]; + + +extern int addicmp(char ***, struct frentry *, int); +extern int addipopt(char *, struct ipopt_names *, int, char *); +extern int addkeep(char ***, struct frentry *, int); +extern alist_t *alist_new(int, char *); +extern void alist_free(alist_t *); +extern void assigndefined(char *); +extern void binprint(void *, size_t); +extern u_32_t buildopts(char *, char *, int); +extern int checkrev(char *); +extern int connecttcp(char *, int); +extern int count6bits(u_32_t *); +extern int count4bits(u_32_t); +extern char *fac_toname(int); +extern int fac_findname(char *); +extern const char *familyname(const int); +extern void fill6bits(int, u_int *); +extern wordtab_t *findword(wordtab_t *, char *); +extern int ftov(int); +extern char *ipf_geterror(int, ioctlfunc_t *); +extern int genmask(int, char *, i6addr_t *); +extern int gethost(int, char *, i6addr_t *); +extern int geticmptype(int, char *); +extern int getport(struct frentry *, char *, u_short *, char *); +extern int getportproto(char *, int); +extern int getproto(char *); +extern char *getnattype(struct nat *); +extern char *getsumd(u_32_t); +extern u_32_t getoptbyname(char *); +extern u_32_t getoptbyvalue(int); +extern u_32_t getv6optbyname(char *); +extern u_32_t getv6optbyvalue(int); +extern char *icmptypename(int, int); +extern void initparse(void); +extern void ipf_dotuning(int, char *, ioctlfunc_t); +extern int ipf_addrule(int, ioctlfunc_t, void *); +extern void ipf_mutex_clean(void); +extern int ipf_parsefile(int, addfunc_t, ioctlfunc_t *, char *); +extern int ipf_parsesome(int, addfunc_t, ioctlfunc_t *, FILE *); +extern void ipf_perror(int, char *); +extern int ipf_perror_fd( int, ioctlfunc_t, char *); +extern void ipf_rwlock_clean(void); +extern char *ipf_strerror(int); +extern void ipferror(int, char *); +extern int ipmon_parsefile(char *); +extern int ipmon_parsesome(FILE *); +extern int ipnat_addrule(int, ioctlfunc_t, void *); +extern int ipnat_parsefile(int, addfunc_t, ioctlfunc_t, char *); +extern int ipnat_parsesome(int, addfunc_t, ioctlfunc_t, FILE *); +extern int ippool_parsefile(int, char *, ioctlfunc_t); +extern int ippool_parsesome(int, FILE *, ioctlfunc_t); +extern int kmemcpywrap(void *, void *, size_t); +extern char *kvatoname(ipfunc_t, ioctlfunc_t); +extern int load_dstlist(struct ippool_dst *, ioctlfunc_t, + ipf_dstnode_t *); +extern int load_dstlistnode(int, char *, struct ipf_dstnode *, + ioctlfunc_t); +extern alist_t *load_file(char *); +extern int load_hash(struct iphtable_s *, struct iphtent_s *, + ioctlfunc_t); +extern int load_hashnode(int, char *, struct iphtent_s *, int, + ioctlfunc_t); +extern alist_t *load_http(char *); +extern int load_pool(struct ip_pool_s *list, ioctlfunc_t); +extern int load_poolnode(int, char *, ip_pool_node_t *, int, ioctlfunc_t); +extern alist_t *load_url(char *); +extern alist_t *make_range(int, struct in_addr, struct in_addr); +extern void mb_hexdump(mb_t *, FILE *); +extern ipfunc_t nametokva(char *, ioctlfunc_t); +extern void nat_setgroupmap(struct ipnat *); +extern int ntomask(int, int, u_32_t *); +extern u_32_t optname(char ***, u_short *, int); +extern wordtab_t *parsefields(wordtab_t *, char *); +extern int *parseipfexpr(char *, char **); +extern int parsewhoisline(char *, addrfamily_t *, addrfamily_t *); +extern void pool_close(void); +extern int pool_fd(void); +extern int pool_ioctl(ioctlfunc_t, ioctlcmd_t, void *); +extern int pool_open(void); +extern char *portname(int, int); +extern int pri_findname(char *); +extern char *pri_toname(int); +extern void print_toif(int, char *, char *, struct frdest *); +extern void printaps(ap_session_t *, int, int); +extern void printaddr(int, int, char *, int, u_32_t *, u_32_t *); +extern void printbuf(char *, int, int); +extern void printfieldhdr(wordtab_t *, wordtab_t *); +extern void printfr(struct frentry *, ioctlfunc_t); +extern struct iphtable_s *printhash(struct iphtable_s *, copyfunc_t, + char *, int, wordtab_t *); +extern struct iphtable_s *printhash_live(iphtable_t *, int, char *, + int, wordtab_t *); +extern ippool_dst_t *printdstl_live(ippool_dst_t *, int, char *, + int, wordtab_t *); +extern void printhashdata(iphtable_t *, int); +extern struct iphtent_s *printhashnode(struct iphtable_s *, + struct iphtent_s *, + copyfunc_t, int, wordtab_t *); +extern void printhost(int, u_32_t *); +extern void printhostmask(int, u_32_t *, u_32_t *); +extern void printip(int, u_32_t *); +extern void printlog(struct frentry *); +extern void printlookup(char *, i6addr_t *addr, i6addr_t *mask); +extern void printmask(int, u_32_t *); +extern void printnataddr(int, char *, nat_addr_t *, int); +extern void printnatfield(nat_t *, int); +extern void printnatside(char *, nat_stat_side_t *); +extern void printpacket(int, mb_t *); +extern void printpacket6(int, mb_t *); +extern struct ippool_dst *printdstlist(struct ippool_dst *, copyfunc_t, + char *, int, ipf_dstnode_t *, + wordtab_t *); +extern void printdstlistdata(ippool_dst_t *, int); +extern ipf_dstnode_t *printdstlistnode(ipf_dstnode_t *, copyfunc_t, + int, wordtab_t *); +extern void printdstlistpolicy(ippool_policy_t); +extern struct ip_pool_s *printpool(struct ip_pool_s *, copyfunc_t, + char *, int, wordtab_t *); +extern struct ip_pool_s *printpool_live(struct ip_pool_s *, int, + char *, int, wordtab_t *); +extern void printpooldata(ip_pool_t *, int); +extern void printpoolfield(void *, int, int); +extern struct ip_pool_node *printpoolnode(struct ip_pool_node *, + int, wordtab_t *); +extern void printproto(struct protoent *, int, struct ipnat *); +extern void printportcmp(int, struct frpcmp *); +extern void printstatefield(ipstate_t *, int); +extern void printtqtable(ipftq_t *); +extern void printtunable(ipftune_t *); +extern void printunit(int); +extern void optprint(u_short *, u_long, u_long); +#ifdef USE_INET6 +extern void optprintv6(u_short *, u_long, u_long); +#endif +extern int remove_hash(struct iphtable_s *, ioctlfunc_t); +extern int remove_hashnode(int, char *, struct iphtent_s *, ioctlfunc_t); +extern int remove_pool(ip_pool_t *, ioctlfunc_t); +extern int remove_poolnode(int, char *, ip_pool_node_t *, ioctlfunc_t); +extern u_char tcpflags(char *); +extern void printc(struct frentry *); +extern void printC(int); +extern void emit(int, int, void *, struct frentry *); +extern u_char secbit(int); +extern u_char seclevel(char *); +extern void printfraginfo(char *, struct ipfr *); +extern void printifname(char *, char *, void *); +extern char *hostname(int, void *); +extern struct ipstate *printstate(struct ipstate *, int, u_long); +extern void printsbuf(char *); +extern void printnat(struct ipnat *, int); +extern void printactiveaddress(int, char *, i6addr_t *, char *); +extern void printactivenat(struct nat *, int, u_long); +extern void printhostmap(struct hostmap *, u_int); +extern void printtcpflags(u_32_t, u_32_t); +extern void printipfexpr(int *); +extern void printstatefield(ipstate_t *, int); +extern void printstatefieldhdr(int); +extern int sendtrap_v1_0(int, char *, char *, int, time_t); +extern int sendtrap_v2_0(int, char *, char *, int); +extern int vtof(int); + +extern void set_variable(char *, char *); +extern char *get_variable(char *, char **, int); +extern void resetlexer(void); + +extern void debug(int, char *, ...); +extern void verbose(int, char *, ...); +extern void ipfkdebug(char *, ...); +extern void ipfkverbose(char *, ...); + +#if SOLARIS +extern int gethostname(char *, int ); +extern void sync(void); +#endif + +#endif /* __IPF_H__ */ diff --git a/sbin/ipf/common/ipf_y.y b/sbin/ipf/common/ipf_y.y new file mode 100644 index 000000000000..2013fe5b9452 --- /dev/null +++ b/sbin/ipf/common/ipf_y.y @@ -0,0 +1,2754 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +%{ +#include "ipf.h" +#include <sys/ioctl.h> +#include <syslog.h> +#include <err.h> +#ifdef IPFILTER_BPF +# include <pcap.h> +#endif +#include "netinet/ip_pool.h" +#include "netinet/ip_htable.h" +#include "netinet/ipl.h" +#include "ipf_l.h" + +#define YYDEBUG 1 +#define DOALL(x) for (fr = frc; fr != NULL; fr = fr->fr_next) { x } +#define DOREM(x) for (; fr != NULL; fr = fr->fr_next) { x } + +extern void yyerror(char *); +extern int yyparse(void); +extern int yylex(void); +extern int yydebug; +extern FILE *yyin; +extern int yylineNum; + +static int addname(frentry_t **, char *); +static frentry_t *addrule(void); +static frentry_t *allocfr(void); +static void build_dstaddr_af(frentry_t *, void *); +static void build_srcaddr_af(frentry_t *, void *); +static void dobpf(int, char *); +static void doipfexpr(char *); +static void do_tuneint(char *, int); +static void do_tunestr(char *, char *); +static void fillgroup(frentry_t *); +static int lookuphost(char *, i6addr_t *); +static u_int makehash(struct alist_s *); +static int makepool(struct alist_s *); +static struct alist_s *newalist(struct alist_s *); +static void newrule(void); +static void resetaddr(void); +static void setgroup(frentry_t **, char *); +static void setgrhead(frentry_t **, char *); +static void seticmphead(frentry_t **, char *); +static void setifname(frentry_t **, int, char *); +static void setipftype(void); +static void setsyslog(void); +static void unsetsyslog(void); + +frentry_t *fr = NULL, *frc = NULL, *frtop = NULL, *frold = NULL; + +static int ifpflag = 0; +static int nowith = 0; +static int dynamic = -1; +static int pooled = 0; +static int hashed = 0; +static int nrules = 0; +static int newlist = 0; +static int added = 0; +static int ipffd = -1; +static int *yycont = NULL; +static ioctlfunc_t ipfioctls[IPL_LOGSIZE]; +static addfunc_t ipfaddfunc = NULL; + +%} +%union { + char *str; + u_32_t num; + frentry_t fr; + frtuc_t *frt; + struct alist_s *alist; + u_short port; + struct in_addr ip4; + struct { + u_short p1; + u_short p2; + int pc; + } pc; + struct ipp_s { + int type; + int ifpos; + int f; + int v; + int lif; + union i6addr a; + union i6addr m; + char *name; + } ipp; + struct { + i6addr_t adr; + int f; + } adr; + i6addr_t ip6; + struct { + char *if1; + char *if2; + } ifs; + char gname[FR_GROUPLEN]; +}; + +%type <port> portnum +%type <num> facility priority icmpcode seclevel secname icmptype +%type <num> opt compare range opttype flagset optlist ipv6hdrlist ipv6hdr +%type <num> portc porteq ipmask maskopts +%type <ip4> ipv4 ipv4_16 ipv4_24 +%type <adr> hostname +%type <ipp> addr ipaddr +%type <str> servicename name interfacename groupname +%type <pc> portrange portcomp +%type <alist> addrlist poollist +%type <ifs> onname + +%token <num> YY_NUMBER YY_HEX +%token <str> YY_STR +%token YY_COMMENT +%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT +%token YY_RANGE_OUT YY_RANGE_IN +%token <ip6> YY_IPV6 + +%token IPFY_SET +%token IPFY_PASS IPFY_BLOCK IPFY_COUNT IPFY_CALL IPFY_NOMATCH +%token IPFY_RETICMP IPFY_RETRST IPFY_RETICMPASDST +%token IPFY_IN IPFY_OUT +%token IPFY_QUICK IPFY_ON IPFY_OUTVIA IPFY_INVIA +%token IPFY_DUPTO IPFY_TO IPFY_FROUTE IPFY_REPLY_TO IPFY_ROUTETO +%token IPFY_TOS IPFY_TTL IPFY_PROTO IPFY_INET IPFY_INET6 +%token IPFY_HEAD IPFY_GROUP +%token IPFY_AUTH IPFY_PREAUTH +%token IPFY_LOG IPFY_BODY IPFY_FIRST IPFY_LEVEL IPFY_ORBLOCK IPFY_L5AS +%token IPFY_LOGTAG IPFY_MATCHTAG IPFY_SETTAG IPFY_SKIP IPFY_DECAPS +%token IPFY_FROM IPFY_ALL IPFY_ANY IPFY_BPFV4 IPFY_BPFV6 IPFY_POOL IPFY_HASH +%token IPFY_IPFEXPR IPFY_PPS IPFY_FAMILY IPFY_DSTLIST +%token IPFY_ESP IPFY_AH +%token IPFY_WITH IPFY_AND IPFY_NOT IPFY_NO IPFY_OPT +%token IPFY_TCPUDP IPFY_TCP IPFY_UDP +%token IPFY_FLAGS IPFY_MULTICAST +%token IPFY_MASK IPFY_BROADCAST IPFY_NETWORK IPFY_NETMASKED IPFY_PEER +%token IPFY_RPC IPFY_PORT +%token IPFY_NOW IPFY_COMMENT IPFY_RULETTL +%token IPFY_ICMP IPFY_ICMPTYPE IPFY_ICMPCODE +%token IPFY_IPOPTS IPFY_SHORT IPFY_NAT IPFY_BADSRC IPFY_LOWTTL IPFY_FRAG +%token IPFY_MBCAST IPFY_BAD IPFY_BADNAT IPFY_OOW IPFY_NEWISN IPFY_NOICMPERR +%token IPFY_KEEP IPFY_STATE IPFY_FRAGS IPFY_LIMIT IPFY_STRICT IPFY_AGE +%token IPFY_SYNC IPFY_FRAGBODY IPFY_ICMPHEAD IPFY_NOLOG IPFY_LOOSE +%token IPFY_MAX_SRCS IPFY_MAX_PER_SRC +%token IPFY_IPOPT_NOP IPFY_IPOPT_RR IPFY_IPOPT_ZSU IPFY_IPOPT_MTUP +%token IPFY_IPOPT_MTUR IPFY_IPOPT_ENCODE IPFY_IPOPT_TS IPFY_IPOPT_TR +%token IPFY_IPOPT_SEC IPFY_IPOPT_LSRR IPFY_IPOPT_ESEC IPFY_IPOPT_CIPSO +%token IPFY_IPOPT_SATID IPFY_IPOPT_SSRR IPFY_IPOPT_ADDEXT IPFY_IPOPT_VISA +%token IPFY_IPOPT_IMITD IPFY_IPOPT_EIP IPFY_IPOPT_FINN IPFY_IPOPT_DPS +%token IPFY_IPOPT_SDB IPFY_IPOPT_NSAPA IPFY_IPOPT_RTRALRT IPFY_IPOPT_UMP +%token IPFY_SECCLASS IPFY_SEC_UNC IPFY_SEC_CONF IPFY_SEC_RSV1 IPFY_SEC_RSV2 +%token IPFY_SEC_RSV4 IPFY_SEC_SEC IPFY_SEC_TS IPFY_SEC_RSV3 IPFY_DOI + +%token IPFY_V6HDRS IPFY_IPV6OPT IPFY_IPV6OPT_DSTOPTS IPFY_IPV6OPT_HOPOPTS +%token IPFY_IPV6OPT_IPV6 IPFY_IPV6OPT_NONE IPFY_IPV6OPT_ROUTING IPFY_V6HDR +%token IPFY_IPV6OPT_MOBILITY IPFY_IPV6OPT_ESP IPFY_IPV6OPT_FRAG + +%token IPFY_ICMPT_UNR IPFY_ICMPT_ECHO IPFY_ICMPT_ECHOR IPFY_ICMPT_SQUENCH +%token IPFY_ICMPT_REDIR IPFY_ICMPT_TIMEX IPFY_ICMPT_PARAMP IPFY_ICMPT_TIMEST +%token IPFY_ICMPT_TIMESTREP IPFY_ICMPT_INFOREQ IPFY_ICMPT_INFOREP +%token IPFY_ICMPT_MASKREQ IPFY_ICMPT_MASKREP IPFY_ICMPT_ROUTERAD +%token IPFY_ICMPT_ROUTERSOL + +%token IPFY_ICMPC_NETUNR IPFY_ICMPC_HSTUNR IPFY_ICMPC_PROUNR IPFY_ICMPC_PORUNR +%token IPFY_ICMPC_NEEDF IPFY_ICMPC_SRCFAIL IPFY_ICMPC_NETUNK IPFY_ICMPC_HSTUNK +%token IPFY_ICMPC_ISOLATE IPFY_ICMPC_NETPRO IPFY_ICMPC_HSTPRO +%token IPFY_ICMPC_NETTOS IPFY_ICMPC_HSTTOS IPFY_ICMPC_FLTPRO IPFY_ICMPC_HSTPRE +%token IPFY_ICMPC_CUTPRE + +%token IPFY_FAC_KERN IPFY_FAC_USER IPFY_FAC_MAIL IPFY_FAC_DAEMON IPFY_FAC_AUTH +%token IPFY_FAC_SYSLOG IPFY_FAC_LPR IPFY_FAC_NEWS IPFY_FAC_UUCP IPFY_FAC_CRON +%token IPFY_FAC_LOCAL0 IPFY_FAC_LOCAL1 IPFY_FAC_LOCAL2 IPFY_FAC_LOCAL3 +%token IPFY_FAC_LOCAL4 IPFY_FAC_LOCAL5 IPFY_FAC_LOCAL6 IPFY_FAC_LOCAL7 +%token IPFY_FAC_SECURITY IPFY_FAC_FTP IPFY_FAC_AUTHPRIV IPFY_FAC_AUDIT +%token IPFY_FAC_LFMT IPFY_FAC_CONSOLE + +%token IPFY_PRI_EMERG IPFY_PRI_ALERT IPFY_PRI_CRIT IPFY_PRI_ERR IPFY_PRI_WARN +%token IPFY_PRI_NOTICE IPFY_PRI_INFO IPFY_PRI_DEBUG +%% +file: settings rules + | rules + ; + +settings: + YY_COMMENT + | setting + | settings setting + ; + +rules: line + | assign + | rules line + | rules assign + ; + +setting: + IPFY_SET YY_STR YY_NUMBER ';' { do_tuneint($2, $3); } + | IPFY_SET YY_STR YY_HEX ';' { do_tuneint($2, $3); } + | IPFY_SET YY_STR YY_STR ';' { do_tunestr($2, $3); } + ; + +line: rule { while ((fr = frtop) != NULL) { + frtop = fr->fr_next; + fr->fr_next = NULL; + if ((fr->fr_type == FR_T_IPF) && + (fr->fr_ip.fi_v == 0)) + fr->fr_mip.fi_v = 0; + /* XXX validate ? */ + (*ipfaddfunc)(ipffd, ipfioctls[IPL_LOGIPF], fr); + fr->fr_next = frold; + frold = fr; + } + resetlexer(); + } + | YY_COMMENT + ; + +xx: { newrule(); } + ; + +assign: YY_STR assigning YY_STR ';' { set_variable($1, $3); + resetlexer(); + free($1); + free($3); + yyvarnext = 0; + } + ; + +assigning: + '=' { yyvarnext = 1; } + ; + +rule: inrule eol + | outrule eol + ; + +eol: | ';' + ; + +inrule: + rulehead markin inopts rulemain ruletail intag ruletail2 + ; + +outrule: + rulehead markout outopts rulemain ruletail outtag ruletail2 + ; + +rulehead: + xx collection action + | xx insert collection action + ; + +markin: IPFY_IN { fr->fr_flags |= FR_INQUE; } + ; + +markout: + IPFY_OUT { fr->fr_flags |= FR_OUTQUE; } + ; + +rulemain: + ipfrule + | bpfrule + | exprrule + ; + +ipfrule: + family tos ttl proto ip + ; + +family: | IPFY_FAMILY IPFY_INET { if (use_inet6 == 1) { + YYERROR; + } else { + frc->fr_family = AF_INET; + } + } + | IPFY_INET { if (use_inet6 == 1) { + YYERROR; + } else { + frc->fr_family = AF_INET; + } + } + | IPFY_FAMILY IPFY_INET6 { if (use_inet6 == -1) { + YYERROR; + } else { + frc->fr_family = AF_INET6; + } + } + | IPFY_INET6 { if (use_inet6 == -1) { + YYERROR; + } else { + frc->fr_family = AF_INET6; + } + } + ; + +bpfrule: + IPFY_BPFV4 '{' YY_STR '}' { dobpf(4, $3); free($3); } + | IPFY_BPFV6 '{' YY_STR '}' { dobpf(6, $3); free($3); } + ; + +exprrule: + IPFY_IPFEXPR '{' YY_STR '}' { doipfexpr($3); } + ; + +ruletail: + with keep head group + ; + +ruletail2: + pps age new rulettl comment + ; + +intag: settagin matchtagin + ; + +outtag: settagout matchtagout + ; + +insert: + '@' YY_NUMBER { fr->fr_hits = (U_QUAD_T)$2 + 1; } + ; + +collection: + | YY_NUMBER { fr->fr_collect = $1; } + ; + +action: block + | IPFY_PASS { fr->fr_flags |= FR_PASS; } + | IPFY_NOMATCH { fr->fr_flags |= FR_NOMATCH; } + | log + | IPFY_COUNT { fr->fr_flags |= FR_ACCOUNT; } + | decaps { fr->fr_flags |= FR_DECAPSULATE; } + | auth + | IPFY_SKIP YY_NUMBER { fr->fr_flags |= FR_SKIP; + fr->fr_arg = $2; } + | IPFY_CALL func + | IPFY_CALL IPFY_NOW func { fr->fr_flags |= FR_CALLNOW; } + ; + +block: blocked + | blocked blockreturn + ; + +blocked: + IPFY_BLOCK { fr->fr_flags = FR_BLOCK; } + ; +blockreturn: + IPFY_RETICMP { fr->fr_flags |= FR_RETICMP; } + | IPFY_RETICMP returncode { fr->fr_flags |= FR_RETICMP; } + | IPFY_RETICMPASDST { fr->fr_flags |= FR_FAKEICMP; } + | IPFY_RETICMPASDST returncode { fr->fr_flags |= FR_FAKEICMP; } + | IPFY_RETRST { fr->fr_flags |= FR_RETRST; } + ; + +decaps: IPFY_DECAPS + | IPFY_DECAPS IPFY_L5AS '(' YY_STR ')' + { fr->fr_icode = atoi($4); } + ; + +log: IPFY_LOG { fr->fr_flags |= FR_LOG; } + | IPFY_LOG logoptions { fr->fr_flags |= FR_LOG; } + ; + +auth: IPFY_AUTH { fr->fr_flags |= FR_AUTH; } + | IPFY_AUTH blockreturn { fr->fr_flags |= FR_AUTH;} + | IPFY_PREAUTH { fr->fr_flags |= FR_PREAUTH; } + ; + +func: YY_STR '/' YY_NUMBER + { fr->fr_func = nametokva($1, ipfioctls[IPL_LOGIPF]); + fr->fr_arg = $3; + free($1); + } + ; + +inopts: + | inopts inopt + ; + +inopt: + logopt + | quick + | on + | dup + | froute + | proute + | replyto + ; + +outopts: + | outopts outopt + ; + +outopt: + logopt + | quick + | on + | dup + | proute + | froute + | replyto + ; + +tos: | settos YY_NUMBER { DOALL(fr->fr_tos = $2; fr->fr_mtos = 0xff;) } + | settos YY_HEX { DOALL(fr->fr_tos = $2; fr->fr_mtos = 0xff;) } + | settos lstart toslist lend + ; + +settos: IPFY_TOS { setipftype(); } + ; + +toslist: + YY_NUMBER { DOALL(fr->fr_tos = $1; fr->fr_mtos = 0xff;) } + | YY_HEX { DOREM(fr->fr_tos = $1; fr->fr_mtos = 0xff;) } + | toslist lmore YY_NUMBER + { DOREM(fr->fr_tos = $3; fr->fr_mtos = 0xff;) } + | toslist lmore YY_HEX + { DOREM(fr->fr_tos = $3; fr->fr_mtos = 0xff;) } + ; + +ttl: | setttl YY_NUMBER + { DOALL(fr->fr_ttl = $2; fr->fr_mttl = 0xff;) } + | setttl lstart ttllist lend + ; + +lstart: '{' { newlist = 1; fr = frc; added = 0; } + ; + +lend: '}' { nrules += added; } + ; + +lmore: lanother { if (newlist == 1) { + newlist = 0; + } + fr = addrule(); + if (yycont != NULL) + *yycont = 1; + } + ; + +lanother: + | ',' + ; + +setttl: IPFY_TTL { setipftype(); } + ; + +ttllist: + YY_NUMBER { DOREM(fr->fr_ttl = $1; fr->fr_mttl = 0xff;) } + | ttllist lmore YY_NUMBER + { DOREM(fr->fr_ttl = $3; fr->fr_mttl = 0xff;) } + ; + +proto: | protox protocol { yyresetdict(); } + ; + +protox: IPFY_PROTO { setipftype(); + fr = frc; + yysetdict(NULL); } + ; + +ip: srcdst flags icmp + ; + +group: | IPFY_GROUP groupname { DOALL(setgroup(&fr, $2); \ + fillgroup(fr);); + free($2); + } + ; + +head: | IPFY_HEAD groupname { DOALL(setgrhead(&fr, $2);); + free($2); + } + ; + +groupname: + YY_STR { $$ = $1; + if (strlen($$) >= FR_GROUPLEN) + $$[FR_GROUPLEN - 1] = '\0'; + } + | YY_NUMBER { $$ = malloc(16); + sprintf($$, "%d", $1); + } + ; + +settagin: + | IPFY_SETTAG '(' taginlist ')' + ; + +taginlist: + taginspec + | taginlist ',' taginspec + ; + +taginspec: + logtag + ; + +nattag: IPFY_NAT '=' YY_STR { DOALL(strncpy(fr->fr_nattag.ipt_tag,\ + $3, IPFTAG_LEN);); + free($3); } + | IPFY_NAT '=' YY_NUMBER { DOALL(sprintf(fr->fr_nattag.ipt_tag,\ + "%d", $3 & 0xffffffff);) } + ; + +logtag: IPFY_LOG '=' YY_NUMBER { DOALL(fr->fr_logtag = $3;) } + ; + +settagout: + | IPFY_SETTAG '(' tagoutlist ')' + ; + +tagoutlist: + tagoutspec + | tagoutlist ',' tagoutspec + ; + +tagoutspec: + logtag + | nattag + ; + +matchtagin: + | IPFY_MATCHTAG '(' tagoutlist ')' + ; + +matchtagout: + | IPFY_MATCHTAG '(' taginlist ')' + ; + +pps: | IPFY_PPS YY_NUMBER { DOALL(fr->fr_pps = $2;) } + ; + +new: | savegroup file restoregroup + ; + +rulettl: + | IPFY_RULETTL YY_NUMBER { DOALL(fr->fr_die = $2;) } + ; + +comment: + | IPFY_COMMENT YY_STR { DOALL(fr->fr_comment = addname(&fr, \ + $2);) } + ; + +savegroup: + '{' + ; + +restoregroup: + '}' + ; + +logopt: log + ; + +quick: IPFY_QUICK { fr->fr_flags |= FR_QUICK; } + ; + +on: IPFY_ON onname { setifname(&fr, 0, $2.if1); + free($2.if1); + if ($2.if2 != NULL) { + setifname(&fr, 1, + $2.if2); + free($2.if2); + } + } + | IPFY_ON lstart onlist lend + | IPFY_ON onname IPFY_INVIA vianame { setifname(&fr, 0, $2.if1); + free($2.if1); + if ($2.if2 != NULL) { + setifname(&fr, 1, + $2.if2); + free($2.if2); + } + } + | IPFY_ON onname IPFY_OUTVIA vianame { setifname(&fr, 0, $2.if1); + free($2.if1); + if ($2.if2 != NULL) { + setifname(&fr, 1, + $2.if2); + free($2.if2); + } + } + ; + +onlist: onname { DOREM(setifname(&fr, 0, $1.if1); \ + if ($1.if2 != NULL) \ + setifname(&fr, 1, $1.if2); \ + ) + free($1.if1); + if ($1.if2 != NULL) + free($1.if2); + } + | onlist lmore onname { DOREM(setifname(&fr, 0, $3.if1); \ + if ($3.if2 != NULL) \ + setifname(&fr, 1, $3.if2); \ + ) + free($3.if1); + if ($3.if2 != NULL) + free($3.if2); + } + ; + +onname: interfacename { $$.if1 = $1; + $$.if2 = NULL; + } + | interfacename ',' interfacename + { $$.if1 = $1; + $$.if2 = $3; + } + ; + +vianame: + name { setifname(&fr, 2, $1); + free($1); + } + | name ',' name { setifname(&fr, 2, $1); + free($1); + setifname(&fr, 3, $3); + free($3); + } + ; + +dup: IPFY_DUPTO name + { int idx = addname(&fr, $2); + fr->fr_dif.fd_name = idx; + free($2); + } + | IPFY_DUPTO IPFY_DSTLIST '/' name + { int idx = addname(&fr, $4); + fr->fr_dif.fd_name = idx; + fr->fr_dif.fd_type = FRD_DSTLIST; + free($4); + } + | IPFY_DUPTO name duptoseparator hostname + { int idx = addname(&fr, $2); + fr->fr_dif.fd_name = idx; + fr->fr_dif.fd_ptr = (void *)-1; + fr->fr_dif.fd_ip6 = $4.adr; + if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC) + fr->fr_family = $4.f; + yyexpectaddr = 0; + free($2); + } + ; + +duptoseparator: + ':' { yyexpectaddr = 1; yycont = &yyexpectaddr; resetaddr(); } + ; + +froute: IPFY_FROUTE { fr->fr_flags |= FR_FASTROUTE; } + ; + +proute: routeto name + { int idx = addname(&fr, $2); + fr->fr_tif.fd_name = idx; + free($2); + } + | routeto IPFY_DSTLIST '/' name + { int idx = addname(&fr, $4); + fr->fr_tif.fd_name = idx; + fr->fr_tif.fd_type = FRD_DSTLIST; + free($4); + } + | routeto name duptoseparator hostname + { int idx = addname(&fr, $2); + fr->fr_tif.fd_name = idx; + fr->fr_tif.fd_ptr = (void *)-1; + fr->fr_tif.fd_ip6 = $4.adr; + if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC) + fr->fr_family = $4.f; + yyexpectaddr = 0; + free($2); + } + ; + +routeto: + IPFY_TO + | IPFY_ROUTETO + ; + +replyto: + IPFY_REPLY_TO name + { int idx = addname(&fr, $2); + fr->fr_rif.fd_name = idx; + free($2); + } + | IPFY_REPLY_TO IPFY_DSTLIST '/' name + { fr->fr_rif.fd_name = addname(&fr, $4); + fr->fr_rif.fd_type = FRD_DSTLIST; + free($4); + } + | IPFY_REPLY_TO name duptoseparator hostname + { int idx = addname(&fr, $2); + fr->fr_rif.fd_name = idx; + fr->fr_rif.fd_ptr = (void *)-1; + fr->fr_rif.fd_ip6 = $4.adr; + if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC) + fr->fr_family = $4.f; + free($2); + } + ; + +logoptions: + logoption + | logoptions logoption + ; + +logoption: + IPFY_BODY { fr->fr_flags |= FR_LOGBODY; } + | IPFY_FIRST { fr->fr_flags |= FR_LOGFIRST; } + | IPFY_ORBLOCK { fr->fr_flags |= FR_LOGORBLOCK; } + | level loglevel { unsetsyslog(); } + ; + +returncode: + starticmpcode icmpcode ')' { fr->fr_icode = $2; yyresetdict(); } + ; + +starticmpcode: + '(' { yysetdict(icmpcodewords); } + ; + +srcdst: | IPFY_ALL + | fromto + ; + +protocol: + YY_NUMBER { DOALL(fr->fr_proto = $1; \ + fr->fr_mproto = 0xff;) + } + | YY_STR { if (!strcmp($1, "tcp-udp")) { + DOALL(fr->fr_flx |= FI_TCPUDP; \ + fr->fr_mflx |= FI_TCPUDP;) + } else { + int p = getproto($1); + if (p == -1) + yyerror("protocol unknown"); + DOALL(fr->fr_proto = p; \ + fr->fr_mproto = 0xff;) + } + free($1); + } + | YY_STR nextstring YY_STR + { if (!strcmp($1, "tcp") && + !strcmp($3, "udp")) { + DOREM(fr->fr_flx |= FI_TCPUDP; \ + fr->fr_mflx |= FI_TCPUDP;) + } else { + YYERROR; + } + free($1); + free($3); + } + ; + +nextstring: + '/' { yysetdict(NULL); } + ; + +fromto: from srcobject to dstobject { yyexpectaddr = 0; yycont = NULL; } + | to dstobject { yyexpectaddr = 0; yycont = NULL; } + | from srcobject { yyexpectaddr = 0; yycont = NULL; } + ; + +from: IPFY_FROM { setipftype(); + if (fr == NULL) + fr = frc; + yyexpectaddr = 1; + if (yydebug) + printf("set yyexpectaddr\n"); + yycont = &yyexpectaddr; + yysetdict(addrwords); + resetaddr(); } + ; + +to: IPFY_TO { if (fr == NULL) + fr = frc; + yyexpectaddr = 1; + if (yydebug) + printf("set yyexpectaddr\n"); + yycont = &yyexpectaddr; + yysetdict(addrwords); + resetaddr(); + } + ; + +with: | andwith withlist + ; + +andwith: + IPFY_WITH { nowith = 0; setipftype(); } + | IPFY_AND { nowith = 0; setipftype(); } + ; + +flags: | startflags flagset + { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = FR_TCPFMAX;) } + | startflags flagset '/' flagset + { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) } + | startflags '/' flagset + { DOALL(fr->fr_tcpf = 0; fr->fr_tcpfm = $3;) } + | startflags YY_NUMBER + { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = FR_TCPFMAX;) } + | startflags '/' YY_NUMBER + { DOALL(fr->fr_tcpf = 0; fr->fr_tcpfm = $3;) } + | startflags YY_NUMBER '/' YY_NUMBER + { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) } + | startflags flagset '/' YY_NUMBER + { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) } + | startflags YY_NUMBER '/' flagset + { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) } + ; + +startflags: + IPFY_FLAGS { if (frc->fr_type != FR_T_IPF) + yyerror("flags with non-ipf type rule"); + if (frc->fr_proto != IPPROTO_TCP) + yyerror("flags with non-TCP rule"); + } + ; + +flagset: + YY_STR { $$ = tcpflags($1); free($1); } + | YY_HEX { $$ = $1; } + ; + +srcobject: + { yyresetdict(); } fromport + | srcaddr srcport + | '!' srcaddr srcport + { DOALL(fr->fr_flags |= FR_NOTSRCIP;) } + ; + +srcaddr: + addr { build_srcaddr_af(fr, &$1); } + | lstart srcaddrlist lend + ; + +srcaddrlist: + addr { build_srcaddr_af(fr, &$1); } + | srcaddrlist lmore addr + { build_srcaddr_af(fr, &$3); } + ; + +srcport: + | portcomp + { DOALL(fr->fr_scmp = $1.pc; fr->fr_sport = $1.p1;) } + | portrange + { DOALL(fr->fr_scmp = $1.pc; fr->fr_sport = $1.p1; \ + fr->fr_stop = $1.p2;) } + | porteq lstart srcportlist lend + { yyresetdict(); } + ; + +fromport: + portcomp + { DOALL(fr->fr_scmp = $1.pc; fr->fr_sport = $1.p1;) } + | portrange + { DOALL(fr->fr_scmp = $1.pc; fr->fr_sport = $1.p1; \ + fr->fr_stop = $1.p2;) } + | porteq lstart srcportlist lend + { yyresetdict(); } + ; + +srcportlist: + portnum { DOREM(fr->fr_scmp = FR_EQUAL; fr->fr_sport = $1;) } + | portnum ':' portnum + { DOREM(fr->fr_scmp = FR_INCRANGE; fr->fr_sport = $1; \ + fr->fr_stop = $3;) } + | portnum YY_RANGE_IN portnum + { DOREM(fr->fr_scmp = FR_INRANGE; fr->fr_sport = $1; \ + fr->fr_stop = $3;) } + | srcportlist lmore portnum + { DOREM(fr->fr_scmp = FR_EQUAL; fr->fr_sport = $3;) } + | srcportlist lmore portnum ':' portnum + { DOREM(fr->fr_scmp = FR_INCRANGE; fr->fr_sport = $3; \ + fr->fr_stop = $5;) } + | srcportlist lmore portnum YY_RANGE_IN portnum + { DOREM(fr->fr_scmp = FR_INRANGE; fr->fr_sport = $3; \ + fr->fr_stop = $5;) } + ; + +dstobject: + { yyresetdict(); } toport + | dstaddr dstport + | '!' dstaddr dstport + { DOALL(fr->fr_flags |= FR_NOTDSTIP;) } + ; + +dstaddr: + addr { if (($1.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) && + ($1.f != frc->fr_family)) + yyerror("1.src/dst address family mismatch"); + build_dstaddr_af(fr, &$1); + } + | lstart dstaddrlist lend + ; + +dstaddrlist: + addr { if (($1.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) && + ($1.f != frc->fr_family)) + yyerror("2.src/dst address family mismatch"); + build_dstaddr_af(fr, &$1); + } + | dstaddrlist lmore addr + { if (($3.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) && + ($3.f != frc->fr_family)) + yyerror("3.src/dst address family mismatch"); + build_dstaddr_af(fr, &$3); + } + ; + + +dstport: + | portcomp + { DOALL(fr->fr_dcmp = $1.pc; fr->fr_dport = $1.p1;) } + | portrange + { DOALL(fr->fr_dcmp = $1.pc; fr->fr_dport = $1.p1; \ + fr->fr_dtop = $1.p2;) } + | porteq lstart dstportlist lend + { yyresetdict(); } + ; + +toport: + portcomp + { DOALL(fr->fr_dcmp = $1.pc; fr->fr_dport = $1.p1;) } + | portrange + { DOALL(fr->fr_dcmp = $1.pc; fr->fr_dport = $1.p1; \ + fr->fr_dtop = $1.p2;) } + | porteq lstart dstportlist lend + { yyresetdict(); } + ; + +dstportlist: + portnum { DOREM(fr->fr_dcmp = FR_EQUAL; fr->fr_dport = $1;) } + | portnum ':' portnum + { DOREM(fr->fr_dcmp = FR_INCRANGE; fr->fr_dport = $1; \ + fr->fr_dtop = $3;) } + | portnum YY_RANGE_IN portnum + { DOREM(fr->fr_dcmp = FR_INRANGE; fr->fr_dport = $1; \ + fr->fr_dtop = $3;) } + | dstportlist lmore portnum + { DOREM(fr->fr_dcmp = FR_EQUAL; fr->fr_dport = $3;) } + | dstportlist lmore portnum ':' portnum + { DOREM(fr->fr_dcmp = FR_INCRANGE; fr->fr_dport = $3; \ + fr->fr_dtop = $5;) } + | dstportlist lmore portnum YY_RANGE_IN portnum + { DOREM(fr->fr_dcmp = FR_INRANGE; fr->fr_dport = $3; \ + fr->fr_dtop = $5;) } + ; + +addr: pool '/' YY_NUMBER { pooled = 1; + yyexpectaddr = 0; + $$.type = FRI_LOOKUP; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.a.iplookuptype = IPLT_POOL; + $$.a.iplookupsubtype = 0; + $$.a.iplookupnum = $3; } + | pool '/' YY_STR { pooled = 1; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.type = FRI_LOOKUP; + $$.a.iplookuptype = IPLT_POOL; + $$.a.iplookupsubtype = 1; + $$.a.iplookupname = addname(&fr, $3); + } + | pool '=' '(' { yyexpectaddr = 1; + pooled = 1; + } + poollist ')' { yyexpectaddr = 0; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.type = FRI_LOOKUP; + $$.a.iplookuptype = IPLT_POOL; + $$.a.iplookupsubtype = 0; + $$.a.iplookupnum = makepool($5); + } + | hash '/' YY_NUMBER { hashed = 1; + yyexpectaddr = 0; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.type = FRI_LOOKUP; + $$.a.iplookuptype = IPLT_HASH; + $$.a.iplookupsubtype = 0; + $$.a.iplookupnum = $3; + } + | hash '/' YY_STR { hashed = 1; + $$.type = FRI_LOOKUP; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.a.iplookuptype = IPLT_HASH; + $$.a.iplookupsubtype = 1; + $$.a.iplookupname = addname(&fr, $3); + } + | hash '=' '(' { hashed = 1; + yyexpectaddr = 1; + } + addrlist ')' { yyexpectaddr = 0; + $$.v = 0; + $$.ifpos = -1; + $$.f = AF_UNSPEC; + $$.type = FRI_LOOKUP; + $$.a.iplookuptype = IPLT_HASH; + $$.a.iplookupsubtype = 0; + $$.a.iplookupnum = makehash($5); + } + | ipaddr { $$ = $1; + yyexpectaddr = 0; } + ; + +ipaddr: IPFY_ANY { memset(&($$), 0, sizeof($$)); + $$.type = FRI_NORMAL; + $$.ifpos = -1; + yyexpectaddr = 0; + } + | hostname { memset(&($$), 0, sizeof($$)); + $$.a = $1.adr; + $$.f = $1.f; + if ($1.f == AF_INET6) + fill6bits(128, $$.m.i6); + else if ($1.f == AF_INET) + fill6bits(32, $$.m.i6); + $$.v = ftov($1.f); + $$.ifpos = dynamic; + $$.type = FRI_NORMAL; + } + | hostname { yyresetdict(); } + maskspace { yysetdict(maskwords); + yyexpectaddr = 2; } + ipmask { memset(&($$), 0, sizeof($$)); + ntomask($1.f, $5, $$.m.i6); + $$.a = $1.adr; + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.f = $1.f; + $$.v = ftov($1.f); + $$.type = ifpflag; + $$.ifpos = dynamic; + if (ifpflag != 0 && $$.v == 0) { + if (frc->fr_family == AF_INET6){ + $$.v = 6; + $$.f = AF_INET6; + } else { + $$.v = 4; + $$.f = AF_INET; + } + } + yyresetdict(); + yyexpectaddr = 0; + } + | '(' YY_STR ')' { memset(&($$), 0, sizeof($$)); + $$.type = FRI_DYNAMIC; + ifpflag = FRI_DYNAMIC; + $$.ifpos = addname(&fr, $2); + $$.lif = 0; + } + | '(' YY_STR ')' '/' + { ifpflag = FRI_DYNAMIC; yysetdict(maskwords); } + maskopts + { memset(&($$), 0, sizeof($$)); + $$.type = ifpflag; + $$.ifpos = addname(&fr, $2); + $$.lif = 0; + if (frc->fr_family == AF_UNSPEC) + frc->fr_family = AF_INET; + if (ifpflag == FRI_DYNAMIC) { + ntomask(frc->fr_family, + $6, $$.m.i6); + } + yyresetdict(); + yyexpectaddr = 0; + } + | '(' YY_STR ':' YY_NUMBER ')' '/' + { ifpflag = FRI_DYNAMIC; yysetdict(maskwords); } + maskopts + { memset(&($$), 0, sizeof($$)); + $$.type = ifpflag; + $$.ifpos = addname(&fr, $2); + $$.lif = $4; + if (frc->fr_family == AF_UNSPEC) + frc->fr_family = AF_INET; + if (ifpflag == FRI_DYNAMIC) { + ntomask(frc->fr_family, + $8, $$.m.i6); + } + yyresetdict(); + yyexpectaddr = 0; + } + ; + +maskspace: + '/' + | IPFY_MASK + ; + +ipmask: ipv4 { $$ = count4bits($1.s_addr); } + | YY_HEX { $$ = count4bits(htonl($1)); } + | YY_NUMBER { $$ = $1; } + | YY_IPV6 { $$ = count6bits($1.i6); } + | maskopts { $$ = $1; } + ; + +maskopts: + IPFY_BROADCAST { if (ifpflag == FRI_DYNAMIC) { + ifpflag = FRI_BROADCAST; + } else { + YYERROR; + } + $$ = 0; + } + | IPFY_NETWORK { if (ifpflag == FRI_DYNAMIC) { + ifpflag = FRI_NETWORK; + } else { + YYERROR; + } + $$ = 0; + } + | IPFY_NETMASKED { if (ifpflag == FRI_DYNAMIC) { + ifpflag = FRI_NETMASKED; + } else { + YYERROR; + } + $$ = 0; + } + | IPFY_PEER { if (ifpflag == FRI_DYNAMIC) { + ifpflag = FRI_PEERADDR; + } else { + YYERROR; + } + $$ = 0; + } + | YY_NUMBER { $$ = $1; } + ; + +hostname: + ipv4 { memset(&($$), 0, sizeof($$)); + $$.adr.in4 = $1; + if (frc->fr_family == AF_INET6) + YYERROR; + $$.f = AF_INET; + yyexpectaddr = 2; + } + | YY_NUMBER { memset(&($$), 0, sizeof($$)); + if (frc->fr_family == AF_INET6) + YYERROR; + $$.adr.in4_addr = $1; + $$.f = AF_INET; + yyexpectaddr = 2; + } + | YY_HEX { memset(&($$), 0, sizeof($$)); + if (frc->fr_family == AF_INET6) + YYERROR; + $$.adr.in4_addr = $1; + $$.f = AF_INET; + yyexpectaddr = 2; + } + | YY_STR { memset(&($$), 0, sizeof($$)); + if (lookuphost($1, &$$.adr) == 0) + $$.f = AF_INET; + free($1); + yyexpectaddr = 2; + } + | YY_IPV6 { memset(&($$), 0, sizeof($$)); + if (frc->fr_family == AF_INET) + YYERROR; + $$.adr = $1; + $$.f = AF_INET6; + yyexpectaddr = 2; + } + ; + +addrlist: + ipaddr { $$ = newalist(NULL); + $$->al_family = $1.f; + $$->al_i6addr = $1.a; + $$->al_i6mask = $1.m; + } + | ipaddr ',' { yyexpectaddr = 1; } addrlist + { $$ = newalist($4); + $$->al_family = $1.f; + $$->al_i6addr = $1.a; + $$->al_i6mask = $1.m; + } + ; + +pool: IPFY_POOL { yyexpectaddr = 0; yycont = NULL; yyresetdict(); } + ; + +hash: IPFY_HASH { yyexpectaddr = 0; yycont = NULL; yyresetdict(); } + ; + +poollist: + ipaddr { $$ = newalist(NULL); + $$->al_family = $1.f; + $$->al_i6addr = $1.a; + $$->al_i6mask = $1.m; + } + | '!' ipaddr { $$ = newalist(NULL); + $$->al_not = 1; + $$->al_family = $2.f; + $$->al_i6addr = $2.a; + $$->al_i6mask = $2.m; + } + | poollist ',' ipaddr + { $$ = newalist($1); + $$->al_family = $3.f; + $$->al_i6addr = $3.a; + $$->al_i6mask = $3.m; + } + | poollist ',' '!' ipaddr + { $$ = newalist($1); + $$->al_not = 1; + $$->al_family = $4.f; + $$->al_i6addr = $4.a; + $$->al_i6mask = $4.m; + } + ; + +port: IPFY_PORT { yyexpectaddr = 0; + yycont = NULL; + if (frc->fr_proto != 0 && + frc->fr_proto != IPPROTO_UDP && + frc->fr_proto != IPPROTO_TCP) + yyerror("port use incorrect"); + } + ; + +portc: port compare { $$ = $2; + yysetdict(NULL); + } + | porteq { $$ = $1; } + ; + +porteq: port '=' { $$ = FR_EQUAL; + yysetdict(NULL); + } + ; + +portr: IPFY_PORT { yyexpectaddr = 0; + yycont = NULL; + yysetdict(NULL); + } + ; + +portcomp: + portc portnum { $$.pc = $1; + $$.p1 = $2; + yyresetdict(); + } + ; + +portrange: + portr portnum range portnum { $$.p1 = $2; + $$.pc = $3; + $$.p2 = $4; + yyresetdict(); + } + ; + +icmp: | itype icode + ; + +itype: seticmptype icmptype + { DOALL(fr->fr_icmp = htons($2 << 8); fr->fr_icmpm = htons(0xff00);); + yyresetdict(); + } + | seticmptype lstart typelist lend { yyresetdict(); } + ; + +seticmptype: + IPFY_ICMPTYPE { if (frc->fr_family == AF_UNSPEC) + frc->fr_family = AF_INET; + if (frc->fr_family == AF_INET && + frc->fr_type == FR_T_IPF && + frc->fr_proto != IPPROTO_ICMP) { + yyerror("proto not icmp"); + } + if (frc->fr_family == AF_INET6 && + frc->fr_type == FR_T_IPF && + frc->fr_proto != IPPROTO_ICMPV6) { + yyerror("proto not ipv6-icmp"); + } + setipftype(); + DOALL(if (fr->fr_family == AF_INET) { \ + fr->fr_ip.fi_v = 4; \ + fr->fr_mip.fi_v = 0xf; \ + } + if (fr->fr_family == AF_INET6) { \ + fr->fr_ip.fi_v = 6; \ + fr->fr_mip.fi_v = 0xf; \ + } + ) + yysetdict(NULL); + } + ; + +icode: | seticmpcode icmpcode + { DOALL(fr->fr_icmp |= htons($2); fr->fr_icmpm |= htons(0xff);); + yyresetdict(); + } + | seticmpcode lstart codelist lend { yyresetdict(); } + ; + +seticmpcode: + IPFY_ICMPCODE { yysetdict(icmpcodewords); } + ; + +typelist: + icmptype + { DOREM(fr->fr_icmp = htons($1 << 8); fr->fr_icmpm = htons(0xff00);) } + | typelist lmore icmptype + { DOREM(fr->fr_icmp = htons($3 << 8); fr->fr_icmpm = htons(0xff00);) } + ; + +codelist: + icmpcode + { DOREM(fr->fr_icmp |= htons($1); fr->fr_icmpm |= htons(0xff);) } + | codelist lmore icmpcode + { DOREM(fr->fr_icmp &= htons(0xff00); fr->fr_icmp |= htons($3); \ + fr->fr_icmpm |= htons(0xff);) } + ; + +age: | IPFY_AGE YY_NUMBER { DOALL(fr->fr_age[0] = $2; \ + fr->fr_age[1] = $2;) } + | IPFY_AGE YY_NUMBER '/' YY_NUMBER + { DOALL(fr->fr_age[0] = $2; \ + fr->fr_age[1] = $4;) } + ; + +keep: | IPFY_KEEP keepstate keep + | IPFY_KEEP keepfrag keep + ; + +keepstate: + IPFY_STATE stateoptlist { DOALL(fr->fr_flags |= FR_KEEPSTATE;)} + ; + +keepfrag: + IPFY_FRAGS fragoptlist { DOALL(fr->fr_flags |= FR_KEEPFRAG;) } + | IPFY_FRAG fragoptlist { DOALL(fr->fr_flags |= FR_KEEPFRAG;) } + ; + +fragoptlist: + | '(' fragopts ')' + ; + +fragopts: + fragopt lanother fragopts + | fragopt + ; + +fragopt: + IPFY_STRICT { DOALL(fr->fr_flags |= FR_FRSTRICT;) } + ; + +stateoptlist: + | '(' stateopts ')' + ; + +stateopts: + stateopt lanother stateopts + | stateopt + ; + +stateopt: + IPFY_LIMIT YY_NUMBER { DOALL(fr->fr_statemax = $2;) } + | IPFY_STRICT { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \ + YYERROR; \ + } else if (fr->fr_flags & FR_STLOOSE) {\ + YYERROR; \ + } else \ + fr->fr_flags |= FR_STSTRICT;) + } + | IPFY_LOOSE { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \ + YYERROR; \ + } else if (fr->fr_flags & FR_STSTRICT){\ + YYERROR; \ + } else \ + fr->fr_flags |= FR_STLOOSE;) + } + | IPFY_NEWISN { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \ + YYERROR; \ + } else \ + fr->fr_flags |= FR_NEWISN;) + } + | IPFY_NOICMPERR { DOALL(fr->fr_flags |= FR_NOICMPERR;) } + + | IPFY_SYNC { DOALL(fr->fr_flags |= FR_STATESYNC;) } + | IPFY_AGE YY_NUMBER { DOALL(fr->fr_age[0] = $2; \ + fr->fr_age[1] = $2;) } + | IPFY_AGE YY_NUMBER '/' YY_NUMBER + { DOALL(fr->fr_age[0] = $2; \ + fr->fr_age[1] = $4;) } + | IPFY_ICMPHEAD groupname + { DOALL(seticmphead(&fr, $2);) + free($2); + } + | IPFY_NOLOG + { DOALL(fr->fr_nostatelog = 1;) } + | IPFY_RPC + { DOALL(fr->fr_rpc = 1;) } + | IPFY_RPC IPFY_IN YY_STR + { DOALL(fr->fr_rpc = 1;) } + | IPFY_MAX_SRCS YY_NUMBER + { DOALL(fr->fr_srctrack.ht_max_nodes = $2;) } + | IPFY_MAX_PER_SRC YY_NUMBER + { DOALL(fr->fr_srctrack.ht_max_per_node = $2; \ + fr->fr_srctrack.ht_netmask = \ + fr->fr_family == AF_INET ? 32: 128;) + } + | IPFY_MAX_PER_SRC YY_NUMBER '/' YY_NUMBER + { DOALL(fr->fr_srctrack.ht_max_per_node = $2; \ + fr->fr_srctrack.ht_netmask = $4;) + } + ; + +portnum: + servicename { if (getport(frc, $1, + &($$), NULL) == -1) + yyerror("service unknown"); + $$ = ntohs($$); + free($1); + } + | YY_NUMBER { if ($1 > 65535) /* Unsigned */ + yyerror("invalid port number"); + else + $$ = $1; + } + ; + +withlist: + withopt { nowith = 0; } + | withlist withopt { nowith = 0; } + | withlist ',' withopt { nowith = 0; } + ; + +withopt: + opttype { DOALL(fr->fr_flx |= $1; fr->fr_mflx |= $1;) } + | notwith opttype { DOALL(fr->fr_mflx |= $2;) } + | ipopt ipopts { yyresetdict(); } + | notwith ipopt ipopts { yyresetdict(); } + | startv6hdr ipv6hdrs { yyresetdict(); } + ; + +ipopt: IPFY_OPT { yysetdict(ipv4optwords); } + ; + +startv6hdr: + IPFY_V6HDR { if (frc->fr_family != AF_INET6) + yyerror("only available with IPv6"); + yysetdict(ipv6optwords); + } + ; + +notwith: + IPFY_NOT { nowith = 1; } + | IPFY_NO { nowith = 1; } + ; + +opttype: + IPFY_IPOPTS { $$ = FI_OPTIONS; } + | IPFY_SHORT { $$ = FI_SHORT; } + | IPFY_NAT { $$ = FI_NATED; } + | IPFY_BAD { $$ = FI_BAD; } + | IPFY_BADNAT { $$ = FI_BADNAT; } + | IPFY_BADSRC { $$ = FI_BADSRC; } + | IPFY_LOWTTL { $$ = FI_LOWTTL; } + | IPFY_FRAG { $$ = FI_FRAG; } + | IPFY_FRAGBODY { $$ = FI_FRAGBODY; } + | IPFY_FRAGS { $$ = FI_FRAG; } + | IPFY_MBCAST { $$ = FI_MBCAST; } + | IPFY_MULTICAST { $$ = FI_MULTICAST; } + | IPFY_BROADCAST { $$ = FI_BROADCAST; } + | IPFY_STATE { $$ = FI_STATE; } + | IPFY_OOW { $$ = FI_OOW; } + | IPFY_AH { $$ = FI_AH; } + | IPFY_V6HDRS { $$ = FI_V6EXTHDR; } + ; + +ipopts: optlist { DOALL(fr->fr_mip.fi_optmsk |= $1; + if (fr->fr_family == AF_UNSPEC) { + fr->fr_family = AF_INET; + fr->fr_ip.fi_v = 4; + fr->fr_mip.fi_v = 0xf; + } else if (fr->fr_family != AF_INET) { + YYERROR; + } + if (!nowith) + fr->fr_ip.fi_optmsk |= $1;) + } + ; + +optlist: + opt { $$ |= $1; } + | optlist ',' opt { $$ |= $1 | $3; } + ; + +ipv6hdrs: + ipv6hdrlist { DOALL(fr->fr_mip.fi_optmsk |= $1; + if (!nowith) + fr->fr_ip.fi_optmsk |= $1;) + } + ; + +ipv6hdrlist: + ipv6hdr { $$ |= $1; } + | ipv6hdrlist ',' ipv6hdr { $$ |= $1 | $3; } + ; + +secname: + seclevel { $$ |= $1; } + | secname ',' seclevel { $$ |= $1 | $3; } + ; + +seclevel: + IPFY_SEC_UNC { $$ = secbit(IPSO_CLASS_UNCL); } + | IPFY_SEC_CONF { $$ = secbit(IPSO_CLASS_CONF); } + | IPFY_SEC_RSV1 { $$ = secbit(IPSO_CLASS_RES1); } + | IPFY_SEC_RSV2 { $$ = secbit(IPSO_CLASS_RES2); } + | IPFY_SEC_RSV3 { $$ = secbit(IPSO_CLASS_RES3); } + | IPFY_SEC_RSV4 { $$ = secbit(IPSO_CLASS_RES4); } + | IPFY_SEC_SEC { $$ = secbit(IPSO_CLASS_SECR); } + | IPFY_SEC_TS { $$ = secbit(IPSO_CLASS_TOPS); } + ; + +icmptype: + YY_NUMBER { $$ = $1; } + | YY_STR { $$ = geticmptype(frc->fr_family, $1); + if ($$ == -1) + yyerror("unrecognised icmp type"); + } + ; + +icmpcode: + YY_NUMBER { $$ = $1; } + | IPFY_ICMPC_NETUNR { $$ = ICMP_UNREACH_NET; } + | IPFY_ICMPC_HSTUNR { $$ = ICMP_UNREACH_HOST; } + | IPFY_ICMPC_PROUNR { $$ = ICMP_UNREACH_PROTOCOL; } + | IPFY_ICMPC_PORUNR { $$ = ICMP_UNREACH_PORT; } + | IPFY_ICMPC_NEEDF { $$ = ICMP_UNREACH_NEEDFRAG; } + | IPFY_ICMPC_SRCFAIL { $$ = ICMP_UNREACH_SRCFAIL; } + | IPFY_ICMPC_NETUNK { $$ = ICMP_UNREACH_NET_UNKNOWN; } + | IPFY_ICMPC_HSTUNK { $$ = ICMP_UNREACH_HOST_UNKNOWN; } + | IPFY_ICMPC_ISOLATE { $$ = ICMP_UNREACH_ISOLATED; } + | IPFY_ICMPC_NETPRO { $$ = ICMP_UNREACH_NET_PROHIB; } + | IPFY_ICMPC_HSTPRO { $$ = ICMP_UNREACH_HOST_PROHIB; } + | IPFY_ICMPC_NETTOS { $$ = ICMP_UNREACH_TOSNET; } + | IPFY_ICMPC_HSTTOS { $$ = ICMP_UNREACH_TOSHOST; } + | IPFY_ICMPC_FLTPRO { $$ = ICMP_UNREACH_ADMIN_PROHIBIT; } + | IPFY_ICMPC_HSTPRE { $$ = 14; } + | IPFY_ICMPC_CUTPRE { $$ = 15; } + ; + +opt: + IPFY_IPOPT_NOP { $$ = getoptbyvalue(IPOPT_NOP); } + | IPFY_IPOPT_RR { $$ = getoptbyvalue(IPOPT_RR); } + | IPFY_IPOPT_ZSU { $$ = getoptbyvalue(IPOPT_ZSU); } + | IPFY_IPOPT_MTUP { $$ = getoptbyvalue(IPOPT_MTUP); } + | IPFY_IPOPT_MTUR { $$ = getoptbyvalue(IPOPT_MTUR); } + | IPFY_IPOPT_ENCODE { $$ = getoptbyvalue(IPOPT_ENCODE); } + | IPFY_IPOPT_TS { $$ = getoptbyvalue(IPOPT_TS); } + | IPFY_IPOPT_TR { $$ = getoptbyvalue(IPOPT_TR); } + | IPFY_IPOPT_SEC { $$ = getoptbyvalue(IPOPT_SECURITY); } + | IPFY_IPOPT_LSRR { $$ = getoptbyvalue(IPOPT_LSRR); } + | IPFY_IPOPT_ESEC { $$ = getoptbyvalue(IPOPT_E_SEC); } + | IPFY_IPOPT_CIPSO { $$ = getoptbyvalue(IPOPT_CIPSO); } + | IPFY_IPOPT_CIPSO doi { $$ = getoptbyvalue(IPOPT_CIPSO); } + | IPFY_IPOPT_SATID { $$ = getoptbyvalue(IPOPT_SATID); } + | IPFY_IPOPT_SSRR { $$ = getoptbyvalue(IPOPT_SSRR); } + | IPFY_IPOPT_ADDEXT { $$ = getoptbyvalue(IPOPT_ADDEXT); } + | IPFY_IPOPT_VISA { $$ = getoptbyvalue(IPOPT_VISA); } + | IPFY_IPOPT_IMITD { $$ = getoptbyvalue(IPOPT_IMITD); } + | IPFY_IPOPT_EIP { $$ = getoptbyvalue(IPOPT_EIP); } + | IPFY_IPOPT_FINN { $$ = getoptbyvalue(IPOPT_FINN); } + | IPFY_IPOPT_DPS { $$ = getoptbyvalue(IPOPT_DPS); } + | IPFY_IPOPT_SDB { $$ = getoptbyvalue(IPOPT_SDB); } + | IPFY_IPOPT_NSAPA { $$ = getoptbyvalue(IPOPT_NSAPA); } + | IPFY_IPOPT_RTRALRT { $$ = getoptbyvalue(IPOPT_RTRALRT); } + | IPFY_IPOPT_UMP { $$ = getoptbyvalue(IPOPT_UMP); } + | setsecclass secname + { DOALL(fr->fr_mip.fi_secmsk |= $2; + if (fr->fr_family == AF_UNSPEC) { + fr->fr_family = AF_INET; + fr->fr_ip.fi_v = 4; + fr->fr_mip.fi_v = 0xf; + } else if (fr->fr_family != AF_INET) { + YYERROR; + } + if (!nowith) + fr->fr_ip.fi_secmsk |= $2;) + $$ = 0; + yyresetdict(); + } + ; + +setsecclass: + IPFY_SECCLASS { yysetdict(ipv4secwords); } + ; + +doi: IPFY_DOI YY_NUMBER { DOALL(fr->fr_doimask = 0xffffffff; \ + if (!nowith) \ + fr->fr_doi = $2;) } + | IPFY_DOI YY_HEX { DOALL(fr->fr_doimask = 0xffffffff; \ + if (!nowith) \ + fr->fr_doi = $2;) } + ; + +ipv6hdr: + IPFY_AH { $$ = getv6optbyvalue(IPPROTO_AH); } + | IPFY_IPV6OPT_DSTOPTS { $$ = getv6optbyvalue(IPPROTO_DSTOPTS); } + | IPFY_IPV6OPT_ESP { $$ = getv6optbyvalue(IPPROTO_ESP); } + | IPFY_IPV6OPT_HOPOPTS { $$ = getv6optbyvalue(IPPROTO_HOPOPTS); } + | IPFY_IPV6OPT_IPV6 { $$ = getv6optbyvalue(IPPROTO_IPV6); } + | IPFY_IPV6OPT_NONE { $$ = getv6optbyvalue(IPPROTO_NONE); } + | IPFY_IPV6OPT_ROUTING { $$ = getv6optbyvalue(IPPROTO_ROUTING); } + | IPFY_IPV6OPT_FRAG { $$ = getv6optbyvalue(IPPROTO_FRAGMENT); } + | IPFY_IPV6OPT_MOBILITY { $$ = getv6optbyvalue(IPPROTO_MOBILITY); } + ; + +level: IPFY_LEVEL { setsyslog(); } + ; + +loglevel: + priority { fr->fr_loglevel = LOG_LOCAL0|$1; } + | facility '.' priority { fr->fr_loglevel = $1 | $3; } + ; + +facility: + IPFY_FAC_KERN { $$ = LOG_KERN; } + | IPFY_FAC_USER { $$ = LOG_USER; } + | IPFY_FAC_MAIL { $$ = LOG_MAIL; } + | IPFY_FAC_DAEMON { $$ = LOG_DAEMON; } + | IPFY_FAC_AUTH { $$ = LOG_AUTH; } + | IPFY_FAC_SYSLOG { $$ = LOG_SYSLOG; } + | IPFY_FAC_LPR { $$ = LOG_LPR; } + | IPFY_FAC_NEWS { $$ = LOG_NEWS; } + | IPFY_FAC_UUCP { $$ = LOG_UUCP; } + | IPFY_FAC_CRON { $$ = LOG_CRON; } + | IPFY_FAC_FTP { $$ = LOG_FTP; } + | IPFY_FAC_AUTHPRIV { $$ = LOG_AUTHPRIV; } + | IPFY_FAC_AUDIT { $$ = LOG_AUDIT; } + | IPFY_FAC_LFMT { $$ = LOG_LFMT; } + | IPFY_FAC_LOCAL0 { $$ = LOG_LOCAL0; } + | IPFY_FAC_LOCAL1 { $$ = LOG_LOCAL1; } + | IPFY_FAC_LOCAL2 { $$ = LOG_LOCAL2; } + | IPFY_FAC_LOCAL3 { $$ = LOG_LOCAL3; } + | IPFY_FAC_LOCAL4 { $$ = LOG_LOCAL4; } + | IPFY_FAC_LOCAL5 { $$ = LOG_LOCAL5; } + | IPFY_FAC_LOCAL6 { $$ = LOG_LOCAL6; } + | IPFY_FAC_LOCAL7 { $$ = LOG_LOCAL7; } + | IPFY_FAC_SECURITY { $$ = LOG_SECURITY; } + ; + +priority: + IPFY_PRI_EMERG { $$ = LOG_EMERG; } + | IPFY_PRI_ALERT { $$ = LOG_ALERT; } + | IPFY_PRI_CRIT { $$ = LOG_CRIT; } + | IPFY_PRI_ERR { $$ = LOG_ERR; } + | IPFY_PRI_WARN { $$ = LOG_WARNING; } + | IPFY_PRI_NOTICE { $$ = LOG_NOTICE; } + | IPFY_PRI_INFO { $$ = LOG_INFO; } + | IPFY_PRI_DEBUG { $$ = LOG_DEBUG; } + ; + +compare: + YY_CMP_EQ { $$ = FR_EQUAL; } + | YY_CMP_NE { $$ = FR_NEQUAL; } + | YY_CMP_LT { $$ = FR_LESST; } + | YY_CMP_LE { $$ = FR_LESSTE; } + | YY_CMP_GT { $$ = FR_GREATERT; } + | YY_CMP_GE { $$ = FR_GREATERTE; } + ; + +range: YY_RANGE_IN { $$ = FR_INRANGE; } + | YY_RANGE_OUT { $$ = FR_OUTRANGE; } + | ':' { $$ = FR_INCRANGE; } + ; + +servicename: + YY_STR { $$ = $1; } + ; + +interfacename: name { $$ = $1; } + | name ':' YY_NUMBER + { $$ = $1; + fprintf(stderr, "%d: Logical interface %s:%d unsupported, " + "use the physical interface %s instead.\n", + yylineNum, $1, $3, $1); + } + ; + +name: YY_STR { $$ = $1; } + | '-' { $$ = strdup("-"); } + ; + +ipv4_16: + YY_NUMBER '.' YY_NUMBER + { if ($1 > 255 || $3 > 255) { + yyerror("Invalid octet string for IP address"); + return 0; + } + $$.s_addr = ($1 << 24) | ($3 << 16); + $$.s_addr = htonl($$.s_addr); + } + ; + +ipv4_24: + ipv4_16 '.' YY_NUMBER + { if ($3 > 255) { + yyerror("Invalid octet string for IP address"); + return 0; + } + $$.s_addr |= htonl($3 << 8); + } + ; + +ipv4: ipv4_24 '.' YY_NUMBER + { if ($3 > 255) { + yyerror("Invalid octet string for IP address"); + return 0; + } + $$.s_addr |= htonl($3); + } + | ipv4_24 + | ipv4_16 + ; + +%% + + +static struct wordtab ipfwords[] = { + { "age", IPFY_AGE }, + { "ah", IPFY_AH }, + { "all", IPFY_ALL }, + { "and", IPFY_AND }, + { "auth", IPFY_AUTH }, + { "bad", IPFY_BAD }, + { "bad-nat", IPFY_BADNAT }, + { "bad-src", IPFY_BADSRC }, + { "bcast", IPFY_BROADCAST }, + { "block", IPFY_BLOCK }, + { "body", IPFY_BODY }, + { "bpf-v4", IPFY_BPFV4 }, +#ifdef USE_INET6 + { "bpf-v6", IPFY_BPFV6 }, +#endif + { "call", IPFY_CALL }, + { "code", IPFY_ICMPCODE }, + { "comment", IPFY_COMMENT }, + { "count", IPFY_COUNT }, + { "decapsulate", IPFY_DECAPS }, + { "dstlist", IPFY_DSTLIST }, + { "doi", IPFY_DOI }, + { "dup-to", IPFY_DUPTO }, + { "eq", YY_CMP_EQ }, + { "esp", IPFY_ESP }, + { "exp", IPFY_IPFEXPR }, + { "family", IPFY_FAMILY }, + { "fastroute", IPFY_FROUTE }, + { "first", IPFY_FIRST }, + { "flags", IPFY_FLAGS }, + { "frag", IPFY_FRAG }, + { "frag-body", IPFY_FRAGBODY }, + { "frags", IPFY_FRAGS }, + { "from", IPFY_FROM }, + { "ge", YY_CMP_GE }, + { "group", IPFY_GROUP }, + { "gt", YY_CMP_GT }, + { "head", IPFY_HEAD }, + { "icmp", IPFY_ICMP }, + { "icmp-head", IPFY_ICMPHEAD }, + { "icmp-type", IPFY_ICMPTYPE }, + { "in", IPFY_IN }, + { "in-via", IPFY_INVIA }, + { "inet", IPFY_INET }, + { "inet6", IPFY_INET6 }, + { "ipopt", IPFY_IPOPTS }, + { "ipopts", IPFY_IPOPTS }, + { "keep", IPFY_KEEP }, + { "l5-as", IPFY_L5AS }, + { "le", YY_CMP_LE }, + { "level", IPFY_LEVEL }, + { "limit", IPFY_LIMIT }, + { "log", IPFY_LOG }, + { "loose", IPFY_LOOSE }, + { "lowttl", IPFY_LOWTTL }, + { "lt", YY_CMP_LT }, + { "mask", IPFY_MASK }, + { "match-tag", IPFY_MATCHTAG }, + { "max-per-src", IPFY_MAX_PER_SRC }, + { "max-srcs", IPFY_MAX_SRCS }, + { "mbcast", IPFY_MBCAST }, + { "mcast", IPFY_MULTICAST }, + { "multicast", IPFY_MULTICAST }, + { "nat", IPFY_NAT }, + { "ne", YY_CMP_NE }, + { "net", IPFY_NETWORK }, + { "newisn", IPFY_NEWISN }, + { "no", IPFY_NO }, + { "no-icmp-err", IPFY_NOICMPERR }, + { "nolog", IPFY_NOLOG }, + { "nomatch", IPFY_NOMATCH }, + { "now", IPFY_NOW }, + { "not", IPFY_NOT }, + { "oow", IPFY_OOW }, + { "on", IPFY_ON }, + { "opt", IPFY_OPT }, + { "or-block", IPFY_ORBLOCK }, + { "out", IPFY_OUT }, + { "out-via", IPFY_OUTVIA }, + { "pass", IPFY_PASS }, + { "port", IPFY_PORT }, + { "pps", IPFY_PPS }, + { "preauth", IPFY_PREAUTH }, + { "proto", IPFY_PROTO }, + { "quick", IPFY_QUICK }, + { "reply-to", IPFY_REPLY_TO }, + { "return-icmp", IPFY_RETICMP }, + { "return-icmp-as-dest", IPFY_RETICMPASDST }, + { "return-rst", IPFY_RETRST }, + { "route-to", IPFY_ROUTETO }, + { "rule-ttl", IPFY_RULETTL }, + { "rpc", IPFY_RPC }, + { "sec-class", IPFY_SECCLASS }, + { "set", IPFY_SET }, + { "set-tag", IPFY_SETTAG }, + { "skip", IPFY_SKIP }, + { "short", IPFY_SHORT }, + { "state", IPFY_STATE }, + { "state-age", IPFY_AGE }, + { "strict", IPFY_STRICT }, + { "sync", IPFY_SYNC }, + { "tcp", IPFY_TCP }, + { "tcp-udp", IPFY_TCPUDP }, + { "tos", IPFY_TOS }, + { "to", IPFY_TO }, + { "ttl", IPFY_TTL }, + { "udp", IPFY_UDP }, + { "v6hdr", IPFY_V6HDR }, + { "v6hdrs", IPFY_V6HDRS }, + { "with", IPFY_WITH }, + { NULL, 0 } +}; + +static struct wordtab addrwords[] = { + { "any", IPFY_ANY }, + { "hash", IPFY_HASH }, + { "pool", IPFY_POOL }, + { NULL, 0 } +}; + +static struct wordtab maskwords[] = { + { "broadcast", IPFY_BROADCAST }, + { "netmasked", IPFY_NETMASKED }, + { "network", IPFY_NETWORK }, + { "peer", IPFY_PEER }, + { NULL, 0 } +}; + +static struct wordtab icmpcodewords[] = { + { "cutoff-preced", IPFY_ICMPC_CUTPRE }, + { "filter-prohib", IPFY_ICMPC_FLTPRO }, + { "isolate", IPFY_ICMPC_ISOLATE }, + { "needfrag", IPFY_ICMPC_NEEDF }, + { "net-prohib", IPFY_ICMPC_NETPRO }, + { "net-tos", IPFY_ICMPC_NETTOS }, + { "host-preced", IPFY_ICMPC_HSTPRE }, + { "host-prohib", IPFY_ICMPC_HSTPRO }, + { "host-tos", IPFY_ICMPC_HSTTOS }, + { "host-unk", IPFY_ICMPC_HSTUNK }, + { "host-unr", IPFY_ICMPC_HSTUNR }, + { "net-unk", IPFY_ICMPC_NETUNK }, + { "net-unr", IPFY_ICMPC_NETUNR }, + { "port-unr", IPFY_ICMPC_PORUNR }, + { "proto-unr", IPFY_ICMPC_PROUNR }, + { "srcfail", IPFY_ICMPC_SRCFAIL }, + { NULL, 0 }, +}; + +static struct wordtab ipv4optwords[] = { + { "addext", IPFY_IPOPT_ADDEXT }, + { "cipso", IPFY_IPOPT_CIPSO }, + { "dps", IPFY_IPOPT_DPS }, + { "e-sec", IPFY_IPOPT_ESEC }, + { "eip", IPFY_IPOPT_EIP }, + { "encode", IPFY_IPOPT_ENCODE }, + { "finn", IPFY_IPOPT_FINN }, + { "imitd", IPFY_IPOPT_IMITD }, + { "lsrr", IPFY_IPOPT_LSRR }, + { "mtup", IPFY_IPOPT_MTUP }, + { "mtur", IPFY_IPOPT_MTUR }, + { "nop", IPFY_IPOPT_NOP }, + { "nsapa", IPFY_IPOPT_NSAPA }, + { "rr", IPFY_IPOPT_RR }, + { "rtralrt", IPFY_IPOPT_RTRALRT }, + { "satid", IPFY_IPOPT_SATID }, + { "sdb", IPFY_IPOPT_SDB }, + { "sec", IPFY_IPOPT_SEC }, + { "ssrr", IPFY_IPOPT_SSRR }, + { "tr", IPFY_IPOPT_TR }, + { "ts", IPFY_IPOPT_TS }, + { "ump", IPFY_IPOPT_UMP }, + { "visa", IPFY_IPOPT_VISA }, + { "zsu", IPFY_IPOPT_ZSU }, + { NULL, 0 }, +}; + +static struct wordtab ipv4secwords[] = { + { "confid", IPFY_SEC_CONF }, + { "reserv-1", IPFY_SEC_RSV1 }, + { "reserv-2", IPFY_SEC_RSV2 }, + { "reserv-3", IPFY_SEC_RSV3 }, + { "reserv-4", IPFY_SEC_RSV4 }, + { "secret", IPFY_SEC_SEC }, + { "topsecret", IPFY_SEC_TS }, + { "unclass", IPFY_SEC_UNC }, + { NULL, 0 }, +}; + +static struct wordtab ipv6optwords[] = { + { "dstopts", IPFY_IPV6OPT_DSTOPTS }, + { "esp", IPFY_IPV6OPT_ESP }, + { "frag", IPFY_IPV6OPT_FRAG }, + { "hopopts", IPFY_IPV6OPT_HOPOPTS }, + { "ipv6", IPFY_IPV6OPT_IPV6 }, + { "mobility", IPFY_IPV6OPT_MOBILITY }, + { "none", IPFY_IPV6OPT_NONE }, + { "routing", IPFY_IPV6OPT_ROUTING }, + { NULL, 0 }, +}; + +static struct wordtab logwords[] = { + { "kern", IPFY_FAC_KERN }, + { "user", IPFY_FAC_USER }, + { "mail", IPFY_FAC_MAIL }, + { "daemon", IPFY_FAC_DAEMON }, + { "auth", IPFY_FAC_AUTH }, + { "syslog", IPFY_FAC_SYSLOG }, + { "lpr", IPFY_FAC_LPR }, + { "news", IPFY_FAC_NEWS }, + { "uucp", IPFY_FAC_UUCP }, + { "cron", IPFY_FAC_CRON }, + { "ftp", IPFY_FAC_FTP }, + { "authpriv", IPFY_FAC_AUTHPRIV }, + { "audit", IPFY_FAC_AUDIT }, + { "logalert", IPFY_FAC_LFMT }, + { "console", IPFY_FAC_CONSOLE }, + { "security", IPFY_FAC_SECURITY }, + { "local0", IPFY_FAC_LOCAL0 }, + { "local1", IPFY_FAC_LOCAL1 }, + { "local2", IPFY_FAC_LOCAL2 }, + { "local3", IPFY_FAC_LOCAL3 }, + { "local4", IPFY_FAC_LOCAL4 }, + { "local5", IPFY_FAC_LOCAL5 }, + { "local6", IPFY_FAC_LOCAL6 }, + { "local7", IPFY_FAC_LOCAL7 }, + { "emerg", IPFY_PRI_EMERG }, + { "alert", IPFY_PRI_ALERT }, + { "crit", IPFY_PRI_CRIT }, + { "err", IPFY_PRI_ERR }, + { "warn", IPFY_PRI_WARN }, + { "notice", IPFY_PRI_NOTICE }, + { "info", IPFY_PRI_INFO }, + { "debug", IPFY_PRI_DEBUG }, + { NULL, 0 }, +}; + + + + +int ipf_parsefile(fd, addfunc, iocfuncs, filename) +int fd; +addfunc_t addfunc; +ioctlfunc_t *iocfuncs; +char *filename; +{ + FILE *fp = NULL; + char *s; + + yylineNum = 1; + yysettab(ipfwords); + + s = getenv("YYDEBUG"); + if (s != NULL) + yydebug = atoi(s); + else + yydebug = 0; + + if (strcmp(filename, "-")) { + fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr, "fopen(%s) failed: %s\n", filename, + STRERROR(errno)); + return -1; + } + } else + fp = stdin; + + while (ipf_parsesome(fd, addfunc, iocfuncs, fp) == 1) + ; + if (fp != NULL) + fclose(fp); + return 0; +} + + +int ipf_parsesome(fd, addfunc, iocfuncs, fp) +int fd; +addfunc_t addfunc; +ioctlfunc_t *iocfuncs; +FILE *fp; +{ + char *s; + int i; + + ipffd = fd; + for (i = 0; i <= IPL_LOGMAX; i++) + ipfioctls[i] = iocfuncs[i]; + ipfaddfunc = addfunc; + + if (feof(fp)) + return 0; + i = fgetc(fp); + if (i == EOF) + return 0; + if (ungetc(i, fp) == 0) + return 0; + if (feof(fp)) + return 0; + s = getenv("YYDEBUG"); + if (s != NULL) + yydebug = atoi(s); + else + yydebug = 0; + + yyin = fp; + yyparse(); + return 1; +} + + +static void newrule() +{ + frentry_t *frn; + + frn = allocfr(); + for (fr = frtop; fr != NULL && fr->fr_next != NULL; fr = fr->fr_next) + ; + if (fr != NULL) { + fr->fr_next = frn; + frn->fr_pnext = &fr->fr_next; + } + if (frtop == NULL) { + frtop = frn; + frn->fr_pnext = &frtop; + } + fr = frn; + frc = frn; + fr->fr_loglevel = 0xffff; + fr->fr_isc = (void *)-1; + fr->fr_logtag = FR_NOLOGTAG; + fr->fr_type = FR_T_NONE; + fr->fr_flineno = yylineNum; + + if (use_inet6 == 1) + fr->fr_family = AF_INET6; + else if (use_inet6 == -1) + fr->fr_family = AF_INET; + + nrules = 1; +} + + +static void setipftype() +{ + for (fr = frc; fr != NULL; fr = fr->fr_next) { + if (fr->fr_type == FR_T_NONE) { + fr->fr_type = FR_T_IPF; + fr->fr_data = (void *)calloc(sizeof(fripf_t), 1); + fr->fr_dsize = sizeof(fripf_t); + fr->fr_family = frc->fr_family; + if (fr->fr_family == AF_INET) { + fr->fr_ip.fi_v = 4; + } + else if (fr->fr_family == AF_INET6) { + fr->fr_ip.fi_v = 6; + } + fr->fr_mip.fi_v = 0xf; + fr->fr_ipf->fri_sifpidx = -1; + fr->fr_ipf->fri_difpidx = -1; + } + if (fr->fr_type != FR_T_IPF) { + fprintf(stderr, "IPF Type not set\n"); + } + } +} + + +static frentry_t *addrule() +{ + frentry_t *f, *f1, *f2; + int count; + + for (f2 = frc; f2->fr_next != NULL; f2 = f2->fr_next) + ; + + count = nrules; + f = f2; + for (f1 = frc; count > 0; count--, f1 = f1->fr_next) { + f->fr_next = allocfr(); + if (f->fr_next == NULL) + return NULL; + f->fr_next->fr_pnext = &f->fr_next; + added++; + f = f->fr_next; + *f = *f1; + f->fr_next = NULL; + if (f->fr_caddr != NULL) { + f->fr_caddr = malloc(f->fr_dsize); + bcopy(f1->fr_caddr, f->fr_caddr, f->fr_dsize); + } + } + + return f2->fr_next; +} + + +static int +lookuphost(name, addrp) + char *name; + i6addr_t *addrp; +{ + int i; + + hashed = 0; + pooled = 0; + dynamic = -1; + + for (i = 0; i < 4; i++) { + if (fr->fr_ifnames[i] == -1) + continue; + if (strcmp(name, fr->fr_names + fr->fr_ifnames[i]) == 0) { + ifpflag = FRI_DYNAMIC; + dynamic = addname(&fr, name); + return 1; + } + } + + if (gethost(AF_INET, name, addrp) == -1) { + fprintf(stderr, "unknown name \"%s\"\n", name); + return -1; + } + return 0; +} + + +static void dobpf(v, phrase) +int v; +char *phrase; +{ +#ifdef IPFILTER_BPF + struct bpf_program bpf; + struct pcap *p; +#endif + fakebpf_t *fb; + u_32_t l; + char *s; + int i; + + for (fr = frc; fr != NULL; fr = fr->fr_next) { + if (fr->fr_type != FR_T_NONE) { + fprintf(stderr, "cannot mix IPF and BPF matching\n"); + return; + } + fr->fr_family = vtof(v); + fr->fr_type = FR_T_BPFOPC; + + if (!strncmp(phrase, "0x", 2)) { + fb = malloc(sizeof(fakebpf_t)); + + for (i = 0, s = strtok(phrase, " \r\n\t"); s != NULL; + s = strtok(NULL, " \r\n\t"), i++) { + fb = reallocarray(fb, i / 4 + 1, sizeof(*fb)); + if (fb == NULL) { + warnx("memory allocation error at %d in %s in %s", __LINE__, __FUNCTION__, __FILE__); + abort(); + } + l = (u_32_t)strtol(s, NULL, 0); + switch (i & 3) + { + case 0 : + fb[i / 4].fb_c = l & 0xffff; + break; + case 1 : + fb[i / 4].fb_t = l & 0xff; + break; + case 2 : + fb[i / 4].fb_f = l & 0xff; + break; + case 3 : + fb[i / 4].fb_k = l; + break; + } + } + if ((i & 3) != 0) { + fprintf(stderr, + "Odd number of bytes in BPF code\n"); + exit(1); + } + i--; + fr->fr_dsize = (i / 4 + 1) * sizeof(*fb); + fr->fr_data = fb; + return; + } + +#ifdef IPFILTER_BPF + bzero((char *)&bpf, sizeof(bpf)); + p = pcap_open_dead(DLT_RAW, 1); + if (!p) { + fprintf(stderr, "pcap_open_dead failed\n"); + return; + } + + if (pcap_compile(p, &bpf, phrase, 1, 0xffffffff)) { + pcap_perror(p, "ipf"); + pcap_close(p); + fprintf(stderr, "pcap parsing failed (%s)\n", phrase); + return; + } + pcap_close(p); + + fr->fr_dsize = bpf.bf_len * sizeof(struct bpf_insn); + fr->fr_data = malloc(fr->fr_dsize); + bcopy((char *)bpf.bf_insns, fr->fr_data, fr->fr_dsize); + if (!bpf_validate(fr->fr_data, bpf.bf_len)) { + fprintf(stderr, "BPF validation failed\n"); + return; + } +#endif + } + +#ifdef IPFILTER_BPF + if (opts & OPT_DEBUG) + bpf_dump(&bpf, 0); +#else + fprintf(stderr, "BPF filter expressions not supported\n"); + exit(1); +#endif +} + + +static void resetaddr() +{ + hashed = 0; + pooled = 0; + dynamic = -1; +} + + +static alist_t *newalist(ptr) +alist_t *ptr; +{ + alist_t *al; + + al = malloc(sizeof(*al)); + if (al == NULL) + return NULL; + al->al_not = 0; + al->al_next = ptr; + return al; +} + + +static int +makepool(list) + alist_t *list; +{ + ip_pool_node_t *n, *top; + ip_pool_t pool; + alist_t *a; + int num; + + if (list == NULL) + return 0; + top = calloc(1, sizeof(*top)); + if (top == NULL) + return 0; + + for (n = top, a = list; (n != NULL) && (a != NULL); a = a->al_next) { + if (use_inet6 == 1) { +#ifdef USE_INET6 + n->ipn_addr.adf_family = AF_INET6; + n->ipn_addr.adf_addr = a->al_i6addr; + n->ipn_addr.adf_len = offsetof(addrfamily_t, + adf_addr) + 16; + n->ipn_mask.adf_family = AF_INET6; + n->ipn_mask.adf_addr = a->al_i6mask; + n->ipn_mask.adf_len = offsetof(addrfamily_t, + adf_addr) + 16; + +#endif + } else { + n->ipn_addr.adf_family = AF_INET; + n->ipn_addr.adf_addr.in4.s_addr = a->al_1; + n->ipn_addr.adf_len = offsetof(addrfamily_t, + adf_addr) + 4; + n->ipn_mask.adf_family = AF_INET; + n->ipn_mask.adf_addr.in4.s_addr = a->al_2; + n->ipn_mask.adf_len = offsetof(addrfamily_t, + adf_addr) + 4; + } + n->ipn_info = a->al_not; + if (a->al_next != NULL) { + n->ipn_next = calloc(1, sizeof(*n)); + n = n->ipn_next; + } + } + + bzero((char *)&pool, sizeof(pool)); + pool.ipo_unit = IPL_LOGIPF; + pool.ipo_list = top; + num = load_pool(&pool, ipfioctls[IPL_LOGLOOKUP]); + + while ((n = top) != NULL) { + top = n->ipn_next; + free(n); + } + return num; +} + + +static u_int makehash(list) +alist_t *list; +{ + iphtent_t *n, *top; + iphtable_t iph; + alist_t *a; + int num; + + if (list == NULL) + return 0; + top = calloc(1, sizeof(*top)); + if (top == NULL) + return 0; + + for (n = top, a = list; (n != NULL) && (a != NULL); a = a->al_next) { + if (a->al_family == AF_INET6) { + n->ipe_family = AF_INET6; + n->ipe_addr = a->al_i6addr; + n->ipe_mask = a->al_i6mask; + } else { + n->ipe_family = AF_INET; + n->ipe_addr.in4_addr = a->al_1; + n->ipe_mask.in4_addr = a->al_2; + } + n->ipe_value = 0; + if (a->al_next != NULL) { + n->ipe_next = calloc(1, sizeof(*n)); + n = n->ipe_next; + } + } + + bzero((char *)&iph, sizeof(iph)); + iph.iph_unit = IPL_LOGIPF; + iph.iph_type = IPHASH_LOOKUP; + *iph.iph_name = '\0'; + + if (load_hash(&iph, top, ipfioctls[IPL_LOGLOOKUP]) == 0) + sscanf(iph.iph_name, "%u", &num); + else + num = 0; + + while ((n = top) != NULL) { + top = n->ipe_next; + free(n); + } + return num; +} + + +int ipf_addrule(fd, ioctlfunc, ptr) +int fd; +ioctlfunc_t ioctlfunc; +void *ptr; +{ + ioctlcmd_t add, del; + frentry_t *fr; + ipfobj_t obj; + + if (ptr == NULL) + return 0; + + fr = ptr; + add = 0; + del = 0; + + bzero((char *)&obj, sizeof(obj)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = fr->fr_size; + obj.ipfo_type = IPFOBJ_FRENTRY; + obj.ipfo_ptr = ptr; + + if ((opts & OPT_DONOTHING) != 0) + fd = -1; + + if (opts & OPT_ZERORULEST) { + add = SIOCZRLST; + } else if (opts & OPT_INACTIVE) { + add = (u_int)fr->fr_hits ? SIOCINIFR : + SIOCADIFR; + del = SIOCRMIFR; + } else { + add = (u_int)fr->fr_hits ? SIOCINAFR : + SIOCADAFR; + del = SIOCRMAFR; + } + + if ((opts & OPT_OUTQUE) != 0) + fr->fr_flags |= FR_OUTQUE; + if (fr->fr_hits) + fr->fr_hits--; + if ((opts & OPT_VERBOSE) != 0) + printfr(fr, ioctlfunc); + + if ((opts & OPT_DEBUG) != 0) { + binprint(fr, sizeof(*fr)); + if (fr->fr_data != NULL) + binprint(fr->fr_data, fr->fr_dsize); + } + + if ((opts & OPT_ZERORULEST) != 0) { + if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%d:ioctl(zero rule)", + fr->fr_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); + } + } else { +#ifdef USE_QUAD_T + printf("hits %qd bytes %qd ", + (long long)fr->fr_hits, + (long long)fr->fr_bytes); +#else + printf("hits %ld bytes %ld ", + fr->fr_hits, fr->fr_bytes); +#endif + printfr(fr, ioctlfunc); + } + } else if ((opts & OPT_REMOVE) != 0) { + if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%d:ioctl(delete rule)", + fr->fr_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); + } + } + } else { + if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%d:ioctl(add/insert rule)", + fr->fr_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); + } + } + } + return 0; +} + +static void setsyslog() +{ + yysetdict(logwords); + yybreakondot = 1; +} + + +static void unsetsyslog() +{ + yyresetdict(); + yybreakondot = 0; +} + + +static void fillgroup(fr) +frentry_t *fr; +{ + frentry_t *f; + + for (f = frold; f != NULL; f = f->fr_next) { + if (f->fr_grhead == -1 && fr->fr_group == -1) + break; + if (f->fr_grhead == -1 || fr->fr_group == -1) + continue; + if (strcmp(f->fr_names + f->fr_grhead, + fr->fr_names + fr->fr_group) == 0) + break; + } + + if (f == NULL) + return; + + /* + * Only copy down matching fields if the rules are of the same type + * and are of ipf type. The only fields that are copied are those + * that impact the rule parsing itself, eg. need for knowing what the + * protocol should be for rules with port comparisons in them. + */ + if (f->fr_type != fr->fr_type || f->fr_type != FR_T_IPF) + return; + + if (fr->fr_family == 0 && f->fr_family != 0) + fr->fr_family = f->fr_family; + + if (fr->fr_mproto == 0 && f->fr_mproto != 0) + fr->fr_mproto = f->fr_mproto; + if (fr->fr_proto == 0 && f->fr_proto != 0) + fr->fr_proto = f->fr_proto; + + if ((fr->fr_mproto == 0) && ((fr->fr_flx & FI_TCPUDP) == 0) && + ((f->fr_flx & FI_TCPUDP) != 0)) { + fr->fr_flx |= FI_TCPUDP; + fr->fr_mflx |= FI_TCPUDP; + } +} + + +static void doipfexpr(line) +char *line; +{ + int *array; + char *error; + + array = parseipfexpr(line, &error); + if (array == NULL) { + fprintf(stderr, "%s:", error); + yyerror("error parsing ipf matching expression"); + return; + } + + fr->fr_type = FR_T_IPFEXPR; + fr->fr_data = array; + fr->fr_dsize = array[0] * sizeof(*array); +} + + +static void do_tuneint(varname, value) +char *varname; +int value; +{ + char buffer[80]; + + strncpy(buffer, varname, 60); + buffer[59] = '\0'; + strcat(buffer, "="); + snprintf(buffer, sizeof(buffer), "%u", value); + ipf_dotuning(ipffd, buffer, ioctl); +} + + +static void do_tunestr(varname, value) +char *varname, *value; +{ + + if (!strcasecmp(value, "true")) { + do_tuneint(varname, 1); + } else if (!strcasecmp(value, "false")) { + do_tuneint(varname, 0); + } else { + yyerror("did not find true/false where expected"); + } +} + + +static void setifname(frp, idx, name) +frentry_t **frp; +int idx; +char *name; +{ + int pos; + + pos = addname(frp, name); + if (pos == -1) + return; + (*frp)->fr_ifnames[idx] = pos; +} + + +static int addname(frp, name) +frentry_t **frp; +char *name; +{ + frentry_t *f; + int nlen; + int pos; + + nlen = strlen(name) + 1; + f = realloc(*frp, (*frp)->fr_size + nlen); + if (*frp == frc) + frc = f; + *frp = f; + if (f == NULL) + return -1; + if (f->fr_pnext != NULL) + *f->fr_pnext = f; + f->fr_size += nlen; + pos = f->fr_namelen; + f->fr_namelen += nlen; + strcpy(f->fr_names + pos, name); + f->fr_names[f->fr_namelen] = '\0'; + return pos; +} + + +static frentry_t *allocfr() +{ + frentry_t *fr; + + fr = calloc(1, sizeof(*fr)); + if (fr != NULL) { + fr->fr_size = sizeof(*fr); + fr->fr_comment = -1; + fr->fr_group = -1; + fr->fr_grhead = -1; + fr->fr_icmphead = -1; + fr->fr_ifnames[0] = -1; + fr->fr_ifnames[1] = -1; + fr->fr_ifnames[2] = -1; + fr->fr_ifnames[3] = -1; + fr->fr_tif.fd_name = -1; + fr->fr_rif.fd_name = -1; + fr->fr_dif.fd_name = -1; + } + return fr; +} + + +static void setgroup(frp, name) +frentry_t **frp; +char *name; +{ + int pos; + + pos = addname(frp, name); + if (pos == -1) + return; + (*frp)->fr_group = pos; +} + + +static void setgrhead(frp, name) +frentry_t **frp; +char *name; +{ + int pos; + + pos = addname(frp, name); + if (pos == -1) + return; + (*frp)->fr_grhead = pos; +} + + +static void seticmphead(frp, name) +frentry_t **frp; +char *name; +{ + int pos; + + pos = addname(frp, name); + if (pos == -1) + return; + (*frp)->fr_icmphead = pos; +} + + +static void +build_dstaddr_af(fp, ptr) + frentry_t *fp; + void *ptr; +{ + struct ipp_s *ipp = ptr; + frentry_t *f = fp; + + if (f->fr_family != AF_UNSPEC && ipp->f == AF_UNSPEC) { + ipp->f = f->fr_family; + ipp->v = f->fr_ip.fi_v; + } + if (ipp->f == AF_INET) + ipp->v = 4; + else if (ipp->f == AF_INET6) + ipp->v = 6; + + for (; f != NULL; f = f->fr_next) { + f->fr_ip.fi_dst = ipp->a; + f->fr_mip.fi_dst = ipp->m; + f->fr_family = ipp->f; + f->fr_ip.fi_v = ipp->v; + f->fr_mip.fi_v = 0xf; + f->fr_datype = ipp->type; + if (ipp->ifpos != -1) + f->fr_ipf->fri_difpidx = ipp->ifpos; + } + fr = NULL; +} + + +static void +build_srcaddr_af(fp, ptr) + frentry_t *fp; + void *ptr; +{ + struct ipp_s *ipp = ptr; + frentry_t *f = fp; + + if (f->fr_family != AF_UNSPEC && ipp->f == AF_UNSPEC) { + ipp->f = f->fr_family; + ipp->v = f->fr_ip.fi_v; + } + if (ipp->f == AF_INET) + ipp->v = 4; + else if (ipp->f == AF_INET6) + ipp->v = 6; + + for (; f != NULL; f = f->fr_next) { + f->fr_ip.fi_src = ipp->a; + f->fr_mip.fi_src = ipp->m; + f->fr_family = ipp->f; + f->fr_ip.fi_v = ipp->v; + f->fr_mip.fi_v = 0xf; + f->fr_satype = ipp->type; + f->fr_ipf->fri_sifpidx = ipp->ifpos; + } + fr = NULL; +} diff --git a/sbin/ipf/common/ipmon.h b/sbin/ipf/common/ipmon.h new file mode 100644 index 000000000000..4807299c49d2 --- /dev/null +++ b/sbin/ipf/common/ipmon.h @@ -0,0 +1,142 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * @(#)ip_fil.h 1.35 6/5/96 + * $Id$ + */ + +typedef struct ipmon_msg_s { + int imm_msglen; + char *imm_msg; + int imm_dsize; + void *imm_data; + time_t imm_when; + int imm_loglevel; +} ipmon_msg_t; + +typedef void (*ims_destroy_func_t)(void *); +typedef void *(*ims_dup_func_t)(void *); +typedef int (*ims_match_func_t)(void *, void *); +typedef void *(*ims_parse_func_t)(char **); +typedef void (*ims_print_func_t)(void *); +typedef int (*ims_store_func_t)(void *, ipmon_msg_t *); + +typedef struct ipmon_saver_s { + char *ims_name; + ims_destroy_func_t ims_destroy; + ims_dup_func_t ims_dup; + ims_match_func_t ims_match; + ims_parse_func_t ims_parse; + ims_print_func_t ims_print; + ims_store_func_t ims_store; +} ipmon_saver_t; + +typedef struct ipmon_saver_int_s { + struct ipmon_saver_int_s *imsi_next; + ipmon_saver_t *imsi_stor; + void *imsi_handle; +} ipmon_saver_int_t; + +typedef struct ipmon_doing_s { + struct ipmon_doing_s *ipmd_next; + void *ipmd_token; + ipmon_saver_t *ipmd_saver; + /* + * ipmd_store is "cached" in this structure to avoid a double + * deref when doing saves.... + */ + int (*ipmd_store)(void *, ipmon_msg_t *); +} ipmon_doing_t; + + +typedef struct ipmon_action { + struct ipmon_action *ac_next; + int ac_mflag; /* collection of things to compare */ + int ac_dflag; /* flags to compliment the doing fields */ + int ac_logpri; + int ac_direction; + char ac_group[FR_GROUPLEN]; + char ac_nattag[16]; + u_32_t ac_logtag; + int ac_type; /* nat/state/ipf */ + int ac_proto; + int ac_rule; + int ac_packet; + int ac_second; + int ac_result; + u_32_t ac_sip; + u_32_t ac_smsk; + u_32_t ac_dip; + u_32_t ac_dmsk; + u_short ac_sport; + u_short ac_dport; + char *ac_iface; + /* + * used with ac_packet/ac_second + */ + struct timeval ac_last; + int ac_pktcnt; + /* + * What to do with matches + */ + ipmon_doing_t *ac_doing; +} ipmon_action_t; + +#define ac_lastsec ac_last.tv_sec +#define ac_lastusec ac_last.tv_usec + +/* + * Flags indicating what fields to do matching upon (ac_mflag). + */ +#define IPMAC_DIRECTION 0x0001 +#define IPMAC_DSTIP 0x0002 +#define IPMAC_DSTPORT 0x0004 +#define IPMAC_EVERY 0x0008 +#define IPMAC_GROUP 0x0010 +#define IPMAC_INTERFACE 0x0020 +#define IPMAC_LOGTAG 0x0040 +#define IPMAC_NATTAG 0x0080 +#define IPMAC_PROTOCOL 0x0100 +#define IPMAC_RESULT 0x0200 +#define IPMAC_RULE 0x0400 +#define IPMAC_SRCIP 0x0800 +#define IPMAC_SRCPORT 0x1000 +#define IPMAC_TYPE 0x2000 +#define IPMAC_WITH 0x4000 + +#define IPMR_BLOCK 1 +#define IPMR_PASS 2 +#define IPMR_NOMATCH 3 +#define IPMR_LOG 4 + +#define IPMON_SYSLOG 0x001 +#define IPMON_RESOLVE 0x002 +#define IPMON_HEXBODY 0x004 +#define IPMON_HEXHDR 0x010 +#define IPMON_TAIL 0x020 +#define IPMON_VERBOSE 0x040 +#define IPMON_NAT 0x080 +#define IPMON_STATE 0x100 +#define IPMON_FILTER 0x200 +#define IPMON_PORTNUM 0x400 +#define IPMON_LOGALL (IPMON_NAT|IPMON_STATE|IPMON_FILTER) +#define IPMON_LOGBODY 0x800 + +#define HOSTNAME_V4(a,b) hostname((a), 4, (u_32_t *)&(b)) + +#ifndef LOGFAC +#define LOGFAC LOG_LOCAL0 +#endif + +extern void dump_config(void); +extern int load_config(char *); +extern void unload_config(void); +extern void dumphex(FILE *, int, char *, int); +extern int check_action(char *, char *, int, int); +extern char *getword(int); +extern void *add_doing(ipmon_saver_t *); + diff --git a/sbin/ipf/common/ipt.h b/sbin/ipf/common/ipt.h new file mode 100644 index 000000000000..9a4d75a85ccb --- /dev/null +++ b/sbin/ipf/common/ipt.h @@ -0,0 +1,40 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#ifndef __IPT_H__ +#define __IPT_H__ + +#ifndef __P +# define P_DEF +# ifdef __STDC__ +# define __P(x) x +# else +# define __P(x) () +# endif +#endif + +#include <fcntl.h> + + +struct ipread { + int (*r_open)(char *); + int (*r_close)(void); + int (*r_readip)(mb_t *, char **, int *); + int r_flags; +}; + +#define R_DO_CKSUM 0x01 + +#ifdef P_DEF +# undef __P +# undef P_DEF +#endif + +#endif /* __IPT_H__ */ diff --git a/sbin/ipf/common/kmem.h b/sbin/ipf/common/kmem.h new file mode 100644 index 000000000000..c4b65ed63ce9 --- /dev/null +++ b/sbin/ipf/common/kmem.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * $Id$ + */ + +#ifndef __KMEM_H__ +#define __KMEM_H__ + +#ifndef __P +# define __P(x) x +#endif +extern int openkmem(char *, char *); +extern int kmemcpy(char *, long, int); +extern int kstrncpy(char *, long, int); + +#if defined(__NetBSD__) || defined(__OpenBSD) +# include <paths.h> +#endif + +#ifdef _PATH_KMEM +# define KMEM _PATH_KMEM +#else +# define KMEM "/dev/kmem" +#endif + +#endif /* __KMEM_H__ */ diff --git a/sbin/ipf/common/lexer.c b/sbin/ipf/common/lexer.c new file mode 100644 index 000000000000..2dc2c3e8fe8c --- /dev/null +++ b/sbin/ipf/common/lexer.c @@ -0,0 +1,735 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include <ctype.h> +#include "ipf.h" +#ifdef IPFILTER_SCAN +# include "netinet/ip_scan.h" +#endif +#include <sys/ioctl.h> +#include <syslog.h> +#ifdef TEST_LEXER +# define NO_YACC +union { + int num; + char *str; + struct in_addr ipa; + i6addr_t ip6; +} yylval; +#endif +#include "lexer.h" +#include "y.tab.h" + +FILE *yyin; + +#define ishex(c) (ISDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || \ + ((c) >= 'A' && (c) <= 'F')) +#define TOOLONG -3 + +extern int string_start; +extern int string_end; +extern char *string_val; +extern int pos; +extern int yydebug; + +char *yystr = NULL; +int yytext[YYBUFSIZ+1]; +char yychars[YYBUFSIZ+1]; +int yylineNum = 1; +int yypos = 0; +int yylast = -1; +int yydictfixed = 0; +int yyexpectaddr = 0; +int yybreakondot = 0; +int yyvarnext = 0; +int yytokentype = 0; +wordtab_t *yywordtab = NULL; +int yysavedepth = 0; +wordtab_t *yysavewords[30]; + + +static wordtab_t *yyfindkey(char *); +static int yygetc(int); +static void yyunputc(int); +static int yyswallow(int); +static char *yytexttostr(int, int); +static void yystrtotext(char *); +static char *yytexttochar(void); + +static int yygetc(docont) + int docont; +{ + int c; + + if (yypos < yylast) { + c = yytext[yypos++]; + if (c == '\n') + yylineNum++; + return c; + } + + if (yypos == YYBUFSIZ) + return TOOLONG; + + if (pos >= string_start && pos <= string_end) { + c = string_val[pos - string_start]; + yypos++; + } else { + c = fgetc(yyin); + if (docont && (c == '\\')) { + c = fgetc(yyin); + if (c == '\n') { + yylineNum++; + c = fgetc(yyin); + } + } + } + if (c == '\n') + yylineNum++; + yytext[yypos++] = c; + yylast = yypos; + yytext[yypos] = '\0'; + + return c; +} + + +static void yyunputc(c) + int c; +{ + if (c == '\n') + yylineNum--; + yytext[--yypos] = c; +} + + +static int yyswallow(last) + int last; +{ + int c; + + while (((c = yygetc(0)) > '\0') && (c != last)) + ; + + if (c != EOF) + yyunputc(c); + if (c == last) + return 0; + return -1; +} + + +static char *yytexttochar() +{ + int i; + + for (i = 0; i < yypos; i++) + yychars[i] = (char)(yytext[i] & 0xff); + yychars[i] = '\0'; + return yychars; +} + + +static void yystrtotext(str) + char *str; +{ + int len; + char *s; + + len = strlen(str); + if (len > YYBUFSIZ) + len = YYBUFSIZ; + + for (s = str; *s != '\0' && len > 0; s++, len--) + yytext[yylast++] = *s; + yytext[yylast] = '\0'; +} + + +static char *yytexttostr(offset, max) + int offset, max; +{ + char *str; + int i; + + if ((yytext[offset] == '\'' || yytext[offset] == '"') && + (yytext[offset] == yytext[offset + max - 1])) { + offset++; + max--; + } + + if (max > yylast) + max = yylast; + str = malloc(max + 1); + if (str != NULL) { + for (i = offset; i < max; i++) + str[i - offset] = (char)(yytext[i] & 0xff); + str[i - offset] = '\0'; + } + return str; +} + + +int yylex() +{ + static int prior = 0; + static int priornum = 0; + int c, n, isbuilding, rval, lnext, nokey = 0; + char *name; + int triedv6 = 0; + + isbuilding = 0; + lnext = 0; + rval = 0; + + if (yystr != NULL) { + free(yystr); + yystr = NULL; + } + +nextchar: + c = yygetc(0); + if (yydebug > 1) + printf("yygetc = (%x) %c [%*.*s]\n", + c, c, yypos, yypos, yytexttochar()); + + switch (c) + { + case '\n' : + lnext = 0; + nokey = 0; + case '\t' : + case '\r' : + case ' ' : + if (isbuilding == 1) { + yyunputc(c); + goto done; + } + if (yylast > yypos) { + bcopy(yytext + yypos, yytext, + sizeof(yytext[0]) * (yylast - yypos + 1)); + } + yylast -= yypos; + if (yyexpectaddr == 2) + yyexpectaddr = 0; + yypos = 0; + lnext = 0; + nokey = 0; + goto nextchar; + + case '\\' : + if (lnext == 0) { + lnext = 1; + if (yylast == yypos) { + yylast--; + yypos--; + } else + yypos--; + if (yypos == 0) + nokey = 1; + goto nextchar; + } + break; + } + + if (lnext == 1) { + lnext = 0; + if ((isbuilding == 0) && !ISALNUM(c)) { + prior = c; + return c; + } + goto nextchar; + } + + switch (c) + { + case '#' : + if (isbuilding == 1) { + yyunputc(c); + goto done; + } + yyswallow('\n'); + rval = YY_COMMENT; + goto done; + + case '$' : + if (isbuilding == 1) { + yyunputc(c); + goto done; + } + n = yygetc(0); + if (n == '{') { + if (yyswallow('}') == -1) { + rval = -2; + goto done; + } + (void) yygetc(0); + } else { + if (!ISALPHA(n)) { + yyunputc(n); + break; + } + do { + n = yygetc(1); + } while (ISALPHA(n) || ISDIGIT(n) || n == '_'); + yyunputc(n); + } + + name = yytexttostr(1, yypos); /* skip $ */ + + if (name != NULL) { + string_val = get_variable(name, NULL, yylineNum); + free(name); + if (string_val != NULL) { + name = yytexttostr(yypos, yylast); + if (name != NULL) { + yypos = 0; + yylast = 0; + yystrtotext(string_val); + yystrtotext(name); + free(string_val); + free(name); + goto nextchar; + } + free(string_val); + } + } + break; + + case '\'': + case '"' : + if (isbuilding == 1) { + goto done; + } + do { + n = yygetc(1); + if (n == EOF || n == TOOLONG) { + rval = -2; + goto done; + } + if (n == '\n') { + yyunputc(' '); + yypos++; + } + } while (n != c); + rval = YY_STR; + goto done; + /* NOTREACHED */ + + case EOF : + yylineNum = 1; + yypos = 0; + yylast = -1; + yyexpectaddr = 0; + yybreakondot = 0; + yyvarnext = 0; + yytokentype = 0; + if (yydebug) + fprintf(stderr, "reset at EOF\n"); + prior = 0; + return 0; + } + + if (strchr("=,/;{}()@", c) != NULL) { + if (isbuilding == 1) { + yyunputc(c); + goto done; + } + rval = c; + goto done; + } else if (c == '.') { + if (isbuilding == 0) { + rval = c; + goto done; + } + if (yybreakondot != 0) { + yyunputc(c); + goto done; + } + } + + switch (c) + { + case '-' : + n = yygetc(0); + if (n == '>') { + isbuilding = 1; + goto done; + } + yyunputc(n); + if (yyexpectaddr) { + if (isbuilding == 1) + yyunputc(c); + else + rval = '-'; + goto done; + } + if (isbuilding == 1) + break; + rval = '-'; + goto done; + + case '!' : + if (isbuilding == 1) { + yyunputc(c); + goto done; + } + n = yygetc(0); + if (n == '=') { + rval = YY_CMP_NE; + goto done; + } + yyunputc(n); + rval = '!'; + goto done; + + case '<' : + if (yyexpectaddr) + break; + if (isbuilding == 1) { + yyunputc(c); + goto done; + } + n = yygetc(0); + if (n == '=') { + rval = YY_CMP_LE; + goto done; + } + if (n == '>') { + rval = YY_RANGE_OUT; + goto done; + } + yyunputc(n); + rval = YY_CMP_LT; + goto done; + + case '>' : + if (yyexpectaddr) + break; + if (isbuilding == 1) { + yyunputc(c); + goto done; + } + n = yygetc(0); + if (n == '=') { + rval = YY_CMP_GE; + goto done; + } + if (n == '<') { + rval = YY_RANGE_IN; + goto done; + } + yyunputc(n); + rval = YY_CMP_GT; + goto done; + } + + /* + * Now for the reason this is here...IPv6 address parsing. + * The longest string we can expect is of this form: + * 0000:0000:0000:0000:0000:0000:000.000.000.000 + * not: + * 0000:0000:0000:0000:0000:0000:0000:0000 + */ +#ifdef USE_INET6 + if (yyexpectaddr != 0 && isbuilding == 0 && + (ishex(c) || isdigit(c) || c == ':')) { + char ipv6buf[45 + 1], *s, oc; + int start; + +buildipv6: + start = yypos; + s = ipv6buf; + oc = c; + + if (prior == YY_NUMBER && c == ':') { + snprintf(s, sizeof(s), "%d", priornum); + s += strlen(s); + } + + /* + * Perhaps we should implement stricter controls on what we + * swallow up here, but surely it would just be duplicating + * the code in inet_pton() anyway. + */ + do { + *s++ = c; + c = yygetc(1); + } while ((ishex(c) || c == ':' || c == '.') && + (s - ipv6buf < 46)); + yyunputc(c); + *s = '\0'; + + if (inet_pton(AF_INET6, ipv6buf, &yylval.ip6) == 1) { + rval = YY_IPV6; + yyexpectaddr = 0; + goto done; + } + yypos = start; + c = oc; + } +#endif + + if ((c == ':') && (rval != YY_IPV6) && (triedv6 == 0)) { +#ifdef USE_INET6 + yystr = yytexttostr(0, yypos - 1); + if (yystr != NULL) { + char *s; + + for (s = yystr; *s && ishex(*s); s++) + ; + if (!*s && *yystr) { + isbuilding = 0; + c = *yystr; + free(yystr); + triedv6 = 1; + yypos = 1; + goto buildipv6; + } + free(yystr); + } +#endif + if (isbuilding == 1) { + yyunputc(c); + goto done; + } + rval = ':'; + goto done; + } + + if (isbuilding == 0 && c == '0') { + n = yygetc(0); + if (n == 'x') { + do { + n = yygetc(1); + } while (ishex(n)); + yyunputc(n); + rval = YY_HEX; + goto done; + } + yyunputc(n); + } + + /* + * No negative numbers with leading - sign.. + */ + if (isbuilding == 0 && ISDIGIT(c)) { + do { + n = yygetc(1); + } while (ISDIGIT(n)); + yyunputc(n); + rval = YY_NUMBER; + goto done; + } + + isbuilding = 1; + goto nextchar; + +done: + yystr = yytexttostr(0, yypos); + + if (yydebug) + printf("isbuilding %d yyvarnext %d nokey %d fixed %d addr %d\n", + isbuilding, yyvarnext, nokey, yydictfixed, yyexpectaddr); + if (isbuilding == 1) { + wordtab_t *w; + + w = NULL; + isbuilding = 0; + + if ((yyvarnext == 0) && (nokey == 0)) { + w = yyfindkey(yystr); + if (w == NULL && yywordtab != NULL && !yydictfixed) { + yyresetdict(); + w = yyfindkey(yystr); + } + } else + yyvarnext = 0; + if (w != NULL) + rval = w->w_value; + else + rval = YY_STR; + } + + if (rval == YY_STR) { + if (yysavedepth > 0 && !yydictfixed) + yyresetdict(); + if (yyexpectaddr != 0) + yyexpectaddr = 0; + } + + yytokentype = rval; + + if (yydebug) + printf("lexed(%s) %d,%d,%d [%d,%d,%d] => %d @%d\n", + yystr, isbuilding, yyexpectaddr, yysavedepth, + string_start, string_end, pos, rval, yysavedepth); + + switch (rval) + { + case YY_NUMBER : + sscanf(yystr, "%u", &yylval.num); + break; + + case YY_HEX : + sscanf(yystr, "0x%x", (u_int *)&yylval.num); + break; + + case YY_STR : + yylval.str = strdup(yystr); + break; + + default : + break; + } + + if (yylast > 0) { + bcopy(yytext + yypos, yytext, + sizeof(yytext[0]) * (yylast - yypos + 1)); + yylast -= yypos; + yypos = 0; + } + + if (rval == YY_NUMBER) + priornum = yylval.num; + prior = rval; + return rval; +} + + +static wordtab_t *yyfindkey(key) + char *key; +{ + wordtab_t *w; + + if (yywordtab == NULL) + return NULL; + + for (w = yywordtab; w->w_word != 0; w++) + if (strcasecmp(key, w->w_word) == 0) + return w; + return NULL; +} + + +char *yykeytostr(num) + int num; +{ + wordtab_t *w; + + if (yywordtab == NULL) + return "<unknown>"; + + for (w = yywordtab; w->w_word; w++) + if (w->w_value == num) + return w->w_word; + return "<unknown>"; +} + + +wordtab_t *yysettab(words) + wordtab_t *words; +{ + wordtab_t *save; + + save = yywordtab; + yywordtab = words; + return save; +} + + +void yyerror(msg) + char *msg; +{ + char *txt, letter[2]; + int freetxt = 0; + + if (yytokentype < 256) { + letter[0] = yytokentype; + letter[1] = '\0'; + txt = letter; + } else if (yytokentype == YY_STR || yytokentype == YY_HEX || + yytokentype == YY_NUMBER) { + if (yystr == NULL) { + txt = yytexttostr(yypos, YYBUFSIZ); + freetxt = 1; + } else + txt = yystr; + } else { + txt = yykeytostr(yytokentype); + } + fprintf(stderr, "%s error at \"%s\", line %d\n", msg, txt, yylineNum); + if (freetxt == 1) + free(txt); + exit(1); +} + + +void yysetfixeddict(newdict) + wordtab_t *newdict; +{ + if (yydebug) + printf("yysetfixeddict(%lx)\n", (u_long)newdict); + + if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) { + fprintf(stderr, "%d: at maximum dictionary depth\n", + yylineNum); + return; + } + + yysavewords[yysavedepth++] = yysettab(newdict); + if (yydebug) + printf("yysavedepth++ => %d\n", yysavedepth); + yydictfixed = 1; +} + + +void yysetdict(newdict) + wordtab_t *newdict; +{ + if (yydebug) + printf("yysetdict(%lx)\n", (u_long)newdict); + + if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) { + fprintf(stderr, "%d: at maximum dictionary depth\n", + yylineNum); + return; + } + + yysavewords[yysavedepth++] = yysettab(newdict); + if (yydebug) + printf("yysavedepth++ => %d\n", yysavedepth); +} + +void yyresetdict() +{ + if (yydebug) + printf("yyresetdict(%d)\n", yysavedepth); + if (yysavedepth > 0) { + yysettab(yysavewords[--yysavedepth]); + if (yydebug) + printf("yysavedepth-- => %d\n", yysavedepth); + } + yydictfixed = 0; +} + + + +#ifdef TEST_LEXER +int main(argc, argv) + int argc; + char *argv[]; +{ + int n; + + yyin = stdin; + + while ((n = yylex()) != 0) + printf("%d.n = %d [%s] %d %d\n", + yylineNum, n, yystr, yypos, yylast); +} +#endif diff --git a/sbin/ipf/common/lexer.h b/sbin/ipf/common/lexer.h new file mode 100644 index 000000000000..cc200f1cad41 --- /dev/null +++ b/sbin/ipf/common/lexer.h @@ -0,0 +1,38 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#ifdef NO_YACC +#define YY_COMMENT 1000 +#define YY_CMP_NE 1001 +#define YY_CMP_LE 1002 +#define YY_RANGE_OUT 1003 +#define YY_CMP_GE 1004 +#define YY_RANGE_IN 1005 +#define YY_HEX 1006 +#define YY_NUMBER 1007 +#define YY_IPV6 1008 +#define YY_STR 1009 +#define YY_IPADDR 1010 +#endif + +#define YYBUFSIZ 8192 + +extern wordtab_t *yysettab(wordtab_t *); +extern void yysetdict(wordtab_t *); +extern void yysetfixeddict(wordtab_t *); +extern int yylex(void); +extern void yyerror(char *); +extern char *yykeytostr(int); +extern void yyresetdict(void); + +extern FILE *yyin; +extern int yylineNum; +extern int yyexpectaddr; +extern int yybreakondot; +extern int yyvarnext; + diff --git a/sbin/ipf/common/opts.h b/sbin/ipf/common/opts.h new file mode 100644 index 000000000000..17844e89ecfc --- /dev/null +++ b/sbin/ipf/common/opts.h @@ -0,0 +1,69 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#ifndef __OPTS_H__ +#define __OPTS_H__ + +#ifndef SOLARIS +# if defined(sun) && (defined(__svr4__) || defined(__SVR4)) +# define SOLARIS 1 +# else +# define SOLARIS 0 +# endif +#endif +#define OPT_REMOVE 0x000001 +#define OPT_DEBUG 0x000002 +#define OPT_AUTHSTATS 0x000004 +#define OPT_RAW 0x000008 +#define OPT_LOG 0x000010 +#define OPT_SHOWLIST 0x000020 +#define OPT_VERBOSE 0x000040 +#define OPT_DONOTHING 0x000080 +#define OPT_HITS 0x000100 +#define OPT_BRIEF 0x000200 +#define OPT_ACCNT 0x000400 +#define OPT_FRSTATES 0x000800 +#define OPT_SHOWLINENO 0x001000 +#define OPT_PRINTFR 0x002000 +#define OPT_OUTQUE FR_OUTQUE /* 0x4000 */ +#define OPT_INQUE FR_INQUE /* 0x8000 */ +#define OPT_ZERORULEST 0x010000 +#define OPT_SAVEOUT 0x020000 +#define OPT_IPSTATES 0x040000 +#define OPT_INACTIVE 0x080000 +#define OPT_NAT 0x100000 +#define OPT_GROUPS 0x200000 +#define OPT_STATETOP 0x400000 +#define OPT_FLUSH 0x800000 +#define OPT_CLEAR 0x1000000 +#define OPT_HEX 0x2000000 +#define OPT_ASCII 0x4000000 +#define OPT_NORESOLVE 0x8000000 +#define OPT_DONTOPEN 0x10000000 +#define OPT_PURGE 0x20000000 + +#define OPT_STAT OPT_FRSTATES +#define OPT_LIST OPT_SHOWLIST + + +#ifndef __P +# define __P(x) x +#endif + +#if defined(sun) && !SOLARIS +# define STRERROR(x) sys_errlist[x] +extern char *sys_errlist[]; +#else +# define STRERROR(x) strerror(x) +#endif + +extern int opts; + +#endif /* __OPTS_H__ */ diff --git a/sbin/ipf/common/pcap-ipf.h b/sbin/ipf/common/pcap-ipf.h new file mode 100644 index 000000000000..b856760eaa53 --- /dev/null +++ b/sbin/ipf/common/pcap-ipf.h @@ -0,0 +1,35 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +/* + * 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 TCPDUMP_MAGIC 0xa1b2c3d4 + +#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/sbin/ipf/ipf/bpf-ipf.h b/sbin/ipf/ipf/bpf-ipf.h new file mode 100644 index 000000000000..a114949de6e0 --- /dev/null +++ b/sbin/ipf/ipf/bpf-ipf.h @@ -0,0 +1,440 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bpf.h 7.1 (Berkeley) 5/7/91 + * + * @(#) $Header: /devel/CVS/IP-Filter/bpf-ipf.h,v 2.1 2002/10/26 12:14:26 darrenr Exp $ (LBL) + */ + +#ifndef BPF_MAJOR_VERSION + +#ifdef __cplusplus +extern "C" { +#endif + +/* BSD style release date */ +#define BPF_RELEASE 199606 + +typedef int bpf_int32; +typedef u_int bpf_u_int32; + +/* + * Alignment macros. BPF_WORDALIGN rounds up to the next + * even multiple of BPF_ALIGNMENT. + */ +#ifndef __NetBSD__ +#define BPF_ALIGNMENT sizeof(bpf_int32) +#else +#define BPF_ALIGNMENT sizeof(long) +#endif +#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) + +#define BPF_MAXINSNS 512 +#define BPF_MAXBUFSIZE 0x8000 +#define BPF_MINBUFSIZE 32 + +/* + * Structure for BIOCSETF. + */ +struct bpf_program { + u_int bf_len; + struct bpf_insn *bf_insns; +}; + +/* + * Struct returned by BIOCGSTATS. + */ +struct bpf_stat { + u_int bs_recv; /* number of packets received */ + u_int bs_drop; /* number of packets dropped */ +}; + +/* + * Struct return by BIOCVERSION. This represents the version number of + * the filter language described by the instruction encodings below. + * bpf understands a program iff kernel_major == filter_major && + * kernel_minor >= filter_minor, that is, if the value returned by the + * running kernel has the same major number and a minor number equal + * equal to or less than the filter being downloaded. Otherwise, the + * results are undefined, meaning an error may be returned or packets + * may be accepted haphazardly. + * It has nothing to do with the source code version. + */ +struct bpf_version { + u_short bv_major; + u_short bv_minor; +}; +/* Current version number of filter architecture. */ +#define BPF_MAJOR_VERSION 1 +#define BPF_MINOR_VERSION 1 + +/* + * BPF ioctls + * + * The first set is for compatibility with Sun's pcc style + * header files. If your using gcc, we assume that you + * have run fixincludes so the latter set should work. + */ +#if (defined(sun) || defined(ibm032)) && !defined(__GNUC__) +#define BIOCGBLEN _IOR(B,102, u_int) +#define BIOCSBLEN _IOWR(B,102, u_int) +#define BIOCSETF _IOW(B,103, struct bpf_program) +#define BIOCFLUSH _IO(B,104) +#define BIOCPROMISC _IO(B,105) +#define BIOCGDLT _IOR(B,106, u_int) +#define BIOCGETIF _IOR(B,107, struct ifreq) +#define BIOCSETIF _IOW(B,108, struct ifreq) +#define BIOCSRTIMEOUT _IOW(B,109, struct timeval) +#define BIOCGRTIMEOUT _IOR(B,110, struct timeval) +#define BIOCGSTATS _IOR(B,111, struct bpf_stat) +#define BIOCIMMEDIATE _IOW(B,112, u_int) +#define BIOCVERSION _IOR(B,113, struct bpf_version) +#define BIOCSTCPF _IOW(B,114, struct bpf_program) +#define BIOCSUDPF _IOW(B,115, struct bpf_program) +#else +#define BIOCGBLEN _IOR('B',102, u_int) +#define BIOCSBLEN _IOWR('B',102, u_int) +#define BIOCSETF _IOW('B',103, struct bpf_program) +#define BIOCFLUSH _IO('B',104) +#define BIOCPROMISC _IO('B',105) +#define BIOCGDLT _IOR('B',106, u_int) +#define BIOCGETIF _IOR('B',107, struct ifreq) +#define BIOCSETIF _IOW('B',108, struct ifreq) +#define BIOCSRTIMEOUT _IOW('B',109, struct timeval) +#define BIOCGRTIMEOUT _IOR('B',110, struct timeval) +#define BIOCGSTATS _IOR('B',111, struct bpf_stat) +#define BIOCIMMEDIATE _IOW('B',112, u_int) +#define BIOCVERSION _IOR('B',113, struct bpf_version) +#define BIOCSTCPF _IOW('B',114, struct bpf_program) +#define BIOCSUDPF _IOW('B',115, struct bpf_program) +#endif + +/* + * Structure prepended to each packet. + */ +struct bpf_hdr { + struct timeval bh_tstamp; /* time stamp */ + bpf_u_int32 bh_caplen; /* length of captured portion */ + bpf_u_int32 bh_datalen; /* original length of packet */ + u_short bh_hdrlen; /* length of bpf header (this struct + plus alignment padding) */ +}; +/* + * Because the structure above is not a multiple of 4 bytes, some compilers + * will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't work. + * Only the kernel needs to know about it; applications use bh_hdrlen. + */ +#if defined(KERNEL) || defined(_KERNEL) +#define SIZEOF_BPF_HDR 18 +#endif + +/* + * Data-link level type codes. + */ + +/* + * These are the types that are the same on all platforms; on other + * platforms, a <net/bpf.h> should be supplied that defines the additional + * DLT_* codes appropriately for that platform (the BSDs, for example, + * should not just pick up this version of "bpf.h"; they should also define + * the additional DLT_* codes used by their kernels, as well as the values + * defined here - and, if the values they use for particular DLT_ types + * differ from those here, they should use their values, not the ones + * here). + */ +#define DLT_NULL 0 /* no link-layer encapsulation */ +#define DLT_EN10MB 1 /* Ethernet (10Mb) */ +#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */ +#define DLT_AX25 3 /* Amateur Radio AX.25 */ +#define DLT_PRONET 4 /* Proteon ProNET Token Ring */ +#define DLT_CHAOS 5 /* Chaos */ +#define DLT_IEEE802 6 /* IEEE 802 Networks */ +#define DLT_ARCNET 7 /* ARCNET */ +#define DLT_SLIP 8 /* Serial Line IP */ +#define DLT_PPP 9 /* Point-to-point Protocol */ +#define DLT_FDDI 10 /* FDDI */ + +/* + * These are values from the traditional libpcap "bpf.h". + * Ports of this to particular platforms should replace these definitions + * with the ones appropriate to that platform, if the values are + * different on that platform. + */ +#define DLT_ATM_RFC1483 11 /* LLC/SNAP encapsulated atm */ +#define DLT_RAW 12 /* raw IP */ + +/* + * These are values from BSD/OS's "bpf.h". + * These are not the same as the values from the traditional libpcap + * "bpf.h"; however, these values shouldn't be generated by any + * OS other than BSD/OS, so the correct values to use here are the + * BSD/OS values. + * + * Platforms that have already assigned these values to other + * DLT_ codes, however, should give these codes the values + * from that platform, so that programs that use these codes will + * continue to compile - even though they won't correctly read + * files of these types. + */ +#ifdef __NetBSD__ +#ifndef DLT_SLIP_BSDOS +#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */ +#endif +#else +#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */ +#endif + +#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */ + +/* + * These values are defined by NetBSD; other platforms should refrain from + * using them for other purposes, so that NetBSD savefiles with link + * types of 50 or 51 can be read as this type on all platforms. + */ +#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */ +#define DLT_PPP_ETHER 51 /* PPP over Ethernet */ + +/* + * Values between 100 and 103 are used in capture file headers as + * link-layer types corresponding to DLT_ types that differ + * between platforms; don't use those values for new DLT_ new types. + */ + +/* + * This value was defined by libpcap 0.5; platforms that have defined + * it with a different value should define it here with that value - + * a link type of 104 in a save file will be mapped to DLT_C_HDLC, + * whatever value that happens to be, so programs will correctly + * handle files with that link type regardless of the value of + * DLT_C_HDLC. + * + * The name DLT_C_HDLC was used by BSD/OS; we use that name for source + * compatibility with programs written for BSD/OS. + * + * libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well, + * for source compatibility with programs written for libpcap 0.5. + */ +#define DLT_C_HDLC 104 /* Cisco HDLC */ +#define DLT_CHDLC DLT_C_HDLC + +#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */ + +/* + * Values between 106 and 107 are used in capture file headers as + * link-layer types corresponding to DLT_ types that might differ + * between platforms; don't use those values for new DLT_ new types. + */ + +/* + * OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except + * that the AF_ type in the link-layer header is in network byte order. + * + * OpenBSD defines it as 12, but that collides with DLT_RAW, so we + * define it as 108 here. If OpenBSD picks up this file, it should + * define DLT_LOOP as 12 in its version, as per the comment above - + * and should not use 108 as a DLT_ value. + */ +#define DLT_LOOP 108 + +/* + * Values between 109 and 112 are used in capture file headers as + * link-layer types corresponding to DLT_ types that might differ + * between platforms; don't use those values for new DLT_ types + * other than the corresponding DLT_ types. + */ + +/* + * This is for Linux cooked sockets. + */ +#define DLT_LINUX_SLL 113 + +/* + * Apple LocalTalk hardware. + */ +#define DLT_LTALK 114 + +/* + * Acorn Econet. + */ +#define DLT_ECONET 115 + +/* + * Reserved for use with OpenBSD ipfilter. + */ +#define DLT_IPFILTER 116 + +/* + * Reserved for use in capture-file headers as a link-layer type + * corresponding to OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD, + * but that's DLT_LANE8023 in SuSE 6.3, so we can't use 17 for it + * in capture-file headers. + */ +#define DLT_PFLOG 117 + +/* + * Registered for Cisco-internal use. + */ +#define DLT_CISCO_IOS 118 + +/* + * Reserved for 802.11 cards using the Prism II chips, with a link-layer + * header including Prism monitor mode information plus an 802.11 + * header. + */ +#define DLT_PRISM_HEADER 119 + +/* + * Reserved for Aironet 802.11 cards, with an Aironet link-layer header + * (see Doug Ambrisko's FreeBSD patches). + */ +#define DLT_AIRONET_HEADER 120 + +/* + * Reserved for Siemens HiPath HDLC. + */ +#define DLT_HHDLC 121 + +/* + * Reserved for RFC 2625 IP-over-Fibre Channel, as per a request from + * Don Lee <donlee@cray.com>. + * + * This is not for use with raw Fibre Channel, where the link-layer + * header starts with a Fibre Channel frame header; it's for IP-over-FC, + * where the link-layer header starts with an RFC 2625 Network_Header + * field. + */ +#define DLT_IP_OVER_FC 122 + +/* + * The instruction encodings. + */ +/* instruction classes */ +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 +#define BPF_H 0x08 +#define BPF_B 0x10 +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +/* ret - BPF_K and BPF_X also apply */ +#define BPF_RVAL(code) ((code) & 0x18) +#define BPF_A 0x10 + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +#define BPF_TXA 0x80 + +/* + * The instruction data structure. + */ +struct bpf_insn { + u_short code; + u_char jt; + u_char jf; + bpf_int32 k; +}; + +/* + * Macros for insn array initializers. + */ +#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } +#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } + +#if defined(BSD) && (defined(KERNEL) || defined(_KERNEL)) +/* + * Systems based on non-BSD kernels don't have ifnet's (or they don't mean + * anything if it is in <net/if.h>) and won't work like this. + */ +extern void bpf_tap(struct ifnet *, u_char *, u_int); +extern void bpf_mtap(struct ifnet *, struct mbuf *); +extern void bpfattach(struct ifnet *, u_int, u_int); +extern void bpfilterattach(int); +#endif /* BSD && (_KERNEL || KERNEL) */ +extern int bpf_validate(struct bpf_insn *, int); +extern u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); + +/* + * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). + */ +#define BPF_MEMWORDS 16 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sbin/ipf/ipf/bpf_filter.c b/sbin/ipf/ipf/bpf_filter.c new file mode 100644 index 000000000000..85a38a88bf57 --- /dev/null +++ b/sbin/ipf/ipf/bpf_filter.c @@ -0,0 +1,595 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bpf.c 7.5 (Berkeley) 7/15/91 + */ + +#if !(defined(lint) || defined(KERNEL) || defined(_KERNEL)) +static const char rcsid[] = + "@(#) $Header: /devel/CVS/IP-Filter/bpf_filter.c,v 2.2.2.3 2006/10/03 11:25:56 darrenr Exp $ (LBL)"; +#endif + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <net/if.h> + +#include "netinet/ip_compat.h" +#include "bpf-ipf.h" + + +#if (defined(__hpux) || SOLARIS) && (defined(_KERNEL) || defined(KERNEL)) +# include <sys/sysmacros.h> +# include <sys/stream.h> +#endif + +#include "pcap-ipf.h" + +#if !defined(KERNEL) && !defined(_KERNEL) +#include <stdlib.h> +#endif + +#define int32 bpf_int32 +#define u_int32 bpf_u_int32 + +static int m_xword(mb_t *, int, int *); +static int m_xhalf(mb_t *, int, int *); + +#ifndef LBL_ALIGN +/* + * XXX - IA-64? If not, this probably won't work on Win64 IA-64 + * systems, unless LBL_ALIGN is defined elsewhere for them. + * XXX - SuperH? If not, this probably won't work on WinCE SuperH + * systems, unless LBL_ALIGN is defined elsewhere for them. + */ +#if defined(sparc) || defined(__sparc__) || defined(mips) || \ + defined(ibm032) || defined(__alpha) || defined(__hpux) || \ + defined(__arm__) +#define LBL_ALIGN +#endif +#endif + +#ifndef LBL_ALIGN + +#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p)) +#define EXTRACT_LONG(p) (ntohl(*(u_int32 *)p)) +#else +#define EXTRACT_SHORT(p)\ + ((u_short)\ + ((u_short)*((u_char *)p+0)<<8|\ + (u_short)*((u_char *)p+1)<<0)) +#define EXTRACT_LONG(p)\ + ((u_int32)*((u_char *)p+0)<<24|\ + (u_int32)*((u_char *)p+1)<<16|\ + (u_int32)*((u_char *)p+2)<<8|\ + (u_int32)*((u_char *)p+3)<<0) +#endif + +#define MINDEX(len, _m, _k) \ +{ \ + len = M_LEN(m); \ + while ((_k) >= len) { \ + (_k) -= len; \ + (_m) = (_m)->m_next; \ + if ((_m) == 0) \ + return 0; \ + len = M_LEN(m); \ + } \ +} + +static int +m_xword(m, k, err) + register mb_t *m; + register int k, *err; +{ + register int len; + register u_char *cp, *np; + register mb_t *m0; + + MINDEX(len, m, k); + cp = MTOD(m, u_char *) + k; + if (len - k >= 4) { + *err = 0; + return EXTRACT_LONG(cp); + } + m0 = m->m_next; + if (m0 == NULL || M_LEN(m0) + len - k < 4) + goto bad; + *err = 0; + np = MTOD(m0, u_char *); + switch (len - k) { + + case 1: + return (cp[0] << 24) | (np[0] << 16) | (np[1] << 8) | np[2]; + + case 2: + return (cp[0] << 24) | (cp[1] << 16) | (np[0] << 8) | np[1]; + + default: + return (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | np[0]; + } + bad: + *err = 1; + return 0; +} + +static int +m_xhalf(m, k, err) + register mb_t *m; + register int k, *err; +{ + register int len; + register u_char *cp; + register mb_t *m0; + + MINDEX(len, m, k); + cp = MTOD(m, u_char *) + k; + if (len - k >= 2) { + *err = 0; + return EXTRACT_SHORT(cp); + } + m0 = m->m_next; + if (m0 == NULL) + goto bad; + *err = 0; + return (cp[0] << 8) | MTOD(m0, u_char *)[0]; + bad: + *err = 1; + return 0; +} + +/* + * Execute the filter program starting at pc on the packet p + * wirelen is the length of the original packet + * buflen is the amount of data present + * For the kernel, p is assumed to be a pointer to an mbuf if buflen is 0, + * in all other cases, p is a pointer to a buffer and buflen is its size. + */ +u_int +bpf_filter(pc, p, wirelen, buflen) + register struct bpf_insn *pc; + register u_char *p; + u_int wirelen; + register u_int buflen; +{ + register u_int32 A, X; + register int k; + int32 mem[BPF_MEMWORDS]; + mb_t *m, *n; + int merr = 0; /* XXX: GCC */ + int len; + + if (buflen == 0) { + m = (mb_t *)p; + p = MTOD(m, u_char *); + buflen = M_LEN(m); + } else + m = NULL; + + if (pc == NULL) + /* + * No filter means accept all. + */ + return (u_int)-1; + A = 0; + X = 0; + --pc; + while (1) { + ++pc; + switch (pc->code) { + + default: + return 0; + case BPF_RET|BPF_K: + return (u_int)pc->k; + + case BPF_RET|BPF_A: + return (u_int)A; + + case BPF_LD|BPF_W|BPF_ABS: + k = pc->k; + if (k + sizeof(int32) > buflen) { + if (m == NULL) + return 0; + A = m_xword(m, k, &merr); + if (merr != 0) + return 0; + continue; + } + A = EXTRACT_LONG(&p[k]); + continue; + + case BPF_LD|BPF_H|BPF_ABS: + k = pc->k; + if (k + sizeof(short) > buflen) { + if (m == NULL) + return 0; + A = m_xhalf(m, k, &merr); + if (merr != 0) + return 0; + continue; + } + A = EXTRACT_SHORT(&p[k]); + continue; + + case BPF_LD|BPF_B|BPF_ABS: + k = pc->k; + if (k >= buflen) { + if (m == NULL) + return 0; + n = m; + MINDEX(len, n, k); + A = MTOD(n, u_char *)[k]; + continue; + } + A = p[k]; + continue; + + case BPF_LD|BPF_W|BPF_LEN: + A = wirelen; + continue; + + case BPF_LDX|BPF_W|BPF_LEN: + X = wirelen; + continue; + + case BPF_LD|BPF_W|BPF_IND: + k = X + pc->k; + if (k + sizeof(int32) > buflen) { + if (m == NULL) + return 0; + A = m_xword(m, k, &merr); + if (merr != 0) + return 0; + continue; + } + A = EXTRACT_LONG(&p[k]); + continue; + + case BPF_LD|BPF_H|BPF_IND: + k = X + pc->k; + if (k + sizeof(short) > buflen) { + if (m == NULL) + return 0; + A = m_xhalf(m, k, &merr); + if (merr != 0) + return 0; + continue; + } + A = EXTRACT_SHORT(&p[k]); + continue; + + case BPF_LD|BPF_B|BPF_IND: + k = X + pc->k; + if (k >= buflen) { + if (m == NULL) + return 0; + n = m; + MINDEX(len, n, k); + A = MTOD(n, u_char *)[k]; + continue; + } + A = p[k]; + continue; + + case BPF_LDX|BPF_MSH|BPF_B: + k = pc->k; + if (k >= buflen) { + if (m == NULL) + return 0; + n = m; + MINDEX(len, n, k); + X = (MTOD(n, char *)[k] & 0xf) << 2; + continue; + } + X = (p[pc->k] & 0xf) << 2; + continue; + + case BPF_LD|BPF_IMM: + A = pc->k; + continue; + + case BPF_LDX|BPF_IMM: + X = pc->k; + continue; + + case BPF_LD|BPF_MEM: + A = mem[pc->k]; + continue; + + case BPF_LDX|BPF_MEM: + X = mem[pc->k]; + continue; + + case BPF_ST: + mem[pc->k] = A; + continue; + + case BPF_STX: + mem[pc->k] = X; + continue; + + case BPF_JMP|BPF_JA: + pc += pc->k; + continue; + + case BPF_JMP|BPF_JGT|BPF_K: + pc += (A > pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_K: + pc += (A >= pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_K: + pc += (A == pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_K: + pc += (A & pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGT|BPF_X: + pc += (A > X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_X: + pc += (A >= X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_X: + pc += (A == X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_X: + pc += (A & X) ? pc->jt : pc->jf; + continue; + + case BPF_ALU|BPF_ADD|BPF_X: + A += X; + continue; + + case BPF_ALU|BPF_SUB|BPF_X: + A -= X; + continue; + + case BPF_ALU|BPF_MUL|BPF_X: + A *= X; + continue; + + case BPF_ALU|BPF_DIV|BPF_X: + if (X == 0) + return 0; + A /= X; + continue; + + case BPF_ALU|BPF_AND|BPF_X: + A &= X; + continue; + + case BPF_ALU|BPF_OR|BPF_X: + A |= X; + continue; + + case BPF_ALU|BPF_LSH|BPF_X: + A <<= X; + continue; + + case BPF_ALU|BPF_RSH|BPF_X: + A >>= X; + continue; + + case BPF_ALU|BPF_ADD|BPF_K: + A += pc->k; + continue; + + case BPF_ALU|BPF_SUB|BPF_K: + A -= pc->k; + continue; + + case BPF_ALU|BPF_MUL|BPF_K: + A *= pc->k; + continue; + + case BPF_ALU|BPF_DIV|BPF_K: + A /= pc->k; + continue; + + case BPF_ALU|BPF_AND|BPF_K: + A &= pc->k; + continue; + + case BPF_ALU|BPF_OR|BPF_K: + A |= pc->k; + continue; + + case BPF_ALU|BPF_LSH|BPF_K: + A <<= pc->k; + continue; + + case BPF_ALU|BPF_RSH|BPF_K: + A >>= pc->k; + continue; + + case BPF_ALU|BPF_NEG: + A = -A; + continue; + + case BPF_MISC|BPF_TAX: + X = A; + continue; + + case BPF_MISC|BPF_TXA: + A = X; + continue; + } + } +} + + +/* + * Return true if the 'fcode' is a valid filter program. + * The constraints are that each jump be forward and to a valid + * code, that memory accesses are within valid ranges (to the + * extent that this can be checked statically; loads of packet + * data have to be, and are, also checked at run time), and that + * the code terminates with either an accept or reject. + * + * The kernel needs to be able to verify an application's filter code. + * Otherwise, a bogus program could easily crash the system. + */ +int +bpf_validate(f, len) + struct bpf_insn *f; + int len; +{ + u_int i, from; + const struct bpf_insn *p; + + if (len == 0) + return 1; + + if (len < 1 || len > BPF_MAXINSNS) + return 0; + + for (i = 0; i < len; ++i) { + p = &f[i]; + switch (BPF_CLASS(p->code)) { + /* + * Check that memory operations use valid addresses. + */ + case BPF_LD: + case BPF_LDX: + switch (BPF_MODE(p->code)) { + case BPF_IMM: + break; + case BPF_ABS: + case BPF_IND: + case BPF_MSH: + /* + * More strict check with actual packet length + * is done runtime. + */ +#if 0 + if (p->k >= bpf_maxbufsize) + return 0; +#endif + break; + case BPF_MEM: + if (p->k >= BPF_MEMWORDS) + return 0; + break; + case BPF_LEN: + break; + default: + return 0; + } + break; + case BPF_ST: + case BPF_STX: + if (p->k >= BPF_MEMWORDS) + return 0; + break; + case BPF_ALU: + switch (BPF_OP(p->code)) { + case BPF_ADD: + case BPF_SUB: + case BPF_OR: + case BPF_AND: + case BPF_LSH: + case BPF_RSH: + case BPF_NEG: + break; + case BPF_DIV: + /* + * Check for constant division by 0. + */ + if (BPF_RVAL(p->code) == BPF_K && p->k == 0) + return 0; + default: + return 0; + } + break; + case BPF_JMP: + /* + * Check that jumps are within the code block, + * and that unconditional branches don't go + * backwards as a result of an overflow. + * Unconditional branches have a 32-bit offset, + * so they could overflow; we check to make + * sure they don't. Conditional branches have + * an 8-bit offset, and the from address is <= + * BPF_MAXINSNS, and we assume that BPF_MAXINSNS + * is sufficiently small that adding 255 to it + * won't overflow. + * + * We know that len is <= BPF_MAXINSNS, and we + * assume that BPF_MAXINSNS is < the maximum size + * of a u_int, so that i + 1 doesn't overflow. + */ + from = i + 1; + switch (BPF_OP(p->code)) { + case BPF_JA: + if (from + p->k < from || from + p->k >= len) + return 0; + break; + case BPF_JEQ: + case BPF_JGT: + case BPF_JGE: + case BPF_JSET: + if (from + p->jt >= len || from + p->jf >= len) + return 0; + break; + default: + return 0; + } + break; + case BPF_RET: + break; + case BPF_MISC: + break; + default: + return 0; + } + } + return BPF_CLASS(f[len - 1].code) == BPF_RET; +} diff --git a/sbin/ipf/ipf/ipf.4 b/sbin/ipf/ipf/ipf.4 new file mode 100644 index 000000000000..73a17a0cc8d3 --- /dev/null +++ b/sbin/ipf/ipf/ipf.4 @@ -0,0 +1,254 @@ +.\" $FreeBSD$ +.TH IPF 4 +.SH NAME +ipf \- packet filtering kernel interface +.SH SYNOPSIS +#include <netinet/ip_compat.h> +.br +#include <netinet/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 SIOCADDFR) + ioctl(fd, SIOCRMAFR, struct frentry **) (same as SIOCDELFR) + ioctl(fd, SIOCADIFR, struct frentry **) + ioctl(fd, SIOCRMIFR, struct frentry **) + ioctl(fd, SIOCINAFR, struct frentry **) + ioctl(fd, SIOCINIFR, struct frentry **) + ioctl(fd, SIOCSETFF, u_int *) + ioctl(fd, SIOGGETFF, u_int *) + ioctl(fd, SIOCGETFS, struct friostat **) + ioctl(fd, SIOCIPFFL, int *) + ioctl(fd, SIOCIPFFB, int *) + ioctl(fd, SIOCSWAPA, u_int *) + ioctl(fd, SIOCFRENB, u_int *) + ioctl(fd, SIOCFRSYN, u_int *) + ioctl(fd, SIOCFRZST, struct friostat **) + ioctl(fd, SIOCZRLST, struct frentry **) + ioctl(fd, SIOCAUTHW, struct fr_info **) + ioctl(fd, SIOCAUTHR, struct fr_info **) + ioctl(fd, SIOCATHST, struct fr_authstat **) +.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 (i.e., /dev/ipl). +.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; + u_short fr_group; /* group to which this rule belongs */ + u_short fr_grhead; /* group # which this rule starts */ + struct frentry *fr_grp; + int fr_ref; /* reference count - for grouping */ + void *fr_ifa; +#ifdef BSD + void *fr_oifa; +#endif + /* + * These are only incremented when a packet matches this rule and + * it is the last match + */ + U_QUAD_T fr_hits; + U_QUAD_T fr_bytes; + /* + * Fields after this may not change whilst in the kernel. + */ + struct fr_ip fr_ip; + struct fr_ip fr_mip; /* mask structure */ + + 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_32_t fr_flags; /* per-rule flags && options (see below) */ + u_short fr_skip; /* # of rules to skip */ + u_short fr_loglevel; /* syslog log facility + priority */ + int (*fr_func)(int, ip_t *, fr_info_t *)); + char fr_icode; /* return ICMP code */ + char fr_ifname[IFNAMSIZ]; +#ifdef BSD + char fr_oifname[IFNAMSIZ]; +#endif + struct frdest fr_tif; /* "to" interface */ + struct frdest fr_dif; /* duplicate packet interfaces */ +} frentry_t; +.fi +.PP +When adding a new rule, all unused fields (in the filter rule) should be +initialised to be zero. To insert a rule, at a particular position in the +filter list, the number of the rule which it is to be inserted before must +be put in the "fr_hits" field (the first rule is number 0). +.PP +Flags which are recognised in fr_flags: +.nf + + FR_BLOCK 0x000001 /* do not allow packet to pass */ + FR_PASS 0x000002 /* allow packet to pass */ + FR_OUTQUE 0x000004 /* outgoing packets */ + FR_INQUE 0x000008 /* ingoing packets */ + FR_LOG 0x000010 /* Log */ + FR_LOGB 0x000011 /* Log-fail */ + FR_LOGP 0x000012 /* Log-pass */ + FR_LOGBODY 0x000020 /* log the body of packets too */ + FR_LOGFIRST 0x000040 /* log only the first packet to match */ + FR_RETRST 0x000080 /* return a TCP RST packet if blocked */ + FR_RETICMP 0x000100 /* return an ICMP packet if blocked */ + FR_FAKEICMP 0x00180 /* Return ICMP unreachable with fake source */ + FR_NOMATCH 0x000200 /* no match occured */ + FR_ACCOUNT 0x000400 /* count packet bytes */ + FR_KEEPFRAG 0x000800 /* keep fragment information */ + FR_KEEPSTATE 0x001000 /* keep `connection' state information */ + FR_INACTIVE 0x002000 + FR_QUICK 0x004000 /* match & stop processing list */ + FR_FASTROUTE 0x008000 /* bypass normal routing */ + FR_CALLNOW 0x010000 /* call another function (fr_func) if matches */ + FR_DUP 0x020000 /* duplicate the packet */ + FR_LOGORBLOCK 0x040000 /* block the packet if it can't be logged */ + FR_NOTSRCIP 0x080000 /* not the src IP# */ + FR_NOTDSTIP 0x100000 /* not the dst IP# */ + FR_AUTH 0x200000 /* use authentication */ + FR_PREAUTH 0x400000 /* require preauthentication */ + +.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). This ioctl is also implemented for +\fB/dev/ipstate\fP and will flush all state tables entries if passed 0 +or just all those which are not established if passed 1. + +.IP "\fBGeneral Logging Flags\fP" 0 +There are two flags which can be set to log packets independently 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 0x10000000 + FF_LOGBLOCK 0x20000000 + FF_LOGNOMATCH 0x40000000 + FF_BLOCKNONIP 0x80000000 /* Solaris 2.x only */ +.fi +.IP SIOCGETFF 16 +Takes a pointer to an unsigned integer as the parameter. A copy of the +flags currently in used is copied to user space. +.IP "\fBFilter statistics\fP" 0 +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[2]; + struct frentry *f_fout[2]; + struct frentry *f_acctin[2]; + struct frentry *f_acctout[2]; + struct frentry *f_auth; + u_long f_froute[2]; + int f_active; /* 1 or 0 - active rule set */ + int f_defpass; /* default pass - from fr_pass */ + int f_running; /* 1 if running, else 0 */ + int f_logging; /* 1 if enabled, else 0 */ + char f_version[32]; /* version string */ +}; + +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_npkl; /* packets unmatched 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 */ + u_long fr_acct; /* packets for which counting was performed */ + u_long fr_bnfr; /* bad attempts to allocate fragment state */ + u_long fr_nfr; /* new fragment state kept */ + u_long fr_cfr; /* add new fragment state but complete pkt */ + u_long fr_bads; /* bad attempts to allocate packet state */ + u_long fr_ads; /* new packet state kept */ + u_long fr_chit; /* cached hit */ + u_long fr_pull[2]; /* good and bad pullup attempts */ +#if SOLARIS + u_long fr_notdata; /* PROTO/PCPROTO that have no data */ + u_long fr_nodata; /* mblks that have no data */ + 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 +}; +.fi +If we wanted to retrieve all the statistics and reset the counters back to +0, then the ioctl() call would be made to SIOCFRZST rather than SIOCGETFS. +In addition to the statistics above, each rule keeps a hit count, counting +both number of packets and bytes. To reset these counters for a rule, +load the various rule information into a frentry structure and call +SIOCZRLST. +.IP "Swapping Active lists" 0 +IP Filter supports two lists of rules for filtering and accounting: an +active list and an inactive list. This allows for large scale rule base +changes to be put in place atomically with otherwise minimal interruption. +Which of the two is active can be changed using the SIOCSWAPA ioctl. It +is important to note that no passed argument is recognised and that the +value returned is that of the list which is now inactive. +.br +.SH FILES +/dev/ipauth +.br +/dev/ipl +.br +/dev/ipnat +.br +/dev/ipstate +.SH SEE ALSO +ipl(4), ipnat(4), ipf(5), ipf(8), ipfstat(8) diff --git a/sbin/ipf/ipf/ipf.5 b/sbin/ipf/ipf/ipf.5 new file mode 100644 index 000000000000..8ef56493df5a --- /dev/null +++ b/sbin/ipf/ipf/ipf.5 @@ -0,0 +1,1698 @@ +.\" $FreeBSD$ +.TH IPF 5 +.SH NAME +ipf, ipf.conf \- IPFilter firewall rules file format +.SH DESCRIPTION +.PP +The ipf.conf file is used to specify rules for the firewall, packet +authentication and packet accounting components of IPFilter. To load rules +specified in the ipf.conf file, the ipf(8) program is used. +.PP +For use as a firewall, there are two important rule types: those that block +and drop packets (block rules) and those that allow packets through (pass +rules.) Accompanying the decision to apply is a collection of statements +that specify under what conditions the result is to be applied and how. +.PP +The simplest rules that can be used in ipf.conf are expressed like this: +.PP +.nf +block in all +pass out all +.fi +.PP +Each rule must contain at least the following three components +.RS +.IP * +a decision keyword (pass, block, etc.) +.IP * +the direction of the packet (in or out) +.IP * +address patterns or "all" to match any address information +.RE +.SS Long lines +.PP +For rules lines that are particularly long, it is possible to split +them over multiple lines implicity like this: +.PP +.nf +pass in on bgeo proto tcp from 1.1.1.1 port > 1000 + to 2.2.2.2 port < 5000 flags S keep state +.fi +.PP +or explicitly using the backslash ('\\') character: +.PP +.nf +pass in on bgeo proto tcp from 1.1.1.1 port > 1000 \\ + to 2.2.2.2 port < 5000 flags S keep state +.fi +.SS Comments +.PP +Comments in the ipf.conf file are indicated by the use of the '#' character. +This can either be at the start of the line, like this: +.PP +.nf +# Allow all ICMP packets in +pass in proto icmp from any to any +.fi +.PP +Or at the end of a like, like this: +.PP +.nf +pass in proto icmp from any to any # Allow all ICMP packets in +.fi +.SH Firewall rules +.PP +This section goes into detail on how to construct firewall rules that +are placed in the ipf.conf file. +.PP +It is beyond the scope of this document to describe what makes a good +firewall rule set or which packets should be blocked or allowed in. +Some suggestions will be provided but further reading is expected to +fully understand what is safe and unsafe to allow in/out. +.SS Filter rule keywords +.PP +The first word found in any filter rule describes what the eventual outcome +of a packet that matches it will be. Descriptions of the many and various +sections that can be used to match on the contents of packet headers will +follow on below. +.PP +The complete list of keywords, along with what they do is as follows: +.RS +.HP +pass +rules that match a packet indicate to ipfilter that it should be +allowed to continue on in the direction it is flowing. +.HP +block +rules are used when it is desirable to prevent a packet from going +any further. Packets that are blocked on the "in" side are never seen by +TCP/IP and those that are blocked going "out" are never seen on the wire. +.HP +log +when IPFilter successfully matches a packet against a log rule a log +record is generated and made available for ipmon(8) to read. These rules +have no impact on whether or not a packet is allowed through or not. +So if a packet first matched a block rule and then matched a log rule, +the status of the packet after the log rule is that it will still be +blocked. +.HP +count +rules provide the administrator with the ability to count packets and +bytes that match the criteria laid out in the configuration file. +The count rules are applied after NAT and filter rules on the inbound +path. For outbound packets, count rules are applied before NAT and +before the packet is dropped. Thus the count rule cannot be used as +a true indicator of link layer +.HP +auth +rules cause the matching packet to be queued up for processing by a +user space program. The user space program is responsible for making +an ioctl system call to collect the information about the queued +packet and another ioctl system call to return the verdict (block, +pass, etc) on what to do with the packet. In the event that the queue +becomes full, the packets will end up being dropped. +.HP +call +provides access to functions built into IPFilter that allow for more +complex actions to be taken as part of the decision making that goes +with the rule. +.HP +decapsulate +rules instruct ipfilter to remove any +other headers (IP, UDP, AH) and then process what is inside as a new packet. +For non-UDP packets, there are builtin checks that are applied in addition +to whatever is specified in the rule, to only allow decapsulation of +recognised protocols. After decapsulating the inner packet, any filtering +result that is applied to the inner packet is also applied to the other +packet. +.PP +The default way in which filter rules are applied is for the last +matching rule to be used as the decision maker. So even if the first +rule to match a packet is a pass, if there is a later matching rule +that is a block and no further rules match the packet, then it will +be blocked. +.SS Matching Network Interfaces +.PP +On systems with more than one network interface, it is necessary +to be able to specify different filter rules for each of them. +In the first instance, this is because different networks will send us +packets via each network interface but it is also because of the hosts, +the role and the resulting security policy that we need to be able to +distinguish which network interface a packet is on. +.PP +To accomodate systems where the presence of a network interface is +dynamic, it is not necessary for the network interface named in a +filter rule to be present in the system when the rule is loaded. +This can lead to silent errors being introduced and unexpected +behaviour with the simplest of keyboard mistakes - for example, +typing in hem0 instead of hme0 or hme2 instead of hme3. +.PP +On Solaris systems prior to Solaris 10 Update 4, it is not possible +to filter packets on the loopback interface (lo0) so filter rules +that specify it will have no impact on the corresponding flow of +packets. See below for Solaris specific tips on how to enable this. +.PP +Some examples of including the network interface in filter rules are: +.PP +.nf +block in on bge0 all +pass out on bge0 all +.fi +.SS Address matching (basic) +.PP +The first and most basic part of matching for filtering rules is to +specify IP addresses and TCP/UDP port numbers. The source address +information is matched by the "from" information in a filter rule +and the destination address information is matched with the "to" +information in a filter rule. +.PP +The typical format used for IP addresses is CIDR notation, where an +IP address (or network) is followed by a '/' and a number representing +the size of the netmask in bits. This notation is used for specifying +address matching in both IPv4 and IPv6. If the '/' and bitmask size +are excluded from the matching string, it is assumed that the address +specified is a host address and that the netmask applied should be +all 1's. +.PP +Some examples of this are: +.PP +.nf +pass in from 10.1.0.0/24 to any +block out from any to 10.1.1.1 +.fi +.PP +It is not possible to specify a range of addresses that does not +have a boundary that can be defined by a standard subnet mask. +.IP +.B Names instead of addresses +.RS +.PP +Hostnames, resolved either via DNS or /etc/hosts, or network names, +resolved via /etc/networks, may be used in place of actual addresses +in the filter rules. WARNING: if a hostname expands to more than one +address, only the *first* is used in building the filter rule. +.PP +Caution should be exercised when relying on DNS for filter rules in +case the sending and receiving of DNS packets is blocked when ipf(8) +is processing that part of the configuration file, leading to long +delays, if not errors, in loading the filter rules. +.RE +.SS Protocol Matching +.PP +To match packets based on TCP/UDP port information, it is first necessary +to indicate which protocol the packet must be. This is done using the +"proto" keyword, followed by either the protocol number or a name which +is mapped to the protocol number, usually through the /etc/protocols file. +.PP +.nf +pass in proto tcp from 10.1.0.0/24 to any +block out proto udp from any to 10.1.1.1 +pass in proto icmp from any to 192.168.0.0/16 +.fi +.SS Sending back error packets +.PP +When a packet is just discarded using a block rule, there is no feedback given +to the host that sent the packet. This is both good and bad. If this is the +desired behaviour and it is not desirable to send any feedback about packets +that are to be denied. The catch is that often a host trying to connect to a +TCP port or with a UDP based application will send more than one packet +because it assumes that just one packet may be discarded so a retry is +required. The end result being logs can become cluttered with duplicate +entries due to the retries. +.PP +To address this problem, a block rule can be qualified in two ways. +The first of these is specific to TCP and instructs IPFilter to send back +a reset (RST) packet. This packet indicates to the remote system that the +packet it sent has been rejected and that it shouldn't make any further +attempts to send packets to that port. Telling IPFilter to return a TCP +RST packet in response to something that has been received is achieved +with the return-rst keyword like this: +.PP +.nf +block return-rst in proto tcp from 10.0.0.0/8 to any +.fi +.PP +When sending back a TCP RST packet, IPFilter must construct a new packet +that has the source address of the intended target, not the source address +of the system it is running on (if they are different.) +.PP +For all of the other protocols handled by the IP protocol suite, to send +back an error indicating that the received packet was dropped requires +sending back an ICMP error packet. Whilst these can also be used for TCP, +the sending host may not treat the received ICMP error as a hard error +in the same way as it does the TCP RST packet. To return an ICMP error +it is necessary to place return-icmp after the block keyword like this: +.PP +.nf +block return-icmp in proto udp from any to 192.168.0.1/24 +.fi +.PP +When electing to return an ICMP error packet, it is also possible to +select what type of ICMP error is returned. Whilst the full compliment +of ICMP unreachable codes can be used by specifying a number instead of +the string below, only the following should be used in conjunction with +return-icmp. Which return code to use is a choice to be made when +weighing up the pro's and con's. Using some of the codes may make it +more obvious that a firewall is being used rather than just the host +not responding. +.RS +.HP +filter-prohib +(prohibited by filter) +sending packets to the destination given in the received packet is +prohibited due to the application of a packet filter +.HP +net-prohib +(prohibited network) +sending packets to the destination given in the received packet is +administratively prohibited. +.HP +host-unk +(host unknown) +the destination host address is not known by the system receiving +the packet and therefore cannot be reached. +.HP +host-unr +(host unreachable) +it is not possible to reach the host as given by the destination address +in the packet header. +.HP +net-unk +(network unknown) +the destination network address is not known by the system receiving +the packet and therefore cannot be reached. +.HP +net-unr +(network unreachable) +it is not possible to forward the packet on to its final destination +as given by the destination address +.HP +port-unr +(port unreachable) +there is no application using the given destination port and therefore +it is not possible to reach that port. +.HP +proto-unr +(protocol unreachable) +the IP protocol specified in the packet is not available to receive +packets. +.DE +.PP +An example that shows how to send back a port unreachable packet for +UDP packets to 192.168.1.0/24 is as follows: +.PP +.nf +block return-icmp(port-unr) in proto udp from any to 192.168.1.0/24 +.fi +.PP +In the above examples, when sending the ICMP packet, IPFilter will construct +a new ICMP packet with a source address of the network interface used to +send the packet back to the original source. This can give away that there +is an intermediate system blocking packets. To have IPFilter send back +ICMP packets where the source address is the original destination, regardless +of whether or not it is on the local host, return-icmp-as-dest is used like +this: +.PP +.nf +block return-icmp-as-dest(port-unr) in proto udp \\ + from any to 192.168.1.0/24 +.fi +.SS TCP/UDP Port Matching +.PP +Having specified which protocol is being matched, it is then possible to +indicate which port numbers a packet must have in order to match the rule. +Due to port numbers being used differently to addresses, it is therefore +possible to match on them in different ways. IPFilter allows you to use +the following logical operations: +.IP "< x" +is true if the port number is greater than or equal to x and less than or +equal to y +is true if the port number in the packet is less than x +.IP "<= x" +is true if the port number in the packet is less than or equal to x +.IP "> x" +is true if the port number in the packet is greater than x +.IP ">= x" +is true if the port number in the packet is greater or equal to x +.IP "= x" +is true if the port number in the packet is equal to x +.IP "!= x" +is true if the port number in the packet is not equal to x +.PP +Additionally, there are a number of ways to specify a range of ports: +.IP "x <> y" +is true if the port number is less than a and greater than y +.IP "x >< y" +is true if the port number is greater than x and less than y +.IP "x:y" +is true if the port number is greater than or equal to x and less than or +equal to y +.PP +Some examples of this are: +.PP +.nf +block in proto tcp from any port >= 1024 to any port < 1024 +pass in proto tcp from 10.1.0.0/24 to any port = 22 +block out proto udp from any to 10.1.1.1 port = 135 +pass in proto udp from 1.1.1.1 port = 123 to 10.1.1.1 port = 123 +pass in proto tcp from 127.0.0.0/8 to any port 6000:6009 +.fi +.PP +If there is no desire to mention any specific source or destintion +information in a filter rule then the word "all" can be used to +indicate that all addresses are considered to match the rule. +.SS IPv4 or IPv6 +.PP +If a filter rule is constructed without any addresses then IPFilter +will attempt to match both IPv4 and IPv6 packets with it. In the +next list of rules, each one can be applied to either network protocol +because there is no address specified from which IPFilter can derive +with network protocol to expect. +.PP +.nf +pass in proto udp from any to any port = 53 +block in proto tcp from any port < 1024 to any +.fi +.PP +To explicitly match a particular network address family with a specific +rule, the family must be added to the rule. For IPv4 it is necessary to +add family inet and for IPv6, family inet6. Thus the next rule will +block all packets (both IPv4 and IPv6: +.PP +.nf +block in all +.fi +.PP +but in the following example, we block all IPv4 packets and only allow +in IPv6 packets: +.PP +.nf +block in family inet all +pass in family inet6 all +.fi +.PP +To continue on from the example where we allowed either IPv4 or IPv6 +packets to port 53 in, to change that such that only IPv6 packets to +port 53 need to allowed blocked then it is possible to add in a +protocol family qualifier: +.PP +.nf +pass in family inet6 proto udp from any to any port = 53 +.fi +.SS First match vs last match +.PP +To change the default behaviour from being the last matched rule decides +the outcome to being the first matched rule, the word "quick" is inserted +to the rule. +.SH Extended Packet Matching +.SS Beyond using plain addresses +.PP +On firewalls that are working with large numbers of hosts and networks +or simply trying to filter discretely against various hosts, it can +be an easier administration task to define a pool of addresses and have +a filter rule reference that address pool rather than have a rule for +each address. +.PP +In addition to being able to use address pools, it is possible to use +the interface name(s) in the from/to address fields of a rule. If the +name being used in the address section can be matched to any of the +interface names mentioned in the rule's "on" or "via" fields then it +can be used with one of the following keywords for extended effect: +.HP +broadcast +use the primary broadcast address of the network interface for matching +packets with this filter rule; +.IP +.nf +pass in on fxp0 proto udp from any to fxp0/broadcast port = 123 +.fi +.HP +peer +use the peer address on point to point network interfaces for matching +packets with this filter rule. This option typically only has meaningful +use with link protocols such as SLIP and PPP. +For example, this rule allows ICMP packets from the remote peer of ppp0 +to be received if they're destined for the address assigned to the link +at the firewall end. +.IP +.nf +pass in on ppp0 proto icmp from ppp0/peer to ppp0/32 +.fi +.HP +netmasked +use the primary network address, with its netmask, of the network interface +for matching packets with this filter rule. If a network interface had an +IP address of 192.168.1.1 and its netmask was 255.255.255.0 (/24), then +using the word "netmasked" after the interface name would match any +addresses that would match 192.168.1.0/24. If we assume that bge0 has +this IP address and netmask then the following two rules both serve +to produce the same effect: +.IP +.nf +pass in on bge0 proto icmp from any to 192.168.1.0/24 +pass in on bge0 proto icmp from any to bge0/netmasked +.fi +.HP +network +using the primary network address, and its netmask, of the network interface, +construct an address for exact matching. If a network interface has an +address of 192.168.1.1 and its netmask is 255.255.255.0, using this +option would only match packets to 192.168.1.0. +.IP +.nf +pass in on bge0 proto icmp from any to bge0/network +.fi +.PP +Another way to use the name of a network interface to get the address +is to wrap the name in ()'s. In the above method, IPFilter +looks at the interface names in use and to decide whether or not +the name given is a hostname or network interface name. With the +use of ()'s, it is possible to tell IPFilter that the name should +be treated as a network interface name even though it doesn't +appear in the list of network interface that the rule ias associated +with. +.IP +.nf +pass in proto icmp from any to (bge0)/32 +.fi +.SS Using address pools +.PP +Rather than list out multiple rules that either allow or deny specific +addresses, it is possible to create a single object, call an address +pool, that contains all of those addresses and reference that in the +filter rule. For documentation on how to write the configuration file +for those pools and load them, see ippool.conf(5) and ippool(8). +There are two types of address pools that can be defined in ippool.conf(5): +trees and hash tables. To refer to a tree defined in ippool.conf(5), +use this syntax: +.PP +.nf +pass in from pool/trusted to any +.fi +.PP +Either a name or number can be used after the '/', just so long as it +matches up with something that has already been defined in ipool.conf(5) +and loaded in with ippool(8). Loading a filter rule that references a +pool that does not exist will result in an error. +.PP +If hash tables have been used in ippool.conf(5) to store the addresses +in instead of a tree, then replace the word pool with hash: +.IP +.nf +pass in from any to hash/webservers +.fi +.PP +There are different operational characteristics with each, so there +may be some situations where a pool works better than hash and vice +versa. +.SS Matching TCP flags +.PP +The TCP header contains a field of flags that is used to decide if the +packet is a connection request, connection termination, data, etc. +By matching on the flags in conjunction with port numbers, it is +possible to restrict the way in which IPFilter allows connections to +be created. A quick overview of the TCP +flags is below. Each is listed with the letter used in IPFilter +rules, followed by its three or four letter pneumonic. +.HP +S +SYN - this bit is set when a host is setting up a connection. +The initiator typically sends a packet with the SYN bit and the +responder sends back SYN plus ACK. +.HP +A +ACK - this bit is set when the sender wishes to acknowledge the receipt +of a packet from another host +.HP +P +PUSH - this bit is set when a sending host has send some data that +is yet to be acknowledged and a reply is sought +.HP +F +FIN - this bit is set when one end of a connection starts to close +the connection down +.HP +U +URG - this bit is set to indicate that the packet contains urgent data +.HP +R +RST - this bit is set only in packets that are a reply to another +that has been received but is not targetted at any open port +.HP +C +CWN +.HP +E +ECN +.PP +When matching TCP flags, it is normal to just list the flag that you +wish to be set. By default the set of flags it is compared against +is "FSRPAU". Rules that say "flags S" will be displayed by ipfstat(8) +as having "flags S/FSRPAU". This is normal. +The last two flags, "C" and "E", are optional - they +may or may not be used by an end host and have no bearing on either +the acceptance of data nor control of the connection. Masking them +out with "flags S/FSRPAUCE" may cause problems for remote hosts +making a successful connection. +.PP +.nf +pass in quick proto tcp from any to any port = 22 flags S/SAFR +pass out quick proto tcp from any port = 22 to any flags SA +.fi +.PP +By itself, filtering based on the TCP flags becomes more work but when +combined with stateful filtering (see below), the situation changes. +.SS Matching on ICMP header information +.PP +The TCP and UDP are not the only protocols for which filtering beyond +just the IP header is possible, extended matching on ICMP packets is +also available. The list of valid ICMP types is different for IPv4 +vs IPv6. +.PP +As a practical example, to allow the ping command to work +against a specific target requires allowing two different types of +ICMP packets, like this: +.PP +.nf +pass in proto icmp from any to webserver icmp-type echo +pass out proto icmp from webserver to any icmp-type echorep +.fi +.PP +The ICMP header has two fields that are of interest for filtering: +the ICMP type and code. Filter rules can accept either a name or +number for both the type and code. The list of names supported for +ICMP types is listed below, however only ICMP unreachable errors +have named codes (see above.) +.PP +The list of ICMP types that are available for matching an IPv4 packet +are as follows: +.PP +echo (echo request), +echorep (echo reply), +inforeq (information request), +inforep (information reply), +maskreq (mask request), +maskrep (mask reply), +paramprob (parameter problem), +redir (redirect), +routerad (router advertisement), +routersol (router solicit), +squence (source quence), +timest (timestamp), +timestreq (timestamp reply), +timex (time exceeded), +unreach (unreachable). +.PP +The list of ICMP types that are available for matching an IPv6 packet +are as follows: +.PP +echo (echo request), +echorep (echo reply), +fqdnquery (FQDN query), +fqdnreply (FQDN reply), +inforeq (information request), +inforep (information reply), +listendone (MLD listener done), +listendqry (MLD listener query), +listendrep (MLD listener reply), +neighadvert (neighbour advert), +neighborsol (neighbour solicit), +paramprob (parameter problem), +redir (redirect), +renumber (router renumbering), +routerad (router advertisement), +routersol (router solicit), +timex (time exceeded), +toobig (packet too big), +unreach (unreachable, +whoreq (WRU request), +whorep (WRU reply). +.SH Stateful Packet Filtering +.PP +Stateful packet filtering is where IPFilter remembers some information from +one or more packets that it has seen and is able to apply it to future +packets that it receives from the network. +.PP +What this means for each transport layer protocol is different. +For TCP it means that if IPFilter +sees the very first packet of an attempt to make a connection, it has enough +information to allow all other subsequent packets without there needing to +be any explicit rules to match them. IPFilter uses the TCP port numbers, +TCP flags, window size and sequence numbers to determine which packets +should be matched. For UDP, only the UDP port numbers are available. +For ICMP, the ICMP types can be combined with the ICMP id field to +determine which reply packets match a request/query that has already +been seen. For all other protocols, only matching on IP address and +protocol number is available for determining if a packet received is a mate +to one that has already been let through. +.PP +The difference this makes is a reduction in the number of rules from +2 or 4 to 1. For example, these 4 rules: +.PP +.nf +pass in on bge0 proto tcp from any to any port = 22 +pass out on bge1 proto tcp from any to any port = 22 +pass in on bge1 proto tcp from any port = 22 to any +pass out on bge0 proto tcp from any port = 22 to any +.fi +.PP +can be replaced with this single rule: +.PP +.nf +pass in on bge0 proto tcp from any to any port = 22 flags S keep state +.fi +.PP +Similar rules for UDP and ICMP might be: +.PP +.nf +pass in on bge0 proto udp from any to any port = 53 keep state +pass in on bge0 proto icmp all icmp-type echo keep state +.fi +.PP +When using stateful filtering with TCP it is best to add "flags S" to the +rule to ensure that state is only created when a packet is seen that is +an indication of a new connection. Although IPFilter can gather some +information from packets in the middle of a TCP connection to do stateful +filtering, there are some options that are only sent at the start of the +connection which alter the valid window of what TCP accepts. The end result +of trying to pickup TCP state in mid connection is that some later packets +that are part of the connection may not match the known state information +and be dropped or blocked, causing problems. If a TCP packet matches IP +addresses and port numbers but does not fit into the recognised window, +it will not be automatically allowed and will be flagged inside of +IPFitler as "out of window" (oow). See below, "Extra packet attributes", +for how to match on this attribute. +.PP +Once a TCP connection has reached the established state, the default +timeout allows for it to be idle for 5 days before it is removed from +the state table. The timeouts for the other TCP connection states +vary from 240 seconds to 30 seconds. +Both UDP and ICMP state entries have asymetric timeouts where the timeout +set upon seeing packets in the forward direction is much larger than +for the reverse direction. For UDP the default timeouts are 120 and +12 seconds, for ICMP 60 and 6 seconds. This is a reflection of the +use of these protocols being more for query-response than for ongoing +connections. For all other protocols the +timeout is 60 seconds in both directions. +.SS Stateful filtering options +.PP +The following options can be used with stateful filtering: +.HP +limit +limit the number of state table entries that this rule can create to +the number given after limit. A rule that has a limit specified is +always permitted that many state table entries, even if creating an +additional entry would cause the table to have more entries than the +otherwise global limit. +.IP +.nf +pass ... keep state(limit 100) +.fi +.HP +age +sets the timeout for the state entry when it sees packets going through +it. Additionally it is possible to set the tieout for the reply packets +that come back through the firewall to a different value than for the +forward path. allowing a short timeout to be set after the reply has +been seen and the state no longer required. +.RS +.PP +.nf +pass in quick proto icmp all icmp-type echo \\ + keep state (age 3) +pass in quick proto udp from any \\ + to any port = 53 keep state (age 30/1) +.fi +.RE +.HP +strict +only has an impact when used with TCP. It forces all packets that are +allowed through the firewall to be sequential: no out of order delivery +of packets is allowed. This can cause significant slowdown for some +connections and may stall others. Use with caution. +.IP +.nf +pass in proto tcp ... keep state(strict) +.fi +.HP +noicmperr +prevents ICMP error packets from being able to match state table entries +created with this flag using the contents of the original packet included. +.IP +.nf +pass ... keep state(noicmperr) +.fi +.HP +sync +indicates to IPFilter that it needs to provide information to the user +land daemons responsible for syncing other machines state tables up +with this one. +.IP +.nf +pass ... keep state(sync) +.fi +.HP +nolog +do not generate any log records for the creation or deletion of state +table entries. +.IP +.nf +pass ... keep state(nolog) +.fi +.HP +icmp-head +rather than just precent ICMP error packets from being able to match +state table entries, allow an ACL to be processed that can filter in or +out ICMP error packets based as you would with normal firewall rules. +The icmp-head option requires a filter rule group number or name to +be present, just as you would use with head. +.RS +.PP +.nf +pass in quick proto tcp ... keep state(icmp-head 101) +block in proto icmp from 10.0.0.0/8 to any group 101 +.fi +.RE +.HP +max-srcs +allows the number of distinct hosts that can create a state entry to +be defined. +.IP +.nf +pass ... keep state(max-srcs 100) +pass ... keep state(limit 1000, max-srcs 100) +.fi +.HP +max-per-src +whilst max-srcs limits the number of individual hosts that may cause +the creation of a state table entry, each one of those hosts is still +table to fill up the state table with new entries until the global +maximum is reached. This option allows the number of state table entries +per address to be limited. +.IP +.nf +pass ... keep state(max-srcs 100, max-per-src 1) +pass ... keep state(limit 100, max-srcs 100, max-per-src 1) +.fi +.IP +Whilst these two rules might seem identical, in that they both +ultimately limit the number of hosts and state table entries created +from the rule to 100, there is a subtle difference: the second will +always allow up to 100 state table entries to be created whereas the +first may not if the state table fills up from other rules. +.IP +Further, it is possible to specify a netmask size after the per-host +limit that enables the per-host limit to become a per-subnet or +per-network limit. +.IP +.nf +pass ... keep state(max-srcs 100, max-per-src 1/24) +.fi +.IP +If there is no IP protocol implied by addresses or other features of +the rule, IPFilter will assume that no netmask is an all ones netmask +for both IPv4 and IPv6. +.SS Tieing down a connection +.PP +For any connection that transits a firewall, each packet will be seen +twice: once going in and once going out. Thus a connection has 4 flows +of packets: +.HP +forward +inbound packets +.HP +forward +outbound packets +.HP +reverse +inbound packets +.HP +reverse +outbound packets +.PP +IPFilter allows you to define the network interface to be used at all +four points in the flow of packets. For rules that match inbound packets, +out-via is used to specify which interfaces the packets go out, For rules +that match outbound packets, in-via is used to match the inbound packets. +In each case, the syntax generalises to this: +.PP +.nf +pass ... in on forward-in,reverse-in \\ + out-via forward-out,reverse-out ... + +pass ... out on forward-out,reverse-out \\ + in-via forward-in,reverse-in ... +.fi +.PP +An example that pins down all 4 network interfaces used by an ssh +connection might look something like this: +.PP +.nf +pass in on bge0,bge1 out-via bge1,bge0 proto tcp \\ + from any to any port = 22 flags S keep state +.fi +.SS Working with packet fragments +.PP +Fragmented packets result in 1 packet containing all of the layer 3 and 4 +header information whilst the data is split across a number of other packets. +.PP +To enforce access control on fragmented packets, one of two approaches +can be taken. The first is to allow through all of the data fragments +(those that made up the body of the original packet) and rely on matching +the header information in the "first" fragment, when it is seen. The +reception of body fragments without the first will result in the receiving +host being unable to completely reassemble the packet and discarding all +of the fragments. The following three rules deny all fragmented packets +from being received except those that are UDP and even then only allows +those destined for port 2049 to be completed. +.PP +.nf +block in all with frags +pass in proto udp from any to any with frag-body +pass in proto udp from any to any port = 2049 with frags +.fi +.PP +Another mechanism that is available is to track "fragment state". +This relies on the first fragment of a packet that arrives to be +the fragment that contains all of the layer 3 and layer 4 header +information. With the receipt of that fragment before any other, +it is possible to determine which other fragments are to be allowed +through without needing to explicitly allow all fragment body packets. +An example of how this is done is as follows: +.PP +.nf +pass in proto udp from any port = 2049 to any with frags keep frags +.fi +.SH Building a tree of rules +.PP +Writing your filter rules as one long list of rules can be both inefficient +in terms of processing the rules and difficult to understand. To make the +construction of filter rules easier, it is possible to place them in groups. +A rule can be both a member of a group and the head of a new group. +.PP +Using filter groups requires at least two rules: one to be in the group +one one to send matchign packets to the group. If a packet matches a +filtre rule that is a group head but does not match any of the rules +in that group, then the packet is considered to have matched the head +rule. +.PP +Rules that are a member of a group contain the word group followed by +either a name or number that defines which group they're in. Rules that +form the branch point or starting point for the group must use the +word head, followed by either a group name or number. If rules are +loaded in that define a group but there is no matching head then they +will effectively be orphaned rules. It is possible to have more than +one head rule point to the same group, allowing groups to be used +like subroutines to implement specific common policies. +.PP +A common use of filter groups is to define head rules that exist in the +filter "main line" for each direction with the interfaces in use. For +example: +.PP +.nf +block in quick on bge0 all head 100 +block out quick on bge0 all head 101 +block in quick on fxp0 all head internal-in +block out quick on fxp0 all head internal-out +pass in quick proto icmp all icmp-type echo group 100 +.fi +.PP +In the above set of rules, there are four groups defined but only one +of them has a member rule. The only packets that would be allowed +through the above ruleset would be ICMP echo packets that are +received on bge0. +.PP +Rules can be both a member of a group and the head of a new group, +allowing groups to specialise. +.PP +.nf +block in quick on bge0 all head 100 +block in quick proto tcp all head 1006 group 100 +.fi +.PP +Another use of filter rule groups is to provide a place for rules to +be dynamically added without needing to worry about their specific +ordering amongst the entire ruleset. For example, if I was using this +simple ruleset: +.PP +.nf +block in quick all with bad +block in proto tcp from any to any port = smtp head spammers +pass in quick proto tcp from any to any port = smtp flags S keep state +.fi +.PP +and I was getting lots of connections to my email server from 10.1.1.1 +to deliver spam, I could load the following rule to complement the above: +.IP +.nf +block in quick from 10.1.1.1 to any group spammers +.fi +.SS Decapsulation +.PP +Rule groups also form a different but vital role for decapsulation rules. +With the following simple rule, if IPFilter receives an IP packet that has +an AH header as its layer 4 payload, IPFilter would adjust its view of the +packet internally and then jump to group 1001 using the data beyond the +AH header as the new transport header. +.PP +.nf +decapsulate in proto ah all head 1001 +.fi +.PP +For protocols that +are recognised as being used with tunnelling or otherwise encapsulating +IP protocols, IPFilter is able to decide what it has on the inside +without any assistance. Some tunnelling protocols use UDP as the +transport mechanism. In this case, it is necessary to instruct IPFilter +as to what protocol is inside UDP. +.PP +.nf +decapsulate l5-as(ip) in proto udp from any \\ + to any port = 1520 head 1001 +.fi +.PP +Currently IPFilter only supports finding IPv4 and IPv6 headers +directly after the UDP header. +.PP +If a packet matches a decapsulate rule but fails to match any of the rules +that are within the specified group, processing of the packet continues +to the next rule after the decapsulate and IPFilter's internal view of the +packet is returned to what it was prior to the decapsulate rule. +.PP +It is possible to construct a decapsulate rule without the group +head at the end that ipf(8) will accept but such rules will not +result in anything happening. +.SS Policy Based Routing +.PP +With firewalls being in the position they often are, at the boundary +of different networks connecting together and multiple connections that +have different properties, it is often desirable to have packets flow +in a direction different to what the routing table instructs the kernel. +These decisions can often be extended to changing the route based on +both source and destination address or even port numbers. +.PP +To support this kind of configuration, IPFilter allows the next hop +destination to be specified with a filter rule. The next hop is given +with the interface name to use for output. The syntax for this is +interface:ip.address. It is expected that the address given as the next +hop is directly connected to the network to which the interface is. +.PP +.nf +pass in on bge0 to bge1:1.1.1.1 proto tcp \\ + from 1.1.2.3 to any port = 80 flags S keep state +.fi +.PP +When this feature is combined with stateful filtering, it becomes +possible to influence the network interface used to transmit packets +in both directions because we now have a sense for what its reverse +flow of packets is. +.PP +.nf +pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\ + proto tcp from 1.1.2.3 to any port = 80 flags S keep state +.fi +.PP +If the actions of the routing table are perfectly acceptable, but +you would like to mask the presence of the firewall by not changing +the TTL in IP packets as they transit it, IPFilter can be instructed +to do a "fastroute" action like this: +.PP +.nf +pass in on bge0 fastroute proto icmp all +.fi +.PP +This should be used with caution as it can lead to endless packet +loops. Additionally, policy based routing does not change the IP +header's TTL value. +.PP +A variation on this type of rule supports a duplicate of the original +packet being created and sent out a different network. This can be +useful for monitoring traffic and other purposes. +.PP +.nf +pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\ + dup-to fxp0:10.0.0.1 proto tcp from 1.1.2.3 \\ + to any port = 80 flags S keep state +.fi +.SS Matching IPv4 options +.PP +The design for IPv4 allows for the header to be upto 64 bytes long, +however most traffic only uses the basic header which is 20 bytes long. +The other 44 bytes can be uesd to store IP options. These options are +generally not necessary for proper interaction and function on the +Internet today. For most people it is sufficient to block and drop +all packets that have any options set. This can be achieved with this +rule: +.PP +.nf +block in quick all with ipopts +.fi +.PP +This rule is usually placed towards the top of the configuration +so that all incoming packets are blocked. +.PP +If you wanted to allow in a specific IP option type, the syntax +changes slightly: +.PP +.nf +pass in quick proto igmp all with opt rtralrt +.fi +.PP +The following is a list of IP options that most people encounter and +what their use/threat is. +.HP +lsrr +(loose source route) the sender of the packet includes a list of addresses +that they wish the packet to be routed through to on the way to the +destination. Because replies to such packets are expected to use the +list of addresses in reverse, hackers are able to very effectively use +this header option in address spoofing attacks. +.HP +rr +(record route) the sender allocates some buffer space for recording the +IP address of each router that the packet goes through. This is most often +used with ping, where the ping response contains a copy of all addresses +from the original packet, telling the sender what route the packet took +to get there. Due to performance and security issues with IP header +options, this is almost no longer used. +.HP +rtralrt +(router alert) this option is often used in IGMP messages as a flag to +routers that the packet needs to be handled differently. It is unlikely +to ever be received from an unknown sender. It may be found on LANs or +otherwise controlled networks where the RSVP protocol and multicast +traffic is in heavy use. +.HP +ssrr +(strict source route) the sender of the packet includes a list of addresses +that they wish the packet to be routed through to on the way to the +destination. Where the lsrr option allows the sender to specify only +some of the nodes the packet must go through, with the ssrr option, +every next hop router must be specified. +.PP +The complete list of IPv4 options that can be matched on is: +addext (Address Extention), +cipso (Classical IP Security Option), +dps (Dynamic Packet State), +e-sec (Extended Security), +eip (Extended Internet Protocol), +encode (ENCODE), +finn (Experimental Flow Control), +imitd (IMI Traffic Descriptor), +lsrr (Loose Source Route), +mtup (MTU Probe - obsolete), +mtur (MTU response - obsolete), +nop (No Operation), +nsapa (NSAP Address), +rr (Record Route), +rtralrt (Router Alert), +satid (Stream Identifier), +sdb (Selective Directed Broadcast), +sec (Security), +ssrr (Strict Source Route), +tr (Tracerote), +ts (Timestamp), +ump (Upstream Multicast Packet), +visa (Experimental Access Control) +and zsu (Experimental Measurement). +.SS Security with CIPSO and IPSO +.PP +IPFilter supports filtering on IPv4 packets using security attributes embedded +in the IP options part of the packet. These options are usually only used on +networks and systems that are using lablled security. Unless you know that +you are using labelled security and your networking is also labelled, it +is highly unlikely that this section will be relevant to you. +.PP +With the traditional IP Security Options (IPSO), packets can be tagged with +a security level. The following keywords are recognised and match with the +relevant RFC with respect to the bit patterns matched: +confid (confidential), +rserve-1 (1st reserved value), +rserve-2 (2nd reserved value), +rserve-3 (3rd reserved value), +rserve-4 (4th reserved value), +secret (secret), +topsecret (top secret), +unclass (unclassified). +.PP +.nf +block in quick all with opt sec-class unclass +pass in all with opt sec-class secret +.fi +.SS Matching IPv6 extension headers +.PP +Just as it is possible to filter on the various IPv4 header options, +so too it is possible to filter on the IPv6 extension headers that are +placed between the IPv6 header and the transport protocol header. +.PP +dstopts (destination options), +esp (encrypted, secure, payload), +frag (fragment), +hopopts (hop-by-hop options), +ipv6 (IPv6 header), +mobility (IP mobility), +none, +routing. +.SS Logging +.PP +There are two ways in which packets can be logged with IPFilter. The +first is with a rule that specifically says log these types of packets +and the second is a qualifier to one of the other keywords. Thus it is +possible to both log and allow or deny a packet with a single rule. +.PP +.nf +pass in log quick proto tcp from any to any port = 22 +.fi +.PP +When using stateful filtering, the log action becomes part of the result +that is remembered about a packet. Thus if the above rule was qualified +with keep state, every packet in the connection would be logged. To only +log the first packet from every packet flow tracked with keep state, it +is necessary to indicate to IPFilter that you only wish to log the first +packet. +.PP +.nf +pass in log first quick proto tcp from any to any port = 22 \\ + flags S keep state +.fi +.PP +If it is a requirement that the logging provide an accurate representation +of which connections are allowed, the log action can be qualified with the +option or-block. This allows the administrator to instruct IPFilter to +block the packet if the attempt to record the packet in IPFilter's kernel +log records (which have an upper bound on size) failed. Unless the system +shuts down or reboots, once a log record is written into the kernel buffer, +it is there until ipmon(8) reads it. +.PP +.nf +block in log proto tcp from any to any port = smtp +pass in log or-block first quick proto tcp from any \\ + to any port = 22 flags S keep state +.fi +.PP +By default, IPFilter will only log the header portion of a packet received +on the network. Up to 128 bytes of a packet's body can also +be logged with the body keyword. ipmon(8) will display the contents of the +portion of the body logged in hex. +.PP +.nf +block in log body proto icmp all +.fi +.PP +When logging packets from ipmon(8) to syslog, by default ipmon(8) will +control what syslog facility and priority a packet will be logged with. +This can be tuned on a per rule basis like this: +.PP +.nf +block in quick log level err all with bad +pass in log level local1.info proto tcp \\ + from any to any port = 22 flags S keep state +.fi +.PP +ipfstat(8) reports how many packets have been successfully logged and how +many failed attempts to log a packet there were. +.SS Filter rule comments +.PP +If there is a desire to associate a text string, be it an administrative +comment or otherwise, with an IPFilter rule, this can be achieved by giving +the filter rule a comment. The comment is loaded with the rule into the +kernel and can be seen when the rules are listed with ipfstat. +.PP +.nf +pass in quick proto tcp from any \\ + to port = 80 comment "all web server traffic is ok" +pass out quick proto tcp from any port = 80 \\ + to any comment "all web server traffic is ok" +.fi +.SS Tags +.PP +To enable filtering and NAT to correctly match up packets with rules, +tags can be added at with NAT (for inbound packets) and filtering (for +outbound packets.) This allows a filter to be correctly mated with its +NAT rule in the event that the NAT rule changed the packet in a way +that would mean it is not obvious what it was. +.PP +For inbound packets, IPFilter can match the tag used in the filter +rules with that set by NAT. For outbound rules, it is the reverse, +the filter sets the tag and the NAT rule matches up with it. +.PP +.nf +pass in ... match-tag(nat=proxy) +pass out ... set-tag(nat=proxy) +.fi +.PP +Another use of tags is to supply a number that is only used with logging. +When packets match these rules, the log tag is carried over into the +log file records generated by ipmon(8). With the correct use of tools +such as grep, extracting log records of interest is simplified. +.PP +.nf +block in quick log ... set-tag(log=33) +.fi +.SH Filter Rule Expiration +.PP +IPFilter allows rules to be added into the kernel that it will remove after +a specific period of time by specifying rule-ttl at the end of a rule. +When listing rules in the kernel using ipfstat(8), rules that are going +to expire will NOT display "rule-ttl" with the timeout, rather what will +be seen is a comment with how many ipfilter ticks left the rule has to +live. +.PP +The time to live is specified in seconds. +.PP +.nf +pass in on fxp0 proto tcp from any \\ + to port = 22 flags S keep state rule-ttl 30 +.fi +.SH Internal packet attributes +.PP +In addition to being able to filter on very specific network and transport +header fields, it is possible to filter on other attributes that IPFilter +attaches to a packet. These attributes are placed in a rule after the +keyword "with", as can be seen with frags and frag-body above. The +following is a list of the other attributes available: +.HP +oow +the packet's IP addresses and TCP ports match an existing entry in the +state table but the sequence numbers indicate that it is outside of the +accepted window. +.IP +.nf +block return-rst in quick proto tcp from any to any with not oow +.fi +.HP +bcast +this is set by IPFilter when it receives notification that the link +layer packet was a broadcast packet. No checking of the IP addresses +is performned to determine if it is a broadcast destination or not. +.IP +.nf +block in quick proto udp all with bcast +.fi +.HP +mcast +this is set by IPFilter when it receives notification that the link +layer packet was a multicast packet. No checking of the IP addresses +is performned to determine if it is a multicast destination or not. +.IP +.nf +pass in quick proto udp from any to any port = dns with mcast +.fi +.HP +mbcast +can be used to match a packet that is either a multicast or broadcast +packet at the link layer, as indicated by the operating system. +.IP +.nf +pass in quick proto udp from any to any port = ntp with mbcast +.fi +.HP +nat +the packet positively matched a NAT table entry. +.HP +bad +sanity checking of the packet failed. This could indicate that the +layer 3/4 headers are not properly formed. +.HP +bad-src +when reverse path verification is enabled, this flag will be set when +the interface the packet is received on does not match that which would +be used to send a packet out of to the source address in the received +packet. +.HP +bad-nat +an attempt to perform NAT on the packet failed. +.HP +not +each one of the attributes matched using the "with" keyword can also be +looked for to not be present. For example, to only allow in good packets, +I can do this: +.PP +.nf +block in all +pass in all with not bad +.fi +.SH Tuning IPFilter +.PP +The ipf.conf file can also be used to tune the behaviour of IPFilter, +allowing, for example, timeouts for the NAT/state table(s) to be set +along with their sizes. The presence and names of tunables may change +from one release of IPFilter to the next. The tunables that can be +changed via ipf.conf is the same as those that can be seen and modified +using the -T command line option to ipf(8). +.PP +NOTE: When parsing ipf.conf, ipf(8) will apply the settings before +loading any rules. Thus if your settings are at the top, these may +be applied whilst the rules not applied if there is an error further +down in the configuration file. +.PP +To set one of the values below, the syntax is simple: "set", followed +by the name of the tuneable to set and then the value to set it to. +.PP +.nf +set state_max 9999; +set state_size 10101; +.fi +.PP +A list of the currently available variables inside IPFilter that may +be tuned from ipf.conf are as follows: +.HP +active +set through -s command line switch of ipf(8). See ipf(8) for detals. +.HP +chksrc +when set, enables reverse path verification on source addresses and +for filters to match packets with bad-src attribute. +.HP +control_forwarding +when set turns off kernel forwarding when IPFilter is disabled or unloaded. +.HP +default_pass +the default policy - whether packets are blocked or passed, etc - is +represented by the value of this variable. It is a bit field and the +bits that can be set are found in <netinet/ip_fil.h>. It is not +recommended to tune this value directly. +.HP +ftp_debug +set the debugging level of the in-kernel FTP proxy. +Debug messages will be printed to the system console. +.HP +ftp_forcepasv +when set the FTP proxy must see a PASV/EPSV command before creating +the state/NAT entries for the 227 response. +.HP +ftp_insecure +when set the FTP proxy will not wait for a user to login before allowing +data connections to be created. +.HP +ftp_pasvonly +when set the proxy will not create state/NAT entries for when it +sees either the PORT or EPRT command. +.HP +ftp_pasvrdr +when enabled causes the FTP proxy to create very insecure NAT/state +entries that will allow any connection between the client and server +hosts when a 227 reply is seen. Use with extreme caution. +.HP +ftp_single_xfer +when set the FTP proxy will only allow one data connection at a time. +.HP +hostmap_size +sets the size of the hostmap table used by NAT to store address mappings +for use with sticky rules. +.HP +icmp_ack_timeout +default timeout used for ICMP NAT/state when a reply packet is seen for +an ICMP state that already exists +.HP +icmp_minfragmtu +sets the minimum MTU that is considered acceptable in an ICMP error +before deciding it is a bad packet. +.HP +icmp_timeout +default timeout used for ICMP NAT/state when the packet matches the rule +.HP +ip_timeout +default timeout used for NAT/state entries that are not TCP/UDP/ICMP. +.HP +ipf_flags +.HP +ips_proxy_debug +this sets the debugging level for the proxy support code. +When enabled, debugging messages will be printed to the system console. +.HP +log_all +when set it changes the behaviour of "log body" to log the entire packet +rather than just the first 128 bytes. +.HP +log_size +sets the size of the in-kernel log buffer in bytes. +.HP +log_suppress +when set, IPFilter will check to see if the packet it is logging is +similar to the one it previously logged and if so, increases +the occurance count for that packet. The previously logged packet +must not have yet been read by ipmon(8). +.HP +min_ttl +is used to set the TTL value that packets below will be marked with +the low-ttl attribute. +.HP +nat_doflush +if set it will cause the NAT code to do a more aggressive flush of the +NAT table at the next opportunity. Once the flush has been done, the +value is reset to 0. +.HP +nat_lock +this should only be changed using ipfs(8) +.HP +nat_logging +when set, NAT will create log records that can be read from /dev/ipnat. +.HP +nat_maxbucket +maximum number of entries allowed to exist in each NAT hash bucket. +This prevents an attacker trying to load up the hash table with +entries in a single bucket, reducing performance. +.HP +nat_rules_size +size of the hash table to store map rules. +.HP +nat_table_max +maximum number of entries allowed into the NAT table +.HP +nat_table_size +size of the hash table used for NAT +.HP +nat_table_wm_high +when the fill percentage of the NAT table exceeds this mark, more +aggressive flushing is enabled. +.HP +nat_table_wm_low +this sets the percentage at which the NAT table's agressive flushing +will turn itself off at. +.HP +rdr_rules_size +size of the hash table to store rdr rules. +.HP +state_lock +this should only be changed using ipfs(8) +.HP +state_logging +when set, the stateful filtering will create log records +that can be read from /dev/ipstate. +.HP +state_max +maximum number of entries allowed into the state table +.HP +state_maxbucket +maximum number of entries allowed to exist in each state hash bucket. +This prevents an attacker trying to load up the hash table with +entries in a single bucket, reducing performance. +.HP +state_size +size of the hash table used for stateful filtering +.HP +state_wm_freq +this controls how often the agressive flushing should be run once the +state table exceeds state_wm_high in percentage full. +.HP +state_wm_high +when the fill percentage of the state table exceeds this mark, more +aggressive flushing is enabled. +.HP +state_wm_low +this sets the percentage at which the state table's agressive flushing +will turn itself off at. +.HP +tcp_close_wait +timeout used when a TCP state entry reaches the FIN_WAIT_2 state. +.HP +tcp_closed +timeout used when a TCP state entry is ready to be removed after either +a RST packet is seen. +.HP +tcp_half_closed +timeout used when a TCP state entry reaches the CLOSE_WAIT state. +.HP +tcp_idle_timeout +timeout used when a TCP state entry reaches the ESTABLISHED state. +.HP +tcp_last_ack +timeout used when a TCP NAT/state entry reaches the LAST_ACK state. +.HP +tcp_syn_received +timeout applied to a TCP NAT/state entry after SYN-ACK packet has been seen. +.HP +tcp_syn_sent +timeout applied to a TCP NAT/state entry after SYN packet has been seen. +.HP +tcp_time_wait +timeout used when a TCP NAT/state entry reaches the TIME_WAIT state. +.HP +tcp_timeout +timeout used when a TCP NAT/state entry reaches either the half established +state (one ack is seen after a SYN-ACK) or one side is in FIN_WAIT_1. +.HP +udp_ack_timeout +default timeout used for UDP NAT/state when a reply packet is seen for +a UDP state that already exists +.HP +udp_timeout +default timeout used for UDP NAT/state when the packet matches the rule +.HP +update_ipid +when set, turns on changing the IP id field in NAT'd packets to a random +number. +.SS Table of visible variables +.PP +A list of all of the tunables, their minimum, maximum and current +values is as follows. +.PP +.nf +Name Min Max Current +active 0 0 0 +chksrc 0 1 0 +control_forwarding 0 1 0 +default_pass 0 MAXUINT 134217730 +ftp_debug 0 10 0 +ftp_forcepasv 0 1 1 +ftp_insecure 0 1 0 +ftp_pasvonly 0 1 0 +ftp_pasvrdr 0 1 0 +ftp_single_xfer 0 1 0 +hostmap_size 1 MAXINT 2047 +icmp_ack_timeout 1 MAXINT 12 +icmp_minfragmtu 0 1 68 +icmp_timeout 1 MAXINT 120 +ip_timeout 1 MAXINT 120 +ipf_flags 0 MAXUINT 0 +ips_proxy_debug 0 10 0 +log_all 0 1 0 +log_size 0 524288 32768 +log_suppress 0 1 1 +min_ttl 0 1 4 +nat_doflush 0 1 0 +nat_lock 0 1 0 +nat_logging 0 1 1 +nat_maxbucket 1 MAXINT 22 +nat_rules_size 1 MAXINT 127 +nat_table_max 1 MAXINT 30000 +nat_table_size 1 MAXINT 2047 +nat_table_wm_high 2 100 99 +nat_table_wm_low 1 99 90 +rdr_rules_size 1 MAXINT 127 +state_lock 0 1 0 +state_logging 0 1 1 +state_max 1 MAXINT 4013 +state_maxbucket 1 MAXINT 26 +state_size 1 MAXINT 5737 +state_wm_freq 2 999999 20 +state_wm_high 2 100 99 +state_wm_low 1 99 90 +tcp_close_wait 1 MAXINT 480 +tcp_closed 1 MAXINT 60 +tcp_half_closed 1 MAXINT 14400 +tcp_idle_timeout 1 MAXINT 864000 +tcp_last_ack 1 MAXINT 60 +tcp_syn_received 1 MAXINT 480 +tcp_syn_sent 1 MAXINT 480 +tcp_time_wait 1 MAXINT 480 +tcp_timeout 1 MAXINT 480 +udp_ack_timeout 1 MAXINT 24 +udp_timeout 1 MAXINT 240 +update_ipid 0 1 0 +.fi +.SH Calling out to internal functions +.PP +IPFilter provides a pair of functions that can be called from a rule +that allow for a single rule to jump out to a group rather than walk +through a list of rules to find the group. If you've got multiple +networks, each with its own group of rules, this feature may help +provide better filtering performance. +.PP +The lookup to find which rule group to jump to is done on either the +source address or the destination address but not both. +.PP +In this example below, we are blocking all packets by default but then +doing a lookup on the source address from group 1010. The two rules in +the ipf.conf section are lone members of their group. For an incoming +packet that is from 1.1.1.1, it will go through three rules: (1) the +block rule, (2) the call rule and (3) the pass rule for group 1020. +For a packet that is from 3.3.2.2, it will also go through three rules: +(1) the block rule, (2) the call rule and (3) the pass rule for group +1030. Should a packet from 3.1.1.1 arrive, it will be blocked as it +does not match any of the entries in group 1010, leaving it to only +match the first rule. +.PP +.nf +from ipf.conf +------------- +block in all +call now srcgrpmap/1010 in all +pass in proto tcp from any to any port = 80 group 1020 +pass in proto icmp all icmp-type echo group 1030 + +from ippool.conf +---------------- +group-map in role=ipf number=1010 + { 1.1.1.1 group = 1020, 3.3.0.0/16 group = 1030; }; +.fi +.SS IPFilter matching expressions +.PP +An experimental feature that has been added to filter rules is to use +the same expression matching that is available with various commands +to flush and list state/NAT table entries. The use of such an expression +precludes the filter rule from using the normal IP header matching. +.PP +.nf +pass in exp { "tcp.sport 23 or tcp.sport 50" } keep state +.fi +.SS Filter rules with BPF +.PP +On platforms that have the BPF built into the kernel, IPFilter can be +built to allow BPF expressions in filter rules. This allows for packet +matching to be on arbitrary data in the packt. The use of a BPF expression +replaces all of the other protocol header matching done by IPFilter. +.PP +.nf +pass in bpf-v4 { "tcp and (src port 23 or src port 50)" } \\ + keep state +.fi +.PP +These rules tend to be +write-only because the act of compiling the filter expression into the +BPF instructions loaded into the kernel can make it difficut to +accurately reconstruct the original text filter. The end result is that +while ipf.conf() can be easy to read, understanding the output from +ipfstat might not be. +.SH VARIABLES +.PP +This configuration file, like all others used with IPFilter, supports the +use of variable substitution throughout the text. +.PP +.nf +nif="ppp0"; +pass in on $nif from any to any +.fi +.PP +would become +.PP +.nf +pass in on ppp0 from any to any +.fi +.PP +Variables can be used recursively, such as 'foo="$bar baz";', so long as +$bar exists when the parser reaches the assignment for foo. +.PP +See +.B ipf(8) +for instructions on how to define variables to be used from a shell +environment. +.DT +.SH FILES +/dev/ipf +/etc/ipf.conf +.br +/usr/share/examples/ipfilter Directory with examples. +.SH SEE ALSO +ipf(8), ipfstat(8), ippool.conf(5), ippool(8) diff --git a/sbin/ipf/ipf/ipf.8 b/sbin/ipf/ipf/ipf.8 new file mode 100644 index 000000000000..3a84e7776b47 --- /dev/null +++ b/sbin/ipf/ipf/ipf.8 @@ -0,0 +1,184 @@ +.\" $FreeBSD$ +.TH IPF 8 +.SH NAME +ipf \- alters packet filtering lists for IP packet input and output +.SH SYNOPSIS +.B ipf +[ +.B \-6AcdDEInoPrsvVyzZ +] [ +.B \-l +<block|pass|nomatch> +] [ +.B \-T +<optionlist> +] [ +.B \-F +<i|o|a|s|S> +] +.B \-f +<\fIfilename\fP> +[ +.B \-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 kernel's 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 +.TP +.B \-6 +IPv4 and IPv6 rules are stored in a single table and can be read from a +single file. This option is no longer required to load IPv6 rules. This +option is ignored when specified with the -F option and the -F option +will flush IPv4 rules even if this option is specified. +.TP +.B \-A +Set the list to make changes to the active list (default). +.TP +.B \-c <language> +This option causes \fBipf\fP to generate output files for a compiler that +supports \fBlanguage\fI. At present, the only target language supported is +\fBC\fB (-cc) for which two files - \fBip_rules.c\fP +and \fBip_rules.h\fP are generated in the \fBCURRENT DIRECTORY\fP when +\fBipf\fP is being run. These files can be used with the +\fBIPFILTER_COMPILED\fP kernel option to build filter rules staticlly into +the kernel. +.TP +.B \-d +Turn debug mode on. Causes a hexdump of filter rules to be generated as +it processes each one. +.TP +.B \-D +Disable the filter (if enabled). Not effective for loadable kernel versions. +.TP +.B \-E +Enable the filter (if disabled). Not effective for loadable kernel versions. +.TP +.BR \-F \0<i|o|a> +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. +.TP +.BR \-F \0<s|S> +To flush entries from the state table, the \fB-F\fP option is used in +conjunction with either "s" (removes state information about any non-fully +established connections) or "S" (deletes the entire state table). Only +one of the two options may be given. A fully established connection +will show up in \fBipfstat -s\fP output as 5/5, with deviations either +way indicating it is not fully established any more. +.TP +.BR \-F <5|6|7|8|9|10|11> +For the TCP states that represent the closing of a connection has begun, +be it only one side or the complete connection, it is possible to flush +those states directly using the number corresponding to that state. +The numbers relate to the states as follows: 5 = close-wait, 6 = fin-wait-1, +7 = closing, 8 = last-ack, 9 = fin-wait-2, 10 = time-wait, 11 = closed. +.TP +.BR \-F <number> +If the argument supplied to \fB-F\fP is greater than 30, then state table +entries that have been idle for more than this many seconds will be flushed. +.TP +.BR \-f \0<filename> +This option specifies which files +\fBipf\fP should use to get input from for modifying the packet filter rule +lists. +.TP +.B \-I +Set the list to make changes to the inactive list. +.TP +.B \-l \0<pass|block|nomatch> +Use of the \fB-l\fP flag toggles default logging of packets. Valid +arguments to this option are \fBpass\fP, \fBblock\fP and \fBnomatch\fP. +When an option is set, any packet which exits filtering and matches the +set category is logged. This is most useful for causing all packets +which don't match any of the loaded rules to be logged. +.TP +.B \-n +This flag (no-change) prevents \fBipf\fP from actually making any ioctl +calls or doing anything which would alter the currently running kernel. +.TP +.B \-o +Force rules by default to be added/deleted to/from the output list, rather +than the (default) input list. +.TP +.B \-P +Add rules as temporary entries in the authentication rule table. +.TP +.B \-r +Remove matching filter rules rather than add them to the internal lists +.TP +.B \-s +Swap the active filter list in use to be the "other" one. +.TP +.B \-T <optionlist> +This option allows run-time changing of IPFilter kernel variables. Some +variables require IPFilter to be in a disabled state (\fB-D\fP) for changing, +others do not. The optionlist parameter is a comma separated list of tuning +commands. A tuning command is either "list" (retrieve a list of all variables +in the kernel, their maximum, minimum and current value), a single variable +name (retrieve its current value) and a variable name with a following +assignment to set a new value. Some examples follow. +.nf +# Print out all IPFilter kernel tunable parameters +ipf -T list +# Display the current TCP idle timeout and then set it to 3600 +ipf -D -T fr_tcpidletimeout,fr_tcpidletimeout=3600 -E +# Display current values for fr_pass and fr_chksrc, then set fr_chksrc to 1. +ipf -T fr_pass,fr_chksrc,fr_chksrc=1 +.fi +.TP +.B \-v +Turn verbose mode on. Displays information relating to rule processing. +.TP +.B \-V +Show version information. This will display the version information compiled +into the ipf binary and retrieve it from the kernel code (if running/present). +If it is present in the kernel, information about its current state will be +displayed (whether logging is active, default filtering, etc). +.TP +.B \-y +Manually resync the in-kernel interface list maintained by IP Filter with +the current interface status list. +.TP +.B \-z +For each rule in the input file, reset the statistics for it to zero and +display the statistics prior to them being zeroed. +.TP +.B \-Z +Zero global statistics held in the kernel for filtering only (this doesn't +affect fragment or state statistics). +.DT +.SH ENVIRONMENT +.NM utilizes the following environment variable. +.TP +.B IPF_PREDEFINED +ipfilter variables, see VARIABLES in ipf(5), can be specified in this +environment variable providing shell access to ipfilter and ipnat variables. +For example, +.br +IPF_PREDEFINED='my_server="10.1.1.1"; my_client="10.1.1.2";' +.SH FILES +/dev/ipauth +.br +/dev/ipl +.br +/dev/ipstate +.SH SEE ALSO +ipftest(1), mkfilters(1), ipf(4), ipl(4), ipf(5), ipfstat(8), ipmon(8), ipnat(8) +.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@pobox.com diff --git a/sbin/ipf/ipf/ipf.c b/sbin/ipf/ipf/ipf.c new file mode 100644 index 000000000000..406737e25d8e --- /dev/null +++ b/sbin/ipf/ipf/ipf.c @@ -0,0 +1,577 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include "ipf.h" +#include <fcntl.h> +#include <ctype.h> +#include <sys/ioctl.h> +#include "netinet/ipl.h" + +#if !defined(lint) +static const char sccsid[] = "@(#)ipf.c 1.23 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +#if !defined(__SVR4) && defined(__GNUC__) +extern char *index(const char *, int); +#endif + +extern char *optarg; +extern int optind; +extern frentry_t *frtop; + + +void ipf_frsync(void); +void zerostats(void); +int main(int, char *[]); + +int opts = 0; +int outputc = 0; +int use_inet6 = 0; +int exitstatus = 0; + +static void procfile(char *); +static void flushfilter(char *, int *); +static void set_state(u_int); +static void showstats(friostat_t *); +static void packetlogon(char *); +static void swapactive(void); +static int opendevice(char *, int); +static void closedevice(void); +static char *ipfname = IPL_NAME; +static void usage(void); +static int showversion(void); +static int get_flags(void); +static int ipf_interceptadd(int, ioctlfunc_t, void *); + +static int fd = -1; +static ioctlfunc_t iocfunctions[IPL_LOGSIZE] = { ioctl, ioctl, ioctl, + ioctl, ioctl, ioctl, + ioctl, ioctl }; + +/* XXX The following was added to satisfy a rescue/rescue/ build + XXX requirement. */ +int nohdrfields; + +static void usage() +{ + fprintf(stderr, "usage: ipf [-6AdDEInoPrRsvVyzZ] %s %s %s\n", + "[-l block|pass|nomatch|state|nat]", "[-cc] [-F i|o|a|s|S|u]", + "[-f filename] [-T <tuneopts>]"); + exit(1); +} + + +int main(argc,argv) + int argc; + char *argv[]; +{ + int c, *filter = NULL; + + if (argc < 2) + usage(); + + assigndefined(getenv("IPF_PREDEFINED")); + + while ((c = getopt(argc, argv, "46Ac:dDEf:F:Il:m:noPrRsT:vVyzZ")) != -1) { + switch (c) + { + case '?' : + usage(); + break; + case '4' : + use_inet6 = -1; + break; + case '6' : + use_inet6 = 1; + break; + case 'A' : + opts &= ~OPT_INACTIVE; + break; + case 'c' : + if (strcmp(optarg, "c") == 0) + outputc = 1; + break; + case 'E' : + set_state((u_int)1); + break; + case 'D' : + set_state((u_int)0); + break; + case 'd' : + opts ^= OPT_DEBUG; + break; + case 'f' : + procfile(optarg); + break; + case 'F' : + flushfilter(optarg, filter); + break; + case 'I' : + opts ^= OPT_INACTIVE; + break; + case 'l' : + packetlogon(optarg); + break; + case 'm' : + filter = parseipfexpr(optarg, NULL); + break; + case 'n' : + opts ^= OPT_DONOTHING|OPT_DONTOPEN; + break; + case 'o' : + break; + case 'P' : + ipfname = IPAUTH_NAME; + break; + case 'R' : + opts ^= OPT_NORESOLVE; + break; + case 'r' : + opts ^= OPT_REMOVE; + break; + case 's' : + swapactive(); + break; + case 'T' : + if (opendevice(ipfname, 1) >= 0) + ipf_dotuning(fd, optarg, ioctl); + break; + case 'v' : + opts += OPT_VERBOSE; + break; + case 'V' : + if (showversion()) + exit(1); + break; + case 'y' : + ipf_frsync(); + break; + case 'z' : + opts ^= OPT_ZERORULEST; + break; + case 'Z' : + zerostats(); + break; + } + } + + if (optind < 2) + usage(); + + if (fd != -1) + (void) close(fd); + + return(exitstatus); + /* NOTREACHED */ +} + + +static int opendevice(ipfdev, check) + char *ipfdev; + int check; +{ + if (opts & OPT_DONOTHING) + return -2; + + if (check && checkrev(ipfname) == -1) { + fprintf(stderr, "User/kernel version check failed\n"); + return -2; + } + + if (!ipfdev) + ipfdev = ipfname; + + if (fd == -1) + if ((fd = open(ipfdev, O_RDWR)) == -1) + if ((fd = open(ipfdev, O_RDONLY)) == -1) + ipferror(fd, "open device"); + return fd; +} + + +static void closedevice() +{ + close(fd); + fd = -1; +} + + +static int get_flags() +{ + int i = 0; + + if ((opendevice(ipfname, 1) != -2) && + (ioctl(fd, SIOCGETFF, &i) == -1)) { + ipferror(fd, "SIOCGETFF"); + return 0; + } + return i; +} + + +static void set_state(enable) + u_int enable; +{ + if (opendevice(ipfname, 0) != -2) { + if (ioctl(fd, SIOCFRENB, &enable) == -1) { + if (errno == EBUSY) { + fprintf(stderr, + "IP FIlter: already initialized\n"); + } else { + ipferror(fd, "SIOCFRENB"); + } + } + } + return; +} + + +static void procfile(file) + char *file; +{ + (void) opendevice(ipfname, 1); + + initparse(); + + ipf_parsefile(fd, ipf_interceptadd, iocfunctions, file); + + if (outputc) { + printC(0); + printC(1); + emit(-1, -1, NULL, NULL); + } +} + + +static int ipf_interceptadd(fd, ioctlfunc, ptr) + int fd; + ioctlfunc_t ioctlfunc; + void *ptr; +{ + if (outputc) + printc(ptr); + + if (ipf_addrule(fd, ioctlfunc, ptr) != 0) + exitstatus = 1; + return 0; +} + + +static void packetlogon(opt) + char *opt; +{ + int flag, xfd, logopt, change = 0; + + flag = get_flags(); + if (flag != 0) { + if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) + printf("log flag is currently %#x\n", flag); + } + + flag &= ~(FF_LOGPASS|FF_LOGNOMATCH|FF_LOGBLOCK); + + if (strstr(opt, "pass")) { + flag |= FF_LOGPASS; + if (opts & OPT_VERBOSE) + printf("set log flag: pass\n"); + change = 1; + } + if (strstr(opt, "nomatch")) { + flag |= FF_LOGNOMATCH; + if (opts & OPT_VERBOSE) + printf("set log flag: nomatch\n"); + change = 1; + } + if (strstr(opt, "block") || strchr(opt, 'd')) { + flag |= FF_LOGBLOCK; + if (opts & OPT_VERBOSE) + printf("set log flag: block\n"); + change = 1; + } + if (strstr(opt, "none")) { + if (opts & OPT_VERBOSE) + printf("disable all log flags\n"); + change = 1; + } + + if (change == 1) { + if (opendevice(ipfname, 1) != -2 && + (ioctl(fd, SIOCSETFF, &flag) != 0)) + ipferror(fd, "ioctl(SIOCSETFF)"); + } + + if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { + flag = get_flags(); + printf("log flags are now %#x\n", flag); + } + + if (strstr(opt, "state")) { + if (opts & OPT_VERBOSE) + printf("set state log flag\n"); + xfd = open(IPSTATE_NAME, O_RDWR); + if (xfd >= 0) { + logopt = 0; + if (ioctl(xfd, SIOCGETLG, &logopt)) + ipferror(fd, "ioctl(SIOCGETLG)"); + else { + logopt = 1 - logopt; + if (ioctl(xfd, SIOCSETLG, &logopt)) + ipferror(xfd, "ioctl(SIOCSETLG)"); + } + close(xfd); + } + } + + if (strstr(opt, "nat")) { + if (opts & OPT_VERBOSE) + printf("set nat log flag\n"); + xfd = open(IPNAT_NAME, O_RDWR); + if (xfd >= 0) { + logopt = 0; + if (ioctl(xfd, SIOCGETLG, &logopt)) + ipferror(xfd, "ioctl(SIOCGETLG)"); + else { + logopt = 1 - logopt; + if (ioctl(xfd, SIOCSETLG, &logopt)) + ipferror(xfd, "ioctl(SIOCSETLG)"); + } + close(xfd); + } + } +} + + +static void flushfilter(arg, filter) + char *arg; + int *filter; +{ + int fl = 0, rem; + + if (!arg || !*arg) + return; + if (!strcmp(arg, "s") || !strcmp(arg, "S") || ISDIGIT(*arg)) { + if (*arg == 'S') + fl = 0; + else if (*arg == 's') + fl = 1; + else + fl = atoi(arg); + rem = fl; + + closedevice(); + if (opendevice(IPSTATE_NAME, 1) == -2) + exit(1); + + if (!(opts & OPT_DONOTHING)) { + if (use_inet6) { + fprintf(stderr, + "IPv6 rules are no longer seperate\n"); + } else if (filter != NULL) { + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = filter[0] * sizeof(int); + obj.ipfo_type = IPFOBJ_IPFEXPR; + obj.ipfo_ptr = filter; + if (ioctl(fd, SIOCMATCHFLUSH, &obj) == -1) { + ipferror(fd, "ioctl(SIOCMATCHFLUSH)"); + fl = -1; + } else { + fl = obj.ipfo_retval; + } + } else { + if (ioctl(fd, SIOCIPFFL, &fl) == -1) { + ipferror(fd, "ioctl(SIOCIPFFL)"); + exit(1); + } + } + } + if ((opts & (OPT_DONOTHING|OPT_DEBUG)) == OPT_DEBUG) { + printf("remove flags %s (%d)\n", arg, rem); + } + if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { + printf("%d state entries removed\n", fl); + } + closedevice(); + return; + } else if (strchr(arg, 'i') || strchr(arg, 'I')) + fl = FR_INQUE; + else if (strchr(arg, 'o') || strchr(arg, 'O')) + fl = FR_OUTQUE; + else if (strchr(arg, 'a') || strchr(arg, 'A')) + fl = FR_OUTQUE|FR_INQUE; + else { + fprintf(stderr, "Incorrect flush argument: %s\n", arg); + usage(); + } + if (opts & OPT_INACTIVE) + fl |= FR_INACTIVE; + rem = fl; + + if (opendevice(ipfname, 1) == -2) + exit(1); + + if (!(opts & OPT_DONOTHING)) { + if (use_inet6) { + if (ioctl(fd, SIOCIPFL6, &fl) == -1) { + ipferror(fd, "ioctl(SIOCIPFL6)"); + exit(1); + } + } else { + if (ioctl(fd, SIOCIPFFL, &fl) == -1) { + ipferror(fd, "ioctl(SIOCIPFFL)"); + exit(1); + } + } + } + + if ((opts & (OPT_DONOTHING|OPT_DEBUG)) == OPT_DEBUG) { + printf("remove flags %s%s (%d)\n", (rem & FR_INQUE) ? "I" : "", + (rem & FR_OUTQUE) ? "O" : "", rem); + } + if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { + printf("%d filter rules removed\n", fl); + } + return; +} + + +static void swapactive() +{ + int in = 2; + + if (opendevice(ipfname, 1) != -2 && ioctl(fd, SIOCSWAPA, &in) == -1) + ipferror(fd, "ioctl(SIOCSWAPA)"); + else + printf("Set %d now inactive\n", in); +} + + +void ipf_frsync() +{ + int frsyn = 0; + + if (opendevice(ipfname, 1) != -2 && ioctl(fd, SIOCFRSYN, &frsyn) == -1) + ipferror(fd, "SIOCFRSYN"); + else + printf("filter sync'd\n"); +} + + +void zerostats() +{ + ipfobj_t obj; + friostat_t fio; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_IPFSTAT; + obj.ipfo_size = sizeof(fio); + obj.ipfo_ptr = &fio; + obj.ipfo_offset = 0; + + if (opendevice(ipfname, 1) != -2) { + if (ioctl(fd, SIOCFRZST, &obj) == -1) { + ipferror(fd, "ioctl(SIOCFRZST)"); + exit(-1); + } + showstats(&fio); + } + +} + + +/* + * read the kernel stats for packets blocked and passed + */ +static void showstats(fp) + friostat_t *fp; +{ + printf("bad packets:\t\tin %lu\tout %lu\n", + fp->f_st[0].fr_bad, fp->f_st[1].fr_bad); + printf(" input packets:\t\tblocked %lu passed %lu nomatch %lu", + fp->f_st[0].fr_block, fp->f_st[0].fr_pass, + fp->f_st[0].fr_nom); + printf(" counted %lu\n", fp->f_st[0].fr_acct); + printf("output packets:\t\tblocked %lu passed %lu nomatch %lu", + fp->f_st[1].fr_block, fp->f_st[1].fr_pass, + fp->f_st[1].fr_nom); + printf(" counted %lu\n", fp->f_st[0].fr_acct); + printf(" input packets logged:\tblocked %lu passed %lu\n", + fp->f_st[0].fr_bpkl, fp->f_st[0].fr_ppkl); + printf("output packets logged:\tblocked %lu passed %lu\n", + fp->f_st[1].fr_bpkl, fp->f_st[1].fr_ppkl); +} + + +static int showversion() +{ + struct friostat fio; + ipfobj_t ipfo; + u_32_t flags; + char *s; + int vfd; + + bzero((caddr_t)&ipfo, sizeof(ipfo)); + ipfo.ipfo_rev = IPFILTER_VERSION; + ipfo.ipfo_size = sizeof(fio); + ipfo.ipfo_ptr = (void *)&fio; + ipfo.ipfo_type = IPFOBJ_IPFSTAT; + + printf("ipf: %s (%d)\n", IPL_VERSION, (int)sizeof(frentry_t)); + + if ((vfd = open(ipfname, O_RDONLY)) == -1) { + perror("open device"); + return 1; + } + + if (ioctl(vfd, SIOCGETFS, &ipfo)) { + ipferror(vfd, "ioctl(SIOCGETFS)"); + close(vfd); + return 1; + } + close(vfd); + flags = get_flags(); + + printf("Kernel: %-*.*s\n", (int)sizeof(fio.f_version), + (int)sizeof(fio.f_version), fio.f_version); + printf("Running: %s\n", (fio.f_running > 0) ? "yes" : "no"); + printf("Log Flags: %#x = ", flags); + s = ""; + if (flags & FF_LOGPASS) { + printf("pass"); + s = ", "; + } + if (flags & FF_LOGBLOCK) { + printf("%sblock", s); + s = ", "; + } + if (flags & FF_LOGNOMATCH) { + printf("%snomatch", s); + s = ", "; + } + if (flags & FF_BLOCKNONIP) { + printf("%snonip", s); + s = ", "; + } + if (!*s) + printf("none set"); + putchar('\n'); + + printf("Default: "); + if (FR_ISPASS(fio.f_defpass)) + s = "pass"; + else if (FR_ISBLOCK(fio.f_defpass)) + s = "block"; + else + s = "nomatch -> block"; + printf("%s all, Logging: %savailable\n", s, fio.f_logging ? "" : "un"); + printf("Active list: %d\n", fio.f_active); + printf("Feature mask: %#x\n", fio.f_features); + + return 0; +} diff --git a/sbin/ipf/ipf/ipfcomp.c b/sbin/ipf/ipf/ipfcomp.c new file mode 100644 index 000000000000..5bbb35c610fa --- /dev/null +++ b/sbin/ipf/ipf/ipfcomp.c @@ -0,0 +1,1372 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +#include "ipf.h" + + +typedef struct { + int c; + int e; + int n; + int p; + int s; +} mc_t; + + +static char *portcmp[] = { "*", "==", "!=", "<", ">", "<=", ">=", "**", "***" }; +static int count = 0; + +int intcmp(const void *, const void *); +static void indent(FILE *, int); +static void printeq(FILE *, char *, int, int, int); +static void printipeq(FILE *, char *, int, int, int); +static void addrule(FILE *, frentry_t *); +static void printhooks(FILE *, int, int, frgroup_t *); +static void emitheader(frgroup_t *, u_int, u_int); +static void emitGroup(int, int, void *, frentry_t *, char *, + u_int, u_int); +static void emittail(void); +static void printCgroup(int, frentry_t *, mc_t *, char *); + +#define FRC_IFN 0 +#define FRC_V 1 +#define FRC_P 2 +#define FRC_FL 3 +#define FRC_TOS 4 +#define FRC_TTL 5 +#define FRC_SRC 6 +#define FRC_DST 7 +#define FRC_TCP 8 +#define FRC_SP 9 +#define FRC_DP 10 +#define FRC_OPT 11 +#define FRC_SEC 12 +#define FRC_ATH 13 +#define FRC_ICT 14 +#define FRC_ICC 15 +#define FRC_MAX 16 + + +static FILE *cfile = NULL; + +/* + * This is called once per filter rule being loaded to emit data structures + * required. + */ +void printc(fr) + frentry_t *fr; +{ + u_long *ulp; + char *and; + FILE *fp; + int i; + + if (fr->fr_family == 6) + return; + if ((fr->fr_type != FR_T_IPF) && (fr->fr_type != FR_T_NONE)) + return; + if ((fr->fr_type == FR_T_IPF) && + ((fr->fr_datype != FRI_NORMAL) || (fr->fr_satype != FRI_NORMAL))) + return; + + if (cfile == NULL) + cfile = fopen("ip_rules.c", "w"); + if (cfile == NULL) + return; + fp = cfile; + if (count == 0) { + fprintf(fp, "/*\n"); + fprintf(fp, "* Copyright (C) 2012 by Darren Reed.\n"); + fprintf(fp, "*\n"); + fprintf(fp, "* Redistribution and use in source and binary forms are permitted\n"); + fprintf(fp, "* provided that this notice is preserved and due credit is given\n"); + fprintf(fp, "* to the original author and the contributors.\n"); + fprintf(fp, "*/\n\n"); + + fprintf(fp, "#include <sys/param.h>\n"); + fprintf(fp, "#include <sys/types.h>\n"); + fprintf(fp, "#include <sys/time.h>\n"); + fprintf(fp, "#include <sys/socket.h>\n"); + fprintf(fp, "#if (__FreeBSD_version >= 40000)\n"); + fprintf(fp, "# if defined(_KERNEL)\n"); + fprintf(fp, "# include <sys/libkern.h>\n"); + fprintf(fp, "# else\n"); + fprintf(fp, "# include <sys/unistd.h>\n"); + fprintf(fp, "# endif\n"); + fprintf(fp, "#endif\n"); + fprintf(fp, "#if (__NetBSD_Version__ >= 399000000)\n"); + fprintf(fp, "#else\n"); + fprintf(fp, "# if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi)\n"); + fprintf(fp, "# include <sys/systm.h>\n"); + fprintf(fp, "# endif\n"); + fprintf(fp, "#endif\n"); + fprintf(fp, "#include <sys/errno.h>\n"); + fprintf(fp, "#include <sys/param.h>\n"); + fprintf(fp, +"#if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux)\n"); + fprintf(fp, "# include <sys/mbuf.h>\n"); + fprintf(fp, "#endif\n"); + fprintf(fp, +"#if defined(__FreeBSD__) && (__FreeBSD_version > 220000)\n"); + fprintf(fp, "# include <sys/sockio.h>\n"); + fprintf(fp, "#else\n"); + fprintf(fp, "# include <sys/ioctl.h>\n"); + fprintf(fp, "#endif /* FreeBSD */\n"); + fprintf(fp, "#include <net/if.h>\n"); + fprintf(fp, "#include <netinet/in.h>\n"); + fprintf(fp, "#include <netinet/in_systm.h>\n"); + fprintf(fp, "#include <netinet/ip.h>\n"); + fprintf(fp, "#include <netinet/tcp.h>\n"); + fprintf(fp, "#include \"netinet/ip_compat.h\"\n"); + fprintf(fp, "#include \"netinet/ip_fil.h\"\n\n"); + fprintf(fp, "#include \"netinet/ip_rules.h\"\n\n"); + fprintf(fp, "#ifndef _KERNEL\n"); + fprintf(fp, "# include <string.h>\n"); + fprintf(fp, "#endif /* _KERNEL */\n"); + fprintf(fp, "\n"); + fprintf(fp, "#ifdef IPFILTER_COMPILED\n"); + fprintf(fp, "\n"); + fprintf(fp, "extern ipf_main_softc_t ipfmain;\n"); + fprintf(fp, "\n"); + } + + addrule(fp, fr); + fr->fr_type |= FR_T_BUILTIN; + and = ""; + fr->fr_ref = 1; + i = sizeof(*fr); + if (i & -(1 - sizeof(*ulp))) + i += sizeof(u_long); + for (i /= sizeof(u_long), ulp = (u_long *)fr; i > 0; i--) { + fprintf(fp, "%s%#lx", and, *ulp++); + and = ", "; + } + fprintf(fp, "\n};\n"); + fr->fr_type &= ~FR_T_BUILTIN; + + count++; + + fflush(fp); +} + + +static frgroup_t *groups = NULL; + + +static void addrule(fp, fr) + FILE *fp; + frentry_t *fr; +{ + frentry_t *f, **fpp; + frgroup_t *g; + u_long *ulp; + char *ghead; + char *gname; + char *and; + int i; + + f = (frentry_t *)malloc(sizeof(*f)); + bcopy((char *)fr, (char *)f, sizeof(*fr)); + if (fr->fr_ipf) { + f->fr_ipf = (fripf_t *)malloc(sizeof(*f->fr_ipf)); + bcopy((char *)fr->fr_ipf, (char *)f->fr_ipf, + sizeof(*fr->fr_ipf)); + } + + f->fr_next = NULL; + gname = FR_NAME(fr, fr_group); + + for (g = groups; g != NULL; g = g->fg_next) + if ((strncmp(g->fg_name, gname, FR_GROUPLEN) == 0) && + (g->fg_flags == (f->fr_flags & FR_INOUT))) + break; + + if (g == NULL) { + g = (frgroup_t *)calloc(1, sizeof(*g)); + g->fg_next = groups; + groups = g; + g->fg_head = f; + strncpy(g->fg_name, gname, FR_GROUPLEN); + g->fg_ref = 0; + g->fg_flags = f->fr_flags & FR_INOUT; + } + + for (fpp = &g->fg_start; *fpp != NULL; ) + fpp = &((*fpp)->fr_next); + *fpp = f; + + if (fr->fr_dsize > 0) { + fprintf(fp, "\ +static u_long ipf%s_rule_data_%s_%u[] = {\n", + f->fr_flags & FR_INQUE ? "in" : "out", + g->fg_name, g->fg_ref); + and = ""; + i = fr->fr_dsize; + ulp = fr->fr_data; + for (i /= sizeof(u_long); i > 0; i--) { + fprintf(fp, "%s%#lx", and, *ulp++); + and = ", "; + } + fprintf(fp, "\n};\n"); + } + + fprintf(fp, "\nstatic u_long %s_rule_%s_%d[] = {\n", + f->fr_flags & FR_INQUE ? "in" : "out", g->fg_name, g->fg_ref); + + g->fg_ref++; + + if (f->fr_grhead != -1) { + ghead = FR_NAME(f, fr_grhead); + for (g = groups; g != NULL; g = g->fg_next) + if ((strncmp(g->fg_name, ghead, FR_GROUPLEN) == 0) && + g->fg_flags == (f->fr_flags & FR_INOUT)) + break; + if (g == NULL) { + g = (frgroup_t *)calloc(1, sizeof(*g)); + g->fg_next = groups; + groups = g; + g->fg_head = f; + strncpy(g->fg_name, ghead, FR_GROUPLEN); + g->fg_ref = 0; + g->fg_flags = f->fr_flags & FR_INOUT; + } + } +} + + +int intcmp(c1, c2) + const void *c1, *c2; +{ + const mc_t *i1 = (const mc_t *)c1, *i2 = (const mc_t *)c2; + + if (i1->n == i2->n) { + return i1->c - i2->c; + } + return i2->n - i1->n; +} + + +static void indent(fp, in) + FILE *fp; + int in; +{ + for (; in; in--) + fputc('\t', fp); +} + +static void printeq(fp, var, m, max, v) + FILE *fp; + char *var; + int m, max, v; +{ + if (m == max) + fprintf(fp, "%s == %#x) {\n", var, v); + else + fprintf(fp, "(%s & %#x) == %#x) {\n", var, m, v); +} + +/* + * Parameters: var - IP# being compared + * fl - 0 for positive match, 1 for negative match + * m - netmask + * v - required address + */ +static void printipeq(fp, var, fl, m, v) + FILE *fp; + char *var; + int fl, m, v; +{ + if (m == 0xffffffff) + fprintf(fp, "%s ", var); + else + fprintf(fp, "(%s & %#x) ", var, m); + fprintf(fp, "%c", fl ? '!' : '='); + fprintf(fp, "= %#x) {\n", v); +} + + +void emit(num, dir, v, fr) + int num, dir; + void *v; + frentry_t *fr; +{ + u_int incnt, outcnt; + frgroup_t *g; + frentry_t *f; + + for (g = groups; g != NULL; g = g->fg_next) { + if (dir == 0 || dir == -1) { + if ((g->fg_flags & FR_INQUE) == 0) + continue; + for (incnt = 0, f = g->fg_start; f != NULL; + f = f->fr_next) + incnt++; + emitGroup(num, dir, v, fr, g->fg_name, incnt, 0); + } + if (dir == 1 || dir == -1) { + if ((g->fg_flags & FR_OUTQUE) == 0) + continue; + for (outcnt = 0, f = g->fg_start; f != NULL; + f = f->fr_next) + outcnt++; + emitGroup(num, dir, v, fr, g->fg_name, 0, outcnt); + } + } + + if (num == -1 && dir == -1) { + for (g = groups; g != NULL; g = g->fg_next) { + if ((g->fg_flags & FR_INQUE) != 0) { + for (incnt = 0, f = g->fg_start; f != NULL; + f = f->fr_next) + incnt++; + if (incnt > 0) + emitheader(g, incnt, 0); + } + if ((g->fg_flags & FR_OUTQUE) != 0) { + for (outcnt = 0, f = g->fg_start; f != NULL; + f = f->fr_next) + outcnt++; + if (outcnt > 0) + emitheader(g, 0, outcnt); + } + } + emittail(); + fprintf(cfile, "#endif /* IPFILTER_COMPILED */\n"); + } + +} + + +static void emitheader(grp, incount, outcount) + frgroup_t *grp; + u_int incount, outcount; +{ + static FILE *fph = NULL; + frgroup_t *g; + + if (fph == NULL) { + fph = fopen("ip_rules.h", "w"); + if (fph == NULL) + return; + + fprintf(fph, "extern int ipfrule_add(void));\n"); + fprintf(fph, "extern int ipfrule_remove(void));\n"); + } + + printhooks(cfile, incount, outcount, grp); + + if (incount) { + fprintf(fph, "\n\ +extern frentry_t *ipfrule_match_in_%s(fr_info_t *, u_32_t *));\n\ +extern frentry_t *ipf_rules_in_%s[%d];\n", + grp->fg_name, grp->fg_name, incount); + + for (g = groups; g != grp; g = g->fg_next) + if ((strncmp(g->fg_name, grp->fg_name, + FR_GROUPLEN) == 0) && + g->fg_flags == grp->fg_flags) + break; + if (g == grp) { + fprintf(fph, "\n\ +extern int ipfrule_add_in_%s(void));\n\ +extern int ipfrule_remove_in_%s(void));\n", grp->fg_name, grp->fg_name); + } + } + if (outcount) { + fprintf(fph, "\n\ +extern frentry_t *ipfrule_match_out_%s(fr_info_t *, u_32_t *));\n\ +extern frentry_t *ipf_rules_out_%s[%d];\n", + grp->fg_name, grp->fg_name, outcount); + + for (g = groups; g != grp; g = g->fg_next) + if ((strncmp(g->fg_name, grp->fg_name, + FR_GROUPLEN) == 0) && + g->fg_flags == grp->fg_flags) + break; + if (g == grp) { + fprintf(fph, "\n\ +extern int ipfrule_add_out_%s(void));\n\ +extern int ipfrule_remove_out_%s(void));\n", + grp->fg_name, grp->fg_name); + } + } +} + +static void emittail() +{ + frgroup_t *g; + + fprintf(cfile, "\n\ +int ipfrule_add()\n\ +{\n\ + int err;\n\ +\n"); + for (g = groups; g != NULL; g = g->fg_next) + fprintf(cfile, "\ + err = ipfrule_add_%s_%s();\n\ + if (err != 0)\n\ + return err;\n", + (g->fg_flags & FR_INQUE) ? "in" : "out", g->fg_name); + fprintf(cfile, "\ + return 0;\n"); + fprintf(cfile, "}\n\ +\n"); + + fprintf(cfile, "\n\ +int ipfrule_remove()\n\ +{\n\ + int err;\n\ +\n"); + for (g = groups; g != NULL; g = g->fg_next) + fprintf(cfile, "\ + err = ipfrule_remove_%s_%s();\n\ + if (err != 0)\n\ + return err;\n", + (g->fg_flags & FR_INQUE) ? "in" : "out", g->fg_name); + fprintf(cfile, "\ + return 0;\n"); + fprintf(cfile, "}\n"); +} + + +static void emitGroup(num, dir, v, fr, group, incount, outcount) + int num, dir; + void *v; + frentry_t *fr; + char *group; + u_int incount, outcount; +{ + static FILE *fp = NULL; + static int header[2] = { 0, 0 }; + static char egroup[FR_GROUPLEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + static int openfunc = 0; + static mc_t *n = NULL; + static int sin = 0; + frentry_t *f; + frgroup_t *g; + fripf_t *ipf; + int i, in, j; + mc_t *m = v; + + if (fp == NULL) + fp = cfile; + if (fp == NULL) + return; + if (strncmp(egroup, group, FR_GROUPLEN)) { + for (sin--; sin > 0; sin--) { + indent(fp, sin); + fprintf(fp, "}\n"); + } + if (openfunc == 1) { + fprintf(fp, "\treturn fr;\n}\n"); + openfunc = 0; + if (n != NULL) { + free(n); + n = NULL; + } + } + sin = 0; + header[0] = 0; + header[1] = 0; + strncpy(egroup, group, FR_GROUPLEN); + } else if (openfunc == 1 && num < 0) { + if (n != NULL) { + free(n); + n = NULL; + } + for (sin--; sin > 0; sin--) { + indent(fp, sin); + fprintf(fp, "}\n"); + } + if (openfunc == 1) { + fprintf(fp, "\treturn fr;\n}\n"); + openfunc = 0; + } + } + + if (dir == -1) + return; + + for (g = groups; g != NULL; g = g->fg_next) { + if (dir == 0 && (g->fg_flags & FR_INQUE) == 0) + continue; + else if (dir == 1 && (g->fg_flags & FR_OUTQUE) == 0) + continue; + if (strncmp(g->fg_name, group, FR_GROUPLEN) != 0) + continue; + break; + } + + /* + * Output the array of pointers to rules for this group. + */ + if (g != NULL && num == -2 && dir == 0 && header[0] == 0 && + incount != 0) { + fprintf(fp, "\nfrentry_t *ipf_rules_in_%s[%d] = {", + group, incount); + for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) { + if ((f->fr_flags & FR_INQUE) == 0) + continue; + if ((i & 1) == 0) { + fprintf(fp, "\n\t"); + } + fprintf(fp, "(frentry_t *)&in_rule_%s_%d", + FR_NAME(f, fr_group), i); + if (i + 1 < incount) + fprintf(fp, ", "); + i++; + } + fprintf(fp, "\n};\n"); + } + + if (g != NULL && num == -2 && dir == 1 && header[0] == 0 && + outcount != 0) { + fprintf(fp, "\nfrentry_t *ipf_rules_out_%s[%d] = {", + group, outcount); + for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) { + if ((f->fr_flags & FR_OUTQUE) == 0) + continue; + if ((i & 1) == 0) { + fprintf(fp, "\n\t"); + } + fprintf(fp, "(frentry_t *)&out_rule_%s_%d", + FR_NAME(f, fr_group), i); + if (i + 1 < outcount) + fprintf(fp, ", "); + i++; + } + fprintf(fp, "\n};\n"); + fp = NULL; + } + + if (num < 0) + return; + + in = 0; + ipf = fr->fr_ipf; + + /* + * If the function header has not been printed then print it now. + */ + if (g != NULL && header[dir] == 0) { + int pdst = 0, psrc = 0; + + openfunc = 1; + fprintf(fp, "\nfrentry_t *ipfrule_match_%s_%s(fin, passp)\n", + (dir == 0) ? "in" : "out", group); + fprintf(fp, "fr_info_t *fin;\n"); + fprintf(fp, "u_32_t *passp;\n"); + fprintf(fp, "{\n"); + fprintf(fp, "\tfrentry_t *fr = NULL;\n"); + + /* + * Print out any variables that need to be declared. + */ + for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) { + if (incount + outcount > m[FRC_SRC].e + 1) + psrc = 1; + if (incount + outcount > m[FRC_DST].e + 1) + pdst = 1; + } + if (psrc == 1) + fprintf(fp, "\tu_32_t src = ntohl(%s);\n", + "fin->fin_fi.fi_saddr"); + if (pdst == 1) + fprintf(fp, "\tu_32_t dst = ntohl(%s);\n", + "fin->fin_fi.fi_daddr"); + } + + for (i = 0; i < FRC_MAX; i++) { + switch(m[i].c) + { + case FRC_IFN : + if (fr->fr_ifnames[0] != -1) + m[i].s = 1; + break; + case FRC_V : + if (ipf != NULL && ipf->fri_mip.fi_v != 0) + m[i].s = 1; + break; + case FRC_FL : + if (ipf != NULL && ipf->fri_mip.fi_flx != 0) + m[i].s = 1; + break; + case FRC_P : + if (ipf != NULL && ipf->fri_mip.fi_p != 0) + m[i].s = 1; + break; + case FRC_TTL : + if (ipf != NULL && ipf->fri_mip.fi_ttl != 0) + m[i].s = 1; + break; + case FRC_TOS : + if (ipf != NULL && ipf->fri_mip.fi_tos != 0) + m[i].s = 1; + break; + case FRC_TCP : + if (ipf == NULL) + break; + if ((ipf->fri_ip.fi_p == IPPROTO_TCP) && + fr->fr_tcpfm != 0) + m[i].s = 1; + break; + case FRC_SP : + if (ipf == NULL) + break; + if (fr->fr_scmp == FR_INRANGE) + m[i].s = 1; + else if (fr->fr_scmp == FR_OUTRANGE) + m[i].s = 1; + else if (fr->fr_scmp != 0) + m[i].s = 1; + break; + case FRC_DP : + if (ipf == NULL) + break; + if (fr->fr_dcmp == FR_INRANGE) + m[i].s = 1; + else if (fr->fr_dcmp == FR_OUTRANGE) + m[i].s = 1; + else if (fr->fr_dcmp != 0) + m[i].s = 1; + break; + case FRC_SRC : + if (ipf == NULL) + break; + if (fr->fr_satype == FRI_LOOKUP) { + ; + } else if ((fr->fr_smask != 0) || + (fr->fr_flags & FR_NOTSRCIP) != 0) + m[i].s = 1; + break; + case FRC_DST : + if (ipf == NULL) + break; + if (fr->fr_datype == FRI_LOOKUP) { + ; + } else if ((fr->fr_dmask != 0) || + (fr->fr_flags & FR_NOTDSTIP) != 0) + m[i].s = 1; + break; + case FRC_OPT : + if (ipf == NULL) + break; + if (fr->fr_optmask != 0) + m[i].s = 1; + break; + case FRC_SEC : + if (ipf == NULL) + break; + if (fr->fr_secmask != 0) + m[i].s = 1; + break; + case FRC_ATH : + if (ipf == NULL) + break; + if (fr->fr_authmask != 0) + m[i].s = 1; + break; + case FRC_ICT : + if (ipf == NULL) + break; + if ((fr->fr_icmpm & 0xff00) != 0) + m[i].s = 1; + break; + case FRC_ICC : + if (ipf == NULL) + break; + if ((fr->fr_icmpm & 0xff) != 0) + m[i].s = 1; + break; + } + } + + if (!header[dir]) { + fprintf(fp, "\n"); + header[dir] = 1; + sin = 0; + } + + qsort(m, FRC_MAX, sizeof(mc_t), intcmp); + + if (n) { + /* + * Calculate the indentation interval upto the last common + * common comparison being made. + */ + for (i = 0, in = 1; i < FRC_MAX; i++) { + if (n[i].c != m[i].c) + break; + if (n[i].s != m[i].s) + break; + if (n[i].s) { + if (n[i].n && (n[i].n > n[i].e)) { + m[i].p++; + in += m[i].p; + break; + } + if (n[i].e > 0) { + in++; + } else + break; + } + } + if (sin != in) { + for (j = sin - 1; j >= in; j--) { + indent(fp, j); + fprintf(fp, "}\n"); + } + } + } else { + in = 1; + i = 0; + } + + /* + * print out C code that implements a filter rule. + */ + for (; i < FRC_MAX; i++) { + switch(m[i].c) + { + case FRC_IFN : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if (fin->fin_ifp == "); + fprintf(fp, "ipf_rules_%s_%s[%d]->fr_ifa) {\n", + dir ? "out" : "in", group, num); + in++; + } + break; + case FRC_V : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if (fin->fin_v == %d) {\n", + ipf->fri_ip.fi_v); + in++; + } + break; + case FRC_FL : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if ("); + printeq(fp, "fin->fin_flx", + ipf->fri_mip.fi_flx, 0xf, + ipf->fri_ip.fi_flx); + in++; + } + break; + case FRC_P : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if (fin->fin_p == %d) {\n", + ipf->fri_ip.fi_p); + in++; + } + break; + case FRC_TTL : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if ("); + printeq(fp, "fin->fin_ttl", + ipf->fri_mip.fi_ttl, 0xff, + ipf->fri_ip.fi_ttl); + in++; + } + break; + case FRC_TOS : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if (fin->fin_tos"); + printeq(fp, "fin->fin_tos", + ipf->fri_mip.fi_tos, 0xff, + ipf->fri_ip.fi_tos); + in++; + } + break; + case FRC_TCP : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if ("); + printeq(fp, "fin->fin_tcpf", fr->fr_tcpfm, + 0xff, fr->fr_tcpf); + in++; + } + break; + case FRC_SP : + if (!m[i].s) + break; + if (fr->fr_scmp == FR_INRANGE) { + indent(fp, in); + fprintf(fp, "if ((fin->fin_data[0] > %d) && ", + fr->fr_sport); + fprintf(fp, "(fin->fin_data[0] < %d)", + fr->fr_stop); + fprintf(fp, ") {\n"); + in++; + } else if (fr->fr_scmp == FR_OUTRANGE) { + indent(fp, in); + fprintf(fp, "if ((fin->fin_data[0] < %d) || ", + fr->fr_sport); + fprintf(fp, "(fin->fin_data[0] > %d)", + fr->fr_stop); + fprintf(fp, ") {\n"); + in++; + } else if (fr->fr_scmp) { + indent(fp, in); + fprintf(fp, "if (fin->fin_data[0] %s %d)", + portcmp[fr->fr_scmp], fr->fr_sport); + fprintf(fp, " {\n"); + in++; + } + break; + case FRC_DP : + if (!m[i].s) + break; + if (fr->fr_dcmp == FR_INRANGE) { + indent(fp, in); + fprintf(fp, "if ((fin->fin_data[1] > %d) && ", + fr->fr_dport); + fprintf(fp, "(fin->fin_data[1] < %d)", + fr->fr_dtop); + fprintf(fp, ") {\n"); + in++; + } else if (fr->fr_dcmp == FR_OUTRANGE) { + indent(fp, in); + fprintf(fp, "if ((fin->fin_data[1] < %d) || ", + fr->fr_dport); + fprintf(fp, "(fin->fin_data[1] > %d)", + fr->fr_dtop); + fprintf(fp, ") {\n"); + in++; + } else if (fr->fr_dcmp) { + indent(fp, in); + fprintf(fp, "if (fin->fin_data[1] %s %d)", + portcmp[fr->fr_dcmp], fr->fr_dport); + fprintf(fp, " {\n"); + in++; + } + break; + case FRC_SRC : + if (!m[i].s) + break; + if (fr->fr_satype == FRI_LOOKUP) { + ; + } else if ((fr->fr_smask != 0) || + (fr->fr_flags & FR_NOTSRCIP) != 0) { + indent(fp, in); + fprintf(fp, "if ("); + printipeq(fp, "src", + fr->fr_flags & FR_NOTSRCIP, + fr->fr_smask, fr->fr_saddr); + in++; + } + break; + case FRC_DST : + if (!m[i].s) + break; + if (fr->fr_datype == FRI_LOOKUP) { + ; + } else if ((fr->fr_dmask != 0) || + (fr->fr_flags & FR_NOTDSTIP) != 0) { + indent(fp, in); + fprintf(fp, "if ("); + printipeq(fp, "dst", + fr->fr_flags & FR_NOTDSTIP, + fr->fr_dmask, fr->fr_daddr); + in++; + } + break; + case FRC_OPT : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if ("); + printeq(fp, "fin->fin_fi.fi_optmsk", + fr->fr_optmask, 0xffffffff, + fr->fr_optbits); + in++; + } + break; + case FRC_SEC : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if ("); + printeq(fp, "fin->fin_fi.fi_secmsk", + fr->fr_secmask, 0xffff, + fr->fr_secbits); + in++; + } + break; + case FRC_ATH : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if ("); + printeq(fp, "fin->fin_fi.fi_authmsk", + fr->fr_authmask, 0xffff, + fr->fr_authbits); + in++; + } + break; + case FRC_ICT : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if ("); + printeq(fp, "fin->fin_data[0]", + fr->fr_icmpm & 0xff00, 0xffff, + fr->fr_icmp & 0xff00); + in++; + } + break; + case FRC_ICC : + if (m[i].s) { + indent(fp, in); + fprintf(fp, "if ("); + printeq(fp, "fin->fin_data[0]", + fr->fr_icmpm & 0xff, 0xffff, + fr->fr_icmp & 0xff); + in++; + } + break; + } + + } + + indent(fp, in); + if (fr->fr_flags & FR_QUICK) { + fprintf(fp, "return (frentry_t *)&%s_rule_%s_%d;\n", + fr->fr_flags & FR_INQUE ? "in" : "out", + FR_NAME(fr, fr_group), num); + } else { + fprintf(fp, "fr = (frentry_t *)&%s_rule_%s_%d;\n", + fr->fr_flags & FR_INQUE ? "in" : "out", + FR_NAME(fr, fr_group), num); + } + if (n == NULL) + n = (mc_t *)malloc(sizeof(*n) * FRC_MAX); + bcopy((char *)m, (char *)n, sizeof(*n) * FRC_MAX); + sin = in; +} + + +void printC(dir) + int dir; +{ + static mc_t *m = NULL; + frgroup_t *g; + + if (m == NULL) + m = (mc_t *)calloc(FRC_MAX, sizeof(*m)); + + for (g = groups; g != NULL; g = g->fg_next) { + if ((dir == 0) && ((g->fg_flags & FR_INQUE) != 0)) + printCgroup(dir, g->fg_start, m, g->fg_name); + if ((dir == 1) && ((g->fg_flags & FR_OUTQUE) != 0)) + printCgroup(dir, g->fg_start, m, g->fg_name); + } + + emit(-1, dir, m, NULL); +} + + +/* + * Now print out code to implement all of the rules. + */ +static void printCgroup(dir, top, m, group) + int dir; + frentry_t *top; + mc_t *m; + char *group; +{ + frentry_t *fr, *fr1; + int i, n, rn; + u_int count; + + for (count = 0, fr1 = top; fr1 != NULL; fr1 = fr1->fr_next) { + if ((dir == 0) && ((fr1->fr_flags & FR_INQUE) != 0)) + count++; + else if ((dir == 1) && ((fr1->fr_flags & FR_OUTQUE) != 0)) + count++; + } + + if (dir == 0) + emitGroup(-2, dir, m, fr1, group, count, 0); + else if (dir == 1) + emitGroup(-2, dir, m, fr1, group, 0, count); + + /* + * Before printing each rule, check to see how many of its fields are + * matched by subsequent rules. + */ + for (fr1 = top, rn = 0; fr1 != NULL; fr1 = fr1->fr_next, rn++) { + if (!dir && !(fr1->fr_flags & FR_INQUE)) + continue; + if (dir && !(fr1->fr_flags & FR_OUTQUE)) + continue; + n = 0xfffffff; + + for (i = 0; i < FRC_MAX; i++) + m[i].e = 0; + qsort(m, FRC_MAX, sizeof(mc_t), intcmp); + + for (i = 0; i < FRC_MAX; i++) { + m[i].c = i; + m[i].e = 0; + m[i].n = 0; + m[i].s = 0; + } + + for (fr = fr1->fr_next; fr; fr = fr->fr_next) { + if (!dir && !(fr->fr_flags & FR_INQUE)) + continue; + if (dir && !(fr->fr_flags & FR_OUTQUE)) + continue; + + if ((n & 0x0001) && + !strcmp(fr1->fr_names + fr1->fr_ifnames[0], + fr->fr_names + fr->fr_ifnames[0])) { + m[FRC_IFN].e++; + m[FRC_IFN].n++; + } else + n &= ~0x0001; + + if ((n & 0x0002) && (fr1->fr_family == fr->fr_family)) { + m[FRC_V].e++; + m[FRC_V].n++; + } else + n &= ~0x0002; + + if ((n & 0x0004) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (fr1->fr_mip.fi_flx == fr->fr_mip.fi_flx) && + (fr1->fr_ip.fi_flx == fr->fr_ip.fi_flx)) { + m[FRC_FL].e++; + m[FRC_FL].n++; + } else + n &= ~0x0004; + + if ((n & 0x0008) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (fr1->fr_proto == fr->fr_proto)) { + m[FRC_P].e++; + m[FRC_P].n++; + } else + n &= ~0x0008; + + if ((n & 0x0010) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (fr1->fr_ttl == fr->fr_ttl)) { + m[FRC_TTL].e++; + m[FRC_TTL].n++; + } else + n &= ~0x0010; + + if ((n & 0x0020) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (fr1->fr_tos == fr->fr_tos)) { + m[FRC_TOS].e++; + m[FRC_TOS].n++; + } else + n &= ~0x0020; + + if ((n & 0x0040) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + ((fr1->fr_tcpfm == fr->fr_tcpfm) && + (fr1->fr_tcpf == fr->fr_tcpf))) { + m[FRC_TCP].e++; + m[FRC_TCP].n++; + } else + n &= ~0x0040; + + if ((n & 0x0080) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + ((fr1->fr_scmp == fr->fr_scmp) && + (fr1->fr_stop == fr->fr_stop) && + (fr1->fr_sport == fr->fr_sport))) { + m[FRC_SP].e++; + m[FRC_SP].n++; + } else + n &= ~0x0080; + + if ((n & 0x0100) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + ((fr1->fr_dcmp == fr->fr_dcmp) && + (fr1->fr_dtop == fr->fr_dtop) && + (fr1->fr_dport == fr->fr_dport))) { + m[FRC_DP].e++; + m[FRC_DP].n++; + } else + n &= ~0x0100; + + if ((n & 0x0200) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + ((fr1->fr_satype == FRI_LOOKUP) && + (fr->fr_satype == FRI_LOOKUP) && + (fr1->fr_srcnum == fr->fr_srcnum))) { + m[FRC_SRC].e++; + m[FRC_SRC].n++; + } else if ((n & 0x0200) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (((fr1->fr_flags & FR_NOTSRCIP) == + (fr->fr_flags & FR_NOTSRCIP)))) { + if ((fr1->fr_smask == fr->fr_smask) && + (fr1->fr_saddr == fr->fr_saddr)) + m[FRC_SRC].e++; + else + n &= ~0x0200; + if (fr1->fr_smask && + (fr1->fr_saddr & fr1->fr_smask) == + (fr->fr_saddr & fr1->fr_smask)) { + m[FRC_SRC].n++; + n |= 0x0200; + } + } else { + n &= ~0x0200; + } + + if ((n & 0x0400) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + ((fr1->fr_datype == FRI_LOOKUP) && + (fr->fr_datype == FRI_LOOKUP) && + (fr1->fr_dstnum == fr->fr_dstnum))) { + m[FRC_DST].e++; + m[FRC_DST].n++; + } else if ((n & 0x0400) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (((fr1->fr_flags & FR_NOTDSTIP) == + (fr->fr_flags & FR_NOTDSTIP)))) { + if ((fr1->fr_dmask == fr->fr_dmask) && + (fr1->fr_daddr == fr->fr_daddr)) + m[FRC_DST].e++; + else + n &= ~0x0400; + if (fr1->fr_dmask && + (fr1->fr_daddr & fr1->fr_dmask) == + (fr->fr_daddr & fr1->fr_dmask)) { + m[FRC_DST].n++; + n |= 0x0400; + } + } else { + n &= ~0x0400; + } + + if ((n & 0x0800) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (fr1->fr_optmask == fr->fr_optmask) && + (fr1->fr_optbits == fr->fr_optbits)) { + m[FRC_OPT].e++; + m[FRC_OPT].n++; + } else + n &= ~0x0800; + + if ((n & 0x1000) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (fr1->fr_secmask == fr->fr_secmask) && + (fr1->fr_secbits == fr->fr_secbits)) { + m[FRC_SEC].e++; + m[FRC_SEC].n++; + } else + n &= ~0x1000; + + if ((n & 0x10000) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + (fr1->fr_authmask == fr->fr_authmask) && + (fr1->fr_authbits == fr->fr_authbits)) { + m[FRC_ATH].e++; + m[FRC_ATH].n++; + } else + n &= ~0x10000; + + if ((n & 0x20000) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + ((fr1->fr_icmpm & 0xff00) == + (fr->fr_icmpm & 0xff00)) && + ((fr1->fr_icmp & 0xff00) == + (fr->fr_icmp & 0xff00))) { + m[FRC_ICT].e++; + m[FRC_ICT].n++; + } else + n &= ~0x20000; + + if ((n & 0x40000) && + (fr->fr_type == fr1->fr_type) && + (fr->fr_type == FR_T_IPF) && + ((fr1->fr_icmpm & 0xff) == (fr->fr_icmpm & 0xff)) && + ((fr1->fr_icmp & 0xff) == (fr->fr_icmp & 0xff))) { + m[FRC_ICC].e++; + m[FRC_ICC].n++; + } else + n &= ~0x40000; + } + /*msort(m);*/ + + if (dir == 0) + emitGroup(rn, dir, m, fr1, group, count, 0); + else if (dir == 1) + emitGroup(rn, dir, m, fr1, group, 0, count); + } +} + +static void printhooks(fp, in, out, grp) + FILE *fp; + int in; + int out; + frgroup_t *grp; +{ + frentry_t *fr; + char *group; + int dogrp, i; + char *instr; + + group = grp->fg_name; + dogrp = 0; + + if (in && out) { + fprintf(stderr, + "printhooks called with both in and out set\n"); + exit(1); + } + + if (in) { + instr = "in"; + } else if (out) { + instr = "out"; + } else { + instr = "???"; + } + fprintf(fp, "static frentry_t ipfrule_%s_%s;\n", instr, group); + + fprintf(fp, "\ +\n\ +int ipfrule_add_%s_%s()\n", instr, group); + fprintf(fp, "\ +{\n\ + int i, j, err = 0, max;\n\ + frentry_t *fp;\n"); + + if (dogrp) + fprintf(fp, "\ + frgroup_t *fg;\n"); + + fprintf(fp, "\n"); + + for (i = 0, fr = grp->fg_start; fr != NULL; i++, fr = fr->fr_next) + if (fr->fr_dsize > 0) { + fprintf(fp, "\ + ipf_rules_%s_%s[%d]->fr_data = &ipf%s_rule_data_%s_%u;\n", + instr, grp->fg_name, i, + instr, grp->fg_name, i); + } + fprintf(fp, "\ + max = sizeof(ipf_rules_%s_%s)/sizeof(frentry_t *);\n\ + for (i = 0; i < max; i++) {\n\ + fp = ipf_rules_%s_%s[i];\n\ + fp->fr_next = NULL;\n", instr, group, instr, group); + + fprintf(fp, "\ + for (j = i + 1; j < max; j++)\n\ + if (strncmp(fp->fr_names + fp->fr_group,\n\ + ipf_rules_%s_%s[j]->fr_names +\n\ + ipf_rules_%s_%s[j]->fr_group,\n\ + FR_GROUPLEN) == 0) {\n\ + if (ipf_rules_%s_%s[j] != NULL)\n\ + ipf_rules_%s_%s[j]->fr_pnext =\n\ + &fp->fr_next;\n\ + fp->fr_pnext = &ipf_rules_%s_%s[j];\n\ + fp->fr_next = ipf_rules_%s_%s[j];\n\ + break;\n\ + }\n", instr, group, instr, group, instr, group, + instr, group, instr, group, instr, group); + if (dogrp) + fprintf(fp, "\ +\n\ + if (fp->fr_grhead != -1) {\n\ + fg = fr_addgroup(fp->fr_names + fp->fr_grhead,\n\ + fp, FR_INQUE, IPL_LOGIPF, 0);\n\ + if (fg != NULL)\n\ + fp->fr_grp = &fg->fg_start;\n\ + }\n"); + fprintf(fp, "\ + }\n\ +\n\ + fp = &ipfrule_%s_%s;\n", instr, group); + fprintf(fp, "\ + bzero((char *)fp, sizeof(*fp));\n\ + fp->fr_type = FR_T_CALLFUNC_BUILTIN;\n\ + fp->fr_flags = FR_%sQUE|FR_NOMATCH;\n\ + fp->fr_data = (void *)ipf_rules_%s_%s[0];\n", + (in != 0) ? "IN" : "OUT", instr, group); + fprintf(fp, "\ + fp->fr_dsize = sizeof(ipf_rules_%s_%s[0]);\n", + instr, group); + + fprintf(fp, "\ + fp->fr_family = AF_INET;\n\ + fp->fr_func = (ipfunc_t)ipfrule_match_%s_%s;\n\ + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp,\n\ + ipfmain.ipf_active, 0);\n", + instr, group); + fprintf(fp, "\treturn err;\n}\n"); + + fprintf(fp, "\n\n\ +int ipfrule_remove_%s_%s()\n", instr, group); + fprintf(fp, "\ +{\n\ + int err = 0, i;\n\ + frentry_t *fp;\n\ +\n\ + /*\n\ + * Try to remove the %sbound rule.\n", instr); + + fprintf(fp, "\ + */\n\ + if (ipfrule_%s_%s.fr_ref > 0) {\n", instr, group); + + fprintf(fp, "\ + err = EBUSY;\n\ + } else {\n"); + + fprintf(fp, "\ + i = sizeof(ipf_rules_%s_%s)/sizeof(frentry_t *) - 1;\n\ + for (; i >= 0; i--) {\n\ + fp = ipf_rules_%s_%s[i];\n\ + if (fp->fr_ref > 1) {\n\ + err = EBUSY;\n\ + break;\n\ + }\n\ + }\n\ + }\n\ + if (err == 0)\n\ + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCDELFR,\n\ + (caddr_t)&ipfrule_%s_%s,\n\ + ipfmain.ipf_active, 0);\n", + instr, group, instr, group, instr, group); + fprintf(fp, "\ + if (err)\n\ + return err;\n\ +\n\n"); + + fprintf(fp, "\treturn err;\n}\n"); +} diff --git a/sbin/ipf/ipf/ipfilter.4 b/sbin/ipf/ipf/ipfilter.4 new file mode 100644 index 000000000000..10fd18e0606f --- /dev/null +++ b/sbin/ipf/ipf/ipfilter.4 @@ -0,0 +1,241 @@ +.\" $FreeBSD$ +.\" +.TH IP\ FILTER 4 +.SH NAME +ipfilter \- Introduction to IP packet filtering +.SH DESCRIPTION +IP Filter is a TCP/IP packet filter, suitable for use in a firewall +environment. To use, it can either be used as a loadable kernel module or +incorporated into your UNIX kernel; use as a loadable kernel module where +possible is highly recommended. Scripts are provided to install and patch +system files, as required. +.SH FEATURES +The IP packet filter can: +.IP +explicitly deny/permit any packet from passing through +.IP +distinguish between various interfaces +.IP +filter by IP networks or hosts +.IP +selectively filter any IP protocol +.IP +selectively filter fragmented IP packets +.IP +selectively filter packets with IP options +.IP +send back an ICMP error/TCP reset for blocked packets +.IP +keep packet state information for TCP, UDP and ICMP packet flows +.IP +keep fragment state information for any IP packet, applying the same rule +to all fragments. +.IP +act as a Network Address Translator (NAT) +.IP +use redirection to setup true transparent proxy connections +.IP +provide packet header details to a user program for authentication +.IP +in addition, supports temporary storage of pre-authenticated rules for passing packets through +.PP +Special provision is made for the three most common Internet protocols, TCP, +UDP and ICMP. The IP Packet filter allows filtering of: +.IP +Inverted host/net matchingTCP/UDP packets by port number or a port number +range +.IP +ICMP packets by type/code +.IP +"established" TCP packets +.IP +On any arbitrary combination of TCP flags +.IP +"short" (fragmented) IP packets with incomplete headers can be filtered +.IP +any of the 19 IP options or 8 registered IP security classes TOS (Type of +Service) field in packets +.PP +To keep track of the performance of the IP packet filter, a logging device +is used which supports logging of: +.IP +the TCP/UDP/ICMP and IP packet headers +.IP +the first 128 bytes of the packet (including headers) +.PP +A packet can be logged when: +.IP +it is successfully passed through +.IP +it is blocked from passing through +.IP +it matches a rule setup to look for suspicious packets +.PP +IP Filter keeps its own set of statistics on: +.IP +packets blocked +.IP +packets (and bytes!) used for accounting +.IP +packets passed +.IP +packets logged +.IP +attempts to log which failed (buffer full) +.IP +and much more, for packets going both in and out. + +.SH Tools +The current implementation provides a small set of tools, which can easily +be used and integrated with regular unix shells and tools. A brief description +of the tools provided: +.PP +ipf(8) +reads in a set of rules, from either stdin or a file, and adds them to +the kernels current list (appending them). It can also be used to flush the +current filter set or delete individual filter rules. The file format is +described in ipf(5). +.PP +ipfs(8) +is a utility to temporarily lock the IP Filter kernel tables (state tables +and NAT mappings) and write them to disk. After that the system can be +rebooted, and ipfs can be used to read these tables from disk and restore +them into the kernel. This way the system can be rebooted without the +connections being terminated. +.PP +ipfstat(8) +interrogates the kernel for statistics on packet filtering, so +far, and retrieves the list of filters in operation for inbound and outbound +packets. +.PP +ipftest(1) +reads in a filter rule file and then applies sample IP packets to +the rule file. This allows for testing of filter list and examination of how +a packet is passed along through it. +.PP +ipmon(8) +reads buffered data from the logging device (default is /dev/ipl) +for output to either: +.IP +screen (standard output) +.IP +file +.IP +syslog +.PP +ipsend(1) +generates arbitary IP packets for ethernet connected machines. +.PP +ipresend(1) +reads in a data file of saved IP packets (ie +snoop/tcpdump/etherfind output) and sends it back across the network. +.PP +iptest(1) +contains a set of test "programs" which send out a series of IP +packets, aimed at testing the strength of the TCP/IP stack at which it is +aimed at. WARNING: this may crash machine(s) targeted! +.PP +ipnat(8) +reads in a set of rules, from either stdin or a file and adds them +to the kernels current list of active NAT rules. NAT rules can also be +deleted using ipnat. The format of the configuration file to be used +with ipnat is described in ipnat(5). +.PP +For use in your own programs (e.g. for writing of transparent application +proxies), the programming interface and the associated ioctl's are +documented in ipf(4). + +Documentation on ioctl's and the format of data saved +to the logging character device is provided in ipl(4) +so that you may develop your own applications to work with or in place of any +of the above. + +Similar, the interface to the NAT code is documented in ipnat(4). + +.SH PACKET PROCESSING FLOW +The following diagram illustrates the flow of TCP/IP packets through the +various stages introduced by IP Filter. +.PP +.nf + IN + | + V + +-------------------------+--------------------------+ + | | | + | V | + | Network Address Translation | + | | | + | authenticated | | + | +-------<---------+ | + | | | | + | | V | + | V IP Accounting | + | | | | + | | V | + | | Fragment Cache Check--+ | + | | | | | + | V V V | + | | Packet State Check-->+ | + | | | | | + | | +->--+ | | | + | | | | V | | + | V groups IP Filtering V | + | | | | | | | + | | +--<-+ | | | + | | | | | + | +---------------->|<-----------+ | + | | | + | V | + | +---<----+ | + | | | | + | function | | + | | V | + | +--->----+ | + | | | + | V | + +--|---<--- fast-route ---<--+ | + | | | | + | | V | + | +-------------------------+--------------------------+ + | | + | pass only + | | + | V + V [KERNEL TCP/IP Processing] + | | + | +-------------------------+--------------------------+ + | | | | + | | V | + | | Fragment Cache Check--+ | + | | | | | + | | V V | + | | Packet State Check-->+ | + | | | | | + | | V | | + V | IP Filtering | | + | | | V | + | | |<-----------+ | + | | V | + | | IP Accounting | + | | | | + | | V | + | | Network Address Translation | + | | | | + | | V | + | +-------------------------+--------------------------+ + | | + | pass only + V | + +--------------------------->| + V + OUT +.fi + +.SH MORE INFORMATION +More information (including pointers to the FAQ and the mailing list) can be +obtained from the sofware's official homepage: www.ipfilter.org + +.SH SEE ALSO +ipf(4), ipf(5), ipf(8), ipfilter(5), ipfs(8), ipfstat(8), ipftest(1), +ipl(4), ipmon(8), ipnat(8), ipnat(4), + diff --git a/sbin/ipf/ipf/ipfilter.5 b/sbin/ipf/ipf/ipfilter.5 new file mode 100644 index 000000000000..97e504df15fa --- /dev/null +++ b/sbin/ipf/ipf/ipfilter.5 @@ -0,0 +1,11 @@ +.\" $FreeBSD$ +.TH IPFILTER 1 +.SH NAME +IP Filter +.SH DESCRIPTION +.PP +IP Filter is a package providing packet filtering capabilities for a variety +of operating systems. On a properly setup system, it can be used to build a +firewall. +.SH SEE ALSO +ipf(8), ipf(1), ipf(5), ipnat(8), ipnat(5), mkfilters(1) diff --git a/sbin/ipf/ipf/ipl.4 b/sbin/ipf/ipf/ipl.4 new file mode 100644 index 000000000000..da1d9e61ce0f --- /dev/null +++ b/sbin/ipf/ipf/ipl.4 @@ -0,0 +1,81 @@ +.\" $FreeBSD$ +.\" +.TH IPL 4 +.SH NAME +ipl \- IP packet log device +.SH DESCRIPTION +The \fBipl\fP pseudo device's purpose is to provide an easy way to gather +packet headers of packets you wish to log. If a packet header is to be +logged, the entire header is logged (including any IP options \- TCP/UDP +options are not included when it calculates header size) or not at all. +The packet contents are also logged after the header. If the log reader +is busy or otherwise unable to read log records, up to IPLLOGSIZE (8192 is the +default) bytes of data are stored. +.PP +Prepending every packet header logged is a structure containing information +relevant to the packet following and why it was logged. The structure's +format is as follows: +.LP +.nf +/* + * Log structure. Each packet header logged is prepended by one of these. + * Following this in the log records read from the device will be an ipflog + * structure which is then followed by any packet data. + */ +typedef struct iplog { + u_long ipl_sec; + u_long ipl_usec; + u_int ipl_len; + u_int ipl_count; + size_t ipl_dsize; + struct iplog *ipl_next; +} iplog_t; + + +typedef struct ipflog { +#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) + u_char fl_ifname[IFNAMSIZ]; +#else + u_int fl_unit; + u_char fl_ifname[4]; +#endif + u_char fl_plen; /* extra data after hlen */ + u_char fl_hlen; /* length of IP headers saved */ + u_short fl_rule; /* assume never more than 64k rules, total */ + u_32_t fl_flags; +} ipflog_t; + +.fi +.PP +When reading from the \fBipl\fP device, it is necessary to call read(2) with +a buffer big enough to hold at least 1 complete log record - reading of partial +log records is not supported. +.PP +If the packet contents are more than 128 bytes when \fBlog body\fP is used, +then only 128 bytes of the packet contents are logged. +.PP +Although it is only possible to read from the \fBipl\fP device, opening it +for writing is required when using an ioctl which changes any kernel data. +.PP +The ioctls which are loaded with this device can be found under \fBipf(4)\fP. +The ioctls which are for use with logging and don't affect the filter are: +.LP +.nf + ioctl(fd, SIOCIPFFB, int *) + ioctl(fd, FIONREAD, int *) +.fi +.PP +The SIOCIPFFB ioctl flushes the log buffer and returns the number of bytes +flushed. FIONREAD returns the number of bytes currently used for storing +log data. If IPFILTER_LOG is not defined when compiling, SIOCIPFFB is not +available and FIONREAD will return but not do anything. +.PP +There is currently no support for non-blocking IO with this device, meaning +all read operations should be considered blocking in nature (if there is no +data to read, it will sleep until some is made available). +.SH SEE ALSO +ipf(4) +.SH BUGS +Packet headers are dropped when the internal buffer (static size) fills. +.SH FILES +/dev/ipl0 diff --git a/sbin/ipf/ipfs/ipfs.8 b/sbin/ipf/ipfs/ipfs.8 new file mode 100644 index 000000000000..01d0c707d60c --- /dev/null +++ b/sbin/ipf/ipfs/ipfs.8 @@ -0,0 +1,127 @@ +.\" $FreeBSD$ +.\" +.TH IPFS 8 +.SH NAME +ipfs \- saves and restores information for NAT and state tables. +.SH SYNOPSIS +.B ipfs +[-nv] -l +.PP +.B ipfs +[-nv] -u +.PP +.B ipfs +[-nv] [ +.B \-d +<\fIdirname\fP> +] -R +.PP +.B ipfs +[-nv] [ +.B \-d +<\fIdirname\fP> +] -W +.PP +.B ipfs +[-nNSv] [ +.B \-f +<\fIfilename\fP> +] -r +.PP +.B ipfs +[-nNSv] [ +.B \-f +<\fIfilename\fP> +] -w +.PP +.B ipfs +[-nNSv] +.B \-f +<\fIfilename\fP> +.B \-i +<if1>,<if2> +.SH DESCRIPTION +.PP +\fBipfs\fP allows state information created for NAT entries and rules using +\fIkeep state\fP to be locked (modification prevented) and then saved to disk, +allowing for the system to experience a reboot, followed by the restoration +of that information, resulting in connections not being interrupted. +.SH OPTIONS +.TP +.B \-d +Change the default directory used with +.B \-R +and +.B \-W +options for saving state information. +.TP +.B \-n +Don't actually take any action that would affect information stored in +the kernel or on disk. +.TP +.B \-v +Provides a verbose description of what's being done. +.TP +.B \-i <ifname1>,<ifname2> +Change all instances of interface name ifname1 in the state save file to +ifname2. Useful if you're restoring state information after a hardware +reconfiguration or change. +.TP +.B \-N +Operate on NAT information. +.TP +.B \-S +Operate on filtering state information. +.TP +.B \-u +Unlock state tables in the kernel. +.TP +.B \-l +Lock state tables in the kernel. +.TP +.B \-r +Read information in from the specified file and load it into the +kernel. This requires the state tables to have already been locked +and does not change the lock once complete. +.TP +.B \-w +Write information out to the specified file and from the kernel. +This requires the state tables to have already been locked +and does not change the lock once complete. +.TP +.B \-R +Restores all saved state information, if any, from two files, +\fIipstate.ipf\fP and \fIipnat.ipf\fP, stored in the \fI/var/db/ipf\fP +directory unless otherwise specified by the +.B \-d +option. The state tables are locked at the beginning of this +operation and unlocked once complete. +.TP +.B \-W +Saves in-kernel state information, if any, out to two files, +\fIipstate.ipf\fP and \fIipnat.ipf\fP, stored in the \fI/var/db/ipf\fP +directory unless otherwise specified by the +.B \-d +option. The state tables are locked at the beginning of this +operation and unlocked once complete. +.DT +.SH FILES +/var/db/ipf/ipstate.ipf +.br +/var/db/ipf/ipnat.ipf +.br +/dev/ipl +.br +/dev/ipstate +.br +/dev/ipnat +.SH SEE ALSO +ipf(8), ipl(4), ipmon(8), ipnat(8) +.SH DIAGNOSTICS +.PP +Perhaps the -W and -R operations should set the locking but rather than +undo it, restore it to what it was previously. Fragment table information +is currently not saved. +.SH BUGS +.PP +If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipfs/ipfs.c b/sbin/ipf/ipfs/ipfs.c new file mode 100644 index 000000000000..e9a535977bd9 --- /dev/null +++ b/sbin/ipf/ipfs/ipfs.c @@ -0,0 +1,872 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#if !defined(__SVR4) && !defined(__GNUC__) +#include <strings.h> +#endif +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <stdlib.h> +#include <stddef.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/ip.h> +#include <netdb.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include "ipf.h" +#include "netinet/ipl.h" + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + +#ifndef IPF_SAVEDIR +# define IPF_SAVEDIR "/var/db/ipf" +#endif +#ifndef IPF_NATFILE +# define IPF_NATFILE "ipnat.ipf" +#endif +#ifndef IPF_STATEFILE +# define IPF_STATEFILE "ipstate.ipf" +#endif + +#if !defined(__SVR4) && defined(__GNUC__) +extern char *index(const char *, int); +#endif + +extern char *optarg; +extern int optind; + +int main(int, char *[]); +void usage(void); +int changestateif(char *, char *); +int changenatif(char *, char *); +int readstate(int, char *); +int readnat(int, char *); +int writestate(int, char *); +int opendevice(char *); +void closedevice(int); +int setlock(int, int); +int writeall(char *); +int readall(char *); +int writenat(int, char *); + +int opts = 0; +char *progname; + + +void usage() +{ + fprintf(stderr, "usage: %s [-nv] -l\n", progname); + fprintf(stderr, "usage: %s [-nv] -u\n", progname); + fprintf(stderr, "usage: %s [-nv] [-d <dir>] -R\n", progname); + fprintf(stderr, "usage: %s [-nv] [-d <dir>] -W\n", progname); + fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -r\n", progname); + fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -w\n", progname); + fprintf(stderr, "usage: %s [-nNSv] -f <filename> -i <if1>,<if2>\n", + progname); + exit(1); +} + + +/* + * Change interface names in state information saved out to disk. + */ +int changestateif(ifs, fname) + char *ifs, *fname; +{ + int fd, olen, nlen, rw; + ipstate_save_t ips; + off_t pos; + char *s; + + s = strchr(ifs, ','); + if (!s) + usage(); + *s++ = '\0'; + nlen = strlen(s); + olen = strlen(ifs); + if (nlen >= sizeof(ips.ips_is.is_ifname) || + olen >= sizeof(ips.ips_is.is_ifname)) + usage(); + + fd = open(fname, O_RDWR); + if (fd == -1) { + perror("open"); + exit(1); + } + + for (pos = 0; read(fd, &ips, sizeof(ips)) == sizeof(ips); ) { + rw = 0; + if (!strncmp(ips.ips_is.is_ifname[0], ifs, olen + 1)) { + strcpy(ips.ips_is.is_ifname[0], s); + rw = 1; + } + if (!strncmp(ips.ips_is.is_ifname[1], ifs, olen + 1)) { + strcpy(ips.ips_is.is_ifname[1], s); + rw = 1; + } + if (!strncmp(ips.ips_is.is_ifname[2], ifs, olen + 1)) { + strcpy(ips.ips_is.is_ifname[2], s); + rw = 1; + } + if (!strncmp(ips.ips_is.is_ifname[3], ifs, olen + 1)) { + strcpy(ips.ips_is.is_ifname[3], s); + rw = 1; + } + if (rw == 1) { + if (lseek(fd, pos, SEEK_SET) != pos) { + perror("lseek"); + exit(1); + } + if (write(fd, &ips, sizeof(ips)) != sizeof(ips)) { + perror("write"); + exit(1); + } + } + pos = lseek(fd, 0, SEEK_CUR); + } + close(fd); + + return 0; +} + + +/* + * Change interface names in NAT information saved out to disk. + */ +int changenatif(ifs, fname) + char *ifs, *fname; +{ + int fd, olen, nlen, rw; + nat_save_t ipn; + nat_t *nat; + off_t pos; + char *s; + + s = strchr(ifs, ','); + if (!s) + usage(); + *s++ = '\0'; + nlen = strlen(s); + olen = strlen(ifs); + nat = &ipn.ipn_nat; + if (nlen >= sizeof(nat->nat_ifnames[0]) || + olen >= sizeof(nat->nat_ifnames[0])) + usage(); + + fd = open(fname, O_RDWR); + if (fd == -1) { + perror("open"); + exit(1); + } + + for (pos = 0; read(fd, &ipn, sizeof(ipn)) == sizeof(ipn); ) { + rw = 0; + if (!strncmp(nat->nat_ifnames[0], ifs, olen + 1)) { + strcpy(nat->nat_ifnames[0], s); + rw = 1; + } + if (!strncmp(nat->nat_ifnames[1], ifs, olen + 1)) { + strcpy(nat->nat_ifnames[1], s); + rw = 1; + } + if (rw == 1) { + if (lseek(fd, pos, SEEK_SET) != pos) { + perror("lseek"); + exit(1); + } + if (write(fd, &ipn, sizeof(ipn)) != sizeof(ipn)) { + perror("write"); + exit(1); + } + } + pos = lseek(fd, 0, SEEK_CUR); + } + close(fd); + + return 0; +} + + +int main(argc,argv) + int argc; + char *argv[]; +{ + int c, lock = -1, devfd = -1, err = 0, rw = -1, ns = -1, set = 0; + char *dirname = NULL, *filename = NULL, *ifs = NULL; + + progname = argv[0]; + while ((c = getopt(argc, argv, "d:f:i:lNnSRruvWw")) != -1) + switch (c) + { + case 'd' : + if ((set == 0) && !dirname && !filename) + dirname = optarg; + else + usage(); + break; + case 'f' : + if ((set != 0) && !dirname && !filename) + filename = optarg; + else + usage(); + break; + case 'i' : + ifs = optarg; + set = 1; + break; + case 'l' : + if (filename || dirname || set) + usage(); + lock = 1; + set = 1; + break; + case 'n' : + opts |= OPT_DONOTHING; + break; + case 'N' : + if ((ns >= 0) || dirname || (rw != -1) || set) + usage(); + ns = 0; + set = 1; + break; + case 'r' : + if (dirname || (rw != -1) || (ns == -1)) + usage(); + rw = 0; + set = 1; + break; + case 'R' : + rw = 2; + set = 1; + break; + case 'S' : + if ((ns >= 0) || dirname || (rw != -1) || set) + usage(); + ns = 1; + set = 1; + break; + case 'u' : + if (filename || dirname || set) + usage(); + lock = 0; + set = 1; + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + case 'w' : + if (dirname || (rw != -1) || (ns == -1)) + usage(); + rw = 1; + set = 1; + break; + case 'W' : + rw = 3; + set = 1; + break; + case '?' : + default : + usage(); + } + + if (ifs) { + if (!filename || ns < 0) + usage(); + if (ns == 0) + return changenatif(ifs, filename); + else + return changestateif(ifs, filename); + } + + if ((ns >= 0) || (lock >= 0)) { + if (lock >= 0) + devfd = opendevice(NULL); + else if (ns >= 0) { + if (ns == 1) + devfd = opendevice(IPSTATE_NAME); + else if (ns == 0) + devfd = opendevice(IPNAT_NAME); + } + if (devfd == -1) + exit(1); + } + + if (lock >= 0) + err = setlock(devfd, lock); + else if (rw >= 0) { + if (rw & 1) { /* WRITE */ + if (rw & 2) + err = writeall(dirname); + else { + if (ns == 0) + err = writenat(devfd, filename); + else if (ns == 1) + err = writestate(devfd, filename); + } + } else { + if (rw & 2) + err = readall(dirname); + else { + if (ns == 0) + err = readnat(devfd, filename); + else if (ns == 1) + err = readstate(devfd, filename); + } + } + } + return err; +} + + +int opendevice(ipfdev) + char *ipfdev; +{ + int fd = -1; + + if (opts & OPT_DONOTHING) + return -2; + + if (!ipfdev) + ipfdev = IPL_NAME; + + if ((fd = open(ipfdev, O_RDWR)) == -1) + if ((fd = open(ipfdev, O_RDONLY)) == -1) + perror("open device"); + return fd; +} + + +void closedevice(fd) + int fd; +{ + close(fd); +} + + +int setlock(fd, lock) + int fd, lock; +{ + if (opts & OPT_VERBOSE) + printf("Turn lock %s\n", lock ? "on" : "off"); + if (!(opts & OPT_DONOTHING)) { + if (ioctl(fd, SIOCSTLCK, &lock) == -1) { + perror("SIOCSTLCK"); + return 1; + } + if (opts & OPT_VERBOSE) + printf("Lock now %s\n", lock ? "on" : "off"); + } + return 0; +} + + +int writestate(fd, file) + int fd; + char *file; +{ + ipstate_save_t ips, *ipsp; + ipfobj_t obj; + int wfd = -1; + + if (!file) + file = IPF_STATEFILE; + + wfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600); + if (wfd == -1) { + fprintf(stderr, "%s ", file); + perror("state:open"); + return 1; + } + + ipsp = &ips; + bzero((char *)&obj, sizeof(obj)); + bzero((char *)ipsp, sizeof(ips)); + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = sizeof(*ipsp); + obj.ipfo_type = IPFOBJ_STATESAVE; + obj.ipfo_ptr = ipsp; + + do { + + if (opts & OPT_VERBOSE) + printf("Getting state from addr %p\n", ips.ips_next); + if (ioctl(fd, SIOCSTGET, &obj)) { + if (errno == ENOENT) + break; + perror("state:SIOCSTGET"); + close(wfd); + return 1; + } + if (opts & OPT_VERBOSE) + printf("Got state next %p\n", ips.ips_next); + if (write(wfd, ipsp, sizeof(ips)) != sizeof(ips)) { + perror("state:write"); + close(wfd); + return 1; + } + } while (ips.ips_next != NULL); + close(wfd); + + return 0; +} + + +int readstate(fd, file) + int fd; + char *file; +{ + ipstate_save_t ips, *is, *ipshead = NULL, *is1, *ipstail = NULL; + int sfd = -1, i; + ipfobj_t obj; + + if (!file) + file = IPF_STATEFILE; + + sfd = open(file, O_RDONLY, 0600); + if (sfd == -1) { + fprintf(stderr, "%s ", file); + perror("open"); + return 1; + } + + bzero((char *)&ips, sizeof(ips)); + + /* + * 1. Read all state information in. + */ + do { + i = read(sfd, &ips, sizeof(ips)); + if (i == -1) { + perror("read"); + goto freeipshead; + } + if (i == 0) + break; + if (i != sizeof(ips)) { + fprintf(stderr, "state:incomplete read: %d != %d\n", + i, (int)sizeof(ips)); + goto freeipshead; + } + is = (ipstate_save_t *)malloc(sizeof(*is)); + if (is == NULL) { + fprintf(stderr, "malloc failed\n"); + goto freeipshead; + } + + bcopy((char *)&ips, (char *)is, sizeof(ips)); + + /* + * Check to see if this is the first state entry that will + * reference a particular rule and if so, flag it as such + * else just adjust the rule pointer to become a pointer to + * the other. We do this so we have a means later for tracking + * who is referencing us when we get back the real pointer + * in is_rule after doing the ioctl. + */ + for (is1 = ipshead; is1 != NULL; is1 = is1->ips_next) + if (is1->ips_rule == is->ips_rule) + break; + if (is1 == NULL) + is->ips_is.is_flags |= SI_NEWFR; + else + is->ips_rule = (void *)&is1->ips_rule; + + /* + * Use a tail-queue type list (add things to the end).. + */ + is->ips_next = NULL; + if (!ipshead) + ipshead = is; + if (ipstail) + ipstail->ips_next = is; + ipstail = is; + } while (1); + + close(sfd); + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = sizeof(*is); + obj.ipfo_type = IPFOBJ_STATESAVE; + + while ((is = ipshead) != NULL) { + if (opts & OPT_VERBOSE) + printf("Loading new state table entry\n"); + if (is->ips_is.is_flags & SI_NEWFR) { + if (opts & OPT_VERBOSE) + printf("Loading new filter rule\n"); + } + + obj.ipfo_ptr = is; + if (!(opts & OPT_DONOTHING)) + if (ioctl(fd, SIOCSTPUT, &obj)) { + perror("SIOCSTPUT"); + goto freeipshead; + } + + if (is->ips_is.is_flags & SI_NEWFR) { + if (opts & OPT_VERBOSE) + printf("Real rule addr %p\n", is->ips_rule); + for (is1 = is->ips_next; is1; is1 = is1->ips_next) + if (is1->ips_rule == (frentry_t *)&is->ips_rule) + is1->ips_rule = is->ips_rule; + } + + ipshead = is->ips_next; + free(is); + } + + return 0; + +freeipshead: + while ((is = ipshead) != NULL) { + ipshead = is->ips_next; + free(is); + } + if (sfd != -1) + close(sfd); + return 1; +} + + +int readnat(fd, file) + int fd; + char *file; +{ + nat_save_t ipn, *in, *ipnhead = NULL, *in1, *ipntail = NULL; + ipfobj_t obj; + int nfd, i; + nat_t *nat; + char *s; + int n; + + nfd = -1; + in = NULL; + ipnhead = NULL; + ipntail = NULL; + + if (!file) + file = IPF_NATFILE; + + nfd = open(file, O_RDONLY); + if (nfd == -1) { + fprintf(stderr, "%s ", file); + perror("nat:open"); + return 1; + } + + bzero((char *)&ipn, sizeof(ipn)); + + /* + * 1. Read all state information in. + */ + do { + i = read(nfd, &ipn, sizeof(ipn)); + if (i == -1) { + perror("read"); + goto freenathead; + } + if (i == 0) + break; + if (i != sizeof(ipn)) { + fprintf(stderr, "nat:incomplete read: %d != %d\n", + i, (int)sizeof(ipn)); + goto freenathead; + } + + in = (nat_save_t *)malloc(ipn.ipn_dsize); + if (in == NULL) { + fprintf(stderr, "nat:cannot malloc nat save atruct\n"); + goto freenathead; + } + + if (ipn.ipn_dsize > sizeof(ipn)) { + n = ipn.ipn_dsize - sizeof(ipn); + if (n > 0) { + s = in->ipn_data + sizeof(in->ipn_data); + i = read(nfd, s, n); + if (i == 0) + break; + if (i != n) { + fprintf(stderr, + "nat:incomplete read: %d != %d\n", + i, n); + goto freenathead; + } + } + } + bcopy((char *)&ipn, (char *)in, sizeof(ipn)); + + /* + * Check to see if this is the first NAT entry that will + * reference a particular rule and if so, flag it as such + * else just adjust the rule pointer to become a pointer to + * the other. We do this so we have a means later for tracking + * who is referencing us when we get back the real pointer + * in is_rule after doing the ioctl. + */ + nat = &in->ipn_nat; + if (nat->nat_fr != NULL) { + for (in1 = ipnhead; in1 != NULL; in1 = in1->ipn_next) + if (in1->ipn_rule == nat->nat_fr) + break; + if (in1 == NULL) + nat->nat_flags |= SI_NEWFR; + else + nat->nat_fr = &in1->ipn_fr; + } + + /* + * Use a tail-queue type list (add things to the end).. + */ + in->ipn_next = NULL; + if (!ipnhead) + ipnhead = in; + if (ipntail) + ipntail->ipn_next = in; + ipntail = in; + } while (1); + + close(nfd); + nfd = -1; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_NATSAVE; + + while ((in = ipnhead) != NULL) { + if (opts & OPT_VERBOSE) + printf("Loading new NAT table entry\n"); + nat = &in->ipn_nat; + if (nat->nat_flags & SI_NEWFR) { + if (opts & OPT_VERBOSE) + printf("Loading new filter rule\n"); + } + + obj.ipfo_ptr = in; + obj.ipfo_size = in->ipn_dsize; + if (!(opts & OPT_DONOTHING)) + if (ioctl(fd, SIOCSTPUT, &obj)) { + fprintf(stderr, "in=%p:", in); + perror("SIOCSTPUT"); + return 1; + } + + if (nat->nat_flags & SI_NEWFR) { + if (opts & OPT_VERBOSE) + printf("Real rule addr %p\n", nat->nat_fr); + for (in1 = in->ipn_next; in1; in1 = in1->ipn_next) + if (in1->ipn_rule == &in->ipn_fr) + in1->ipn_rule = nat->nat_fr; + } + + ipnhead = in->ipn_next; + free(in); + } + + return 0; + +freenathead: + while ((in = ipnhead) != NULL) { + ipnhead = in->ipn_next; + free(in); + } + if (nfd != -1) + close(nfd); + return 1; +} + + +int writenat(fd, file) + int fd; + char *file; +{ + nat_save_t *ipnp = NULL, *next = NULL; + ipfobj_t obj; + int nfd = -1; + natget_t ng; + + if (!file) + file = IPF_NATFILE; + + nfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600); + if (nfd == -1) { + fprintf(stderr, "%s ", file); + perror("nat:open"); + return 1; + } + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_NATSAVE; + + do { + if (opts & OPT_VERBOSE) + printf("Getting nat from addr %p\n", ipnp); + ng.ng_ptr = next; + ng.ng_sz = 0; + if (ioctl(fd, SIOCSTGSZ, &ng)) { + perror("nat:SIOCSTGSZ"); + close(nfd); + if (ipnp != NULL) + free(ipnp); + return 1; + } + + if (opts & OPT_VERBOSE) + printf("NAT size %d from %p\n", ng.ng_sz, ng.ng_ptr); + + if (ng.ng_sz == 0) + break; + + if (!ipnp) + ipnp = malloc(ng.ng_sz); + else + ipnp = realloc((char *)ipnp, ng.ng_sz); + if (!ipnp) { + fprintf(stderr, + "malloc for %d bytes failed\n", ng.ng_sz); + break; + } + + bzero((char *)ipnp, ng.ng_sz); + obj.ipfo_size = ng.ng_sz; + obj.ipfo_ptr = ipnp; + ipnp->ipn_dsize = ng.ng_sz; + ipnp->ipn_next = next; + if (ioctl(fd, SIOCSTGET, &obj)) { + if (errno == ENOENT) + break; + perror("nat:SIOCSTGET"); + close(nfd); + free(ipnp); + return 1; + } + + if (opts & OPT_VERBOSE) + printf("Got nat next %p ipn_dsize %d ng_sz %d\n", + ipnp->ipn_next, ipnp->ipn_dsize, ng.ng_sz); + if (write(nfd, ipnp, ipnp->ipn_dsize) != ipnp->ipn_dsize) { + perror("nat:write"); + close(nfd); + free(ipnp); + return 1; + } + next = ipnp->ipn_next; + } while (ipnp && next); + if (ipnp != NULL) + free(ipnp); + close(nfd); + + return 0; +} + + +int writeall(dirname) + char *dirname; +{ + int fd, devfd; + + if (!dirname) + dirname = IPF_SAVEDIR; + + if (chdir(dirname)) { + fprintf(stderr, "IPF_SAVEDIR=%s: ", dirname); + perror("chdir(IPF_SAVEDIR)"); + return 1; + } + + fd = opendevice(NULL); + if (fd == -1) + return 1; + if (setlock(fd, 1)) { + close(fd); + return 1; + } + + devfd = opendevice(IPSTATE_NAME); + if (devfd == -1) + goto bad; + if (writestate(devfd, NULL)) + goto bad; + close(devfd); + + devfd = opendevice(IPNAT_NAME); + if (devfd == -1) + goto bad; + if (writenat(devfd, NULL)) + goto bad; + close(devfd); + + if (setlock(fd, 0)) { + close(fd); + return 1; + } + + close(fd); + return 0; + +bad: + setlock(fd, 0); + close(fd); + return 1; +} + + +int readall(dirname) + char *dirname; +{ + int fd, devfd; + + if (!dirname) + dirname = IPF_SAVEDIR; + + if (chdir(dirname)) { + perror("chdir(IPF_SAVEDIR)"); + return 1; + } + + fd = opendevice(NULL); + if (fd == -1) + return 1; + if (setlock(fd, 1)) { + close(fd); + return 1; + } + + devfd = opendevice(IPSTATE_NAME); + if (devfd == -1) + return 1; + if (readstate(devfd, NULL)) + return 1; + close(devfd); + + devfd = opendevice(IPNAT_NAME); + if (devfd == -1) + return 1; + if (readnat(devfd, NULL)) + return 1; + close(devfd); + + if (setlock(fd, 0)) { + close(fd); + return 1; + } + + return 0; +} diff --git a/sbin/ipf/ipfstat/ipfstat.8 b/sbin/ipf/ipfstat/ipfstat.8 new file mode 100644 index 000000000000..3762bccbdccf --- /dev/null +++ b/sbin/ipf/ipfstat/ipfstat.8 @@ -0,0 +1,199 @@ +.\" $FreeBSD$ +.TH ipfstat 8 +.SH NAME +ipfstat \- reports on packet filter statistics and filter list +.SH SYNOPSIS +.B ipfstat +[ +.B \-46aAdfghIilnoRsv +] +.br +.B ipfstat -t +[ +.B \-6C +] [ +.B \-D +<addrport> +] [ +.B \-P +<protocol> +] [ +.B \-S +<addrport> +] [ +.B \-T +<refresh time> +] +.SH DESCRIPTION +\fBipfstat\fP 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/boot/kernel/kernel\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 +.TP +.B \-4 +Display filter lists and states for IPv4, if available. This is the default +when displaying states. \fB-4\fP and \fB-6\fP is the default when +displaying lists. +.TP +.B \-6 +Display filter lists and states for IPv6, if available. +.TP +.B \-a +Display the accounting filter list and show bytes counted against each rule. +.TP +.B \-A +Display packet authentication statistics. +.TP +.B \-C +This option is only valid in combination with \fB\-t\fP. +Display "closed" states as well in the top. Normally, a TCP connection is +not displayed when it reaches the CLOSE_WAIT protocol state. With this +option enabled, all state entries are displayed. +.TP +.BR \-d +Produce debugging output when displaying data. +.TP +.BR \-D \0<addrport> +This option is only valid in combination with \fB\-t\fP. Limit the state top +display to show only state entries whose destination IP address and port +match the addrport argument. The addrport specification is of the form +ipaddress[,port]. The ipaddress and port should be either numerical or the +string "any" (specifying any IP address resp. any port). If the \fB\-D\fP +option is not specified, it defaults to "\fB\-D\fP any,any". +.TP +.B \-f +Show fragment state information (statistics) and held state information (in +the kernel) if any is present. +.TP +.B \-g +Show groups currently configured (both active and inactive). +.TP +.B \-h +Show per-rule the number of times each one scores a "hit". +.TP +.B \-i +Display the filter list used for the input side of the kernel IP processing. +.TP +.B \-I +Swap between retrieving "inactive"/"active" filter list details. For use +in combination with \fB\-i\fP. +.TP +.B \-n +Show the "rule number" for each rule as it is printed. +.TP +.B \-o +Display the filter list used for the output side of the kernel IP processing. +.TP +.BR \-P \0<protocol> +This option is only valid in combination with \fB\-t\fP. Limit the state top +display to show only state entries that match a specific protocol. The +argument can be a protocol name (as defined in \fB/etc/protocols\fP) or a +protocol number. If this option is not specified, state entries for any +protocol are specified. +.TP +.BR \-R +Don't try to resolve addresses to hostnames and ports to services while +printing statistics. +.TP +.B \-s +Show packet/flow state information (statistics only). +.TP +.B \-sl +Show held state information (in the kernel) if any is present (no statistics). +.TP +.BR \-S \0<addrport> +This option is only valid in combination with \fB\-t\fP. Limit the state top +display to show only state entries whose source IP address and port match +the addrport argument. The addrport specification is of the form +ipaddress[,port]. The ipaddress and port should be either numerical or the +string "any" (specifying any IP address resp. any port). If the \fB\-S\fP +option is not specified, it defaults to "\fB\-S\fP any,any". +.TP +.B \-t +Show the state table in a way similar to the way \fBtop(1)\fP shows the process +table. States can be sorted using a number of different ways. This option +requires \fBcurses(3)\fP and needs to be compiled in. It may not be available on +all operating systems. See below, for more information on the keys that can +be used while ipfstat is in top mode. +.TP +.BR \-T \0<refreshtime> +This option is only valid in combination with \fB\-t\fP. Specifies how often +the state top display should be updated. The refresh time is the number of +seconds between an update. Any positive integer can be used. The default (and +minimal update time) is 1. +.TP +.B \-v +Turn verbose mode on. Displays more debugging information. When used with +either \fB-i\fP or \fB-o\fP, counters associated with the rule, such as the +number of times it has been matched and the number of bytes from such packets +is displayed. For "keep state" rules, a count of the number of state sessions +active against the rule is also displayed. +.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. +.PP +One of the statistics that \fBipfstat\fP shows is \fBticks\fP. +This number indicates how long the filter has been enabled. +The number is incremented every half\-second. +.SH STATE TOP +Using the \fB\-t\fP option \fBipfstat\fP will enter the state top mode. In +this mode the state table is displayed similar to the way \fBtop\fP displays +the process table. The \fB\-C\fP, \fB\-D\fP, \fB\-P\fP, \fB\-S\fP and \fB\-T\fP +command line options can be used to restrict the state entries that will be +shown and to specify the frequency of display updates. +.PP +In state top mode, the following keys can be used to influence the displayed +information: +.TP +\fBb\fP show packets/bytes from backward direction. +.TP +\fBf\fP show packets/bytes from forward direction. (default) +.TP +\fBl\fP redraw the screen. +.TP +\fBq\fP quit the program. +.TP +\fBs\fP switch between different sorting criterion. +.TP +\fBr\fP reverse the sorting criterion. +.PP +States can be sorted by protocol number, by number of IP packets, by number +of bytes and by time-to-live of the state entry. The default is to sort by +the number of bytes. States are sorted in descending order, but you can use +the \fBr\fP key to sort them in ascending order. +.SH STATE TOP LIMITATIONS +It is currently not possible to interactively change the source, destination +and protocol filters or the refresh frequency. This must be done from the +command line. +.PP +The screen must have at least 80 columns. This is however not checked. +When running state top in IPv6 mode, the screen must be much wider to display +the very long IPv6 addresses. +.PP +Only the first X-5 entries that match the sort and filter criteria are +displayed (where X is the number of rows on the display. The only way to see +more entries is to resize the screen. +.SH FILES +/dev/kmem +.br +/dev/ipl +.br +/dev/ipstate +.br +/kernel +.SH SEE ALSO +ipf(8) +.SH BUGS +\fB-4\fP and \fB-6\fP are only valid with \fB-i\fP, \fB-o\fP, and \fB-t\fP. +An error should result when used with other arguments. diff --git a/sbin/ipf/ipfstat/ipfstat.c b/sbin/ipf/ipfstat/ipfstat.c new file mode 100644 index 000000000000..4517d3e857b4 --- /dev/null +++ b/sbin/ipf/ipfstat/ipfstat.c @@ -0,0 +1,2383 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include <sys/ioctl.h> +#include <ctype.h> +#include <fcntl.h> +# include <nlist.h> +#include <ctype.h> +#if defined(sun) && defined(__SVR4) +# include <stddef.h> +#endif +#include "ipf.h" +#include "netinet/ipl.h" +#if defined(STATETOP) +# if defined(sun) && defined(__SVR4) +# include <sys/select.h> +# endif +# include <netinet/ip_var.h> +# include <netinet/tcp_fsm.h> +# include <ctype.h> +# include <signal.h> +# include <time.h> +# if SOLARIS || defined(__NetBSD__) +# ifdef ERR +# undef ERR +# endif +# include <curses.h> +# else /* SOLARIS */ +# include <ncurses.h> +# endif /* SOLARIS */ +#endif /* STATETOP */ +#include "kmem.h" +#if defined(__NetBSD__) +# include <paths.h> +#endif + +#if !defined(lint) +static const char sccsid[] = "@(#)fils.c 1.21 4/20/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + + +extern char *optarg; +extern int optind; +extern int opterr; + +#define PRINTF (void)printf +#define FPRINTF (void)fprintf +static char *filters[4] = { "ipfilter(in)", "ipfilter(out)", + "ipacct(in)", "ipacct(out)" }; +static int state_logging = -1; +static wordtab_t *state_fields = NULL; + +int nohdrfields = 0; +int opts = 0; +#ifdef USE_INET6 +int use_inet4 = 0; +int use_inet6 = 0; +#endif +int live_kernel = 1; +int state_fd = -1; +int ipf_fd = -1; +int auth_fd = -1; +int nat_fd = -1; +frgroup_t *grtop = NULL; +frgroup_t *grtail = NULL; + +char *blockreasons[FRB_MAX_VALUE + 1] = { + "packet blocked", + "log rule failure", + "pps rate exceeded", + "jumbogram", + "makefrip failed", + "cannot add state", + "IP ID update failed", + "log-or-block failed", + "decapsulate failure", + "cannot create new auth entry", + "packet queued for auth", + "buffer coalesce failure", + "buffer pullup failure", + "auth feedback", + "bad fragment", + "IPv4 NAT failure", + "IPv6 NAT failure" +}; + +#ifdef STATETOP +#define STSTRSIZE 80 +#define STGROWSIZE 16 +#define HOSTNMLEN 40 + +#define STSORT_PR 0 +#define STSORT_PKTS 1 +#define STSORT_BYTES 2 +#define STSORT_TTL 3 +#define STSORT_SRCIP 4 +#define STSORT_SRCPT 5 +#define STSORT_DSTIP 6 +#define STSORT_DSTPT 7 +#define STSORT_MAX STSORT_DSTPT +#define STSORT_DEFAULT STSORT_BYTES + + +typedef struct statetop { + i6addr_t st_src; + i6addr_t st_dst; + u_short st_sport; + u_short st_dport; + u_char st_p; + u_char st_v; + u_char st_state[2]; + U_QUAD_T st_pkts; + U_QUAD_T st_bytes; + u_long st_age; +} statetop_t; +#endif + +int main(int, char *[]); + +static int fetchfrag(int, int, ipfr_t *); +static void showstats(friostat_t *, u_32_t); +static void showfrstates(ipfrstat_t *, u_long); +static void showlist(friostat_t *); +static void showstatestats(ips_stat_t *); +static void showipstates(ips_stat_t *, int *); +static void showauthstates(ipf_authstat_t *); +static void showtqtable_live(int); +static void showgroups(friostat_t *); +static void usage(char *); +static int state_matcharray(ipstate_t *, int *); +static int printlivelist(friostat_t *, int, int, frentry_t *, + char *, char *); +static void printdeadlist(friostat_t *, int, int, frentry_t *, + char *, char *); +static void printside(char *, ipf_statistics_t *); +static void parse_ipportstr(const char *, i6addr_t *, int *); +static void ipfstate_live(char *, friostat_t **, ips_stat_t **, + ipfrstat_t **, ipf_authstat_t **, u_32_t *); +static void ipfstate_dead(char *, friostat_t **, ips_stat_t **, + ipfrstat_t **, ipf_authstat_t **, u_32_t *); +static ipstate_t *fetchstate(ipstate_t *, ipstate_t *); +#ifdef STATETOP +static void topipstates(i6addr_t, i6addr_t, int, int, int, + int, int, int, int *); +static void sig_break(int); +static void sig_resize(int); +static char *getip(int, i6addr_t *); +static char *ttl_to_string(long); +static int sort_p(const void *, const void *); +static int sort_pkts(const void *, const void *); +static int sort_bytes(const void *, const void *); +static int sort_ttl(const void *, const void *); +static int sort_srcip(const void *, const void *); +static int sort_srcpt(const void *, const void *); +static int sort_dstip(const void *, const void *); +static int sort_dstpt(const void *, const void *); +#endif + + +static void usage(name) + char *name; +{ +#ifdef USE_INET6 + fprintf(stderr, "Usage: %s [-46aAdfghIilnoRsv]\n", name); +#else + fprintf(stderr, "Usage: %s [-4aAdfghIilnoRsv]\n", name); +#endif + fprintf(stderr, " %s [-M corefile] [-N symbol-list]\n", name); +#ifdef STATETOP +#ifdef USE_INET6 + fprintf(stderr, " %s -t [-46C] ", name); +#else + fprintf(stderr, " %s -t [-4C] ", name); +#endif +#endif + fprintf(stderr, "[-D destination address] [-P protocol] [-S source address] [-T refresh time]\n"); + exit(1); +} + + +int main(argc,argv) + int argc; + char *argv[]; +{ + ipf_authstat_t frauthst; + ipf_authstat_t *frauthstp = &frauthst; + friostat_t fio; + friostat_t *fiop = &fio; + ips_stat_t ipsst; + ips_stat_t *ipsstp = &ipsst; + ipfrstat_t ifrst; + ipfrstat_t *ifrstp = &ifrst; + char *options; + char *kern = NULL; + char *memf = NULL; + int c; + int myoptind; + int *filter = NULL; + + int protocol = -1; /* -1 = wild card for any protocol */ + int refreshtime = 1; /* default update time */ + int sport = -1; /* -1 = wild card for any source port */ + int dport = -1; /* -1 = wild card for any dest port */ + int topclosed = 0; /* do not show closed tcp sessions */ + i6addr_t saddr, daddr; + u_32_t frf; + +#ifdef USE_INET6 + options = "46aACdfghIilnostvD:m:M:N:O:P:RS:T:"; +#else + options = "4aACdfghIilnostvD:m:M:N:O:P:RS:T:"; +#endif + + saddr.in4.s_addr = INADDR_ANY; /* default any v4 source addr */ + daddr.in4.s_addr = INADDR_ANY; /* default any v4 dest addr */ +#ifdef USE_INET6 + saddr.in6 = in6addr_any; /* default any v6 source addr */ + daddr.in6 = in6addr_any; /* default any v6 dest addr */ +#endif + + /* Don't warn about invalid flags when we run getopt for the 1st time */ + opterr = 0; + + /* + * Parse these two arguments now lest there be any buffer overflows + * in the parsing of the rest. + */ + myoptind = optind; + while ((c = getopt(argc, argv, options)) != -1) { + switch (c) + { + case 'M' : + memf = optarg; + live_kernel = 0; + break; + case 'N' : + kern = optarg; + live_kernel = 0; + break; + } + } + optind = myoptind; + + if (live_kernel == 1) { + if ((state_fd = open(IPSTATE_NAME, O_RDONLY)) == -1) { + perror("open(IPSTATE_NAME)"); + exit(-1); + } + if ((auth_fd = open(IPAUTH_NAME, O_RDONLY)) == -1) { + perror("open(IPAUTH_NAME)"); + exit(-1); + } + if ((nat_fd = open(IPNAT_NAME, O_RDONLY)) == -1) { + perror("open(IPAUTH_NAME)"); + exit(-1); + } + if ((ipf_fd = open(IPL_NAME, O_RDONLY)) == -1) { + fprintf(stderr, "open(%s)", IPL_NAME); + perror(""); + exit(-1); + } + } + + if (kern != NULL || memf != NULL) { + (void)setgid(getgid()); + (void)setuid(getuid()); + } + + if (live_kernel == 1) { + (void) checkrev(IPL_NAME); + } else { + if (openkmem(kern, memf) == -1) + exit(-1); + } + + (void)setgid(getgid()); + (void)setuid(getuid()); + + opterr = 1; + + while ((c = getopt(argc, argv, options)) != -1) + { + switch (c) + { +#ifdef USE_INET6 + case '4' : + use_inet4 = 1; + break; + case '6' : + use_inet6 = 1; + break; +#endif + case 'a' : + opts |= OPT_ACCNT|OPT_SHOWLIST; + break; + case 'A' : + opts |= OPT_AUTHSTATS; + break; + case 'C' : + topclosed = 1; + break; + case 'd' : + opts |= OPT_DEBUG; + break; + case 'D' : + parse_ipportstr(optarg, &daddr, &dport); + break; + case 'f' : + opts |= OPT_FRSTATES; + break; + case 'g' : + opts |= OPT_GROUPS; + break; + case 'h' : + opts |= OPT_HITS; + break; + case 'i' : + opts |= OPT_INQUE|OPT_SHOWLIST; + break; + case 'I' : + opts |= OPT_INACTIVE; + break; + case 'l' : + opts |= OPT_SHOWLIST; + break; + case 'm' : + filter = parseipfexpr(optarg, NULL); + if (filter == NULL) { + fprintf(stderr, "Error parseing '%s'\n", + optarg); + exit(1); + } + break; + case 'M' : + break; + case 'N' : + break; + case 'n' : + opts |= OPT_SHOWLINENO; + break; + case 'o' : + opts |= OPT_OUTQUE|OPT_SHOWLIST; + break; + case 'O' : + state_fields = parsefields(statefields, optarg); + break; + case 'P' : + protocol = getproto(optarg); + if (protocol == -1) { + fprintf(stderr, "%s: Invalid protocol: %s\n", + argv[0], optarg); + exit(-2); + } + break; + case 'R' : + opts |= OPT_NORESOLVE; + break; + case 's' : + opts |= OPT_IPSTATES; + break; + case 'S' : + parse_ipportstr(optarg, &saddr, &sport); + break; + case 't' : +#ifdef STATETOP + opts |= OPT_STATETOP; + break; +#else + fprintf(stderr, + "%s: state top facility not compiled in\n", + argv[0]); + exit(-2); +#endif + case 'T' : + if (!sscanf(optarg, "%d", &refreshtime) || + (refreshtime <= 0)) { + fprintf(stderr, + "%s: Invalid refreshtime < 1 : %s\n", + argv[0], optarg); + exit(-2); + } + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + default : + usage(argv[0]); + break; + } + } +#ifdef USE_INET6 + if ((use_inet4 || use_inet6) && + !(opts & (OPT_INQUE | OPT_OUTQUE | OPT_STATETOP))) { +#ifdef STATETOP + FPRINTF(stderr, "No -i, -o, or -t given with -4 or -6\n"); +#else + FPRINTF(stderr, "No -i or -o given with -4 or -6\n"); +#endif + exit(-2); + } + if (use_inet4 == 0 && use_inet6 == 0) + use_inet4 = use_inet6 = 1; +#endif + + if (live_kernel == 1) { + bzero((char *)&fio, sizeof(fio)); + bzero((char *)&ipsst, sizeof(ipsst)); + bzero((char *)&ifrst, sizeof(ifrst)); + + ipfstate_live(IPL_NAME, &fiop, &ipsstp, &ifrstp, + &frauthstp, &frf); + } else { + ipfstate_dead(kern, &fiop, &ipsstp, &ifrstp, &frauthstp, &frf); + } + + if (opts & OPT_IPSTATES) { + showipstates(ipsstp, filter); + } else if (opts & OPT_SHOWLIST) { + showlist(fiop); + if ((opts & OPT_OUTQUE) && (opts & OPT_INQUE)){ + opts &= ~OPT_OUTQUE; + showlist(fiop); + } + } else if (opts & OPT_FRSTATES) + showfrstates(ifrstp, fiop->f_ticks); +#ifdef STATETOP + else if (opts & OPT_STATETOP) + topipstates(saddr, daddr, sport, dport, protocol, +#ifdef USE_INET6 + use_inet6 && use_inet4 ? 0 : use_inet6 && !use_inet4 ? 6 : 4, +#else + 4, +#endif +#endif + refreshtime, topclosed, filter); + else if (opts & OPT_AUTHSTATS) + showauthstates(frauthstp); + else if (opts & OPT_GROUPS) + showgroups(fiop); + else + showstats(fiop, frf); + + return 0; +} + + +/* + * Fill in the stats structures from the live kernel, using a combination + * of ioctl's and copying directly from kernel memory. + */ +static void ipfstate_live(device, fiopp, ipsstpp, ifrstpp, frauthstpp, frfp) + char *device; + friostat_t **fiopp; + ips_stat_t **ipsstpp; + ipfrstat_t **ifrstpp; + ipf_authstat_t **frauthstpp; + u_32_t *frfp; +{ + ipfobj_t ipfo; + + if (checkrev(device) == -1) { + fprintf(stderr, "User/kernel version check failed\n"); + exit(1); + } + + if ((opts & OPT_AUTHSTATS) == 0) { + bzero((caddr_t)&ipfo, sizeof(ipfo)); + ipfo.ipfo_rev = IPFILTER_VERSION; + ipfo.ipfo_type = IPFOBJ_IPFSTAT; + ipfo.ipfo_size = sizeof(friostat_t); + ipfo.ipfo_ptr = (void *)*fiopp; + + if (ioctl(ipf_fd, SIOCGETFS, &ipfo) == -1) { + ipferror(ipf_fd, "ioctl(ipf:SIOCGETFS)"); + exit(-1); + } + + if (ioctl(ipf_fd, SIOCGETFF, frfp) == -1) + ipferror(ipf_fd, "ioctl(SIOCGETFF)"); + } + + if ((opts & OPT_IPSTATES) != 0) { + + bzero((caddr_t)&ipfo, sizeof(ipfo)); + ipfo.ipfo_rev = IPFILTER_VERSION; + ipfo.ipfo_type = IPFOBJ_STATESTAT; + ipfo.ipfo_size = sizeof(ips_stat_t); + ipfo.ipfo_ptr = (void *)*ipsstpp; + + if ((ioctl(state_fd, SIOCGETFS, &ipfo) == -1)) { + ipferror(state_fd, "ioctl(state:SIOCGETFS)"); + exit(-1); + } + if (ioctl(state_fd, SIOCGETLG, &state_logging) == -1) { + ipferror(state_fd, "ioctl(state:SIOCGETLG)"); + exit(-1); + } + } + + if ((opts & OPT_FRSTATES) != 0) { + bzero((caddr_t)&ipfo, sizeof(ipfo)); + ipfo.ipfo_rev = IPFILTER_VERSION; + ipfo.ipfo_type = IPFOBJ_FRAGSTAT; + ipfo.ipfo_size = sizeof(ipfrstat_t); + ipfo.ipfo_ptr = (void *)*ifrstpp; + + if (ioctl(ipf_fd, SIOCGFRST, &ipfo) == -1) { + ipferror(ipf_fd, "ioctl(SIOCGFRST)"); + exit(-1); + } + } + + if (opts & OPT_DEBUG) + PRINTF("opts %#x name %s\n", opts, device); + + if ((opts & OPT_AUTHSTATS) != 0) { + bzero((caddr_t)&ipfo, sizeof(ipfo)); + ipfo.ipfo_rev = IPFILTER_VERSION; + ipfo.ipfo_type = IPFOBJ_AUTHSTAT; + ipfo.ipfo_size = sizeof(ipf_authstat_t); + ipfo.ipfo_ptr = (void *)*frauthstpp; + + if (ioctl(auth_fd, SIOCATHST, &ipfo) == -1) { + ipferror(auth_fd, "ioctl(SIOCATHST)"); + exit(-1); + } + } +} + + +/* + * Build up the stats structures from data held in the "core" memory. + * This is mainly useful when looking at data in crash dumps and ioctl's + * just won't work any more. + */ +static void ipfstate_dead(kernel, fiopp, ipsstpp, ifrstpp, frauthstpp, frfp) + char *kernel; + friostat_t **fiopp; + ips_stat_t **ipsstpp; + ipfrstat_t **ifrstpp; + ipf_authstat_t **frauthstpp; + u_32_t *frfp; +{ + static ipf_authstat_t frauthst, *frauthstp; + static ipftq_t ipstcptab[IPF_TCP_NSTATES]; + static ips_stat_t ipsst, *ipsstp; + static ipfrstat_t ifrst, *ifrstp; + static friostat_t fio, *fiop; + int temp; + + void *rules[2][2]; + struct nlist deadlist[44] = { + { "ipf_auth_stats", 0, 0, 0, 0 }, /* 0 */ + { "fae_list", 0, 0, 0, 0 }, + { "ipauth", 0, 0, 0, 0 }, + { "ipf_auth_list", 0, 0, 0, 0 }, + { "ipf_auth_start", 0, 0, 0, 0 }, + { "ipf_auth_end", 0, 0, 0, 0 }, /* 5 */ + { "ipf_auth_next", 0, 0, 0, 0 }, + { "ipf_auth", 0, 0, 0, 0 }, + { "ipf_auth_used", 0, 0, 0, 0 }, + { "ipf_auth_size", 0, 0, 0, 0 }, + { "ipf_auth_defaultage", 0, 0, 0, 0 }, /* 10 */ + { "ipf_auth_pkts", 0, 0, 0, 0 }, + { "ipf_auth_lock", 0, 0, 0, 0 }, + { "frstats", 0, 0, 0, 0 }, + { "ips_stats", 0, 0, 0, 0 }, + { "ips_num", 0, 0, 0, 0 }, /* 15 */ + { "ips_wild", 0, 0, 0, 0 }, + { "ips_list", 0, 0, 0, 0 }, + { "ips_table", 0, 0, 0, 0 }, + { "ipf_state_max", 0, 0, 0, 0 }, + { "ipf_state_size", 0, 0, 0, 0 }, /* 20 */ + { "ipf_state_doflush", 0, 0, 0, 0 }, + { "ipf_state_lock", 0, 0, 0, 0 }, + { "ipfr_heads", 0, 0, 0, 0 }, + { "ipfr_nattab", 0, 0, 0, 0 }, + { "ipfr_stats", 0, 0, 0, 0 }, /* 25 */ + { "ipfr_inuse", 0, 0, 0, 0 }, + { "ipf_ipfrttl", 0, 0, 0, 0 }, + { "ipf_frag_lock", 0, 0, 0, 0 }, + { "ipfr_timer_id", 0, 0, 0, 0 }, + { "ipf_nat_lock", 0, 0, 0, 0 }, /* 30 */ + { "ipf_rules", 0, 0, 0, 0 }, + { "ipf_acct", 0, 0, 0, 0 }, + { "ipl_frouteok", 0, 0, 0, 0 }, + { "ipf_running", 0, 0, 0, 0 }, + { "ipf_groups", 0, 0, 0, 0 }, /* 35 */ + { "ipf_active", 0, 0, 0, 0 }, + { "ipf_pass", 0, 0, 0, 0 }, + { "ipf_flags", 0, 0, 0, 0 }, + { "ipf_state_logging", 0, 0, 0, 0 }, + { "ips_tqtqb", 0, 0, 0, 0 }, /* 40 */ + { NULL, 0, 0, 0, 0 } + }; + + + frauthstp = &frauthst; + ipsstp = &ipsst; + ifrstp = &ifrst; + fiop = &fio; + + *frfp = 0; + *fiopp = fiop; + *ipsstpp = ipsstp; + *ifrstpp = ifrstp; + *frauthstpp = frauthstp; + + bzero((char *)fiop, sizeof(*fiop)); + bzero((char *)ipsstp, sizeof(*ipsstp)); + bzero((char *)ifrstp, sizeof(*ifrstp)); + bzero((char *)frauthstp, sizeof(*frauthstp)); + + if (nlist(kernel, deadlist) == -1) { + fprintf(stderr, "nlist error\n"); + return; + } + + /* + * This is for SIOCGETFF. + */ + kmemcpy((char *)frfp, (u_long)deadlist[40].n_value, sizeof(*frfp)); + + /* + * f_locks is a combination of the lock variable from each part of + * ipfilter (state, auth, nat, fragments). + */ + kmemcpy((char *)fiop, (u_long)deadlist[13].n_value, sizeof(*fiop)); + kmemcpy((char *)&fiop->f_locks[0], (u_long)deadlist[22].n_value, + sizeof(fiop->f_locks[0])); + kmemcpy((char *)&fiop->f_locks[0], (u_long)deadlist[30].n_value, + sizeof(fiop->f_locks[1])); + kmemcpy((char *)&fiop->f_locks[2], (u_long)deadlist[28].n_value, + sizeof(fiop->f_locks[2])); + kmemcpy((char *)&fiop->f_locks[3], (u_long)deadlist[12].n_value, + sizeof(fiop->f_locks[3])); + + /* + * Get pointers to each list of rules (active, inactive, in, out) + */ + kmemcpy((char *)&rules, (u_long)deadlist[31].n_value, sizeof(rules)); + fiop->f_fin[0] = rules[0][0]; + fiop->f_fin[1] = rules[0][1]; + fiop->f_fout[0] = rules[1][0]; + fiop->f_fout[1] = rules[1][1]; + + /* + * Now get accounting rules pointers. + */ + kmemcpy((char *)&rules, (u_long)deadlist[33].n_value, sizeof(rules)); + fiop->f_acctin[0] = rules[0][0]; + fiop->f_acctin[1] = rules[0][1]; + fiop->f_acctout[0] = rules[1][0]; + fiop->f_acctout[1] = rules[1][1]; + + /* + * A collection of "global" variables used inside the kernel which + * are all collected in friostat_t via ioctl. + */ + kmemcpy((char *)&fiop->f_froute, (u_long)deadlist[33].n_value, + sizeof(fiop->f_froute)); + kmemcpy((char *)&fiop->f_running, (u_long)deadlist[34].n_value, + sizeof(fiop->f_running)); + kmemcpy((char *)&fiop->f_groups, (u_long)deadlist[35].n_value, + sizeof(fiop->f_groups)); + kmemcpy((char *)&fiop->f_active, (u_long)deadlist[36].n_value, + sizeof(fiop->f_active)); + kmemcpy((char *)&fiop->f_defpass, (u_long)deadlist[37].n_value, + sizeof(fiop->f_defpass)); + + /* + * Build up the state information stats structure. + */ + kmemcpy((char *)ipsstp, (u_long)deadlist[14].n_value, sizeof(*ipsstp)); + kmemcpy((char *)&temp, (u_long)deadlist[15].n_value, sizeof(temp)); + kmemcpy((char *)ipstcptab, (u_long)deadlist[40].n_value, + sizeof(ipstcptab)); + ipsstp->iss_active = temp; + ipsstp->iss_table = (void *)deadlist[18].n_value; + ipsstp->iss_list = (void *)deadlist[17].n_value; + ipsstp->iss_tcptab = ipstcptab; + + /* + * Build up the authentiation information stats structure. + */ + kmemcpy((char *)frauthstp, (u_long)deadlist[0].n_value, + sizeof(*frauthstp)); + frauthstp->fas_faelist = (void *)deadlist[1].n_value; + + /* + * Build up the fragment information stats structure. + */ + kmemcpy((char *)ifrstp, (u_long)deadlist[25].n_value, + sizeof(*ifrstp)); + ifrstp->ifs_table = (void *)deadlist[23].n_value; + ifrstp->ifs_nattab = (void *)deadlist[24].n_value; + kmemcpy((char *)&ifrstp->ifs_inuse, (u_long)deadlist[26].n_value, + sizeof(ifrstp->ifs_inuse)); + + /* + * Get logging on/off switches + */ + kmemcpy((char *)&state_logging, (u_long)deadlist[41].n_value, + sizeof(state_logging)); +} + + +static void printside(side, frs) + char *side; + ipf_statistics_t *frs; +{ + int i; + + PRINTF("%lu\t%s bad packets\n", frs->fr_bad, side); +#ifdef USE_INET6 + PRINTF("%lu\t%s IPv6 packets\n", frs->fr_ipv6, side); +#endif + PRINTF("%lu\t%s packets blocked\n", frs->fr_block, side); + PRINTF("%lu\t%s packets passed\n", frs->fr_pass, side); + PRINTF("%lu\t%s packets not matched\n", frs->fr_nom, side); + PRINTF("%lu\t%s packets counted\n", frs->fr_acct, side); + PRINTF("%lu\t%s packets short\n", frs->fr_short, side); + PRINTF("%lu\t%s packets logged and blocked\n", frs->fr_bpkl, side); + PRINTF("%lu\t%s packets logged and passed\n", frs->fr_ppkl, side); + PRINTF("%lu\t%s fragment state kept\n", frs->fr_nfr, side); + PRINTF("%lu\t%s fragment state lost\n", frs->fr_bnfr, side); + PRINTF("%lu\t%s packet state kept\n", frs->fr_ads, side); + PRINTF("%lu\t%s packet state lost\n", frs->fr_bads, side); + PRINTF("%lu\t%s invalid source\n", frs->fr_v4_badsrc, side); + PRINTF("%lu\t%s cache hits\n", frs->fr_chit, side); + PRINTF("%lu\t%s cache misses\n", frs->fr_cmiss, side); + PRINTF("%lu\t%s bad coalesces\n", frs->fr_badcoalesces, side); + PRINTF("%lu\t%s pullups succeeded\n", frs->fr_pull[0], side); + PRINTF("%lu\t%s pullups failed\n", frs->fr_pull[1], side); + PRINTF("%lu\t%s TCP checksum failures\n", frs->fr_tcpbad, side); + for (i = 0; i <= FRB_MAX_VALUE; i++) + PRINTF("%lu\t%s block reason %s\n", + frs->fr_blocked[i], side, blockreasons[i]); +} + + +/* + * Display the kernel stats for packets blocked and passed and other + * associated running totals which are kept. + */ +static void showstats(fp, frf) + struct friostat *fp; + u_32_t frf; +{ + printside("input", &fp->f_st[0]); + printside("output", &fp->f_st[1]); + + PRINTF("%lu\tpackets logged\n", fp->f_log_ok); + PRINTF("%lu\tlog failures\n", fp->f_log_fail); + PRINTF("%lu\tred-black no memory\n", fp->f_rb_no_mem); + PRINTF("%lu\tred-black node maximum\n", fp->f_rb_node_max); + PRINTF("%lu\tICMP replies sent\n", fp->f_st[0].fr_ret); + PRINTF("%lu\tTCP RSTs sent\n", fp->f_st[1].fr_ret); + PRINTF("%lu\tfastroute successes\n", fp->f_froute[0]); + PRINTF("%lu\tfastroute failures\n", fp->f_froute[1]); + PRINTF("%u\tIPF Ticks\n", fp->f_ticks); + + PRINTF("%x\tPacket log flags set:\n", frf); + if (frf & FF_LOGPASS) + PRINTF("\tpackets passed through filter\n"); + if (frf & FF_LOGBLOCK) + PRINTF("\tpackets blocked by filter\n"); + if (frf & FF_LOGNOMATCH) + PRINTF("\tpackets not matched by filter\n"); + if (!frf) + PRINTF("\tnone\n"); +} + + +/* + * Print out a list of rules from the kernel, starting at the one passed. + */ +static int +printlivelist(fiop, out, set, fp, group, comment) + struct friostat *fiop; + int out, set; + frentry_t *fp; + char *group, *comment; +{ + struct frentry fb; + ipfruleiter_t rule; + frentry_t zero; + frgroup_t *g; + ipfobj_t obj; + int rules; + int num; + + rules = 0; + + rule.iri_inout = out; + rule.iri_active = set; + rule.iri_rule = &fb; + rule.iri_nrules = 1; + if (group != NULL) + strncpy(rule.iri_group, group, FR_GROUPLEN); + else + rule.iri_group[0] = '\0'; + + bzero((char *)&zero, sizeof(zero)); + + bzero((char *)&obj, sizeof(obj)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_IPFITER; + obj.ipfo_size = sizeof(rule); + obj.ipfo_ptr = &rule; + + while (rule.iri_rule != NULL) { + u_long array[1000]; + + memset(array, 0xff, sizeof(array)); + fp = (frentry_t *)array; + rule.iri_rule = fp; + if (ioctl(ipf_fd, SIOCIPFITER, &obj) == -1) { + ipferror(ipf_fd, "ioctl(SIOCIPFITER)"); + num = IPFGENITER_IPF; + (void) ioctl(ipf_fd,SIOCIPFDELTOK, &num); + return rules; + } + if (bcmp(fp, &zero, sizeof(zero)) == 0) + break; + if (rule.iri_rule == NULL) + break; +#ifdef USE_INET6 + if (use_inet6 != 0 && use_inet4 == 0) { + if (fp->fr_family != 0 && fp->fr_family != AF_INET6) + continue; + } else if (use_inet4 != 0 && use_inet6 == 0) { +#endif + if (fp->fr_family != 0 && fp->fr_family != AF_INET) + continue; +#ifdef USE_INET6 + } else { + if (fp->fr_family != 0 && + fp->fr_family != AF_INET && fp->fr_family != AF_INET6) + continue; + } +#endif + + if (fp->fr_data != NULL) + fp->fr_data = (char *)fp + fp->fr_size; + + rules++; + + if (opts & (OPT_HITS|OPT_DEBUG)) +#ifdef USE_QUAD_T + PRINTF("%"PRIu64" ", (unsigned long long) fp->fr_hits); +#else + PRINTF("%lu ", fp->fr_hits); +#endif + if (opts & (OPT_ACCNT|OPT_DEBUG)) +#ifdef USE_QUAD_T + PRINTF("%"PRIu64" ", (unsigned long long) fp->fr_bytes); +#else + PRINTF("%lu ", fp->fr_bytes); +#endif + if (opts & OPT_SHOWLINENO) + PRINTF("@%d ", rules); + + if (fp->fr_die != 0) + fp->fr_die -= fiop->f_ticks; + + printfr(fp, ioctl); + if (opts & OPT_DEBUG) { + binprint(fp, fp->fr_size); + if (fp->fr_data != NULL && fp->fr_dsize > 0) + binprint(fp->fr_data, fp->fr_dsize); + } + if (fp->fr_grhead != -1) { + for (g = grtop; g != NULL; g = g->fg_next) { + if (!strncmp(fp->fr_names + fp->fr_grhead, + g->fg_name, + FR_GROUPLEN)) + break; + } + if (g == NULL) { + g = calloc(1, sizeof(*g)); + + if (g != NULL) { + strncpy(g->fg_name, + fp->fr_names + fp->fr_grhead, + FR_GROUPLEN); + if (grtop == NULL) { + grtop = g; + grtail = g; + } else { + grtail->fg_next = g; + grtail = g; + } + } + } + } + if (fp->fr_type == FR_T_CALLFUNC) { + rules += printlivelist(fiop, out, set, fp->fr_data, + group, "# callfunc: "); + } + } + + num = IPFGENITER_IPF; + (void) ioctl(ipf_fd,SIOCIPFDELTOK, &num); + + return rules; +} + + +static void printdeadlist(fiop, out, set, fp, group, comment) + friostat_t *fiop; + int out, set; + frentry_t *fp; + char *group, *comment; +{ + frgroup_t *grtop, *grtail, *g; + struct frentry fb; + char *data; + u_32_t type; + int n; + + fb.fr_next = fp; + n = 0; + grtop = NULL; + grtail = NULL; + + for (n = 1; fp; fp = fb.fr_next, n++) { + if (kmemcpy((char *)&fb, (u_long)fb.fr_next, + fb.fr_size) == -1) { + perror("kmemcpy"); + return; + } + fp = &fb; +#ifdef USE_INET6 + if (use_inet6 != 0 && use_inet4 == 0) { + if (fp->fr_family != 0 && fp->fr_family != AF_INET6) + continue; + } else if (use_inet4 != 0 && use_inet6 == 0) { +#endif + if (fp->fr_family != 0 && fp->fr_family != AF_INET) + continue; +#ifdef USE_INET6 + } else { + if (fp->fr_family != 0 && + fp->fr_family != AF_INET && fp->fr_family != AF_INET6) + continue; + } +#endif + + data = NULL; + type = fb.fr_type & ~FR_T_BUILTIN; + if (type == FR_T_IPF || type == FR_T_BPFOPC) { + if (fb.fr_dsize) { + data = malloc(fb.fr_dsize); + + if (kmemcpy(data, (u_long)fb.fr_data, + fb.fr_dsize) == -1) { + perror("kmemcpy"); + return; + } + fb.fr_data = data; + } + } + + if (opts & OPT_HITS) +#ifdef USE_QUAD_T + PRINTF("%"PRIu64" ", (unsigned long long) fb.fr_hits); +#else + PRINTF("%lu ", fb.fr_hits); +#endif + if (opts & OPT_ACCNT) +#ifdef USE_QUAD_T + PRINTF("%"PRIu64" ", (unsigned long long) fb.fr_bytes); +#else + PRINTF("%lu ", fb.fr_bytes); +#endif + if (opts & OPT_SHOWLINENO) + PRINTF("@%d ", n); + + printfr(fp, ioctl); + if (opts & OPT_DEBUG) { + binprint(fp, fp->fr_size); + if (fb.fr_data != NULL && fb.fr_dsize > 0) + binprint(fb.fr_data, fb.fr_dsize); + } + if (data != NULL) + free(data); + if (fb.fr_grhead != -1) { + g = calloc(1, sizeof(*g)); + + if (g != NULL) { + strncpy(g->fg_name, fb.fr_names + fb.fr_grhead, + FR_GROUPLEN); + if (grtop == NULL) { + grtop = g; + grtail = g; + } else { + grtail->fg_next = g; + grtail = g; + } + } + } + if (type == FR_T_CALLFUNC) { + printdeadlist(fiop, out, set, fb.fr_data, group, + "# callfunc: "); + } + } + + while ((g = grtop) != NULL) { + printdeadlist(fiop, out, set, NULL, g->fg_name, comment); + grtop = g->fg_next; + free(g); + } +} + +/* + * print out all of the asked for rule sets, using the stats struct as + * the base from which to get the pointers. + */ +static void showlist(fiop) + struct friostat *fiop; +{ + struct frentry *fp = NULL; + int i, set; + + set = fiop->f_active; + if (opts & OPT_INACTIVE) + set = 1 - set; + if (opts & OPT_ACCNT) { + if (opts & OPT_OUTQUE) { + i = F_ACOUT; + fp = (struct frentry *)fiop->f_acctout[set]; + } else if (opts & OPT_INQUE) { + i = F_ACIN; + fp = (struct frentry *)fiop->f_acctin[set]; + } else { + FPRINTF(stderr, "No -i or -o given with -a\n"); + return; + } + } else { + if (opts & OPT_OUTQUE) { + i = F_OUT; + fp = (struct frentry *)fiop->f_fout[set]; + } else if (opts & OPT_INQUE) { + i = F_IN; + fp = (struct frentry *)fiop->f_fin[set]; + } else + return; + } + if (opts & OPT_DEBUG) + FPRINTF(stderr, "showlist:opts %#x i %d\n", opts, i); + + if (opts & OPT_DEBUG) + PRINTF("fp %p set %d\n", fp, set); + + if (live_kernel == 1) { + int printed; + + printed = printlivelist(fiop, i, set, fp, NULL, NULL); + if (printed == 0) { + FPRINTF(stderr, "# empty list for %s%s\n", + (opts & OPT_INACTIVE) ? "inactive " : "", + filters[i]); + } + } else { + if (!fp) { + FPRINTF(stderr, "# empty list for %s%s\n", + (opts & OPT_INACTIVE) ? "inactive " : "", + filters[i]); + } else { + printdeadlist(fiop, i, set, fp, NULL, NULL); + } + } +} + + +/* + * Display ipfilter stateful filtering information + */ +static void showipstates(ipsp, filter) + ips_stat_t *ipsp; + int *filter; +{ + ipstate_t *is; + int i; + + /* + * If a list of states hasn't been asked for, only print out stats + */ + if (!(opts & OPT_SHOWLIST)) { + showstatestats(ipsp); + return; + } + + if ((state_fields != NULL) && (nohdrfields == 0)) { + for (i = 0; state_fields[i].w_value != 0; i++) { + printfieldhdr(statefields, state_fields + i); + if (state_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } + + /* + * Print out all the state information currently held in the kernel. + */ + for (is = ipsp->iss_list; is != NULL; ) { + ipstate_t ips; + + is = fetchstate(is, &ips); + + if (is == NULL) + break; + + is = ips.is_next; + if ((filter != NULL) && + (state_matcharray(&ips, filter) == 0)) { + continue; + } + if (state_fields != NULL) { + for (i = 0; state_fields[i].w_value != 0; i++) { + printstatefield(&ips, state_fields[i].w_value); + if (state_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else { + printstate(&ips, opts, ipsp->iss_ticks); + } + } +} + + +static void showstatestats(ipsp) + ips_stat_t *ipsp; +{ + int minlen, maxlen, totallen; + ipftable_t table; + u_int *buckets; + ipfobj_t obj; + int i, sz; + + /* + * If a list of states hasn't been asked for, only print out stats + */ + + sz = sizeof(*buckets) * ipsp->iss_state_size; + buckets = (u_int *)malloc(sz); + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_GTABLE; + obj.ipfo_size = sizeof(table); + obj.ipfo_ptr = &table; + + table.ita_type = IPFTABLE_BUCKETS; + table.ita_table = buckets; + + if (live_kernel == 1) { + if (ioctl(state_fd, SIOCGTABL, &obj) != 0) { + free(buckets); + return; + } + } else { + if (kmemcpy((char *)buckets, + (u_long)ipsp->iss_bucketlen, sz)) { + free(buckets); + return; + } + } + + PRINTF("%u\tactive state table entries\n",ipsp->iss_active); + PRINTF("%lu\tadd bad\n", ipsp->iss_add_bad); + PRINTF("%lu\tadd duplicate\n", ipsp->iss_add_dup); + PRINTF("%lu\tadd locked\n", ipsp->iss_add_locked); + PRINTF("%lu\tadd oow\n", ipsp->iss_add_oow); + PRINTF("%lu\tbucket full\n", ipsp->iss_bucket_full); + PRINTF("%lu\tcheck bad\n", ipsp->iss_check_bad); + PRINTF("%lu\tcheck miss\n", ipsp->iss_check_miss); + PRINTF("%lu\tcheck nattag\n", ipsp->iss_check_nattag); + PRINTF("%lu\tclone nomem\n", ipsp->iss_clone_nomem); + PRINTF("%lu\tcheck notag\n", ipsp->iss_check_notag); + PRINTF("%lu\tcheck success\n", ipsp->iss_hits); + PRINTF("%lu\tcloned\n", ipsp->iss_cloned); + PRINTF("%lu\texpired\n", ipsp->iss_expire); + PRINTF("%lu\tflush all\n", ipsp->iss_flush_all); + PRINTF("%lu\tflush closing\n", ipsp->iss_flush_closing); + PRINTF("%lu\tflush queue\n", ipsp->iss_flush_queue); + PRINTF("%lu\tflush state\n", ipsp->iss_flush_state); + PRINTF("%lu\tflush timeout\n", ipsp->iss_flush_timeout); + PRINTF("%u\thash buckets in use\n", ipsp->iss_inuse); + PRINTF("%lu\tICMP bad\n", ipsp->iss_icmp_bad); + PRINTF("%lu\tICMP banned\n", ipsp->iss_icmp_banned); + PRINTF("%lu\tICMP errors\n", ipsp->iss_icmp_icmperr); + PRINTF("%lu\tICMP head block\n", ipsp->iss_icmp_headblock); + PRINTF("%lu\tICMP hits\n", ipsp->iss_icmp_hits); + PRINTF("%lu\tICMP not query\n", ipsp->iss_icmp_notquery); + PRINTF("%lu\tICMP short\n", ipsp->iss_icmp_short); + PRINTF("%lu\tICMP too many\n", ipsp->iss_icmp_toomany); + PRINTF("%lu\tICMPv6 errors\n", ipsp->iss_icmp6_icmperr); + PRINTF("%lu\tICMPv6 miss\n", ipsp->iss_icmp6_miss); + PRINTF("%lu\tICMPv6 not info\n", ipsp->iss_icmp6_notinfo); + PRINTF("%lu\tICMPv6 not query\n", ipsp->iss_icmp6_notquery); + PRINTF("%lu\tlog fail\n", ipsp->iss_log_fail); + PRINTF("%lu\tlog ok\n", ipsp->iss_log_ok); + PRINTF("%lu\tlookup interface mismatch\n", ipsp->iss_lookup_badifp); + PRINTF("%lu\tlookup mask mismatch\n", ipsp->iss_miss_mask); + PRINTF("%lu\tlookup port mismatch\n", ipsp->iss_lookup_badport); + PRINTF("%lu\tlookup miss\n", ipsp->iss_lookup_miss); + PRINTF("%lu\tmaximum rule references\n", ipsp->iss_max_ref); + PRINTF("%lu\tmaximum hosts per rule\n", ipsp->iss_max_track); + PRINTF("%lu\tno memory\n", ipsp->iss_nomem); + PRINTF("%lu\tout of window\n", ipsp->iss_oow); + PRINTF("%lu\torphans\n", ipsp->iss_orphan); + PRINTF("%lu\tscan block\n", ipsp->iss_scan_block); + PRINTF("%lu\tstate table maximum reached\n", ipsp->iss_max); + PRINTF("%lu\tTCP closing\n", ipsp->iss_tcp_closing); + PRINTF("%lu\tTCP OOW\n", ipsp->iss_tcp_oow); + PRINTF("%lu\tTCP RST add\n", ipsp->iss_tcp_rstadd); + PRINTF("%lu\tTCP too small\n", ipsp->iss_tcp_toosmall); + PRINTF("%lu\tTCP bad options\n", ipsp->iss_tcp_badopt); + PRINTF("%lu\tTCP removed\n", ipsp->iss_fin); + PRINTF("%lu\tTCP FSM\n", ipsp->iss_tcp_fsm); + PRINTF("%lu\tTCP strict\n", ipsp->iss_tcp_strict); + PRINTF("%lu\tTCP wild\n", ipsp->iss_wild); + PRINTF("%lu\tMicrosoft Windows SACK\n", ipsp->iss_winsack); + + PRINTF("State logging %sabled\n", state_logging ? "en" : "dis"); + + PRINTF("IP states added:\n"); + for (i = 0; i < 256; i++) { + if (ipsp->iss_proto[i] != 0) { + struct protoent *proto; + + proto = getprotobynumber(i); + PRINTF("%lu", ipsp->iss_proto[i]); + if (proto != NULL) + PRINTF("\t%s\n", proto->p_name); + else + PRINTF("\t%d\n", i); + } + } + + PRINTF("\nState table bucket statistics:\n"); + PRINTF("%u\tin use\n", ipsp->iss_inuse); + + minlen = ipsp->iss_max; + totallen = 0; + maxlen = 0; + + for (i = 0; i < ipsp->iss_state_size; i++) { + if (buckets[i] > maxlen) + maxlen = buckets[i]; + if (buckets[i] < minlen) + minlen = buckets[i]; + totallen += buckets[i]; + } + + PRINTF("%d\thash efficiency\n", + totallen ? ipsp->iss_inuse * 100 / totallen : 0); + PRINTF("%2.2f%%\tbucket usage\n%u\tminimal length\n", + ((float)ipsp->iss_inuse / ipsp->iss_state_size) * 100.0, + minlen); + PRINTF("%u\tmaximal length\n%.3f\taverage length\n", + maxlen, + ipsp->iss_inuse ? (float) totallen/ ipsp->iss_inuse : + 0.0); + +#define ENTRIES_PER_LINE 5 + + if (opts & OPT_VERBOSE) { + PRINTF("\nCurrent bucket sizes :\n"); + for (i = 0; i < ipsp->iss_state_size; i++) { + if ((i % ENTRIES_PER_LINE) == 0) + PRINTF("\t"); + PRINTF("%4d -> %4u", i, buckets[i]); + if ((i % ENTRIES_PER_LINE) == + (ENTRIES_PER_LINE - 1)) + PRINTF("\n"); + else + PRINTF(" "); + } + PRINTF("\n"); + } + PRINTF("\n"); + + free(buckets); + + if (live_kernel == 1) { + showtqtable_live(state_fd); + } else { + printtqtable(ipsp->iss_tcptab); + } +} + + +#ifdef STATETOP +static int handle_resize = 0, handle_break = 0; + +static void topipstates(saddr, daddr, sport, dport, protocol, ver, + refreshtime, topclosed, filter) + i6addr_t saddr; + i6addr_t daddr; + int sport; + int dport; + int protocol; + int ver; + int refreshtime; + int topclosed; + int *filter; +{ + char str1[STSTRSIZE], str2[STSTRSIZE], str3[STSTRSIZE], str4[STSTRSIZE]; + int maxtsentries = 0, reverse = 0, sorting = STSORT_DEFAULT; + int i, j, winy, tsentry, maxx, maxy, redraw = 0, ret = 0; + int len, srclen, dstlen, forward = 1, c = 0; + ips_stat_t ipsst, *ipsstp = &ipsst; + int token_type = IPFGENITER_STATE; + statetop_t *tstable = NULL, *tp; + const char *errstr = ""; + ipstate_t ips; + ipfobj_t ipfo; + struct timeval selecttimeout; + char hostnm[HOSTNMLEN]; + struct protoent *proto; + fd_set readfd; + time_t t; + + /* install signal handlers */ + signal(SIGINT, sig_break); + signal(SIGQUIT, sig_break); + signal(SIGTERM, sig_break); + signal(SIGWINCH, sig_resize); + + /* init ncurses stuff */ + initscr(); + cbreak(); + noecho(); + curs_set(0); + timeout(0); + getmaxyx(stdscr, maxy, maxx); + + /* init hostname */ + gethostname(hostnm, sizeof(hostnm) - 1); + hostnm[sizeof(hostnm) - 1] = '\0'; + + /* init ipfobj_t stuff */ + bzero((caddr_t)&ipfo, sizeof(ipfo)); + ipfo.ipfo_rev = IPFILTER_VERSION; + ipfo.ipfo_type = IPFOBJ_STATESTAT; + ipfo.ipfo_size = sizeof(*ipsstp); + ipfo.ipfo_ptr = (void *)ipsstp; + + /* repeat until user aborts */ + while ( 1 ) { + + /* get state table */ + bzero((char *)&ipsst, sizeof(ipsst)); + if ((ioctl(state_fd, SIOCGETFS, &ipfo) == -1)) { + errstr = "ioctl(SIOCGETFS)"; + ret = -1; + goto out; + } + + /* clear the history */ + tsentry = -1; + + /* reset max str len */ + srclen = dstlen = 0; + + /* read the state table and store in tstable */ + for (; ipsstp->iss_list; ipsstp->iss_list = ips.is_next) { + + ipsstp->iss_list = fetchstate(ipsstp->iss_list, &ips); + if (ipsstp->iss_list == NULL) + break; + + if (ver != 0 && ips.is_v != ver) + continue; + + if ((filter != NULL) && + (state_matcharray(&ips, filter) == 0)) + continue; + + /* check v4 src/dest addresses */ + if (ips.is_v == 4) { + if ((saddr.in4.s_addr != INADDR_ANY && + saddr.in4.s_addr != ips.is_saddr) || + (daddr.in4.s_addr != INADDR_ANY && + daddr.in4.s_addr != ips.is_daddr)) + continue; + } +#ifdef USE_INET6 + /* check v6 src/dest addresses */ + if (ips.is_v == 6) { + if ((IP6_NEQ(&saddr, &in6addr_any) && + IP6_NEQ(&saddr, &ips.is_src)) || + (IP6_NEQ(&daddr, &in6addr_any) && + IP6_NEQ(&daddr, &ips.is_dst))) + continue; + } +#endif + /* check protocol */ + if (protocol > 0 && protocol != ips.is_p) + continue; + + /* check ports if protocol is TCP or UDP */ + if (((ips.is_p == IPPROTO_TCP) || + (ips.is_p == IPPROTO_UDP)) && + (((sport > 0) && (htons(sport) != ips.is_sport)) || + ((dport > 0) && (htons(dport) != ips.is_dport)))) + continue; + + /* show closed TCP sessions ? */ + if ((topclosed == 0) && (ips.is_p == IPPROTO_TCP) && + (ips.is_state[0] >= IPF_TCPS_LAST_ACK) && + (ips.is_state[1] >= IPF_TCPS_LAST_ACK)) + continue; + + /* + * if necessary make room for this state + * entry + */ + tsentry++; + if (!maxtsentries || tsentry == maxtsentries) { + maxtsentries += STGROWSIZE; + tstable = reallocarray(tstable, maxtsentries, + sizeof(statetop_t)); + if (tstable == NULL) { + perror("realloc"); + exit(-1); + } + } + + /* get max src/dest address string length */ + len = strlen(getip(ips.is_v, &ips.is_src)); + if (srclen < len) + srclen = len; + len = strlen(getip(ips.is_v, &ips.is_dst)); + if (dstlen < len) + dstlen = len; + + /* fill structure */ + tp = tstable + tsentry; + tp->st_src = ips.is_src; + tp->st_dst = ips.is_dst; + tp->st_p = ips.is_p; + tp->st_v = ips.is_v; + tp->st_state[0] = ips.is_state[0]; + tp->st_state[1] = ips.is_state[1]; + if (forward) { + tp->st_pkts = ips.is_pkts[0]+ips.is_pkts[1]; + tp->st_bytes = ips.is_bytes[0]+ips.is_bytes[1]; + } else { + tp->st_pkts = ips.is_pkts[2]+ips.is_pkts[3]; + tp->st_bytes = ips.is_bytes[2]+ips.is_bytes[3]; + } + tp->st_age = ips.is_die - ipsstp->iss_ticks; + if ((ips.is_p == IPPROTO_TCP) || + (ips.is_p == IPPROTO_UDP)) { + tp->st_sport = ips.is_sport; + tp->st_dport = ips.is_dport; + } + } + + (void) ioctl(state_fd, SIOCIPFDELTOK, &token_type); + + /* sort the array */ + if (tsentry != -1) { + switch (sorting) + { + case STSORT_PR: + qsort(tstable, tsentry + 1, + sizeof(statetop_t), sort_p); + break; + case STSORT_PKTS: + qsort(tstable, tsentry + 1, + sizeof(statetop_t), sort_pkts); + break; + case STSORT_BYTES: + qsort(tstable, tsentry + 1, + sizeof(statetop_t), sort_bytes); + break; + case STSORT_TTL: + qsort(tstable, tsentry + 1, + sizeof(statetop_t), sort_ttl); + break; + case STSORT_SRCIP: + qsort(tstable, tsentry + 1, + sizeof(statetop_t), sort_srcip); + break; + case STSORT_SRCPT: + qsort(tstable, tsentry +1, + sizeof(statetop_t), sort_srcpt); + break; + case STSORT_DSTIP: + qsort(tstable, tsentry + 1, + sizeof(statetop_t), sort_dstip); + break; + case STSORT_DSTPT: + qsort(tstable, tsentry + 1, + sizeof(statetop_t), sort_dstpt); + break; + default: + break; + } + } + + /* handle window resizes */ + if (handle_resize) { + endwin(); + initscr(); + cbreak(); + noecho(); + curs_set(0); + timeout(0); + getmaxyx(stdscr, maxy, maxx); + redraw = 1; + handle_resize = 0; + } + + /* stop program? */ + if (handle_break) + break; + + /* print title */ + erase(); + attron(A_BOLD); + winy = 0; + move(winy,0); + snprintf(str1, sizeof(str1), "%s - %s - state top", hostnm, IPL_VERSION); + for (j = 0 ; j < (maxx - 8 - strlen(str1)) / 2; j++) + printw(" "); + printw("%s", str1); + attroff(A_BOLD); + + /* just for fun add a clock */ + move(winy, maxx - 8); + t = time(NULL); + strftime(str1, 80, "%T", localtime(&t)); + printw("%s\n", str1); + + /* + * print the display filters, this is placed in the loop, + * because someday I might add code for changing these + * while the programming is running :-) + */ + if (sport >= 0) + snprintf(str1, sizeof(str1), "%s,%d", getip(ver, &saddr), sport); + else + snprintf(str1, sizeof(str1), "%s", getip(ver, &saddr)); + + if (dport >= 0) + snprintf(str2, sizeof(str2), "%s,%d", getip(ver, &daddr), dport); + else + snprintf(str2, sizeof(str2), "%s", getip(ver, &daddr)); + + if (protocol < 0) + strcpy(str3, "any"); + else if ((proto = getprotobynumber(protocol)) != NULL) + snprintf(str3, sizeof(str3), "%s", proto->p_name); + else + snprintf(str3, sizeof(str3), "%d", protocol); + + switch (sorting) + { + case STSORT_PR: + snprintf(str4, sizeof(str4), "proto"); + break; + case STSORT_PKTS: + snprintf(str4, sizeof(str4), "# pkts"); + break; + case STSORT_BYTES: + snprintf(str4, sizeof(str4), "# bytes"); + break; + case STSORT_TTL: + snprintf(str4, sizeof(str4), "ttl"); + break; + case STSORT_SRCIP: + snprintf(str4, sizeof(str4), "src ip"); + break; + case STSORT_SRCPT: + snprintf(str4, sizeof(str4), "src port"); + break; + case STSORT_DSTIP: + snprintf(str4, sizeof(str4), "dest ip"); + break; + case STSORT_DSTPT: + snprintf(str4, sizeof(str4), "dest port"); + break; + default: + snprintf(str4, sizeof(str4), "unknown"); + break; + } + + if (reverse) + strcat(str4, " (reverse)"); + + winy += 2; + move(winy,0); + printw("Src: %s, Dest: %s, Proto: %s, Sorted by: %s\n\n", + str1, str2, str3, str4); + + /* + * For an IPv4 IP address we need at most 15 characters, + * 4 tuples of 3 digits, separated by 3 dots. Enforce this + * length, so the colums do not change positions based + * on the size of the IP address. This length makes the + * output fit in a 80 column terminal. + * We are lacking a good solution for IPv6 addresses (that + * can be longer that 15 characters), so we do not enforce + * a maximum on the IP field size. + */ + if (srclen < 15) + srclen = 15; + if (dstlen < 15) + dstlen = 15; + + /* print column description */ + winy += 2; + move(winy,0); + attron(A_BOLD); + printw("%-*s %-*s %3s %4s %7s %9s %9s\n", + srclen + 6, "Source IP", dstlen + 6, "Destination IP", + "ST", "PR", "#pkts", "#bytes", "ttl"); + attroff(A_BOLD); + + /* print all the entries */ + tp = tstable; + if (reverse) + tp += tsentry; + + if (tsentry > maxy - 6) + tsentry = maxy - 6; + for (i = 0; i <= tsentry; i++) { + /* print src/dest and port */ + if ((tp->st_p == IPPROTO_TCP) || + (tp->st_p == IPPROTO_UDP)) { + snprintf(str1, sizeof(str1), "%s,%hu", + getip(tp->st_v, &tp->st_src), + ntohs(tp->st_sport)); + snprintf(str2, sizeof(str2), "%s,%hu", + getip(tp->st_v, &tp->st_dst), + ntohs(tp->st_dport)); + } else { + snprintf(str1, sizeof(str1), "%s", getip(tp->st_v, + &tp->st_src)); + snprintf(str2, sizeof(str2), "%s", getip(tp->st_v, + &tp->st_dst)); + } + winy++; + move(winy, 0); + printw("%-*s %-*s", srclen + 6, str1, dstlen + 6, str2); + + /* print state */ + snprintf(str1, sizeof(str1), "%X/%X", tp->st_state[0], + tp->st_state[1]); + printw(" %3s", str1); + + /* print protocol */ + proto = getprotobynumber(tp->st_p); + if (proto) { + strncpy(str1, proto->p_name, 4); + str1[4] = '\0'; + } else { + snprintf(str1, sizeof(str1), "%d", tp->st_p); + } + /* just print icmp for IPv6-ICMP */ + if (tp->st_p == IPPROTO_ICMPV6) + strcpy(str1, "icmp"); + printw(" %4s", str1); + + /* print #pkt/#bytes */ +#ifdef USE_QUAD_T + printw(" %7qu %9qu", (unsigned long long) tp->st_pkts, + (unsigned long long) tp->st_bytes); +#else + printw(" %7lu %9lu", tp->st_pkts, tp->st_bytes); +#endif + printw(" %9s", ttl_to_string(tp->st_age)); + + if (reverse) + tp--; + else + tp++; + } + + /* screen data structure is filled, now update the screen */ + if (redraw) + clearok(stdscr,1); + + if (refresh() == ERR) + break; + if (redraw) { + clearok(stdscr,0); + redraw = 0; + } + + /* wait for key press or a 1 second time out period */ + selecttimeout.tv_sec = refreshtime; + selecttimeout.tv_usec = 0; + FD_ZERO(&readfd); + FD_SET(0, &readfd); + select(1, &readfd, NULL, NULL, &selecttimeout); + + /* if key pressed, read all waiting keys */ + if (FD_ISSET(0, &readfd)) { + c = wgetch(stdscr); + if (c == ERR) + continue; + + if (ISALPHA(c) && ISUPPER(c)) + c = TOLOWER(c); + if (c == 'l') { + redraw = 1; + } else if (c == 'q') { + break; + } else if (c == 'r') { + reverse = !reverse; + } else if (c == 'b') { + forward = 0; + } else if (c == 'f') { + forward = 1; + } else if (c == 's') { + if (++sorting > STSORT_MAX) + sorting = 0; + } + } + } /* while */ + +out: + printw("\n"); + curs_set(1); + /* nocbreak(); XXX - endwin() should make this redundant */ + endwin(); + + free(tstable); + if (ret != 0) + perror(errstr); +} +#endif + + +/* + * Show fragment cache information that's held in the kernel. + */ +static void showfrstates(ifsp, ticks) + ipfrstat_t *ifsp; + u_long ticks; +{ + struct ipfr *ipfrtab[IPFT_SIZE], ifr; + int i; + + /* + * print out the numeric statistics + */ + PRINTF("IP fragment states:\n%lu\tnew\n%lu\texpired\n%lu\thits\n", + ifsp->ifs_new, ifsp->ifs_expire, ifsp->ifs_hits); + PRINTF("%lu\tretrans\n%lu\ttoo short\n", + ifsp->ifs_retrans0, ifsp->ifs_short); + PRINTF("%lu\tno memory\n%lu\talready exist\n", + ifsp->ifs_nomem, ifsp->ifs_exists); + PRINTF("%lu\tinuse\n", ifsp->ifs_inuse); + PRINTF("\n"); + + if (live_kernel == 0) { + if (kmemcpy((char *)ipfrtab, (u_long)ifsp->ifs_table, + sizeof(ipfrtab))) + return; + } + + /* + * Print out the contents (if any) of the fragment cache table. + */ + if (live_kernel == 1) { + do { + if (fetchfrag(ipf_fd, IPFGENITER_FRAG, &ifr) != 0) + break; + if (ifr.ipfr_ifp == NULL) + break; + ifr.ipfr_ttl -= ticks; + printfraginfo("", &ifr); + } while (ifr.ipfr_next != NULL); + } else { + for (i = 0; i < IPFT_SIZE; i++) + while (ipfrtab[i] != NULL) { + if (kmemcpy((char *)&ifr, (u_long)ipfrtab[i], + sizeof(ifr)) == -1) + break; + printfraginfo("", &ifr); + ipfrtab[i] = ifr.ipfr_next; + } + } + /* + * Print out the contents (if any) of the NAT fragment cache table. + */ + + if (live_kernel == 0) { + if (kmemcpy((char *)ipfrtab, (u_long)ifsp->ifs_nattab, + sizeof(ipfrtab))) + return; + } + + if (live_kernel == 1) { + do { + if (fetchfrag(nat_fd, IPFGENITER_NATFRAG, &ifr) != 0) + break; + if (ifr.ipfr_ifp == NULL) + break; + ifr.ipfr_ttl -= ticks; + printfraginfo("NAT: ", &ifr); + } while (ifr.ipfr_next != NULL); + } else { + for (i = 0; i < IPFT_SIZE; i++) + while (ipfrtab[i] != NULL) { + if (kmemcpy((char *)&ifr, (u_long)ipfrtab[i], + sizeof(ifr)) == -1) + break; + printfraginfo("NAT: ", &ifr); + ipfrtab[i] = ifr.ipfr_next; + } + } +} + + +/* + * Show stats on how auth within IPFilter has been used + */ +static void showauthstates(asp) + ipf_authstat_t *asp; +{ + frauthent_t *frap, fra; + ipfgeniter_t auth; + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_GENITER; + obj.ipfo_size = sizeof(auth); + obj.ipfo_ptr = &auth; + + auth.igi_type = IPFGENITER_AUTH; + auth.igi_nitems = 1; + auth.igi_data = &fra; + +#ifdef USE_QUAD_T + printf("Authorisation hits: %"PRIu64"\tmisses %"PRIu64"\n", + (unsigned long long) asp->fas_hits, + (unsigned long long) asp->fas_miss); +#else + printf("Authorisation hits: %ld\tmisses %ld\n", asp->fas_hits, + asp->fas_miss); +#endif + printf("nospace %ld\nadded %ld\nsendfail %ld\nsendok %ld\n", + asp->fas_nospace, asp->fas_added, asp->fas_sendfail, + asp->fas_sendok); + printf("queok %ld\nquefail %ld\nexpire %ld\n", + asp->fas_queok, asp->fas_quefail, asp->fas_expire); + + frap = asp->fas_faelist; + while (frap) { + if (live_kernel == 1) { + if (ioctl(auth_fd, SIOCGENITER, &obj)) + break; + } else { + if (kmemcpy((char *)&fra, (u_long)frap, + sizeof(fra)) == -1) + break; + } + printf("age %ld\t", fra.fae_age); + printfr(&fra.fae_fr, ioctl); + frap = fra.fae_next; + } +} + + +/* + * Display groups used for each of filter rules, accounting rules and + * authentication, separately. + */ +static void showgroups(fiop) + struct friostat *fiop; +{ + static char *gnames[3] = { "Filter", "Accounting", "Authentication" }; + static int gnums[3] = { IPL_LOGIPF, IPL_LOGCOUNT, IPL_LOGAUTH }; + frgroup_t *fp, grp; + int on, off, i; + + on = fiop->f_active; + off = 1 - on; + + for (i = 0; i < 3; i++) { + printf("%s groups (active):\n", gnames[i]); + for (fp = fiop->f_groups[gnums[i]][on]; fp != NULL; + fp = grp.fg_next) + if (kmemcpy((char *)&grp, (u_long)fp, sizeof(grp))) + break; + else + printf("%s\n", grp.fg_name); + printf("%s groups (inactive):\n", gnames[i]); + for (fp = fiop->f_groups[gnums[i]][off]; fp != NULL; + fp = grp.fg_next) + if (kmemcpy((char *)&grp, (u_long)fp, sizeof(grp))) + break; + else + printf("%s\n", grp.fg_name); + } +} + + +static void parse_ipportstr(argument, ip, port) + const char *argument; + i6addr_t *ip; + int *port; +{ + char *s, *comma; + int ok = 0; + + /* make working copy of argument, Theoretically you must be able + * to write to optarg, but that seems very ugly to me.... + */ + s = strdup(argument); + if (s == NULL) + return; + + /* get port */ + if ((comma = strchr(s, ',')) != NULL) { + if (!strcasecmp(comma + 1, "any")) { + *port = -1; + } else if (!sscanf(comma + 1, "%d", port) || + (*port < 0) || (*port > 65535)) { + fprintf(stderr, "Invalid port specification in %s\n", + argument); + free(s); + exit(-2); + } + *comma = '\0'; + } + + + /* get ip address */ + if (!strcasecmp(s, "any")) { + ip->in4.s_addr = INADDR_ANY; + ok = 1; +#ifdef USE_INET6 + ip->in6 = in6addr_any; + } else if (use_inet6 && !use_inet4 && inet_pton(AF_INET6, s, &ip->in6)) { + ok = 1; +#endif + } else if (inet_aton(s, &ip->in4)) + ok = 1; + + if (ok == 0) { + fprintf(stderr, "Invalid IP address: %s\n", s); + free(s); + exit(-2); + } + + /* free allocated memory */ + free(s); +} + + +#ifdef STATETOP +static void sig_resize(s) + int s; +{ + handle_resize = 1; +} + +static void sig_break(s) + int s; +{ + handle_break = 1; +} + +static char *getip(v, addr) + int v; + i6addr_t *addr; +{ +#ifdef USE_INET6 + static char hostbuf[MAXHOSTNAMELEN+1]; +#endif + + if (v == 0) + return ("any"); + + if (v == 4) + return inet_ntoa(addr->in4); + +#ifdef USE_INET6 + (void) inet_ntop(AF_INET6, &addr->in6, hostbuf, sizeof(hostbuf) - 1); + hostbuf[MAXHOSTNAMELEN] = '\0'; + return hostbuf; +#else + return "IPv6"; +#endif +} + + +static char *ttl_to_string(ttl) + long int ttl; +{ + static char ttlbuf[STSTRSIZE]; + int hours, minutes, seconds; + + /* ttl is in half seconds */ + ttl /= 2; + + hours = ttl / 3600; + ttl = ttl % 3600; + minutes = ttl / 60; + seconds = ttl % 60; + + if (hours > 0) + snprintf(ttlbuf, sizeof(ttlbuf), "%2d:%02d:%02d", hours, minutes, seconds); + else + snprintf(ttlbuf, sizeof(ttlbuf), "%2d:%02d", minutes, seconds); + return ttlbuf; +} + + +static int sort_pkts(a, b) + const void *a; + const void *b; +{ + + register const statetop_t *ap = a; + register const statetop_t *bp = b; + + if (ap->st_pkts == bp->st_pkts) + return 0; + else if (ap->st_pkts < bp->st_pkts) + return 1; + return -1; +} + + +static int sort_bytes(a, b) + const void *a; + const void *b; +{ + register const statetop_t *ap = a; + register const statetop_t *bp = b; + + if (ap->st_bytes == bp->st_bytes) + return 0; + else if (ap->st_bytes < bp->st_bytes) + return 1; + return -1; +} + + +static int sort_p(a, b) + const void *a; + const void *b; +{ + register const statetop_t *ap = a; + register const statetop_t *bp = b; + + if (ap->st_p == bp->st_p) + return 0; + else if (ap->st_p < bp->st_p) + return 1; + return -1; +} + + +static int sort_ttl(a, b) + const void *a; + const void *b; +{ + register const statetop_t *ap = a; + register const statetop_t *bp = b; + + if (ap->st_age == bp->st_age) + return 0; + else if (ap->st_age < bp->st_age) + return 1; + return -1; +} + +static int sort_srcip(a, b) + const void *a; + const void *b; +{ + register const statetop_t *ap = a; + register const statetop_t *bp = b; + +#ifdef USE_INET6 + if (use_inet6 && !use_inet4) { + if (IP6_EQ(&ap->st_src, &bp->st_src)) + return 0; + else if (IP6_GT(&ap->st_src, &bp->st_src)) + return 1; + } else +#endif + { + if (ntohl(ap->st_src.in4.s_addr) == + ntohl(bp->st_src.in4.s_addr)) + return 0; + else if (ntohl(ap->st_src.in4.s_addr) > + ntohl(bp->st_src.in4.s_addr)) + return 1; + } + return -1; +} + +static int sort_srcpt(a, b) + const void *a; + const void *b; +{ + register const statetop_t *ap = a; + register const statetop_t *bp = b; + + if (htons(ap->st_sport) == htons(bp->st_sport)) + return 0; + else if (htons(ap->st_sport) > htons(bp->st_sport)) + return 1; + return -1; +} + +static int sort_dstip(a, b) + const void *a; + const void *b; +{ + register const statetop_t *ap = a; + register const statetop_t *bp = b; + +#ifdef USE_INET6 + if (use_inet6 && !use_inet4) { + if (IP6_EQ(&ap->st_dst, &bp->st_dst)) + return 0; + else if (IP6_GT(&ap->st_dst, &bp->st_dst)) + return 1; + } else +#endif + { + if (ntohl(ap->st_dst.in4.s_addr) == + ntohl(bp->st_dst.in4.s_addr)) + return 0; + else if (ntohl(ap->st_dst.in4.s_addr) > + ntohl(bp->st_dst.in4.s_addr)) + return 1; + } + return -1; +} + +static int sort_dstpt(a, b) + const void *a; + const void *b; +{ + register const statetop_t *ap = a; + register const statetop_t *bp = b; + + if (htons(ap->st_dport) == htons(bp->st_dport)) + return 0; + else if (htons(ap->st_dport) > htons(bp->st_dport)) + return 1; + return -1; +} + +#endif + + +ipstate_t *fetchstate(src, dst) + ipstate_t *src, *dst; +{ + + if (live_kernel == 1) { + ipfgeniter_t state; + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_GENITER; + obj.ipfo_size = sizeof(state); + obj.ipfo_ptr = &state; + + state.igi_type = IPFGENITER_STATE; + state.igi_nitems = 1; + state.igi_data = dst; + + if (ioctl(state_fd, SIOCGENITER, &obj) != 0) + return NULL; + if (dst->is_next == NULL) { + int n = IPFGENITER_STATE; + (void) ioctl(ipf_fd,SIOCIPFDELTOK, &n); + } + } else { + if (kmemcpy((char *)dst, (u_long)src, sizeof(*dst))) + return NULL; + } + return dst; +} + + +static int fetchfrag(fd, type, frp) + int fd, type; + ipfr_t *frp; +{ + ipfgeniter_t frag; + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_GENITER; + obj.ipfo_size = sizeof(frag); + obj.ipfo_ptr = &frag; + + frag.igi_type = type; + frag.igi_nitems = 1; + frag.igi_data = frp; + + if (ioctl(fd, SIOCGENITER, &obj)) + return EFAULT; + return 0; +} + + +static int state_matcharray(stp, array) + ipstate_t *stp; + int *array; +{ + int i, n, *x, rv, p; + ipfexp_t *e; + + rv = 0; + + for (n = array[0], x = array + 1; n > 0; x += e->ipfe_size) { + e = (ipfexp_t *)x; + if (e->ipfe_cmd == IPF_EXP_END) + break; + n -= e->ipfe_size; + + rv = 0; + /* + * The upper 16 bits currently store the protocol value. + * This is currently used with TCP and UDP port compares and + * allows "tcp.port = 80" without requiring an explicit + " "ip.pr = tcp" first. + */ + p = e->ipfe_cmd >> 16; + if ((p != 0) && (p != stp->is_p)) + break; + + switch (e->ipfe_cmd) + { + case IPF_EXP_IP_PR : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_p == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IP_SRCADDR : + if (stp->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((stp->is_saddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_DSTADDR : + if (stp->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((stp->is_daddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_ADDR : + if (stp->is_v != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((stp->is_saddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((stp->is_daddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + +#ifdef USE_INET6 + case IPF_EXP_IP6_SRCADDR : + if (stp->is_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&stp->is_src, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_DSTADDR : + if (stp->is_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&stp->is_dst, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_ADDR : + if (stp->is_v != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&stp->is_src, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&stp->is_dst, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; +#endif + + case IPF_EXP_UDP_PORT : + case IPF_EXP_TCP_PORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_sport == e->ipfe_arg0[i]) || + (stp->is_dport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_SPORT : + case IPF_EXP_TCP_SPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_sport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_DPORT : + case IPF_EXP_TCP_DPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_dport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IDLE_GT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_die < e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_TCP_STATE : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (stp->is_state[0] == e->ipfe_arg0[i]) || + (stp->is_state[1] == e->ipfe_arg0[i]); + } + break; + } + rv ^= e->ipfe_not; + + if (rv == 0) + break; + } + + return rv; +} + + +static void showtqtable_live(fd) + int fd; +{ + ipftq_t table[IPF_TCP_NSTATES]; + ipfobj_t obj; + + bzero((char *)&obj, sizeof(obj)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = sizeof(table); + obj.ipfo_ptr = (void *)table; + obj.ipfo_type = IPFOBJ_STATETQTAB; + + if (ioctl(fd, SIOCGTQTAB, &obj) == 0) { + printtqtable(table); + } +} diff --git a/sbin/ipf/ipftest/ip_fil.c b/sbin/ipf/ipftest/ip_fil.c new file mode 100644 index 000000000000..327f90fc356c --- /dev/null +++ b/sbin/ipf/ipftest/ip_fil.c @@ -0,0 +1,812 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +#include "ipf.h" +#include "md5.h" +#include "ipt.h" + +ipf_main_softc_t ipfmain; + +static struct ifnet **ifneta = NULL; +static int nifs = 0; + +struct rtentry; + +static void ipf_setifpaddr(struct ifnet *, char *); +void init_ifp(void); +static int no_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); +static int write_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); + +struct ifaddr { + struct sockaddr_storage ifa_addr; +}; + +int +ipfattach(softc) + ipf_main_softc_t *softc; +{ + return 0; +} + + +int +ipfdetach(softc) + ipf_main_softc_t *softc; +{ + return 0; +} + + +/* + * Filter ioctl interface. + */ +int +ipfioctl(softc, dev, cmd, data, mode) + ipf_main_softc_t *softc; + int dev; + ioctlcmd_t cmd; + caddr_t data; + int mode; +{ + int error = 0, unit = 0, uid; + + uid = getuid(); + unit = dev; + + SPL_NET(s); + + error = ipf_ioctlswitch(softc, unit, data, cmd, mode, uid, NULL); + if (error != -1) { + SPL_X(s); + return error; + } + SPL_X(s); + return error; +} + + +void +ipf_forgetifp(softc, ifp) + ipf_main_softc_t *softc; + void *ifp; +{ + register frentry_t *f; + + WRITE_ENTER(&softc->ipf_mutex); + for (f = softc->ipf_acct[0][softc->ipf_active]; (f != NULL); + f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = softc->ipf_acct[1][softc->ipf_active]; (f != NULL); + f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = softc->ipf_rules[0][softc->ipf_active]; (f != NULL); + f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = softc->ipf_rules[1][softc->ipf_active]; (f != NULL); + f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + RWLOCK_EXIT(&softc->ipf_mutex); + ipf_nat_sync(softc, ifp); + ipf_lookup_sync(softc, ifp); +} + + +static int +no_output(ifp, m, s, rt) + struct rtentry *rt; + struct ifnet *ifp; + struct mbuf *m; + struct sockaddr *s; +{ + return 0; +} + + +static int +write_output(ifp, m, s, rt) + struct rtentry *rt; + struct ifnet *ifp; + struct mbuf *m; + struct sockaddr *s; +{ + char fname[32]; + mb_t *mb; + ip_t *ip; + int fd; + + mb = (mb_t *)m; + ip = MTOD(mb, ip_t *); + +#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + defined(__FreeBSD__) + sprintf(fname, "/tmp/%s", ifp->if_xname); +#else + sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit); +#endif + fd = open(fname, O_WRONLY|O_APPEND); + if (fd == -1) { + perror("open"); + return -1; + } + write(fd, (char *)ip, ntohs(ip->ip_len)); + close(fd); + return 0; +} + + +static void +ipf_setifpaddr(ifp, addr) + struct ifnet *ifp; + char *addr; +{ + struct ifaddr *ifa; + +#if defined(__NetBSD__) || defined(__FreeBSD__) + if (ifp->if_addrlist.tqh_first != NULL) +#else + if (ifp->if_addrlist != NULL) +#endif + return; + + ifa = (struct ifaddr *)malloc(sizeof(*ifa)); +#if defined(__NetBSD__) || defined(__FreeBSD__) + ifp->if_addrlist.tqh_first = ifa; +#else + ifp->if_addrlist = ifa; +#endif + + if (ifa != NULL) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)&ifa->ifa_addr; +#ifdef USE_INET6 + if (index(addr, ':') != NULL) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)&ifa->ifa_addr; + sin6->sin6_family = AF_INET6; + /* Abort if bad address. */ + switch (inet_pton(AF_INET6, addr, &sin6->sin6_addr)) + { + case 1: + break; + case -1: + perror("inet_pton"); + abort(); + break; + default: + abort(); + break; + } + } else +#endif + { + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = inet_addr(addr); + if (sin->sin_addr.s_addr == 0) + abort(); + } + } +} + +struct ifnet * +get_unit(name, family) + char *name; + int family; +{ + struct ifnet *ifp, **ifpp, **old_ifneta; + char *addr; +#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + defined(__FreeBSD__) + + if (!*name) + return NULL; + + if (name == NULL) + name = "anon0"; + + addr = strchr(name, '='); + if (addr != NULL) + *addr++ = '\0'; + + for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) { + if (!strcmp(name, ifp->if_xname)) { + if (addr != NULL) + ipf_setifpaddr(ifp, addr); + return ifp; + } + } +#else + char *s, ifname[LIFNAMSIZ+1]; + + if (name == NULL) + name = "anon0"; + + addr = strchr(name, '='); + if (addr != NULL) + *addr++ = '\0'; + + for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) { + COPYIFNAME(family, ifp, ifname); + if (!strcmp(name, ifname)) { + if (addr != NULL) + ipf_setifpaddr(ifp, addr); + return ifp; + } + } +#endif + + if (!ifneta) { + ifneta = (struct ifnet **)malloc(sizeof(ifp) * 2); + if (!ifneta) + return NULL; + ifneta[1] = NULL; + ifneta[0] = (struct ifnet *)calloc(1, sizeof(*ifp)); + if (!ifneta[0]) { + free(ifneta); + return NULL; + } + nifs = 1; + } else { + old_ifneta = ifneta; + nifs++; + ifneta = (struct ifnet **)reallocarray(ifneta, nifs + 1, + sizeof(ifp)); + if (!ifneta) { + free(old_ifneta); + nifs = 0; + return NULL; + } + ifneta[nifs] = NULL; + ifneta[nifs - 1] = (struct ifnet *)malloc(sizeof(*ifp)); + if (!ifneta[nifs - 1]) { + nifs--; + return NULL; + } + } + ifp = ifneta[nifs - 1]; + +#if defined(__NetBSD__) || defined(__FreeBSD__) + TAILQ_INIT(&ifp->if_addrlist); +#endif +#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + defined(__FreeBSD__) + (void) strncpy(ifp->if_xname, name, sizeof(ifp->if_xname)); +#else + s = name + strlen(name) - 1; + for (; s > name; s--) { + if (!ISDIGIT(*s)) { + s++; + break; + } + } + + if ((s > name) && (*s != 0) && ISDIGIT(*s)) { + ifp->if_unit = atoi(s); + ifp->if_name = (char *)malloc(s - name + 1); + (void) strncpy(ifp->if_name, name, s - name); + ifp->if_name[s - name] = '\0'; + } else { + ifp->if_name = strdup(name); + ifp->if_unit = -1; + } +#endif + ifp->if_output = (void *)no_output; + + if (addr != NULL) { + ipf_setifpaddr(ifp, addr); + } + + return ifp; +} + + +char * +get_ifname(ifp) + struct ifnet *ifp; +{ + static char ifname[LIFNAMSIZ]; + +#if defined(__NetBSD__) || defined(__FreeBSD__) + sprintf(ifname, "%s", ifp->if_xname); +#else + if (ifp->if_unit != -1) + sprintf(ifname, "%s%d", ifp->if_name, ifp->if_unit); + else + strcpy(ifname, ifp->if_name); +#endif + return ifname; +} + + + +void +init_ifp() +{ + struct ifnet *ifp, **ifpp; + char fname[32]; + int fd; + +#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + defined(__FreeBSD__) + for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) { + ifp->if_output = (void *)write_output; + sprintf(fname, "/tmp/%s", ifp->if_xname); + fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); + if (fd == -1) + perror("open"); + else + close(fd); + } +#else + + for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) { + ifp->if_output = (void *)write_output; + sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit); + fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); + if (fd == -1) + perror("open"); + else + close(fd); + } +#endif +} + + +int +ipf_fastroute(m, mpp, fin, fdp) + mb_t *m, **mpp; + fr_info_t *fin; + frdest_t *fdp; +{ + struct ifnet *ifp; + ip_t *ip = fin->fin_ip; + frdest_t node; + int error = 0; + frentry_t *fr; + void *sifp; + int sout; + + sifp = fin->fin_ifp; + sout = fin->fin_out; + fr = fin->fin_fr; + ip->ip_sum = 0; + + if (!(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) && + (fdp->fd_type == FRD_DSTLIST)) { + bzero(&node, sizeof(node)); + ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node); + fdp = &node; + } + ifp = fdp->fd_ptr; + + if (ifp == NULL) + return 0; /* no routing table out here */ + + if (fin->fin_out == 0) { + fin->fin_ifp = ifp; + fin->fin_out = 1; + (void) ipf_acctpkt(fin, NULL); + fin->fin_fr = NULL; + if (!fr || !(fr->fr_flags & FR_RETMASK)) { + u_32_t pass; + + (void) ipf_state_check(fin, &pass); + } + + switch (ipf_nat_checkout(fin, NULL)) + { + case 0 : + break; + case 1 : + ip->ip_sum = 0; + break; + case -1 : + error = -1; + goto done; + break; + } + + } + + m->mb_ifp = ifp; + printpacket(fin->fin_out, m); + + (*ifp->if_output)(ifp, (void *)m, NULL, 0); +done: + fin->fin_ifp = sifp; + fin->fin_out = sout; + return error; +} + + +int +ipf_send_reset(fin) + fr_info_t *fin; +{ + ipfkverbose("- TCP RST sent\n"); + return 0; +} + + +int +ipf_send_icmp_err(type, fin, dst) + int type; + fr_info_t *fin; + int dst; +{ + ipfkverbose("- ICMP unreachable sent\n"); + return 0; +} + + +void +m_freem(m) + mb_t *m; +{ + return; +} + + +void +m_copydata(m, off, len, cp) + mb_t *m; + int off, len; + caddr_t cp; +{ + bcopy((char *)m + off, cp, len); +} + + +int +ipfuiomove(buf, len, rwflag, uio) + caddr_t buf; + int len, rwflag; + struct uio *uio; +{ + int left, ioc, num, offset; + struct iovec *io; + char *start; + + if (rwflag == UIO_READ) { + left = len; + ioc = 0; + + offset = uio->uio_offset; + + while ((left > 0) && (ioc < uio->uio_iovcnt)) { + io = uio->uio_iov + ioc; + num = io->iov_len; + if (num > left) + num = left; + start = (char *)io->iov_base + offset; + if (start > (char *)io->iov_base + io->iov_len) { + offset -= io->iov_len; + ioc++; + continue; + } + bcopy(buf, start, num); + uio->uio_resid -= num; + uio->uio_offset += num; + left -= num; + if (left > 0) + ioc++; + } + if (left > 0) + return EFAULT; + } + return 0; +} + + +u_32_t +ipf_newisn(fin) + fr_info_t *fin; +{ + static int iss_seq_off = 0; + u_char hash[16]; + u_32_t newiss; + MD5_CTX ctx; + + /* + * Compute the base value of the ISS. It is a hash + * of (saddr, sport, daddr, dport, secret). + */ + MD5Init(&ctx); + + MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src, + sizeof(fin->fin_fi.fi_src)); + MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst, + sizeof(fin->fin_fi.fi_dst)); + MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat)); + + /* MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret)); */ + + MD5Final(hash, &ctx); + + memcpy(&newiss, hash, sizeof(newiss)); + + /* + * Now increment our "timer", and add it in to + * the computed value. + * + * XXX Use `addin'? + * XXX TCP_ISSINCR too large to use? + */ + iss_seq_off += 0x00010000; + newiss += iss_seq_off; + return newiss; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nextipid */ +/* Returns: int - 0 == success, -1 == error (packet should be droppped) */ +/* Parameters: fin(I) - pointer to packet information */ +/* */ +/* Returns the next IPv4 ID to use for this packet. */ +/* ------------------------------------------------------------------------ */ +INLINE u_short +ipf_nextipid(fin) + fr_info_t *fin; +{ + static u_short ipid = 0; + ipf_main_softc_t *softc = fin->fin_main_soft; + u_short id; + + MUTEX_ENTER(&softc->ipf_rw); + if (fin->fin_pktnum != 0) { + /* + * The -1 is for aligned test results. + */ + id = (fin->fin_pktnum - 1) & 0xffff; + } else { + } + id = ipid++; + MUTEX_EXIT(&softc->ipf_rw); + + return id; +} + + +INLINE int +ipf_checkv4sum(fin) + fr_info_t *fin; +{ + + if (fin->fin_flx & FI_SHORT) + return 1; + + if (ipf_checkl4sum(fin) == -1) { + fin->fin_flx |= FI_BAD; + return -1; + } + return 0; +} + + +#ifdef USE_INET6 +INLINE int +ipf_checkv6sum(fin) + fr_info_t *fin; +{ + if (fin->fin_flx & FI_SHORT) + return 1; + + if (ipf_checkl4sum(fin) == -1) { + fin->fin_flx |= FI_BAD; + return -1; + } + return 0; +} +#endif + + +#if 0 +/* + * See above for description, except that all addressing is in user space. + */ +int +copyoutptr(softc, src, dst, size) + void *src, *dst; + size_t size; +{ + caddr_t ca; + + bcopy(dst, (char *)&ca, sizeof(ca)); + bcopy(src, ca, size); + return 0; +} + + +/* + * See above for description, except that all addressing is in user space. + */ +int +copyinptr(src, dst, size) + void *src, *dst; + size_t size; +{ + caddr_t ca; + + bcopy(src, (char *)&ca, sizeof(ca)); + bcopy(ca, dst, size); + return 0; +} +#endif + + +/* + * return the first IP Address associated with an interface + */ +int +ipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask) + ipf_main_softc_t *softc; + int v, atype; + void *ifptr; + i6addr_t *inp, *inpmask; +{ + struct ifnet *ifp = ifptr; + struct ifaddr *ifa; + +#if defined(__NetBSD__) || defined(__FreeBSD__) + ifa = ifp->if_addrlist.tqh_first; +#else + ifa = ifp->if_addrlist; +#endif + if (ifa != NULL) { + if (v == 4) { + struct sockaddr_in *sin, mask; + + mask.sin_addr.s_addr = 0xffffffff; + + sin = (struct sockaddr_in *)&ifa->ifa_addr; + + return ipf_ifpfillv4addr(atype, sin, &mask, + &inp->in4, &inpmask->in4); + } +#ifdef USE_INET6 + if (v == 6) { + struct sockaddr_in6 *sin6, mask; + + sin6 = (struct sockaddr_in6 *)&ifa->ifa_addr; + ((i6addr_t *)&mask.sin6_addr)->i6[0] = 0xffffffff; + ((i6addr_t *)&mask.sin6_addr)->i6[1] = 0xffffffff; + ((i6addr_t *)&mask.sin6_addr)->i6[2] = 0xffffffff; + ((i6addr_t *)&mask.sin6_addr)->i6[3] = 0xffffffff; + return ipf_ifpfillv6addr(atype, sin6, &mask, + inp, inpmask); + } +#endif + } + return 0; +} + + +/* + * This function is not meant to be random, rather just produce a + * sequence of numbers that isn't linear to show "randomness". + */ +u_32_t +ipf_random() +{ + static unsigned int last = 0xa5a5a5a5; + static int calls = 0; + int number; + + calls++; + + /* + * These are deliberately chosen to ensure that there is some + * attempt to test whether the output covers the range in test n18. + */ + switch (calls) + { + case 1 : + number = 0; + break; + case 2 : + number = 4; + break; + case 3 : + number = 3999; + break; + case 4 : + number = 4000; + break; + case 5 : + number = 48999; + break; + case 6 : + number = 49000; + break; + default : + number = last; + last *= calls; + last++; + number ^= last; + break; + } + return number; +} + + +int +ipf_verifysrc(fin) + fr_info_t *fin; +{ + return 1; +} + + +int +ipf_inject(fin, m) + fr_info_t *fin; + mb_t *m; +{ + FREE_MB_T(m); + + return 0; +} + + +u_int +ipf_pcksum(fin, hlen, sum) + fr_info_t *fin; + int hlen; + u_int sum; +{ + u_short *sp; + u_int sum2; + int slen; + + slen = fin->fin_plen - hlen; + sp = (u_short *)((u_char *)fin->fin_ip + hlen); + + for (; slen > 1; slen -= 2) + sum += *sp++; + if (slen) + sum += ntohs(*(u_char *)sp << 8); + while (sum > 0xffff) + sum = (sum & 0xffff) + (sum >> 16); + sum2 = (u_short)(~sum & 0xffff); + + return sum2; +} + + +void * +ipf_pullup(m, fin, plen) + mb_t *m; + fr_info_t *fin; + int plen; +{ + if (M_LEN(m) >= plen) + return fin->fin_ip; + + /* + * Fake ipf_pullup failing + */ + fin->fin_reason = FRB_PULLUP; + *fin->fin_mp = NULL; + fin->fin_m = NULL; + fin->fin_ip = NULL; + return NULL; +} diff --git a/sbin/ipf/ipftest/ipftest.1 b/sbin/ipf/ipftest/ipftest.1 new file mode 100644 index 000000000000..10232d338d9f --- /dev/null +++ b/sbin/ipf/ipftest/ipftest.1 @@ -0,0 +1,205 @@ +.\" $FreeBSD$ +.TH ipftest 1 +.SH NAME +ipftest \- test packet filter rules with arbitrary input. +.SH SYNOPSIS +.B ipftest +[ +.B \-6bCdDoRvx +] [ +.B \-F +input-format +] [ +.B \-i +<filename> +] [ +.B \-I +interface +] [ +.B \-l +<filename> +] [ +.B \-N +<filename> +] [ +.B \-P +<filename> +] [ +.B \-r +<filename> +] [ +.B \-S +<ip_address> +] [ +.B \-T +<optionlist> +] +.SH DESCRIPTION +.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 proceed +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, +\fBipnat\fP and/or \fBippool\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 +At least one of \fB\-N\fP, \fB-P\fP or \fB\-r\fP must be specified. +.SH OPTIONS +.TP +.B \-6 +Use IPv6. +.TP +.B \-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. +.TP +.B \-C +Force the checksums to be (re)calculated for all packets being input into +\fBipftest\fP. This may be necessary if pcap files from tcpdump are being +fed in where there are partial checksums present due to hardware offloading. +.TP +.B \-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). +.TP +.B \-D +Dump internal tables before exiting. +This excludes log messages. +.TP +.B \-F +This option is used to select which input format the input file is in. +The following formats are available: etherfind, hex, pcap, snoop, tcpdump,text. +.RS +.TP +.B etherfind +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 +.TP +.B hex +The input file is to be hex digits, representing the binary makeup of the +packet. No length correction is made, if an incorrect length is put in +the IP header. A packet may be broken up over several lines of hex digits, +a blank line indicating the end of the packet. It is possible to specify +both the interface name and direction of the packet (for filtering purposes) +at the start of the line using this format: [direction,interface] To define +a packet going in on le0, we would use \fB[in,le0]\fP - the []'s are required +and part of the input syntax. +.HP +.B pcap +The input file specified by \fB\-i\fP is a binary file produced using libpcap +(i.e., tcpdump version 3). Packets are read from this file as being input +(for rule purposes). An interface maybe specified using \fB\-I\fP. +.TP +.B snoop +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. +.TP +.B tcpdump +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 +.TP +.B text +The input file is in \fBipftest\fP text input format. +This is the default if no \fB\-F\fP argument is specified. +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 +.RE +.DT +.TP +.BR \-i \0<filename> +Specify the filename from which to take input. Default is stdin. +.TP +.BR \-I \0<interface> +Set the interface name (used in rule matching) to be the name supplied. +This is useful where it is +not otherwise possible to associate a packet with an interface. Normal +"text packets" can override this setting. +.TP +.BR \-l \0<filename> +Dump log messages generated during testing to the specified file. +.TP +.BR \-N \0<filename> +Specify the filename from which to read NAT rules in \fBipnat\fP(5) format. +.TP +.B \-o +Save output packets that would have been written to each interface in +a file /tmp/\fIinterface_name\fP in raw format. +.TP +.BR \-P \0<filename> +Read IP pool configuration information in \fBippool\fP(5) format from the +specified file. +.TP +.BR \-r \0<filename> +Specify the filename from which to read filter rules in \fBipf\fP(5) format. +.TP +.B \-R +Don't attempt to convert IP addresses to hostnames. +.TP +.BR \-S \0<ip_address> +The IP address specifived with this option is used by ipftest to determine +whether a packet should be treated as "input" or "output". If the source +address in an IP packet matches then it is considered to be inbound. If it +does not match then it is considered to be outbound. This is primarily +for use with tcpdump (pcap) files where there is no in/out information +saved with each packet. +.TP +.BR \-T \0<optionlist> +This option simulates the run-time changing of IPFilter kernel variables +available with the \fB\-T\fP option of \fBipf\fP. +The optionlist parameter is a comma separated list of tuning +commands. A tuning command is either "list" (retrieve a list of all variables +in the kernel, their maximum, minimum and current value), a single variable +name (retrieve its current value) and a variable name with a following +assignment to set a new value. See \fBipf\fP(8) for examples. +.TP +.B \-v +Verbose mode. This provides more information about which parts of rule +matching the input packet passes and fails. +.TP +.B \-x +Print a hex dump of each packet before printing the decoded contents. +.SH SEE ALSO +ipf(5), ipf(8), 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/sbin/ipf/ipftest/ipftest.c b/sbin/ipf/ipftest/ipftest.c new file mode 100644 index 000000000000..d7f97c1fa66b --- /dev/null +++ b/sbin/ipf/ipftest/ipftest.c @@ -0,0 +1,716 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include "ipf.h" +#include "ipt.h" +#include <sys/ioctl.h> +#include <sys/file.h> + +#if !defined(lint) +static const char sccsid[] = "@(#)ipt.c 1.19 6/3/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +extern char *optarg; +extern struct ipread pcap, iptext, iphex; +extern struct ifnet *get_unit(char *, int); +extern void init_ifp(void); +extern ipnat_t *natparse(char *, int); +extern hostmap_t **ipf_hm_maptable; +extern hostmap_t *ipf_hm_maplist; + +ipfmutex_t ipl_mutex, ipf_auth_mx, ipf_rw, ipf_stinsert; +ipfmutex_t ipf_nat_new, ipf_natio, ipf_timeoutlock; +ipfrwlock_t ipf_mutex, ipf_global, ipf_ipidfrag, ip_poolrw, ipf_frcache; +ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_authlk; +ipfrwlock_t ipf_tokens; +int opts = OPT_DONTOPEN; +int use_inet6 = 0; +int docksum = 0; +int pfil_delayed_copy = 0; +int main(int, char *[]); +int loadrules(char *, int); +int kmemcpy(char *, long, int); +int kstrncpy(char *, long, int n); +int blockreason; +void dumpnat(void *); +void dumpgroups(ipf_main_softc_t *); +void dumprules(frentry_t *); +void drain_log(char *); +void fixv4sums(mb_t *, ip_t *); + +int ipftestioctl(int, ioctlcmd_t, ...); +int ipnattestioctl(int, ioctlcmd_t, ...); +int ipstatetestioctl(int, ioctlcmd_t, ...); +int ipauthtestioctl(int, ioctlcmd_t, ...); +int ipscantestioctl(int, ioctlcmd_t, ...); +int ipsynctestioctl(int, ioctlcmd_t, ...); +int ipooltestioctl(int, ioctlcmd_t, ...); + +static ioctlfunc_t iocfunctions[IPL_LOGSIZE] = { ipftestioctl, + ipnattestioctl, + ipstatetestioctl, + ipauthtestioctl, + ipsynctestioctl, + ipscantestioctl, + ipooltestioctl, + NULL }; +static ipf_main_softc_t *softc = NULL; + + +int +main(argc,argv) + int argc; + char *argv[]; +{ + char *datain, *iface, *ifname, *logout; + int fd, i, dir, c, loaded, dump, hlen; + struct in_addr sip; + struct ifnet *ifp; + struct ipread *r; + mb_t mb, *m, *n; + ip_t *ip; + + m = &mb; + dir = 0; + dump = 0; + hlen = 0; + loaded = 0; + r = &iptext; + iface = NULL; + logout = NULL; + datain = NULL; + sip.s_addr = 0; + ifname = "anon0"; + + initparse(); + + ipf_load_all(); + + softc = ipf_create_all(NULL); + if (softc == NULL) + exit(1); + + if (ipf_init_all(softc) == -1) + exit(1); + + i = 1; + if (ipftestioctl(IPL_LOGIPF, SIOCFRENB, &i) != 0) + exit(1); + + while ((c = getopt(argc, argv, "6bCdDF:i:I:l:N:P:or:RS:T:vxX")) != -1) + switch (c) + { + case '6' : +#ifdef USE_INET6 + use_inet6 = 1; +#else + fprintf(stderr, "IPv6 not supported\n"); + exit(1); +#endif + break; + case 'b' : + opts |= OPT_BRIEF; + break; + case 'd' : + opts |= OPT_DEBUG; + break; + case 'C' : + docksum = 1; + break; + case 'D' : + dump = 1; + break; + case 'F' : + if (strcasecmp(optarg, "pcap") == 0) + r = &pcap; + else if (strcasecmp(optarg, "hex") == 0) + r = &iphex; + else if (strcasecmp(optarg, "text") == 0) + r = &iptext; + break; + case 'i' : + datain = optarg; + break; + case 'I' : + ifname = optarg; + break; + case 'l' : + logout = optarg; + break; + case 'N' : + if (ipnat_parsefile(-1, ipnat_addrule, ipnattestioctl, + optarg) == -1) + return -1; + loaded = 1; + opts |= OPT_NAT; + break; + case 'o' : + opts |= OPT_SAVEOUT; + break; + case 'P' : + if (ippool_parsefile(-1, optarg, ipooltestioctl) == -1) + return -1; + loaded = 1; + break; + case 'r' : + if (ipf_parsefile(-1, ipf_addrule, iocfunctions, + optarg) == -1) + return -1; + loaded = 1; + break; + case 'S' : + sip.s_addr = inet_addr(optarg); + break; + case 'R' : + opts |= OPT_NORESOLVE; + break; + case 'T' : + ipf_dotuning(-1, optarg, ipftestioctl); + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + case 'x' : + opts |= OPT_HEX; + break; + } + + if (loaded == 0) { + (void)fprintf(stderr,"no rules loaded\n"); + exit(-1); + } + + if (opts & OPT_SAVEOUT) + init_ifp(); + + if (datain) + fd = (*r->r_open)(datain); + else + fd = (*r->r_open)("-"); + + if (fd < 0) { + perror("error opening input"); + exit(-1); + } + + m->m_data = (char *)m->mb_buf; + while ((i = (*r->r_readip)(m, &iface, &dir)) > 0) { + + if ((iface == NULL) || (*iface == '\0')) + iface = ifname; + + ip = MTOD(m, ip_t *); + ifp = get_unit(iface, IP_V(ip)); + + if (IP_V(ip) == 4) { + if ((r->r_flags & R_DO_CKSUM) || docksum) + fixv4sums(m, ip); + hlen = IP_HL(ip) << 2; + if (sip.s_addr) + dir = !(sip.s_addr == ip->ip_src.s_addr); + } +#ifdef USE_INET6 + else + hlen = sizeof(ip6_t); +#endif + /* ipfr_slowtimer(); */ + blockreason = 0; + m = &mb; + m->mb_ifp = ifp; + m->mb_len = i; + i = ipf_check(softc, ip, hlen, ifp, dir, &m); + if ((opts & OPT_NAT) == 0) + switch (i) + { + case -4 : + (void)printf("preauth"); + break; + case -3 : + (void)printf("account"); + break; + case -2 : + (void)printf("auth"); + break; + case -1 : + (void)printf("block"); + break; + case 0 : + (void)printf("pass"); + break; + case 1 : + if (m == NULL) + (void)printf("bad-packet"); + else + (void)printf("nomatch"); + break; + case 3 : + (void)printf("block return-rst"); + break; + case 4 : + (void)printf("block return-icmp"); + break; + case 5 : + (void)printf("block return-icmp-as-dest"); + break; + default : + (void)printf("recognised return %#x\n", i); + break; + } + + if (!(opts & OPT_BRIEF)) { + putchar(' '); + if (m != NULL) + printpacket(dir, m); + else + printpacket(dir, &mb); + printf("--------------"); + } else if ((opts & (OPT_BRIEF|OPT_NAT)) == + (OPT_NAT|OPT_BRIEF)) { + if (m != NULL) + printpacket(dir, m); + else + PRINTF("%d\n", blockreason); + } + + ipf_state_flush(softc, 1, 0); + + if (dir && (ifp != NULL) && IP_V(ip) && (m != NULL)) + (*ifp->if_output)(ifp, (void *)m, NULL, 0); + + while ((m != NULL) && (m != &mb)) { + n = m->mb_next; + freembt(m); + m = n; + } + + if ((opts & (OPT_BRIEF|OPT_NAT)) != (OPT_NAT|OPT_BRIEF)) + putchar('\n'); + dir = 0; + if (iface != ifname) { + free(iface); + iface = ifname; + } + m = &mb; + m->mb_data = (char *)m->mb_buf; + } + + if (i != 0) + fprintf(stderr, "readip failed: %d\n", i); + (*r->r_close)(); + + if (logout != NULL) { + drain_log(logout); + } + + if (dump == 1) { + dumpnat(softc->ipf_nat_soft); + ipf_state_dump(softc, softc->ipf_state_soft); + ipf_lookup_dump(softc, softc->ipf_state_soft); + dumpgroups(softc); + } + + ipf_fini_all(softc); + + ipf_destroy_all(softc); + + ipf_unload_all(); + + ipf_mutex_clean(); + ipf_rwlock_clean(); + + if (getenv("FINDLEAKS")) { + fflush(stdout); + abort(); + } + return 0; +} + + +int ipftestioctl(int dev, ioctlcmd_t cmd, ...) +{ + caddr_t data; + va_list ap; + int i; + + dev = dev; /* gcc -Wextra */ + va_start(ap, cmd); + data = va_arg(ap, caddr_t); + va_end(ap); + + i = ipfioctl(softc, IPL_LOGIPF, cmd, data, FWRITE|FREAD); + if (opts & OPT_DEBUG) + fprintf(stderr, "ipfioctl(IPF,%#x,%p) = %d (%d)\n", + (u_int)cmd, data, i, softc->ipf_interror); + if (i != 0) { + errno = i; + return -1; + } + return 0; +} + + +int ipnattestioctl(int dev, ioctlcmd_t cmd, ...) +{ + caddr_t data; + va_list ap; + int i; + + dev = dev; /* gcc -Wextra */ + va_start(ap, cmd); + data = va_arg(ap, caddr_t); + va_end(ap); + + i = ipfioctl(softc, IPL_LOGNAT, cmd, data, FWRITE|FREAD); + if (opts & OPT_DEBUG) + fprintf(stderr, "ipfioctl(NAT,%#x,%p) = %d\n", + (u_int)cmd, data, i); + if (i != 0) { + errno = i; + return -1; + } + return 0; +} + + +int ipstatetestioctl(int dev, ioctlcmd_t cmd, ...) +{ + caddr_t data; + va_list ap; + int i; + + dev = dev; /* gcc -Wextra */ + va_start(ap, cmd); + data = va_arg(ap, caddr_t); + va_end(ap); + + i = ipfioctl(softc, IPL_LOGSTATE, cmd, data, FWRITE|FREAD); + if ((opts & OPT_DEBUG) || (i != 0)) + fprintf(stderr, "ipfioctl(STATE,%#x,%p) = %d\n", + (u_int)cmd, data, i); + if (i != 0) { + errno = i; + return -1; + } + return 0; +} + + +int ipauthtestioctl(int dev, ioctlcmd_t cmd, ...) +{ + caddr_t data; + va_list ap; + int i; + + dev = dev; /* gcc -Wextra */ + va_start(ap, cmd); + data = va_arg(ap, caddr_t); + va_end(ap); + + i = ipfioctl(softc, IPL_LOGAUTH, cmd, data, FWRITE|FREAD); + if ((opts & OPT_DEBUG) || (i != 0)) + fprintf(stderr, "ipfioctl(AUTH,%#x,%p) = %d\n", + (u_int)cmd, data, i); + if (i != 0) { + errno = i; + return -1; + } + return 0; +} + + +int ipscantestioctl(int dev, ioctlcmd_t cmd, ...) +{ + caddr_t data; + va_list ap; + int i; + + dev = dev; /* gcc -Wextra */ + va_start(ap, cmd); + data = va_arg(ap, caddr_t); + va_end(ap); + + i = ipfioctl(softc, IPL_LOGSCAN, cmd, data, FWRITE|FREAD); + if ((opts & OPT_DEBUG) || (i != 0)) + fprintf(stderr, "ipfioctl(SCAN,%#x,%p) = %d\n", + (u_int)cmd, data, i); + if (i != 0) { + errno = i; + return -1; + } + return 0; +} + + +int ipsynctestioctl(int dev, ioctlcmd_t cmd, ...) +{ + caddr_t data; + va_list ap; + int i; + + dev = dev; /* gcc -Wextra */ + va_start(ap, cmd); + data = va_arg(ap, caddr_t); + va_end(ap); + + i = ipfioctl(softc, IPL_LOGSYNC, cmd, data, FWRITE|FREAD); + if ((opts & OPT_DEBUG) || (i != 0)) + fprintf(stderr, "ipfioctl(SYNC,%#x,%p) = %d\n", + (u_int)cmd, data, i); + if (i != 0) { + errno = i; + return -1; + } + return 0; +} + + +int ipooltestioctl(int dev, ioctlcmd_t cmd, ...) +{ + caddr_t data; + va_list ap; + int i; + + dev = dev; /* gcc -Wextra */ + va_start(ap, cmd); + data = va_arg(ap, caddr_t); + va_end(ap); + + i = ipfioctl(softc, IPL_LOGLOOKUP, cmd, data, FWRITE|FREAD); + if ((opts & OPT_DEBUG) || (i != 0)) + fprintf(stderr, "ipfioctl(POOL,%#x,%p) = %d (%d)\n", + (u_int)cmd, data, i, softc->ipf_interror); + if (i != 0) { + errno = i; + return -1; + } + return 0; +} + + +int kmemcpy(addr, offset, size) + char *addr; + long offset; + int size; +{ + bcopy((char *)offset, addr, size); + return 0; +} + + +int kstrncpy(buf, pos, n) + char *buf; + long pos; + int n; +{ + char *ptr; + + ptr = (char *)pos; + + while ((n > 0) && (*buf++ = *ptr++)) + ; + return 0; +} + + +/* + * Display the built up NAT table rules and mapping entries. + */ +void dumpnat(arg) + void *arg; +{ + ipf_nat_softc_t *softn = arg; + hostmap_t *hm; + ipnat_t *ipn; + nat_t *nat; + + printf("List of active MAP/Redirect filters:\n"); + for (ipn = softn->ipf_nat_list; ipn != NULL; ipn = ipn->in_next) + printnat(ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); + printf("\nList of active sessions:\n"); + for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { + printactivenat(nat, opts, 0); + if (nat->nat_aps) + printf("\tproxy active\n"); + } + + printf("\nHostmap table:\n"); + for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) + printhostmap(hm, hm->hm_hv); +} + + +void dumpgroups(softc) + ipf_main_softc_t *softc; +{ + frgroup_t *fg; + int i; + + printf("List of groups configured (set 0)\n"); + for (i = 0; i < IPL_LOGSIZE; i++) + for (fg = softc->ipf_groups[i][0]; fg != NULL; + fg = fg->fg_next) { + printf("Dev.%d. Group %s Ref %d Flags %#x\n", + i, fg->fg_name, fg->fg_ref, fg->fg_flags); + dumprules(fg->fg_start); + } + + printf("List of groups configured (set 1)\n"); + for (i = 0; i < IPL_LOGSIZE; i++) + for (fg = softc->ipf_groups[i][1]; fg != NULL; + fg = fg->fg_next) { + printf("Dev.%d. Group %s Ref %d Flags %#x\n", + i, fg->fg_name, fg->fg_ref, fg->fg_flags); + dumprules(fg->fg_start); + } + + printf("Rules configured (set 0, in)\n"); + dumprules(softc->ipf_rules[0][0]); + printf("Rules configured (set 0, out)\n"); + dumprules(softc->ipf_rules[1][0]); + printf("Rules configured (set 1, in)\n"); + dumprules(softc->ipf_rules[0][1]); + printf("Rules configured (set 1, out)\n"); + dumprules(softc->ipf_rules[1][1]); + + printf("Accounting rules configured (set 0, in)\n"); + dumprules(softc->ipf_acct[0][0]); + printf("Accounting rules configured (set 0, out)\n"); + dumprules(softc->ipf_acct[0][1]); + printf("Accounting rules configured (set 1, in)\n"); + dumprules(softc->ipf_acct[1][0]); + printf("Accounting rules configured (set 1, out)\n"); + dumprules(softc->ipf_acct[1][1]); +} + +void dumprules(rulehead) + frentry_t *rulehead; +{ + frentry_t *fr; + + for (fr = rulehead; fr != NULL; fr = fr->fr_next) { +#ifdef USE_QUAD_T + printf("%"PRIu64" ",(unsigned long long)fr->fr_hits); +#else + printf("%ld ", fr->fr_hits); +#endif + printfr(fr, ipftestioctl); + } +} + + +void drain_log(filename) + char *filename; +{ + char buffer[DEFAULT_IPFLOGSIZE]; + struct iovec iov; + struct uio uio; + size_t resid; + int fd, i; + + fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0644); + if (fd == -1) { + perror("drain_log:open"); + return; + } + + for (i = 0; i <= IPL_LOGMAX; i++) + while (1) { + bzero((char *)&iov, sizeof(iov)); + iov.iov_base = buffer; + iov.iov_len = sizeof(buffer); + + bzero((char *)&uio, sizeof(uio)); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = iov.iov_len; + resid = uio.uio_resid; + + if (ipf_log_read(softc, i, &uio) == 0) { + /* + * If nothing was read then break out. + */ + if (uio.uio_resid == resid) + break; + write(fd, buffer, resid - uio.uio_resid); + } else + break; + } + + close(fd); +} + + +void fixv4sums(m, ip) + mb_t *m; + ip_t *ip; +{ + u_char *csump, *hdr, p; + fr_info_t tmp; + int len; + + p = 0; + len = 0; + bzero((char *)&tmp, sizeof(tmp)); + + csump = (u_char *)ip; + if (IP_V(ip) == 4) { + ip->ip_sum = 0; + ip->ip_sum = ipf_cksum((u_short *)ip, IP_HL(ip) << 2); + tmp.fin_hlen = IP_HL(ip) << 2; + csump += IP_HL(ip) << 2; + p = ip->ip_p; + len = ntohs(ip->ip_len); +#ifdef USE_INET6 + } else if (IP_V(ip) == 6) { + tmp.fin_hlen = sizeof(ip6_t); + csump += sizeof(ip6_t); + p = ((ip6_t *)ip)->ip6_nxt; + len = ntohs(((ip6_t *)ip)->ip6_plen); + len += sizeof(ip6_t); +#endif + } + tmp.fin_plen = len; + tmp.fin_dlen = len - tmp.fin_hlen; + + switch (p) + { + case IPPROTO_TCP : + hdr = csump; + csump += offsetof(tcphdr_t, th_sum); + break; + case IPPROTO_UDP : + hdr = csump; + csump += offsetof(udphdr_t, uh_sum); + break; + case IPPROTO_ICMP : + hdr = csump; + csump += offsetof(icmphdr_t, icmp_cksum); + break; + default : + csump = NULL; + hdr = NULL; + break; + } + if (hdr != NULL) { + tmp.fin_m = m; + tmp.fin_mp = &m; + tmp.fin_dp = hdr; + tmp.fin_ip = ip; + tmp.fin_plen = len; + *csump = 0; + *(u_short *)csump = fr_cksum(&tmp, ip, p, hdr); + } +} + +void +ip_fillid(struct ip *ip) +{ + static uint16_t ip_id; + + ip->ip_id = ip_id++; +} diff --git a/sbin/ipf/ipftest/md5.c b/sbin/ipf/ipftest/md5.c new file mode 100644 index 000000000000..899bb91700ba --- /dev/null +++ b/sbin/ipf/ipftest/md5.c @@ -0,0 +1,310 @@ +/* $FreeBSD$ */ + + + +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +# if defined(_KERNEL) +# include <sys/systm.h> +# else +# include <string.h> +# endif + +#include "md5.h" + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform(UINT4 *, UINT4 *); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#define UL(x) x##U + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void MD5Final (hash, mdContext) +unsigned char hash[]; +MD5_CTX *mdContext; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + bcopy((char *)mdContext->digest, (char *)hash, 16); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + *********************************************************************** + ** End of md5.c ** + ******************************** (cut) ******************************** + */ diff --git a/sbin/ipf/ipftest/md5.h b/sbin/ipf/ipftest/md5.h new file mode 100644 index 000000000000..6f59500daf19 --- /dev/null +++ b/sbin/ipf/ipftest/md5.h @@ -0,0 +1,64 @@ +/* $FreeBSD$ */ + +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#if !defined(__MD5_INCLUDE__) && !defined(_SYS_MD5_H) + +#ifndef __P +# define __P(x) x +#endif + +/* typedef a 32-bit type */ +typedef unsigned int UINT4; + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +extern void MD5Init(MD5_CTX *); +extern void MD5Update(MD5_CTX *, unsigned char *, unsigned int); +extern void MD5Final(unsigned char *, MD5_CTX *); + +#define __MD5_INCLUDE__ +#endif /* __MD5_INCLUDE__ */ diff --git a/sbin/ipf/iplang/BNF b/sbin/ipf/iplang/BNF new file mode 100644 index 000000000000..b5fb8d09ae2d --- /dev/null +++ b/sbin/ipf/iplang/BNF @@ -0,0 +1,69 @@ +line ::= iface | arp | send | defrouter | ipv4line . + +iface ::= ifhdr "{" ifaceopts "}" ";" . +ifhdr ::= "interface" | "iface" . +ifaceopts ::= "ifname" name | "mtu" mtu | "v4addr" ipaddr | + "eaddr" eaddr . + +send ::= "send" ";" | "send" "{" sendbodyopts "}" ";" . +sendbodyopts ::= sendbody [ sendbodyopts ] . +sendbody ::= "ifname" name | "via" ipaddr . + +defrouter ::= "router" ipaddr . + +arp ::= "arp" "{" arpbodyopts "}" ";" . +arpbodyopts ::= arpbody [ arpbodyopts ] . +arpbody ::= "v4addr" ipaddr | "eaddr" eaddr . + +bodyline ::= ipv4line | tcpline | udpline | icmpline | dataline . + +ipv4line ::= "ipv4" "{" ipv4bodyopts "}" ";" . +ipv4bodyopts ::= ipv4body [ ipv4bodyopts ] | bodyline . +ipv4body ::= "proto" protocol | "src" ipaddr | "dst" ipaddr | + "off" number | "v" number | "hl" number| "id" number | + "ttl" number | "tos" number | "sum" number | "len" number | + "opt" "{" ipv4optlist "}" ";" . +ipv4optlist ::= ipv4option [ ipv4optlist ] . +ipv4optlist = "nop" | "rr" | "zsu" | "mtup" | "mtur" | "encode" | "ts" | + "tr" | "sec" | "lsrr" | "e-sec" | "cipso" | "satid" | + "ssrr" | "addext" | "visa" | "imitd" | "eip" | "finn" | + "secclass" ipv4secclass. +ipv4secclass := "unclass" | "confid" | "reserv-1" | "reserv-2" | + "reserv-3" | "reserv-4" | "secret" | "topsecret" . + +tcpline ::= "tcp" "{" tcpbodyopts "}" ";" . +tcpbodyopts ::= tcpbody [ tcpbodyopts ] | bodyline . +tcpbody ::= "sport" port | "dport" port | "seq" number | "ack" number | + "off" number | "urp" number | "win" number | "sum" number | + "flags" tcpflags | data . + +udpline ::= "udp" "{" udpbodyopts "}" ";" . +udpbodyopts ::= udpbody [ udpbodyopts ] | bodyline . +udpbody ::= "sport" port | "dport" port | "len" number | "sum" number | + data . + +icmpline ::= "icmp" "{" icmpbodyopts "}" ";" . +icmpbodyopts ::= icmpbody [ icmpbodyopts ] | bodyline . +icmpbody ::= "type" icmptype [ "code" icmpcode ] . +icmptype ::= "echorep" | "echorep" "{" echoopts "}" ";" | "unreach" | + "unreach" "{" unreachtype "}" ";" | "squench" | "redir" | + "redir" "{" redirtype "}" ";" | "echo" "{" echoopts "}" ";" | + "echo" | "routerad" | "routersol" | "timex" | + "timex" "{" timextype "}" ";" | "paramprob" | + "paramprob" "{" parapptype "}" ";" | "timest" | "timestrep" | + "inforeq" | "inforep" | "maskreq" | "maskrep" . + +echoopts ::= echoopts [ icmpechoopts ] . +unreachtype ::= "net-unr" | "host-unr" | "proto-unr" | "port-unr" | + "needfrag" | "srcfail" | "net-unk" | "host-unk" | "isolate" | + "net-prohib" | "host-prohib" | "net-tos" | "host-tos" | + "filter-prohib" | "host-preced" | "cutoff-preced" . +redirtype ::= "net-redir" | "host-redir" | "tos-net-redir" | + "tos-host-redir" . +timextype ::= "intrans" | "reass" . +paramptype ::= "optabsent" . + +data ::= "data" "{" databodyopts "}" ";" . +databodyopts ::= "len" number | "value" string | "file" filename . + +icmpechoopts ::= "icmpseq" number | "icmpid" number . diff --git a/sbin/ipf/iplang/Makefile b/sbin/ipf/iplang/Makefile new file mode 100644 index 000000000000..5b53e9a43609 --- /dev/null +++ b/sbin/ipf/iplang/Makefile @@ -0,0 +1,31 @@ +# +# See the IPFILTER.LICENCE file for details on licencing. +# +#CC=gcc -Wuninitialized -Wstrict-prototypes -Werror -O +CFLAGS=-I.. + +all: $(DESTDIR)/iplang_y.o $(DESTDIR)/iplang_l.o + +$(DESTDIR)/iplang_y.o: $(DESTDIR)/iplang_y.c + $(CC) $(DEBUG) -I. -I.. -I$(DESTDIR) -I../ipsend $(CFLAGS) $(LINUX) -c $(DESTDIR)/iplang_y.c -o $@ + +$(DESTDIR)/iplang_l.o: $(DESTDIR)/iplang_l.c + $(CC) $(DEBUG) -I. -I.. -I$(DESTDIR) -I../ipsend $(CFLAGS) $(LINUX) -c $(DESTDIR)/iplang_l.c -o $@ + +iplang_y.o: iplang_y.c + $(CC) $(DEBUG) -I. -I.. -I../ipsend $(CFLAGS) $(LINUX) -c $< -o $@ + +iplang_l.o: iplang_l.c + $(CC) $(DEBUG) -I. -I.. -I../ipsend $(CFLAGS) $(LINUX) -c $< -o $@ + +$(DESTDIR)/iplang_l.c: iplang_l.l $(DESTDIR)/iplang_y.h + lex iplang_l.l + mv lex.yy.c $(DESTDIR)/iplang_l.c + +$(DESTDIR)/iplang_y.c $(DESTDIR)/iplang_y.h: iplang_y.y + yacc -d iplang_y.y + mv y.tab.c $(DESTDIR)/iplang_y.c + mv y.tab.h $(DESTDIR)/iplang_y.h + +clean: + /bin/rm -f *.o lex.yy.c y.tab.c y.tab.h diff --git a/sbin/ipf/iplang/iplang.h b/sbin/ipf/iplang/iplang.h new file mode 100644 index 000000000000..f38ef9671701 --- /dev/null +++ b/sbin/ipf/iplang/iplang.h @@ -0,0 +1,54 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +typedef struct iface { + int if_MTU; + char *if_name; + struct in_addr if_addr; + struct ether_addr if_eaddr; + struct iface *if_next; + int if_fd; +} iface_t; + + +typedef struct send { + struct iface *snd_if; + struct in_addr snd_gw; +} send_t; + + +typedef struct arp { + struct in_addr arp_addr; + struct ether_addr arp_eaddr; + struct arp *arp_next; +} arp_t; + + +typedef struct aniphdr { + union { + ip_t *ahu_ip; + char *ahu_data; + tcphdr_t *ahu_tcp; + udphdr_t *ahu_udp; + icmphdr_t *ahu_icmp; + } ah_un; + int ah_optlen; + int ah_lastopt; + int ah_p; + size_t ah_len; + struct aniphdr *ah_next; + struct aniphdr *ah_prev; +} aniphdr_t; + +#define ah_ip ah_un.ahu_ip +#define ah_data ah_un.ahu_data +#define ah_tcp ah_un.ahu_tcp +#define ah_udp ah_un.ahu_udp +#define ah_icmp ah_un.ahu_icmp + +extern int get_arpipv4(char *, char *); + diff --git a/sbin/ipf/iplang/iplang.tst b/sbin/ipf/iplang/iplang.tst new file mode 100644 index 000000000000..841c3aed1316 --- /dev/null +++ b/sbin/ipf/iplang/iplang.tst @@ -0,0 +1,11 @@ +# +interface { ifname le0; mtu 1500; } ; + +ipv4 { + src 1.1.1.1; dst 2.2.2.2; + tcp { + seq 12345; ack 0; sport 9999; dport 23; flags S; + data { value "abcdef"; } ; + } ; +} ; +send { via 10.1.1.1; } ; diff --git a/sbin/ipf/iplang/iplang_l.l b/sbin/ipf/iplang/iplang_l.l new file mode 100644 index 000000000000..f8b1b82d4707 --- /dev/null +++ b/sbin/ipf/iplang/iplang_l.l @@ -0,0 +1,319 @@ +/* $FreeBSD$ */ + +%{ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include <stdio.h> +#include <string.h> +#include <sys/param.h> +#if defined(__SVR4) || defined(__sysv__) +#include <sys/stream.h> +#endif +#include <sys/types.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include "iplang_y.h" +#include "ipf.h" + +#ifndef __P +# define __P(x) x +#endif + +extern int opts; + +int lineNum = 0, ipproto = 0, oldipproto = 0, next = -1, laststate = 0; +int *prstack = NULL, numpr = 0, state = 0, token = 0; + +void yyerror(char *); +void push_proto(void); +void pop_proto(void); +int next_state(int, int); +int next_item(int); +int save_token(void); +void swallow(void); +int yylex(void); + +struct lwordtab { + char *word; + int state; + int next; +}; + +struct lwordtab words[] = { + { "interface", IL_INTERFACE, -1 }, + { "iface", IL_INTERFACE, -1 }, + { "name", IL_IFNAME, IL_TOKEN }, + { "ifname", IL_IFNAME, IL_TOKEN }, + { "router", IL_DEFROUTER, IL_TOKEN }, + { "mtu", IL_MTU, IL_NUMBER }, + { "eaddr", IL_EADDR, IL_TOKEN }, + { "v4addr", IL_V4ADDR, IL_TOKEN }, + { "ipv4", IL_IPV4, -1 }, + { "v", IL_V4V, IL_TOKEN }, + { "proto", IL_V4PROTO, IL_TOKEN }, + { "hl", IL_V4HL, IL_TOKEN }, + { "id", IL_V4ID, IL_TOKEN }, + { "ttl", IL_V4TTL, IL_TOKEN }, + { "tos", IL_V4TOS, IL_TOKEN }, + { "src", IL_V4SRC, IL_TOKEN }, + { "dst", IL_V4DST, IL_TOKEN }, + { "opt", IL_OPT, -1 }, + { "len", IL_LEN, IL_TOKEN }, + { "off", IL_OFF, IL_TOKEN }, + { "sum", IL_SUM, IL_TOKEN }, + { "tcp", IL_TCP, -1 }, + { "sport", IL_SPORT, IL_TOKEN }, + { "dport", IL_DPORT, IL_TOKEN }, + { "seq", IL_TCPSEQ, IL_TOKEN }, + { "ack", IL_TCPACK, IL_TOKEN }, + { "flags", IL_TCPFL, IL_TOKEN }, + { "urp", IL_TCPURP, IL_TOKEN }, + { "win", IL_TCPWIN, IL_TOKEN }, + { "udp", IL_UDP, -1 }, + { "send", IL_SEND, -1 }, + { "via", IL_VIA, IL_TOKEN }, + { "arp", IL_ARP, -1 }, + { "data", IL_DATA, -1 }, + { "value", IL_DVALUE, IL_TOKEN }, + { "file", IL_DFILE, IL_TOKEN }, + { "nop", IL_IPO_NOP, -1 }, + { "eol", IL_IPO_EOL, -1 }, + { "rr", IL_IPO_RR, -1 }, + { "zsu", IL_IPO_ZSU, -1 }, + { "mtup", IL_IPO_MTUP, -1 }, + { "mtur", IL_IPO_MTUR, -1 }, + { "encode", IL_IPO_ENCODE, -1 }, + { "ts", IL_IPO_TS, -1 }, + { "tr", IL_IPO_TR, -1 }, + { "sec", IL_IPO_SEC, -1 }, + { "secclass", IL_IPO_SECCLASS, IL_TOKEN }, + { "lsrr", IL_IPO_LSRR, -1 }, + { "esec", IL_IPO_ESEC, -1 }, + { "cipso", IL_IPO_CIPSO, -1 }, + { "satid", IL_IPO_SATID, -1 }, + { "ssrr", IL_IPO_SSRR, -1 }, + { "addext", IL_IPO_ADDEXT, -1 }, + { "visa", IL_IPO_VISA, -1 }, + { "imitd", IL_IPO_IMITD, -1 }, + { "eip", IL_IPO_EIP, -1 }, + { "finn", IL_IPO_FINN, -1 }, + { "mss", IL_TCPO_MSS, IL_TOKEN }, + { "wscale", IL_TCPO_WSCALE, IL_TOKEN }, + { "reserv-4", IL_IPS_RESERV4, -1 }, + { "topsecret", IL_IPS_TOPSECRET, -1 }, + { "secret", IL_IPS_SECRET, -1 }, + { "reserv-3", IL_IPS_RESERV3, -1 }, + { "confid", IL_IPS_CONFID, -1 }, + { "unclass", IL_IPS_UNCLASS, -1 }, + { "reserv-2", IL_IPS_RESERV2, -1 }, + { "reserv-1", IL_IPS_RESERV1, -1 }, + { "icmp", IL_ICMP, -1 }, + { "type", IL_ICMPTYPE, -1 }, + { "code", IL_ICMPCODE, -1 }, + { "echorep", IL_ICMP_ECHOREPLY, -1 }, + { "unreach", IL_ICMP_UNREACH, -1 }, + { "squench", IL_ICMP_SOURCEQUENCH, -1 }, + { "redir", IL_ICMP_REDIRECT, -1 }, + { "echo", IL_ICMP_ECHO, -1 }, + { "routerad", IL_ICMP_ROUTERADVERT, -1 }, + { "routersol", IL_ICMP_ROUTERSOLICIT, -1 }, + { "timex", IL_ICMP_TIMXCEED, -1 }, + { "paramprob", IL_ICMP_PARAMPROB, -1 }, + { "timest", IL_ICMP_TSTAMP, -1 }, + { "timestrep", IL_ICMP_TSTAMPREPLY, -1 }, + { "inforeq", IL_ICMP_IREQ, -1 }, + { "inforep", IL_ICMP_IREQREPLY, -1 }, + { "maskreq", IL_ICMP_MASKREQ, -1 }, + { "maskrep", IL_ICMP_MASKREPLY, -1 }, + { "net-unr", IL_ICMP_UNREACH_NET, -1 }, + { "host-unr", IL_ICMP_UNREACH_HOST, -1 }, + { "proto-unr", IL_ICMP_UNREACH_PROTOCOL, -1 }, + { "port-unr", IL_ICMP_UNREACH_PORT, -1 }, + { "needfrag", IL_ICMP_UNREACH_NEEDFRAG, -1 }, + { "srcfail", IL_ICMP_UNREACH_SRCFAIL, -1 }, + { "net-unk", IL_ICMP_UNREACH_NET_UNKNOWN, -1 }, + { "host-unk", IL_ICMP_UNREACH_HOST_UNKNOWN, -1 }, + { "isolate", IL_ICMP_UNREACH_ISOLATED, -1 }, + { "net-prohib", IL_ICMP_UNREACH_NET_PROHIB, -1 }, + { "host-prohib", IL_ICMP_UNREACH_HOST_PROHIB, -1 }, + { "net-tos", IL_ICMP_UNREACH_TOSNET, -1 }, + { "host-tos", IL_ICMP_UNREACH_TOSHOST, -1 }, + { "filter-prohib", IL_ICMP_UNREACH_FILTER_PROHIB, -1 }, + { "host-preced", IL_ICMP_UNREACH_HOST_PRECEDENCE, -1 }, + { "cutoff-preced", IL_ICMP_UNREACH_PRECEDENCE_CUTOFF, -1 }, + { "net-redir", IL_ICMP_REDIRECT_NET, -1 }, + { "host-redir", IL_ICMP_REDIRECT_HOST, -1 }, + { "tos-net-redir", IL_ICMP_REDIRECT_TOSNET, -1 }, + { "tos-host-redir", IL_ICMP_REDIRECT_TOSHOST, -1 }, + { "intrans", IL_ICMP_TIMXCEED_INTRANS, -1 }, + { "reass", IL_ICMP_TIMXCEED_REASS, -1 }, + { "optabsent", IL_ICMP_PARAMPROB_OPTABSENT, -1 }, + { "otime", IL_ICMP_OTIME, -1 }, + { "rtime", IL_ICMP_RTIME, -1 }, + { "ttime", IL_ICMP_TTIME, -1 }, + { "icmpseq", IL_ICMP_SEQ, -1 }, + { "icmpid", IL_ICMP_SEQ, -1 }, + { ".", IL_DOT, -1 }, + { NULL, 0, 0 } +}; +%} +white [ \t\r]+ +%% +{white} ; +\n { lineNum++; swallow(); } +\{ { push_proto(); return next_item('{'); } +\} { pop_proto(); return next_item('}'); } +; { return next_item(';'); } +[0-9]+ { return next_item(IL_NUMBER); } +[0-9a-fA-F] { return next_item(IL_HEXDIGIT); } +: { return next_item(IL_COLON); } +#[^\n]* { return next_item(IL_COMMENT); } +[^ \{\}\n\t;:{}]* { return next_item(IL_TOKEN); } +\"[^\"]*\" { return next_item(IL_TOKEN); } +%% +void yyerror(msg) +char *msg; +{ + fprintf(stderr, "%s error at \"%s\", line %d\n", msg, yytext, + lineNum + 1); + exit(1); +} + + +void push_proto() +{ + numpr++; + if (!prstack) + prstack = (int *)malloc(sizeof(int)); + else + prstack = (int *)reallocarray((char *)prstack, numpr, + sizeof(int)); + prstack[numpr - 1] = oldipproto; +} + + +void pop_proto() +{ + numpr--; + ipproto = prstack[numpr]; + if (!numpr) { + free(prstack); + prstack = NULL; + return; + } + prstack = (int *)realloc((char *)prstack, numpr * sizeof(int)); +} + + +int save_token() +{ + + yylval.str = strdup((char *)yytext); + return IL_TOKEN; +} + + +int next_item(nstate) +int nstate; +{ + struct lwordtab *wt; + + if (opts & OPT_DEBUG) + printf("text=[%s] id=%d next=%d\n", yytext, nstate, next); + if (next == IL_TOKEN) { + next = -1; + return save_token(); + } + token++; + + for (wt = words; wt->word; wt++) + if (!strcasecmp(wt->word, (char *)yytext)) + return next_state(wt->state, wt->next); + if (opts & OPT_DEBUG) + printf("unknown keyword=[%s]\n", yytext); + next = -1; + if (nstate == IL_NUMBER) + yylval.num = atoi((char *)yytext); + token++; + return nstate; +} + + +int next_state(nstate, fornext) +int nstate, fornext; +{ + next = fornext; + + switch (nstate) + { + case IL_IPV4 : + case IL_TCP : + case IL_UDP : + case IL_ICMP : + case IL_DATA : + case IL_INTERFACE : + case IL_ARP : + oldipproto = ipproto; + ipproto = nstate; + break; + case IL_SUM : + if (ipproto == IL_IPV4) + nstate = IL_V4SUM; + else if (ipproto == IL_TCP) + nstate = IL_TCPSUM; + else if (ipproto == IL_UDP) + nstate = IL_UDPSUM; + break; + case IL_OPT : + if (ipproto == IL_IPV4) + nstate = IL_V4OPT; + else if (ipproto == IL_TCP) + nstate = IL_TCPOPT; + break; + case IL_IPO_NOP : + if (ipproto == IL_TCP) + nstate = IL_TCPO_NOP; + break; + case IL_IPO_EOL : + if (ipproto == IL_TCP) + nstate = IL_TCPO_EOL; + break; + case IL_IPO_TS : + if (ipproto == IL_TCP) + nstate = IL_TCPO_TS; + break; + case IL_OFF : + if (ipproto == IL_IPV4) + nstate = IL_V4OFF; + else if (ipproto == IL_TCP) + nstate = IL_TCPOFF; + break; + case IL_LEN : + if (ipproto == IL_IPV4) + nstate = IL_V4LEN; + else if (ipproto == IL_UDP) + nstate = IL_UDPLEN; + break; + } + return nstate; +} + + +void swallow() +{ + int c; + + c = input(); + + if (c == '#') { + while ((c != '\n') && (c != EOF)) + c = input(); + } + if (c != EOF) + unput(c); +} diff --git a/sbin/ipf/iplang/iplang_y.y b/sbin/ipf/iplang/iplang_y.y new file mode 100644 index 000000000000..484fe1951d52 --- /dev/null +++ b/sbin/ipf/iplang/iplang_y.y @@ -0,0 +1,1819 @@ +/* $FreeBSD$ */ + +%{ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Id: iplang_y.y,v 2.9.2.4 2006/03/17 12:11:29 darrenr Exp $ + * $FreeBSD$ + */ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#if !defined(__SVR4) && !defined(__svr4__) +# include <strings.h> +#else +# include <sys/byteorder.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/time.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +# include <netinet/ip_var.h> +# include <net/route.h> +# include <netinet/if_ether.h> +#include <netdb.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <resolv.h> +#include <ctype.h> +#include "ipsend.h" +#include "ip_compat.h" +#include "ipf.h" +#include "iplang.h" + +extern int opts; +extern struct ipopt_names ionames[]; +extern int state, state, lineNum, token; +extern int yylineno; +extern char yytext[]; +extern FILE *yyin; +int yylex __P((void)); +#define YYDEBUG 1 +int yydebug = 1; + +iface_t *iflist = NULL, **iftail = &iflist; +iface_t *cifp = NULL; +arp_t *arplist = NULL, **arptail = &arplist, *carp = NULL; +struct in_addr defrouter; +send_t sending; +char *sclass = NULL; +u_short c_chksum(u_short *, u_int, u_long); +u_long p_chksum(u_short *, u_int); + +u_long ipbuffer[67584/sizeof(u_long)]; /* 66K */ +aniphdr_t *aniphead = NULL, *canip = NULL, **aniptail = &aniphead; +ip_t *ip = NULL; +udphdr_t *udp = NULL; +tcphdr_t *tcp = NULL; +icmphdr_t *icmp = NULL; + +struct statetoopt { + int sto_st; + int sto_op; +}; + +struct in_addr getipv4addr(char *arg); +u_short getportnum(char *, char *); +struct ether_addr *geteaddr(char *, struct ether_addr *); +void *new_header(int); +void free_aniplist(void); +void inc_anipheaders(int); +void new_data(void); +void set_datalen(char **); +void set_datafile(char **); +void set_data(char **); +void new_packet(void); +void set_ipv4proto(char **); +void set_ipv4src(char **); +void set_ipv4dst(char **); +void set_ipv4off(char **); +void set_ipv4v(char **); +void set_ipv4hl(char **); +void set_ipv4ttl(char **); +void set_ipv4tos(char **); +void set_ipv4id(char **); +void set_ipv4sum(char **); +void set_ipv4len(char **); +void new_tcpheader(void); +void set_tcpsport(char **); +void set_tcpdport(char **); +void set_tcpseq(char **); +void set_tcpack(char **); +void set_tcpoff(char **); +void set_tcpurp(char **); +void set_tcpwin(char **); +void set_tcpsum(char **); +void set_tcpflags(char **); +void set_tcpopt(int, char **); +void end_tcpopt(void); +void new_udpheader(void); +void set_udplen(char **); +void set_udpsum(char **); +void prep_packet(void); +void packet_done(void); +void new_interface(void); +void check_interface(void); +void set_ifname(char **); +void set_ifmtu(int); +void set_ifv4addr(char **); +void set_ifeaddr(char **); +void new_arp(void); +void set_arpeaddr(char **); +void set_arpv4addr(char **); +void reset_send(void); +void set_sendif(char **); +void set_sendvia(char **); +void set_defaultrouter(char **); +void new_icmpheader(void); +void set_icmpcode(int); +void set_icmptype(int); +void set_icmpcodetok(char **); +void set_icmptypetok(char **); +void set_icmpid(int); +void set_icmpseq(int); +void set_icmpotime(int); +void set_icmprtime(int); +void set_icmpttime(int); +void set_icmpmtu(int); +void set_redir(int, char **); +void new_ipv4opt(void); +void set_icmppprob(int); +void add_ipopt(int, void *); +void end_ipopt(void); +void set_secclass(char **); +void free_anipheader(void); +void end_ipv4(void); +void end_icmp(void); +void end_udp(void); +void end_tcp(void); +void end_data(void); +void yyerror(char *); +void iplang(FILE *); +int arp_getipv4(char *, char *); +int yyparse(void); +%} +%union { + char *str; + int num; +} +%token <num> IL_NUMBER +%type <num> number digits optnumber +%token <str> IL_TOKEN +%type <str> token optoken +%token IL_HEXDIGIT IL_COLON IL_DOT IL_EOF IL_COMMENT +%token IL_INTERFACE IL_IFNAME IL_MTU IL_EADDR +%token IL_IPV4 IL_V4PROTO IL_V4SRC IL_V4DST IL_V4OFF IL_V4V IL_V4HL IL_V4TTL +%token IL_V4TOS IL_V4SUM IL_V4LEN IL_V4OPT IL_V4ID +%token IL_TCP IL_SPORT IL_DPORT IL_TCPFL IL_TCPSEQ IL_TCPACK IL_TCPOFF +%token IL_TCPWIN IL_TCPSUM IL_TCPURP IL_TCPOPT IL_TCPO_NOP IL_TCPO_EOL +%token IL_TCPO_MSS IL_TCPO_WSCALE IL_TCPO_TS +%token IL_UDP IL_UDPLEN IL_UDPSUM +%token IL_ICMP IL_ICMPTYPE IL_ICMPCODE +%token IL_SEND IL_VIA +%token IL_ARP +%token IL_DEFROUTER +%token IL_SUM IL_OFF IL_LEN IL_V4ADDR IL_OPT +%token IL_DATA IL_DLEN IL_DVALUE IL_DFILE +%token IL_IPO_NOP IL_IPO_RR IL_IPO_ZSU IL_IPO_MTUP IL_IPO_MTUR IL_IPO_EOL +%token IL_IPO_TS IL_IPO_TR IL_IPO_SEC IL_IPO_LSRR IL_IPO_ESEC +%token IL_IPO_SATID IL_IPO_SSRR IL_IPO_ADDEXT IL_IPO_VISA IL_IPO_IMITD +%token IL_IPO_EIP IL_IPO_FINN IL_IPO_SECCLASS IL_IPO_CIPSO IL_IPO_ENCODE +%token <str> IL_IPS_RESERV4 IL_IPS_TOPSECRET IL_IPS_SECRET IL_IPS_RESERV3 +%token <str> IL_IPS_CONFID IL_IPS_UNCLASS IL_IPS_RESERV2 IL_IPS_RESERV1 +%token IL_ICMP_ECHOREPLY IL_ICMP_UNREACH IL_ICMP_UNREACH_NET +%token IL_ICMP_UNREACH_HOST IL_ICMP_UNREACH_PROTOCOL IL_ICMP_UNREACH_PORT +%token IL_ICMP_UNREACH_NEEDFRAG IL_ICMP_UNREACH_SRCFAIL +%token IL_ICMP_UNREACH_NET_UNKNOWN IL_ICMP_UNREACH_HOST_UNKNOWN +%token IL_ICMP_UNREACH_ISOLATED IL_ICMP_UNREACH_NET_PROHIB +%token IL_ICMP_UNREACH_HOST_PROHIB IL_ICMP_UNREACH_TOSNET +%token IL_ICMP_UNREACH_TOSHOST IL_ICMP_UNREACH_FILTER_PROHIB +%token IL_ICMP_UNREACH_HOST_PRECEDENCE IL_ICMP_UNREACH_PRECEDENCE_CUTOFF +%token IL_ICMP_SOURCEQUENCH IL_ICMP_REDIRECT IL_ICMP_REDIRECT_NET +%token IL_ICMP_REDIRECT_HOST IL_ICMP_REDIRECT_TOSNET +%token IL_ICMP_REDIRECT_TOSHOST IL_ICMP_ECHO IL_ICMP_ROUTERADVERT +%token IL_ICMP_ROUTERSOLICIT IL_ICMP_TIMXCEED IL_ICMP_TIMXCEED_INTRANS +%token IL_ICMP_TIMXCEED_REASS IL_ICMP_PARAMPROB IL_ICMP_PARAMPROB_OPTABSENT +%token IL_ICMP_TSTAMP IL_ICMP_TSTAMPREPLY IL_ICMP_IREQ IL_ICMP_IREQREPLY +%token IL_ICMP_MASKREQ IL_ICMP_MASKREPLY IL_ICMP_SEQ IL_ICMP_ID +%token IL_ICMP_OTIME IL_ICMP_RTIME IL_ICMP_TTIME + +%% +file: line + | line file + | IL_COMMENT + | IL_COMMENT file + ; + +line: iface + | arp + | send + | defrouter + | ipline + ; + +iface: ifhdr '{' ifaceopts '}' ';' { check_interface(); } + ; + +ifhdr: IL_INTERFACE { new_interface(); } + ; + +ifaceopts: + ifaceopt + | ifaceopt ifaceopts + ; + +ifaceopt: + IL_IFNAME token { set_ifname(&$2); } + | IL_MTU number { set_ifmtu($2); } + | IL_V4ADDR token { set_ifv4addr(&$2); } + | IL_EADDR token { set_ifeaddr(&$2); } + ; + +send: sendhdr '{' sendbody '}' ';' { packet_done(); } + | sendhdr ';' { packet_done(); } + ; + +sendhdr: + IL_SEND { reset_send(); } + ; + +sendbody: + sendopt + | sendbody sendopt + ; + +sendopt: + IL_IFNAME token { set_sendif(&$2); } + | IL_VIA token { set_sendvia(&$2); } + ; + +arp: arphdr '{' arpbody '}' ';' + ; + +arphdr: IL_ARP { new_arp(); } + ; + +arpbody: + arpopt + | arpbody arpopt + ; + +arpopt: IL_V4ADDR token { set_arpv4addr(&$2); } + | IL_EADDR token { set_arpeaddr(&$2); } + ; + +defrouter: + IL_DEFROUTER token { set_defaultrouter(&$2); } + ; + +bodyline: + ipline + | tcp tcpline + | udp udpline + | icmp icmpline + | data dataline + ; + +ipline: ipv4 '{' ipv4body '}' ';' { end_ipv4(); } + ; + +ipv4: IL_IPV4 { new_packet(); } + +ipv4body: + ipv4type + | ipv4type ipv4body + | bodyline + ; + +ipv4type: + IL_V4PROTO token { set_ipv4proto(&$2); } + | IL_V4SRC token { set_ipv4src(&$2); } + | IL_V4DST token { set_ipv4dst(&$2); } + | IL_V4OFF token { set_ipv4off(&$2); } + | IL_V4V token { set_ipv4v(&$2); } + | IL_V4HL token { set_ipv4hl(&$2); } + | IL_V4ID token { set_ipv4id(&$2); } + | IL_V4TTL token { set_ipv4ttl(&$2); } + | IL_V4TOS token { set_ipv4tos(&$2); } + | IL_V4SUM token { set_ipv4sum(&$2); } + | IL_V4LEN token { set_ipv4len(&$2); } + | ipv4opt '{' ipv4optlist '}' ';' { end_ipopt(); } + ; + +tcp: IL_TCP { new_tcpheader(); } + ; + +tcpline: + '{' tcpheader '}' ';' { end_tcp(); } + ; + +tcpheader: + tcpbody + | tcpbody tcpheader + | bodyline + ; + +tcpbody: + IL_SPORT token { set_tcpsport(&$2); } + | IL_DPORT token { set_tcpdport(&$2); } + | IL_TCPSEQ token { set_tcpseq(&$2); } + | IL_TCPACK token { set_tcpack(&$2); } + | IL_TCPOFF token { set_tcpoff(&$2); } + | IL_TCPURP token { set_tcpurp(&$2); } + | IL_TCPWIN token { set_tcpwin(&$2); } + | IL_TCPSUM token { set_tcpsum(&$2); } + | IL_TCPFL token { set_tcpflags(&$2); } + | IL_TCPOPT '{' tcpopts '}' ';' { end_tcpopt(); } + ; + +tcpopts: + | tcpopt tcpopts + ; + +tcpopt: IL_TCPO_NOP ';' { set_tcpopt(IL_TCPO_NOP, NULL); } + | IL_TCPO_EOL ';' { set_tcpopt(IL_TCPO_EOL, NULL); } + | IL_TCPO_MSS optoken { set_tcpopt(IL_TCPO_MSS,&$2);} + | IL_TCPO_WSCALE optoken { set_tcpopt(IL_TCPO_WSCALE,&$2);} + | IL_TCPO_TS optoken { set_tcpopt(IL_TCPO_TS, &$2);} + ; + +udp: IL_UDP { new_udpheader(); } + ; + +udpline: + '{' udpheader '}' ';' { end_udp(); } + ; + + +udpheader: + udpbody + | udpbody udpheader + | bodyline + ; + +udpbody: + IL_SPORT token { set_tcpsport(&$2); } + | IL_DPORT token { set_tcpdport(&$2); } + | IL_UDPLEN token { set_udplen(&$2); } + | IL_UDPSUM token { set_udpsum(&$2); } + ; + +icmp: IL_ICMP { new_icmpheader(); } + ; + +icmpline: + '{' icmpbody '}' ';' { end_icmp(); } + ; + +icmpbody: + icmpheader + | icmpheader bodyline + ; + +icmpheader: + IL_ICMPTYPE icmptype + | IL_ICMPTYPE icmptype icmpcode + ; + +icmpcode: + IL_ICMPCODE token { set_icmpcodetok(&$2); } + ; + +icmptype: + IL_ICMP_ECHOREPLY ';' { set_icmptype(ICMP_ECHOREPLY); } + | IL_ICMP_ECHOREPLY '{' icmpechoopts '}' ';' + | unreach + | IL_ICMP_SOURCEQUENCH ';' { set_icmptype(ICMP_SOURCEQUENCH); } + | redirect + | IL_ICMP_ROUTERADVERT ';' { set_icmptype(ICMP_ROUTERADVERT); } + | IL_ICMP_ROUTERSOLICIT ';' { set_icmptype(ICMP_ROUTERSOLICIT); } + | IL_ICMP_ECHO ';' { set_icmptype(ICMP_ECHO); } + | IL_ICMP_ECHO '{' icmpechoopts '}' ';' + | IL_ICMP_TIMXCEED ';' { set_icmptype(ICMP_TIMXCEED); } + | IL_ICMP_TIMXCEED '{' exceed '}' ';' + | IL_ICMP_TSTAMP ';' { set_icmptype(ICMP_TSTAMP); } + | IL_ICMP_TSTAMPREPLY ';' { set_icmptype(ICMP_TSTAMPREPLY); } + | IL_ICMP_TSTAMPREPLY '{' icmptsopts '}' ';' + | IL_ICMP_IREQ ';' { set_icmptype(ICMP_IREQ); } + | IL_ICMP_IREQREPLY ';' { set_icmptype(ICMP_IREQREPLY); } + | IL_ICMP_IREQREPLY '{' data dataline '}' ';' + | IL_ICMP_MASKREQ ';' { set_icmptype(ICMP_MASKREQ); } + | IL_ICMP_MASKREPLY ';' { set_icmptype(ICMP_MASKREPLY); } + | IL_ICMP_MASKREPLY '{' token '}' ';' + | IL_ICMP_PARAMPROB ';' { set_icmptype(ICMP_PARAMPROB); } + | IL_ICMP_PARAMPROB '{' paramprob '}' ';' + | IL_TOKEN ';' { set_icmptypetok(&$1); } + ; + +icmpechoopts: + | icmpechoopts icmpecho + ; + +icmpecho: + IL_ICMP_SEQ number { set_icmpseq($2); } + | IL_ICMP_ID number { set_icmpid($2); } + ; + +icmptsopts: + | icmptsopts icmpts ';' + ; + +icmpts: IL_ICMP_OTIME number { set_icmpotime($2); } + | IL_ICMP_RTIME number { set_icmprtime($2); } + | IL_ICMP_TTIME number { set_icmpttime($2); } + ; + +unreach: + IL_ICMP_UNREACH + | IL_ICMP_UNREACH '{' unreachopts '}' ';' + ; + +unreachopts: + IL_ICMP_UNREACH_NET line + | IL_ICMP_UNREACH_HOST line + | IL_ICMP_UNREACH_PROTOCOL line + | IL_ICMP_UNREACH_PORT line + | IL_ICMP_UNREACH_NEEDFRAG number ';' { set_icmpmtu($2); } + | IL_ICMP_UNREACH_SRCFAIL line + | IL_ICMP_UNREACH_NET_UNKNOWN line + | IL_ICMP_UNREACH_HOST_UNKNOWN line + | IL_ICMP_UNREACH_ISOLATED line + | IL_ICMP_UNREACH_NET_PROHIB line + | IL_ICMP_UNREACH_HOST_PROHIB line + | IL_ICMP_UNREACH_TOSNET line + | IL_ICMP_UNREACH_TOSHOST line + | IL_ICMP_UNREACH_FILTER_PROHIB line + | IL_ICMP_UNREACH_HOST_PRECEDENCE line + | IL_ICMP_UNREACH_PRECEDENCE_CUTOFF line + ; + +redirect: + IL_ICMP_REDIRECT + | IL_ICMP_REDIRECT '{' redirectopts '}' ';' + ; + +redirectopts: + | IL_ICMP_REDIRECT_NET token { set_redir(0, &$2); } + | IL_ICMP_REDIRECT_HOST token { set_redir(1, &$2); } + | IL_ICMP_REDIRECT_TOSNET token { set_redir(2, &$2); } + | IL_ICMP_REDIRECT_TOSHOST token { set_redir(3, &$2); } + ; + +exceed: + IL_ICMP_TIMXCEED_INTRANS line + | IL_ICMP_TIMXCEED_REASS line + ; + +paramprob: + IL_ICMP_PARAMPROB_OPTABSENT + | IL_ICMP_PARAMPROB_OPTABSENT paraprobarg + +paraprobarg: + '{' number '}' ';' { set_icmppprob($2); } + ; + +ipv4opt: IL_V4OPT { new_ipv4opt(); } + ; + +ipv4optlist: + | ipv4opts ipv4optlist + ; + +ipv4opts: + IL_IPO_NOP ';' { add_ipopt(IL_IPO_NOP, NULL); } + | IL_IPO_RR optnumber { add_ipopt(IL_IPO_RR, &$2); } + | IL_IPO_ZSU ';' { add_ipopt(IL_IPO_ZSU, NULL); } + | IL_IPO_MTUP ';' { add_ipopt(IL_IPO_MTUP, NULL); } + | IL_IPO_MTUR ';' { add_ipopt(IL_IPO_MTUR, NULL); } + | IL_IPO_ENCODE ';' { add_ipopt(IL_IPO_ENCODE, NULL); } + | IL_IPO_TS ';' { add_ipopt(IL_IPO_TS, NULL); } + | IL_IPO_TR ';' { add_ipopt(IL_IPO_TR, NULL); } + | IL_IPO_SEC ';' { add_ipopt(IL_IPO_SEC, NULL); } + | IL_IPO_SECCLASS secclass { add_ipopt(IL_IPO_SECCLASS, sclass); } + | IL_IPO_LSRR token { add_ipopt(IL_IPO_LSRR,&$2); } + | IL_IPO_ESEC ';' { add_ipopt(IL_IPO_ESEC, NULL); } + | IL_IPO_CIPSO ';' { add_ipopt(IL_IPO_CIPSO, NULL); } + | IL_IPO_SATID optnumber { add_ipopt(IL_IPO_SATID,&$2);} + | IL_IPO_SSRR token { add_ipopt(IL_IPO_SSRR,&$2); } + | IL_IPO_ADDEXT ';' { add_ipopt(IL_IPO_ADDEXT, NULL); } + | IL_IPO_VISA ';' { add_ipopt(IL_IPO_VISA, NULL); } + | IL_IPO_IMITD ';' { add_ipopt(IL_IPO_IMITD, NULL); } + | IL_IPO_EIP ';' { add_ipopt(IL_IPO_EIP, NULL); } + | IL_IPO_FINN ';' { add_ipopt(IL_IPO_FINN, NULL); } + ; + +secclass: + IL_IPS_RESERV4 ';' { set_secclass(&$1); } + | IL_IPS_TOPSECRET ';' { set_secclass(&$1); } + | IL_IPS_SECRET ';' { set_secclass(&$1); } + | IL_IPS_RESERV3 ';' { set_secclass(&$1); } + | IL_IPS_CONFID ';' { set_secclass(&$1); } + | IL_IPS_UNCLASS ';' { set_secclass(&$1); } + | IL_IPS_RESERV2 ';' { set_secclass(&$1); } + | IL_IPS_RESERV1 ';' { set_secclass(&$1); } + ; + +data: IL_DATA { new_data(); } + ; + +dataline: + '{' databody '}' ';' { end_data(); } + ; + +databody: dataopts + | dataopts databody + ; + +dataopts: + IL_DLEN token { set_datalen(&$2); } + | IL_DVALUE token { set_data(&$2); } + | IL_DFILE token { set_datafile(&$2); } + ; + +token: IL_TOKEN ';' + ; + +optoken: ';' { $$ = ""; } + | token + ; + +number: digits ';' + ; + +optnumber: ';' { $$ = 0; } + | number + ; + +digits: IL_NUMBER + | digits IL_NUMBER + ; +%% + +struct statetoopt toipopts[] = { + { IL_IPO_NOP, IPOPT_NOP }, + { IL_IPO_RR, IPOPT_RR }, + { IL_IPO_ZSU, IPOPT_ZSU }, + { IL_IPO_MTUP, IPOPT_MTUP }, + { IL_IPO_MTUR, IPOPT_MTUR }, + { IL_IPO_ENCODE, IPOPT_ENCODE }, + { IL_IPO_TS, IPOPT_TS }, + { IL_IPO_TR, IPOPT_TR }, + { IL_IPO_SEC, IPOPT_SECURITY }, + { IL_IPO_SECCLASS, IPOPT_SECURITY }, + { IL_IPO_LSRR, IPOPT_LSRR }, + { IL_IPO_ESEC, IPOPT_E_SEC }, + { IL_IPO_CIPSO, IPOPT_CIPSO }, + { IL_IPO_SATID, IPOPT_SATID }, + { IL_IPO_SSRR, IPOPT_SSRR }, + { IL_IPO_ADDEXT, IPOPT_ADDEXT }, + { IL_IPO_VISA, IPOPT_VISA }, + { IL_IPO_IMITD, IPOPT_IMITD }, + { IL_IPO_EIP, IPOPT_EIP }, + { IL_IPO_FINN, IPOPT_FINN }, + { 0, 0 } +}; + +struct statetoopt tosecopts[] = { + { IL_IPS_RESERV4, IPSO_CLASS_RES4 }, + { IL_IPS_TOPSECRET, IPSO_CLASS_TOPS }, + { IL_IPS_SECRET, IPSO_CLASS_SECR }, + { IL_IPS_RESERV3, IPSO_CLASS_RES3 }, + { IL_IPS_CONFID, IPSO_CLASS_CONF }, + { IL_IPS_UNCLASS, IPSO_CLASS_UNCL }, + { IL_IPS_RESERV2, IPSO_CLASS_RES2 }, + { IL_IPS_RESERV1, IPSO_CLASS_RES1 }, + { 0, 0 } +}; + + +struct in_addr getipv4addr(arg) +char *arg; +{ + struct hostent *hp; + struct in_addr in; + + in.s_addr = 0xffffffff; + + if ((hp = gethostbyname(arg))) + bcopy(hp->h_addr, &in.s_addr, sizeof(struct in_addr)); + else + in.s_addr = inet_addr(arg); + return in; +} + + +u_short getportnum(pr, name) +char *pr, *name; +{ + struct servent *sp; + + if (!(sp = getservbyname(name, pr))) + return htons(atoi(name)); + return sp->s_port; +} + + +struct ether_addr *geteaddr(arg, buf) +char *arg; +struct ether_addr *buf; +{ + struct ether_addr *e; + + e = ether_aton(arg); + if (!e) + fprintf(stderr, "Invalid ethernet address: %s\n", arg); + else +# ifdef __FreeBSD__ + bcopy(e->octet, buf->octet, sizeof(e->octet)); +# else + bcopy(e->ether_addr_octet, buf->ether_addr_octet, + sizeof(e->ether_addr_octet)); +# endif + return e; +} + + +void *new_header(type) +int type; +{ + aniphdr_t *aip, *oip = canip; + int sz = 0; + + aip = (aniphdr_t *)calloc(1, sizeof(*aip)); + *aniptail = aip; + aniptail = &aip->ah_next; + aip->ah_p = type; + aip->ah_prev = oip; + canip = aip; + + if (type == IPPROTO_UDP) + sz = sizeof(udphdr_t); + else if (type == IPPROTO_TCP) + sz = sizeof(tcphdr_t); + else if (type == IPPROTO_ICMP) + sz = sizeof(icmphdr_t); + else if (type == IPPROTO_IP) + sz = sizeof(ip_t); + + if (oip) + canip->ah_data = oip->ah_data + oip->ah_len; + else + canip->ah_data = (char *)ipbuffer; + + /* + * Increase the size fields in all wrapping headers. + */ + for (aip = aniphead; aip; aip = aip->ah_next) { + aip->ah_len += sz; + if (aip->ah_p == IPPROTO_IP) + aip->ah_ip->ip_len += sz; + else if (aip->ah_p == IPPROTO_UDP) + aip->ah_udp->uh_ulen += sz; + } + return (void *)canip->ah_data; +} + + +void free_aniplist() +{ + aniphdr_t *aip, **aipp = &aniphead; + + while ((aip = *aipp)) { + *aipp = aip->ah_next; + free(aip); + } + aniptail = &aniphead; +} + + +void inc_anipheaders(inc) +int inc; +{ + aniphdr_t *aip; + + for (aip = aniphead; aip; aip = aip->ah_next) { + aip->ah_len += inc; + if (aip->ah_p == IPPROTO_IP) + aip->ah_ip->ip_len += inc; + else if (aip->ah_p == IPPROTO_UDP) + aip->ah_udp->uh_ulen += inc; + } +} + + +void new_data() +{ + (void) new_header(-1); + canip->ah_len = 0; +} + + +void set_datalen(arg) +char **arg; +{ + int len; + + len = strtol(*arg, NULL, 0); + inc_anipheaders(len); + free(*arg); + *arg = NULL; +} + + +void set_data(arg) +char **arg; +{ + u_char *s = (u_char *)*arg, *t = (u_char *)canip->ah_data, c; + int len = 0, todo = 0, quote = 0, val = 0; + + while ((c = *s++)) { + if (todo) { + if (ISDIGIT(c)) { + todo--; + if (c > '7') { + fprintf(stderr, "octal with %c!\n", c); + break; + } + val <<= 3; + val |= (c - '0'); + } + if (!ISDIGIT(c) || !todo) { + *t++ = (u_char)(val & 0xff); + todo = 0; + } + if (todo) + continue; + } + if (quote) { + if (ISDIGIT(c)) { + todo = 2; + if (c > '7') { + fprintf(stderr, "octal with %c!\n", c); + break; + } + val = (c - '0'); + } else { + switch (c) + { + case '\"' : + *t++ = '\"'; + break; + case '\\' : + *t++ = '\\'; + break; + case 'n' : + *t++ = '\n'; + break; + case 'r' : + *t++ = '\r'; + break; + case 't' : + *t++ = '\t'; + break; + } + } + quote = 0; + continue; + } + + if (c == '\\') + quote = 1; + else + *t++ = c; + } + if (todo) + *t++ = (u_char)(val & 0xff); + if (quote) + *t++ = '\\'; + len = t - (u_char *)canip->ah_data; + inc_anipheaders(len - canip->ah_len); + canip->ah_len = len; +} + + +void set_datafile(arg) +char **arg; +{ + struct stat sb; + char *file = *arg; + int fd, len; + + if ((fd = open(file, O_RDONLY)) == -1) { + perror("open"); + exit(-1); + } + + if (fstat(fd, &sb) == -1) { + perror("fstat"); + exit(-1); + } + + if ((sb.st_size + aniphead->ah_len ) > 65535) { + fprintf(stderr, "data file %s too big to include.\n", file); + close(fd); + return; + } + if ((len = read(fd, canip->ah_data, sb.st_size)) == -1) { + perror("read"); + close(fd); + return; + } + inc_anipheaders(len); + canip->ah_len += len; + close(fd); +} + + +void new_packet() +{ + static u_short id = 0; + + if (!aniphead) + bzero((char *)ipbuffer, sizeof(ipbuffer)); + + ip = (ip_t *)new_header(IPPROTO_IP); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(ip_t) >> 2; + ip->ip_len = sizeof(ip_t); + ip->ip_ttl = 63; + ip->ip_id = htons(id++); +} + + +void set_ipv4proto(arg) +char **arg; +{ + struct protoent *pr; + + if ((pr = getprotobyname(*arg))) + ip->ip_p = pr->p_proto; + else + if (!(ip->ip_p = atoi(*arg))) + fprintf(stderr, "unknown protocol %s\n", *arg); + free(*arg); + *arg = NULL; +} + + +void set_ipv4src(arg) +char **arg; +{ + ip->ip_src = getipv4addr(*arg); + free(*arg); + *arg = NULL; +} + + +void set_ipv4dst(arg) +char **arg; +{ + ip->ip_dst = getipv4addr(*arg); + free(*arg); + *arg = NULL; +} + + +void set_ipv4off(arg) +char **arg; +{ + ip->ip_off = htons(strtol(*arg, NULL, 0)); + free(*arg); + *arg = NULL; +} + + +void set_ipv4v(arg) +char **arg; +{ + ip->ip_v = strtol(*arg, NULL, 0); + free(*arg); + *arg = NULL; +} + + +void set_ipv4hl(arg) +char **arg; +{ + int newhl, inc; + + newhl = strtol(*arg, NULL, 0); + inc = (newhl - ip->ip_hl) << 2; + ip->ip_len += inc; + ip->ip_hl = newhl; + canip->ah_len += inc; + free(*arg); + *arg = NULL; +} + + +void set_ipv4ttl(arg) +char **arg; +{ + ip->ip_ttl = strtol(*arg, NULL, 0); + free(*arg); + *arg = NULL; +} + + +void set_ipv4tos(arg) +char **arg; +{ + ip->ip_tos = strtol(*arg, NULL, 0); + free(*arg); + *arg = NULL; +} + + +void set_ipv4id(arg) +char **arg; +{ + ip->ip_id = htons(strtol(*arg, NULL, 0)); + free(*arg); + *arg = NULL; +} + + +void set_ipv4sum(arg) +char **arg; +{ + ip->ip_sum = strtol(*arg, NULL, 0); + free(*arg); + *arg = NULL; +} + + +void set_ipv4len(arg) +char **arg; +{ + int len; + + len = strtol(*arg, NULL, 0); + inc_anipheaders(len - ip->ip_len); + ip->ip_len = len; + free(*arg); + *arg = NULL; +} + + +void new_tcpheader() +{ + + if ((ip->ip_p) && (ip->ip_p != IPPROTO_TCP)) { + fprintf(stderr, "protocol %d specified with TCP!\n", ip->ip_p); + return; + } + ip->ip_p = IPPROTO_TCP; + + tcp = (tcphdr_t *)new_header(IPPROTO_TCP); + tcp->th_win = htons(4096); + tcp->th_off = sizeof(*tcp) >> 2; +} + + +void set_tcpsport(arg) +char **arg; +{ + u_short *port; + char *pr; + + if (ip->ip_p == IPPROTO_UDP) { + port = &udp->uh_sport; + pr = "udp"; + } else { + port = &tcp->th_sport; + pr = "udp"; + } + + *port = getportnum(pr, *arg); + free(*arg); + *arg = NULL; +} + + +void set_tcpdport(arg) +char **arg; +{ + u_short *port; + char *pr; + + if (ip->ip_p == IPPROTO_UDP) { + port = &udp->uh_dport; + pr = "udp"; + } else { + port = &tcp->th_dport; + pr = "udp"; + } + + *port = getportnum(pr, *arg); + free(*arg); + *arg = NULL; +} + + +void set_tcpseq(arg) +char **arg; +{ + tcp->th_seq = htonl(strtol(*arg, NULL, 0)); + free(*arg); + *arg = NULL; +} + + +void set_tcpack(arg) +char **arg; +{ + tcp->th_ack = htonl(strtol(*arg, NULL, 0)); + free(*arg); + *arg = NULL; +} + + +void set_tcpoff(arg) +char **arg; +{ + int off; + + off = strtol(*arg, NULL, 0); + inc_anipheaders((off - tcp->th_off) << 2); + tcp->th_off = off; + free(*arg); + *arg = NULL; +} + + +void set_tcpurp(arg) +char **arg; +{ + tcp->th_urp = htons(strtol(*arg, NULL, 0)); + free(*arg); + *arg = NULL; +} + + +void set_tcpwin(arg) +char **arg; +{ + tcp->th_win = htons(strtol(*arg, NULL, 0)); + free(*arg); + *arg = NULL; +} + + +void set_tcpsum(arg) +char **arg; +{ + tcp->th_sum = strtol(*arg, NULL, 0); + free(*arg); + *arg = NULL; +} + + +void set_tcpflags(arg) +char **arg; +{ + static char flags[] = "ASURPF"; + static int flagv[] = { TH_ACK, TH_SYN, TH_URG, TH_RST, TH_PUSH, + TH_FIN } ; + char *s, *t; + + for (s = *arg; *s; s++) + if (!(t = strchr(flags, *s))) { + if (s - *arg) { + fprintf(stderr, "unknown TCP flag %c\n", *s); + break; + } + tcp->th_flags = strtol(*arg, NULL, 0); + break; + } else + tcp->th_flags |= flagv[t - flags]; + free(*arg); + *arg = NULL; +} + + +void set_tcpopt(state, arg) +int state; +char **arg; +{ + u_char *s; + int val, len, val2, pad, optval; + + if (arg && *arg) + val = atoi(*arg); + else + val = 0; + + s = (u_char *)tcp + sizeof(*tcp) + canip->ah_optlen; + switch (state) + { + case IL_TCPO_EOL : + optval = 0; + len = 1; + break; + case IL_TCPO_NOP : + optval = 1; + len = 1; + break; + case IL_TCPO_MSS : + optval = 2; + len = 4; + break; + case IL_TCPO_WSCALE : + optval = 3; + len = 3; + break; + case IL_TCPO_TS : + optval = 8; + len = 10; + break; + default : + optval = 0; + len = 0; + break; + } + + if (len > 1) { + /* + * prepend padding - if required. + */ + if (len & 3) + for (pad = 4 - (len & 3); pad; pad--) { + *s++ = 1; + canip->ah_optlen++; + } + /* + * build tcp option + */ + *s++ = (u_char)optval; + *s++ = (u_char)len; + if (len > 2) { + if (len == 3) { /* 1 byte - char */ + *s++ = (u_char)val; + } else if (len == 4) { /* 2 bytes - short */ + *s++ = (u_char)((val >> 8) & 0xff); + *s++ = (u_char)(val & 0xff); + } else if (len >= 6) { /* 4 bytes - long */ + val2 = htonl(val); + bcopy((char *)&val2, s, 4); + } + s += (len - 2); + } + } else + *s++ = (u_char)optval; + + canip->ah_lastopt = optval; + canip->ah_optlen += len; + + if (arg && *arg) { + free(*arg); + *arg = NULL; + } +} + + +void end_tcpopt() +{ + int pad; + char *s = (char *)tcp; + + s += sizeof(*tcp) + canip->ah_optlen; + /* + * pad out so that we have a multiple of 4 bytes in size fo the + * options. make sure last byte is EOL. + */ + if (canip->ah_optlen & 3) { + if (canip->ah_lastopt != 1) { + for (pad = 3 - (canip->ah_optlen & 3); pad; pad--) { + *s++ = 1; + canip->ah_optlen++; + } + canip->ah_optlen++; + } else { + s -= 1; + + for (pad = 3 - (canip->ah_optlen & 3); pad; pad--) { + *s++ = 1; + canip->ah_optlen++; + } + } + *s++ = 0; + } + tcp->th_off = (sizeof(*tcp) + canip->ah_optlen) >> 2; + inc_anipheaders(canip->ah_optlen); +} + + +void new_udpheader() +{ + if ((ip->ip_p) && (ip->ip_p != IPPROTO_UDP)) { + fprintf(stderr, "protocol %d specified with UDP!\n", ip->ip_p); + return; + } + ip->ip_p = IPPROTO_UDP; + + udp = (udphdr_t *)new_header(IPPROTO_UDP); + udp->uh_ulen = sizeof(*udp); +} + + +void set_udplen(arg) +char **arg; +{ + int len; + + len = strtol(*arg, NULL, 0); + inc_anipheaders(len - udp->uh_ulen); + udp->uh_ulen = len; + free(*arg); + *arg = NULL; +} + + +void set_udpsum(arg) +char **arg; +{ + udp->uh_sum = strtol(*arg, NULL, 0); + free(*arg); + *arg = NULL; +} + + +void prep_packet() +{ + iface_t *ifp; + struct in_addr gwip; + + ifp = sending.snd_if; + if (!ifp) { + fprintf(stderr, "no interface defined for sending!\n"); + return; + } + if (ifp->if_fd == -1) + ifp->if_fd = initdevice(ifp->if_name, 5); + gwip = sending.snd_gw; + if (!gwip.s_addr) { + if (aniphead == NULL) { + fprintf(stderr, + "no destination address defined for sending\n"); + return; + } + gwip = aniphead->ah_ip->ip_dst; + } + (void) send_ip(ifp->if_fd, ifp->if_MTU, (ip_t *)ipbuffer, gwip, 2); +} + + +void packet_done() +{ + char outline[80]; + int i, j, k; + u_char *s = (u_char *)ipbuffer, *t = (u_char *)outline; + + if (opts & OPT_VERBOSE) { + ip->ip_len = htons(ip->ip_len); + for (i = ntohs(ip->ip_len), j = 0; i; i--, j++, s++) { + if (j && !(j & 0xf)) { + *t++ = '\n'; + *t = '\0'; + fputs(outline, stdout); + fflush(stdout); + t = (u_char *)outline; + *t = '\0'; + } + sprintf((char *)t, "%02x", *s & 0xff); + t += 2; + if (!((j + 1) & 0xf)) { + s -= 15; + sprintf((char *)t, " "); + t += 8; + for (k = 16; k; k--, s++) + *t++ = (isprint(*s) ? *s : '.'); + s--; + } + + if ((j + 1) & 0xf) + *t++ = ' ';; + } + + if (j & 0xf) { + for (k = 16 - (j & 0xf); k; k--) { + *t++ = ' '; + *t++ = ' '; + *t++ = ' '; + } + sprintf((char *)t, " "); + t += 7; + s -= j & 0xf; + for (k = j & 0xf; k; k--, s++) + *t++ = (isprint(*s) ? *s : '.'); + *t++ = '\n'; + *t = '\0'; + } + fputs(outline, stdout); + fflush(stdout); + ip->ip_len = ntohs(ip->ip_len); + } + + prep_packet(); + free_aniplist(); +} + + +void new_interface() +{ + cifp = (iface_t *)calloc(1, sizeof(iface_t)); + *iftail = cifp; + iftail = &cifp->if_next; + cifp->if_fd = -1; +} + + +void check_interface() +{ + if (!cifp->if_name || !*cifp->if_name) + fprintf(stderr, "No interface name given!\n"); + if (!cifp->if_MTU || !*cifp->if_name) + fprintf(stderr, "Interface %s has an MTU of 0!\n", + cifp->if_name); +} + + +void set_ifname(arg) +char **arg; +{ + cifp->if_name = *arg; + *arg = NULL; +} + + +void set_ifmtu(arg) +int arg; +{ + cifp->if_MTU = arg; +} + + +void set_ifv4addr(arg) +char **arg; +{ + cifp->if_addr = getipv4addr(*arg); + free(*arg); + *arg = NULL; +} + + +void set_ifeaddr(arg) +char **arg; +{ + (void) geteaddr(*arg, &cifp->if_eaddr); + free(*arg); + *arg = NULL; +} + + +void new_arp() +{ + carp = (arp_t *)calloc(1, sizeof(arp_t)); + *arptail = carp; + arptail = &carp->arp_next; +} + + +void set_arpeaddr(arg) +char **arg; +{ + (void) geteaddr(*arg, &carp->arp_eaddr); + free(*arg); + *arg = NULL; +} + + +void set_arpv4addr(arg) +char **arg; +{ + carp->arp_addr = getipv4addr(*arg); + free(*arg); + *arg = NULL; +} + + +int arp_getipv4(ip, addr) +char *ip; +char *addr; +{ + arp_t *a; + + for (a = arplist; a; a = a->arp_next) + if (!bcmp(ip, (char *)&a->arp_addr, 4)) { + bcopy((char *)&a->arp_eaddr, addr, 6); + return 0; + } + return -1; +} + + +void reset_send() +{ + sending.snd_if = iflist; + sending.snd_gw = defrouter; +} + + +void set_sendif(arg) +char **arg; +{ + iface_t *ifp; + + for (ifp = iflist; ifp; ifp = ifp->if_next) + if (ifp->if_name && !strcmp(ifp->if_name, *arg)) + break; + sending.snd_if = ifp; + if (!ifp) + fprintf(stderr, "couldn't find interface %s\n", *arg); + free(*arg); + *arg = NULL; +} + + +void set_sendvia(arg) +char **arg; +{ + sending.snd_gw = getipv4addr(*arg); + free(*arg); + *arg = NULL; +} + + +void set_defaultrouter(arg) +char **arg; +{ + defrouter = getipv4addr(*arg); + free(*arg); + *arg = NULL; +} + + +void new_icmpheader() +{ + if ((ip->ip_p) && (ip->ip_p != IPPROTO_ICMP)) { + fprintf(stderr, "protocol %d specified with ICMP!\n", + ip->ip_p); + return; + } + ip->ip_p = IPPROTO_ICMP; + icmp = (icmphdr_t *)new_header(IPPROTO_ICMP); +} + + +void set_icmpcode(code) +int code; +{ + icmp->icmp_code = code; +} + + +void set_icmptype(type) +int type; +{ + icmp->icmp_type = type; +} + + +void set_icmpcodetok(code) +char **code; +{ + char *s; + int i; + + for (i = 0; (s = icmpcodes[i]); i++) + if (!strcmp(s, *code)) { + icmp->icmp_code = i; + break; + } + if (!s) + fprintf(stderr, "unknown ICMP code %s\n", *code); + free(*code); + *code = NULL; +} + + +void set_icmptypetok(type) +char **type; +{ + char *s; + int i, done = 0; + + for (i = 0; !(s = icmptypes[i]) || strcmp(s, "END"); i++) + if (s && !strcmp(s, *type)) { + icmp->icmp_type = i; + done = 1; + break; + } + if (!done) + fprintf(stderr, "unknown ICMP type %s\n", *type); + free(*type); + *type = NULL; +} + + +void set_icmpid(arg) +int arg; +{ + icmp->icmp_id = htons(arg); +} + + +void set_icmpseq(arg) +int arg; +{ + icmp->icmp_seq = htons(arg); +} + + +void set_icmpotime(arg) +int arg; +{ + icmp->icmp_otime = htonl(arg); +} + + +void set_icmprtime(arg) +int arg; +{ + icmp->icmp_rtime = htonl(arg); +} + + +void set_icmpttime(arg) +int arg; +{ + icmp->icmp_ttime = htonl(arg); +} + + +void set_icmpmtu(arg) +int arg; +{ + icmp->icmp_nextmtu = htons(arg); +} + + +void set_redir(redir, arg) +int redir; +char **arg; +{ + icmp->icmp_code = redir; + icmp->icmp_gwaddr = getipv4addr(*arg); + free(*arg); + *arg = NULL; +} + + +void set_icmppprob(num) +int num; +{ + icmp->icmp_pptr = num; +} + + +void new_ipv4opt() +{ + new_header(-2); +} + + +void add_ipopt(state, ptr) +int state; +void *ptr; +{ + struct ipopt_names *io; + struct statetoopt *sto; + char numbuf[16], *arg, **param = ptr; + int inc, hlen; + + if (state == IL_IPO_RR || state == IL_IPO_SATID) { + if (param) + snprintf(numbuf, sizeof(numbuf), "%d", *(int *)param); + else + strcpy(numbuf, "0"); + arg = numbuf; + } else + arg = param ? *param : NULL; + + if (canip->ah_next) { + fprintf(stderr, "cannot specify options after data body\n"); + return; + } + for (sto = toipopts; sto->sto_st; sto++) + if (sto->sto_st == state) + break; + if (!sto->sto_st) { + fprintf(stderr, "No mapping for state %d to IP option\n", + state); + return; + } + + hlen = sizeof(ip_t) + canip->ah_optlen; + for (io = ionames; io->on_name; io++) + if (io->on_value == sto->sto_op) + break; + canip->ah_lastopt = io->on_value; + + if (io->on_name) { + inc = addipopt((char *)ip + hlen, io, hlen - sizeof(ip_t),arg); + if (inc > 0) { + while (inc & 3) { + ((char *)ip)[sizeof(*ip) + inc] = IPOPT_NOP; + canip->ah_lastopt = IPOPT_NOP; + inc++; + } + hlen += inc; + } + } + + canip->ah_optlen = hlen - sizeof(ip_t); + + if (state != IL_IPO_RR && state != IL_IPO_SATID) + if (param && *param) { + free(*param); + *param = NULL; + } + sclass = NULL; +} + + +void end_ipopt() +{ + int pad; + char *s, *buf = (char *)ip; + + /* + * pad out so that we have a multiple of 4 bytes in size fo the + * options. make sure last byte is EOL. + */ + if (canip->ah_lastopt == IPOPT_NOP) { + buf[sizeof(*ip) + canip->ah_optlen - 1] = IPOPT_EOL; + } else if (canip->ah_lastopt != IPOPT_EOL) { + s = buf + sizeof(*ip) + canip->ah_optlen; + + for (pad = 3 - (canip->ah_optlen & 3); pad; pad--) { + *s++ = IPOPT_NOP; + *s = IPOPT_EOL; + canip->ah_optlen++; + } + canip->ah_optlen++; + } else { + s = buf + sizeof(*ip) + canip->ah_optlen - 1; + + for (pad = 3 - (canip->ah_optlen & 3); pad; pad--) { + *s++ = IPOPT_NOP; + *s = IPOPT_EOL; + canip->ah_optlen++; + } + } + ip->ip_hl = (sizeof(*ip) + canip->ah_optlen) >> 2; + inc_anipheaders(canip->ah_optlen); + free_anipheader(); +} + + +void set_secclass(arg) +char **arg; +{ + sclass = *arg; + *arg = NULL; +} + + +void free_anipheader() +{ + aniphdr_t *aip; + + aip = canip; + if ((canip = aip->ah_prev)) { + canip->ah_next = NULL; + aniptail = &canip->ah_next; + } + + if (canip) + free(aip); +} + + +void end_ipv4() +{ + aniphdr_t *aip; + + ip->ip_sum = 0; + ip->ip_len = htons(ip->ip_len); + ip->ip_sum = chksum((u_short *)ip, ip->ip_hl << 2); + ip->ip_len = ntohs(ip->ip_len); + free_anipheader(); + for (aip = aniphead, ip = NULL; aip; aip = aip->ah_next) + if (aip->ah_p == IPPROTO_IP) + ip = aip->ah_ip; +} + + +void end_icmp() +{ + aniphdr_t *aip; + + icmp->icmp_cksum = 0; + icmp->icmp_cksum = chksum((u_short *)icmp, canip->ah_len); + free_anipheader(); + for (aip = aniphead, icmp = NULL; aip; aip = aip->ah_next) + if (aip->ah_p == IPPROTO_ICMP) + icmp = aip->ah_icmp; +} + + +void end_udp() +{ + u_long sum; + aniphdr_t *aip; + ip_t iptmp; + + bzero((char *)&iptmp, sizeof(iptmp)); + iptmp.ip_p = ip->ip_p; + iptmp.ip_src = ip->ip_src; + iptmp.ip_dst = ip->ip_dst; + iptmp.ip_len = htons(ip->ip_len - (ip->ip_hl << 2)); + sum = p_chksum((u_short *)&iptmp, (u_int)sizeof(iptmp)); + udp->uh_ulen = htons(udp->uh_ulen); + udp->uh_sum = c_chksum((u_short *)udp, (u_int)ntohs(iptmp.ip_len), sum); + free_anipheader(); + for (aip = aniphead, udp = NULL; aip; aip = aip->ah_next) + if (aip->ah_p == IPPROTO_UDP) + udp = aip->ah_udp; +} + + +void end_tcp() +{ + u_long sum; + aniphdr_t *aip; + ip_t iptmp; + + bzero((char *)&iptmp, sizeof(iptmp)); + iptmp.ip_p = ip->ip_p; + iptmp.ip_src = ip->ip_src; + iptmp.ip_dst = ip->ip_dst; + iptmp.ip_len = htons(ip->ip_len - (ip->ip_hl << 2)); + sum = p_chksum((u_short *)&iptmp, (u_int)sizeof(iptmp)); + tcp->th_sum = 0; + tcp->th_sum = c_chksum((u_short *)tcp, (u_int)ntohs(iptmp.ip_len), sum); + free_anipheader(); + for (aip = aniphead, tcp = NULL; aip; aip = aip->ah_next) + if (aip->ah_p == IPPROTO_TCP) + tcp = aip->ah_tcp; +} + + +void end_data() +{ + free_anipheader(); +} + + +void iplang(fp) +FILE *fp; +{ + yyin = fp; + + yydebug = (opts & OPT_DEBUG) ? 1 : 0; + + while (!feof(fp)) + yyparse(); +} + + +u_short c_chksum(buf, len, init) +u_short *buf; +u_int len; +u_long init; +{ + u_long sum = init; + int nwords = len >> 1; + + for(; nwords > 0; nwords--) + sum += *buf++; + sum = (sum>>16) + (sum & 0xffff); + sum += (sum >>16); + return (~sum); +} + + +u_long p_chksum(buf,len) +u_short *buf; +u_int len; +{ + u_long sum = 0; + int nwords = len >> 1; + + for(; nwords > 0; nwords--) + sum += *buf++; + return sum; +} diff --git a/sbin/ipf/ipmon/ipmon.5 b/sbin/ipf/ipmon/ipmon.5 new file mode 100644 index 000000000000..95126f0c83c3 --- /dev/null +++ b/sbin/ipf/ipmon/ipmon.5 @@ -0,0 +1,226 @@ +.\" $FreeBSD$ +.\" +.TH IPMON 5 +.SH NAME +ipmon, ipmon.conf \- ipmon configuration file format +.SH DESCRIPTION +The +.B ipmon.conf +file is optionally loaded by +.B ipmon +when it starts. Its primary purpose is to direct +.B ipmon +to do extra actions when it sees a specific log entry from the kernel. +.PP +A line in the +.B ipmon.conf +file is either a comment or a +.B match +line. Each line must have a matching segment and an action segment. +These are to the left and right of the word "do", respectively. +A comment line is any line that starts with a #. +.PP +.B NOTE: +This file differs from all other IPFilter configuration files because it +attempts to match every line with every log record received. It does +.B not +stop at the +.B first +match or only use the +.B last +match. +.PP +For the action segment, a +.B match +line can delivery output to one of three destinations: +\fBfile\fR, \fBemail\fR or \fBcommand\fR. For example: +.nf + +match { type = ipf; } do { save("file:///var/log/ipf-log"); }; +match { type = nat; } do { syslog; }; +match { type = state; } do { execute("/bin/mail root"); }; +.fi +.PP +and is roughly described like this: +.PP +match { \fImatch-it ,match-it, ...\fP } do { \fIaction, action, ...\fP}; +.PP +where there can be a list of matching expressions and a list of actions +to perform if all of the matching expressions are matched up with by +the current log entry. +.PP +The lines above would save all ipf log entries to /var/log/ipf-log, send +all of the entries for NAT (ipnat related) to syslog and generate an email +to root for each log entry from the state tables. +.SH SYNTAX - MATCHING +.PP +In the above example, the matching segment was confined to matching on +the type of log entry generated. The full list of fields that can be +used here is: +.TP +direction <in|out> +This option is used to match on log records generated for packets going +in or out. +.TP +dstip <address/mask> +This option is used to match against the destination address associated +with the packet being logged. A "/mask" must be given and given in CIDR +notation (/0-/32) so to specify host 192.2.2.1, 192.2.2.1/32 must be given. +.TP +dstport <portnumber> +This option is used to match against the destination port in log entries. +A number must be given, symbolic names (such as those from /etc/services) +are not recognised by the parser. +.TP +every <second|# seconds|packet|# packets> +This option is used to regulate how often an \fBipmon.conf\fR entry is +actioned in response to an otherwise matching log record from the kernel. +.TP +group <name|number> +.TP +interface <interface-name> +This option is used to match against the network interface name associated +with the action causing the logging to happen. In general this will be the +network interface where the packet is seen by IPFilter. +.TP +logtag <number> +This option is used to match against tags set by ipf rules in \fBipf.conf\fR. +These tags are set with "set-tag(log=100)" appended to filter rules. +.TP +nattag <string> +This option is used to match against tags set by NAT rules in \fBipnat.conf\fR. +.TP +protocol <name|number> +This option is used to match against the IP protocol field in the packet +being logged. +.TP +result <pass|block|nomatch|log> +This option is used to match against the result of packet matching in the +kernel. If a packet is logged, using a \fBlog\fR rule in \fBipf.conf\fR +then it will match "log" here. The "nomatch" option is for use with +matching log records generated for all packets as the default. +.TP +rule <number> +This option is used to match against the \fInumber\fR of the rule +causing the record to be generated. The \fInumber\fR of a rule can be +observed using "ipfstat -ion". +.TP +srcip <address/mask> +This option is used to match against the source address associated +with the packet being logged. A "/mask" must be given and given in CIDR +notation (/0-/32) so to specify host 192.2.2.1, 192.2.2.1/32 must be given. +.TP +srcport <portnumber> +This option is used to match against the source port in log entries. +A number must be given, symbolic names (such as those from /etc/services) +are not recognised by the parser. +.TP +type <ipf|nat|state> +The format for files accepted by ipmon is described by the following grammar: +.B NOTE: +At present, only IPv4 matching is available for source/destination address +matching. +.SH SYNTAX - ACTIONS +The list of actions supported is as follows: +.TP +save("file://<filename>") +save("raw://<filename>") +Write out the log record to the filename given. This file will be closed +and reopened on receipt of a SIGHUP. If the \fIraw\fP target is used, +binary log data, as read from the kernel, is written out rather than a +text log record. The filename should be an absolute target, including +the root directory. Thus, saving to /var/log/ipmon.log would be, as an +example, save("file:///var/log/ipmon.log"). +.TP +syslog("<facility>.<priority>") +syslog("<facility>.") +syslog(".<priority>") +To log a text record via syslog, the \fBsyslog\fP action word is used. +The facility used by default is determined at first by the default +compiled into \fBipmon\fP (usually LOG_LOCAL0), which can be changed +via the command line (-L <facility>) or in an \fBipf.conf\fP rule +using the \fIlevel\fP option with logging. If the facility is +specified here, it takes precedence over all other settings. +The same applies to the syslog priority. By default, ipmon will +determine a priority for the packet, depending on whether or not it +has been blocked, passed, etc. It is possible to force the complete +facility/priority value for each log entry or to choose to replace +only one of them. +.TP +execute("<command string>") +The +.B execute +action runs the specified command each time the log entry matches +and feeds the log entry, as text, to the command being executed. +The command string given is executed using /bin/sh. +.TP +nothing +Literally, do nothing. Use this if you want to be verbose in your config +file about doing nothing for a particular log record. +.SH PLUGIN ACTIONS +It is possible to configure +.B ipmon +to use externally supplied modules to save log entries with. +These are added to +.B ipmon +using the +.I load_action +configuration line. The syntax of this line is: +.nf + +load_action <name> <path>; +.fi +.TP +name +is a short name for the action. It does not need to correspond to the +name of the library file, but inside the library file, the functions +.B <name>destroy +, +.B <name>parse +and +.B <name>store +must be present. +.TP +path +specifies the path in the filesystem to the shared object +that contains the implementation of the new action. After the new +action has been declared using +.I load_action +it can then be used in any +.I do +statement. +.SH EXAMPLES +.PP +Some further examples are: +.nf + +# +# log everything to syslog local4, regardless +# +match { ; } do { syslog("local4."); }; +# +# keep a local copy of things packets to/from port 80 +# +match { srcport = 80; } do { save("file:///var/log/web"); }; +match { dstport = 80; } do { save("file:///var/log/web"); }; +# +load_action local "/usr/lib/libmyaction.so"; +match { dstip 127.0.0.1; } do { local("local options"); }; +# +.fi +.SH MATCHING +.PP +All entries of the rules present in the file are +compared for matches - there is no first or last rule match. +.SH FILES +/dev/ipl +.br +/dev/ipf +.br +/dev/ipnat +.br +/dev/ipstate +.br +/etc/ipmon.conf +.SH SEE ALSO +ipmon(8), ipl(4) diff --git a/sbin/ipf/ipmon/ipmon.8 b/sbin/ipf/ipmon/ipmon.8 new file mode 100644 index 000000000000..3f4036d96e21 --- /dev/null +++ b/sbin/ipf/ipmon/ipmon.8 @@ -0,0 +1,196 @@ +.\" $FreeBSD$ +.TH ipmon 8 +.SH NAME +ipmon \- monitors /dev/ipl for logged packets +.SH SYNOPSIS +.B ipmon +[ +.B \-abBDFhnpstvxX +] [ +.B "\-B <binarylogfile>" +] [ +.B "\-C <configfile>" +] [ +.B "\-N <device>" +] [ +.B "\-L <facility>" +] [ +.B "\-o [NSI]" +] [ +.B "\-O [NSI]" +] [ +.B "\-P <pidfile>" +] [ +.B "\-S <device>" +] [ +.B "\-f <device>" +] [ +.B <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 form, 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. +.LP +Messages generated by ipmon consist of whitespace separated fields. +Fields common to all messages are: +.LP +1. The date of packet receipt. This is suppressed when the message is +sent to syslog. +.LP +2. The time of packet receipt. This is in the form HH:MM:SS.F, for hours, +minutes seconds, and fractions of a second (which can be several digits +long). +.LP +3. The name of the interface the packet was processed on, e.g., \fBwe1\fP. +.LP +4. The group and rule number of the rule, e.g., \fB@0:17\fP. These can be +viewed with \fBipfstat -n\fP. +.LP +5. The action: \fBp\fP for passed, \fBb\fP for blocked, \fB\fP for a short +packet, \fBn\fP did not match any rules or \fBL\fP for a log rule. +.LP +6. The addresses. +This is actually three fields: the source address and port +(separated by a comma), the \fB->\fP symbol, and the destination address +and port. E.g.: \fB209.53.17.22,80 -> 198.73.220.17,1722\fP. +.LP +7. \fBPR\fP followed by the protocol name or number, e.g., \fBPR tcp\fP. +.LP +8. \fBlen\fP followed by the header length and total length of the packet, +e.g., \fBlen 20 40\fP. +.LP +If the packet is a TCP packet, there will be an additional field starting +with a hyphen followed by letters corresponding to any flags that were set. +See the ipf.conf manual page for a list of letters and their flags. +.LP +If the packet is an ICMP packet, there will be two fields at the end, +the first always being `icmp', and the next being the ICMP message and +submessage type, separated by a slash, e.g., \fBicmp 3/3\fP for a port +unreachable message. +.LP +In order for \fBipmon\fP to properly work, the kernel option +\fBIPFILTER_LOG\fP must be turned on in your kernel. Please see +\fBoptions(4)\fP for more details. +.LP +\fBipmon\fP reopens its log file(s) and rereads its configuration file +when it receives a SIGHUP signal. +.SH OPTIONS +.TP +.B \-a +Open all of the device logfiles for reading log entries from. All entries +are displayed to the same output 'device' (stderr or syslog). +.TP +.B \-b +For rules which log the body of a packet, generate hex output representing +the packet contents after the headers. +.TP +.B \-B <binarylogfilename> +Enable logging of the raw, unformatted binary data to the specified +\fI<binarylogfilename>\fP file. This can be read, later, using \fBipmon\fP +with the \fB-f\fP option. +.TP +.B \-C <configfilename> +This option specifies a file to be used to specify optional extra actions +when it sees specific log entries from the kernel. +.TP +.B \-D +Cause ipmon to turn itself into a daemon. Using subshells or backgrounding +of ipmon is not required to turn it into an orphan so it can run indefinitely. +.TP +.B "\-f <device>" +specify an alternative device/file from which to read the log information +for normal IP Filter log records. +.TP +.B \-F +Flush the current packet log buffer. The number of bytes flushed is displayed, +even should the result be zero. +.TP +.B \-L <facility> +Using this option allows you to change the default syslog facility that +ipmon uses for syslog messages. The default is local0. +.TP +.B \-n +IP addresses and port numbers will be mapped, where possible, back into +hostnames and service names. +.TP +.B "\-N <device>" +Set the logfile to be opened for reading NAT log records from to <device>. +.TP +.B \-o +Specify which log files to actually read data from. N - NAT logfile, +S - State logfile, I - normal IP Filter logfile. The \fB-a\fP option is +equivalent to using \fB-o NSI\fP. +.TP +.B \-O +Specify which log files you do not wish to read from. This is most sensibly +used with the \fB-a\fP. Letters available as parameters to this are the same +as for \fB-o\fP. +.TP +.B \-p +Cause the port number in log messages to always be printed as a number and +never attempt to look it up as from \fI/etc/services\fP, etc. +.TP +.B \-P <pidfile> +Write the pid of the ipmon process to a file. By default this is +\fI//etc/opt/ipf/ipmon.pid\fP (Solaris), \fI/var/run/ipmon.pid\fP (44BSD +or later) or \fI/etc/ipmon.pid\fP for all others. +.TP +.B \-s +Packet information read in will be sent through syslogd rather than +saved to a file. The default facility when compiled and installed is +\fBsecurity\fP. The following levels are used: +.IP +.B LOG_INFO +\- packets logged using the "log" keyword as the action rather +than pass or block. +.IP +.B LOG_NOTICE +\- packets logged which are also passed +.IP +.B LOG_WARNING +\- packets logged which are also blocked +.IP +.B LOG_ERR +\- packets which have been logged and which can be considered +"short". +.TP +.B "\-S <device>" +Set the logfile to be opened for reading state log records from to <device>. +.TP +.B \-t +read the input file/device in a manner akin to tail(1). +.TP +.B \-v +show tcp window, ack and sequence fields. +.TP +.B \-x +show the packet data in hex. +.TP +.B \-X +show the log header record data in hex. +.SH DIAGNOSTICS +\fBipmon\fP expects data that it reads to be consistent with how it should be +saved and will abort if it fails an assertion which detects an anomaly in the +recorded data. +.SH FILES +/dev/ipl +.br +/dev/ipnat +.br +/dev/ipstate +.br +/etc/ipmon.conf +.br +/etc/services +.SH SEE ALSO +ipl(4), ipmon(5), ipf(8), ipfstat(8), ipnat(8) +.SH BUGS +.PP +If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipmon/ipmon.c b/sbin/ipf/ipmon/ipmon.c new file mode 100644 index 000000000000..28586537da5b --- /dev/null +++ b/sbin/ipf/ipmon/ipmon.c @@ -0,0 +1,1876 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include "ipf.h" +#include "ipmon.h" +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <syslog.h> +#include <ctype.h> +#include <fcntl.h> +#include <signal.h> + +#if !defined(lint) +static const char sccsid[] = "@(#)ipmon.c 1.21 6/5/96 (C)1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + + +#define STRERROR(x) strerror(x) + +extern int optind; +extern char *optarg; + +extern ipmon_saver_t executesaver; +extern ipmon_saver_t filesaver; +extern ipmon_saver_t nothingsaver; +extern ipmon_saver_t snmpv1saver; +extern ipmon_saver_t snmpv2saver; +extern ipmon_saver_t syslogsaver; + + +struct flags { + int value; + char flag; +}; + +typedef struct logsource { + int fd; + int logtype; + char *file; + int regular; + size_t size; +} logsource_t; + +typedef struct config { + int opts; + int maxfd; + logsource_t logsrc[3]; + fd_set fdmr; + FILE *blog; + char *bfile; + FILE *log; + char *file; + char *cfile; +} config_t; + +typedef struct icmp_subtype { + int ist_val; + char *ist_name; +} icmp_subtype_t; + +typedef struct icmp_type { + int it_val; + struct icmp_subtype *it_subtable; + size_t it_stsize; + char *it_name; +} icmp_type_t; + + +#define IST_SZ(x) (sizeof(x)/sizeof(icmp_subtype_t)) + + +struct flags tcpfl[] = { + { TH_ACK, 'A' }, + { TH_RST, 'R' }, + { TH_SYN, 'S' }, + { TH_FIN, 'F' }, + { TH_URG, 'U' }, + { TH_PUSH,'P' }, + { TH_ECN, 'E' }, + { TH_CWR, 'C' }, + { 0, '\0' } +}; + +char *reasons[] = { + "filter-rule", + "log-or-block_1", + "pps-rate", + "jumbogram", + "makefrip-fail", + "state_add-fail", + "updateipid-fail", + "log-or-block_2", + "decap-fail", + "auth_new-fail", + "auth_captured", + "coalesce-fail", + "pullup-fail", + "auth-feedback", + "bad-frag", + "natv4_out-fail", + "natv4_in-fail", + "natv6_out-fail", + "natv6_in-fail", +}; + +#if SOLARIS +static char *pidfile = "/etc/opt/ipf/ipmon.pid"; +#else +static char *pidfile = "/var/run/ipmon.pid"; +#endif + +static char line[2048]; +static int donehup = 0; +static void usage(char *); +static void handlehup(int); +static void flushlogs(char *, FILE *); +static void print_log(config_t *, logsource_t *, char *, int); +static void print_ipflog(config_t *, char *, int); +static void print_natlog(config_t *, char *, int); +static void print_statelog(config_t *, char *, int); +static int read_log(int, int *, char *, int); +static void write_pid(char *); +static char *icmpname(u_int, u_int); +static char *icmpname6(u_int, u_int); +static icmp_type_t *find_icmptype(int, icmp_type_t *, size_t); +static icmp_subtype_t *find_icmpsubtype(int, icmp_subtype_t *, size_t); +static struct tm *get_tm(time_t); + +char *portlocalname(int, char *, u_int); +int main(int, char *[]); + +static void logopts(int, char *); +static void init_tabs(void); +static char *getlocalproto(u_int); +static void openlogs(config_t *conf); +static int read_loginfo(config_t *conf); +static void initconfig(config_t *conf); + +static char **protocols = NULL; +static char **udp_ports = NULL; +static char **tcp_ports = NULL; + + +#define HOSTNAMEV4(b) hostname(AF_INET, (u_32_t *)&(b)) + +#ifndef LOGFAC +#define LOGFAC LOG_LOCAL0 +#endif +int logfac = LOGFAC; +int ipmonopts = 0; +int opts = OPT_NORESOLVE; +int use_inet6 = 0; + + +static icmp_subtype_t icmpunreachnames[] = { + { ICMP_UNREACH_NET, "net" }, + { ICMP_UNREACH_HOST, "host" }, + { ICMP_UNREACH_PROTOCOL, "protocol" }, + { ICMP_UNREACH_PORT, "port" }, + { ICMP_UNREACH_NEEDFRAG, "needfrag" }, + { ICMP_UNREACH_SRCFAIL, "srcfail" }, + { ICMP_UNREACH_NET_UNKNOWN, "net_unknown" }, + { ICMP_UNREACH_HOST_UNKNOWN, "host_unknown" }, + { ICMP_UNREACH_NET, "isolated" }, + { ICMP_UNREACH_NET_PROHIB, "net_prohib" }, + { ICMP_UNREACH_NET_PROHIB, "host_prohib" }, + { ICMP_UNREACH_TOSNET, "tosnet" }, + { ICMP_UNREACH_TOSHOST, "toshost" }, + { ICMP_UNREACH_ADMIN_PROHIBIT, "admin_prohibit" }, + { -2, NULL } +}; + +static icmp_subtype_t redirectnames[] = { + { ICMP_REDIRECT_NET, "net" }, + { ICMP_REDIRECT_HOST, "host" }, + { ICMP_REDIRECT_TOSNET, "tosnet" }, + { ICMP_REDIRECT_TOSHOST, "toshost" }, + { -2, NULL } +}; + +static icmp_subtype_t timxceednames[] = { + { ICMP_TIMXCEED_INTRANS, "transit" }, + { ICMP_TIMXCEED_REASS, "reassem" }, + { -2, NULL } +}; + +static icmp_subtype_t paramnames[] = { + { ICMP_PARAMPROB_ERRATPTR, "errata_pointer" }, + { ICMP_PARAMPROB_OPTABSENT, "optmissing" }, + { ICMP_PARAMPROB_LENGTH, "length" }, + { -2, NULL } +}; + +static icmp_type_t icmptypes4[] = { + { ICMP_ECHOREPLY, NULL, 0, "echoreply" }, + { -1, NULL, 0, NULL }, + { -1, NULL, 0, NULL }, + { ICMP_UNREACH, icmpunreachnames, + IST_SZ(icmpunreachnames),"unreach" }, + { ICMP_SOURCEQUENCH, NULL, 0, "sourcequench" }, + { ICMP_REDIRECT, redirectnames, + IST_SZ(redirectnames), "redirect" }, + { -1, NULL, 0, NULL }, + { -1, NULL, 0, NULL }, + { ICMP_ECHO, NULL, 0, "echo" }, + { ICMP_ROUTERADVERT, NULL, 0, "routeradvert" }, + { ICMP_ROUTERSOLICIT, NULL, 0, "routersolicit" }, + { ICMP_TIMXCEED, timxceednames, + IST_SZ(timxceednames), "timxceed" }, + { ICMP_PARAMPROB, paramnames, + IST_SZ(paramnames), "paramprob" }, + { ICMP_TSTAMP, NULL, 0, "timestamp" }, + { ICMP_TSTAMPREPLY, NULL, 0, "timestampreply" }, + { ICMP_IREQ, NULL, 0, "inforeq" }, + { ICMP_IREQREPLY, NULL, 0, "inforeply" }, + { ICMP_MASKREQ, NULL, 0, "maskreq" }, + { ICMP_MASKREPLY, NULL, 0, "maskreply" }, + { -2, NULL, 0, NULL } +}; + +static icmp_subtype_t icmpredirect6[] = { + { ICMP6_DST_UNREACH_NOROUTE, "noroute" }, + { ICMP6_DST_UNREACH_ADMIN, "admin" }, + { ICMP6_DST_UNREACH_NOTNEIGHBOR, "neighbour" }, + { ICMP6_DST_UNREACH_ADDR, "address" }, + { ICMP6_DST_UNREACH_NOPORT, "noport" }, + { -2, NULL } +}; + +static icmp_subtype_t icmptimexceed6[] = { + { ICMP6_TIME_EXCEED_TRANSIT, "intransit" }, + { ICMP6_TIME_EXCEED_REASSEMBLY, "reassem" }, + { -2, NULL } +}; + +static icmp_subtype_t icmpparamprob6[] = { + { ICMP6_PARAMPROB_HEADER, "header" }, + { ICMP6_PARAMPROB_NEXTHEADER, "nextheader" }, + { ICMP6_PARAMPROB_OPTION, "option" }, + { -2, NULL } +}; + +static icmp_subtype_t icmpquerysubject6[] = { + { ICMP6_NI_SUBJ_IPV6, "ipv6" }, + { ICMP6_NI_SUBJ_FQDN, "fqdn" }, + { ICMP6_NI_SUBJ_IPV4, "ipv4" }, + { -2, NULL }, +}; + +static icmp_subtype_t icmpnodeinfo6[] = { + { ICMP6_NI_SUCCESS, "success" }, + { ICMP6_NI_REFUSED, "refused" }, + { ICMP6_NI_UNKNOWN, "unknown" }, + { -2, NULL } +}; + +static icmp_subtype_t icmprenumber6[] = { + { ICMP6_ROUTER_RENUMBERING_COMMAND, "command" }, + { ICMP6_ROUTER_RENUMBERING_RESULT, "result" }, + { ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET, "seqnum_reset" }, + { -2, NULL } +}; + +static icmp_type_t icmptypes6[] = { + { 0, NULL, 0, NULL }, + { ICMP6_DST_UNREACH, icmpredirect6, + IST_SZ(icmpredirect6), "unreach" }, + { ICMP6_PACKET_TOO_BIG, NULL, 0, "toobig" }, + { ICMP6_TIME_EXCEEDED, icmptimexceed6, + IST_SZ(icmptimexceed6), "timxceed" }, + { ICMP6_PARAM_PROB, icmpparamprob6, + IST_SZ(icmpparamprob6), "paramprob" }, + { ICMP6_ECHO_REQUEST, NULL, 0, "echo" }, + { ICMP6_ECHO_REPLY, NULL, 0, "echoreply" }, + { ICMP6_MEMBERSHIP_QUERY, icmpquerysubject6, + IST_SZ(icmpquerysubject6), "groupmemberquery" }, + { ICMP6_MEMBERSHIP_REPORT,NULL, 0, "groupmemberreport" }, + { ICMP6_MEMBERSHIP_REDUCTION,NULL, 0, "groupmemberterm" }, + { ND_ROUTER_SOLICIT, NULL, 0, "routersolicit" }, + { ND_ROUTER_ADVERT, NULL, 0, "routeradvert" }, + { ND_NEIGHBOR_SOLICIT, NULL, 0, "neighborsolicit" }, + { ND_NEIGHBOR_ADVERT, NULL, 0, "neighboradvert" }, + { ND_REDIRECT, NULL, 0, "redirect" }, + { ICMP6_ROUTER_RENUMBERING, icmprenumber6, + IST_SZ(icmprenumber6), "routerrenumber" }, + { ICMP6_WRUREQUEST, NULL, 0, "whoareyourequest" }, + { ICMP6_WRUREPLY, NULL, 0, "whoareyoureply" }, + { ICMP6_FQDN_QUERY, NULL, 0, "fqdnquery" }, + { ICMP6_FQDN_REPLY, NULL, 0, "fqdnreply" }, + { ICMP6_NI_QUERY, icmpnodeinfo6, + IST_SZ(icmpnodeinfo6), "nodeinforequest" }, + { ICMP6_NI_REPLY, NULL, 0, "nodeinforeply" }, + { MLD6_MTRACE_RESP, NULL, 0, "mtraceresponse" }, + { MLD6_MTRACE, NULL, 0, "mtracerequest" }, + { -2, NULL, 0, NULL } +}; + +static icmp_subtype_t *find_icmpsubtype(type, table, tablesz) + int type; + icmp_subtype_t *table; + size_t tablesz; +{ + icmp_subtype_t *ist; + int i; + + if (tablesz < 2) + return NULL; + + if ((type < 0) || (type > table[tablesz - 2].ist_val)) + return NULL; + + i = type; + if (table[type].ist_val == type) + return table + type; + + for (i = 0, ist = table; ist->ist_val != -2; i++, ist++) + if (ist->ist_val == type) + return ist; + return NULL; +} + + +static icmp_type_t *find_icmptype(type, table, tablesz) + int type; + icmp_type_t *table; + size_t tablesz; +{ + icmp_type_t *it; + int i; + + if (tablesz < 2) + return NULL; + + if ((type < 0) || (type > table[tablesz - 2].it_val)) + return NULL; + + i = type; + if (table[type].it_val == type) + return table + type; + + for (i = 0, it = table; it->it_val != -2; i++, it++) + if (it->it_val == type) + return it; + return NULL; +} + + +static void handlehup(sig) + int sig; +{ + signal(SIGHUP, handlehup); + donehup = 1; +} + + +static void init_tabs() +{ + struct protoent *p; + struct servent *s; + char *name, **tab; + int port, i; + + if (protocols != NULL) { + for (i = 0; i < 256; i++) + if (protocols[i] != NULL) { + free(protocols[i]); + protocols[i] = NULL; + } + free(protocols); + protocols = NULL; + } + protocols = (char **)malloc(256 * sizeof(*protocols)); + if (protocols != NULL) { + bzero((char *)protocols, 256 * sizeof(*protocols)); + + setprotoent(1); + while ((p = getprotoent()) != NULL) + if (p->p_proto >= 0 && p->p_proto <= 255 && + p->p_name != NULL && protocols[p->p_proto] == NULL) + protocols[p->p_proto] = strdup(p->p_name); + endprotoent(); + if (protocols[0]) + free(protocols[0]); + protocols[0] = strdup("ip"); + } + + if (udp_ports != NULL) { + for (i = 0; i < 65536; i++) + if (udp_ports[i] != NULL) { + free(udp_ports[i]); + udp_ports[i] = NULL; + } + free(udp_ports); + udp_ports = NULL; + } + udp_ports = (char **)malloc(65536 * sizeof(*udp_ports)); + if (udp_ports != NULL) + bzero((char *)udp_ports, 65536 * sizeof(*udp_ports)); + + if (tcp_ports != NULL) { + for (i = 0; i < 65536; i++) + if (tcp_ports[i] != NULL) { + free(tcp_ports[i]); + tcp_ports[i] = NULL; + } + free(tcp_ports); + tcp_ports = NULL; + } + tcp_ports = (char **)malloc(65536 * sizeof(*tcp_ports)); + if (tcp_ports != NULL) + bzero((char *)tcp_ports, 65536 * sizeof(*tcp_ports)); + + setservent(1); + while ((s = getservent()) != NULL) { + if (s->s_proto == NULL) + continue; + else if (!strcmp(s->s_proto, "tcp")) { + port = ntohs(s->s_port); + name = s->s_name; + tab = tcp_ports; + } else if (!strcmp(s->s_proto, "udp")) { + port = ntohs(s->s_port); + name = s->s_name; + tab = udp_ports; + } else + continue; + if ((port < 0 || port > 65535) || (name == NULL)) + continue; + if (tab != NULL) + tab[port] = strdup(name); + } + endservent(); +} + + +static char *getlocalproto(p) + u_int p; +{ + static char pnum[4]; + char *s; + + p &= 0xff; + s = protocols ? protocols[p] : NULL; + if (s == NULL) { + snprintf(pnum, sizeof(pnum), "%u", p); + s = pnum; + } + return s; +} + + +static int read_log(fd, lenp, buf, bufsize) + int fd, bufsize, *lenp; + char *buf; +{ + int nr; + + if (bufsize > IPFILTER_LOGSIZE) + bufsize = IPFILTER_LOGSIZE; + + nr = read(fd, buf, bufsize); + if (!nr) + return 2; + if ((nr < 0) && (errno != EINTR)) + return -1; + *lenp = nr; + return 0; +} + + +char *portlocalname(res, proto, port) + int res; + char *proto; + u_int port; +{ + static char pname[8]; + char *s; + + port = ntohs(port); + port &= 0xffff; + snprintf(pname, sizeof(pname), "%u", port); + if (!res || (ipmonopts & IPMON_PORTNUM)) + return pname; + s = NULL; + if (!strcmp(proto, "tcp")) + s = tcp_ports[port]; + else if (!strcmp(proto, "udp")) + s = udp_ports[port]; + if (s == NULL) + s = pname; + return s; +} + + +static char *icmpname(type, code) + u_int type; + u_int code; +{ + static char name[80]; + icmp_subtype_t *ist; + icmp_type_t *it; + char *s; + + s = NULL; + it = find_icmptype(type, icmptypes4, sizeof(icmptypes4) / sizeof(*it)); + if (it != NULL) + s = it->it_name; + + if (s == NULL) + snprintf(name, sizeof(name), "icmptype(%d)/", type); + else + snprintf(name, sizeof(name), "%s/", s); + + ist = NULL; + if (it != NULL && it->it_subtable != NULL) + ist = find_icmpsubtype(code, it->it_subtable, it->it_stsize); + + if (ist != NULL && ist->ist_name != NULL) + strcat(name, ist->ist_name); + else { + int strlen_name = strlen(name); + snprintf(name + strlen_name, sizeof(name) - strlen_name, "%d", code); + } + + return name; +} + +static char *icmpname6(type, code) + u_int type; + u_int code; +{ + static char name[80]; + icmp_subtype_t *ist; + icmp_type_t *it; + char *s; + + s = NULL; + it = find_icmptype(type, icmptypes6, sizeof(icmptypes6) / sizeof(*it)); + if (it != NULL) + s = it->it_name; + + if (s == NULL) + snprintf(name, sizeof(name), "icmpv6type(%d)/", type); + else + snprintf(name, sizeof(name), "%s/", s); + + ist = NULL; + if (it != NULL && it->it_subtable != NULL) + ist = find_icmpsubtype(code, it->it_subtable, it->it_stsize); + + if (ist != NULL && ist->ist_name != NULL) + strcat(name, ist->ist_name); + else { + int strlen_name = strlen(name); + snprintf(name + strlen_name, sizeof(name) - strlen_name, "%d", code); + } + + return name; +} + + +void dumphex(log, dopts, buf, len) + FILE *log; + int dopts; + char *buf; + int len; +{ + char hline[80]; + int i, j, k; + u_char *s = (u_char *)buf, *t = (u_char *)hline; + + if (buf == NULL || len == 0) + return; + + *hline = '\0'; + + for (i = len, j = 0; i; i--, j++, s++) { + if (j && !(j & 0xf)) { + *t++ = '\n'; + *t = '\0'; + if ((dopts & IPMON_SYSLOG)) + syslog(LOG_INFO, "%s", hline); + else if (log != NULL) + fputs(hline, log); + t = (u_char *)hline; + *t = '\0'; + } + sprintf((char *)t, "%02x", *s & 0xff); + t += 2; + if (!((j + 1) & 0xf)) { + s -= 15; + sprintf((char *)t, " "); + t += 8; + for (k = 16; k; k--, s++) + *t++ = (isprint(*s) ? *s : '.'); + s--; + } + + if ((j + 1) & 0xf) + *t++ = ' ';; + } + + if (j & 0xf) { + for (k = 16 - (j & 0xf); k; k--) { + *t++ = ' '; + *t++ = ' '; + *t++ = ' '; + } + sprintf((char *)t, " "); + t += 7; + s -= j & 0xf; + for (k = j & 0xf; k; k--, s++) + *t++ = (isprint(*s) ? *s : '.'); + *t++ = '\n'; + *t = '\0'; + } + if ((dopts & IPMON_SYSLOG) != 0) + syslog(LOG_INFO, "%s", hline); + else if (log != NULL) { + fputs(hline, log); + fflush(log); + } +} + + +static struct tm *get_tm(sec) + time_t sec; +{ + struct tm *tm; + time_t t; + + t = sec; + tm = localtime(&t); + return tm; +} + +static void print_natlog(conf, buf, blen) + config_t *conf; + char *buf; + int blen; +{ + static u_32_t seqnum = 0; + int res, i, len, family; + struct natlog *nl; + struct tm *tm; + iplog_t *ipl; + char *proto; + int simple; + char *t; + + t = line; + simple = 0; + ipl = (iplog_t *)buf; + if (ipl->ipl_seqnum != seqnum) { + if ((ipmonopts & IPMON_SYSLOG) != 0) { + syslog(LOG_WARNING, + "missed %u NAT log entries: %u %u", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } else { + (void) fprintf(conf->log, + "missed %u NAT log entries: %u %u\n", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } + } + seqnum = ipl->ipl_seqnum + ipl->ipl_count; + + nl = (struct natlog *)((char *)ipl + sizeof(*ipl)); + res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0; + tm = get_tm(ipl->ipl_sec); + len = sizeof(line); + + if (!(ipmonopts & IPMON_SYSLOG)) { + (void) strftime(t, len, "%d/%m/%Y ", tm); + i = strlen(t); + len -= i; + t += i; + } + (void) strftime(t, len, "%T", tm); + t += strlen(t); + snprintf(t, sizeof(t), ".%-.6ld @%hd ", (long)ipl->ipl_usec, nl->nl_rule + 1); + t += strlen(t); + + switch (nl->nl_action) + { + case NL_NEW : + strcpy(t, "NAT:NEW"); + break; + + case NL_FLUSH : + strcpy(t, "NAT:FLUSH"); + break; + + case NL_CLONE : + strcpy(t, "NAT:CLONE"); + break; + + case NL_EXPIRE : + strcpy(t, "NAT:EXPIRE"); + break; + + case NL_DESTROY : + strcpy(t, "NAT:DESTROY"); + break; + + case NL_PURGE : + strcpy(t, "NAT:PURGE"); + break; + + default : + snprintf(t, sizeof(t), "NAT:Action(%d)", nl->nl_action); + break; + } + t += strlen(t); + + + switch (nl->nl_type) + { + case NAT_MAP : + strcpy(t, "-MAP "); + simple = 1; + break; + + case NAT_REDIRECT : + strcpy(t, "-RDR "); + simple = 1; + break; + + case NAT_BIMAP : + strcpy(t, "-BIMAP "); + simple = 1; + break; + + case NAT_MAPBLK : + strcpy(t, "-MAPBLOCK "); + simple = 1; + break; + + case NAT_REWRITE|NAT_MAP : + strcpy(t, "-RWR_MAP "); + break; + + case NAT_REWRITE|NAT_REDIRECT : + strcpy(t, "-RWR_RDR "); + break; + + case NAT_ENCAP|NAT_MAP : + strcpy(t, "-ENC_MAP "); + break; + + case NAT_ENCAP|NAT_REDIRECT : + strcpy(t, "-ENC_RDR "); + break; + + case NAT_DIVERTUDP|NAT_MAP : + strcpy(t, "-DIV_MAP "); + break; + + case NAT_DIVERTUDP|NAT_REDIRECT : + strcpy(t, "-DIV_RDR "); + break; + + default : + snprintf(t, sizeof(t), "-Type(%d) ", nl->nl_type); + break; + } + t += strlen(t); + + proto = getlocalproto(nl->nl_p[0]); + + family = vtof(nl->nl_v[0]); + + if (simple == 1) { + snprintf(t, sizeof(t), "%s,%s <- -> ", hostname(family, nl->nl_osrcip.i6), + portlocalname(res, proto, (u_int)nl->nl_osrcport)); + t += strlen(t); + snprintf(t, sizeof(t), "%s,%s ", hostname(family, nl->nl_nsrcip.i6), + portlocalname(res, proto, (u_int)nl->nl_nsrcport)); + t += strlen(t); + snprintf(t, sizeof(t), "[%s,%s] ", hostname(family, nl->nl_odstip.i6), + portlocalname(res, proto, (u_int)nl->nl_odstport)); + } else { + snprintf(t, sizeof(t), "%s,%s ", hostname(family, nl->nl_osrcip.i6), + portlocalname(res, proto, (u_int)nl->nl_osrcport)); + t += strlen(t); + snprintf(t, sizeof(t), "%s,%s <- -> ", hostname(family, nl->nl_odstip.i6), + portlocalname(res, proto, (u_int)nl->nl_odstport)); + t += strlen(t); + snprintf(t, sizeof(t), "%s,%s ", hostname(family, nl->nl_nsrcip.i6), + portlocalname(res, proto, (u_int)nl->nl_nsrcport)); + t += strlen(t); + snprintf(t, sizeof(t), "%s,%s ", hostname(family, nl->nl_ndstip.i6), + portlocalname(res, proto, (u_int)nl->nl_ndstport)); + } + t += strlen(t); + + strcpy(t, getlocalproto(nl->nl_p[0])); + t += strlen(t); + + if (nl->nl_action == NL_EXPIRE || nl->nl_action == NL_FLUSH) { +#ifdef USE_QUAD_T +# ifdef PRId64 + snprintf(t, sizeof(t), " Pkts %" PRId64 "/%" PRId64 " Bytes %" PRId64 "/%" + PRId64, +# else + snprintf(t, sizeof(t), " Pkts %qd/%qd Bytes %qd/%qd", +# endif +#else + snprintf(t, sizeof(t), " Pkts %ld/%ld Bytes %ld/%ld", +#endif + nl->nl_pkts[0], nl->nl_pkts[1], + nl->nl_bytes[0], nl->nl_bytes[1]); + t += strlen(t); + } + + *t++ = '\n'; + *t++ = '\0'; + if (ipmonopts & IPMON_SYSLOG) + syslog(LOG_INFO, "%s", line); + else if (conf->log != NULL) + (void) fprintf(conf->log, "%s", line); +} + + +static void print_statelog(conf, buf, blen) + config_t *conf; + char *buf; + int blen; +{ + static u_32_t seqnum = 0; + int res, i, len, family; + struct ipslog *sl; + char *t, *proto; + struct tm *tm; + iplog_t *ipl; + + t = line; + ipl = (iplog_t *)buf; + if (ipl->ipl_seqnum != seqnum) { + if ((ipmonopts & IPMON_SYSLOG) != 0) { + syslog(LOG_WARNING, + "missed %u state log entries: %u %u", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } else { + (void) fprintf(conf->log, + "missed %u state log entries: %u %u\n", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } + } + seqnum = ipl->ipl_seqnum + ipl->ipl_count; + + sl = (struct ipslog *)((char *)ipl + sizeof(*ipl)); + res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0; + tm = get_tm(ipl->ipl_sec); + len = sizeof(line); + if (!(ipmonopts & IPMON_SYSLOG)) { + (void) strftime(t, len, "%d/%m/%Y ", tm); + i = strlen(t); + len -= i; + t += i; + } + (void) strftime(t, len, "%T", tm); + t += strlen(t); + snprintf(t, sizeof(t), ".%-.6ld ", (long)ipl->ipl_usec); + t += strlen(t); + + family = vtof(sl->isl_v); + + switch (sl->isl_type) + { + case ISL_NEW : + strcpy(t, "STATE:NEW "); + break; + + case ISL_CLONE : + strcpy(t, "STATE:CLONED "); + break; + + case ISL_EXPIRE : + if ((sl->isl_p == IPPROTO_TCP) && + (sl->isl_state[0] > IPF_TCPS_ESTABLISHED || + sl->isl_state[1] > IPF_TCPS_ESTABLISHED)) + strcpy(t, "STATE:CLOSE "); + else + strcpy(t, "STATE:EXPIRE "); + break; + + case ISL_FLUSH : + strcpy(t, "STATE:FLUSH "); + break; + + case ISL_INTERMEDIATE : + strcpy(t, "STATE:INTERMEDIATE "); + break; + + case ISL_REMOVE : + strcpy(t, "STATE:REMOVE "); + break; + + case ISL_KILLED : + strcpy(t, "STATE:KILLED "); + break; + + case ISL_UNLOAD : + strcpy(t, "STATE:UNLOAD "); + break; + + default : + snprintf(t, sizeof(t), "Type: %d ", sl->isl_type); + break; + } + t += strlen(t); + + proto = getlocalproto(sl->isl_p); + + if (sl->isl_p == IPPROTO_TCP || sl->isl_p == IPPROTO_UDP) { + snprintf(t, sizeof(t), "%s,%s -> ", + hostname(family, (u_32_t *)&sl->isl_src), + portlocalname(res, proto, (u_int)sl->isl_sport)); + t += strlen(t); + snprintf(t, sizeof(t), "%s,%s PR %s", + hostname(family, (u_32_t *)&sl->isl_dst), + portlocalname(res, proto, (u_int)sl->isl_dport), proto); + } else if (sl->isl_p == IPPROTO_ICMP) { + snprintf(t, sizeof(t), "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); + t += strlen(t); + snprintf(t, sizeof(t), "%s PR icmp %d", + hostname(family, (u_32_t *)&sl->isl_dst), + sl->isl_itype); + } else if (sl->isl_p == IPPROTO_ICMPV6) { + snprintf(t, sizeof(t), "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); + t += strlen(t); + snprintf(t, sizeof(t), "%s PR icmpv6 %d", + hostname(family, (u_32_t *)&sl->isl_dst), + sl->isl_itype); + } else { + snprintf(t, sizeof(t), "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); + t += strlen(t); + snprintf(t, sizeof(t), "%s PR %s", + hostname(family, (u_32_t *)&sl->isl_dst), proto); + } + t += strlen(t); + if (sl->isl_tag != FR_NOLOGTAG) { + snprintf(t, sizeof(t), " tag %u", sl->isl_tag); + t += strlen(t); + } + if (sl->isl_type != ISL_NEW) { + snprintf(t, sizeof(t), +#ifdef USE_QUAD_T +#ifdef PRId64 + " Forward: Pkts in %" PRId64 " Bytes in %" PRId64 + " Pkts out %" PRId64 " Bytes out %" PRId64 + " Backward: Pkts in %" PRId64 " Bytes in %" PRId64 + " Pkts out %" PRId64 " Bytes out %" PRId64, +#else + " Forward: Pkts in %qd Bytes in %qd Pkts out %qd Bytes out %qd Backward: Pkts in %qd Bytes in %qd Pkts out %qd Bytes out %qd", +#endif /* PRId64 */ +#else + " Forward: Pkts in %ld Bytes in %ld Pkts out %ld Bytes out %ld Backward: Pkts in %ld Bytes in %ld Pkts out %ld Bytes out %ld", +#endif + sl->isl_pkts[0], sl->isl_bytes[0], + sl->isl_pkts[1], sl->isl_bytes[1], + sl->isl_pkts[2], sl->isl_bytes[2], + sl->isl_pkts[3], sl->isl_bytes[3]); + + t += strlen(t); + } + + *t++ = '\n'; + *t++ = '\0'; + if (ipmonopts & IPMON_SYSLOG) + syslog(LOG_INFO, "%s", line); + else if (conf->log != NULL) + (void) fprintf(conf->log, "%s", line); +} + + +static void print_log(conf, log, buf, blen) + config_t *conf; + logsource_t *log; + char *buf; + int blen; +{ + char *bp, *bpo; + iplog_t *ipl; + int psize; + + bp = NULL; + bpo = NULL; + + while (blen > 0) { + ipl = (iplog_t *)buf; + if ((u_long)ipl & (sizeof(long)-1)) { + if (bp) + bpo = bp; + bp = (char *)malloc(blen); + bcopy((char *)ipl, bp, blen); + if (bpo) { + free(bpo); + bpo = NULL; + } + buf = bp; + continue; + } + + psize = ipl->ipl_dsize; + if (psize > blen) + break; + + if (conf->blog != NULL) { + fwrite(buf, psize, 1, conf->blog); + fflush(conf->blog); + } + + if (log->logtype == IPL_LOGIPF) { + if (ipl->ipl_magic == IPL_MAGIC) + print_ipflog(conf, buf, psize); + + } else if (log->logtype == IPL_LOGNAT) { + if (ipl->ipl_magic == IPL_MAGIC_NAT) + print_natlog(conf, buf, psize); + + } else if (log->logtype == IPL_LOGSTATE) { + if (ipl->ipl_magic == IPL_MAGIC_STATE) + print_statelog(conf, buf, psize); + } + + blen -= psize; + buf += psize; + } + if (bp) + free(bp); + return; +} + + +static void print_ipflog(conf, buf, blen) + config_t *conf; + char *buf; + int blen; +{ + static u_32_t seqnum = 0; + int i, f, lvl, res, len, off, plen, ipoff, defaction; + struct icmp *icmp; + struct icmp *ic; + char *t, *proto; + ip_t *ipc, *ip; + struct tm *tm; + u_32_t *s, *d; + u_short hl, p; + ipflog_t *ipf; + iplog_t *ipl; + tcphdr_t *tp; +#ifdef USE_INET6 + struct ip6_ext *ehp; + u_short ehl; + ip6_t *ip6; + int go; +#endif + + ipl = (iplog_t *)buf; + if (ipl->ipl_seqnum != seqnum) { + if ((ipmonopts & IPMON_SYSLOG) != 0) { + syslog(LOG_WARNING, + "missed %u ipf log entries: %u %u", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } else { + (void) fprintf(conf->log, + "missed %u ipf log entries: %u %u\n", + ipl->ipl_seqnum - seqnum, seqnum, + ipl->ipl_seqnum); + } + } + seqnum = ipl->ipl_seqnum + ipl->ipl_count; + + ipf = (ipflog_t *)((char *)buf + sizeof(*ipl)); + ip = (ip_t *)((char *)ipf + sizeof(*ipf)); + f = ipf->fl_family; + res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0; + t = line; + *t = '\0'; + tm = get_tm(ipl->ipl_sec); + + len = sizeof(line); + if (!(ipmonopts & IPMON_SYSLOG)) { + (void) strftime(t, len, "%d/%m/%Y ", tm); + i = strlen(t); + len -= i; + t += i; + } + (void) strftime(t, len, "%T", tm); + t += strlen(t); + snprintf(t, sizeof(t), ".%-.6ld ", (long)ipl->ipl_usec); + t += strlen(t); + if (ipl->ipl_count > 1) { + snprintf(t, sizeof(t), "%dx ", ipl->ipl_count); + t += strlen(t); + } + { + char ifname[sizeof(ipf->fl_ifname) + 1]; + + strncpy(ifname, ipf->fl_ifname, sizeof(ipf->fl_ifname)); + ifname[sizeof(ipf->fl_ifname)] = '\0'; + snprintf(t, sizeof(t), "%s", ifname); + t += strlen(t); +# if SOLARIS + if (ISALPHA(*(t - 1))) { + snprintf(t, sizeof(t), "%d", ipf->fl_unit); + t += strlen(t); + } +# endif + } + if ((ipf->fl_group[0] == (char)~0) && (ipf->fl_group[1] == '\0')) + strcat(t, " @-1:"); + else if (ipf->fl_group[0] == '\0') + (void) strcpy(t, " @0:"); + else + snprintf(t, sizeof(t), " @%s:", ipf->fl_group); + t += strlen(t); + if (ipf->fl_rule == 0xffffffff) + strcat(t, "-1 "); + else + snprintf(t, sizeof(t), "%u ", ipf->fl_rule + 1); + t += strlen(t); + + lvl = LOG_NOTICE; + + if (ipf->fl_lflags & FI_SHORT) { + *t++ = 'S'; + lvl = LOG_ERR; + } + + if (FR_ISPASS(ipf->fl_flags)) { + if (ipf->fl_flags & FR_LOGP) + *t++ = 'p'; + else + *t++ = 'P'; + } else if (FR_ISBLOCK(ipf->fl_flags)) { + if (ipf->fl_flags & FR_LOGB) + *t++ = 'b'; + else + *t++ = 'B'; + lvl = LOG_WARNING; + } else if ((ipf->fl_flags & FR_LOGMASK) == FR_LOG) { + *t++ = 'L'; + lvl = LOG_INFO; + } else if (ipf->fl_flags & FF_LOGNOMATCH) { + *t++ = 'n'; + } else { + *t++ = '?'; + lvl = LOG_EMERG; + } + if (ipf->fl_loglevel != 0xffff) + lvl = ipf->fl_loglevel; + *t++ = ' '; + *t = '\0'; + + if (f == AF_INET) { + hl = IP_HL(ip) << 2; + ipoff = ntohs(ip->ip_off); + off = ipoff & IP_OFFMASK; + p = (u_short)ip->ip_p; + s = (u_32_t *)&ip->ip_src; + d = (u_32_t *)&ip->ip_dst; + plen = ntohs(ip->ip_len); + } else +#ifdef USE_INET6 + if (f == AF_INET6) { + off = 0; + ipoff = 0; + hl = sizeof(ip6_t); + ip6 = (ip6_t *)ip; + p = (u_short)ip6->ip6_nxt; + s = (u_32_t *)&ip6->ip6_src; + d = (u_32_t *)&ip6->ip6_dst; + plen = hl + ntohs(ip6->ip6_plen); + go = 1; + ehp = (struct ip6_ext *)((char *)ip6 + hl); + while (go == 1) { + switch (p) + { + case IPPROTO_HOPOPTS : + case IPPROTO_MOBILITY : + case IPPROTO_DSTOPTS : + case IPPROTO_ROUTING : + case IPPROTO_AH : + p = ehp->ip6e_nxt; + ehl = 8 + (ehp->ip6e_len << 3); + hl += ehl; + ehp = (struct ip6_ext *)((char *)ehp + ehl); + break; + case IPPROTO_FRAGMENT : + hl += sizeof(struct ip6_frag); + /* FALLTHROUGH */ + default : + go = 0; + break; + } + } + } else +#endif + { + goto printipflog; + } + proto = getlocalproto(p); + + if ((p == IPPROTO_TCP || p == IPPROTO_UDP) && !off) { + tp = (tcphdr_t *)((char *)ip + hl); + if (!(ipf->fl_lflags & FI_SHORT)) { + snprintf(t, sizeof(t), "%s,%s -> ", hostname(f, s), + portlocalname(res, proto, (u_int)tp->th_sport)); + t += strlen(t); + snprintf(t, sizeof(t), "%s,%s PR %s len %hu %hu", + hostname(f, d), + portlocalname(res, proto, (u_int)tp->th_dport), + proto, hl, plen); + t += strlen(t); + + if (p == IPPROTO_TCP) { + *t++ = ' '; + *t++ = '-'; + for (i = 0; tcpfl[i].value; i++) + if (tp->th_flags & tcpfl[i].value) + *t++ = tcpfl[i].flag; + if (ipmonopts & IPMON_VERBOSE) { + snprintf(t, sizeof(t), " %lu %lu %hu", + (u_long)(ntohl(tp->th_seq)), + (u_long)(ntohl(tp->th_ack)), + ntohs(tp->th_win)); + t += strlen(t); + } + } + *t = '\0'; + } else { + snprintf(t, sizeof(t), "%s -> ", hostname(f, s)); + t += strlen(t); + sprintf(t, "%s PR %s len %hu %hu", + hostname(f, d), proto, hl, plen); + } +#if defined(AF_INET6) && defined(IPPROTO_ICMPV6) + } else if ((p == IPPROTO_ICMPV6) && !off && (f == AF_INET6)) { + ic = (struct icmp *)((char *)ip + hl); + snprintf(t, sizeof(t), "%s -> ", hostname(f, s)); + t += strlen(t); + snprintf(t, sizeof(t), "%s PR icmpv6 len %hu %hu icmpv6 %s", + hostname(f, d), hl, plen, + icmpname6(ic->icmp_type, ic->icmp_code)); +#endif + } else if ((p == IPPROTO_ICMP) && !off && (f == AF_INET)) { + ic = (struct icmp *)((char *)ip + hl); + snprintf(t, sizeof(t), "%s -> ", hostname(f, s)); + t += strlen(t); + snprintf(t, sizeof(t), "%s PR icmp len %hu %hu icmp %s", + hostname(f, d), hl, plen, + icmpname(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; + i = ntohs(ipc->ip_len); + /* + * XXX - try to guess endian of ip_len in ICMP + * returned data. + */ + if (i > 1500) + i = ipc->ip_len; + ipoff = ntohs(ipc->ip_off); + proto = getlocalproto(ipc->ip_p); + + if (!(ipoff & IP_OFFMASK) && + ((ipc->ip_p == IPPROTO_TCP) || + (ipc->ip_p == IPPROTO_UDP))) { + tp = (tcphdr_t *)((char *)ipc + hl); + t += strlen(t); + snprintf(t, sizeof(t), " for %s,%s -", + HOSTNAMEV4(ipc->ip_src), + portlocalname(res, proto, + (u_int)tp->th_sport)); + t += strlen(t); + snprintf(t, sizeof(t), " %s,%s PR %s len %hu %hu", + HOSTNAMEV4(ipc->ip_dst), + portlocalname(res, proto, + (u_int)tp->th_dport), + proto, IP_HL(ipc) << 2, i); + } else if (!(ipoff & IP_OFFMASK) && + (ipc->ip_p == IPPROTO_ICMP)) { + icmp = (icmphdr_t *)((char *)ipc + hl); + + t += strlen(t); + snprintf(t, sizeof(t), " for %s -", + HOSTNAMEV4(ipc->ip_src)); + t += strlen(t); + snprintf(t, sizeof(t), + " %s PR icmp len %hu %hu icmp %d/%d", + HOSTNAMEV4(ipc->ip_dst), + IP_HL(ipc) << 2, i, + icmp->icmp_type, icmp->icmp_code); + } else { + t += strlen(t); + snprintf(t, sizeof(t), " for %s -", + HOSTNAMEV4(ipc->ip_src)); + t += strlen(t); + snprintf(t, sizeof(t), " %s PR %s len %hu (%hu)", + HOSTNAMEV4(ipc->ip_dst), proto, + IP_HL(ipc) << 2, i); + t += strlen(t); + if (ipoff & IP_OFFMASK) { + snprintf(t, sizeof(t), "(frag %d:%hu@%hu%s%s)", + ntohs(ipc->ip_id), + i - (IP_HL(ipc) << 2), + (ipoff & IP_OFFMASK) << 3, + ipoff & IP_MF ? "+" : "", + ipoff & IP_DF ? "-" : ""); + } + } + + } + } else { + snprintf(t, sizeof(t), "%s -> ", hostname(f, s)); + t += strlen(t); + snprintf(t, sizeof(t), "%s PR %s len %hu (%hu)", + hostname(f, d), proto, hl, plen); + t += strlen(t); + if (off & IP_OFFMASK) + snprintf(t, sizeof(t), " (frag %d:%hu@%hu%s%s)", + ntohs(ip->ip_id), + plen - hl, (off & IP_OFFMASK) << 3, + ipoff & IP_MF ? "+" : "", + ipoff & IP_DF ? "-" : ""); + } + t += strlen(t); + +printipflog: + if (ipf->fl_flags & FR_KEEPSTATE) { + (void) strcpy(t, " K-S"); + t += strlen(t); + } + + if (ipf->fl_flags & FR_KEEPFRAG) { + (void) strcpy(t, " K-F"); + t += strlen(t); + } + + if (ipf->fl_dir == 0) + strcpy(t, " IN"); + else if (ipf->fl_dir == 1) + strcpy(t, " OUT"); + t += strlen(t); + if (ipf->fl_logtag != 0) { + snprintf(t, sizeof(t), " log-tag %d", ipf->fl_logtag); + t += strlen(t); + } + if (ipf->fl_nattag.ipt_num[0] != 0) { + strcpy(t, " nat-tag "); + t += strlen(t); + strncpy(t, ipf->fl_nattag.ipt_tag, sizeof(ipf->fl_nattag)); + t += strlen(t); + } + if ((ipf->fl_lflags & FI_LOWTTL) != 0) { + strcpy(t, " low-ttl"); + t += 8; + } + if ((ipf->fl_lflags & FI_OOW) != 0) { + strcpy(t, " OOW"); + t += 4; + } + if ((ipf->fl_lflags & FI_BAD) != 0) { + strcpy(t, " bad"); + t += 4; + } + if ((ipf->fl_lflags & FI_NATED) != 0) { + strcpy(t, " NAT"); + t += 4; + } + if ((ipf->fl_lflags & FI_BADNAT) != 0) { + strcpy(t, " bad-NAT"); + t += 8; + } + if ((ipf->fl_lflags & FI_BADSRC) != 0) { + strcpy(t, " bad-src"); + t += 8; + } + if ((ipf->fl_lflags & FI_MULTICAST) != 0) { + strcpy(t, " multicast"); + t += 10; + } + if ((ipf->fl_lflags & FI_BROADCAST) != 0) { + strcpy(t, " broadcast"); + t += 10; + } + if ((ipf->fl_lflags & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST)) == + FI_MBCAST) { + strcpy(t, " mbcast"); + t += 7; + } + if (ipf->fl_breason != 0) { + strcpy(t, " reason:"); + t += 8; + strcpy(t, reasons[ipf->fl_breason]); + t += strlen(reasons[ipf->fl_breason]); + } + *t++ = '\n'; + *t++ = '\0'; + defaction = 0; + if (conf->cfile != NULL) + defaction = check_action(buf, line, ipmonopts, lvl); + + if (defaction == 0) { + if (ipmonopts & IPMON_SYSLOG) { + syslog(lvl, "%s", line); + } else if (conf->log != NULL) { + (void) fprintf(conf->log, "%s", line); + } + + if (ipmonopts & IPMON_HEXHDR) { + dumphex(conf->log, ipmonopts, buf, + sizeof(iplog_t) + sizeof(*ipf)); + } + if (ipmonopts & IPMON_HEXBODY) { + dumphex(conf->log, ipmonopts, (char *)ip, + ipf->fl_plen + ipf->fl_hlen); + } else if ((ipmonopts & IPMON_LOGBODY) && + (ipf->fl_flags & FR_LOGBODY)) { + dumphex(conf->log, ipmonopts, (char *)ip + ipf->fl_hlen, + ipf->fl_plen); + } + } +} + + +static void usage(prog) + char *prog; +{ + fprintf(stderr, "Usage: %s [ -abDFhnpstvxX ] [ -B <binary-logfile> ] [ -C <config-file> ]\n" + "\t[ -f <device> ] [ -L <facility> ] [ -N <device> ]\n" + "\t[ -o [NSI] ] [ -O [NSI] ] [ -P <pidfile> ] [ -S <device> ]\n" + "\t[ <filename> ]\n", prog); + exit(1); +} + + +static void write_pid(file) + char *file; +{ + FILE *fp = NULL; + int fd; + + if ((fd = open(file, O_CREAT|O_TRUNC|O_WRONLY, 0644)) >= 0) { + fp = fdopen(fd, "w"); + if (fp == NULL) { + close(fd); + fprintf(stderr, + "unable to open/create pid file: %s\n", file); + return; + } + fprintf(fp, "%d", getpid()); + fclose(fp); + } +} + + +static void flushlogs(file, log) + char *file; + FILE *log; +{ + int fd, flushed = 0; + + if ((fd = open(file, O_RDWR)) == -1) { + (void) fprintf(stderr, "%s: open: %s\n", + file, STRERROR(errno)); + exit(1); + } + + if (ioctl(fd, SIOCIPFFB, &flushed) == 0) { + printf("%d bytes flushed from log buffer\n", + flushed); + fflush(stdout); + } else + ipferror(fd, "SIOCIPFFB"); + (void) close(fd); + + if (flushed) { + if (ipmonopts & IPMON_SYSLOG) { + syslog(LOG_INFO, "%d bytes flushed from log\n", + flushed); + } else if ((log != stdout) && (log != NULL)) { + fprintf(log, "%d bytes flushed from log\n", flushed); + } + } +} + + +static void logopts(turnon, options) + int turnon; + char *options; +{ + int flags = 0; + char *s; + + for (s = options; *s; s++) + { + switch (*s) + { + case 'N' : + flags |= IPMON_NAT; + break; + case 'S' : + flags |= IPMON_STATE; + break; + case 'I' : + flags |= IPMON_FILTER; + break; + default : + fprintf(stderr, "Unknown log option %c\n", *s); + exit(1); + } + } + + if (turnon) + ipmonopts |= flags; + else + ipmonopts &= ~(flags); +} + +static void initconfig(config_t *conf) +{ + int i; + + memset(conf, 0, sizeof(*conf)); + + conf->log = stdout; + conf->maxfd = -1; + + for (i = 0; i < 3; i++) { + conf->logsrc[i].fd = -1; + conf->logsrc[i].logtype = -1; + conf->logsrc[i].regular = -1; + } + + conf->logsrc[0].file = IPL_NAME; + conf->logsrc[1].file = IPNAT_NAME; + conf->logsrc[2].file = IPSTATE_NAME; + + add_doing(&executesaver); + add_doing(&snmpv1saver); + add_doing(&snmpv2saver); + add_doing(&syslogsaver); + add_doing(&filesaver); + add_doing(¬hingsaver); +} + + +int main(argc, argv) + int argc; + char *argv[]; +{ + int doread, c, make_daemon = 0; + char *prog; + config_t config; + + prog = strrchr(argv[0], '/'); + if (prog == NULL) + prog = argv[0]; + else + prog++; + + initconfig(&config); + + while ((c = getopt(argc, argv, + "?abB:C:Df:FhL:nN:o:O:pP:sS:tvxX")) != -1) + switch (c) + { + case 'a' : + ipmonopts |= IPMON_LOGALL; + config.logsrc[0].logtype = IPL_LOGIPF; + config.logsrc[1].logtype = IPL_LOGNAT; + config.logsrc[2].logtype = IPL_LOGSTATE; + break; + case 'b' : + ipmonopts |= IPMON_LOGBODY; + break; + case 'B' : + config.bfile = optarg; + config.blog = fopen(optarg, "a"); + break; + case 'C' : + config.cfile = optarg; + break; + case 'D' : + make_daemon = 1; + break; + case 'f' : case 'I' : + ipmonopts |= IPMON_FILTER; + config.logsrc[0].logtype = IPL_LOGIPF; + config.logsrc[0].file = optarg; + break; + case 'F' : + flushlogs(config.logsrc[0].file, config.log); + flushlogs(config.logsrc[1].file, config.log); + flushlogs(config.logsrc[2].file, config.log); + break; + case 'L' : + logfac = fac_findname(optarg); + if (logfac == -1) { + fprintf(stderr, + "Unknown syslog facility '%s'\n", + optarg); + exit(1); + } + break; + case 'n' : + ipmonopts |= IPMON_RESOLVE; + opts &= ~OPT_NORESOLVE; + break; + case 'N' : + ipmonopts |= IPMON_NAT; + config.logsrc[1].logtype = IPL_LOGNAT; + config.logsrc[1].file = optarg; + break; + case 'o' : case 'O' : + logopts(c == 'o', optarg); + if (ipmonopts & IPMON_FILTER) + config.logsrc[0].logtype = IPL_LOGIPF; + if (ipmonopts & IPMON_NAT) + config.logsrc[1].logtype = IPL_LOGNAT; + if (ipmonopts & IPMON_STATE) + config.logsrc[2].logtype = IPL_LOGSTATE; + break; + case 'p' : + ipmonopts |= IPMON_PORTNUM; + break; + case 'P' : + pidfile = optarg; + break; + case 's' : + ipmonopts |= IPMON_SYSLOG; + config.log = NULL; + break; + case 'S' : + ipmonopts |= IPMON_STATE; + config.logsrc[2].logtype = IPL_LOGSTATE; + config.logsrc[2].file = optarg; + break; + case 't' : + ipmonopts |= IPMON_TAIL; + break; + case 'v' : + ipmonopts |= IPMON_VERBOSE; + break; + case 'x' : + ipmonopts |= IPMON_HEXBODY; + break; + case 'X' : + ipmonopts |= IPMON_HEXHDR; + break; + default : + case 'h' : + case '?' : + usage(argv[0]); + } + + if (ipmonopts & IPMON_SYSLOG) + openlog(prog, LOG_NDELAY|LOG_PID, logfac); + + init_tabs(); + if (config.cfile) + if (load_config(config.cfile) == -1) { + unload_config(); + exit(1); + } + + /* + * Default action is to only open the filter log file. + */ + if ((config.logsrc[0].logtype == -1) && + (config.logsrc[0].logtype == -1) && + (config.logsrc[0].logtype == -1)) + config.logsrc[0].logtype = IPL_LOGIPF; + + openlogs(&config); + + if (!(ipmonopts & IPMON_SYSLOG)) { + config.file = argv[optind]; + config.log = config.file ? fopen(config.file, "a") : stdout; + if (config.log == NULL) { + (void) fprintf(stderr, "%s: fopen: %s\n", + argv[optind], STRERROR(errno)); + exit(1); + /* NOTREACHED */ + } + setvbuf(config.log, NULL, _IONBF, 0); + } else { + config.log = NULL; + } + + if (make_daemon && + ((config.log != stdout) || (ipmonopts & IPMON_SYSLOG))) { +#ifdef BSD + daemon(0, !(ipmonopts & IPMON_SYSLOG)); +#else + int pid; + + switch (fork()) + { + case -1 : + (void) fprintf(stderr, "%s: fork() failed: %s\n", + argv[0], STRERROR(errno)); + exit(1); + /* NOTREACHED */ + case 0 : + break; + default : + exit(0); + } + + setsid(); + if ((ipmonopts & IPMON_SYSLOG)) + close(2); +#endif /* !BSD */ + close(0); + close(1); + write_pid(pidfile); + } + + signal(SIGHUP, handlehup); + + for (doread = 1; doread; ) + doread = read_loginfo(&config); + + unload_config(); + + return(0); + /* NOTREACHED */ +} + + +static void openlogs(config_t *conf) +{ + logsource_t *l; + struct stat sb; + int i; + + for (i = 0; i < 3; i++) { + l = &conf->logsrc[i]; + if (l->logtype == -1) + continue; + if (!strcmp(l->file, "-")) + l->fd = 0; + else { + if ((l->fd= open(l->file, O_RDONLY)) == -1) { + (void) fprintf(stderr, + "%s: open: %s\n", l->file, + STRERROR(errno)); + exit(1); + /* NOTREACHED */ + } + + if (fstat(l->fd, &sb) == -1) { + (void) fprintf(stderr, "%d: fstat: %s\n", + l->fd, STRERROR(errno)); + exit(1); + /* NOTREACHED */ + } + + l->regular = !S_ISCHR(sb.st_mode); + if (l->regular) + l->size = sb.st_size; + + FD_SET(l->fd, &conf->fdmr); + if (l->fd > conf->maxfd) + conf->maxfd = l->fd; + } + } +} + + +static int read_loginfo(config_t *conf) +{ + iplog_t buf[DEFAULT_IPFLOGSIZE/sizeof(iplog_t)+1]; + int n, tr, nr, i; + logsource_t *l; + fd_set fdr; + + fdr = conf->fdmr; + + n = select(conf->maxfd + 1, &fdr, NULL, NULL, NULL); + if (n == 0) + return 1; + if (n == -1) { + if (errno == EINTR) + return 1; + return -1; + } + + for (i = 0, nr = 0; i < 3; i++) { + l = &conf->logsrc[i]; + + if ((l->logtype == -1) || !FD_ISSET(l->fd, &fdr)) + continue; + + tr = 0; + if (l->regular) { + tr = (lseek(l->fd, 0, SEEK_CUR) < l->size); + if (!tr && !(ipmonopts & IPMON_TAIL)) + return 0; + } + + n = 0; + tr = read_log(l->fd, &n, (char *)buf, sizeof(buf)); + if (donehup) { + if (conf->file != NULL) { + if (conf->log != NULL) { + fclose(conf->log); + conf->log = NULL; + } + conf->log = fopen(conf->file, "a"); + } + + if (conf->bfile != NULL) { + if (conf->blog != NULL) { + fclose(conf->blog); + conf->blog = NULL; + } + conf->blog = fopen(conf->bfile, "a"); + } + + init_tabs(); + if (conf->cfile != NULL) + load_config(conf->cfile); + donehup = 0; + } + + switch (tr) + { + case -1 : + if (ipmonopts & IPMON_SYSLOG) + syslog(LOG_CRIT, "read: %m\n"); + else { + ipferror(l->fd, "read"); + } + return 0; + case 1 : + if (ipmonopts & IPMON_SYSLOG) + syslog(LOG_CRIT, "aborting logging\n"); + else if (conf->log != NULL) + fprintf(conf->log, "aborting logging\n"); + return 0; + case 2 : + break; + case 0 : + nr += tr; + if (n > 0) { + print_log(conf, l, (char *)buf, n); + if (!(ipmonopts & IPMON_SYSLOG)) + fflush(conf->log); + } + break; + } + } + + if (!nr && (ipmonopts & IPMON_TAIL)) + sleep(1); + + return 1; +} diff --git a/sbin/ipf/ipmon/ipmon_y.y b/sbin/ipf/ipmon/ipmon_y.y new file mode 100644 index 000000000000..e734c1c8c1f1 --- /dev/null +++ b/sbin/ipf/ipmon/ipmon_y.y @@ -0,0 +1,1052 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +%{ +#include "ipf.h" +#include <syslog.h> +#undef OPT_NAT +#undef OPT_VERBOSE +#include "ipmon_l.h" +#include "ipmon.h" + +#include <dlfcn.h> + +#define YYDEBUG 1 + +extern void yyerror(char *); +extern int yyparse(void); +extern int yylex(void); +extern int yydebug; +extern FILE *yyin; +extern int yylineNum; +extern int ipmonopts; + +typedef struct opt_s { + struct opt_s *o_next; + int o_line; + int o_type; + int o_num; + char *o_str; + struct in_addr o_ip; + int o_logfac; + int o_logpri; +} opt_t; + +static void build_action(opt_t *, ipmon_doing_t *); +static opt_t *new_opt(int); +static void free_action(ipmon_action_t *); +static void print_action(ipmon_action_t *); +static int find_doing(char *); +static ipmon_doing_t *build_doing(char *, char *); +static void print_match(ipmon_action_t *); +static int install_saver(char *, char *); + +static ipmon_action_t *alist = NULL; + +ipmon_saver_int_t *saverlist = NULL; +%} + +%union { + char *str; + u_32_t num; + struct in_addr addr; + struct opt_s *opt; + union i6addr ip6; + struct ipmon_doing_s *ipmd; +} + +%token <num> YY_NUMBER YY_HEX +%token <str> YY_STR +%token <ip6> YY_IPV6 +%token YY_COMMENT +%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT +%token YY_RANGE_OUT YY_RANGE_IN + +%token IPM_MATCH IPM_BODY IPM_COMMENT IPM_DIRECTION IPM_DSTIP IPM_DSTPORT +%token IPM_EVERY IPM_GROUP IPM_INTERFACE IPM_IN IPM_NO IPM_OUT IPM_LOADACTION +%token IPM_PACKET IPM_PACKETS IPM_POOL IPM_PROTOCOL IPM_RESULT IPM_RULE +%token IPM_SECOND IPM_SECONDS IPM_SRCIP IPM_SRCPORT IPM_LOGTAG IPM_WITH +%token IPM_DO IPM_DOING IPM_TYPE IPM_NAT +%token IPM_STATE IPM_NATTAG IPM_IPF +%type <addr> ipv4 +%type <opt> direction dstip dstport every group interface +%type <opt> protocol result rule srcip srcport logtag matching +%type <opt> matchopt nattag type +%type <num> typeopt +%type <ipmd> doopt doing + +%% +file: action + | file action + ; + +action: line ';' + | assign ';' + | IPM_COMMENT + | YY_COMMENT + ; + +line: IPM_MATCH '{' matching ';' '}' IPM_DO '{' doing ';' '}' + { build_action($3, $8); + resetlexer(); + } + | IPM_LOADACTION YY_STR YY_STR { if (install_saver($2, $3)) + yyerror("install saver"); + } + ; + +assign: YY_STR assigning YY_STR { set_variable($1, $3); + resetlexer(); + free($1); + free($3); + yyvarnext = 0; + } + ; + +assigning: + '=' { yyvarnext = 1; } + ; + +matching: + matchopt { $$ = $1; } + | matchopt ',' matching { $1->o_next = $3; $$ = $1; } + ; + +matchopt: + direction { $$ = $1; } + | dstip { $$ = $1; } + | dstport { $$ = $1; } + | every { $$ = $1; } + | group { $$ = $1; } + | interface { $$ = $1; } + | protocol { $$ = $1; } + | result { $$ = $1; } + | rule { $$ = $1; } + | srcip { $$ = $1; } + | srcport { $$ = $1; } + | logtag { $$ = $1; } + | nattag { $$ = $1; } + | type { $$ = $1; } + ; + +doing: + doopt { $$ = $1; } + | doopt ',' doing { $1->ipmd_next = $3; $$ = $1; } + ; + +doopt: + YY_STR { if (find_doing($1) != IPM_DOING) + yyerror("unknown action"); + } + '(' YY_STR ')' { $$ = build_doing($1, $4); + if ($$ == NULL) + yyerror("action building"); + } + | YY_STR { if (find_doing($1) == IPM_DOING) + $$ = build_doing($1, NULL); + } + ; + +direction: + IPM_DIRECTION '=' IPM_IN { $$ = new_opt(IPM_DIRECTION); + $$->o_num = IPM_IN; } + | IPM_DIRECTION '=' IPM_OUT { $$ = new_opt(IPM_DIRECTION); + $$->o_num = IPM_OUT; } + ; + +dstip: IPM_DSTIP '=' ipv4 '/' YY_NUMBER { $$ = new_opt(IPM_DSTIP); + $$->o_ip = $3; + $$->o_num = $5; } + ; + +dstport: + IPM_DSTPORT '=' YY_NUMBER { $$ = new_opt(IPM_DSTPORT); + $$->o_num = $3; } + | IPM_DSTPORT '=' YY_STR { $$ = new_opt(IPM_DSTPORT); + $$->o_str = $3; } + ; + +every: IPM_EVERY IPM_SECOND { $$ = new_opt(IPM_SECOND); + $$->o_num = 1; } + | IPM_EVERY YY_NUMBER IPM_SECONDS { $$ = new_opt(IPM_SECOND); + $$->o_num = $2; } + | IPM_EVERY IPM_PACKET { $$ = new_opt(IPM_PACKET); + $$->o_num = 1; } + | IPM_EVERY YY_NUMBER IPM_PACKETS { $$ = new_opt(IPM_PACKET); + $$->o_num = $2; } + ; + +group: IPM_GROUP '=' YY_NUMBER { $$ = new_opt(IPM_GROUP); + $$->o_num = $3; } + | IPM_GROUP '=' YY_STR { $$ = new_opt(IPM_GROUP); + $$->o_str = $3; } + ; + +interface: + IPM_INTERFACE '=' YY_STR { $$ = new_opt(IPM_INTERFACE); + $$->o_str = $3; } + ; + +logtag: IPM_LOGTAG '=' YY_NUMBER { $$ = new_opt(IPM_LOGTAG); + $$->o_num = $3; } + ; + +nattag: IPM_NATTAG '=' YY_STR { $$ = new_opt(IPM_NATTAG); + $$->o_str = $3; } + ; + +protocol: + IPM_PROTOCOL '=' YY_NUMBER { $$ = new_opt(IPM_PROTOCOL); + $$->o_num = $3; } + | IPM_PROTOCOL '=' YY_STR { $$ = new_opt(IPM_PROTOCOL); + $$->o_num = getproto($3); + free($3); + } + ; + +result: IPM_RESULT '=' YY_STR { $$ = new_opt(IPM_RESULT); + $$->o_str = $3; } + ; + +rule: IPM_RULE '=' YY_NUMBER { $$ = new_opt(IPM_RULE); + $$->o_num = YY_NUMBER; } + ; + +srcip: IPM_SRCIP '=' ipv4 '/' YY_NUMBER { $$ = new_opt(IPM_SRCIP); + $$->o_ip = $3; + $$->o_num = $5; } + ; + +srcport: + IPM_SRCPORT '=' YY_NUMBER { $$ = new_opt(IPM_SRCPORT); + $$->o_num = $3; } + | IPM_SRCPORT '=' YY_STR { $$ = new_opt(IPM_SRCPORT); + $$->o_str = $3; } + ; + +type: IPM_TYPE '=' typeopt { $$ = new_opt(IPM_TYPE); + $$->o_num = $3; } + ; + +typeopt: + IPM_IPF { $$ = IPL_MAGIC; } + | IPM_NAT { $$ = IPL_MAGIC_NAT; } + | IPM_STATE { $$ = IPL_MAGIC_STATE; } + ; + + + +ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER + { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { + yyerror("Invalid octet string for IP address"); + return 0; + } + $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; + $$.s_addr = htonl($$.s_addr); + } +%% +static struct wordtab yywords[] = { + { "body", IPM_BODY }, + { "direction", IPM_DIRECTION }, + { "do", IPM_DO }, + { "dstip", IPM_DSTIP }, + { "dstport", IPM_DSTPORT }, + { "every", IPM_EVERY }, + { "group", IPM_GROUP }, + { "in", IPM_IN }, + { "interface", IPM_INTERFACE }, + { "ipf", IPM_IPF }, + { "load_action",IPM_LOADACTION }, + { "logtag", IPM_LOGTAG }, + { "match", IPM_MATCH }, + { "nat", IPM_NAT }, + { "nattag", IPM_NATTAG }, + { "no", IPM_NO }, + { "out", IPM_OUT }, + { "packet", IPM_PACKET }, + { "packets", IPM_PACKETS }, + { "protocol", IPM_PROTOCOL }, + { "result", IPM_RESULT }, + { "rule", IPM_RULE }, + { "second", IPM_SECOND }, + { "seconds", IPM_SECONDS }, + { "srcip", IPM_SRCIP }, + { "srcport", IPM_SRCPORT }, + { "state", IPM_STATE }, + { "with", IPM_WITH }, + { NULL, 0 } +}; + +static int macflags[17][2] = { + { IPM_DIRECTION, IPMAC_DIRECTION }, + { IPM_DSTIP, IPMAC_DSTIP }, + { IPM_DSTPORT, IPMAC_DSTPORT }, + { IPM_GROUP, IPMAC_GROUP }, + { IPM_INTERFACE, IPMAC_INTERFACE }, + { IPM_LOGTAG, IPMAC_LOGTAG }, + { IPM_NATTAG, IPMAC_NATTAG }, + { IPM_PACKET, IPMAC_EVERY }, + { IPM_PROTOCOL, IPMAC_PROTOCOL }, + { IPM_RESULT, IPMAC_RESULT }, + { IPM_RULE, IPMAC_RULE }, + { IPM_SECOND, IPMAC_EVERY }, + { IPM_SRCIP, IPMAC_SRCIP }, + { IPM_SRCPORT, IPMAC_SRCPORT }, + { IPM_TYPE, IPMAC_TYPE }, + { IPM_WITH, IPMAC_WITH }, + { 0, 0 } +}; + +static opt_t * +new_opt(type) + int type; +{ + opt_t *o; + + o = (opt_t *)calloc(1, sizeof(*o)); + o->o_type = type; + o->o_line = yylineNum; + o->o_logfac = -1; + o->o_logpri = -1; + return o; +} + +static void +build_action(olist, todo) + opt_t *olist; + ipmon_doing_t *todo; +{ + ipmon_action_t *a; + opt_t *o; + int i; + + a = (ipmon_action_t *)calloc(1, sizeof(*a)); + if (a == NULL) + return; + + while ((o = olist) != NULL) { + /* + * Check to see if the same comparator is being used more than + * once per matching statement. + */ + for (i = 0; macflags[i][0]; i++) + if (macflags[i][0] == o->o_type) + break; + if (macflags[i][1] & a->ac_mflag) { + fprintf(stderr, "%s redfined on line %d\n", + yykeytostr(o->o_type), yylineNum); + if (o->o_str != NULL) + free(o->o_str); + olist = o->o_next; + free(o); + continue; + } + + a->ac_mflag |= macflags[i][1]; + + switch (o->o_type) + { + case IPM_DIRECTION : + a->ac_direction = o->o_num; + break; + case IPM_DSTIP : + a->ac_dip = o->o_ip.s_addr; + a->ac_dmsk = htonl(0xffffffff << (32 - o->o_num)); + break; + case IPM_DSTPORT : + a->ac_dport = htons(o->o_num); + break; + case IPM_INTERFACE : + a->ac_iface = o->o_str; + o->o_str = NULL; + break; + case IPM_GROUP : + if (o->o_str != NULL) + strncpy(a->ac_group, o->o_str, FR_GROUPLEN); + else + snprintf(a->ac_group, FR_GROUPLEN, "%d", o->o_num); + break; + case IPM_LOGTAG : + a->ac_logtag = o->o_num; + break; + case IPM_NATTAG : + strncpy(a->ac_nattag, o->o_str, sizeof(a->ac_nattag)); + break; + case IPM_PACKET : + a->ac_packet = o->o_num; + break; + case IPM_PROTOCOL : + a->ac_proto = o->o_num; + break; + case IPM_RULE : + a->ac_rule = o->o_num; + break; + case IPM_RESULT : + if (!strcasecmp(o->o_str, "pass")) + a->ac_result = IPMR_PASS; + else if (!strcasecmp(o->o_str, "block")) + a->ac_result = IPMR_BLOCK; + else if (!strcasecmp(o->o_str, "nomatch")) + a->ac_result = IPMR_NOMATCH; + else if (!strcasecmp(o->o_str, "log")) + a->ac_result = IPMR_LOG; + break; + case IPM_SECOND : + a->ac_second = o->o_num; + break; + case IPM_SRCIP : + a->ac_sip = o->o_ip.s_addr; + a->ac_smsk = htonl(0xffffffff << (32 - o->o_num)); + break; + case IPM_SRCPORT : + a->ac_sport = htons(o->o_num); + break; + case IPM_TYPE : + a->ac_type = o->o_num; + break; + case IPM_WITH : + break; + default : + break; + } + + olist = o->o_next; + if (o->o_str != NULL) + free(o->o_str); + free(o); + } + + a->ac_doing = todo; + a->ac_next = alist; + alist = a; + + if (ipmonopts & IPMON_VERBOSE) + print_action(a); +} + + +int +check_action(buf, log, opts, lvl) + char *buf, *log; + int opts, lvl; +{ + ipmon_action_t *a; + struct timeval tv; + ipmon_doing_t *d; + ipmon_msg_t msg; + ipflog_t *ipf; + tcphdr_t *tcp; + iplog_t *ipl; + int matched; + u_long t1; + ip_t *ip; + + matched = 0; + ipl = (iplog_t *)buf; + ipf = (ipflog_t *)(ipl +1); + ip = (ip_t *)(ipf + 1); + tcp = (tcphdr_t *)((char *)ip + (IP_HL(ip) << 2)); + + msg.imm_data = ipl; + msg.imm_dsize = ipl->ipl_dsize; + msg.imm_when = ipl->ipl_time.tv_sec; + msg.imm_msg = log; + msg.imm_msglen = strlen(log); + msg.imm_loglevel = lvl; + + for (a = alist; a != NULL; a = a->ac_next) { + verbose(0, "== checking config rule\n"); + if ((a->ac_mflag & IPMAC_DIRECTION) != 0) { + if (a->ac_direction == IPM_IN) { + if ((ipf->fl_flags & FR_INQUE) == 0) { + verbose(8, "-- direction not in\n"); + continue; + } + } else if (a->ac_direction == IPM_OUT) { + if ((ipf->fl_flags & FR_OUTQUE) == 0) { + verbose(8, "-- direction not out\n"); + continue; + } + } + } + + if ((a->ac_type != 0) && (a->ac_type != ipl->ipl_magic)) { + verbose(8, "-- type mismatch\n"); + continue; + } + + if ((a->ac_mflag & IPMAC_EVERY) != 0) { + gettimeofday(&tv, NULL); + t1 = tv.tv_sec - a->ac_lastsec; + if (tv.tv_usec <= a->ac_lastusec) + t1--; + if (a->ac_second != 0) { + if (t1 < a->ac_second) { + verbose(8, "-- too soon\n"); + continue; + } + a->ac_lastsec = tv.tv_sec; + a->ac_lastusec = tv.tv_usec; + } + + if (a->ac_packet != 0) { + if (a->ac_pktcnt == 0) + a->ac_pktcnt++; + else if (a->ac_pktcnt == a->ac_packet) { + a->ac_pktcnt = 0; + verbose(8, "-- packet count\n"); + continue; + } else { + a->ac_pktcnt++; + verbose(8, "-- packet count\n"); + continue; + } + } + } + + if ((a->ac_mflag & IPMAC_DSTIP) != 0) { + if ((ip->ip_dst.s_addr & a->ac_dmsk) != a->ac_dip) { + verbose(8, "-- dstip wrong\n"); + continue; + } + } + + if ((a->ac_mflag & IPMAC_DSTPORT) != 0) { + if (ip->ip_p != IPPROTO_UDP && + ip->ip_p != IPPROTO_TCP) { + verbose(8, "-- not port protocol\n"); + continue; + } + if (tcp->th_dport != a->ac_dport) { + verbose(8, "-- dport mismatch\n"); + continue; + } + } + + if ((a->ac_mflag & IPMAC_GROUP) != 0) { + if (strncmp(a->ac_group, ipf->fl_group, + FR_GROUPLEN) != 0) { + verbose(8, "-- group mismatch\n"); + continue; + } + } + + if ((a->ac_mflag & IPMAC_INTERFACE) != 0) { + if (strcmp(a->ac_iface, ipf->fl_ifname)) { + verbose(8, "-- ifname mismatch\n"); + continue; + } + } + + if ((a->ac_mflag & IPMAC_PROTOCOL) != 0) { + if (a->ac_proto != ip->ip_p) { + verbose(8, "-- protocol mismatch\n"); + continue; + } + } + + if ((a->ac_mflag & IPMAC_RESULT) != 0) { + if ((ipf->fl_flags & FF_LOGNOMATCH) != 0) { + if (a->ac_result != IPMR_NOMATCH) { + verbose(8, "-- ff-flags mismatch\n"); + continue; + } + } else if (FR_ISPASS(ipf->fl_flags)) { + if (a->ac_result != IPMR_PASS) { + verbose(8, "-- pass mismatch\n"); + continue; + } + } else if (FR_ISBLOCK(ipf->fl_flags)) { + if (a->ac_result != IPMR_BLOCK) { + verbose(8, "-- block mismatch\n"); + continue; + } + } else { /* Log only */ + if (a->ac_result != IPMR_LOG) { + verbose(8, "-- log mismatch\n"); + continue; + } + } + } + + if ((a->ac_mflag & IPMAC_RULE) != 0) { + if (a->ac_rule != ipf->fl_rule) { + verbose(8, "-- rule mismatch\n"); + continue; + } + } + + if ((a->ac_mflag & IPMAC_SRCIP) != 0) { + if ((ip->ip_src.s_addr & a->ac_smsk) != a->ac_sip) { + verbose(8, "-- srcip mismatch\n"); + continue; + } + } + + if ((a->ac_mflag & IPMAC_SRCPORT) != 0) { + if (ip->ip_p != IPPROTO_UDP && + ip->ip_p != IPPROTO_TCP) { + verbose(8, "-- port protocol mismatch\n"); + continue; + } + if (tcp->th_sport != a->ac_sport) { + verbose(8, "-- sport mismatch\n"); + continue; + } + } + + if ((a->ac_mflag & IPMAC_LOGTAG) != 0) { + if (a->ac_logtag != ipf->fl_logtag) { + verbose(8, "-- logtag %d != %d\n", + a->ac_logtag, ipf->fl_logtag); + continue; + } + } + + if ((a->ac_mflag & IPMAC_NATTAG) != 0) { + if (strncmp(a->ac_nattag, ipf->fl_nattag.ipt_tag, + IPFTAG_LEN) != 0) { + verbose(8, "-- nattag mismatch\n"); + continue; + } + } + + matched = 1; + verbose(8, "++ matched\n"); + + /* + * It matched so now perform the saves + */ + for (d = a->ac_doing; d != NULL; d = d->ipmd_next) + (*d->ipmd_store)(d->ipmd_token, &msg); + } + + return matched; +} + + +static void +free_action(a) + ipmon_action_t *a; +{ + ipmon_doing_t *d; + + while ((d = a->ac_doing) != NULL) { + a->ac_doing = d->ipmd_next; + (*d->ipmd_saver->ims_destroy)(d->ipmd_token); + free(d); + } + + if (a->ac_iface != NULL) { + free(a->ac_iface); + a->ac_iface = NULL; + } + a->ac_next = NULL; + free(a); +} + + +int +load_config(file) + char *file; +{ + FILE *fp; + char *s; + + unload_config(); + + s = getenv("YYDEBUG"); + if (s != NULL) + yydebug = atoi(s); + else + yydebug = 0; + + yylineNum = 1; + + (void) yysettab(yywords); + + fp = fopen(file, "r"); + if (!fp) { + perror("load_config:fopen:"); + return -1; + } + yyin = fp; + while (!feof(fp)) + yyparse(); + fclose(fp); + return 0; +} + + +void +unload_config() +{ + ipmon_saver_int_t *sav, **imsip; + ipmon_saver_t *is; + ipmon_action_t *a; + + while ((a = alist) != NULL) { + alist = a->ac_next; + free_action(a); + } + + /* + * Look for savers that have been added in dynamically from the + * configuration file. + */ + for (imsip = &saverlist; (sav = *imsip) != NULL; ) { + if (sav->imsi_handle == NULL) + imsip = &sav->imsi_next; + else { + dlclose(sav->imsi_handle); + + *imsip = sav->imsi_next; + is = sav->imsi_stor; + free(sav); + + free(is->ims_name); + free(is); + } + } +} + + +void +dump_config() +{ + ipmon_action_t *a; + + for (a = alist; a != NULL; a = a->ac_next) { + print_action(a); + + printf("#\n"); + } +} + + +static void +print_action(a) + ipmon_action_t *a; +{ + ipmon_doing_t *d; + + printf("match { "); + print_match(a); + printf("; }\n"); + printf("do {"); + for (d = a->ac_doing; d != NULL; d = d->ipmd_next) { + printf("%s", d->ipmd_saver->ims_name); + if (d->ipmd_saver->ims_print != NULL) { + printf("(\""); + (*d->ipmd_saver->ims_print)(d->ipmd_token); + printf("\")"); + } + printf(";"); + } + printf("};\n"); +} + + +void * +add_doing(saver) + ipmon_saver_t *saver; +{ + ipmon_saver_int_t *it; + + if (find_doing(saver->ims_name) == IPM_DOING) + return NULL; + + it = calloc(1, sizeof(*it)); + if (it == NULL) + return NULL; + it->imsi_stor = saver; + it->imsi_next = saverlist; + saverlist = it; + return it; +} + + +static int +find_doing(string) + char *string; +{ + ipmon_saver_int_t *it; + + for (it = saverlist; it != NULL; it = it->imsi_next) { + if (!strcmp(it->imsi_stor->ims_name, string)) + return IPM_DOING; + } + return 0; +} + + +static ipmon_doing_t * +build_doing(target, options) + char *target; + char *options; +{ + ipmon_saver_int_t *it; + char *strarray[2]; + ipmon_doing_t *d, *d1; + ipmon_action_t *a; + ipmon_saver_t *save; + + d = calloc(1, sizeof(*d)); + if (d == NULL) + return NULL; + + for (it = saverlist; it != NULL; it = it->imsi_next) { + if (!strcmp(it->imsi_stor->ims_name, target)) + break; + } + if (it == NULL) { + free(d); + return NULL; + } + + strarray[0] = options; + strarray[1] = NULL; + + d->ipmd_token = (*it->imsi_stor->ims_parse)(strarray); + if (d->ipmd_token == NULL) { + free(d); + return NULL; + } + + save = it->imsi_stor; + d->ipmd_saver = save; + d->ipmd_store = it->imsi_stor->ims_store; + + /* + * Look for duplicate do-things that need to be dup'd + */ + for (a = alist; a != NULL; a = a->ac_next) { + for (d1 = a->ac_doing; d1 != NULL; d1 = d1->ipmd_next) { + if (save != d1->ipmd_saver) + continue; + if (save->ims_match == NULL || save->ims_dup == NULL) + continue; + if ((*save->ims_match)(d->ipmd_token, d1->ipmd_token)) + continue; + + (*d->ipmd_saver->ims_destroy)(d->ipmd_token); + d->ipmd_token = (*save->ims_dup)(d1->ipmd_token); + break; + } + } + + return d; +} + + +static void +print_match(a) + ipmon_action_t *a; +{ + char *coma = ""; + + if ((a->ac_mflag & IPMAC_DIRECTION) != 0) { + printf("direction = "); + if (a->ac_direction == IPM_IN) + printf("in"); + else if (a->ac_direction == IPM_OUT) + printf("out"); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_DSTIP) != 0) { + printf("%sdstip = ", coma); + printhostmask(AF_INET, &a->ac_dip, &a->ac_dmsk); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_DSTPORT) != 0) { + printf("%sdstport = %hu", coma, ntohs(a->ac_dport)); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_GROUP) != 0) { + char group[FR_GROUPLEN+1]; + + strncpy(group, a->ac_group, FR_GROUPLEN); + group[FR_GROUPLEN] = '\0'; + printf("%sgroup = %s", coma, group); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_INTERFACE) != 0) { + printf("%siface = %s", coma, a->ac_iface); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_LOGTAG) != 0) { + printf("%slogtag = %u", coma, a->ac_logtag); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_NATTAG) != 0) { + char tag[17]; + + strncpy(tag, a->ac_nattag, 16); + tag[16] = '\0'; + printf("%snattag = %s", coma, tag); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_PROTOCOL) != 0) { + printf("%sprotocol = %u", coma, a->ac_proto); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_RESULT) != 0) { + printf("%sresult = ", coma); + switch (a->ac_result) + { + case IPMR_LOG : + printf("log"); + break; + case IPMR_PASS : + printf("pass"); + break; + case IPMR_BLOCK : + printf("block"); + break; + case IPMR_NOMATCH : + printf("nomatch"); + break; + } + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_RULE) != 0) { + printf("%srule = %u", coma, a->ac_rule); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_EVERY) != 0) { + if (a->ac_packet > 1) { + printf("%severy %d packets", coma, a->ac_packet); + coma = ", "; + } else if (a->ac_packet == 1) { + printf("%severy packet", coma); + coma = ", "; + } + if (a->ac_second > 1) { + printf("%severy %d seconds", coma, a->ac_second); + coma = ", "; + } else if (a->ac_second == 1) { + printf("%severy second", coma); + coma = ", "; + } + } + + if ((a->ac_mflag & IPMAC_SRCIP) != 0) { + printf("%ssrcip = ", coma); + printhostmask(AF_INET, &a->ac_sip, &a->ac_smsk); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_SRCPORT) != 0) { + printf("%ssrcport = %hu", coma, ntohs(a->ac_sport)); + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_TYPE) != 0) { + printf("%stype = ", coma); + switch (a->ac_type) + { + case IPL_LOGIPF : + printf("ipf"); + break; + case IPL_LOGSTATE : + printf("state"); + break; + case IPL_LOGNAT : + printf("nat"); + break; + } + coma = ", "; + } + + if ((a->ac_mflag & IPMAC_WITH) != 0) { + printf("%swith ", coma); + coma = ", "; + } +} + + +static int +install_saver(name, path) + char *name, *path; +{ + ipmon_saver_int_t *isi; + ipmon_saver_t *is; + char nbuf[80]; + + if (find_doing(name) == IPM_DOING) + return -1; + + isi = calloc(1, sizeof(*isi)); + if (isi == NULL) + return -1; + + is = calloc(1, sizeof(*is)); + if (is == NULL) + goto loaderror; + + is->ims_name = name; + +#ifdef RTLD_LAZY + isi->imsi_handle = dlopen(path, RTLD_LAZY); +#endif +#ifdef DL_LAZY + isi->imsi_handle = dlopen(path, DL_LAZY); +#endif + + if (isi->imsi_handle == NULL) + goto loaderror; + + snprintf(nbuf, sizeof(nbuf), "%sdup", name); + is->ims_dup = (ims_dup_func_t)dlsym(isi->imsi_handle, nbuf); + + snprintf(nbuf, sizeof(nbuf), "%sdestroy", name); + is->ims_destroy = (ims_destroy_func_t)dlsym(isi->imsi_handle, nbuf); + if (is->ims_destroy == NULL) + goto loaderror; + + snprintf(nbuf, sizeof(nbuf), "%smatch", name); + is->ims_match = (ims_match_func_t)dlsym(isi->imsi_handle, nbuf); + + snprintf(nbuf, sizeof(nbuf), "%sparse", name); + is->ims_parse = (ims_parse_func_t)dlsym(isi->imsi_handle, nbuf); + if (is->ims_parse == NULL) + goto loaderror; + + snprintf(nbuf, sizeof(nbuf), "%sprint", name); + is->ims_print = (ims_print_func_t)dlsym(isi->imsi_handle, nbuf); + if (is->ims_print == NULL) + goto loaderror; + + snprintf(nbuf, sizeof(nbuf), "%sstore", name); + is->ims_store = (ims_store_func_t)dlsym(isi->imsi_handle, nbuf); + if (is->ims_store == NULL) + goto loaderror; + + isi->imsi_stor = is; + isi->imsi_next = saverlist; + saverlist = isi; + + return 0; + +loaderror: + if (isi->imsi_handle != NULL) + dlclose(isi->imsi_handle); + free(isi); + if (is != NULL) + free(is); + return -1; +} diff --git a/sbin/ipf/ipnat/ipnat.1 b/sbin/ipf/ipnat/ipnat.1 new file mode 100644 index 000000000000..f24141546171 --- /dev/null +++ b/sbin/ipf/ipnat/ipnat.1 @@ -0,0 +1,48 @@ +.TH IPNAT 1 +.SH NAME +ipnat \- user interface to the NAT +.SH SYNOPSIS +.B ipnat +[ +.B \-lnrsvCF +] +.B \-f <\fIfilename\fP> +.SH DESCRIPTION +.PP +\fBipnat\fP opens the filename given (treating "\-" as stdin) and parses the +file for a set of rules which are to be added or removed from the IP NAT. +.PP +Each rule processed by \fBipnat\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 \fBipnat\fP. +.SH OPTIONS +.TP +.B \-C +delete all entries in the current NAT rule listing (NAT rules) +.TP +.B \-F +delete all active entries in the current NAT translation table (currently +active NAT mappings) +.TP +.B \-l +Show the list of current NAT table entry mappings. +.TP +.B \-n +This flag (no-change) prevents \fBipf\fP from actually making any ioctl +calls or doing anything which would alter the currently running kernel. +.TP +.B \-s +Retrieve and display NAT statistics +.TP +.B \-r +Remove matching NAT rules rather than add them to the internal lists +.TP +.B \-v +Turn verbose mode on. Displays information relating to rule processing +and active rules/table entries. +.DT +.SH FILES +/dev/ipnat +.SH SEE ALSO +ipnat(5), ipf(8), ipfstat(8) diff --git a/sbin/ipf/ipnat/ipnat.4 b/sbin/ipf/ipnat/ipnat.4 new file mode 100644 index 000000000000..80c5ba444708 --- /dev/null +++ b/sbin/ipf/ipnat/ipnat.4 @@ -0,0 +1,97 @@ +.\" $FreeBSD$ +.TH IPNAT 4 +.SH NAME +ipnat \- Network Address Translation kernel interface +.SH SYNOPSIS +#include <netinet/ip_compat.h> +.br +#include <netinet/ip_fil.h> +.br +#include <netinet/ip_proxy.h> +.br +#include <netinet/ip_nat.h> +.SH IOCTLS +.PP +To add and delete rules to the NAT list, two 'basic' ioctls are provided +for use. The ioctl's are called as: +.LP +.nf + ioctl(fd, SIOCADNAT, struct ipnat **) + ioctl(fd, SIOCRMNAT, struct ipnat **) + ioctl(fd, SIOCGNATS, struct natstat **) + ioctl(fd, SIOCGNATL, struct natlookup **) +.fi +.PP +Unlike \fBipf(4)\fP, there is only a single list supported by the kernel NAT +interface. An inactive list which can be swapped to is not currently +supported. + +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 +(i.e., /dev/ipl). +.PP +The structure used with the NAT interface is described below: +.LP +.nf +typedef struct ipnat { + struct ipnat *in_next; + void *in_ifp; + u_short in_flags; + u_short in_pnext; + u_short in_port[2]; + struct in_addr in_in[2]; + struct in_addr in_out[2]; + struct in_addr in_nextip; + int in_space; + int in_redir; /* 0 if it's a mapping, 1 if it's a hard redir */ + char in_ifname[IFNAMSIZ]; +} ipnat_t; + +#define in_pmin in_port[0] /* Also holds static redir port */ +#define in_pmax in_port[1] +#define in_nip in_nextip.s_addr +#define in_inip in_in[0].s_addr +#define in_inmsk in_in[1].s_addr +#define in_outip in_out[0].s_addr +#define in_outmsk in_out[1].s_addr + +.fi +.PP +Recognised values for in_redir: +.LP +.nf +#define NAT_MAP 0 +#define NAT_REDIRECT 1 +.fi +.LP +\fBNAT statistics\fP +Statistics on the number of packets mapped, going in and out are kept, +the number of times a new entry is added and deleted (through expiration) to +the NAT table and the current usage level of the NAT table. +.PP +Pointers to the NAT table inside the kernel, as well as to the top of the +internal NAT lists constructed with the \fBSIOCADNAT\fP ioctls. The table +itself is a hash table of size NAT_SIZE (default size is 367). +.PP +To retrieve the statistics, the \fBSIOCGNATS\fP ioctl must be used, with +the appropriate structure passed by reference, as follows: +.nf + ioctl(fd, SIOCGNATS, struct natstat *) + +typedef struct natstat { + u_long ns_mapped[2]; + u_long ns_added; + u_long ns_expire; + u_long ns_inuse; + nat_t ***ns_table; + ipnat_t *ns_list; +} natstat_t; +.fi +.SH BUGS +It would be nice if there were more flexibility when adding and deleting +filter rules. +.SH FILES +/dev/ipnat +.SH SEE ALSO +ipf(4), ipnat(5), ipf(8), ipnat(8), ipfstat(8) diff --git a/sbin/ipf/ipnat/ipnat.5 b/sbin/ipf/ipnat/ipnat.5 new file mode 100644 index 000000000000..ab56573d79ea --- /dev/null +++ b/sbin/ipf/ipnat/ipnat.5 @@ -0,0 +1,728 @@ +.\" $FreeBSD$ +.\" +.TH IPNAT 5 +.SH NAME +ipnat, ipnat.conf \- IPFilter NAT file format +.SH DESCRIPTION +.PP +The +.B ipnat.conf +file is used to specify rules for the Network Address Translation (NAT) +component of IPFilter. To load rules specified in the +.B ipnat.conf +file, the +.B ipnat(8) +program is used. +.PP +For standard NAT functionality, a rule should start with \fBmap\fP and then +proceeds to specify the interface for which outgoing packets will have their +source address rewritten. Following this it is expected that the old source +address, and optionally port number, will be specified. +.PP +In general, all NAT rules conform to the following layout: +the first word indicates what type of NAT rule is present, this is followed +by some stanzas to match a packet, followed by a "->" and this is then +followed by several more stanzas describing the new data to be put in the +packet. +.PP +In this text and in others, +use of the term "left hand side" (LHS) when talking about a NAT rule refers +to text that appears before the "->" and the "right hand side" (RHS) for text +that appears after it. In essence, the LHS is the packet matching and the +RHS is the new data to be used. +.SH VARIABLES +.PP +This configuration file, like all others used with IPFilter, supports the +use of variable substitution throughout the text. +.nf + +nif="ppp0"; +map $nif 0/0 -> 0/32 +.fi +.PP +would become +.nf + +map ppp0 0/0 -> 0/32 +.fi +.PP +Variables can be used recursively, such as 'foo="$bar baz";', so long as +$bar exists when the parser reaches the assignment for foo. +.PP +See +.B ipnat(8) +for instructions on how to define variables to be used from a shell +environment. +.SH OUTBOUND SOURCE TRANSLATION (map'ing) +Changing the source address of a packet is traditionally performed using +.B map +rules. Both the source address and optionally port number can be changed +according to various controls. +.PP +To start out with, a common rule used is of the form: +.nf + +map le0 0/0 -> 0/32 +.fi +.PP +Here we're saying change the source address of all packets going out of +le0 (the address/mask pair of 0/0 matching all packets) to that of the +interface le0 (0/32 is a synonym for the interface's own address at +the current point in time.) If we wanted to pass the packet through +with no change in address, we would write it as: +.nf + +map le0 0/0 -> 0/0 +.fi +.PP +If we only want to change a portion of our internal network and to a +different address that is routed back through this host, we might do: +.nf + +map le0 10.1.1.0/24 -> 192.168.55.3/32 +.fi +.PP +In some instances, we may have an entire subnet to map internal addresses +out onto, in which case we can express the translation as this: +.nf + +map le0 10.0.0.0/8 -> 192.168.55.0/24 +.fi +.PP +IPFilter will cycle through each of the 256 addresses in the 192.168.55.0/24 +address space to ensure that they all get used. +.PP +Of course this poses a problem for TCP and UDP, with many connections made, +each with its own port number pair. If we're unlucky, translations can be +dropped because the new address/port pair mapping already exists. To +mitigate this problem, we add in port translation or port mapping: +.nf + +map le0 10.0.0.0/8 -> 192.168.55.0/24 portmap tcp/udp auto +.fi +.PP +In this instance, the word "auto" tells IPFilter to calculate a private +range of port numbers for each address on the LHS to use without fear +of them being trampled by others. This can lead to problems if there are +connections being generated more quickly than IPFilter can expire them. +In this instance, and if we want to get away from a private range of +port numbers, we can say: +.nf + +map le0 10.0.0.0/8 -> 192.168.55.0/24 portmap tcp/udp 5000:65000 +.fi +.PP +And now each connection through le0 will add to the enumeration of +the port number space 5000-65000 as well as the IP address subnet +of 192.168.55.0/24. +.PP +If the new addresses to be used are in a consecutive range, rather +than a complete subnet, we can express this as: +.nf + +map le0 10.0.0.0/8 -> range 192.168.55.10-192.168.55.249 + portmap tcp/udp 5000:65000 +.fi +.PP +This tells IPFilter that it has a range of 240 IP address to use, from +192.168.55.10 to 192.168.55.249, inclusive. +.PP +If there were several ranges of addresses for use, we can use each one +in a round-robin fashion as followed: +.nf + +map le0 10.0.0.0/8 -> range 192.168.55.10-192.168.55.29 + portmap tcp/udp 5000:65000 round-robin +map le0 10.0.0.0/8 -> range 192.168.55.40-192.168.55.49 + portmap tcp/udp 5000:65000 round-robin +.fi +.PP +To specify translation rules that impact a specific IP protocol, +the protocol name or number is appended to the rule like this: +.nf + +map le0 10.0.0.0/8 -> 192.168.55.0/24 tcp/udp +map le0 10.0.0.0/8 -> 192.168.55.1/32 icmp +map le0 10.0.0.0/8 -> 192.168.55.2/32 gre +.fi +.PP +For TCP connections exiting a connection such as PPPoE where the MTU is +slightly smaller than normal ethernet, it can be useful to reduce the +Maximum Segment Size (MSS) offered by the internal machines to match, +reducing the liklihood that the either end will attempt to send packets +that are too big and result in fragmentation. This is acheived using the +.B mssclamp +option with TCP +.B map +rules like this: +.nf + +map pppoe0 0/0 -> 0/32 mssclamp 1400 tcp +.fi +.PP +For ICMP packets, we can map the ICMP id space in query packets: +.nf + +map le0 10.0.0.0/8 -> 192.168.55.1/32 icmpidmap icmp 1000:20000 +.fi +.PP +If we wish to be more specific about our initial matching criteria on the +LHS, we can expand to using a syntax more similar to that in +.B ipf.conf(5) +: +.nf + +map le0 from 10.0.0.0/8 to 26.0.0.0/8 -> + 192.168.55.1 +map le0 from 10.0.0.0/8 port > 1024 to 26.0.0.0/8 -> + 192.168.55.2 portmap 5000:9999 tcp/udp +map le0 from 10.0.0.0/8 ! to 26.0.0.0/8 -> + 192.168.55.3 portmap 5000:9999 tcp/udp +.fi +.TP +.B NOTE: +negation matching with source addresses is +.B NOT +possible with +.B map +/ +.B map-block +rules. +.PP +The NAT code has builtin default timeouts for TCP, UDP, ICMP and another +for all other protocols. In general, the timeout for an entry to be +deleted shrinks once a reply packet has been seen (excluding TCP.) +If you wish to specify your own timeouts, this can be achieved either +by setting one timeout for both directions: +.nf + +map le0 0/0 -> 0/32 gre age 30 +.fi +.PP +or setting a different timeout for the reply: +.nf + +map le0 from any to any port = 53 -> 0/32 age 60/10 udp +.fi +.PP +A pressing problem that many people encounter when using NAT is that the +address protocol can be embedded inside an application's communication. +To address this problem, IPFilter provides a number of built-in proxies +for the more common trouble makers, such as FTP. These proxies can be +used as follows: +.nf + +map le0 0/0 -> 0/32 proxy port 21 ftp/tcp +.fi +.PP +In this rule, the word "proxy" tells us that we want to connect up this +translation with an internal proxy. The "port 21" is an extra restriction +that requires the destination port number to be 21 if this rule is to be +activated. The word "ftp" is the proxy identifier that the kernel will +try and resolve internally, "tcp" the protocol that packets must match. +.PP +See below for a list of proxies and their relative staus. +.PP +To associate NAT rules with filtering rules, it is possible to set and +match tags during either inbound or outbound processing. At present the +tags for forwarded packets are not preserved by forwarding, so once the +packet leaves IPFilter, the tag is forgotten. For +.B map +rules, we can match tags set by filter rules like this: +.nf + +map le0 0/0 -> 0/32 proxy portmap 5000:5999 tag lan1 tcp +.fi +.PP +This would be used with "pass out" rules that includes a stanza such +as "set-tag (nat = lan1)". +.PP +If the interface in which packets are received is different from the +interface on which packets are sent out, then the translation rule needs +to be written to take this into account: +.nf + +map hme0,le0 0/0 -> 0/32 +.fi +.PP +Although this might seem counterintuitive, the interfaces when listed +in rules for +.B ipnat.conf +are always in the +.I inbound +, +.I outbound +order. In this case, hme0 would be the return interface and le0 would be +the outgoing interface. If you wish to allow return packets on any +interface, the correct syntax to use would be: +.nf + +map *,le0 0/0 -> 0/32 +.fi +.LP +A special variant of +.B map +rules exists, called +.B map-block. +This command is intended for use when there is a large network to be mapped +onto a smaller network, where the difference in netmasks is upto 14 bits +difference in size. This is achieved by dividing the address space and +port space up to ensure that each source address has its own private range +of ports to use. For example, this rule: +.nf + +map-block ppp0 172.192.0.0/16 -> 209.1.2.0/24 ports auto +.fi +.PP +would result in 172.192.0.0/24 being mapped to 209.1.2.0/32 +with each address, from 172.192.0.0 to 172.192.0.255 having 252 ports of its +own. As opposed to the above use of \fBmap\fP, if for some reason the user +of (say) 172.192.0.2 wanted 260 simultaneous connections going out, they would +be limited to 252 with \fBmap-block\fP but would just \fImove on\fP to the next +IP address with the \fBmap\fP command. +.SS Extended matching +.PP +If it is desirable to match on both the source and destination of a packet +before applying an address translation to it, this can be achieved by using +the same from-to syntax as is used in \fBipf.conf\fP(5). What follows +applies equally to the +.B map +rules discussed above and +.B rdr +rules discussed below. A simple example is as follows: +.nf + +map bge0 from 10.1.0.0/16 to 192.168.1.0/24 -> 172.12.1.4 +.fi +.PP +This would only match packets that are coming from hosts that have a source +address matching 10.1.0.0/16 and a destination matching 192.168.1.0/24. +This can be expanded upon with ports for TCP like this: +.nf + +rdr bge0 from 10.1.0.0/16 to any port = 25 -> 127.0.0.1 port 2501 tcp +.fi +.PP +Where only TCP packets from 10.1.0.0/16 to port 25 will be redirected to +port 2501. +.PP +As with \fBipf.conf\fR(5), if we have a large set of networks or addresses +that we would like to match up with then we can define a pool using +\fBippool\fR(8) in \fBippool.conf\fR(5) and then refer to it in an +\fBipnat\fR rule like this: +.nf + +map bge0 from pool/100 to any port = 25 -> 127.0.0.1 port 2501 tcp +.fi +.TP +.B NOTE: +In this situation, the rule is considered to have a netmask of "0" and +thus is looked at last, after any rules with /16's or /24's in them, +.I even if +the defined pool only has /24's or /32's. Pools may also be used +.I wherever +the from-to syntax in \fBipnat.conf\fR(5) is allowed. +.SH INBOUND DESTINATION TRANSLATION (redirection) +.PP +Redirection of packets is used to change the destination fields in a packet +and is supported for packets that are moving \fIin\fP on a network interface. +While the same general syntax for +.B map +rules is supported, there are differences and limitations. +.PP +Firstly, by default all redirection rules target a single IP address, not +a network or range of network addresses, so a rule written like this: +.nf + +rdr le0 0/0 -> 192.168.1.0 +.fi +.PP +Will not spread packets across all 256 IP addresses in that class C network. +If you were to try a rule like this: +.nf + +rdr le0 0/0 -> 192.168.1.0/24 +.fi +.PP +then you will receive a parsing error. +.PP +The from-to source-destination matching used with +.B map +rules can be used with rdr rules, along with negation, however the +restriction moves - only a source address match can be negated: +.nf + +rdr le0 from 1.1.0.0/16 to any -> 192.168.1.3 +rdr le0 ! from 1.1.0.0/16 to any -> 192.168.1.4 +.fi +.PP +If there is a consective set of addresses you wish to spread the packets +over, then this can be done in one of two ways, the word "range" optional +to preserve: +.nf + +rdr le0 0/0 -> 192.168.1.1 - 192.168.1.5 +rdr le0 0/0 -> range 192.168.1.1 - 192.168.1.5 +.fi +.PP +If there are only two addresses to split the packets across, the +recommended method is to use a comma (",") like this: +.nf + +rdr le0 0/0 -> 192.168.1.1,192.168.1.2 +.fi +.PP +If there is a large group of destination addresses that are somewhat +disjoint in nature, we can cycle through them using a +.B round-robin +technique like this: +.nf + +rdr le0 0/0 -> 192.168.1.1,192.168.1.2 round-robin +rdr le0 0/0 -> 192.168.1.5,192.168.1.7 round-robin +rdr le0 0/0 -> 192.168.1.9 round-robin +.fi +.PP +If there are a large number of redirect rules and hosts being targetted +then it may be desirable to have all those from a single source address +be targetted at the same destination address. To achieve this, the +word +.B sticky +is appended to the rule like this: +.nf + +rdr le0 0/0 -> 192.168.1.1,192.168.1.2 sticky +rdr le0 0/0 -> 192.168.1.5,192.168.1.7 round-robin sticky +rdr le0 0/0 -> 192.168.1.9 round-robin sticky +.fi +.PP +The +.B sticky +feature can only be combined with +.B round-robin +and the use of comma. +.PP +For TCP and UDP packets, it is possible to both match on the destiantion +port number and to modify it. For example, to change the destination port +from 80 to 3128, we would use a rule like this: +.nf + +rdr de0 0/0 port 80 -> 127.0.0.1 port 3128 tcp +.fi +.PP +If a range of ports is given on the LHS and a single port is given on the +RHS, the entire range of ports is moved. For example, if we had this: +.nf + +rdr le0 0/0 port 80-88 -> 127.0.0.1 port 3128 tcp +.fi +.PP +then port 80 would become 3128, port 81 would become 3129, etc. If we +want to redirect a number of different pots to just a single port, an +equals sign ("=") is placed before the port number on the RHS like this: +.nf + +rdr le0 0/0 port 80-88 -> 127.0.0.1 port = 3128 tcp +.fi +.PP +In this case, port 80 goes to 3128, port 81 to 3128, etc. +.PP +As with +.B map +rules, it is possible to manually set a timeout using the +.B age +option, like this: +.nf + +rdr le0 0/0 port 53 -> 127.0.0.1 port 10053 udp age 5/5 +.fi +.PP +The use of proxies is not restricted to +.B map +rules and outbound sessions. Proxies can also be used with redirect +rules, although the syntax is slightly different: +.nf + +rdr ge0 0/0 port 21 -> 127.0.0.1 port 21 tcp proxy ftp +.fi +.PP +For +.B rdr +rules, the interfaces supplied are in the same order as +.B map +rules - input first, then output. In situations where the outgoing interface +is not certain, it is also possible to use a wildcard ("*") to effect a match +on any interface. +.nf + +rdr le0,* 0/0 -> 192.168.1.0 +.fi +.PP +A single rule, with as many options set as possible would look something like +this: +.nf + +rdr le0,ppp0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp + round-robin frag age 40/40 sticky mssclamp 1000 tag tagged +.fi +.SH REWRITING SOURCE AND DESTINATION +.PP +Whilst the above two commands provide a lot of flexibility in changing +addressing fields in packets, often it can be of benefit to translate +\fIboth\fP source \fBand\fR destination at the same time or to change +the source address on input or the destination address on output. +Doing all of these things can be accomplished using +.B rewrite +NAT rules. +.PP +A +.B rewrite +rule requires the same level of packet matching as before, protocol and +source/destination information but in addition allows either +.B in +or +.B out +to be specified like this: +.nf + +rewrite in on ppp0 proto tcp from any to any port = 80 -> + src 0/0 dst 127.0.0.1,3128; +rewrite out on ppp0 from any to any -> + src 0/32 dst 10.1.1.0/24; +.fi +.PP +On the RHS we can specify both new source and destination information to place +into the packet being sent out. As with other rules used in +\fBipnat.conf\fR, there are shortcuts syntaxes available to use the original +address information (\fB0/0\fR) and the address associated with the network +interface (\fB0/32\fR.) For TCP and UDP, both address and port information +can be changed. At present it is only possible to specify either a range of +port numbers to be used (\fBX-Y\fR) or a single port number (\fB= X\fR) as +follows: +.nf + +rewrite in on le0 proto tcp from any to any port = 80 -> + src 0/0,2000-20000 dst 127.0.0.1,port = 3128; +.fi +.PP +There are four fields that are stepped through in enumerating the number +space available for creating a new destination: +.LP +source address +.LP +source port +.LP +destination address +.LP +destination port +.PP +If one of these happens to be a static then it will be skipped and the next +one incremented. As an example: +.nf + +rewrite out on le0 proto tcp from any to any port = 80 -> + src 1.0.0.0/8,5000-5999 dst 2.0.0.0/24,6000-6999; +.fi +.PP +The translated packets would be: +.LP +1st src=1.0.0.1,5000 dst=2.0.0.1,6000 +.LP +2nd src=1.0.0.2,5000 dst=2.0.0.1,6000 +.LP +3rd src=1.0.0.2,5001 dst=2.0.0.1,6000 +.LP +4th src=1.0.0.2,5001 dst=2.0.0.2,6000 +.LP +5th src=1.0.0.2,5001 dst=2.0.0.2,6001 +.LP +6th src=1.0.0.3,5001 dst=2.0.0.2,6001 +.PP +and so on. +.PP +As with +.B map +rules, it is possible to specify a range of addresses by including the word +\fIrange\fR before the addresses: +.nf + +rewrite from any to any port = 80 -> + src 1.1.2.3 - 1.1.2.6 dst 2.2.3.4 - 2.2.3.6; +.fi +.SH DIVERTING PACKETS +.PP +If you'd like to send packets to a UDP socket rather than just another +computer to be decapsulated, this can be achieved using a +.B divert +rule. +.PP +Divert rules can be be used with both inbound and outbound packet +matching however the rule +.B must +specify host addresses for the outer packet, not ranges of addresses +or netmasks, just single addresses. +Additionally the syntax must supply required information for UDP. +An example of what a divert rule looks ike is as follows: +.nf + +divert in on le0 proto udp from any to any port = 53 -> + src 192.1.1.1,54 dst 192.168.1.22.1,5300; +.fi +.PP +On the LHS is a normal set of matching capabilities but on the RHS it is +a requirement to specify both the source and destination addresses and +ports. +.PP +As this feature is intended to be used with targetting packets at sockets +and not IPFilter running on other systems, there is no rule provided to +\fIundivert\fR packets. +.TP +.B NOTE: +Diverted packets \fImay\fP be fragmented if the addition of the +encapsulating IP header plus UDP header causes the packet to exceed +the size allowed by the outbound network interface. At present it is +not possible to cause Path MTU discovery to happen as this feature +is intended to be transparent to both endpoints. +.B Path MTU Discovery +If Path MTU discovery is being used and the "do not fragment" flag +is set in packets to be encapsulated, an ICMP error message will +be sent back to the sender if the new packet would need to be +fragmented. +.SH COMMON OPTIONS +This section deals with options that are available with all rules. +.TP +.B purge +When the purge keyword is added to the end of a NAT rule, it will +cause all of the active NAT sessions to be removed when the rule +is removed as an individual operation. If all of the NAT rules +are flushed out, it is expected that the operator will similarly +flush the NAT table and thus NAT sessions are not removed when the +NAT rules are flushed out. +.SH RULE ORDERING +.PP +.B NOTE: +Rules in +.B ipnat.conf +are read in sequentially as listed and loaded into the kernel in this +fashion +.B BUT +packet matching is done on \fBnetmask\fR, going from 32 down to 0. +If a rule uses +.B pool +or +.B hash +to reference a set of addresses or networks, the netmask value for +these fields is considered to be "0". +So if your +.B ipnat.conf +has the following rules: +.nf + +rdr le0 192.0.0.0/8 port 80 -> 127.0.0.1 3132 tcp +rdr le0 192.2.0.0/16 port 80 -> 127.0.0.1 3131 tcp +rdr le0 from any to pool/100 port 80 -> 127.0.0.1 port 3130 tcp +rdr le0 192.2.2.0/24 port 80 -> 127.0.0.1 3129 tcp +rdr le0 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp +.fi +.PP +then the rule with 192.2.2.1 will match \fBfirst\fR, regardless of where +it appears in the ordering of the above rules. In fact, the order in +which they would be used to match a packet is: +.nf + +rdr le0 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp +rdr le0 192.2.2.0/24 port 80 -> 127.0.0.1 3129 tcp +rdr le0 192.2.0.0/16 port 80 -> 127.0.0.1 3131 tcp +rdr le0 192.0.0.0/8 port 80 -> 127.0.0.1 3132 tcp +rdr le0 from any to pool/100 port 80 -> 127.0.0.1 port 3130 tcp +.fi +.PP +where the first line is actually a /32. +.PP +If your +.B ipnat.conf +file has entries with matching target fields (source address for +.B map +rules and destination address for +.B rdr +rules), then the ordering in the +.B ipnat.conf +file does matter. So if you had the following: +.nf + +rdr le0 from 1.1.0.0/16 to 192.2.2.1 port 80 -> 127.0.0.1 3129 tcp +rdr le0 from 1.1.1.0/24 to 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp +.fi +.PP +Then no packets will match the 2nd rule, they'll all match the first. +.SH IPv6 +.PP +In all of the examples above, where an IPv4 address is present, an IPv6 +address can also be used. All rules must use either IPv4 addresses with +both halves of the NAT rule or IPv6 addresses for both halves. Mixing +IPv6 addresses with IPv4 addresses, in a single rule, will result in an +error. +.PP +For shorthand notations such as "0/32", the equivalent for IPv6 is +"0/128". IPFilter will treat any netmask greater than 32 as an +implicit direction that the address should be IPv6, not IPv4. +To be unambiguous with 0/0, for IPv6 use ::0/0. +.SH KERNEL PROXIES +.PP +IP Filter comes with a few, simple, proxies built into the code that is loaded +into the kernel to allow secondary channels to be opened without forcing the +packets through a user program. The current state of the proxies is listed +below, as one of three states: +.HP +Aging - protocol is roughly understood from +the time at which the proxy was written but it is not well tested or +maintained; +.HP +Developmental - basic functionality exists, works most of the time but +may be problematic in extended real use; +.HP +Experimental - rough support for the protocol at best, may or may not +work as testing has been at best sporadic, possible large scale changes +to the code in order to properly support the protocol. +.HP +Mature - well tested, protocol is properly +understood by the proxy; +.PP +The currently compiled in proxy list is as follows: +.TP +FTP - Mature +(map ... proxy port ftp ftp/tcp) +.TP +IRC - Experimental +(proxy port 6667 irc/tcp) +.TP +rpcbind - Experimental +.TP +PPTP - Experimental +.TP +H.323 - Experimental +(map ... proxy port 1720 h323/tcp) +.TP +Real Audio (PNA) - Aging +.TP +DNS - Developmental +(map ... proxy port 53 dns/udp { block .cnn.com; }) +.TP +IPsec - Developmental +(map ... proxy port 500 ipsec/tcp) +.TP +netbios - Experimental +.TP +R-command - Mature +(map ... proxy port shell rcmd/tcp) +.SH KERNEL PROXIES +.SH FILES +/dev/ipnat +.br +/etc/protocols +.br +/etc/services +.br +/etc/hosts +.SH SEE ALSO +ipnat(4), hosts(5), ipf(5), services(5), ipf(8), ipnat(8) diff --git a/sbin/ipf/ipnat/ipnat.8 b/sbin/ipf/ipnat/ipnat.8 new file mode 100644 index 000000000000..a49f33736b40 --- /dev/null +++ b/sbin/ipf/ipnat/ipnat.8 @@ -0,0 +1,76 @@ +.\" $FreeBSD$ +.\" +.TH IPNAT 8 +.SH NAME +ipnat \- user interface to the NAT subsystem +.SH SYNOPSIS +.B ipnat +[ +.B \-dhlnrsvCF +] +[ +.B \-M core +] +[ +.B \-N system +] +.B \-f <\fIfilename\fP> +.SH DESCRIPTION +.PP +\fBipnat\fP opens the filename given (treating "\-" as stdin) and parses the +file for a set of rules which are to be added or removed from the IP NAT. +.PP +Each rule processed by \fBipnat\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 \fBipnat\fP. +.PP +Note that if +\fBipf(8)\fP +is not enabled when NAT is configured, it will be enabled +automatically, as the same kernel facilities are used for +NAT functionality. In addition, packet forwarding must be +enabled. +.SH OPTIONS +.TP +.B \-C +delete all entries in the current NAT rule listing (NAT rules) +.TP +.B \-d +Enable printing of some extra debugging information. +.TP +.B \-F +delete all active entries in the current NAT translation table (currently +active NAT mappings) +.TP +.B \-h +Print number of hits for each MAP/Redirect filter. +.TP +.B \-l +Show the list of current NAT table entry mappings. +.TP +.B \-n +This flag (no-change) prevents \fBipf\fP from actually making any ioctl +calls or doing anything which would alter the currently running kernel. +.TP +.B \-p +This flag is used with the \fB-r\fP flag to cause any active NAT +sessions that were created by the rules being removed and that are +currently active to also be removed. +.TP +.B \-r +Remove matching NAT rules rather than add them to the internal lists. +.TP +.B \-s +Retrieve and display NAT statistics. +.TP +.B \-v +Turn verbose mode on. Displays information relating to rule processing +and active rules/table entries. +.DT +.SH FILES +/dev/ipnat +.br +/usr/share/examples/ipfilter Directory with examples. +.SH SEE ALSO +ipnat(5), ipf(8), ipfstat(8) diff --git a/sbin/ipf/ipnat/ipnat.c b/sbin/ipf/ipnat/ipnat.c new file mode 100644 index 000000000000..1ca6e776ffdf --- /dev/null +++ b/sbin/ipf/ipnat/ipnat.c @@ -0,0 +1,842 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) + */ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#if !defined(__SVR4) +#include <strings.h> +#else +#include <sys/byteorder.h> +#endif +#include <sys/time.h> +#include <sys/param.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/file.h> +#define _KERNEL +#include <sys/uio.h> +#undef _KERNEL +#include <sys/socket.h> +#include <sys/ioctl.h> +#if defined(sun) && defined(__SVR4) +# include <sys/ioccom.h> +# include <sys/sysmacros.h> +#endif +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <net/if.h> +#include <netdb.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <resolv.h> +#include <ctype.h> +# include <nlist.h> +#include "ipf.h" +#include "netinet/ipl.h" +#include "kmem.h" + + +# define STRERROR(x) strerror(x) + +#if !defined(lint) +static const char sccsid[] ="@(#)ipnat.c 1.9 6/5/96 (C) 1993 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + + +#if SOLARIS +#define bzero(a,b) memset(a,0,b) +#endif +int use_inet6 = 0; + +extern char *optarg; + +void dostats(int, natstat_t *, int, int, int *); +void dotable(natstat_t *, int, int, int, char *); +void flushtable(int, int, int *); +void usage(char *); +int main(int, char*[]); +void showhostmap(natstat_t *nsp); +void natstat_dead(natstat_t *, char *); +void dostats_live(int, natstat_t *, int, int *); +void showhostmap_dead(natstat_t *); +void showhostmap_live(int, natstat_t *); +void dostats_dead(natstat_t *, int, int *); +int nat_matcharray(nat_t *, int *); + +int opts; +int nohdrfields = 0; +wordtab_t *nat_fields = NULL; + +void usage(name) + char *name; +{ + fprintf(stderr, "Usage: %s [-CFhlnrRsv] [-f filename]\n", name); + exit(1); +} + + +int main(argc, argv) + int argc; + char *argv[]; +{ + int fd, c, mode, *natfilter; + char *file, *core, *kernel; + natstat_t ns, *nsp; + ipfobj_t obj; + + fd = -1; + opts = 0; + nsp = &ns; + file = NULL; + core = NULL; + kernel = NULL; + mode = O_RDWR; + natfilter = NULL; + + assigndefined(getenv("IPNAT_PREDEFINED")); + + while ((c = getopt(argc, argv, "CdFf:hlm:M:N:nO:prRsv")) != -1) + switch (c) + { + case 'C' : + opts |= OPT_CLEAR; + break; + case 'd' : + opts |= OPT_DEBUG; + break; + case 'f' : + file = optarg; + break; + case 'F' : + opts |= OPT_FLUSH; + break; + case 'h' : + opts |=OPT_HITS; + break; + case 'l' : + opts |= OPT_LIST; + mode = O_RDONLY; + break; + case 'm' : + natfilter = parseipfexpr(optarg, NULL); + break; + case 'M' : + core = optarg; + break; + case 'N' : + kernel = optarg; + break; + case 'n' : + opts |= OPT_DONOTHING|OPT_DONTOPEN; + mode = O_RDONLY; + break; + case 'O' : + nat_fields = parsefields(natfields, optarg); + break; + case 'p' : + opts |= OPT_PURGE; + break; + case 'R' : + opts |= OPT_NORESOLVE; + break; + case 'r' : + opts |= OPT_REMOVE; + break; + case 's' : + opts |= OPT_STAT; + mode = O_RDONLY; + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + default : + usage(argv[0]); + } + + if (((opts & OPT_PURGE) != 0) && ((opts & OPT_REMOVE) == 0)) { + (void) fprintf(stderr, "%s: -p must be used with -r\n", + argv[0]); + exit(1); + } + + initparse(); + + if ((kernel != NULL) || (core != NULL)) { + (void) setgid(getgid()); + (void) setuid(getuid()); + } + + if (!(opts & OPT_DONOTHING)) { + if (((fd = open(IPNAT_NAME, mode)) == -1) && + ((fd = open(IPNAT_NAME, O_RDONLY)) == -1)) { + (void) fprintf(stderr, "%s: open: %s\n", IPNAT_NAME, + STRERROR(errno)); + exit(1); + } + } + + bzero((char *)&ns, sizeof(ns)); + + if ((opts & OPT_DONOTHING) == 0) { + if (checkrev(IPL_NAME) == -1) { + fprintf(stderr, "User/kernel version check failed\n"); + exit(1); + } + } + + if (!(opts & OPT_DONOTHING) && (kernel == NULL) && (core == NULL)) { + bzero((char *)&obj, sizeof(obj)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_NATSTAT; + obj.ipfo_size = sizeof(*nsp); + obj.ipfo_ptr = (void *)nsp; + if (ioctl(fd, SIOCGNATS, &obj) == -1) { + ipferror(fd, "ioctl(SIOCGNATS)"); + exit(1); + } + (void) setgid(getgid()); + (void) setuid(getuid()); + } else if ((kernel != NULL) || (core != NULL)) { + if (openkmem(kernel, core) == -1) + exit(1); + + natstat_dead(nsp, kernel); + if (opts & (OPT_LIST|OPT_STAT)) + dostats(fd, nsp, opts, 0, natfilter); + exit(0); + } + + if (opts & (OPT_FLUSH|OPT_CLEAR)) + flushtable(fd, opts, natfilter); + if (file) { + return ipnat_parsefile(fd, ipnat_addrule, ioctl, file); + } + if (opts & (OPT_LIST|OPT_STAT)) + dostats(fd, nsp, opts, 1, natfilter); + return 0; +} + + +/* + * Read NAT statistic information in using a symbol table and memory file + * rather than doing ioctl's. + */ +void natstat_dead(nsp, kernel) + natstat_t *nsp; + char *kernel; +{ + struct nlist nat_nlist[10] = { + { "nat_table" }, /* 0 */ + { "nat_list" }, + { "maptable" }, + { "ipf_nattable_sz" }, + { "ipf_natrules_sz" }, + { "ipf_rdrrules_sz" }, /* 5 */ + { "ipf_hostmap_sz" }, + { "nat_instances" }, + { NULL } + }; + void *tables[2]; + + if (nlist(kernel, nat_nlist) == -1) { + fprintf(stderr, "nlist error\n"); + return; + } + + /* + * Normally the ioctl copies all of these values into the structure + * for us, before returning it to userland, so here we must copy each + * one in individually. + */ + kmemcpy((char *)&tables, nat_nlist[0].n_value, sizeof(tables)); + nsp->ns_side[0].ns_table = tables[0]; + nsp->ns_side[1].ns_table = tables[1]; + + kmemcpy((char *)&nsp->ns_list, nat_nlist[1].n_value, + sizeof(nsp->ns_list)); + kmemcpy((char *)&nsp->ns_maptable, nat_nlist[2].n_value, + sizeof(nsp->ns_maptable)); + kmemcpy((char *)&nsp->ns_nattab_sz, nat_nlist[3].n_value, + sizeof(nsp->ns_nattab_sz)); + kmemcpy((char *)&nsp->ns_rultab_sz, nat_nlist[4].n_value, + sizeof(nsp->ns_rultab_sz)); + kmemcpy((char *)&nsp->ns_rdrtab_sz, nat_nlist[5].n_value, + sizeof(nsp->ns_rdrtab_sz)); + kmemcpy((char *)&nsp->ns_hostmap_sz, nat_nlist[6].n_value, + sizeof(nsp->ns_hostmap_sz)); + kmemcpy((char *)&nsp->ns_instances, nat_nlist[7].n_value, + sizeof(nsp->ns_instances)); +} + + +/* + * Issue an ioctl to flush either the NAT rules table or the active mapping + * table or both. + */ +void flushtable(fd, opts, match) + int fd, opts, *match; +{ + int n = 0; + + if (opts & OPT_FLUSH) { + n = 0; + if (!(opts & OPT_DONOTHING)) { + if (match != NULL) { + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = match[0] * sizeof(int); + obj.ipfo_type = IPFOBJ_IPFEXPR; + obj.ipfo_ptr = match; + if (ioctl(fd, SIOCMATCHFLUSH, &obj) == -1) { + ipferror(fd, "ioctl(SIOCMATCHFLUSH)"); + n = -1; + } else { + n = obj.ipfo_retval; + } + } else if (ioctl(fd, SIOCIPFFL, &n) == -1) { + ipferror(fd, "ioctl(SIOCIPFFL)"); + n = -1; + } + } + if (n >= 0) + printf("%d entries flushed from NAT table\n", n); + } + + if (opts & OPT_CLEAR) { + n = 1; + if (!(opts & OPT_DONOTHING) && ioctl(fd, SIOCIPFFL, &n) == -1) + ipferror(fd, "ioctl(SIOCCNATL)"); + else + printf("%d entries flushed from NAT list\n", n); + } +} + + +/* + * Display NAT statistics. + */ +void dostats_dead(nsp, opts, filter) + natstat_t *nsp; + int opts, *filter; +{ + nat_t *np, nat; + ipnat_t ipn; + int i; + + if (nat_fields == NULL) { + printf("List of active MAP/Redirect filters:\n"); + while (nsp->ns_list) { + if (kmemcpy((char *)&ipn, (long)nsp->ns_list, + sizeof(ipn))) { + perror("kmemcpy"); + break; + } + if (opts & OPT_HITS) + printf("%lu ", ipn.in_hits); + printnat(&ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); + nsp->ns_list = ipn.in_next; + } + } + + if (nat_fields == NULL) { + printf("\nList of active sessions:\n"); + + } else if (nohdrfields == 0) { + for (i = 0; nat_fields[i].w_value != 0; i++) { + printfieldhdr(natfields, nat_fields + i); + if (nat_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } + + for (np = nsp->ns_instances; np; np = nat.nat_next) { + if (kmemcpy((char *)&nat, (long)np, sizeof(nat))) + break; + if ((filter != NULL) && (nat_matcharray(&nat, filter) == 0)) + continue; + if (nat_fields != NULL) { + for (i = 0; nat_fields[i].w_value != 0; i++) { + printnatfield(&nat, nat_fields[i].w_value); + if (nat_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else { + printactivenat(&nat, opts, nsp->ns_ticks); + if (nat.nat_aps) { + int proto; + + if (nat.nat_dir & NAT_OUTBOUND) + proto = nat.nat_pr[1]; + else + proto = nat.nat_pr[0]; + printaps(nat.nat_aps, opts, proto); + } + } + } + + if (opts & OPT_VERBOSE) + showhostmap_dead(nsp); +} + + +void dotable(nsp, fd, alive, which, side) + natstat_t *nsp; + int fd, alive, which; + char *side; +{ + int sz, i, used, maxlen, minlen, totallen; + ipftable_t table; + u_int *buckets; + ipfobj_t obj; + + sz = sizeof(*buckets) * nsp->ns_nattab_sz; + buckets = (u_int *)malloc(sz); + if (buckets == NULL) { + fprintf(stderr, + "cannot allocate memory (%d) for buckets\n", sz); + return; + } + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_GTABLE; + obj.ipfo_size = sizeof(table); + obj.ipfo_ptr = &table; + + if (which == 0) { + table.ita_type = IPFTABLE_BUCKETS_NATIN; + } else if (which == 1) { + table.ita_type = IPFTABLE_BUCKETS_NATOUT; + } + table.ita_table = buckets; + + if (alive) { + if (ioctl(fd, SIOCGTABL, &obj) != 0) { + ipferror(fd, "SIOCFTABL"); + free(buckets); + return; + } + } else { + if (kmemcpy((char *)buckets, (u_long)nsp->ns_nattab_sz, sz)) { + free(buckets); + return; + } + } + + minlen = nsp->ns_side[which].ns_inuse; + totallen = 0; + maxlen = 0; + used = 0; + + for (i = 0; i < nsp->ns_nattab_sz; i++) { + if (buckets[i] > maxlen) + maxlen = buckets[i]; + if (buckets[i] < minlen) + minlen = buckets[i]; + if (buckets[i] != 0) + used++; + totallen += buckets[i]; + } + + printf("%d%%\thash efficiency %s\n", + totallen ? used * 100 / totallen : 0, side); + printf("%2.2f%%\tbucket usage %s\n", + ((float)used / nsp->ns_nattab_sz) * 100.0, side); + printf("%d\tminimal length %s\n", minlen, side); + printf("%d\tmaximal length %s\n", maxlen, side); + printf("%.3f\taverage length %s\n", + used ? ((float)totallen / used) : 0.0, side); + + free(buckets); +} + + +void dostats(fd, nsp, opts, alive, filter) + natstat_t *nsp; + int fd, opts, alive, *filter; +{ + /* + * Show statistics ? + */ + if (opts & OPT_STAT) { + printnatside("in", &nsp->ns_side[0]); + dotable(nsp, fd, alive, 0, "in"); + + printnatside("out", &nsp->ns_side[1]); + dotable(nsp, fd, alive, 1, "out"); + + printf("%lu\tlog successes\n", nsp->ns_side[0].ns_log); + printf("%lu\tlog failures\n", nsp->ns_side[1].ns_log); + printf("%lu\tadded in\n%lu\tadded out\n", + nsp->ns_side[0].ns_added, + nsp->ns_side[1].ns_added); + printf("%u\tactive\n", nsp->ns_active); + printf("%lu\ttransparent adds\n", nsp->ns_addtrpnt); + printf("%lu\tdivert build\n", nsp->ns_divert_build); + printf("%lu\texpired\n", nsp->ns_expire); + printf("%lu\tflush all\n", nsp->ns_flush_all); + printf("%lu\tflush closing\n", nsp->ns_flush_closing); + printf("%lu\tflush queue\n", nsp->ns_flush_queue); + printf("%lu\tflush state\n", nsp->ns_flush_state); + printf("%lu\tflush timeout\n", nsp->ns_flush_timeout); + printf("%lu\thostmap new\n", nsp->ns_hm_new); + printf("%lu\thostmap fails\n", nsp->ns_hm_newfail); + printf("%lu\thostmap add\n", nsp->ns_hm_addref); + printf("%lu\thostmap NULL rule\n", nsp->ns_hm_nullnp); + printf("%lu\tlog ok\n", nsp->ns_log_ok); + printf("%lu\tlog fail\n", nsp->ns_log_fail); + printf("%u\torphan count\n", nsp->ns_orphans); + printf("%u\trule count\n", nsp->ns_rules); + printf("%u\tmap rules\n", nsp->ns_rules_map); + printf("%u\trdr rules\n", nsp->ns_rules_rdr); + printf("%u\twilds\n", nsp->ns_wilds); + if (opts & OPT_VERBOSE) + printf("list %p\n", nsp->ns_list); + } + + if (opts & OPT_LIST) { + if (alive) + dostats_live(fd, nsp, opts, filter); + else + dostats_dead(nsp, opts, filter); + } +} + + +/* + * Display NAT statistics. + */ +void dostats_live(fd, nsp, opts, filter) + natstat_t *nsp; + int fd, opts, *filter; +{ + ipfgeniter_t iter; + char buffer[2000]; + ipfobj_t obj; + ipnat_t *ipn; + nat_t nat; + int i; + + bzero((char *)&obj, sizeof(obj)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_GENITER; + obj.ipfo_size = sizeof(iter); + obj.ipfo_ptr = &iter; + + iter.igi_type = IPFGENITER_IPNAT; + iter.igi_nitems = 1; + iter.igi_data = buffer; + ipn = (ipnat_t *)buffer; + + /* + * Show list of NAT rules and NAT sessions ? + */ + if (nat_fields == NULL) { + printf("List of active MAP/Redirect filters:\n"); + while (nsp->ns_list) { + if (ioctl(fd, SIOCGENITER, &obj) == -1) + break; + if (opts & OPT_HITS) + printf("%lu ", ipn->in_hits); + printnat(ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); + nsp->ns_list = ipn->in_next; + } + } + + if (nat_fields == NULL) { + printf("\nList of active sessions:\n"); + + } else if (nohdrfields == 0) { + for (i = 0; nat_fields[i].w_value != 0; i++) { + printfieldhdr(natfields, nat_fields + i); + if (nat_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } + + i = IPFGENITER_IPNAT; + (void) ioctl(fd,SIOCIPFDELTOK, &i); + + + iter.igi_type = IPFGENITER_NAT; + iter.igi_nitems = 1; + iter.igi_data = &nat; + + while (nsp->ns_instances != NULL) { + if (ioctl(fd, SIOCGENITER, &obj) == -1) + break; + if ((filter != NULL) && (nat_matcharray(&nat, filter) == 0)) + continue; + if (nat_fields != NULL) { + for (i = 0; nat_fields[i].w_value != 0; i++) { + printnatfield(&nat, nat_fields[i].w_value); + if (nat_fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else { + printactivenat(&nat, opts, nsp->ns_ticks); + if (nat.nat_aps) { + int proto; + + if (nat.nat_dir & NAT_OUTBOUND) + proto = nat.nat_pr[1]; + else + proto = nat.nat_pr[0]; + printaps(nat.nat_aps, opts, proto); + } + } + nsp->ns_instances = nat.nat_next; + } + + if (opts & OPT_VERBOSE) + showhostmap_live(fd, nsp); + + i = IPFGENITER_NAT; + (void) ioctl(fd,SIOCIPFDELTOK, &i); +} + + +/* + * Display the active host mapping table. + */ +void showhostmap_dead(nsp) + natstat_t *nsp; +{ + hostmap_t hm, *hmp, **maptable; + u_int hv; + + printf("\nList of active host mappings:\n"); + + maptable = (hostmap_t **)malloc(sizeof(hostmap_t *) * + nsp->ns_hostmap_sz); + if (kmemcpy((char *)maptable, (u_long)nsp->ns_maptable, + sizeof(hostmap_t *) * nsp->ns_hostmap_sz)) { + perror("kmemcpy (maptable)"); + return; + } + + for (hv = 0; hv < nsp->ns_hostmap_sz; hv++) { + hmp = maptable[hv]; + + while (hmp) { + if (kmemcpy((char *)&hm, (u_long)hmp, sizeof(hm))) { + perror("kmemcpy (hostmap)"); + return; + } + + printhostmap(&hm, hv); + hmp = hm.hm_next; + } + } + free(maptable); +} + + +/* + * Display the active host mapping table. + */ +void showhostmap_live(fd, nsp) + int fd; + natstat_t *nsp; +{ + ipfgeniter_t iter; + hostmap_t hm; + ipfobj_t obj; + int i; + + bzero((char *)&obj, sizeof(obj)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_GENITER; + obj.ipfo_size = sizeof(iter); + obj.ipfo_ptr = &iter; + + iter.igi_type = IPFGENITER_HOSTMAP; + iter.igi_nitems = 1; + iter.igi_data = &hm; + + printf("\nList of active host mappings:\n"); + + while (nsp->ns_maplist != NULL) { + if (ioctl(fd, SIOCGENITER, &obj) == -1) + break; + printhostmap(&hm, hm.hm_hv); + nsp->ns_maplist = hm.hm_next; + } + + i = IPFGENITER_HOSTMAP; + (void) ioctl(fd,SIOCIPFDELTOK, &i); +} + + +int nat_matcharray(nat, array) + nat_t *nat; + int *array; +{ + int i, n, *x, rv, p; + ipfexp_t *e; + + rv = 0; + n = array[0]; + x = array + 1; + + for (; n > 0; x += 3 + x[3], rv = 0) { + e = (ipfexp_t *)x; + if (e->ipfe_cmd == IPF_EXP_END) + break; + n -= e->ipfe_size; + + p = e->ipfe_cmd >> 16; + if ((p != 0) && (p != nat->nat_pr[1])) + break; + + switch (e->ipfe_cmd) + { + case IPF_EXP_IP_PR : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (nat->nat_pr[1] == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_IP_SRCADDR : + if (nat->nat_v[0] != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((nat->nat_osrcaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_nsrcaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_DSTADDR : + if (nat->nat_v[0] != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((nat->nat_odstaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_ndstaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + + case IPF_EXP_IP_ADDR : + if (nat->nat_v[0] != 4) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= ((nat->nat_osrcaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_nsrcaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_odstaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]) || + ((nat->nat_ndstaddr & + e->ipfe_arg0[i * 2 + 1]) == + e->ipfe_arg0[i * 2]); + } + break; + +#ifdef USE_INET6 + case IPF_EXP_IP6_SRCADDR : + if (nat->nat_v[0] != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&nat->nat_osrc6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_nsrc6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_DSTADDR : + if (nat->nat_v[0] != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&nat->nat_odst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_ndst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; + + case IPF_EXP_IP6_ADDR : + if (nat->nat_v[0] != 6) + break; + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= IP6_MASKEQ(&nat->nat_osrc6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_nsrc6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_odst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]) || + IP6_MASKEQ(&nat->nat_ndst6, + &e->ipfe_arg0[i * 8 + 4], + &e->ipfe_arg0[i * 8]); + } + break; +#endif + + case IPF_EXP_UDP_PORT : + case IPF_EXP_TCP_PORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (nat->nat_osport == e->ipfe_arg0[i]) || + (nat->nat_nsport == e->ipfe_arg0[i]) || + (nat->nat_odport == e->ipfe_arg0[i]) || + (nat->nat_ndport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_SPORT : + case IPF_EXP_TCP_SPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (nat->nat_osport == e->ipfe_arg0[i]) || + (nat->nat_nsport == e->ipfe_arg0[i]); + } + break; + + case IPF_EXP_UDP_DPORT : + case IPF_EXP_TCP_DPORT : + for (i = 0; !rv && i < e->ipfe_narg; i++) { + rv |= (nat->nat_odport == e->ipfe_arg0[i]) || + (nat->nat_ndport == e->ipfe_arg0[i]); + } + break; + } + rv ^= e->ipfe_not; + + if (rv == 0) + break; + } + + return rv; +} diff --git a/sbin/ipf/ipnat/ipnat_y.y b/sbin/ipf/ipnat/ipnat_y.y new file mode 100644 index 000000000000..a6a5a0e49d76 --- /dev/null +++ b/sbin/ipf/ipnat/ipnat_y.y @@ -0,0 +1,1774 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +%{ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#if !defined(__SVR4) && !defined(__GNUC__) +#include <strings.h> +#endif +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <stdlib.h> +#include <stddef.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <sys/time.h> +#include <syslog.h> +#include <net/if.h> +#include <netdb.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include "ipf.h" +#include "netinet/ipl.h" +#include "ipnat_l.h" + +#define YYDEBUG 1 + +extern void yyerror(char *); +extern int yyparse(void); +extern int yylex(void); +extern int yydebug; +extern FILE *yyin; +extern int yylineNum; + +static ipnat_t *nattop = NULL; +static ipnat_t *nat = NULL; +static int natfd = -1; +static ioctlfunc_t natioctlfunc = NULL; +static addfunc_t nataddfunc = NULL; +static int suggest_port = 0; +static proxyrule_t *prules = NULL; +static int parser_error = 0; + +static void newnatrule(void); +static void setnatproto(int); +static void setmapifnames(void); +static void setrdrifnames(void); +static void proxy_setconfig(int); +static void proxy_unsetconfig(void); +static namelist_t *proxy_dns_add_pass(char *, char *); +static namelist_t *proxy_dns_add_block(char *, char *); +static void proxy_addconfig(char *, int, char *, namelist_t *); +static void proxy_loadconfig(int, ioctlfunc_t, char *, int, + char *, namelist_t *); +static void proxy_loadrules(int, ioctlfunc_t, proxyrule_t *); +static void setmapifnames(void); +static void setrdrifnames(void); +static void setifname(ipnat_t **, int, char *); +static int addname(ipnat_t **, char *); +%} +%union { + char *str; + u_32_t num; + struct { + i6addr_t a; + int f; + } ipa; + frentry_t fr; + frtuc_t *frt; + u_short port; + struct { + int p1; + int p2; + int pc; + } pc; + struct { + i6addr_t a; + i6addr_t m; + int t; /* Address type */ + int u; + int f; /* Family */ + int v; /* IP version */ + int s; /* 0 = number, 1 = text */ + int n; /* number */ + } ipp; + union i6addr ip6; + namelist_t *names; +}; + +%token <num> YY_NUMBER YY_HEX +%token <str> YY_STR +%token YY_COMMENT +%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT +%token YY_RANGE_OUT YY_RANGE_IN +%token <ip6> YY_IPV6 + +%token IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE +%token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY +%token IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY +%token IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG +%token IPNY_TLATE IPNY_POOL IPNY_HASH IPNY_NO IPNY_REWRITE IPNY_PROTO +%token IPNY_ON IPNY_SRC IPNY_DST IPNY_IN IPNY_OUT IPNY_DIVERT +%token IPNY_CONFIG IPNY_ALLOW IPNY_DENY IPNY_DNS IPNY_INET IPNY_INET6 +%token IPNY_SEQUENTIAL IPNY_DSTLIST IPNY_PURGE +%type <port> portspec +%type <num> hexnumber compare range proto +%type <num> saddr daddr sobject dobject mapfrom rdrfrom dip +%type <ipa> hostname ipv4 ipaddr +%type <ipp> addr rhsaddr rhdaddr erhdaddr +%type <pc> portstuff portpair comaports srcports dstports +%type <names> dnslines dnsline +%% +file: line + | assign + | file line + | file assign + | file pconf ';' + ; + +line: xx rule { int err; + while ((nat = nattop) != NULL) { + if (nat->in_v[0] == 0) + nat->in_v[0] = 4; + if (nat->in_v[1] == 0) + nat->in_v[1] = nat->in_v[0]; + nattop = nat->in_next; + err = (*nataddfunc)(natfd, natioctlfunc, nat); + free(nat); + if (err != 0) { + parser_error = err; + break; + } + } + if (parser_error == 0 && prules != NULL) { + proxy_loadrules(natfd, natioctlfunc, prules); + prules = NULL; + } + resetlexer(); + } + | YY_COMMENT + ; + +assign: YY_STR assigning YY_STR ';' { set_variable($1, $3); + resetlexer(); + free($1); + free($3); + yyvarnext = 0; + } + ; + +assigning: + '=' { yyvarnext = 1; } + ; + +xx: { newnatrule(); } + ; + +rule: map eol + | mapblock eol + | redir eol + | rewrite ';' + | divert ';' + ; + +no: IPNY_NO { nat->in_flags |= IPN_NO; } + ; + +eol: | ';' + ; + +map: mapit ifnames addr tlate rhsaddr proxy mapoptions + { if ($3.f != 0 && $3.f != $5.f && $5.f != 0) + yyerror("3.address family mismatch"); + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[0] == 0 && $3.v != 0) + nat->in_v[0] = $3.v; + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[1] == 0 && $3.v != 0) + nat->in_v[1] = $3.v; + nat->in_osrcatype = $3.t; + bcopy(&$3.a, &nat->in_osrc.na_addr[0], + sizeof($3.a)); + bcopy(&$3.m, &nat->in_osrc.na_addr[1], + sizeof($3.a)); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); + } + | mapit ifnames addr tlate rhsaddr mapport mapoptions + { if ($3.f != $5.f && $3.f != 0 && $5.f != 0) + yyerror("4.address family mismatch"); + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[0] == 0 && $3.v != 0) + nat->in_v[0] = $3.v; + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[1] == 0 && $3.v != 0) + nat->in_v[1] = $3.v; + nat->in_osrcatype = $3.t; + bcopy(&$3.a, &nat->in_osrc.na_addr[0], + sizeof($3.a)); + bcopy(&$3.m, &nat->in_osrc.na_addr[1], + sizeof($3.a)); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); + } + | no mapit ifnames addr setproto ';' + { if (nat->in_v[0] == 0) + nat->in_v[0] = $4.v; + nat->in_osrcatype = $4.t; + bcopy(&$4.a, &nat->in_osrc.na_addr[0], + sizeof($4.a)); + bcopy(&$4.m, &nat->in_osrc.na_addr[1], + sizeof($4.a)); + + setmapifnames(); + } + | mapit ifnames mapfrom tlate rhsaddr proxy mapoptions + { if ($3 != 0 && $5.f != 0 && $3 != $5.f) + yyerror("5.address family mismatch"); + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[0] == 0 && $3 != 0) + nat->in_v[0] = ftov($3); + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[1] == 0 && $3 != 0) + nat->in_v[1] = ftov($3); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); + } + | no mapit ifnames mapfrom setproto ';' + { nat->in_v[0] = ftov($4); + setmapifnames(); + } + | mapit ifnames mapfrom tlate rhsaddr mapport mapoptions + { if ($3 != 0 && $5.f != 0 && $3 != $5.f) + yyerror("6.address family mismatch"); + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[0] == 0 && $3 != 0) + nat->in_v[0] = ftov($3); + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[1] == 0 && $3 != 0) + nat->in_v[1] = ftov($3); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); + } + ; + +mapblock: + mapblockit ifnames addr tlate addr ports mapoptions + { if ($3.f != 0 && $5.f != 0 && $3.f != $5.f) + yyerror("7.address family mismatch"); + if (nat->in_v[0] == 0 && $5.v != 0) + nat->in_v[0] = $5.v; + else if (nat->in_v[0] == 0 && $3.v != 0) + nat->in_v[0] = $3.v; + if (nat->in_v[1] == 0 && $5.v != 0) + nat->in_v[1] = $5.v; + else if (nat->in_v[1] == 0 && $3.v != 0) + nat->in_v[1] = $3.v; + nat->in_osrcatype = $3.t; + bcopy(&$3.a, &nat->in_osrc.na_addr[0], + sizeof($3.a)); + bcopy(&$3.m, &nat->in_osrc.na_addr[1], + sizeof($3.a)); + nat->in_nsrcatype = $5.t; + nat->in_nsrcafunc = $5.u; + bcopy(&$5.a, &nat->in_nsrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_nsrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); + } + | no mapblockit ifnames { yyexpectaddr = 1; } addr setproto ';' + { if (nat->in_v[0] == 0) + nat->in_v[0] = $5.v; + if (nat->in_v[1] == 0) + nat->in_v[1] = $5.v; + nat->in_osrcatype = $5.t; + bcopy(&$5.a, &nat->in_osrc.na_addr[0], + sizeof($5.a)); + bcopy(&$5.m, &nat->in_osrc.na_addr[1], + sizeof($5.a)); + + setmapifnames(); + } + ; + +redir: rdrit ifnames addr dport tlate dip nport setproto rdroptions + { if ($6 != 0 && $3.f != 0 && $6 != $3.f) + yyerror("21.address family mismatch"); + if (nat->in_v[0] == 0) { + if ($3.v != AF_UNSPEC) + nat->in_v[0] = ftov($3.f); + else + nat->in_v[0] = ftov($6); + } + nat->in_odstatype = $3.t; + bcopy(&$3.a, &nat->in_odst.na_addr[0], + sizeof($3.a)); + bcopy(&$3.m, &nat->in_odst.na_addr[1], + sizeof($3.a)); + + setrdrifnames(); + } + | no rdrit ifnames addr dport setproto ';' + { if (nat->in_v[0] == 0) + nat->in_v[0] = ftov($4.f); + nat->in_odstatype = $4.t; + bcopy(&$4.a, &nat->in_odst.na_addr[0], + sizeof($4.a)); + bcopy(&$4.m, &nat->in_odst.na_addr[1], + sizeof($4.a)); + + setrdrifnames(); + } + | rdrit ifnames rdrfrom tlate dip nport setproto rdroptions + { if ($5 != 0 && $3 != 0 && $5 != $3) + yyerror("20.address family mismatch"); + if (nat->in_v[0] == 0) { + if ($3 != AF_UNSPEC) + nat->in_v[0] = ftov($3); + else + nat->in_v[0] = ftov($5); + } + setrdrifnames(); + } + | no rdrit ifnames rdrfrom setproto ';' + { nat->in_v[0] = ftov($4); + + setrdrifnames(); + } + ; + +rewrite: + IPNY_REWRITE oninout rwrproto mapfrom tlate newdst newopts + { if (nat->in_v[0] == 0) + nat->in_v[0] = ftov($4); + if (nat->in_redir & NAT_MAP) + setmapifnames(); + else + setrdrifnames(); + nat->in_redir |= NAT_REWRITE; + } + ; + +divert: IPNY_DIVERT oninout rwrproto mapfrom tlate divdst newopts + { if (nat->in_v[0] == 0) + nat->in_v[0] = ftov($4); + if (nat->in_redir & NAT_MAP) { + setmapifnames(); + nat->in_pr[0] = IPPROTO_UDP; + } else { + setrdrifnames(); + nat->in_pr[1] = IPPROTO_UDP; + } + nat->in_flags &= ~IPN_TCP; + } + ; + +tlate: IPNY_TLATE { yyexpectaddr = 1; } + ; + +pconf: IPNY_PROXY { yysetdict(proxies); } + IPNY_DNS '/' proto IPNY_CONFIG YY_STR '{' + { proxy_setconfig(IPNY_DNS); } + dnslines ';' '}' + { proxy_addconfig("dns", $5, $7, $10); + proxy_unsetconfig(); + } + ; + +dnslines: + dnsline { $$ = $1; } + | dnslines ';' dnsline { $$ = $1; $1->na_next = $3; } + ; + +dnsline: + IPNY_ALLOW YY_STR { $$ = proxy_dns_add_pass(NULL, $2); } + | IPNY_DENY YY_STR { $$ = proxy_dns_add_block(NULL, $2); } + | IPNY_ALLOW '.' YY_STR { $$ = proxy_dns_add_pass(".", $3); } + | IPNY_DENY '.' YY_STR { $$ = proxy_dns_add_block(".", $3); } + ; + +oninout: + inout IPNY_ON ifnames { ; } + ; + +inout: IPNY_IN { nat->in_redir = NAT_REDIRECT; } + | IPNY_OUT { nat->in_redir = NAT_MAP; } + ; + +rwrproto: + | IPNY_PROTO setproto + ; + +newdst: src rhsaddr srcports dst erhdaddr dstports + { nat->in_nsrc.na_addr[0] = $2.a; + nat->in_nsrc.na_addr[1] = $2.m; + nat->in_nsrc.na_atype = $2.t; + if ($2.t == FRI_LOOKUP) { + nat->in_nsrc.na_type = $2.u; + nat->in_nsrc.na_subtype = $2.s; + nat->in_nsrc.na_num = $2.n; + } + nat->in_nsports[0] = $3.p1; + nat->in_nsports[1] = $3.p2; + nat->in_ndst.na_addr[0] = $5.a; + nat->in_ndst.na_addr[1] = $5.m; + nat->in_ndst.na_atype = $5.t; + if ($5.t == FRI_LOOKUP) { + nat->in_ndst.na_type = $5.u; + nat->in_ndst.na_subtype = $5.s; + nat->in_ndst.na_num = $5.n; + } + nat->in_ndports[0] = $6.p1; + nat->in_ndports[1] = $6.p2; + } + ; + +divdst: src addr ',' portspec dst addr ',' portspec IPNY_UDP + { nat->in_nsrc.na_addr[0] = $2.a; + if ($2.m.in4.s_addr != 0xffffffff) + yyerror("divert must have /32 dest"); + nat->in_nsrc.na_addr[1] = $2.m; + nat->in_nsports[0] = $4; + nat->in_nsports[1] = $4; + + nat->in_ndst.na_addr[0] = $6.a; + nat->in_ndst.na_addr[1] = $6.m; + if ($6.m.in4.s_addr != 0xffffffff) + yyerror("divert must have /32 dest"); + nat->in_ndports[0] = $8; + nat->in_ndports[1] = $8; + + nat->in_redir |= NAT_DIVERTUDP; + } + ; + +src: IPNY_SRC { yyexpectaddr = 1; } + ; + +dst: IPNY_DST { yyexpectaddr = 1; } + ; + +srcports: + comaports { $$.p1 = $1.p1; + $$.p2 = $1.p2; + } + | IPNY_PORT '=' portspec + { $$.p1 = $3; + $$.p2 = $3; + nat->in_flags |= IPN_FIXEDSPORT; + } + ; + +dstports: + comaports { $$.p1 = $1.p1; + $$.p2 = $1.p2; + } + | IPNY_PORT '=' portspec + { $$.p1 = $3; + $$.p2 = $3; + nat->in_flags |= IPN_FIXEDDPORT; + } + ; + +comaports: + { $$.p1 = 0; + $$.p2 = 0; + } + | ',' { if (!(nat->in_flags & IPN_TCPUDP)) + yyerror("must be TCP/UDP for ports"); + } + portpair { $$.p1 = $3.p1; + $$.p2 = $3.p2; + } + ; + +proxy: | IPNY_PROXY port portspec YY_STR '/' proto + { int pos; + pos = addname(&nat, $4); + nat->in_plabel = pos; + if (nat->in_dcmp == 0) { + nat->in_odport = $3; + } else if ($3 != nat->in_odport) { + yyerror("proxy port numbers not consistant"); + } + nat->in_ndport = $3; + setnatproto($6); + free($4); + } + | IPNY_PROXY port YY_STR YY_STR '/' proto + { int pnum, pos; + pos = addname(&nat, $4); + nat->in_plabel = pos; + pnum = getportproto($3, $6); + if (pnum == -1) + yyerror("invalid port number"); + nat->in_odport = ntohs(pnum); + nat->in_ndport = ntohs(pnum); + setnatproto($6); + free($3); + free($4); + } + | IPNY_PROXY port portspec YY_STR '/' proto IPNY_CONFIG YY_STR + { int pos; + pos = addname(&nat, $4); + nat->in_plabel = pos; + if (nat->in_dcmp == 0) { + nat->in_odport = $3; + } else if ($3 != nat->in_odport) { + yyerror("proxy port numbers not consistant"); + } + nat->in_ndport = $3; + setnatproto($6); + nat->in_pconfig = addname(&nat, $8); + free($4); + free($8); + } + | IPNY_PROXY port YY_STR YY_STR '/' proto IPNY_CONFIG YY_STR + { int pnum, pos; + pos = addname(&nat, $4); + nat->in_plabel = pos; + pnum = getportproto($3, $6); + if (pnum == -1) + yyerror("invalid port number"); + nat->in_odport = ntohs(pnum); + nat->in_ndport = ntohs(pnum); + setnatproto($6); + pos = addname(&nat, $8); + nat->in_pconfig = pos; + free($3); + free($4); + free($8); + } + ; +setproto: + | proto { if (nat->in_pr[0] != 0 || + nat->in_pr[1] != 0 || + nat->in_flags & IPN_TCPUDP) + yyerror("protocol set twice"); + setnatproto($1); + } + | IPNY_TCPUDP { if (nat->in_pr[0] != 0 || + nat->in_pr[1] != 0 || + nat->in_flags & IPN_TCPUDP) + yyerror("protocol set twice"); + nat->in_flags |= IPN_TCPUDP; + nat->in_pr[0] = 0; + nat->in_pr[1] = 0; + } + | IPNY_TCP '/' IPNY_UDP { if (nat->in_pr[0] != 0 || + nat->in_pr[1] != 0 || + nat->in_flags & IPN_TCPUDP) + yyerror("protocol set twice"); + nat->in_flags |= IPN_TCPUDP; + nat->in_pr[0] = 0; + nat->in_pr[1] = 0; + } + ; + +rhsaddr: + addr { $$ = $1; + yyexpectaddr = 0; + } + | hostname '-' { yyexpectaddr = 1; } hostname + { $$.t = FRI_RANGE; + if ($1.f != $4.f) + yyerror("8.address family " + "mismatch"); + $$.f = $1.f; + $$.v = ftov($1.f); + $$.a = $1.a; + $$.m = $4.a; + nat->in_flags |= IPN_SIPRANGE; + yyexpectaddr = 0; + } + | IPNY_RANGE hostname '-' { yyexpectaddr = 1; } hostname + { $$.t = FRI_RANGE; + if ($2.f != $5.f) + yyerror("9.address family " + "mismatch"); + $$.f = $2.f; + $$.v = ftov($2.f); + $$.a = $2.a; + $$.m = $5.a; + nat->in_flags |= IPN_SIPRANGE; + yyexpectaddr = 0; + } + ; + +dip: + hostname ',' { yyexpectaddr = 1; } hostname + { nat->in_flags |= IPN_SPLIT; + if ($1.f != $4.f) + yyerror("10.address family " + "mismatch"); + $$ = $1.f; + nat->in_ndstip6 = $1.a; + nat->in_ndstmsk6 = $4.a; + nat->in_ndstatype = FRI_SPLIT; + yyexpectaddr = 0; + } + | rhdaddr { int bits; + nat->in_ndstip6 = $1.a; + nat->in_ndstmsk6 = $1.m; + nat->in_ndst.na_atype = $1.t; + yyexpectaddr = 0; + if ($1.f == AF_INET) + bits = count4bits($1.m.in4.s_addr); + else + bits = count6bits($1.m.i6); + if (($1.f == AF_INET) && (bits != 0) && + (bits != 32)) { + yyerror("dest ip bitmask not /32"); + } else if (($1.f == AF_INET6) && + (bits != 0) && (bits != 128)) { + yyerror("dest ip bitmask not /128"); + } + $$ = $1.f; + } + ; + +rhdaddr: + addr { $$ = $1; + yyexpectaddr = 0; + } + | hostname '-' hostname { bzero(&$$, sizeof($$)); + $$.t = FRI_RANGE; + if ($1.f != 0 && $3.f != 0 && + $1.f != $3.f) + yyerror("11.address family " + "mismatch"); + $$.a = $1.a; + $$.m = $3.a; + nat->in_flags |= IPN_DIPRANGE; + yyexpectaddr = 0; + } + | IPNY_RANGE hostname '-' hostname + { bzero(&$$, sizeof($$)); + $$.t = FRI_RANGE; + if ($2.f != 0 && $4.f != 0 && + $2.f != $4.f) + yyerror("12.address family " + "mismatch"); + $$.a = $2.a; + $$.m = $4.a; + nat->in_flags |= IPN_DIPRANGE; + yyexpectaddr = 0; + } + ; + +erhdaddr: + rhdaddr { $$ = $1; } + | IPNY_DSTLIST '/' YY_NUMBER { $$.t = FRI_LOOKUP; + $$.u = IPLT_DSTLIST; + $$.s = 0; + $$.n = $3; + } + | IPNY_DSTLIST '/' YY_STR { $$.t = FRI_LOOKUP; + $$.u = IPLT_DSTLIST; + $$.s = 1; + $$.n = addname(&nat, $3); + } + ; + +port: IPNY_PORT { suggest_port = 1; } + ; + +portspec: + YY_NUMBER { if ($1 > 65535) /* Unsigned */ + yyerror("invalid port number"); + else + $$ = $1; + } + | YY_STR { if (getport(NULL, $1, + &($$), NULL) == -1) + yyerror("invalid port number"); + $$ = ntohs($$); + } + ; + +portpair: + portspec { $$.p1 = $1; $$.p2 = $1; } + | portspec '-' portspec { $$.p1 = $1; $$.p2 = $3; } + | portspec ':' portspec { $$.p1 = $1; $$.p2 = $3; } + ; + +dport: | port portpair { nat->in_odport = $2.p1; + if ($2.p2 == 0) + nat->in_dtop = $2.p1; + else + nat->in_dtop = $2.p2; + } + ; + +nport: | port portpair { nat->in_dpmin = $2.p1; + nat->in_dpnext = $2.p1; + nat->in_dpmax = $2.p2; + nat->in_ndport = $2.p1; + if (nat->in_dtop == 0) + nat->in_dtop = $2.p2; + } + | port '=' portspec { nat->in_dpmin = $3; + nat->in_dpnext = $3; + nat->in_ndport = $3; + if (nat->in_dtop == 0) + nat->in_dtop = nat->in_odport; + nat->in_flags |= IPN_FIXEDDPORT; + } + ; + +ports: | IPNY_PORTS YY_NUMBER { nat->in_spmin = $2; } + | IPNY_PORTS IPNY_AUTO { nat->in_flags |= IPN_AUTOPORTMAP; } + ; + +mapit: IPNY_MAP { nat->in_redir = NAT_MAP; } + | IPNY_BIMAP { nat->in_redir = NAT_BIMAP; } + ; + +rdrit: IPNY_RDR { nat->in_redir = NAT_REDIRECT; } + ; + +mapblockit: + IPNY_MAPBLOCK { nat->in_redir = NAT_MAPBLK; } + ; + +mapfrom: + from sobject to dobject { if ($2 != 0 && $4 != 0 && $2 != $4) + yyerror("13.address family " + "mismatch"); + $$ = $2; + } + | from sobject '!' to dobject + { if ($2 != 0 && $5 != 0 && $2 != $5) + yyerror("14.address family " + "mismatch"); + nat->in_flags |= IPN_NOTDST; + $$ = $2; + } + | from sobject to '!' dobject + { if ($2 != 0 && $5 != 0 && $2 != $5) + yyerror("15.address family " + "mismatch"); + nat->in_flags |= IPN_NOTDST; + $$ = $2; + } + ; + +rdrfrom: + from sobject to dobject { if ($2 != 0 && $4 != 0 && $2 != $4) + yyerror("16.address family " + "mismatch"); + $$ = $2; + } + | '!' from sobject to dobject + { if ($3 != 0 && $5 != 0 && $3 != $5) + yyerror("17.address family " + "mismatch"); + nat->in_flags |= IPN_NOTSRC; + $$ = $3; + } + | from '!' sobject to dobject + { if ($3 != 0 && $5 != 0 && $3 != $5) + yyerror("18.address family " + "mismatch"); + nat->in_flags |= IPN_NOTSRC; + $$ = $3; + } + ; + +from: IPNY_FROM { nat->in_flags |= IPN_FILTER; + yyexpectaddr = 1; + } + ; + +to: IPNY_TO { yyexpectaddr = 1; } + ; + +ifnames: + ifname family { yyexpectaddr = 1; } + | ifname ',' otherifname family { yyexpectaddr = 1; } + ; + +ifname: YY_STR { setifname(&nat, 0, $1); + free($1); + } + ; + +family: | IPNY_INET { nat->in_v[0] = 4; nat->in_v[1] = 4; } + | IPNY_INET6 { nat->in_v[0] = 6; nat->in_v[1] = 6; } + ; + +otherifname: + YY_STR { setifname(&nat, 1, $1); + free($1); + } + ; + +mapport: + IPNY_PORTMAP tcpudp portpair sequential + { nat->in_spmin = $3.p1; + nat->in_spmax = $3.p2; + } + | IPNY_PORTMAP portpair tcpudp sequential + { nat->in_spmin = $2.p1; + nat->in_spmax = $2.p2; + } + | IPNY_PORTMAP tcpudp IPNY_AUTO sequential + { nat->in_flags |= IPN_AUTOPORTMAP; + nat->in_spmin = 1024; + nat->in_spmax = 65535; + } + | IPNY_ICMPIDMAP YY_STR portpair sequential + { if (strcmp($2, "icmp") != 0 && + strcmp($2, "ipv6-icmp") != 0) { + yyerror("icmpidmap not followed by icmp"); + } + free($2); + if ($3.p1 < 0 || $3.p1 > 65535) + yyerror("invalid 1st ICMP Id number"); + if ($3.p2 < 0 || $3.p2 > 65535) + yyerror("invalid 2nd ICMP Id number"); + if (strcmp($2, "ipv6-icmp") == 0) { + nat->in_pr[0] = IPPROTO_ICMPV6; + nat->in_pr[1] = IPPROTO_ICMPV6; + } else { + nat->in_pr[0] = IPPROTO_ICMP; + nat->in_pr[1] = IPPROTO_ICMP; + } + nat->in_flags = IPN_ICMPQUERY; + nat->in_spmin = $3.p1; + nat->in_spmax = $3.p2; + } + ; + +sobject: + saddr { $$ = $1; } + | saddr port portstuff { nat->in_osport = $3.p1; + nat->in_stop = $3.p2; + nat->in_scmp = $3.pc; + $$ = $1; + } + ; + +saddr: addr { nat->in_osrcatype = $1.t; + bcopy(&$1.a, + &nat->in_osrc.na_addr[0], + sizeof($1.a)); + bcopy(&$1.m, + &nat->in_osrc.na_addr[1], + sizeof($1.m)); + $$ = $1.f; + } + ; + +dobject: + daddr { $$ = $1; } + | daddr port portstuff { nat->in_odport = $3.p1; + nat->in_dtop = $3.p2; + nat->in_dcmp = $3.pc; + $$ = $1; + } + ; + +daddr: addr { nat->in_odstatype = $1.t; + bcopy(&$1.a, + &nat->in_odst.na_addr[0], + sizeof($1.a)); + bcopy(&$1.m, + &nat->in_odst.na_addr[1], + sizeof($1.m)); + $$ = $1.f; + } + ; + +addr: IPNY_ANY { yyexpectaddr = 0; + bzero(&$$, sizeof($$)); + $$.t = FRI_NORMAL; + } + | hostname { bzero(&$$, sizeof($$)); + $$.a = $1.a; + $$.t = FRI_NORMAL; + $$.v = ftov($1.f); + $$.f = $1.f; + if ($$.f == AF_INET) { + $$.m.in4.s_addr = 0xffffffff; + } else if ($$.f == AF_INET6) { + $$.m.i6[0] = 0xffffffff; + $$.m.i6[1] = 0xffffffff; + $$.m.i6[2] = 0xffffffff; + $$.m.i6[3] = 0xffffffff; + } + yyexpectaddr = 0; + } + | hostname slash YY_NUMBER + { bzero(&$$, sizeof($$)); + $$.a = $1.a; + $$.f = $1.f; + $$.v = ftov($1.f); + $$.t = FRI_NORMAL; + ntomask($$.f, $3, (u_32_t *)&$$.m); + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + yyexpectaddr = 0; + } + | hostname slash ipaddr { bzero(&$$, sizeof($$)); + if ($1.f != $3.f) { + yyerror("1.address family " + "mismatch"); + } + $$.a = $1.a; + $$.m = $3.a; + $$.t = FRI_NORMAL; + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.f = $1.f; + $$.v = ftov($1.f); + yyexpectaddr = 0; + } + | hostname slash hexnumber { bzero(&$$, sizeof($$)); + $$.a = $1.a; + $$.m.in4.s_addr = htonl($3); + $$.t = FRI_NORMAL; + $$.a.in4.s_addr &= $$.m.in4.s_addr; + $$.f = $1.f; + $$.v = ftov($1.f); + if ($$.f == AF_INET6) + yyerror("incorrect inet6 mask"); + } + | hostname mask ipaddr { bzero(&$$, sizeof($$)); + if ($1.f != $3.f) { + yyerror("2.address family " + "mismatch"); + } + $$.a = $1.a; + $$.m = $3.a; + $$.t = FRI_NORMAL; + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.f = $1.f; + $$.v = ftov($1.f); + yyexpectaddr = 0; + } + | hostname mask hexnumber { bzero(&$$, sizeof($$)); + $$.a = $1.a; + $$.m.in4.s_addr = htonl($3); + $$.t = FRI_NORMAL; + $$.a.in4.s_addr &= $$.m.in4.s_addr; + $$.f = AF_INET; + $$.v = 4; + } + | pool slash YY_NUMBER { bzero(&$$, sizeof($$)); + $$.a.iplookupnum = $3; + $$.a.iplookuptype = IPLT_POOL; + $$.a.iplookupsubtype = 0; + $$.t = FRI_LOOKUP; + } + | pool slash YY_STR { bzero(&$$, sizeof($$)); + $$.a.iplookupname = addname(&nat,$3); + $$.a.iplookuptype = IPLT_POOL; + $$.a.iplookupsubtype = 1; + $$.t = FRI_LOOKUP; + } + | hash slash YY_NUMBER { bzero(&$$, sizeof($$)); + $$.a.iplookupnum = $3; + $$.a.iplookuptype = IPLT_HASH; + $$.a.iplookupsubtype = 0; + $$.t = FRI_LOOKUP; + } + | hash slash YY_STR { bzero(&$$, sizeof($$)); + $$.a.iplookupname = addname(&nat,$3); + $$.a.iplookuptype = IPLT_HASH; + $$.a.iplookupsubtype = 1; + $$.t = FRI_LOOKUP; + } + ; + +slash: '/' { yyexpectaddr = 0; } + ; + +mask: IPNY_MASK { yyexpectaddr = 0; } + ; + +pool: IPNY_POOL { if (!(nat->in_flags & IPN_FILTER)) { + yyerror("Can only use pool with from/to rules\n"); + } + yyexpectaddr = 0; + yyresetdict(); + } + ; + +hash: IPNY_HASH { if (!(nat->in_flags & IPN_FILTER)) { + yyerror("Can only use hash with from/to rules\n"); + } + yyexpectaddr = 0; + yyresetdict(); + } + ; + +portstuff: + compare portspec { $$.pc = $1; $$.p1 = $2; $$.p2 = 0; } + | portspec range portspec { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; } + ; + +mapoptions: + rr frag age mssclamp nattag setproto purge + ; + +rdroptions: + rr frag age sticky mssclamp rdrproxy nattag purge + ; + +nattag: | IPNY_TAG YY_STR { strncpy(nat->in_tag.ipt_tag, $2, + sizeof(nat->in_tag.ipt_tag)); + } +rr: | IPNY_ROUNDROBIN { nat->in_flags |= IPN_ROUNDR; } + ; + +frag: | IPNY_FRAG { nat->in_flags |= IPN_FRAG; } + ; + +age: | IPNY_AGE YY_NUMBER { nat->in_age[0] = $2; + nat->in_age[1] = $2; } + | IPNY_AGE YY_NUMBER '/' YY_NUMBER { nat->in_age[0] = $2; + nat->in_age[1] = $4; } + ; + +sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) && + !(nat->in_flags & IPN_SPLIT)) { + FPRINTF(stderr, + "'sticky' for use with round-robin/IP splitting only\n"); + } else + nat->in_flags |= IPN_STICKY; + } + ; + +mssclamp: + | IPNY_MSSCLAMP YY_NUMBER { nat->in_mssclamp = $2; } + ; + +tcpudp: IPNY_TCP { setnatproto(IPPROTO_TCP); } + | IPNY_UDP { setnatproto(IPPROTO_UDP); } + | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP; + nat->in_pr[0] = 0; + nat->in_pr[1] = 0; + } + | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP; + nat->in_pr[0] = 0; + nat->in_pr[1] = 0; + } + ; + +sequential: + | IPNY_SEQUENTIAL { nat->in_flags |= IPN_SEQUENTIAL; } + ; + +purge: + | IPNY_PURGE { nat->in_flags |= IPN_PURGE; } + ; + +rdrproxy: + IPNY_PROXY YY_STR + { int pos; + pos = addname(&nat, $2); + nat->in_plabel = pos; + nat->in_odport = nat->in_dpnext; + nat->in_dtop = nat->in_odport; + free($2); + } + | proxy { if (nat->in_plabel != -1) { + nat->in_ndport = nat->in_odport; + nat->in_dpmin = nat->in_odport; + nat->in_dpmax = nat->in_dpmin; + nat->in_dtop = nat->in_dpmin; + nat->in_dpnext = nat->in_dpmin; + } + } + ; + +newopts: + | IPNY_PURGE { nat->in_flags |= IPN_PURGE; } + ; + +proto: YY_NUMBER { $$ = $1; + if ($$ != IPPROTO_TCP && + $$ != IPPROTO_UDP) + suggest_port = 0; + } + | IPNY_TCP { $$ = IPPROTO_TCP; } + | IPNY_UDP { $$ = IPPROTO_UDP; } + | YY_STR { $$ = getproto($1); + free($1); + if ($$ == -1) + yyerror("unknown protocol"); + if ($$ != IPPROTO_TCP && + $$ != IPPROTO_UDP) + suggest_port = 0; + } + ; + +hexnumber: + YY_HEX { $$ = $1; } + ; + +hostname: + YY_STR { i6addr_t addr; + int family; + +#ifdef USE_INET6 + if (nat->in_v[0] == 6) + family = AF_INET6; + else +#endif + family = AF_INET; + memset(&($$), 0, sizeof($$)); + memset(&addr, 0, sizeof(addr)); + $$.f = family; + if (gethost(family, $1, + &addr) == 0) { + $$.a = addr; + } else { + FPRINTF(stderr, + "Unknown host '%s'\n", + $1); + } + free($1); + } + | YY_NUMBER { memset(&($$), 0, sizeof($$)); + $$.a.in4.s_addr = htonl($1); + if ($$.a.in4.s_addr != 0) + $$.f = AF_INET; + } + | ipv4 { $$ = $1; } + | YY_IPV6 { memset(&($$), 0, sizeof($$)); + $$.a = $1; + $$.f = AF_INET6; + } + | YY_NUMBER YY_IPV6 { memset(&($$), 0, sizeof($$)); + $$.a = $2; + $$.f = AF_INET6; + } + ; + +compare: + '=' { $$ = FR_EQUAL; } + | YY_CMP_EQ { $$ = FR_EQUAL; } + | YY_CMP_NE { $$ = FR_NEQUAL; } + | YY_CMP_LT { $$ = FR_LESST; } + | YY_CMP_LE { $$ = FR_LESSTE; } + | YY_CMP_GT { $$ = FR_GREATERT; } + | YY_CMP_GE { $$ = FR_GREATERTE; } + +range: + YY_RANGE_OUT { $$ = FR_OUTRANGE; } + | YY_RANGE_IN { $$ = FR_INRANGE; } + | ':' { $$ = FR_INCRANGE; } + ; + +ipaddr: ipv4 { $$ = $1; } + | YY_IPV6 { $$.a = $1; + $$.f = AF_INET6; + } + ; + +ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER + { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { + yyerror("Invalid octet string for IP address"); + return 0; + } + bzero((char *)&$$, sizeof($$)); + $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; + $$.a.in4.s_addr = htonl($$.a.in4.s_addr); + $$.f = AF_INET; + } + ; + +%% + + +static wordtab_t proxies[] = { + { "dns", IPNY_DNS } +}; + +static wordtab_t dnswords[] = { + { "allow", IPNY_ALLOW }, + { "block", IPNY_DENY }, + { "deny", IPNY_DENY }, + { "drop", IPNY_DENY }, + { "pass", IPNY_ALLOW }, + +}; + +static wordtab_t yywords[] = { + { "age", IPNY_AGE }, + { "any", IPNY_ANY }, + { "auto", IPNY_AUTO }, + { "bimap", IPNY_BIMAP }, + { "config", IPNY_CONFIG }, + { "divert", IPNY_DIVERT }, + { "dst", IPNY_DST }, + { "dstlist", IPNY_DSTLIST }, + { "frag", IPNY_FRAG }, + { "from", IPNY_FROM }, + { "hash", IPNY_HASH }, + { "icmpidmap", IPNY_ICMPIDMAP }, + { "in", IPNY_IN }, + { "inet", IPNY_INET }, + { "inet6", IPNY_INET6 }, + { "mask", IPNY_MASK }, + { "map", IPNY_MAP }, + { "map-block", IPNY_MAPBLOCK }, + { "mssclamp", IPNY_MSSCLAMP }, + { "netmask", IPNY_MASK }, + { "no", IPNY_NO }, + { "on", IPNY_ON }, + { "out", IPNY_OUT }, + { "pool", IPNY_POOL }, + { "port", IPNY_PORT }, + { "portmap", IPNY_PORTMAP }, + { "ports", IPNY_PORTS }, + { "proto", IPNY_PROTO }, + { "proxy", IPNY_PROXY }, + { "purge", IPNY_PURGE }, + { "range", IPNY_RANGE }, + { "rewrite", IPNY_REWRITE }, + { "rdr", IPNY_RDR }, + { "round-robin",IPNY_ROUNDROBIN }, + { "sequential", IPNY_SEQUENTIAL }, + { "src", IPNY_SRC }, + { "sticky", IPNY_STICKY }, + { "tag", IPNY_TAG }, + { "tcp", IPNY_TCP }, + { "tcpudp", IPNY_TCPUDP }, + { "to", IPNY_TO }, + { "udp", IPNY_UDP }, + { "-", '-' }, + { "->", IPNY_TLATE }, + { "eq", YY_CMP_EQ }, + { "ne", YY_CMP_NE }, + { "lt", YY_CMP_LT }, + { "gt", YY_CMP_GT }, + { "le", YY_CMP_LE }, + { "ge", YY_CMP_GE }, + { NULL, 0 } +}; + + +int +ipnat_parsefile(fd, addfunc, ioctlfunc, filename) + int fd; + addfunc_t addfunc; + ioctlfunc_t ioctlfunc; + char *filename; +{ + FILE *fp = NULL; + int rval; + char *s; + + yylineNum = 1; + + (void) yysettab(yywords); + + s = getenv("YYDEBUG"); + if (s) + yydebug = atoi(s); + else + yydebug = 0; + + if (strcmp(filename, "-")) { + fp = fopen(filename, "r"); + if (!fp) { + FPRINTF(stderr, "fopen(%s) failed: %s\n", filename, + STRERROR(errno)); + return -1; + } + } else + fp = stdin; + + while ((rval = ipnat_parsesome(fd, addfunc, ioctlfunc, fp)) == 0) + ; + if (fp != NULL) + fclose(fp); + if (rval == -1) + rval = 0; + else if (rval != 0) + rval = 1; + return rval; +} + + +int +ipnat_parsesome(fd, addfunc, ioctlfunc, fp) + int fd; + addfunc_t addfunc; + ioctlfunc_t ioctlfunc; + FILE *fp; +{ + char *s; + int i; + + natfd = fd; + parser_error = 0; + nataddfunc = addfunc; + natioctlfunc = ioctlfunc; + + if (feof(fp)) + return -1; + i = fgetc(fp); + if (i == EOF) + return -1; + if (ungetc(i, fp) == EOF) + return -1; + if (feof(fp)) + return -1; + s = getenv("YYDEBUG"); + if (s) + yydebug = atoi(s); + else + yydebug = 0; + + yyin = fp; + yyparse(); + return parser_error; +} + + +static void +newnatrule() +{ + ipnat_t *n; + + n = calloc(1, sizeof(*n)); + if (n == NULL) + return; + + if (nat == NULL) { + nattop = nat = n; + n->in_pnext = &nattop; + } else { + nat->in_next = n; + n->in_pnext = &nat->in_next; + nat = n; + } + + n->in_flineno = yylineNum; + n->in_ifnames[0] = -1; + n->in_ifnames[1] = -1; + n->in_plabel = -1; + n->in_pconfig = -1; + n->in_size = sizeof(*n); + + suggest_port = 0; +} + + +static void +setnatproto(p) + int p; +{ + nat->in_pr[0] = p; + nat->in_pr[1] = p; + + switch (p) + { + case IPPROTO_TCP : + nat->in_flags |= IPN_TCP; + nat->in_flags &= ~IPN_UDP; + break; + case IPPROTO_UDP : + nat->in_flags |= IPN_UDP; + nat->in_flags &= ~IPN_TCP; + break; +#ifdef USE_INET6 + case IPPROTO_ICMPV6 : +#endif + case IPPROTO_ICMP : + nat->in_flags &= ~IPN_TCPUDP; + if (!(nat->in_flags & IPN_ICMPQUERY) && + !(nat->in_redir & NAT_DIVERTUDP)) { + nat->in_dcmp = 0; + nat->in_scmp = 0; + nat->in_dpmin = 0; + nat->in_dpmax = 0; + nat->in_dpnext = 0; + nat->in_spmin = 0; + nat->in_spmax = 0; + nat->in_spnext = 0; + } + break; + default : + if ((nat->in_redir & NAT_MAPBLK) == 0) { + nat->in_flags &= ~IPN_TCPUDP; + nat->in_dcmp = 0; + nat->in_scmp = 0; + nat->in_dpmin = 0; + nat->in_dpmax = 0; + nat->in_dpnext = 0; + nat->in_spmin = 0; + nat->in_spmax = 0; + nat->in_spnext = 0; + } + break; + } + + if ((nat->in_flags & (IPN_TCP|IPN_UDP)) == 0) { + nat->in_stop = 0; + nat->in_dtop = 0; + nat->in_osport = 0; + nat->in_odport = 0; + nat->in_stop = 0; + nat->in_osport = 0; + nat->in_dtop = 0; + nat->in_odport = 0; + } + if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT) + nat->in_flags &= ~IPN_FIXEDDPORT; +} + + +int +ipnat_addrule(fd, ioctlfunc, ptr) + int fd; + ioctlfunc_t ioctlfunc; + void *ptr; +{ + ioctlcmd_t add, del; + ipfobj_t obj; + ipnat_t *ipn; + + ipn = ptr; + bzero((char *)&obj, sizeof(obj)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = ipn->in_size; + obj.ipfo_type = IPFOBJ_IPNAT; + obj.ipfo_ptr = ptr; + + if ((opts & OPT_DONOTHING) != 0) + fd = -1; + + if (opts & OPT_ZERORULEST) { + add = SIOCZRLST; + del = 0; + } else if (opts & OPT_PURGE) { + add = 0; + del = SIOCPURGENAT; + } else { + add = SIOCADNAT; + del = SIOCRMNAT; + } + + if ((opts & OPT_VERBOSE) != 0) + printnat(ipn, opts); + + if (opts & OPT_DEBUG) + binprint(ipn, ipn->in_size); + + if ((opts & OPT_ZERORULEST) != 0) { + if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%d:ioctl(zero nat rule)", + ipn->in_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); + } + } else { + PRINTF("hits %lu ", ipn->in_hits); +#ifdef USE_QUAD_T + PRINTF("bytes %"PRIu64" ", + ipn->in_bytes[0] + ipn->in_bytes[1]); +#else + PRINTF("bytes %lu ", + ipn->in_bytes[0] + ipn->in_bytes[1]); +#endif + printnat(ipn, opts); + } + } else if ((opts & OPT_REMOVE) != 0) { + if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%d:ioctl(delete nat rule)", + ipn->in_flineno); + return ipf_perror_fd(fd, ioctlfunc, msg); + } + } + } else { + if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%d:ioctl(add/insert nat rule)", + ipn->in_flineno); + if (errno == EEXIST) { + int strlen_msg = strlen(msg); + snprintf(msg + strlen_msg, sizeof(msg) -strlen_msg, "(line %d)", + ipn->in_flineno); + } + return ipf_perror_fd(fd, ioctlfunc, msg); + } + } + } + return 0; +} + + +static void +setmapifnames() +{ + if (nat->in_ifnames[1] == -1) + nat->in_ifnames[1] = nat->in_ifnames[0]; + + if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0) + nat->in_flags |= IPN_TCPUDP; + + if ((nat->in_flags & IPN_TCPUDP) == 0) + setnatproto(nat->in_pr[1]); + + if (((nat->in_redir & NAT_MAPBLK) != 0) || + ((nat->in_flags & IPN_AUTOPORTMAP) != 0)) + nat_setgroupmap(nat); +} + + +static void +setrdrifnames() +{ + if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0) + nat->in_flags |= IPN_TCPUDP; + + if ((nat->in_pr[0] == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) && + (nat->in_dpmin != 0 || nat->in_dpmax != 0 || nat->in_dpnext != 0)) + setnatproto(IPPROTO_TCP); + + if (nat->in_ifnames[1] == -1) + nat->in_ifnames[1] = nat->in_ifnames[0]; +} + + +static void +proxy_setconfig(proxy) + int proxy; +{ + if (proxy == IPNY_DNS) { + yysetfixeddict(dnswords); + } +} + + +static void +proxy_unsetconfig() +{ + yyresetdict(); +} + + +static namelist_t * +proxy_dns_add_pass(prefix, name) + char *prefix, *name; +{ + namelist_t *n; + + n = calloc(1, sizeof(*n)); + if (n != NULL) { + if (prefix == NULL || *prefix == '\0') { + n->na_name = strdup(name); + } else { + n->na_name = malloc(strlen(name) + strlen(prefix) + 1); + strcpy(n->na_name, prefix); + strcat(n->na_name, name); + } + } + return n; +} + + +static namelist_t * +proxy_dns_add_block(prefix, name) + char *prefix, *name; +{ + namelist_t *n; + + n = calloc(1, sizeof(*n)); + if (n != NULL) { + if (prefix == NULL || *prefix == '\0') { + n->na_name = strdup(name); + } else { + n->na_name = malloc(strlen(name) + strlen(prefix) + 1); + strcpy(n->na_name, prefix); + strcat(n->na_name, name); + } + n->na_value = 1; + } + return n; +} + + +static void +proxy_addconfig(proxy, proto, conf, list) + char *proxy, *conf; + int proto; + namelist_t *list; +{ + proxyrule_t *pr; + + pr = calloc(1, sizeof(*pr)); + if (pr != NULL) { + pr->pr_proto = proto; + pr->pr_proxy = proxy; + pr->pr_conf = conf; + pr->pr_names = list; + pr->pr_next = prules; + prules = pr; + } +} + + +static void +proxy_loadrules(fd, ioctlfunc, rules) + int fd; + ioctlfunc_t ioctlfunc; + proxyrule_t *rules; +{ + proxyrule_t *pr; + + while ((pr = rules) != NULL) { + proxy_loadconfig(fd, ioctlfunc, pr->pr_proxy, pr->pr_proto, + pr->pr_conf, pr->pr_names); + rules = pr->pr_next; + free(pr->pr_conf); + free(pr); + } +} + + +static void +proxy_loadconfig(fd, ioctlfunc, proxy, proto, conf, list) + int fd; + ioctlfunc_t ioctlfunc; + char *proxy, *conf; + int proto; + namelist_t *list; +{ + namelist_t *na; + ipfobj_t obj; + ap_ctl_t pcmd; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_PROXYCTL; + obj.ipfo_size = sizeof(pcmd); + obj.ipfo_ptr = &pcmd; + + while ((na = list) != NULL) { + if ((opts & OPT_REMOVE) != 0) + pcmd.apc_cmd = APC_CMD_DEL; + else + pcmd.apc_cmd = APC_CMD_ADD; + pcmd.apc_dsize = strlen(na->na_name) + 1; + pcmd.apc_data = na->na_name; + pcmd.apc_arg = na->na_value; + pcmd.apc_p = proto; + + strncpy(pcmd.apc_label, proxy, APR_LABELLEN); + pcmd.apc_label[APR_LABELLEN - 1] = '\0'; + + strncpy(pcmd.apc_config, conf, APR_LABELLEN); + pcmd.apc_config[APR_LABELLEN - 1] = '\0'; + + if ((*ioctlfunc)(fd, SIOCPROXY, (void *)&obj) == -1) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%d:ioctl(add/remove proxy rule)", + yylineNum); + ipf_perror_fd(fd, ioctlfunc, msg); + return; + } + } + + list = na->na_next; + free(na->na_name); + free(na); + } +} + + +static void +setifname(np, idx, name) + ipnat_t **np; + int idx; + char *name; +{ + int pos; + + pos = addname(np, name); + if (pos == -1) + return; + (*np)->in_ifnames[idx] = pos; +} + + +static int +addname(np, name) + ipnat_t **np; + char *name; +{ + ipnat_t *n; + int nlen; + int pos; + + nlen = strlen(name) + 1; + n = realloc(*np, (*np)->in_size + nlen); + if (*np == nattop) + nattop = n; + *np = n; + if (n == NULL) + return -1; + if (n->in_pnext != NULL) + *n->in_pnext = n; + n->in_size += nlen; + pos = n->in_namelen; + n->in_namelen += nlen; + strcpy(n->in_names + pos, name); + n->in_names[n->in_namelen] = '\0'; + return pos; +} diff --git a/sbin/ipf/ippool/Makefile b/sbin/ipf/ippool/Makefile index ab350f223f53..674978ed98c2 100644 --- a/sbin/ipf/ippool/Makefile +++ b/sbin/ipf/ippool/Makefile @@ -2,7 +2,7 @@ PACKAGE= ipf PROG= ippool -SRCS= ${GENHDRS} ippool_y.c ippool_l.c kmem.c ippool.c +SRCS= ${GENHDRS} ippool_y.c ippool_l.c ippool.c MAN= ippool.5 ippool.8 CFLAGS+= -I. diff --git a/sbin/ipf/ippool/ippool.5 b/sbin/ipf/ippool/ippool.5 new file mode 100644 index 000000000000..4de19a4b3625 --- /dev/null +++ b/sbin/ipf/ippool/ippool.5 @@ -0,0 +1,320 @@ +.\" $FreeBSD$ +.\" +.TH IPPOOL 5 +.SH NAME +ippool, ippool.conf \- IP Pool file format +.SH DESCRIPTION +The file ippool.conf is used with ippool(8) to configure address pools for +use with ipnat(8) and ipf(8). +.PP +There are four different types of address pools that can be configured +through ippool.conf. The various types are presented below with a brief +description of how they are used: +.HP +dstlist +.IP +destination list - is a collection of IP addresses with an optional +network interface name that can be used with either redirect (rdr) rules +in ipnat.conf(5) or as the destination in ipf.conf(5) for policy based +routing. +.HP +group-map +.IP +group maps - support the srcgrpmap and dstgrpmap call functions in +ipf.conf(5) by providing a list of addresses or networks rule group +numbers to start processing them with. +.HP +hash +.IP +hash tables - provide the means for performing a very efficient +lookup address or network when there is expected to be only one +exact match. These are best used with more static sets of addresses +so they can be sized optimally. +.HP +pool +.IP +address pools - are an alternative to hash tables that can perform just +as well in most circumstances. In addition, the address pools allow for +heirarchical matching, so it is possible to define a subnet as matching +but then exclude specific addresses from it. +.SS +Evolving Configuration +.PP +Over time the configuration syntax used by ippool.conf(5) has evolved. +Originally the syntax used was more verbose about what a particular +value was being used for, for example: +.PP +.nf +table role = ipf type = tree number = 100 + { 1.1.1.1/32; !2.2.0.0/16; 2.2.2.0/24; ef00::5/128; }; +.fi +.PP +This is rather long winded. The evolution of the configuration syntax +has also replaced the use of numbers with names, although numbers can +still be used as can be seen here: +.PP +.nf +pool ipf/tree (name "100";) + { 1.1.1.1/32; !2.2.0.0/16; 2.2.2.0/24; ef00::5/128; }; +.fi +.PP +Both of the above examples produce the same configuration in the kernel +for use with ipf.conf(5). +.PP +Newer options for use in ippool.conf(5) will only be offered in the new +configuration syntax and all output using "ippool -l" will also be in the +new configuration syntax. +.SS +IPFilter devices and pools +.PP +To cater to different administration styles, ipool.conf(5) allows you to +tie a pool to a specific role in IPFilter. The recognised role names are: +.HP +ipf +.IP +pools defined for role "ipf" are available for use with all rules that are +found in ipf.conf(5) except for auth rules. +.HP +nat +.IP +pools defined for role "nat" are available for use with all rules that are +found in ipnat.conf(5). +.HP +auth +.IP +pools defined for role "auth" are available only for use with "auth" rules +that are found in ipf.conf(5) +.HP +all +.IP +pools that are defined for the "all" role are available to all types of +rules, be they NAT rules in ipnat.conf(5) or firewall rules in ipf.conf(5). +.SH Address Pools +.PP +An address pool can be used in ipf.conf(5) and ipnat.conf(5) for matching +the source or destination address of packets. They can be referred to either +by name or number and can hold an arbitrary number of address patterns to +match. +.PP +An address pool is considered to be a "tree type". In the older configuration +style, it was necessary to have "type=tree" in ippool.conf(5). In the new +style configuration, it follows the IPFilter device with which the pool +is being configured. +Now it is the default if left out. +.PP +For convenience, both IPv4 and IPv6 addresses can be stored in the same +address pool. It should go without saying that either type of packet can +only ever match an entry in a pool that is of the same address family. +.PP +The address pool searches the list of addresses configured for the best +match. The "best match" is considered to be the match that has the highest +number of bits set in the mask. Thus if both 2.2.0.0/16 and 2.2.2.0/24 are +present in an address pool, the addres 2.2.2.1 will match 2.2.2.0/24 and +2.2.1.1 will match 2.2.0.0/16. The reason for this is to allow exceptions +to be added through the use of negative matching. In the following example, +the pool contains "2.2.0.0/16" and "!2.2.2.0/24", meaning that all packets +that match 2.2.0.0/16, except those that match 2.2.2.0/24, will be considered +as a match for this pool. +.PP +table role = ipf type = tree number = 100 + { 1.1.1.1/32; 2.2.0.0/16; !2.2.2.0/24; ef00::5/128; }; +.PP +For the sake of clarity and to aid in managing large numbers of addresses +inside address pools, it is possible to specify a location to load the +addresses from. To do this simply use a "file://" URL where you would +specify an actual IP address. +.PP +.nf +pool ipf/tree (name rfc1918;) { file:///etc/ipf/rfc1918; }; +.fi +.PP +The contents of the file might look something like this: +.PP +.nf +# RFC 1918 networks +10.0.0.0/8 +!127.0.0.0/8 +172.16.0.0/12 +192.168.0.0/24 +.fi +.PP +In this example, the inclusion of the line "!127.0.0.0/8" is, strictly +speaking not correct and serves only as an example to show that negative +matching is also supported in this file. +.PP +Another format that ippool(8) recognises for input from a file is that +from whois servers. In the following example, output from a query to a +WHOIS server for information about which networks are associated with +the name "microsoft" has been saved in a file named "ms-networks". +There is no need to modify the output from the whois server, so using +either the whois command or dumping data directly from it over a TCP +connection works perfectly file as input. +.PP +.nf +pool ipf/tree (name microsoft;) { whois file "/etc/ipf/ms-networks"; }; +.fi +.PP +And to then block all packets to/from networks defined in that file, +a rule like this might be used: +.PP +.nf +block in from pool/microsoft to any +.fi +.PP +Note that there are limitations on the output returned by whois servers +so be aware that their output may not be 100% perfect for your goal. +.SH Destination Lists +.PP +Destination lists are provided for use primarily with NAT redirect rules +(rdr). Their purpose is to allow more sophisticated methods of selecting +which host to send traffic to next than the simple round-robin technique +that is present with with "round-robin" rules in ipnat.conf(5). +.PP +When building a list of hosts to use as a redirection list, it is +necessary to list each host to be used explicitly. Expressing a +collection of hosts as a range or a subnet is not supported. With each +address it is also possible to specify a network interface name. The +network interface name is ignored by NAT when using destination lists. +The network itnerface name is currently only used with policy based +routing (use of "to"/"dup-to" in ipf.conf(5)). +.PP +Unlike the other directives that can be expressed in this file, destination +lists must be written using the new configuration syntax. Each destination +list must have a name associated with it and a next hop selection policy. +Some policies have further options. The currently available selection +policies are: +.HP +round-robin +.IP +steps through the list of hosts configured with the destination list +one by one +.HP +random +.IP +the next hop is chosen by random selection from the list available +.HP +src-hash +.IP +a hash is made of the source address components of the packet +(address and port number) and this is used to select which +next hop address is used +.HP +dst-hash +.IP +a hash is made of the destination address components of the packet +(address and port number) and this is used to select which +next hop address is used +.HP +hash +.IP +a hash is made of all the address components in the packet +(addresses and port numbers) and this is used to select which +next hop address is used +.HP +weighted +.IP +selecting a weighted policy for destination selection needs further +clarification as to what type of weighted selection will be used. +The sub-options to a weighted policy are: +.RS +.HP +connection +.IP +the host that has received the least number of connections is selected +to be the next hop. When all hosts have the same connection count, +the last one used will be the next address selected. +.RE +.PP +The first example here shows 4 destinations that are used with a +round-robin selection policy. +.PP +.nf +pool nat/dstlist (name servers; policy round-robin;) + { 1.1.1.2; 1.1.1.4; 1.1.1.5; 1.1.1.9; }; +.fi +.PP +In the following example, the destination is chosen by whichever has +had the least number of connections. By placing the interface name +with each address and saying "all/dstlist", the destination list can +be used with both ipnat.conf(5) and ipf.conf(5). +.PP +.nf +pool all/dstlist (name servers; policy weighted connection;) + { bge0:1.1.1.2; bge0:1.1.1.4; bge1:1.1.1.5; bge1:1.1.1.9; }; +.fi +.SH Group maps +.PP +Group maps are provided to allow more efficient processing of packets +where there are a larger number of subnets and groups of rules for those +subnets. Group maps are used with "call" rules in ipf.conf(5) that +use the "srcgrpmap" and "dstgrpmap" functions. +.PP +A group map declaration must mention which group is the default group +for all matching addresses to be applied to. Then inside the list of +addresses and networks for the group, each one may optionally have +a group number associated with it. A simple example like this, where +the first two entries would map to group 2020 but 5.0.0.0/8 sends +rule processing to group 2040. +.PP +.nf +group-map out role = ipf number = 2010 group = 2020 + { 2.2.2.2/32; 4.4.0.0/16; 5.0.0.0/8, group = 2040; }; +.fi +.PP +An example that outlines the real purpose of group maps is below, +where each one of the 12 subnets is mapped to a different group +number. This might be because each subnet has its own policy and +rather than write a list of twelve rules in ipf.conf(5) that match +the subnet and branch off with a head statement, a single rule can +be used with this group map to achieve the same result. +.PP +.nf +group-map ( name "2010"; in; ) + { 192.168.1.0/24, group = 10010; 192.168.2.0/24, group = 10020; + 192.168.3.0/24, group = 10030; 192.168.4.0/24, group = 10040; + 192.168.5.0/24, group = 10050; 192.168.6.0/24, group = 10060; + 192.168.7.0/24, group = 10070; 192.168.8.0/24, group = 10080; + 192.168.9.0/24, group = 10090; 192.168.10.0/24, group = 10100; + 192.168.11.0/24, group = 10110; 192.168.12.0/24, group = 10120; + }; +.fi +.PP +The limitation with group maps is that only the source address or the +destination address can be used to map the packet to the starting group, +not both, in your ipf.conf(5) file. +.SH Hash Tables +.PP +The hash table is operationally similar to the address pool. It is +used as a store for a collection of address to match on, saving the +need to write a lengthy list of rules. As with address pools, searching +will attempt to find the best match - an address specification with the +largest contiguous netmask. +.PP +Hash tables are best used where the list of addresses, subnets and +networks is relatively static, which is something of a contrast to +the address pool that can work with either static or changing +address list sizes. +.PP +Further work is still needed to have IPFilter correctly size and tune +the hash table to optimise searching. The goal is to allow for small to +medium sized tables to achieve close to O(1) for either a positive or +negative match, in contrast to the address pool, which is O(logn). +.PP +The following two examples build the same table in the kernel, using +the old configuration format (first) and the new one (second). +.PP +.nf +table role=all type=hash name=servers size=5 + { 1.1.1.2/32; 1.1.1.3/32; 11.23.44.66/32; }; + +pool all/hash (name servers; size 5;) + { 1.1.1.2; 1.1.1.3; 11.23.44.66; }; +.fi +.SH FILES +/dev/iplookup +.br +/etc/ippool.conf +.br +/etc/hosts +.SH SEE ALSO +ippool(8), hosts(5), ipf(5), ipf(8), ipnat(8) diff --git a/sbin/ipf/ippool/ippool.8 b/sbin/ipf/ippool/ippool.8 new file mode 100644 index 000000000000..bcc8f3cbd71d --- /dev/null +++ b/sbin/ipf/ippool/ippool.8 @@ -0,0 +1,130 @@ +.\" $FreeBSD$ +.\" +.TH IPPOOL 8 +.SH NAME +ippool \- user interface to the IPFilter pools +.SH SYNOPSIS +.br +.B ippool +-a [-dnv] [-m <name>] [-o <role>] [-t <type>] [-T ttl] -i <ipaddr>[/<netmask>] +.br +.B ippool +-A [-dnv] [-m <name>] [-o <role>] [-S <seed>] -t <type> +.br +.B ippool +-f <file> [-dnuvR] [-f <file [-dnuvR]] ... +.br +.B ippool +-F [-dv] [-o <role>] [-t <type>] +.br +.B ippool +-l [-dv] [-m <name>] [-t <type>] [-o <role>] [-M <core>] [-N <namelist>] +.br +.B ippool +-r [-dnv] [-m <name>] [-o <role>] [-t <type>] -i <ipaddr>[/<netmask>] +.br +.B ippool +-R [-dnv] [-m <name>] [-o <role>] -t <type> +.br +.B ippool +-s [-dtv] +.SH DESCRIPTION +.PP +.B Ippool +is used to manage information stored in the IP pools subsystem of IPFilter. +Configuration file information may be parsed and loaded into the kernel, +currently configured pools removed or changed as well as inspected. +.PP +The command line options used are broken into two sections: the global +options and the instance specific options. +.SH GLOBAL OPTIONS +.TP +.B \-d +Toggle debugging of processing the configuration file. +.TP +.B \-n +This flag (no-change) prevents +.B ippool +from actually making any ioctl +calls or doing anything which would alter the currently running kernel. +.TP +.B \-v +Turn verbose mode on. +.SH COMMAND OPTIONS +.TP +.B -a +Add a new data node to an existing pool in the kernel. +.TP +.B -A +Add a new (empty) pool to the kernel. +.TP +.B -f <file> +Read in IP pool configuration information from the file and load it into +the kernel. +.TP +.B -F +Flush loaded pools from the kernel. +.TP +.B -l +Display a list of pools currently loaded into the kernel. +.TP +.B -r +Remove an existing data node from a pool in the kernel. +.TP +.B -R +Remove an existing pool from within the kernel. +.TP +.B -s +Display IP pool statistical information. +.SH OPTIONS +.TP +.B -i <ipaddr>[/<netmask>] +Sets the IP address for the operation being undertaken with an +all-one's mask or, optionally, a specific netmask given in either +the dotted-quad notation or a single integer. +.TP +.B -m <name> +Sets the pool name for the current operation. +.TP +.B -M <core> +Specify an alternative path to /dev/kmem to retrieve statistical information +from. +.TP +.B -N <namelist> +Specify an alternative path to lookup symbol name information from when +retrieving statistical information. +.TP +.B -o <role> +Sets the role with which this pool is to be used. Currently only +.B ipf +(the default) is accepted as arguments to this option. +.TP +.B -S <seed> +Sets the hashing seed to the number specified. Only for use with +.B hash +type pools. +.TP +.B -t <type> +Sets the type of pool being defined. Must be one of +.B tree, +.B hash, +.B group-map. +.TP +.B -T <ttl> +Sets the expiration of the node being added. The timeout is expressed +as a number of seconds. +.B tree, +.B hash, +.B group-map. +.TP +.B -u +When parsing a configuration file, rather than load new pool data into the +kernel, unload it. +.TP +.SH FILES +.br +/dev/iplookup +.br +/etc/ippool.conf +.SH SEE ALSO +ippool(5), ipf(8), ipfstat(8) diff --git a/sbin/ipf/ippool/ippool.c b/sbin/ipf/ippool/ippool.c new file mode 100644 index 000000000000..98666f9868fd --- /dev/null +++ b/sbin/ipf/ippool/ippool.c @@ -0,0 +1,1145 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/socket.h> +# include <sys/cdefs.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <ctype.h> +#include <unistd.h> +# include <nlist.h> + +#include "ipf.h" +#include "netinet/ipl.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_pool.h" +#include "netinet/ip_htable.h" +#include "kmem.h" + + +extern int ippool_yyparse(void); +extern int ippool_yydebug; +extern FILE *ippool_yyin; +extern char *optarg; +extern int lineNum; + +void usage(char *); +int main(int, char **); +int poolcommand(int, int, char *[]); +int poolnodecommand(int, int, char *[]); +int loadpoolfile(int, char *[], char *); +int poollist(int, char *[]); +void poollist_dead(int, char *, int, char *, char *); +void poollist_live(int, char *, int, int); +int poolflush(int, char *[]); +int poolstats(int, char *[]); +int gettype(char *, u_int *); +int getrole(char *); +int setnodeaddr(int, int, void *ptr, char *arg); +void showpools_live(int, int, ipf_pool_stat_t *, char *); +void showhashs_live(int, int, iphtstat_t *, char *); +void showdstls_live(int, int, ipf_dstl_stat_t *, char *); + +int opts = 0; +int fd = -1; +int use_inet6 = 0; +wordtab_t *pool_fields = NULL; +int nohdrfields = 0; + + +void +usage(prog) + char *prog; +{ + fprintf(stderr, "Usage:\t%s\n", prog); + fprintf(stderr, "\t-a [-dnv] -m <name> [-o <role>] [-t type] [-T ttl] -i <ipaddr>[/netmask]\n"); + fprintf(stderr, "\t-A [-dnv] [-m <name>] [-o <role>] [-S <seed>] [-t <type>]\n"); + fprintf(stderr, "\t-f <file> [-dnuvR]\n"); + fprintf(stderr, "\t-F [-dv] [-o <role>] [-t <type>]\n"); + fprintf(stderr, "\t-l [-dv] [-m <name>] [-t <type>] [-o <role>] [-M <core>] [-N <namelist>]\n"); + fprintf(stderr, "\t-r [-dnv] [-m <name>] [-o <role>] [-t type] -i <ipaddr>[/netmask]\n"); + fprintf(stderr, "\t-R [-dnv] [-m <name>] [-o <role>] [-t <type>]\n"); + fprintf(stderr, "\t-s [-dtv]\n"); + exit(1); +} + + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int err = 1; + + if (argc < 2) + usage(argv[0]); + + assigndefined(getenv("IPPOOL_PREDEFINED")); + + switch (getopt(argc, argv, "aAf:FlrRs")) + { + case 'a' : + err = poolnodecommand(0, argc, argv); + break; + case 'A' : + err = poolcommand(0, argc, argv); + break; + case 'f' : + err = loadpoolfile(argc, argv, optarg); + break; + case 'F' : + err = poolflush(argc, argv); + break; + case 'l' : + err = poollist(argc, argv); + break; + case 'r' : + err = poolnodecommand(1, argc, argv); + break; + case 'R' : + err = poolcommand(1, argc, argv); + break; + case 's' : + err = poolstats(argc, argv); + break; + default : + exit(1); + } + + if (err != 0) + exit(1); + return 0; +} + + +int +poolnodecommand(remove, argc, argv) + int remove, argc; + char *argv[]; +{ + int err = 0, c, ipset, role, type = IPLT_POOL, ttl = 0; + char *poolname = NULL; + ip_pool_node_t pnode; + iphtent_t hnode; + void *ptr = &pnode; + + ipset = 0; + role = IPL_LOGIPF; + bzero((char *)&pnode, sizeof(pnode)); + bzero((char *)&hnode, sizeof(hnode)); + + while ((c = getopt(argc, argv, "di:m:no:t:T:v")) != -1) + switch (c) + { + case 'd' : + opts |= OPT_DEBUG; + ippool_yydebug++; + break; + case 'i' : + if (setnodeaddr(type, role, ptr, optarg) == 0) + ipset = 1; + break; + case 'm' : + poolname = optarg; + break; + case 'n' : + opts |= OPT_DONOTHING|OPT_DONTOPEN; + break; + case 'o' : + if (ipset == 1) { + fprintf(stderr, + "cannot set role after ip address\n"); + return -1; + } + role = getrole(optarg); + if (role == IPL_LOGNONE) + return -1; + break; + case 't' : + if (ipset == 1) { + fprintf(stderr, + "cannot set type after ip address\n"); + return -1; + } + type = gettype(optarg, NULL); + switch (type) { + case IPLT_NONE : + fprintf(stderr, "unknown type '%s'\n", optarg); + return -1; + case IPLT_HASH : + ptr = &hnode; + break; + case IPLT_POOL : + default : + break; + } + break; + case 'T' : + if (remove == 0) { + ttl = atoi(optarg); + if (ttl < 0) { + fprintf(stderr, "cannot set negative ttl\n"); + return -1; + } + } else { + usage(argv[0]); + } + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + default : + usage(argv[0]); + break; /* keep compiler happy */ + } + + if (argc - 1 - optind > 0) + usage(argv[0]); + + if (argv[optind] != NULL && ipset == 0) { + if (setnodeaddr(type, role, ptr, argv[optind]) == 0) + ipset = 1; + } + + if (opts & OPT_DEBUG) + fprintf(stderr, "poolnodecommand: opts = %#x\n", opts); + + if (ipset == 0) { + fprintf(stderr, "no IP address given with -i\n"); + return -1; + } + + if (poolname == NULL) { + fprintf(stderr, "poolname not given with add/remove node\n"); + return -1; + } + + switch (type) { + case IPLT_POOL : + if (remove == 0) + err = load_poolnode(role, poolname, &pnode, ttl, ioctl); + else + err = remove_poolnode(role, poolname, &pnode, ioctl); + break; + case IPLT_HASH : + if (remove == 0) + err = load_hashnode(role, poolname, &hnode, ttl, ioctl); + else + err = remove_hashnode(role, poolname, &hnode, ioctl); + break; + default : + break; + } + return err; +} + + +int +poolcommand(remove, argc, argv) + int remove, argc; + char *argv[]; +{ + int type, role, c, err; + char *poolname, *typearg = NULL; + iphtable_t iph; + ip_pool_t pool; + + err = 1; + role = 0; + type = 0; + poolname = NULL; + role = IPL_LOGIPF; + bzero((char *)&iph, sizeof(iph)); + bzero((char *)&pool, sizeof(pool)); + + while ((c = getopt(argc, argv, "dm:no:S:vt:")) != -1) + switch (c) + { + case 'd' : + opts |= OPT_DEBUG; + ippool_yydebug++; + break; + case 'm' : + poolname = optarg; + break; + case 'n' : + opts |= OPT_DONOTHING|OPT_DONTOPEN; + break; + case 'o' : + role = getrole(optarg); + if (role == IPL_LOGNONE) { + fprintf(stderr, "unknown role '%s'\n", optarg); + return -1; + } + break; + case 'S' : + if (remove == 0) + iph.iph_seed = atoi(optarg); + else + usage(argv[0]); + break; + case 't' : + type = gettype(optarg, &iph.iph_type); + typearg = optarg; + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + default : + usage(argv[0]); + break; /* keep compiler happy */ + } + + if (argc - 1 - optind > 0) + usage(argv[0]); + + if (opts & OPT_DEBUG) + fprintf(stderr, "poolcommand: opts = %#x\n", opts); + + if (poolname == NULL) { + fprintf(stderr, "poolname not given with add/remove pool\n"); + return -1; + } + + if (type == IPLT_NONE && remove == 0) { + if (typearg == NULL) { + fprintf(stderr, "type must be specified\n"); + usage(argv[0]); + } else { + fprintf(stderr, "unknown type '%s'\n", typearg); + } + return -1; + } + + if (type == IPLT_HASH || (type == IPLT_NONE && remove == 1)) { + strncpy(iph.iph_name, poolname, sizeof(iph.iph_name)); + iph.iph_name[sizeof(iph.iph_name) - 1] = '\0'; + iph.iph_unit = role; + } + if (type == IPLT_POOL || (type == IPLT_NONE && remove == 1)) { + strncpy(pool.ipo_name, poolname, sizeof(pool.ipo_name)); + pool.ipo_name[sizeof(pool.ipo_name) - 1] = '\0'; + pool.ipo_unit = role; + } + + if (remove == 0) { + switch (type) + { + case IPLT_HASH : + err = load_hash(&iph, NULL, ioctl); + break; + case IPLT_POOL : + err = load_pool(&pool, ioctl); + break; + } + } else { + switch (type) + { + case IPLT_HASH : + err = remove_hash(&iph, ioctl); + break; + case IPLT_POOL : + err = remove_pool(&pool, ioctl); + break; + case IPLT_NONE : + err = 1; + { + int err_h, err_p; + err_h = remove_hash(&iph, ioctl); + err_p = remove_pool(&pool, ioctl); + if (err_h == 0 || err_p == 0) + err = 0; + } + break; + } + } + return err; +} + + +int +loadpoolfile(argc, argv, infile) + int argc; + char *argv[], *infile; +{ + int c; + + while ((c = getopt(argc, argv, "dnuvf:")) != -1) + switch (c) + { + case 'd' : + opts |= OPT_DEBUG; + ippool_yydebug++; + break; + case 'f' : + if (loadpoolfile(argc, argv, optarg) != 0) + return(-1); + break; + case 'n' : + opts |= OPT_DONOTHING|OPT_DONTOPEN; + break; + case 'u' : + opts |= OPT_REMOVE; + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + default : + usage(argv[0]); + break; /* keep compiler happy */ + } + + if (argc - 1 - optind > 0) + usage(argv[0]); + + if (opts & OPT_DEBUG) + fprintf(stderr, "loadpoolfile: opts = %#x\n", opts); + + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN)) && (fd == -1)) { + fd = open(IPLOOKUP_NAME, O_RDWR); + if (fd == -1) { + perror("open(IPLOOKUP_NAME)"); + exit(1); + } + } + + if (ippool_parsefile(fd, infile, ioctl) != 0) + return -1; + return 0; +} + + +int +poolstats(argc, argv) + int argc; + char *argv[]; +{ + int c, type, role; + ipf_pool_stat_t plstat; + ipf_dstl_stat_t dlstat; + iphtstat_t htstat; + iplookupop_t op; + + type = IPLT_ALL; + role = IPL_LOGALL; + + bzero((char *)&op, sizeof(op)); + + while ((c = getopt(argc, argv, "dM:N:o:t:v")) != -1) + switch (c) + { + case 'd' : + opts |= OPT_DEBUG; + break; + case 'o' : + role = getrole(optarg); + if (role == IPL_LOGNONE) { + fprintf(stderr, "unknown role '%s'\n", optarg); + return -1; + } + break; + case 't' : + type = gettype(optarg, NULL); + if (type != IPLT_POOL) { + fprintf(stderr, + "-s not supported for this type yet\n"); + return -1; + } + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + default : + usage(argv[0]); + break; /* keep compiler happy */ + } + + if (argc - 1 - optind > 0) + usage(argv[0]); + + if (opts & OPT_DEBUG) + fprintf(stderr, "poolstats: opts = %#x\n", opts); + + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN)) && (fd == -1)) { + fd = open(IPLOOKUP_NAME, O_RDWR); + if (fd == -1) { + perror("open(IPLOOKUP_NAME)"); + exit(1); + } + } + + if (type == IPLT_ALL || type == IPLT_POOL) { + op.iplo_type = IPLT_POOL; + op.iplo_struct = &plstat; + op.iplo_size = sizeof(plstat); + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN))) { + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(S0IOCLOOKUPSTAT)"); + return -1; + } + printf("%lu\taddress pools\n", plstat.ipls_pools); + printf("%lu\taddress pool nodes\n", plstat.ipls_nodes); + } + } + + if (type == IPLT_ALL || type == IPLT_HASH) { + op.iplo_type = IPLT_HASH; + op.iplo_struct = &htstat; + op.iplo_size = sizeof(htstat); + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN))) { + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return -1; + } + printf("%lu\thash tables\n", htstat.iphs_numtables); + printf("%lu\thash table nodes\n", htstat.iphs_numnodes); + printf("%lu\thash table no memory \n", + htstat.iphs_nomem); + } + } + + if (type == IPLT_ALL || type == IPLT_DSTLIST) { + op.iplo_type = IPLT_DSTLIST; + op.iplo_struct = &dlstat; + op.iplo_size = sizeof(dlstat); + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN))) { + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return -1; + } + printf("%u\tdestination lists\n", + dlstat.ipls_numlists); + printf("%u\tdestination list nodes\n", + dlstat.ipls_numnodes); + printf("%lu\tdestination list no memory\n", + dlstat.ipls_nomem); + printf("%u\tdestination list zombies\n", + dlstat.ipls_numdereflists); + printf("%u\tdesetination list node zombies\n", + dlstat.ipls_numderefnodes); + } + } + return 0; +} + + +int +poolflush(argc, argv) + int argc; + char *argv[]; +{ + int c, role, type, arg; + iplookupflush_t flush; + + arg = IPLT_ALL; + type = IPLT_ALL; + role = IPL_LOGALL; + + while ((c = getopt(argc, argv, "do:t:v")) != -1) + switch (c) + { + case 'd' : + opts |= OPT_DEBUG; + break; + case 'o' : + role = getrole(optarg); + if (role == IPL_LOGNONE) { + fprintf(stderr, "unknown role '%s'\n", optarg); + return -1; + } + break; + case 't' : + type = gettype(optarg, NULL); + if (type == IPLT_NONE) { + fprintf(stderr, "unknown type '%s'\n", optarg); + return -1; + } + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + default : + usage(argv[0]); + break; /* keep compiler happy */ + } + + if (argc - optind > 0) + usage(argv[0]); + + if (opts & OPT_DEBUG) + fprintf(stderr, "poolflush: opts = %#x\n", opts); + + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN)) && (fd == -1)) { + fd = open(IPLOOKUP_NAME, O_RDWR); + if (fd == -1) { + perror("open(IPLOOKUP_NAME)"); + exit(1); + } + } + + bzero((char *)&flush, sizeof(flush)); + flush.iplf_type = type; + flush.iplf_unit = role; + flush.iplf_arg = arg; + + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN))) { + if (ioctl(fd, SIOCLOOKUPFLUSH, &flush) == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPFLUSH)"); + exit(1); + } + + } + printf("%u object%s flushed\n", flush.iplf_count, + (flush.iplf_count == 1) ? "" : "s"); + + return 0; +} + + +int +getrole(rolename) + char *rolename; +{ + int role; + + if (!strcasecmp(rolename, "ipf")) { + role = IPL_LOGIPF; +#if 0 + } else if (!strcasecmp(rolename, "nat")) { + role = IPL_LOGNAT; + } else if (!strcasecmp(rolename, "state")) { + role = IPL_LOGSTATE; + } else if (!strcasecmp(rolename, "auth")) { + role = IPL_LOGAUTH; + } else if (!strcasecmp(rolename, "sync")) { + role = IPL_LOGSYNC; + } else if (!strcasecmp(rolename, "scan")) { + role = IPL_LOGSCAN; + } else if (!strcasecmp(rolename, "pool")) { + role = IPL_LOGLOOKUP; + } else if (!strcasecmp(rolename, "count")) { + role = IPL_LOGCOUNT; +#endif + } else { + role = IPL_LOGNONE; + } + + return role; +} + + +int +gettype(typename, minor) + char *typename; + u_int *minor; +{ + int type; + + if (!strcasecmp(typename, "tree") || !strcasecmp(typename, "pool")) { + type = IPLT_POOL; + } else if (!strcasecmp(typename, "hash")) { + type = IPLT_HASH; + if (minor != NULL) + *minor = IPHASH_LOOKUP; + } else if (!strcasecmp(typename, "group-map")) { + type = IPLT_HASH; + if (minor != NULL) + *minor = IPHASH_GROUPMAP; + } else { + type = IPLT_NONE; + } + return type; +} + + +int +poollist(argc, argv) + int argc; + char *argv[]; +{ + char *kernel, *core, *poolname; + int c, role, type, live_kernel; + iplookupop_t op; + + core = NULL; + kernel = NULL; + live_kernel = 1; + type = IPLT_ALL; + poolname = NULL; + role = IPL_LOGALL; + + while ((c = getopt(argc, argv, "dm:M:N:o:t:v")) != -1) + switch (c) + { + case 'd' : + opts |= OPT_DEBUG; + break; + case 'm' : + poolname = optarg; + break; + case 'M' : + live_kernel = 0; + core = optarg; + break; + case 'N' : + live_kernel = 0; + kernel = optarg; + break; + case 'o' : + role = getrole(optarg); + if (role == IPL_LOGNONE) { + fprintf(stderr, "unknown role '%s'\n", optarg); + return -1; + } + break; +#if 0 + case 'O' : + /* XXX This option does not work. This function as */ + /* XXX used by state and nat can be used to format */ + /* XXX output especially useful for scripting. It */ + /* XXX is left here with the intention of making */ + /* XXX it work for the same purpose at some point. */ + pool_fields = parsefields(poolfields, optarg); + break; +#endif + case 't' : + type = gettype(optarg, NULL); + if (type == IPLT_NONE) { + fprintf(stderr, "unknown type '%s'\n", optarg); + return -1; + } + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + default : + usage(argv[0]); + break; /* keep compiler happy */ + } + + if (argc - optind > 0) + usage(argv[0]); + + if (opts & OPT_DEBUG) + fprintf(stderr, "poollist: opts = %#x\n", opts); + + if (!(opts & (OPT_DONOTHING|OPT_DONTOPEN)) && (fd == -1)) { + fd = open(IPLOOKUP_NAME, O_RDWR); + if (fd == -1) { + perror("open(IPLOOKUP_NAME)"); + exit(1); + } + } + + bzero((char *)&op, sizeof(op)); + if (poolname != NULL) { + strncpy(op.iplo_name, poolname, sizeof(op.iplo_name)); + op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; + } + op.iplo_unit = role; + + if (live_kernel) + poollist_live(role, poolname, type, fd); + else + poollist_dead(role, poolname, type, kernel, core); + return 0; +} + + +void +poollist_dead(role, poolname, type, kernel, core) + int role, type; + char *poolname, *kernel, *core; +{ + iphtable_t *hptr; + ip_pool_t *ptr; + + if (openkmem(kernel, core) == -1) + exit(-1); + + if (type == IPLT_ALL || type == IPLT_POOL) { + ip_pool_t *pools[IPL_LOGSIZE]; + struct nlist names[2] = { { "ip_pool_list" } , { "" } }; + + if (nlist(kernel, names) != 1) + return; + + bzero(&pools, sizeof(pools)); + if (kmemcpy((char *)&pools, names[0].n_value, sizeof(pools))) + return; + + if (role != IPL_LOGALL) { + ptr = pools[role]; + while (ptr != NULL) { + ptr = printpool(ptr, kmemcpywrap, poolname, + opts, pool_fields); + } + } else { + for (role = 0; role <= IPL_LOGMAX; role++) { + ptr = pools[role]; + while (ptr != NULL) { + ptr = printpool(ptr, kmemcpywrap, + poolname, opts, + pool_fields); + } + } + role = IPL_LOGALL; + } + } + if (type == IPLT_ALL || type == IPLT_HASH) { + iphtable_t *tables[IPL_LOGSIZE]; + struct nlist names[2] = { { "ipf_htables" } , { "" } }; + + if (nlist(kernel, names) != 1) + return; + + bzero(&tables, sizeof(tables)); + if (kmemcpy((char *)&tables, names[0].n_value, sizeof(tables))) + return; + + if (role != IPL_LOGALL) { + hptr = tables[role]; + while (hptr != NULL) { + hptr = printhash(hptr, kmemcpywrap, + poolname, opts, pool_fields); + } + } else { + for (role = 0; role <= IPL_LOGMAX; role++) { + hptr = tables[role]; + while (hptr != NULL) { + hptr = printhash(hptr, kmemcpywrap, + poolname, opts, + pool_fields); + } + } + } + } +} + + +void +poollist_live(role, poolname, type, fd) + int role, type, fd; + char *poolname; +{ + ipf_pool_stat_t plstat; + iplookupop_t op; + int c; + + if (type == IPLT_ALL || type == IPLT_POOL) { + op.iplo_type = IPLT_POOL; + op.iplo_size = sizeof(plstat); + op.iplo_struct = &plstat; + op.iplo_name[0] = '\0'; + op.iplo_arg = 0; + + if (role != IPL_LOGALL) { + op.iplo_unit = role; + + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return; + } + + showpools_live(fd, role, &plstat, poolname); + } else { + for (role = -1; role <= IPL_LOGMAX; role++) { + op.iplo_unit = role; + + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return; + } + + showpools_live(fd, role, &plstat, poolname); + } + + role = IPL_LOGALL; + } + } + + if (type == IPLT_ALL || type == IPLT_HASH) { + iphtstat_t htstat; + + op.iplo_type = IPLT_HASH; + op.iplo_size = sizeof(htstat); + op.iplo_struct = &htstat; + op.iplo_name[0] = '\0'; + op.iplo_arg = 0; + + if (role != IPL_LOGALL) { + op.iplo_unit = role; + + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return; + } + showhashs_live(fd, role, &htstat, poolname); + } else { + for (role = 0; role <= IPL_LOGMAX; role++) { + + op.iplo_unit = role; + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return; + } + + showhashs_live(fd, role, &htstat, poolname); + } + role = IPL_LOGALL; + } + } + + if (type == IPLT_ALL || type == IPLT_DSTLIST) { + ipf_dstl_stat_t dlstat; + + op.iplo_type = IPLT_DSTLIST; + op.iplo_size = sizeof(dlstat); + op.iplo_struct = &dlstat; + op.iplo_name[0] = '\0'; + op.iplo_arg = 0; + + if (role != IPL_LOGALL) { + op.iplo_unit = role; + + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return; + } + showdstls_live(fd, role, &dlstat, poolname); + } else { + for (role = 0; role <= IPL_LOGMAX; role++) { + + op.iplo_unit = role; + c = ioctl(fd, SIOCLOOKUPSTAT, &op); + if (c == -1) { + ipferror(fd, "ioctl(SIOCLOOKUPSTAT)"); + return; + } + + showdstls_live(fd, role, &dlstat, poolname); + } + role = IPL_LOGALL; + } + } +} + + +void +showpools_live(fd, role, plstp, poolname) + int fd, role; + ipf_pool_stat_t *plstp; + char *poolname; +{ + ipflookupiter_t iter; + ip_pool_t pool; + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_LOOKUPITER; + obj.ipfo_size = sizeof(iter); + obj.ipfo_ptr = &iter; + + iter.ili_type = IPLT_POOL; + iter.ili_otype = IPFLOOKUPITER_LIST; + iter.ili_ival = IPFGENITER_LOOKUP; + iter.ili_nitems = 1; + iter.ili_data = &pool; + iter.ili_unit = role; + *iter.ili_name = '\0'; + + bzero((char *)&pool, sizeof(pool)); + + while (plstp->ipls_list[role + 1] != NULL) { + if (ioctl(fd, SIOCLOOKUPITER, &obj)) { + ipferror(fd, "ioctl(SIOCLOOKUPITER)"); + break; + } + if (((pool.ipo_flags & IPOOL_DELETE) == 0) || + ((opts & OPT_DEBUG) != 0)) + printpool_live(&pool, fd, poolname, opts, pool_fields); + + plstp->ipls_list[role + 1] = pool.ipo_next; + } +} + + +void +showhashs_live(fd, role, htstp, poolname) + int fd, role; + iphtstat_t *htstp; + char *poolname; +{ + ipflookupiter_t iter; + iphtable_t table; + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_LOOKUPITER; + obj.ipfo_size = sizeof(iter); + obj.ipfo_ptr = &iter; + + iter.ili_type = IPLT_HASH; + iter.ili_otype = IPFLOOKUPITER_LIST; + iter.ili_ival = IPFGENITER_LOOKUP; + iter.ili_nitems = 1; + iter.ili_data = &table; + iter.ili_unit = role; + *iter.ili_name = '\0'; + + while (htstp->iphs_tables != NULL) { + if (ioctl(fd, SIOCLOOKUPITER, &obj)) { + ipferror(fd, "ioctl(SIOCLOOKUPITER)"); + break; + } + + printhash_live(&table, fd, poolname, opts, pool_fields); + + htstp->iphs_tables = table.iph_next; + } +} + + +void +showdstls_live(fd, role, dlstp, poolname) + int fd, role; + ipf_dstl_stat_t *dlstp; + char *poolname; +{ + ipflookupiter_t iter; + ippool_dst_t table; + ipfobj_t obj; + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_LOOKUPITER; + obj.ipfo_size = sizeof(iter); + obj.ipfo_ptr = &iter; + + iter.ili_type = IPLT_DSTLIST; + iter.ili_otype = IPFLOOKUPITER_LIST; + iter.ili_ival = IPFGENITER_LOOKUP; + iter.ili_nitems = 1; + iter.ili_data = &table; + iter.ili_unit = role; + *iter.ili_name = '\0'; + + while (dlstp->ipls_list[role] != NULL) { + if (ioctl(fd, SIOCLOOKUPITER, &obj)) { + ipferror(fd, "ioctl(SIOCLOOKUPITER)"); + break; + } + + printdstl_live(&table, fd, poolname, opts, pool_fields); + + dlstp->ipls_list[role] = table.ipld_next; + } +} + + +int +setnodeaddr(int type, int role, void *ptr, char *arg) +{ + struct in_addr mask; + sa_family_t family; + char *s; + + if (strchr(arg, ':') == NULL) { + family = AF_INET; + s = strchr(arg, '/'); + if (s == NULL) + mask.s_addr = 0xffffffff; + else if (strchr(s, '.') == NULL) { + if (ntomask(AF_INET, atoi(s + 1), &mask.s_addr) != 0) + return -1; + } else { + mask.s_addr = inet_addr(s + 1); + } + if (s != NULL) + *s = '\0'; + } else { + family = AF_INET6; + + /* XXX for now we use mask for IPv6 prefix length */ + /* XXX mask should be a union with prefix */ + /* XXX Currently address handling is sloppy. */ + + if ((s = strchr(arg, '/')) == NULL) + mask.s_addr = 128; + else + mask.s_addr = atoi(s + 1); + } + + if (type == IPLT_POOL) { + ip_pool_node_t *node = ptr; + + node->ipn_addr.adf_family = family; + +#ifdef USE_INET6 + if (node->ipn_addr.adf_family == AF_INET) { +#endif + node->ipn_addr.adf_len = offsetof(addrfamily_t, + adf_addr) + + sizeof(struct in_addr); + node->ipn_addr.adf_addr.in4.s_addr = inet_addr(arg); +#ifdef USE_INET6 + } else { + node->ipn_addr.adf_len = offsetof(addrfamily_t, + adf_addr) + + sizeof(struct in6_addr); + inet_pton(AF_INET6, arg, + &node->ipn_addr.adf_addr.in6.s6_addr); + } +#endif + node->ipn_mask.adf_len = node->ipn_addr.adf_len; + node->ipn_mask.adf_addr.in4.s_addr = mask.s_addr; + } else if (type == IPLT_HASH) { + iphtent_t *node = ptr; + + node->ipe_family = family; + node->ipe_unit = role; + +#ifdef USE_INET6 + if (node->ipe_family == AF_INET) { +#endif + node->ipe_addr.in4.s_addr = inet_addr(arg); + node->ipe_mask.in4.s_addr = mask.s_addr; +#ifdef USE_INET6 + } else { + inet_pton(AF_INET6, arg, + &node->ipe_addr.in6.__u6_addr.__u6_addr32); + node->ipe_mask.in6.__u6_addr.__u6_addr32[0] = + mask.s_addr; + node->ipe_mask.in6.__u6_addr.__u6_addr32[1] = + node->ipe_mask.in6.__u6_addr.__u6_addr32[2] = + node->ipe_mask.in6.__u6_addr.__u6_addr32[3] = 0; + } +#endif + } + + return 0; +} diff --git a/sbin/ipf/ippool/ippool_y.y b/sbin/ipf/ippool/ippool_y.y new file mode 100644 index 000000000000..03ee1731f24f --- /dev/null +++ b/sbin/ipf/ippool/ippool_y.y @@ -0,0 +1,832 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +%{ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/socket.h> +# include <sys/cdefs.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <ctype.h> +#include <unistd.h> + +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_pool.h" +#include "netinet/ip_htable.h" +#include "netinet/ip_dstlist.h" +#include "ippool_l.h" +#include "kmem.h" + +#define YYDEBUG 1 +#define YYSTACKSIZE 0x00ffffff + +extern int yyparse(void); +extern int yydebug; +extern FILE *yyin; + +static iphtable_t ipht; +static iphtent_t iphte; +static ip_pool_t iplo; +static ippool_dst_t ipld; +static ioctlfunc_t poolioctl = NULL; +static char poolname[FR_GROUPLEN]; + +static iphtent_t *add_htablehosts(char *); +static ip_pool_node_t *add_poolhosts(char *); +static ip_pool_node_t *read_whoisfile(char *); +static void setadflen(addrfamily_t *); + +%} + +%union { + char *str; + u_32_t num; + struct in_addr ip4; + struct alist_s *alist; + addrfamily_t adrmsk[2]; + iphtent_t *ipe; + ip_pool_node_t *ipp; + ipf_dstnode_t *ipd; + addrfamily_t ipa; + i6addr_t ip6; +} + +%token <num> YY_NUMBER YY_HEX +%token <str> YY_STR +%token <ip6> YY_IPV6 +%token YY_COMMENT +%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT +%token YY_RANGE_OUT YY_RANGE_IN +%token IPT_IPF IPT_NAT IPT_COUNT IPT_AUTH IPT_IN IPT_OUT IPT_ALL +%token IPT_TABLE IPT_GROUPMAP IPT_HASH IPT_SRCHASH IPT_DSTHASH +%token IPT_ROLE IPT_TYPE IPT_TREE +%token IPT_GROUP IPT_SIZE IPT_SEED IPT_NUM IPT_NAME IPT_POLICY +%token IPT_POOL IPT_DSTLIST IPT_ROUNDROBIN +%token IPT_WEIGHTED IPT_RANDOM IPT_CONNECTION +%token IPT_WHOIS IPT_FILE +%type <num> role table inout unit dstopts weighting +%type <ipp> ipftree range addrlist +%type <adrmsk> addrmask +%type <ipe> ipfgroup ipfhash hashlist hashentry +%type <ipe> groupentry setgrouplist grouplist +%type <ipa> ipaddr mask +%type <ip4> ipv4 +%type <str> number setgroup name +%type <ipd> dstentry dstentries dstlist + +%% +file: line + | assign + | file line + | file assign + ; + +line: table role ipftree eol { ip_pool_node_t *n; + iplo.ipo_unit = $2; + iplo.ipo_list = $3; + load_pool(&iplo, poolioctl); + while ((n = $3) != NULL) { + $3 = n->ipn_next; + free(n); + } + resetlexer(); + use_inet6 = 0; + } + | table role ipfhash eol { iphtent_t *h; + ipht.iph_unit = $2; + ipht.iph_type = IPHASH_LOOKUP; + load_hash(&ipht, $3, poolioctl); + while ((h = $3) != NULL) { + $3 = h->ipe_next; + free(h); + } + resetlexer(); + use_inet6 = 0; + } + | groupmap role number ipfgroup eol + { iphtent_t *h; + ipht.iph_unit = $2; + strncpy(ipht.iph_name, $3, + sizeof(ipht.iph_name)); + ipht.iph_type = IPHASH_GROUPMAP; + load_hash(&ipht, $4, poolioctl); + while ((h = $4) != NULL) { + $4 = h->ipe_next; + free(h); + } + resetlexer(); + use_inet6 = 0; + } + | YY_COMMENT + | poolline eol + ; + +eol: ';' + ; + +assign: YY_STR assigning YY_STR ';' { set_variable($1, $3); + resetlexer(); + free($1); + free($3); + yyvarnext = 0; + } + ; + +assigning: + '=' { yyvarnext = 1; } + ; + +table: IPT_TABLE { bzero((char *)&ipht, sizeof(ipht)); + bzero((char *)&iphte, sizeof(iphte)); + bzero((char *)&iplo, sizeof(iplo)); + bzero((char *)&ipld, sizeof(ipld)); + *ipht.iph_name = '\0'; + iplo.ipo_flags = IPHASH_ANON; + iplo.ipo_name[0] = '\0'; + } + ; + +groupmap: + IPT_GROUPMAP inout { bzero((char *)&ipht, sizeof(ipht)); + bzero((char *)&iphte, sizeof(iphte)); + *ipht.iph_name = '\0'; + ipht.iph_unit = IPHASH_GROUPMAP; + ipht.iph_flags = $2; + } + ; + +inout: IPT_IN { $$ = FR_INQUE; } + | IPT_OUT { $$ = FR_OUTQUE; } + ; + +role: IPT_ROLE '=' unit { $$ = $3; } + ; + +unit: IPT_IPF { $$ = IPL_LOGIPF; } + | IPT_NAT { $$ = IPL_LOGNAT; } + | IPT_AUTH { $$ = IPL_LOGAUTH; } + | IPT_COUNT { $$ = IPL_LOGCOUNT; } + | IPT_ALL { $$ = IPL_LOGALL; } + ; + +ipftree: + IPT_TYPE '=' IPT_TREE number start addrlist end + { strncpy(iplo.ipo_name, $4, + sizeof(iplo.ipo_name)); + $$ = $6; + } + ; + +ipfhash: + IPT_TYPE '=' IPT_HASH number hashopts start hashlist end + { strncpy(ipht.iph_name, $4, + sizeof(ipht.iph_name)); + $$ = $7; + } + ; + +ipfgroup: + setgroup hashopts start grouplist end + { iphtent_t *e; + for (e = $4; e != NULL; + e = e->ipe_next) + if (e->ipe_group[0] == '\0') + strncpy(e->ipe_group, + $1, + FR_GROUPLEN); + $$ = $4; + free($1); + } + | hashopts start setgrouplist end + { $$ = $3; } + ; + +number: IPT_NUM '=' YY_NUMBER { snprintf(poolname, sizeof(poolname), "%u", $3); + $$ = poolname; + } + | IPT_NAME '=' YY_STR { strncpy(poolname, $3, + FR_GROUPLEN); + poolname[FR_GROUPLEN-1]='\0'; + free($3); + $$ = poolname; + } + | { $$ = ""; } + ; + +setgroup: + IPT_GROUP '=' YY_STR { char tmp[FR_GROUPLEN+1]; + strncpy(tmp, $3, FR_GROUPLEN); + $$ = strdup(tmp); + free($3); + } + | IPT_GROUP '=' YY_NUMBER { char tmp[FR_GROUPLEN+1]; + snprintf(tmp, sizeof(tmp), "%u", $3); + $$ = strdup(tmp); + } + ; + +hashopts: + | size + | seed + | size seed + ; + +addrlist: + ';' { $$ = NULL; } + | range next addrlist { $$ = $1; + while ($1->ipn_next != NULL) + $1 = $1->ipn_next; + $1->ipn_next = $3; + } + | range next { $$ = $1; } + ; + +grouplist: + ';' { $$ = NULL; } + | groupentry next grouplist { $$ = $1; $1->ipe_next = $3; } + | addrmask next grouplist { $$ = calloc(1, sizeof(iphtent_t)); + $$->ipe_addr = $1[0].adf_addr; + $$->ipe_mask = $1[1].adf_addr; + $$->ipe_family = $1[0].adf_family; + $$->ipe_next = $3; + } + | groupentry next { $$ = $1; } + | addrmask next { $$ = calloc(1, sizeof(iphtent_t)); + $$->ipe_addr = $1[0].adf_addr; + $$->ipe_mask = $1[1].adf_addr; +#ifdef USE_INET6 + if (use_inet6) + $$->ipe_family = AF_INET6; + else +#endif + $$->ipe_family = AF_INET; + } + | YY_STR { $$ = add_htablehosts($1); + free($1); + } + ; + +setgrouplist: + ';' { $$ = NULL; } + | groupentry next { $$ = $1; } + | groupentry next setgrouplist { $1->ipe_next = $3; $$ = $1; } + ; + +groupentry: + addrmask ',' setgroup { $$ = calloc(1, sizeof(iphtent_t)); + $$->ipe_addr = $1[0].adf_addr; + $$->ipe_mask = $1[1].adf_addr; + strncpy($$->ipe_group, $3, + FR_GROUPLEN); +#ifdef USE_INET6 + if (use_inet6) + $$->ipe_family = AF_INET6; + else +#endif + $$->ipe_family = AF_INET; + free($3); + } + ; + +range: addrmask { $$ = calloc(1, sizeof(*$$)); + $$->ipn_info = 0; + $$->ipn_addr = $1[0]; + $$->ipn_mask = $1[1]; +#ifdef USE_INET6 + if (use_inet6) + $$->ipn_addr.adf_family = + AF_INET6; + else +#endif + $$->ipn_addr.adf_family = + AF_INET; + } + | '!' addrmask { $$ = calloc(1, sizeof(*$$)); + $$->ipn_info = 1; + $$->ipn_addr = $2[0]; + $$->ipn_mask = $2[1]; +#ifdef USE_INET6 + if (use_inet6) + $$->ipn_addr.adf_family = + AF_INET6; + else +#endif + $$->ipn_addr.adf_family = + AF_INET; + } + | YY_STR { $$ = add_poolhosts($1); + free($1); + } + | IPT_WHOIS IPT_FILE YY_STR { $$ = read_whoisfile($3); + free($3); + } + ; + +hashlist: + ';' { $$ = NULL; } + | hashentry next { $$ = $1; } + | hashentry next hashlist { $1->ipe_next = $3; $$ = $1; } + ; + +hashentry: + addrmask { $$ = calloc(1, sizeof(iphtent_t)); + $$->ipe_addr = $1[0].adf_addr; + $$->ipe_mask = $1[1].adf_addr; +#ifdef USE_INET6 + if (use_inet6) + $$->ipe_family = AF_INET6; + else +#endif + $$->ipe_family = AF_INET; + } + | YY_STR { $$ = add_htablehosts($1); + free($1); + } + ; + +addrmask: + ipaddr '/' mask { $$[0] = $1; + setadflen(&$$[0]); + $$[1] = $3; + $$[1].adf_len = $$[0].adf_len; + } + | ipaddr { $$[0] = $1; + setadflen(&$$[1]); + $$[1].adf_len = $$[0].adf_len; +#ifdef USE_INET6 + if (use_inet6) + memset(&$$[1].adf_addr, 0xff, + sizeof($$[1].adf_addr.in6)); + else +#endif + memset(&$$[1].adf_addr, 0xff, + sizeof($$[1].adf_addr.in4)); + } + ; + +ipaddr: ipv4 { $$.adf_addr.in4 = $1; + $$.adf_family = AF_INET; + setadflen(&$$); + use_inet6 = 0; + } + | YY_NUMBER { $$.adf_addr.in4.s_addr = htonl($1); + $$.adf_family = AF_INET; + setadflen(&$$); + use_inet6 = 0; + } + | YY_IPV6 { $$.adf_addr = $1; + $$.adf_family = AF_INET6; + setadflen(&$$); + use_inet6 = 1; + } + ; + +mask: YY_NUMBER { bzero(&$$, sizeof($$)); + if (use_inet6) { + if (ntomask(AF_INET6, $1, + (u_32_t *)&$$.adf_addr) == -1) + yyerror("bad bitmask"); + } else { + if (ntomask(AF_INET, $1, + (u_32_t *)&$$.adf_addr.in4) == -1) + yyerror("bad bitmask"); + } + } + | ipv4 { bzero(&$$, sizeof($$)); + $$.adf_addr.in4 = $1; + } + | YY_IPV6 { bzero(&$$, sizeof($$)); + $$.adf_addr = $1; + } + ; + +size: IPT_SIZE '=' YY_NUMBER { ipht.iph_size = $3; } + ; + +seed: IPT_SEED '=' YY_NUMBER { ipht.iph_seed = $3; } + ; + +ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER + { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { + yyerror("Invalid octet string for IP address"); + return 0; + } + $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; + $$.s_addr = htonl($$.s_addr); + } + ; + +next: ';' { yyexpectaddr = 1; } + ; + +start: '{' { yyexpectaddr = 1; } + ; + +end: '}' { yyexpectaddr = 0; } + ; + +poolline: + IPT_POOL unit '/' IPT_DSTLIST '(' name ';' dstopts ')' + start dstlist end + { bzero((char *)&ipld, sizeof(ipld)); + strncpy(ipld.ipld_name, $6, + sizeof(ipld.ipld_name)); + ipld.ipld_unit = $2; + ipld.ipld_policy = $8; + load_dstlist(&ipld, poolioctl, $11); + resetlexer(); + use_inet6 = 0; + free($6); + } + | IPT_POOL unit '/' IPT_TREE '(' name ';' ')' + start addrlist end + { bzero((char *)&iplo, sizeof(iplo)); + strncpy(iplo.ipo_name, $6, + sizeof(iplo.ipo_name)); + iplo.ipo_list = $10; + iplo.ipo_unit = $2; + load_pool(&iplo, poolioctl); + resetlexer(); + use_inet6 = 0; + free($6); + } + | IPT_POOL '(' name ';' ')' start addrlist end + { bzero((char *)&iplo, sizeof(iplo)); + strncpy(iplo.ipo_name, $3, + sizeof(iplo.ipo_name)); + iplo.ipo_list = $7; + iplo.ipo_unit = IPL_LOGALL; + load_pool(&iplo, poolioctl); + resetlexer(); + use_inet6 = 0; + free($3); + } + | IPT_POOL unit '/' IPT_HASH '(' name ';' hashoptlist ')' + start hashlist end + { iphtent_t *h; + bzero((char *)&ipht, sizeof(ipht)); + strncpy(ipht.iph_name, $6, + sizeof(ipht.iph_name)); + ipht.iph_unit = $2; + load_hash(&ipht, $11, poolioctl); + while ((h = ipht.iph_list) != NULL) { + ipht.iph_list = h->ipe_next; + free(h); + } + resetlexer(); + use_inet6 = 0; + free($6); + } + | IPT_GROUPMAP '(' name ';' inout ';' ')' + start setgrouplist end + { iphtent_t *h; + bzero((char *)&ipht, sizeof(ipht)); + strncpy(ipht.iph_name, $3, + sizeof(ipht.iph_name)); + ipht.iph_type = IPHASH_GROUPMAP; + ipht.iph_unit = IPL_LOGIPF; + ipht.iph_flags = $5; + load_hash(&ipht, $9, poolioctl); + while ((h = ipht.iph_list) != NULL) { + ipht.iph_list = h->ipe_next; + free(h); + } + resetlexer(); + use_inet6 = 0; + free($3); + } + ; + +name: IPT_NAME YY_STR { $$ = $2; } + | IPT_NUM YY_NUMBER { char name[80]; + snprintf(name, sizeof(name), "%d", $2); + $$ = strdup(name); + } + ; + +hashoptlist: + | hashopt ';' + | hashoptlist ';' hashopt ';' + ; +hashopt: + IPT_SIZE YY_NUMBER + | IPT_SEED YY_NUMBER + ; + +dstlist: + dstentries { $$ = $1; } + | ';' { $$ = NULL; } + ; + +dstentries: + dstentry next { $$ = $1; } + | dstentry next dstentries { $1->ipfd_next = $3; $$ = $1; } + ; + +dstentry: + YY_STR ':' ipaddr { int size = sizeof(*$$) + strlen($1) + 1; + $$ = calloc(1, size); + if ($$ != NULL) { + $$->ipfd_dest.fd_name = strlen($1) + 1; + bcopy($1, $$->ipfd_names, + $$->ipfd_dest.fd_name); + $$->ipfd_dest.fd_addr = $3; + $$->ipfd_size = size; + } + free($1); + } + | ipaddr { $$ = calloc(1, sizeof(*$$)); + if ($$ != NULL) { + $$->ipfd_dest.fd_name = -1; + $$->ipfd_dest.fd_addr = $1; + $$->ipfd_size = sizeof(*$$); + } + } + ; + +dstopts: + { $$ = IPLDP_NONE; } + | IPT_POLICY IPT_ROUNDROBIN ';' { $$ = IPLDP_ROUNDROBIN; } + | IPT_POLICY IPT_WEIGHTED weighting ';' { $$ = $3; } + | IPT_POLICY IPT_RANDOM ';' { $$ = IPLDP_RANDOM; } + | IPT_POLICY IPT_HASH ';' { $$ = IPLDP_HASHED; } + | IPT_POLICY IPT_SRCHASH ';' { $$ = IPLDP_SRCHASH; } + | IPT_POLICY IPT_DSTHASH ';' { $$ = IPLDP_DSTHASH; } + ; + +weighting: + IPT_CONNECTION { $$ = IPLDP_CONNECTION; } + ; +%% +static wordtab_t yywords[] = { + { "all", IPT_ALL }, + { "auth", IPT_AUTH }, + { "connection", IPT_CONNECTION }, + { "count", IPT_COUNT }, + { "dst-hash", IPT_DSTHASH }, + { "dstlist", IPT_DSTLIST }, + { "file", IPT_FILE }, + { "group", IPT_GROUP }, + { "group-map", IPT_GROUPMAP }, + { "hash", IPT_HASH }, + { "in", IPT_IN }, + { "ipf", IPT_IPF }, + { "name", IPT_NAME }, + { "nat", IPT_NAT }, + { "number", IPT_NUM }, + { "out", IPT_OUT }, + { "policy", IPT_POLICY }, + { "pool", IPT_POOL }, + { "random", IPT_RANDOM }, + { "round-robin", IPT_ROUNDROBIN }, + { "role", IPT_ROLE }, + { "seed", IPT_SEED }, + { "size", IPT_SIZE }, + { "src-hash", IPT_SRCHASH }, + { "table", IPT_TABLE }, + { "tree", IPT_TREE }, + { "type", IPT_TYPE }, + { "weighted", IPT_WEIGHTED }, + { "whois", IPT_WHOIS }, + { NULL, 0 } +}; + + +int ippool_parsefile(fd, filename, iocfunc) +int fd; +char *filename; +ioctlfunc_t iocfunc; +{ + FILE *fp = NULL; + char *s; + + yylineNum = 1; + (void) yysettab(yywords); + + s = getenv("YYDEBUG"); + if (s) + yydebug = atoi(s); + else + yydebug = 0; + + if (strcmp(filename, "-")) { + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "fopen(%s) failed: %s\n", filename, + STRERROR(errno)); + return -1; + } + } else + fp = stdin; + + while (ippool_parsesome(fd, fp, iocfunc) == 1) + ; + if (fp != NULL) + fclose(fp); + return 0; +} + + +int ippool_parsesome(fd, fp, iocfunc) +int fd; +FILE *fp; +ioctlfunc_t iocfunc; +{ + char *s; + int i; + + poolioctl = iocfunc; + + if (feof(fp)) + return 0; + i = fgetc(fp); + if (i == EOF) + return 0; + if (ungetc(i, fp) == EOF) + return 0; + if (feof(fp)) + return 0; + s = getenv("YYDEBUG"); + if (s) + yydebug = atoi(s); + else + yydebug = 0; + + yyin = fp; + yyparse(); + return 1; +} + + +static iphtent_t * +add_htablehosts(url) +char *url; +{ + iphtent_t *htop, *hbot, *h; + alist_t *a, *hlist; + + if (!strncmp(url, "file://", 7) || !strncmp(url, "http://", 7)) { + hlist = load_url(url); + } else { + use_inet6 = 0; + + hlist = calloc(1, sizeof(*hlist)); + if (hlist == NULL) + return NULL; + + if (gethost(hlist->al_family, url, &hlist->al_i6addr) == -1) { + yyerror("Unknown hostname"); + } + } + + hbot = NULL; + htop = NULL; + + for (a = hlist; a != NULL; a = a->al_next) { + h = calloc(1, sizeof(*h)); + if (h == NULL) + break; + + h->ipe_family = a->al_family; + h->ipe_addr = a->al_i6addr; + h->ipe_mask = a->al_i6mask; + + if (hbot != NULL) + hbot->ipe_next = h; + else + htop = h; + hbot = h; + } + + alist_free(hlist); + + return htop; +} + + +static ip_pool_node_t * +add_poolhosts(url) +char *url; +{ + ip_pool_node_t *ptop, *pbot, *p; + alist_t *a, *hlist; + + if (!strncmp(url, "file://", 7) || !strncmp(url, "http://", 7)) { + hlist = load_url(url); + } else { + use_inet6 = 0; + + hlist = calloc(1, sizeof(*hlist)); + if (hlist == NULL) + return NULL; + + if (gethost(hlist->al_family, url, &hlist->al_i6addr) == -1) { + yyerror("Unknown hostname"); + } + } + + pbot = NULL; + ptop = NULL; + + for (a = hlist; a != NULL; a = a->al_next) { + p = calloc(1, sizeof(*p)); + if (p == NULL) + break; + p->ipn_mask.adf_addr = a->al_i6mask; + + if (a->al_family == AF_INET) { + p->ipn_addr.adf_family = AF_INET; +#ifdef USE_INET6 + } else if (a->al_family == AF_INET6) { + p->ipn_addr.adf_family = AF_INET6; +#endif + } + setadflen(&p->ipn_addr); + p->ipn_addr.adf_addr = a->al_i6addr; + p->ipn_info = a->al_not; + p->ipn_mask.adf_len = p->ipn_addr.adf_len; + + if (pbot != NULL) + pbot->ipn_next = p; + else + ptop = p; + pbot = p; + } + + alist_free(hlist); + + return ptop; +} + + +ip_pool_node_t * +read_whoisfile(file) + char *file; +{ + ip_pool_node_t *ntop, *ipn, node, *last; + char line[1024]; + FILE *fp; + + fp = fopen(file, "r"); + if (fp == NULL) + return NULL; + + last = NULL; + ntop = NULL; + while (fgets(line, sizeof(line) - 1, fp) != NULL) { + line[sizeof(line) - 1] = '\0'; + + if (parsewhoisline(line, &node.ipn_addr, &node.ipn_mask)) + continue; + ipn = calloc(1, sizeof(*ipn)); + if (ipn == NULL) + continue; + ipn->ipn_addr = node.ipn_addr; + ipn->ipn_mask = node.ipn_mask; + if (last == NULL) + ntop = ipn; + else + last->ipn_next = ipn; + last = ipn; + } + fclose(fp); + return ntop; +} + + +static void +setadflen(afp) + addrfamily_t *afp; +{ + afp->adf_len = offsetof(addrfamily_t, adf_addr); + switch (afp->adf_family) + { + case AF_INET : + afp->adf_len += sizeof(struct in_addr); + break; +#ifdef USE_INET6 + case AF_INET6 : + afp->adf_len += sizeof(struct in6_addr); + break; +#endif + default : + break; + } +} diff --git a/sbin/ipf/ipresend/Makefile b/sbin/ipf/ipresend/Makefile index d9b2ed5ef6e3..a4403d537547 100644 --- a/sbin/ipf/ipresend/Makefile +++ b/sbin/ipf/ipresend/Makefile @@ -5,6 +5,6 @@ PROG= ipresend SRCS= ipresend.c ip.c resend.c sbpf.c sock.c 44arp.c MAN= ipresend.1 -.PATH: ${SRCTOP}/contrib/ipfilter/ipsend +.PATH: ${.CURDIR:H}/ipsend .include <bsd.prog.mk> diff --git a/sbin/ipf/ipscan/Makefile b/sbin/ipf/ipscan/Makefile new file mode 100644 index 000000000000..0c7c8783e785 --- /dev/null +++ b/sbin/ipf/ipscan/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +PACKAGE= ipf +PROG= ipscan +SRCS= ${GENHDRS} ipscan_y.c +MAN= ipscan.5 ipscan.8 +MLINKS= ipscan.5 ipscan.conf.5 + +GENHDRS+= ipscan_y.h +CLEANFILES+= ${GENHDRS} ipscan_y.c + +ipscan_y.c: ipscan_y.y + ${YACC} -d ${.ALLSRC} + +ipscan_y.h: ipscan_y.c + + +.include <bsd.prog.mk> diff --git a/sbin/ipf/ipscan/ipscan.5 b/sbin/ipf/ipscan/ipscan.5 new file mode 100644 index 000000000000..91bf9b0f5ebe --- /dev/null +++ b/sbin/ipf/ipscan/ipscan.5 @@ -0,0 +1,52 @@ +.\" $FreeBSD$ +.\" +.TH IPSCAN 5 +.SH NAME +ipscan, ipscan.conf \- ipscan file format +.SH DESCRIPTION +.PP +WARNING: This feature is to be considered experimental and may change +significantly until a final implementation is drawn up. +.PP +The format for files accept by ipscan currently follow this rough grammar: +.LP +.nf +line ::= name ":" matchup [ "," matchup ] "=" action . +matchup ::= "(" ")" | "(" literal ")" | "(" literal "," match ")" . +action ::= result | result "else" result . +result ::= "close" | "track" | redirect . +redirect ::= "redirect" ip-address [ "(" "," port-number ")" ] . +match ::= { match-char } +match-char ::= "*" | "?" | "." +.fi +.PP +In this example an ip-address is a dotted-quad IPv4 address and a port-number +is a number betwee 1 and 65535, inclusive. The match string is must be of +same length as the literal string that it is matching (literal). The length +of either string is limited to 16 bytes. +.PP +Currently, the redirect option is not yet been implemented. +.LP +.nf +# +# * = match any character, . = exact match, ? = case insensitive +# +# Scan for anything that looks like HTTP and redirect it to the local +# proxy. One catch - this feature (redirect) is not yet implemented. +# +http : ("GET ", "???." ) = redirect(127.0.0.1) +# +# Track ssh connections (i.e do nothing) +# +ssh : (), ("SSH-") = track +# +# Things which look like smtp to be tracked else closed. +# Client can start with EHLO (ESMTP) or HELO (SMTP). +# +smtp : ("HELO ", "**??."), ("220 ", "....") = track else close +# +.fi +.SH FILES +/etc/ipscan.conf +.SH SEE ALSO +ipscan(8) diff --git a/sbin/ipf/ipscan/ipscan.8 b/sbin/ipf/ipscan/ipscan.8 new file mode 100644 index 000000000000..513dc94a8050 --- /dev/null +++ b/sbin/ipf/ipscan/ipscan.8 @@ -0,0 +1,44 @@ +.\" $FreeBSD$ +.\" +.TH IPSCAN 8 +.SH NAME +ipscan \- user interface to the IPFilter content scanning +.SH SYNOPSIS +.B ipscan +[ +.B \-dlnrsv +] [ +] +.B \-f <\fIfilename\fP> +.SH DESCRIPTION +.PP +\fBipscan\fP opens the filename given (treating "\-" as stdin) and parses the +file to build up a content scanning configuration to load into the kernel. +Currently only the first 16 bytes of a connection can be compared. +.SH OPTIONS +.TP +.B \-d +Toggle debugging of processing the configuration file. +.TP +.B \-l +Show the list of currently configured content scanning entries. +.TP +.B \-n +This flag (no-change) prevents \fBipscan\fP from actually making any ioctl +calls or doing anything which would alter the currently running kernel. +.TP +.B \-r +Remove commands from kernel configuration as they are read from the +configuration file rather than adding new ones. +.TP +.B \-s +Retrieve and display content scanning statistics +.TP +.B \-v +Turn verbose mode on. +.DT +.SH FILES +/dev/ipscan +/etc/ipscan.conf +.SH SEE ALSO +ipscan(5), ipf(8) diff --git a/sbin/ipf/ipscan/ipscan_y.y b/sbin/ipf/ipscan/ipscan_y.y new file mode 100644 index 000000000000..b36b66db2b97 --- /dev/null +++ b/sbin/ipf/ipscan/ipscan_y.y @@ -0,0 +1,572 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +%{ +#include <sys/types.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "opts.h" +#include "kmem.h" +#include "ipscan_l.h" +#include "netinet/ip_scan.h" +#include <ctype.h> + +#define YYDEBUG 1 + +extern char *optarg; +extern void yyerror(char *); +extern int yyparse(void); +extern int yylex(void); +extern int yydebug; +extern FILE *yyin; +extern int yylineNum; +extern void printbuf(char *, int, int); + + +void printent(ipscan_t *); +void showlist(void); +int getportnum(char *); +struct in_addr gethostip(char *); +struct in_addr combine(int, int, int, int); +char **makepair(char *, char *); +void addtag(char *, char **, char **, struct action *); +int cram(char *, char *); +void usage(char *); +int main(int, char **); + +int opts = 0; +int fd = -1; + + +%} + +%union { + char *str; + char **astr; + u_32_t num; + struct in_addr ipa; + struct action act; + union i6addr ip6; +} + +%type <str> tag +%type <act> action redirect result +%type <ipa> ipaddr +%type <num> portnum +%type <astr> matchup onehalf twohalves + +%token <num> YY_NUMBER YY_HEX +%token <str> YY_STR +%token YY_COMMENT +%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT +%token YY_RANGE_OUT YY_RANGE_IN +%token <ip6> YY_IPV6 +%token IPSL_START IPSL_STARTGROUP IPSL_CONTENT + +%token IPSL_CLOSE IPSL_TRACK IPSL_EOF IPSL_REDIRECT IPSL_ELSE + +%% +file: line ';' + | assign ';' + | file line ';' + | file assign ';' + | YY_COMMENT + ; + +line: IPSL_START dline + | IPSL_STARTGROUP gline + | IPSL_CONTENT oline + ; + +dline: cline { resetlexer(); } + | sline { resetlexer(); } + | csline { resetlexer(); } + ; + +gline: YY_STR ':' glist '=' action + ; + +oline: cline + | sline + | csline + ; + +assign: YY_STR assigning YY_STR + { set_variable($1, $3); + resetlexer(); + free($1); + free($3); + yyvarnext = 0; + } + ; + +assigning: + '=' { yyvarnext = 1; } + ; + +cline: tag ':' matchup '=' action { addtag($1, $3, NULL, &$5); } + ; + +sline: tag ':' '(' ')' ',' matchup '=' action { addtag($1, NULL, $6, &$8); } + ; + +csline: tag ':' matchup ',' matchup '=' action { addtag($1, $3, $5, &$7); } + ; + +glist: YY_STR + | glist ',' YY_STR + ; + +tag: YY_STR { $$ = $1; } + ; + +matchup: + onehalf { $$ = $1; } + | twohalves { $$ = $1; } + ; + +action: result { $$.act_val = $1.act_val; + $$.act_ip = $1.act_ip; + $$.act_port = $1.act_port; } + | result IPSL_ELSE result { $$.act_val = $1.act_val; + $$.act_else = $3.act_val; + if ($1.act_val == IPSL_REDIRECT) { + $$.act_ip = $1.act_ip; + $$.act_port = $1.act_port; + } + if ($3.act_val == IPSL_REDIRECT) { + $$.act_eip = $3.act_eip; + $$.act_eport = $3.act_eport; + } + } + +result: IPSL_CLOSE { $$.act_val = IPSL_CLOSE; } + | IPSL_TRACK { $$.act_val = IPSL_TRACK; } + | redirect { $$.act_val = IPSL_REDIRECT; + $$.act_ip = $1.act_ip; + $$.act_port = $1.act_port; } + ; + +onehalf: + '(' YY_STR ')' { $$ = makepair($2, NULL); } + ; + +twohalves: + '(' YY_STR ',' YY_STR ')' { $$ = makepair($2, $4); } + ; + +redirect: + IPSL_REDIRECT '(' ipaddr ')' { $$.act_ip = $3; + $$.act_port = 0; } + | IPSL_REDIRECT '(' ipaddr ',' portnum ')' + { $$.act_ip = $3; + $$.act_port = $5; } + ; + + +ipaddr: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER + { $$ = combine($1,$3,$5,$7); } + | YY_STR { $$ = gethostip($1); + free($1); + } + ; + +portnum: + YY_NUMBER { $$ = htons($1); } + | YY_STR { $$ = getportnum($1); + free($1); + } + ; + +%% + + +static struct wordtab yywords[] = { + { "close", IPSL_CLOSE }, + { "content", IPSL_CONTENT }, + { "else", IPSL_ELSE }, + { "start-group", IPSL_STARTGROUP }, + { "redirect", IPSL_REDIRECT }, + { "start", IPSL_START }, + { "track", IPSL_TRACK }, + { NULL, 0 } +}; + + +int cram(dst, src) +char *dst; +char *src; +{ + char c, *s, *t, *u; + int i, j, k; + + c = *src; + s = src + 1; + t = strchr(s, c); + *t = '\0'; + for (u = dst, i = 0; (i <= ISC_TLEN) && (s < t); ) { + c = *s++; + if (c == '\\') { + if (s >= t) + break; + j = k = 0; + do { + c = *s++; + if (j && (!ISDIGIT(c) || (c > '7') || + (k >= 248))) { + *u++ = k, i++; + j = k = 0; + s--; + break; + } + i++; + + if (ISALPHA(c) || (c > '7')) { + switch (c) + { + case 'n' : + *u++ = '\n'; + break; + case 'r' : + *u++ = '\r'; + break; + case 't' : + *u++ = '\t'; + break; + default : + *u++ = c; + break; + } + } else if (ISDIGIT(c)) { + j = 1; + k <<= 3; + k |= (c - '0'); + i--; + } else + *u++ = c; + } while ((i <= ISC_TLEN) && (s <= t) && (j > 0)); + } else + *u++ = c, i++; + } + return i; +} + + +void printent(isc) +ipscan_t *isc; +{ + char buf[ISC_TLEN+1]; + u_char *u; + int i, j; + + buf[ISC_TLEN] = '\0'; + bcopy(isc->ipsc_ctxt, buf, ISC_TLEN); + printf("%s : (\"", isc->ipsc_tag); + printbuf(isc->ipsc_ctxt, isc->ipsc_clen, 0); + + bcopy(isc->ipsc_cmsk, buf, ISC_TLEN); + printf("\", \"%s\"), (\"", buf); + + printbuf(isc->ipsc_stxt, isc->ipsc_slen, 0); + + bcopy(isc->ipsc_smsk, buf, ISC_TLEN); + printf("\", \"%s\") = ", buf); + + switch (isc->ipsc_action) + { + case ISC_A_TRACK : + printf("track"); + break; + case ISC_A_REDIRECT : + printf("redirect"); + printf("(%s", inet_ntoa(isc->ipsc_ip)); + if (isc->ipsc_port) + printf(",%d", isc->ipsc_port); + printf(")"); + break; + case ISC_A_CLOSE : + printf("close"); + break; + default : + break; + } + + if (isc->ipsc_else != ISC_A_NONE) { + printf(" else "); + switch (isc->ipsc_else) + { + case ISC_A_TRACK : + printf("track"); + break; + case ISC_A_REDIRECT : + printf("redirect"); + printf("(%s", inet_ntoa(isc->ipsc_eip)); + if (isc->ipsc_eport) + printf(",%d", isc->ipsc_eport); + printf(")"); + break; + case ISC_A_CLOSE : + printf("close"); + break; + default : + break; + } + } + printf("\n"); + + if (opts & OPT_DEBUG) { + for (u = (u_char *)isc, i = sizeof(*isc); i; ) { + printf("#"); + for (j = 32; (j > 0) && (i > 0); j--, i--) + printf("%s%02x", (j & 7) ? "" : " ", *u++); + printf("\n"); + } + } + if (opts & OPT_VERBOSE) { + printf("# hits %d active %d fref %d sref %d\n", + isc->ipsc_hits, isc->ipsc_active, isc->ipsc_fref, + isc->ipsc_sref); + } +} + + +void addtag(tstr, cp, sp, act) +char *tstr; +char **cp, **sp; +struct action *act; +{ + ipscan_t isc, *iscp; + + bzero((char *)&isc, sizeof(isc)); + + strncpy(isc.ipsc_tag, tstr, sizeof(isc.ipsc_tag)); + isc.ipsc_tag[sizeof(isc.ipsc_tag) - 1] = '\0'; + + if (cp) { + isc.ipsc_clen = cram(isc.ipsc_ctxt, cp[0]); + if (cp[1]) { + if (cram(isc.ipsc_cmsk, cp[1]) != isc.ipsc_clen) { + fprintf(stderr, + "client text/mask strings different length\n"); + return; + } + } + } + + if (sp) { + isc.ipsc_slen = cram(isc.ipsc_stxt, sp[0]); + if (sp[1]) { + if (cram(isc.ipsc_smsk, sp[1]) != isc.ipsc_slen) { + fprintf(stderr, + "server text/mask strings different length\n"); + return; + } + } + } + + if (act->act_val == IPSL_CLOSE) { + isc.ipsc_action = ISC_A_CLOSE; + } else if (act->act_val == IPSL_TRACK) { + isc.ipsc_action = ISC_A_TRACK; + } else if (act->act_val == IPSL_REDIRECT) { + isc.ipsc_action = ISC_A_REDIRECT; + isc.ipsc_ip = act->act_ip; + isc.ipsc_port = act->act_port; + fprintf(stderr, "%d: redirect unsupported\n", yylineNum + 1); + } + + if (act->act_else == IPSL_CLOSE) { + isc.ipsc_else = ISC_A_CLOSE; + } else if (act->act_else == IPSL_TRACK) { + isc.ipsc_else = ISC_A_TRACK; + } else if (act->act_else == IPSL_REDIRECT) { + isc.ipsc_else = ISC_A_REDIRECT; + isc.ipsc_eip = act->act_eip; + isc.ipsc_eport = act->act_eport; + fprintf(stderr, "%d: redirect unsupported\n", yylineNum + 1); + } + + if (!(opts & OPT_DONOTHING)) { + iscp = &isc; + if (opts & OPT_REMOVE) { + if (ioctl(fd, SIOCRMSCA, &iscp) == -1) + perror("SIOCADSCA"); + } else { + if (ioctl(fd, SIOCADSCA, &iscp) == -1) + perror("SIOCADSCA"); + } + } + + if (opts & OPT_VERBOSE) + printent(&isc); +} + + +char **makepair(s1, s2) +char *s1, *s2; +{ + char **a; + + a = malloc(sizeof(char *) * 2); + a[0] = s1; + a[1] = s2; + return a; +} + + +struct in_addr combine(a1, a2, a3, a4) +int a1, a2, a3, a4; +{ + struct in_addr in; + + a1 &= 0xff; + in.s_addr = a1 << 24; + a2 &= 0xff; + in.s_addr |= (a2 << 16); + a3 &= 0xff; + in.s_addr |= (a3 << 8); + a4 &= 0xff; + in.s_addr |= a4; + in.s_addr = htonl(in.s_addr); + return in; +} + + +struct in_addr gethostip(host) +char *host; +{ + struct hostent *hp; + struct in_addr in; + + in.s_addr = 0; + + hp = gethostbyname(host); + if (!hp) + return in; + bcopy(hp->h_addr, (char *)&in, sizeof(in)); + return in; +} + + +int getportnum(port) +char *port; +{ + struct servent *s; + + s = getservbyname(port, "tcp"); + if (s == NULL) + return -1; + return s->s_port; +} + + +void showlist() +{ + ipscanstat_t ipsc, *ipscp = &ipsc; + ipscan_t isc; + + if (ioctl(fd, SIOCGSCST, &ipscp) == -1) + perror("ioctl(SIOCGSCST)"); + else if (opts & OPT_SHOWLIST) { + while (ipsc.iscs_list != NULL) { + if (kmemcpy((char *)&isc, (u_long)ipsc.iscs_list, + sizeof(isc)) == -1) { + perror("kmemcpy"); + break; + } else { + printent(&isc); + ipsc.iscs_list = isc.ipsc_next; + } + } + } else { + printf("scan entries loaded\t%d\n", ipsc.iscs_entries); + printf("scan entries matches\t%ld\n", ipsc.iscs_acted); + printf("negative matches\t%ld\n", ipsc.iscs_else); + } +} + + +void usage(prog) +char *prog; +{ + fprintf(stderr, "Usage:\t%s [-dnrv] -f <filename>\n", prog); + fprintf(stderr, "\t%s [-dlv]\n", prog); + exit(1); +} + + +int main(argc, argv) +int argc; +char *argv[]; +{ + FILE *fp = NULL; + int c; + + (void) yysettab(yywords); + + if (argc < 2) + usage(argv[0]); + + while ((c = getopt(argc, argv, "df:lnrsv")) != -1) + switch (c) + { + case 'd' : + opts |= OPT_DEBUG; + yydebug++; + break; + case 'f' : + if (!strcmp(optarg, "-")) + fp = stdin; + else { + fp = fopen(optarg, "r"); + if (!fp) { + perror("open"); + exit(1); + } + } + yyin = fp; + break; + case 'l' : + opts |= OPT_SHOWLIST; + break; + case 'n' : + opts |= OPT_DONOTHING; + break; + case 'r' : + opts |= OPT_REMOVE; + break; + case 's' : + opts |= OPT_STAT; + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + } + + if (!(opts & OPT_DONOTHING)) { + fd = open(IPL_SCAN, O_RDWR); + if (fd == -1) { + perror("open(IPL_SCAN)"); + exit(1); + } + } + + if (fp != NULL) { + yylineNum = 1; + + while (!feof(fp)) + yyparse(); + fclose(fp); + exit(0); + } + + if (opts & (OPT_SHOWLIST|OPT_STAT)) { + showlist(); + exit(0); + } + exit(1); +} diff --git a/sbin/ipf/ipsend/44arp.c b/sbin/ipf/ipsend/44arp.c new file mode 100644 index 000000000000..80521ad15084 --- /dev/null +++ b/sbin/ipf/ipsend/44arp.c @@ -0,0 +1,118 @@ +/* $FreeBSD$ */ + +/* + * 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 <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <netdb.h> +#include <errno.h> +#include <nlist.h> +#include <stdio.h> +#include "ipsend.h" +#include "iplang/iplang.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 *lim, *buf, *next; + struct rt_msghdr *rtm; + struct sockaddr_in *sin; + struct sockaddr_dl *sdl; + +#ifdef IPSEND + if (arp_getipv4(addr, ether) == 0) + return 0; +#endif + + if (!addr) + return -1; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_FLAGS; +#ifdef RTF_LLINFO + mib[5] = RTF_LLINFO; +#else + mib[5] = 0; +#endif + + 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_in *)(rtm + 1); + sdl = (struct sockaddr_dl *)(sin + 1); + if (!bcmp(addr, (char *)&sin->sin_addr, + sizeof(struct in_addr))) + { + bcopy(LLADDR(sdl), eaddr, sdl->sdl_alen); + return 0; + } + } + return -1; +} diff --git a/sbin/ipf/ipsend/Crashable b/sbin/ipf/ipsend/Crashable new file mode 100644 index 000000000000..c7ffcde38c32 --- /dev/null +++ b/sbin/ipf/ipsend/Crashable @@ -0,0 +1,21 @@ +Test 1: + Solaris 2.4 - upto and including 101945-34, > 34 ? + Solaris 2.5 - 11/95 + Linux 1.2.13, < 1.3.45(?) + 3com/sonix bridge + Instant Internet + KA9Q NOS + Netblazer 40i, Version 3.2 OS + Irix 6.x + HP-UX 9.0 + HP-UX 10.1 + LivingstonsComOS + MacOS 7.x, 8.x + +Test 6: + SunOS 4.1.x + ULtrix 4.3 + +Test 7: + SunOS 4.1.x + Linux <= 1.3.84 diff --git a/sbin/ipf/ipsend/Makefile b/sbin/ipf/ipsend/Makefile new file mode 100644 index 000000000000..34485efce0d6 --- /dev/null +++ b/sbin/ipf/ipsend/Makefile @@ -0,0 +1,183 @@ +# +# Copyright (C) 2012 by Darren Reed. +# +# See the IPFILTER.LICENCE file for details on licencing. +# +IPFT=ipft_ef.o ipft_hx.o ipft_pc.o ipft_sn.o ipft_td.o ipft_tx.o opt.o +OBJS=ipsend.o ip.o ipsopt.o y.tab.o lex.yy.o +ROBJS=ipresend.o ip.o resend.o $(IPFT) +TOBJS=iptest.o iptests.o ip.o +BPF=sbpf.o +NIT=snit.o +SUNOS4=sock.o arp.o inet_addr.o +BSD=sock.o 44arp.o +LINUX=lsock.o slinux.o larp.o +LINUXK= +TOP=.. +SUNOS5=dlcommon.o sdlpi.o arp.o inet_addr.o +ULTRIX=ultrix.o sock.o arp.o inet_addr.o +HPUX=hpux.o sock.o arp.o inet_addr.o + +#CC=gcc +DEBUG=-g +CFLAGS=$(DEBUG) -I. -Iipf +# +MFLAGS="BINDEST=$(BINDEST)" "SBINDEST=$(SBINDEST)" "MANDIR=$(MANDIR)" \ + "IPFLKM=$(IPFLKM)" \ + "IPFLOG=$(IPFLOG)" "LOGFAC=$(LOGFAC)" "POLICY=$(POLICY)" \ + "SOLARIS2=$(SOLARIS2)" "DEBUG=$(DEBUG)" "DCPU=$(CPU)" \ + "CPUDIR=$(CPUDIR)" +# +all: + @echo "Use one of these targets:" + @echo " sunos4-nit (standard SunOS 4.1.x)" + @echo " sunos4-bpf (SunOS4.1.x with BPF in the kernel)" + @echo " bsd-bpf (4.4BSD variant with BPF in the kernel)" + @echo " linux10 (Linux 1.0 kernels)" + @echo " linux12 (Linux 1.2 kernels)" + @echo " linux20 (Linux 2.0 kernels)" + @echo " sunos5 (Solaris 2.x)" + +ipf: + -if [ ! -d iplang ] ; then ln -s ../iplang iplang; fi + -if [ ! -d netinet ] ; then ln -s ../netinet netinet; fi + -if [ ! -d ipf ] ; then ln -s .. ipf; fi + +y.tab.o: iplang/iplang_y.y + -if [ -h iplang ] ; then \ + (cd iplang; ${MAKE} $(MFLAGS) 'DESTDIR=../ipsend' ) \ + else \ + (cd iplang; ${MAKE} $(MFLAGS) 'DESTDIR=..' ) \ + fi + +lex.yy.o: iplang/iplang_l.l + -if [ -h iplang ] ; then \ + (cd iplang; ${MAKE} $(MFLAGS) 'DESTDIR=../ipsend' ) \ + else \ + (cd iplang; ${MAKE} $(MFLAGS) 'DESTDIR=..' ) \ + fi + +.c.o: + $(CC) $(CFLAGS) $(LINUXK) -c $< -o $@ + +install: + -$(INSTALL) -cs -g wheel -m 755 -o root ipsend ipresend iptest $(BINDEST) + +bpf sunos4-bpf : + make ipsend "OBJS=$(OBJS)" "UNIXOBJS=$(BPF) $(SUNOS4)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET -DIPSEND" "LLIB=-ll" + make ipresend "ROBJS=$(ROBJS)" "UNIXOBJS=$(BPF) $(SUNOS4)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET" + make iptest "TOBJS=$(TOBJS)" "UNIXOBJS=$(BPF) $(SUNOS4)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET" + +nit sunos4 sunos4-nit : + make ipsend "OBJS=$(OBJS)" "UNIXOBJS=$(NIT) $(SUNOS4)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET -DIPSEND" "LLIB=-ll" + make ipresend "ROBJS=$(ROBJS)" "UNIXOBJS=$(NIT) $(SUNOS4)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET" + make iptest "TOBJS=$(TOBJS)" "UNIXOBJS=$(NIT) $(SUNOS4)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET" + +dlpi sunos5 : + make ipsend "OBJS=$(OBJS)" "UNIXOBJS=$(SUNOS5)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) -Dsolaris -DIPSEND" "LIBS=-lsocket -lnsl" \ + "LLIB=-ll" + make ipresend "ROBJS=$(ROBJS)" "UNIXOBJS=$(SUNOS5)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) -Dsolaris" "LIBS=-lsocket -lnsl" + make iptest "TOBJS=$(TOBJS)" "UNIXOBJS=$(SUNOS5)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) -Dsolaris" "LIBS=-lsocket -lnsl" + +bsd-bpf : + make ipsend "OBJS=$(OBJS)" "UNIXOBJS=$(BPF) $(BSD)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET -DIPSEND" "LLIB=-ll" + make ipresend "ROBJS=$(ROBJS)" "UNIXOBJS=$(BPF) $(BSD)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET" + make iptest "TOBJS=$(TOBJS)" "UNIXOBJS=$(BPF) $(BSD)" "CC=$(CC)" \ + "CFLAGS=$(CFLAGS) -DDOSOCKET" + +linuxrev : + make ipsend "OBJS=$(OBJS)" "UNIXOBJS=$(LINUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) $(INC) -DDOSOCKET -DIPSEND" $(LINUXK) + make ipresend "ROBJS=$(ROBJS)" "UNIXOBJS=$(LINUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) $(INC) -DDOSOCKET" $(LINUXK) + make iptest "TOBJS=$(TOBJS)" "UNIXOBJS=$(LINUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) $(INC) -DDOSOCKET" $(LINUXK) + +linux10: + make linuxrev 'LINUXK="LINUXK=-DLINUX=0100"' \ + "INC=-I/usr/src/linux/include" "LLIB=-lfl" + +linux12: + make linuxrev 'LINUXK="LINUXK=-DLINUX=0102"' "INC=-I/usr/src/linux" \ + "LLIB=-lfl" + +linux20: + make linuxrev 'LINUXK="LINUXK=-DLINUX=0200"' \ + "INC=-I/usr/src/linux/include" "LLIB=-lfl" "ELIB=-lelf" + +ultrix : + make ipsend "OBJS=$(OBJS)" "UNIXOBJS=$(ULTRIX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) -DIPSEND" "LIBS=" "LLIB=-ll" + make ipresend "ROBJS=$(ROBJS)" "UNIXOBJS=$(ULTRIX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS)" "LIBS=" + make iptest "TOBJS=$(TOBJS)" "UNIXOBJS=$(ULTRIX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS)" "LIBS=" + +hpux9 : + make ipsend "OBJS=$(OBJS)" "UNIXOBJS=$(HPUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) -DIPSEND" "LIBS=" + make ipresend "ROBJS=$(ROBJS)" "UNIXOBJS=$(HPUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS)" "LIBS=" + make iptest "TOBJS=$(TOBJS)" "UNIXOBJS=$(HPUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS)" "LIBS=" + +hpux11 : + make ipsend "OBJS=$(OBJS)" "UNIXOBJS=$(HPUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS) -DIPSEND" "LIBS=" + make ipresend "ROBJS=$(ROBJS)" "UNIXOBJS=$(HPUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS)" "LIBS=" + make iptest "TOBJS=$(TOBJS)" "UNIXOBJS=$(HPUX)" "CC=$(CC)" \ + CFLAGS="$(CFLAGS)" "LIBS=" + +ipsend: ipf $(OBJS) $(UNIXOBJS) + $(CC) $(OBJS) $(UNIXOBJS) -o $@ $(LIBS) $(LLIB) $(ELIB) + +ipresend: $(ROBJS) $(UNIXOBJS) + $(CC) $(ROBJS) $(UNIXOBJS) -o $@ $(LIBS) $(ELIB) + +iptest: $(TOBJS) $(UNIXOBJS) + $(CC) $(TOBJS) $(UNIXOBJS) -o $@ $(LIBS) $(ELIB) + +ipft_ef.o: ipf/ipft_ef.c ipf/ipt.h ipf/ipf.h ipf/ip_compat.h + $(CC) $(CFLAGS) $(LINUXK) -c ipf/ipft_ef.c -o $@ + +ipft_hx.o: ipf/ipft_hx.c ipf/ipt.h ipf/ipf.h ipf/ip_compat.h + $(CC) $(CFLAGS) $(LINUXK) -c ipf/ipft_hx.c -o $@ + +ipft_pc.o: ipf/ipft_pc.c ipf/ipt.h ipf/ipf.h ipf/ip_compat.h + $(CC) $(CFLAGS) $(LINUXK) -c ipf/ipft_pc.c -o $@ + +ipft_sn.o: ipf/ipft_sn.c ipf/ipt.h ipf/ipf.h ipf/ip_compat.h + $(CC) $(CFLAGS) $(LINUXK) -c ipf/ipft_sn.c -o $@ + +ipft_td.o: ipf/ipft_td.c ipf/ipt.h ipf/ipf.h ipf/ip_compat.h + $(CC) $(CFLAGS) $(LINUXK) -c ipf/ipft_td.c -o $@ + +ipft_tx.o: ipf/ipft_tx.c ipf/ipt.h ipf/ipf.h ipf/ip_compat.h + $(CC) $(CFLAGS) $(LINUXK) -c ipf/ipft_tx.c -o $@ + +opt.o: ipf/opt.c ipf/ipt.h ipf/ipf.h ipf/ip_compat.h + $(CC) $(CFLAGS) $(LINUXK) -c ipf/opt.c -o $@ + +inet_addr.o: ipf/inet_addr.c + $(CC) $(CFLAGS) $(LINUXK) -c ipf/inet_addr.c -o $@ + +clean: + rm -rf *.o *core a.out ipsend ipresend iptest + if [ -d iplang ]; then (cd iplang; $(MAKE) $(MFLAGS) clean); fi + if [ -d $(TOP)/iplang ]; then (cd $(TOP)/iplang; $(MAKE) $(MFLAGS) clean); fi + +do-cvs: + find . -type d -name CVS -print | xargs /bin/rm -rf + find . -type f -name .cvsignore -print | xargs /bin/rm -f diff --git a/sbin/ipf/ipsend/arp.c b/sbin/ipf/ipsend/arp.c new file mode 100644 index 000000000000..31b70d3e8987 --- /dev/null +++ b/sbin/ipf/ipsend/arp.c @@ -0,0 +1,135 @@ +/* $FreeBSD$ */ + +/* + * arp.c (C) 1995-1998 Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char sccsid[] = "@(#)arp.c 1.4 1/11/96 (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/types.h> +#include <sys/socket.h> +# include <sys/sockio.h> +#include <sys/ioctl.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <net/if.h> +#include <netinet/if_ether.h> +# include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <stdio.h> +#include <errno.h> +#include <netdb.h> +#include "ipsend.h" +#include "iplang/iplang.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; +} + +/* + * ARP for the MAC address corresponding + * to the IP address. This taken from + * some BSD program, I cant remember which. + */ +int arp(ip, ether) + char *ip; + char *ether; +{ + static int sfd = -1; + static char ethersave[6], ipsave[4]; + struct arpreq ar; + struct sockaddr_in *sin, san; + struct hostent *hp; + int fd; + +#ifdef IPSEND + if (arp_getipv4(ip, ether) == 0) + return 0; +#endif + if (!bcmp(ipsave, ip, 4)) { + bcopy(ethersave, ether, 6); + return 0; + } + fd = -1; + bzero((char *)&ar, sizeof(ar)); + sin = (struct sockaddr_in *)&ar.arp_pa; + sin->sin_family = AF_INET; + bcopy(ip, (char *)&sin->sin_addr.s_addr, 4); + if ((hp = gethostbyaddr(ip, 4, AF_INET))) +# if SOLARIS && (SOLARIS2 >= 10) + if (!(ether_hostton(hp->h_name, (struct ether_addr *)ether))) +# else + if (!(ether_hostton(hp->h_name, ether))) +# endif + goto savearp; + + if (sfd == -1) + if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + { + perror("arp: socket"); + return -1; + } +tryagain: + if (ioctl(sfd, SIOCGARP, (caddr_t)&ar) == -1) + { + if (fd == -1) + { + bzero((char *)&san, sizeof(san)); + san.sin_family = AF_INET; + san.sin_port = htons(1); + bcopy(ip, &san.sin_addr.s_addr, 4); + fd = socket(AF_INET, SOCK_DGRAM, 0); + (void) sendto(fd, ip, 4, 0, + (struct sockaddr *)&san, sizeof(san)); + sleep(1); + (void) close(fd); + goto tryagain; + } + fprintf(stderr, "(%s):", inet_ntoa(sin->sin_addr)); + if (errno != ENXIO) + perror("SIOCGARP"); + return -1; + } + + if ((ar.arp_ha.sa_data[0] == 0) && (ar.arp_ha.sa_data[1] == 0) && + (ar.arp_ha.sa_data[2] == 0) && (ar.arp_ha.sa_data[3] == 0) && + (ar.arp_ha.sa_data[4] == 0) && (ar.arp_ha.sa_data[5] == 0)) { + fprintf(stderr, "(%s):", inet_ntoa(sin->sin_addr)); + return -1; + } + + bcopy(ar.arp_ha.sa_data, ether, 6); +savearp: + bcopy(ether, ethersave, 6); + bcopy(ip, ipsave, 4); + return 0; +} diff --git a/sbin/ipf/ipsend/dlcommon.c b/sbin/ipf/ipsend/dlcommon.c new file mode 100644 index 000000000000..efb82df9ad32 --- /dev/null +++ b/sbin/ipf/ipsend/dlcommon.c @@ -0,0 +1,1379 @@ +/* $FreeBSD$ */ + +/* + * Common (shared) DLPI test routines. + * Mostly pretty boring boilerplate sorta stuff. + * These can be split into individual library routines later + * but it's just convenient to keep them in a single file + * while they're being developed. + * + * Not supported: + * Connection Oriented stuff + * QOS stuff + */ + +/* +typedef unsigned long ulong; +*/ + + +#include <sys/types.h> +#include <sys/stream.h> +#include <sys/stropts.h> +# include <sys/dlpi.h> +#include <sys/signal.h> +#include <stdio.h> +#include <string.h> +#include "dltest.h" + +#define CASERET(s) case s: return ("s") + + char *dlprim(); + char *dlstate(); + char *dlerrno(); + char *dlpromisclevel(); + char *dlservicemode(); + char *dlstyle(); + char *dlmactype(); + + +void +dlinforeq(fd) + int fd; +{ + dl_info_req_t info_req; + struct strbuf ctl; + int flags; + + info_req.dl_primitive = DL_INFO_REQ; + + ctl.maxlen = 0; + ctl.len = sizeof (info_req); + ctl.buf = (char *) &info_req; + + flags = RS_HIPRI; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlinforeq: putmsg"); +} + +void +dlinfoack(fd, bufp) + int fd; + char *bufp; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlinfoack"); + + dlp = (union DL_primitives *) ctl.buf; + + expecting(DL_INFO_ACK, dlp); + + if (ctl.len < sizeof (dl_info_ack_t)) + err("dlinfoack: response ctl.len too short: %d", ctl.len); + + if (flags != RS_HIPRI) + err("dlinfoack: DL_INFO_ACK was not M_PCPROTO"); + + if (ctl.len < sizeof (dl_info_ack_t)) + err("dlinfoack: short response ctl.len: %d", ctl.len); +} + +void +dlattachreq(fd, ppa) + int fd; + u_long ppa; +{ + dl_attach_req_t attach_req; + struct strbuf ctl; + int flags; + + attach_req.dl_primitive = DL_ATTACH_REQ; + attach_req.dl_ppa = ppa; + + ctl.maxlen = 0; + ctl.len = sizeof (attach_req); + ctl.buf = (char *) &attach_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlattachreq: putmsg"); +} + +void +dlenabmultireq(fd, addr, length) + int fd; + char *addr; + int length; +{ + long buf[MAXDLBUF]; + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + dlp = (union DL_primitives*) buf; + + dlp->enabmulti_req.dl_primitive = DL_ENABMULTI_REQ; + dlp->enabmulti_req.dl_addr_length = length; + dlp->enabmulti_req.dl_addr_offset = sizeof (dl_enabmulti_req_t); + + (void) memcpy((char*)OFFADDR(buf, sizeof (dl_enabmulti_req_t)), addr, length); + + ctl.maxlen = 0; + ctl.len = sizeof (dl_enabmulti_req_t) + length; + ctl.buf = (char*) buf; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlenabmultireq: putmsg"); +} + +void +dldisabmultireq(fd, addr, length) + int fd; + char *addr; + int length; +{ + long buf[MAXDLBUF]; + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + dlp = (union DL_primitives*) buf; + + dlp->disabmulti_req.dl_primitive = DL_ENABMULTI_REQ; + dlp->disabmulti_req.dl_addr_length = length; + dlp->disabmulti_req.dl_addr_offset = sizeof (dl_disabmulti_req_t); + + (void) memcpy((char*)OFFADDR(buf, sizeof (dl_disabmulti_req_t)), addr, length); + + ctl.maxlen = 0; + ctl.len = sizeof (dl_disabmulti_req_t) + length; + ctl.buf = (char*) buf; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dldisabmultireq: putmsg"); +} + +void +dlpromisconreq(fd, level) + int fd; + u_long level; +{ + dl_promiscon_req_t promiscon_req; + struct strbuf ctl; + int flags; + + promiscon_req.dl_primitive = DL_PROMISCON_REQ; + promiscon_req.dl_level = level; + + ctl.maxlen = 0; + ctl.len = sizeof (promiscon_req); + ctl.buf = (char *) &promiscon_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlpromiscon: putmsg"); + +} + +void +dlpromiscoff(fd, level) + int fd; + u_long level; +{ + dl_promiscoff_req_t promiscoff_req; + struct strbuf ctl; + int flags; + + promiscoff_req.dl_primitive = DL_PROMISCOFF_REQ; + promiscoff_req.dl_level = level; + + ctl.maxlen = 0; + ctl.len = sizeof (promiscoff_req); + ctl.buf = (char *) &promiscoff_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlpromiscoff: putmsg"); +} + +void +dlphysaddrreq(fd, addrtype) + int fd; + u_long addrtype; +{ + dl_phys_addr_req_t phys_addr_req; + struct strbuf ctl; + int flags; + + phys_addr_req.dl_primitive = DL_PHYS_ADDR_REQ; + phys_addr_req.dl_addr_type = addrtype; + + ctl.maxlen = 0; + ctl.len = sizeof (phys_addr_req); + ctl.buf = (char *) &phys_addr_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlphysaddrreq: putmsg"); +} + +void +dlsetphysaddrreq(fd, addr, length) + int fd; + char *addr; + int length; +{ + long buf[MAXDLBUF]; + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + dlp = (union DL_primitives*) buf; + + dlp->set_physaddr_req.dl_primitive = DL_ENABMULTI_REQ; + dlp->set_physaddr_req.dl_addr_length = length; + dlp->set_physaddr_req.dl_addr_offset = sizeof (dl_set_phys_addr_req_t); + + (void) memcpy((char*)OFFADDR(buf, sizeof (dl_set_phys_addr_req_t)), addr, length); + + ctl.maxlen = 0; + ctl.len = sizeof (dl_set_phys_addr_req_t) + length; + ctl.buf = (char*) buf; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlsetphysaddrreq: putmsg"); +} + +void +dldetachreq(fd) + int fd; +{ + dl_detach_req_t detach_req; + struct strbuf ctl; + int flags; + + detach_req.dl_primitive = DL_DETACH_REQ; + + ctl.maxlen = 0; + ctl.len = sizeof (detach_req); + ctl.buf = (char *) &detach_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dldetachreq: putmsg"); +} + +void +dlbindreq(fd, sap, max_conind, service_mode, conn_mgmt, xidtest) + int fd; + u_long sap; + u_long max_conind; + u_long service_mode; + u_long conn_mgmt; + u_long xidtest; +{ + dl_bind_req_t bind_req; + struct strbuf ctl; + int flags; + + bind_req.dl_primitive = DL_BIND_REQ; + bind_req.dl_sap = sap; + bind_req.dl_max_conind = max_conind; + bind_req.dl_service_mode = service_mode; + bind_req.dl_conn_mgmt = conn_mgmt; + bind_req.dl_xidtest_flg = xidtest; + + ctl.maxlen = 0; + ctl.len = sizeof (bind_req); + ctl.buf = (char *) &bind_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlbindreq: putmsg"); +} + +void +dlunitdatareq(fd, addrp, addrlen, minpri, maxpri, datap, datalen) + int fd; + u_char *addrp; + int addrlen; + u_long minpri, maxpri; + u_char *datap; + int datalen; +{ + long buf[MAXDLBUF]; + union DL_primitives *dlp; + struct strbuf data, ctl; + + dlp = (union DL_primitives*) buf; + + dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ; + dlp->unitdata_req.dl_dest_addr_length = addrlen; + dlp->unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t); + dlp->unitdata_req.dl_priority.dl_min = minpri; + dlp->unitdata_req.dl_priority.dl_max = maxpri; + + (void) memcpy(OFFADDR(dlp, sizeof (dl_unitdata_req_t)), addrp, addrlen); + + ctl.maxlen = 0; + ctl.len = sizeof (dl_unitdata_req_t) + addrlen; + ctl.buf = (char *) buf; + + data.maxlen = 0; + data.len = datalen; + data.buf = (char *) datap; + + if (putmsg(fd, &ctl, &data, 0) < 0) + syserr("dlunitdatareq: putmsg"); +} + +void +dlunbindreq(fd) + int fd; +{ + dl_unbind_req_t unbind_req; + struct strbuf ctl; + int flags; + + unbind_req.dl_primitive = DL_UNBIND_REQ; + + ctl.maxlen = 0; + ctl.len = sizeof (unbind_req); + ctl.buf = (char *) &unbind_req; + + flags = 0; + + if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) + syserr("dlunbindreq: putmsg"); +} + +void +dlokack(fd, bufp) + int fd; + char *bufp; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlokack"); + + dlp = (union DL_primitives *) ctl.buf; + + expecting(DL_OK_ACK, dlp); + + if (ctl.len < sizeof (dl_ok_ack_t)) + err("dlokack: response ctl.len too short: %d", ctl.len); + + if (flags != RS_HIPRI) + err("dlokack: DL_OK_ACK was not M_PCPROTO"); + + if (ctl.len < sizeof (dl_ok_ack_t)) + err("dlokack: short response ctl.len: %d", ctl.len); +} + +void +dlerrorack(fd, bufp) + int fd; + char *bufp; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlerrorack"); + + dlp = (union DL_primitives *) ctl.buf; + + expecting(DL_ERROR_ACK, dlp); + + if (ctl.len < sizeof (dl_error_ack_t)) + err("dlerrorack: response ctl.len too short: %d", ctl.len); + + if (flags != RS_HIPRI) + err("dlerrorack: DL_OK_ACK was not M_PCPROTO"); + + if (ctl.len < sizeof (dl_error_ack_t)) + err("dlerrorack: short response ctl.len: %d", ctl.len); +} + +void +dlbindack(fd, bufp) + int fd; + char *bufp; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlbindack"); + + dlp = (union DL_primitives *) ctl.buf; + + expecting(DL_BIND_ACK, dlp); + + if (flags != RS_HIPRI) + err("dlbindack: DL_OK_ACK was not M_PCPROTO"); + + if (ctl.len < sizeof (dl_bind_ack_t)) + err("dlbindack: short response ctl.len: %d", ctl.len); +} + +void +dlphysaddrack(fd, bufp) + int fd; + char *bufp; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlphysaddrack"); + + dlp = (union DL_primitives *) ctl.buf; + + expecting(DL_PHYS_ADDR_ACK, dlp); + + if (flags != RS_HIPRI) + err("dlbindack: DL_OK_ACK was not M_PCPROTO"); + + if (ctl.len < sizeof (dl_phys_addr_ack_t)) + err("dlphysaddrack: short response ctl.len: %d", ctl.len); +} + +void +sigalrm() +{ + (void) err("sigalrm: TIMEOUT"); +} + +strgetmsg(fd, ctlp, datap, flagsp, caller) + int fd; + struct strbuf *ctlp, *datap; + int *flagsp; + char *caller; +{ + int rc; + static char errmsg[80]; + + /* + * Start timer. + */ + (void) signal(SIGALRM, sigalrm); + if (alarm(MAXWAIT) < 0) { + (void) snprintf(errmsg, sizeof(errmsg), "%s: alarm", caller); + syserr(errmsg); + } + + /* + * Set flags argument and issue getmsg(). + */ + *flagsp = 0; + if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) { + (void) snprintf(errmsg, sizeof(errmsg), "%s: getmsg", caller); + syserr(errmsg); + } + + /* + * Stop timer. + */ + if (alarm(0) < 0) { + (void) snprintf(errmsg, sizeof(errmsg), "%s: alarm", caller); + syserr(errmsg); + } + + /* + * Check for MOREDATA and/or MORECTL. + */ + if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) + err("%s: MORECTL|MOREDATA", caller); + if (rc & MORECTL) + err("%s: MORECTL", caller); + if (rc & MOREDATA) + err("%s: MOREDATA", caller); + + /* + * Check for at least sizeof (long) control data portion. + */ + if (ctlp->len < sizeof (long)) + err("getmsg: control portion length < sizeof (long): %d", ctlp->len); +} + +expecting(prim, dlp) + int prim; + union DL_primitives *dlp; +{ + if (dlp->dl_primitive != (u_long)prim) { + printdlprim(dlp); + err("expected %s got %s", dlprim(prim), + dlprim(dlp->dl_primitive)); + exit(1); + } +} + +/* + * Print any DLPI msg in human readable format. + */ +printdlprim(dlp) + union DL_primitives *dlp; +{ + switch (dlp->dl_primitive) { + case DL_INFO_REQ: + printdlinforeq(dlp); + break; + + case DL_INFO_ACK: + printdlinfoack(dlp); + break; + + case DL_ATTACH_REQ: + printdlattachreq(dlp); + break; + + case DL_OK_ACK: + printdlokack(dlp); + break; + + case DL_ERROR_ACK: + printdlerrorack(dlp); + break; + + case DL_DETACH_REQ: + printdldetachreq(dlp); + break; + + case DL_BIND_REQ: + printdlbindreq(dlp); + break; + + case DL_BIND_ACK: + printdlbindack(dlp); + break; + + case DL_UNBIND_REQ: + printdlunbindreq(dlp); + break; + + case DL_SUBS_BIND_REQ: + printdlsubsbindreq(dlp); + break; + + case DL_SUBS_BIND_ACK: + printdlsubsbindack(dlp); + break; + + case DL_SUBS_UNBIND_REQ: + printdlsubsunbindreq(dlp); + break; + + case DL_ENABMULTI_REQ: + printdlenabmultireq(dlp); + break; + + case DL_DISABMULTI_REQ: + printdldisabmultireq(dlp); + break; + + case DL_PROMISCON_REQ: + printdlpromisconreq(dlp); + break; + + case DL_PROMISCOFF_REQ: + printdlpromiscoffreq(dlp); + break; + + case DL_UNITDATA_REQ: + printdlunitdatareq(dlp); + break; + + case DL_UNITDATA_IND: + printdlunitdataind(dlp); + break; + + case DL_UDERROR_IND: + printdluderrorind(dlp); + break; + + case DL_UDQOS_REQ: + printdludqosreq(dlp); + break; + + case DL_PHYS_ADDR_REQ: + printdlphysaddrreq(dlp); + break; + + case DL_PHYS_ADDR_ACK: + printdlphysaddrack(dlp); + break; + + case DL_SET_PHYS_ADDR_REQ: + printdlsetphysaddrreq(dlp); + break; + + default: + err("printdlprim: unknown primitive type 0x%x", + dlp->dl_primitive); + break; + } +} + +/* ARGSUSED */ +printdlinforeq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_INFO_REQ\n"); +} + +printdlinfoack(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + u_char brdcst[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->info_ack.dl_addr_offset), + dlp->info_ack.dl_addr_length, addr); + addrtostring(OFFADDR(dlp, dlp->info_ack.dl_brdcst_addr_offset), + dlp->info_ack.dl_brdcst_addr_length, brdcst); + + (void) printf("DL_INFO_ACK: max_sdu %d min_sdu %d\n", + dlp->info_ack.dl_max_sdu, + dlp->info_ack.dl_min_sdu); + (void) printf("addr_length %d mac_type %s current_state %s\n", + dlp->info_ack.dl_addr_length, + dlmactype(dlp->info_ack.dl_mac_type), + dlstate(dlp->info_ack.dl_current_state)); + (void) printf("sap_length %d service_mode %s qos_length %d\n", + dlp->info_ack.dl_sap_length, + dlservicemode(dlp->info_ack.dl_service_mode), + dlp->info_ack.dl_qos_length); + (void) printf("qos_offset %d qos_range_length %d qos_range_offset %d\n", + dlp->info_ack.dl_qos_offset, + dlp->info_ack.dl_qos_range_length, + dlp->info_ack.dl_qos_range_offset); + (void) printf("provider_style %s addr_offset %d version %d\n", + dlstyle(dlp->info_ack.dl_provider_style), + dlp->info_ack.dl_addr_offset, + dlp->info_ack.dl_version); + (void) printf("brdcst_addr_length %d brdcst_addr_offset %d\n", + dlp->info_ack.dl_brdcst_addr_length, + dlp->info_ack.dl_brdcst_addr_offset); + (void) printf("addr %s\n", addr); + (void) printf("brdcst_addr %s\n", brdcst); +} + +printdlattachreq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_ATTACH_REQ: ppa %d\n", + dlp->attach_req.dl_ppa); +} + +printdlokack(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_OK_ACK: correct_primitive %s\n", + dlprim(dlp->ok_ack.dl_correct_primitive)); +} + +printdlerrorack(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_ERROR_ACK: error_primitive %s errno %s unix_errno %d: %s\n", + dlprim(dlp->error_ack.dl_error_primitive), + dlerrno(dlp->error_ack.dl_errno), + dlp->error_ack.dl_unix_errno, + strerror(dlp->error_ack.dl_unix_errno)); +} + +printdlenabmultireq(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->enabmulti_req.dl_addr_offset), + dlp->enabmulti_req.dl_addr_length, addr); + + (void) printf("DL_ENABMULTI_REQ: addr_length %d addr_offset %d\n", + dlp->enabmulti_req.dl_addr_length, + dlp->enabmulti_req.dl_addr_offset); + (void) printf("addr %s\n", addr); +} + +printdldisabmultireq(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->disabmulti_req.dl_addr_offset), + dlp->disabmulti_req.dl_addr_length, addr); + + (void) printf("DL_DISABMULTI_REQ: addr_length %d addr_offset %d\n", + dlp->disabmulti_req.dl_addr_length, + dlp->disabmulti_req.dl_addr_offset); + (void) printf("addr %s\n", addr); +} + +printdlpromisconreq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_PROMISCON_REQ: level %s\n", + dlpromisclevel(dlp->promiscon_req.dl_level)); +} + +printdlpromiscoffreq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_PROMISCOFF_REQ: level %s\n", + dlpromisclevel(dlp->promiscoff_req.dl_level)); +} + +printdlphysaddrreq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_PHYS_ADDR_REQ: addr_type 0x%x\n", + dlp->physaddr_req.dl_addr_type); +} + +printdlphysaddrack(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->physaddr_ack.dl_addr_offset), + dlp->physaddr_ack.dl_addr_length, addr); + + (void) printf("DL_PHYS_ADDR_ACK: addr_length %d addr_offset %d\n", + dlp->physaddr_ack.dl_addr_length, + dlp->physaddr_ack.dl_addr_offset); + (void) printf("addr %s\n", addr); +} + +printdlsetphysaddrreq(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->set_physaddr_req.dl_addr_offset), + dlp->set_physaddr_req.dl_addr_length, addr); + + (void) printf("DL_SET_PHYS_ADDR_REQ: addr_length %d addr_offset %d\n", + dlp->set_physaddr_req.dl_addr_length, + dlp->set_physaddr_req.dl_addr_offset); + (void) printf("addr %s\n", addr); +} + +/* ARGSUSED */ +printdldetachreq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_DETACH_REQ\n"); +} + +printdlbindreq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_BIND_REQ: sap %d max_conind %d\n", + dlp->bind_req.dl_sap, + dlp->bind_req.dl_max_conind); + (void) printf("service_mode %s conn_mgmt %d xidtest_flg 0x%x\n", + dlservicemode(dlp->bind_req.dl_service_mode), + dlp->bind_req.dl_conn_mgmt, + dlp->bind_req.dl_xidtest_flg); +} + +printdlbindack(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->bind_ack.dl_addr_offset), + dlp->bind_ack.dl_addr_length, addr); + + (void) printf("DL_BIND_ACK: sap %d addr_length %d addr_offset %d\n", + dlp->bind_ack.dl_sap, + dlp->bind_ack.dl_addr_length, + dlp->bind_ack.dl_addr_offset); + (void) printf("max_conind %d xidtest_flg 0x%x\n", + dlp->bind_ack.dl_max_conind, + dlp->bind_ack.dl_xidtest_flg); + (void) printf("addr %s\n", addr); +} + +/* ARGSUSED */ +printdlunbindreq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_UNBIND_REQ\n"); +} + +printdlsubsbindreq(dlp) + union DL_primitives *dlp; +{ + u_char sap[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->subs_bind_req.dl_subs_sap_offset), + dlp->subs_bind_req.dl_subs_sap_length, sap); + + (void) printf("DL_SUBS_BIND_REQ: subs_sap_offset %d sub_sap_len %d\n", + dlp->subs_bind_req.dl_subs_sap_offset, + dlp->subs_bind_req.dl_subs_sap_length); + (void) printf("sap %s\n", sap); +} + +printdlsubsbindack(dlp) + union DL_primitives *dlp; +{ + u_char sap[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->subs_bind_ack.dl_subs_sap_offset), + dlp->subs_bind_ack.dl_subs_sap_length, sap); + + (void) printf("DL_SUBS_BIND_ACK: subs_sap_offset %d sub_sap_length %d\n", + dlp->subs_bind_ack.dl_subs_sap_offset, + dlp->subs_bind_ack.dl_subs_sap_length); + (void) printf("sap %s\n", sap); +} + +printdlsubsunbindreq(dlp) + union DL_primitives *dlp; +{ + u_char sap[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->subs_unbind_req.dl_subs_sap_offset), + dlp->subs_unbind_req.dl_subs_sap_length, sap); + + (void) printf("DL_SUBS_UNBIND_REQ: subs_sap_offset %d sub_sap_length %d\n", + dlp->subs_unbind_req.dl_subs_sap_offset, + dlp->subs_unbind_req.dl_subs_sap_length); + (void) printf("sap %s\n", sap); +} + +printdlunitdatareq(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->unitdata_req.dl_dest_addr_offset), + dlp->unitdata_req.dl_dest_addr_length, addr); + + (void) printf("DL_UNITDATA_REQ: dest_addr_length %d dest_addr_offset %d\n", + dlp->unitdata_req.dl_dest_addr_length, + dlp->unitdata_req.dl_dest_addr_offset); + (void) printf("dl_priority.min %d dl_priority.max %d\n", + dlp->unitdata_req.dl_priority.dl_min, + dlp->unitdata_req.dl_priority.dl_max); + (void) printf("addr %s\n", addr); +} + +printdlunitdataind(dlp) + union DL_primitives *dlp; +{ + u_char dest[MAXDLADDR]; + u_char src[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->unitdata_ind.dl_dest_addr_offset), + dlp->unitdata_ind.dl_dest_addr_length, dest); + addrtostring(OFFADDR(dlp, dlp->unitdata_ind.dl_src_addr_offset), + dlp->unitdata_ind.dl_src_addr_length, src); + + (void) printf("DL_UNITDATA_IND: dest_addr_length %d dest_addr_offset %d\n", + dlp->unitdata_ind.dl_dest_addr_length, + dlp->unitdata_ind.dl_dest_addr_offset); + (void) printf("src_addr_length %d src_addr_offset %d\n", + dlp->unitdata_ind.dl_src_addr_length, + dlp->unitdata_ind.dl_src_addr_offset); + (void) printf("group_address 0x%x\n", + dlp->unitdata_ind.dl_group_address); + (void) printf("dest %s\n", dest); + (void) printf("src %s\n", src); +} + +printdluderrorind(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->uderror_ind.dl_dest_addr_offset), + dlp->uderror_ind.dl_dest_addr_length, addr); + + (void) printf("DL_UDERROR_IND: dest_addr_length %d dest_addr_offset %d\n", + dlp->uderror_ind.dl_dest_addr_length, + dlp->uderror_ind.dl_dest_addr_offset); + (void) printf("unix_errno %d errno %s\n", + dlp->uderror_ind.dl_unix_errno, + dlerrno(dlp->uderror_ind.dl_errno)); + (void) printf("addr %s\n", addr); +} + +printdltestreq(dlp) + union DL_primitives *dlp; +{ + u_char addr[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->test_req.dl_dest_addr_offset), + dlp->test_req.dl_dest_addr_length, addr); + + (void) printf("DL_TEST_REQ: flag 0x%x dest_addr_length %d dest_addr_offset %d\n", + dlp->test_req.dl_flag, + dlp->test_req.dl_dest_addr_length, + dlp->test_req.dl_dest_addr_offset); + (void) printf("dest_addr %s\n", addr); +} + +printdltestind(dlp) + union DL_primitives *dlp; +{ + u_char dest[MAXDLADDR]; + u_char src[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->test_ind.dl_dest_addr_offset), + dlp->test_ind.dl_dest_addr_length, dest); + addrtostring(OFFADDR(dlp, dlp->test_ind.dl_src_addr_offset), + dlp->test_ind.dl_src_addr_length, src); + + (void) printf("DL_TEST_IND: flag 0x%x dest_addr_length %d dest_addr_offset %d\n", + dlp->test_ind.dl_flag, + dlp->test_ind.dl_dest_addr_length, + dlp->test_ind.dl_dest_addr_offset); + (void) printf("src_addr_length %d src_addr_offset %d\n", + dlp->test_ind.dl_src_addr_length, + dlp->test_ind.dl_src_addr_offset); + (void) printf("dest_addr %s\n", dest); + (void) printf("src_addr %s\n", src); +} + +printdltestres(dlp) + union DL_primitives *dlp; +{ + u_char dest[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->test_res.dl_dest_addr_offset), + dlp->test_res.dl_dest_addr_length, dest); + + (void) printf("DL_TEST_RES: flag 0x%x dest_addr_length %d dest_addr_offset %d\n", + dlp->test_res.dl_flag, + dlp->test_res.dl_dest_addr_length, + dlp->test_res.dl_dest_addr_offset); + (void) printf("dest_addr %s\n", dest); +} + +printdltestcon(dlp) + union DL_primitives *dlp; +{ + u_char dest[MAXDLADDR]; + u_char src[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->test_con.dl_dest_addr_offset), + dlp->test_con.dl_dest_addr_length, dest); + addrtostring(OFFADDR(dlp, dlp->test_con.dl_src_addr_offset), + dlp->test_con.dl_src_addr_length, src); + + (void) printf("DL_TEST_CON: flag 0x%x dest_addr_length %d dest_addr_offset %d\n", + dlp->test_con.dl_flag, + dlp->test_con.dl_dest_addr_length, + dlp->test_con.dl_dest_addr_offset); + (void) printf("src_addr_length %d src_addr_offset %d\n", + dlp->test_con.dl_src_addr_length, + dlp->test_con.dl_src_addr_offset); + (void) printf("dest_addr %s\n", dest); + (void) printf("src_addr %s\n", src); +} + +printdlxidreq(dlp) + union DL_primitives *dlp; +{ + u_char dest[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->xid_req.dl_dest_addr_offset), + dlp->xid_req.dl_dest_addr_length, dest); + + (void) printf("DL_XID_REQ: flag 0x%x dest_addr_length %d dest_addr_offset %d\n", + dlp->xid_req.dl_flag, + dlp->xid_req.dl_dest_addr_length, + dlp->xid_req.dl_dest_addr_offset); + (void) printf("dest_addr %s\n", dest); +} + +printdlxidind(dlp) + union DL_primitives *dlp; +{ + u_char dest[MAXDLADDR]; + u_char src[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->xid_ind.dl_dest_addr_offset), + dlp->xid_ind.dl_dest_addr_length, dest); + addrtostring(OFFADDR(dlp, dlp->xid_ind.dl_src_addr_offset), + dlp->xid_ind.dl_src_addr_length, src); + + (void) printf("DL_XID_IND: flag 0x%x dest_addr_length %d dest_addr_offset %d\n", + dlp->xid_ind.dl_flag, + dlp->xid_ind.dl_dest_addr_length, + dlp->xid_ind.dl_dest_addr_offset); + (void) printf("src_addr_length %d src_addr_offset %d\n", + dlp->xid_ind.dl_src_addr_length, + dlp->xid_ind.dl_src_addr_offset); + (void) printf("dest_addr %s\n", dest); + (void) printf("src_addr %s\n", src); +} + +printdlxidres(dlp) + union DL_primitives *dlp; +{ + u_char dest[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->xid_res.dl_dest_addr_offset), + dlp->xid_res.dl_dest_addr_length, dest); + + (void) printf("DL_XID_RES: flag 0x%x dest_addr_length %d dest_addr_offset %d\n", + dlp->xid_res.dl_flag, + dlp->xid_res.dl_dest_addr_length, + dlp->xid_res.dl_dest_addr_offset); + (void) printf("dest_addr %s\n", dest); +} + +printdlxidcon(dlp) + union DL_primitives *dlp; +{ + u_char dest[MAXDLADDR]; + u_char src[MAXDLADDR]; + + addrtostring(OFFADDR(dlp, dlp->xid_con.dl_dest_addr_offset), + dlp->xid_con.dl_dest_addr_length, dest); + addrtostring(OFFADDR(dlp, dlp->xid_con.dl_src_addr_offset), + dlp->xid_con.dl_src_addr_length, src); + + (void) printf("DL_XID_CON: flag 0x%x dest_addr_length %d dest_addr_offset %d\n", + dlp->xid_con.dl_flag, + dlp->xid_con.dl_dest_addr_length, + dlp->xid_con.dl_dest_addr_offset); + (void) printf("src_addr_length %d src_addr_offset %d\n", + dlp->xid_con.dl_src_addr_length, + dlp->xid_con.dl_src_addr_offset); + (void) printf("dest_addr %s\n", dest); + (void) printf("src_addr %s\n", src); +} + +printdludqosreq(dlp) + union DL_primitives *dlp; +{ + (void) printf("DL_UDQOS_REQ: qos_length %d qos_offset %d\n", + dlp->udqos_req.dl_qos_length, + dlp->udqos_req.dl_qos_offset); +} + +/* + * Return string. + */ +addrtostring(addr, length, s) + u_char *addr; + u_long length; + u_char *s; +{ + int i; + + for (i = 0; i < length; i++) { + (void) sprintf((char*) s, "%x:", addr[i] & 0xff); + s = s + strlen((char*)s); + } + if (length) + *(--s) = '\0'; +} + +/* + * Return length + */ +stringtoaddr(sp, addr) + char *sp; + char *addr; +{ + int n = 0; + char *p; + int val; + + p = sp; + while (p = strtok(p, ":")) { + if (sscanf(p, "%x", &val) != 1) + err("stringtoaddr: invalid input string: %s", sp); + if (val > 0xff) + err("stringtoaddr: invalid input string: %s", sp); + *addr++ = val; + n++; + p = NULL; + } + + return (n); +} + + +static char +hexnibble(c) + char c; +{ + static char hextab[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' + }; + + return (hextab[c & 0x0f]); +} + +char* +dlprim(prim) + u_long prim; +{ + static char primbuf[80]; + + switch ((int)prim) { + CASERET(DL_INFO_REQ); + CASERET(DL_INFO_ACK); + CASERET(DL_ATTACH_REQ); + CASERET(DL_DETACH_REQ); + CASERET(DL_BIND_REQ); + CASERET(DL_BIND_ACK); + CASERET(DL_UNBIND_REQ); + CASERET(DL_OK_ACK); + CASERET(DL_ERROR_ACK); + CASERET(DL_SUBS_BIND_REQ); + CASERET(DL_SUBS_BIND_ACK); + CASERET(DL_UNITDATA_REQ); + CASERET(DL_UNITDATA_IND); + CASERET(DL_UDERROR_IND); + CASERET(DL_UDQOS_REQ); + CASERET(DL_CONNECT_REQ); + CASERET(DL_CONNECT_IND); + CASERET(DL_CONNECT_RES); + CASERET(DL_CONNECT_CON); + CASERET(DL_TOKEN_REQ); + CASERET(DL_TOKEN_ACK); + CASERET(DL_DISCONNECT_REQ); + CASERET(DL_DISCONNECT_IND); + CASERET(DL_RESET_REQ); + CASERET(DL_RESET_IND); + CASERET(DL_RESET_RES); + CASERET(DL_RESET_CON); + default: + (void) snprintf(primbuf, sizeof(primbuf), "unknown primitive 0x%x", prim); + return (primbuf); + } +} + + +char* +dlstate(state) + u_long state; +{ + static char statebuf[80]; + + switch (state) { + CASERET(DL_UNATTACHED); + CASERET(DL_ATTACH_PENDING); + CASERET(DL_DETACH_PENDING); + CASERET(DL_UNBOUND); + CASERET(DL_BIND_PENDING); + CASERET(DL_UNBIND_PENDING); + CASERET(DL_IDLE); + CASERET(DL_UDQOS_PENDING); + CASERET(DL_OUTCON_PENDING); + CASERET(DL_INCON_PENDING); + CASERET(DL_CONN_RES_PENDING); + CASERET(DL_DATAXFER); + CASERET(DL_USER_RESET_PENDING); + CASERET(DL_PROV_RESET_PENDING); + CASERET(DL_RESET_RES_PENDING); + CASERET(DL_DISCON8_PENDING); + CASERET(DL_DISCON9_PENDING); + CASERET(DL_DISCON11_PENDING); + CASERET(DL_DISCON12_PENDING); + CASERET(DL_DISCON13_PENDING); + CASERET(DL_SUBS_BIND_PND); + default: + (void) snprintf(statebuf, sizeof(statebuf), "unknown state 0x%x", state); + return (statebuf); + } +} + +char* +dlerrno(errno) + u_long errno; +{ + static char errnobuf[80]; + + switch (errno) { + CASERET(DL_ACCESS); + CASERET(DL_BADADDR); + CASERET(DL_BADCORR); + CASERET(DL_BADDATA); + CASERET(DL_BADPPA); + CASERET(DL_BADPRIM); + CASERET(DL_BADQOSPARAM); + CASERET(DL_BADQOSTYPE); + CASERET(DL_BADSAP); + CASERET(DL_BADTOKEN); + CASERET(DL_BOUND); + CASERET(DL_INITFAILED); + CASERET(DL_NOADDR); + CASERET(DL_NOTINIT); + CASERET(DL_OUTSTATE); + CASERET(DL_SYSERR); + CASERET(DL_UNSUPPORTED); + CASERET(DL_UNDELIVERABLE); + CASERET(DL_NOTSUPPORTED); + CASERET(DL_TOOMANY); + CASERET(DL_NOTENAB); + CASERET(DL_BUSY); + CASERET(DL_NOAUTO); + CASERET(DL_NOXIDAUTO); + CASERET(DL_NOTESTAUTO); + CASERET(DL_XIDAUTO); + CASERET(DL_TESTAUTO); + CASERET(DL_PENDING); + + default: + (void) snprintf(errnobuf, sizeof(errnobuf), "unknown dlpi errno 0x%x", errno); + return (errnobuf); + } +} + +char* +dlpromisclevel(level) + u_long level; +{ + static char levelbuf[80]; + + switch (level) { + CASERET(DL_PROMISC_PHYS); + CASERET(DL_PROMISC_SAP); + CASERET(DL_PROMISC_MULTI); + default: + (void) snprintf(levelbuf, sizeof(levelbuf), "unknown promisc level 0x%x", level); + return (levelbuf); + } +} + +char* +dlservicemode(servicemode) + u_long servicemode; +{ + static char servicemodebuf[80]; + + switch (servicemode) { + CASERET(DL_CODLS); + CASERET(DL_CLDLS); + CASERET(DL_CODLS|DL_CLDLS); + default: + (void) snprintf(servicemodebuf, sizeof(servicemodebuf), + "unknown provider service mode 0x%x", servicemode); + return (servicemodebuf); + } +} + +char* +dlstyle(style) + long style; +{ + static char stylebuf[80]; + + switch (style) { + CASERET(DL_STYLE1); + CASERET(DL_STYLE2); + default: + (void) snprintf(stylebuf, sizeof(stylebuf), "unknown provider style 0x%x", style); + return (stylebuf); + } +} + +char* +dlmactype(media) + u_long media; +{ + static char mediabuf[80]; + + switch (media) { + CASERET(DL_CSMACD); + CASERET(DL_TPB); + CASERET(DL_TPR); + CASERET(DL_METRO); + CASERET(DL_ETHER); + CASERET(DL_HDLC); + CASERET(DL_CHAR); + CASERET(DL_CTCA); + default: + (void) snprintf(mediabuf, sizeof(mediabuf), "unknown media type 0x%x", media); + return (mediabuf); + } +} + +/*VARARGS1*/ +err(fmt, a1, a2, a3, a4) + char *fmt; + char *a1, *a2, *a3, *a4; +{ + (void) fprintf(stderr, fmt, a1, a2, a3, a4); + (void) fprintf(stderr, "\n"); + (void) exit(1); +} + +syserr(s) + char *s; +{ + (void) perror(s); + exit(1); +} + +strioctl(fd, cmd, timout, len, dp) + int fd; + int cmd; + int timout; + int len; + char *dp; +{ + struct strioctl sioc; + int rc; + + sioc.ic_cmd = cmd; + sioc.ic_timout = timout; + sioc.ic_len = len; + sioc.ic_dp = dp; + rc = ioctl(fd, I_STR, &sioc); + + if (rc < 0) + return (rc); + else + return (sioc.ic_len); +} diff --git a/sbin/ipf/ipsend/dltest.h b/sbin/ipf/ipsend/dltest.h new file mode 100644 index 000000000000..086782c1fbb7 --- /dev/null +++ b/sbin/ipf/ipsend/dltest.h @@ -0,0 +1,34 @@ +/* $FreeBSD$ */ + +/* + * 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/sbin/ipf/ipsend/ip.c b/sbin/ipf/ipsend/ip.c new file mode 100644 index 000000000000..c1bb73f0b169 --- /dev/null +++ b/sbin/ipf/ipsend/ip.c @@ -0,0 +1,362 @@ +/* $FreeBSD$ */ + +/* + * ip.c (C) 1995-1998 Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char sccsid[] = "%W% %G% (C)1995"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/param.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 <sys/param.h> +# include <net/route.h> +# include <netinet/if_ether.h> +# include <netinet/ip_var.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "ipsend.h" + + +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, (char *) &eh->ether_dhost, 6); + } + else if (arp((char *)&gwip, (char *) &eh->ether_dhost) == -1) + { + perror("arp"); + return -2; + } + eh->ether_type = htons(ETHERTYPE_IP); + last_gw.s_addr = gwip.s_addr; + err = sendip(nfd, s, sizeof(*eh) + len); + return err; +} + + +/* + */ +int send_ip(nfd, mtu, ip, gwip, frag) + int nfd, mtu; + ip_t *ip; + struct in_addr gwip; + int frag; +{ + static struct in_addr last_gw, local_ip; + static char local_arp[6] = { 0, 0, 0, 0, 0, 0}; + 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, iplen; + + if (!ipbuf) + { + ipbuf = (char *)malloc(65536); + if (!ipbuf) + { + perror("malloc failed"); + return -2; + } + } + + eh = (ether_header_t *)ipbuf; + + bzero((char *) &eh->ether_shost, sizeof(eh->ether_shost)); + if (last_gw.s_addr && (gwip.s_addr == last_gw.s_addr)) + { + bcopy(last_arp, (char *) &eh->ether_dhost, 6); + } + else if (arp((char *)&gwip, (char *) &eh->ether_dhost) == -1) + { + perror("arp"); + return -2; + } + bcopy((char *) &eh->ether_dhost, last_arp, sizeof(last_arp)); + eh->ether_type = htons(ETHERTYPE_IP); + + bcopy((char *)ip, (char *)&ipsv, sizeof(*ip)); + last_gw.s_addr = gwip.s_addr; + iplen = ip->ip_len; + ip->ip_len = htons(iplen); + if (!(frag & 2)) { + if (!IP_V(ip)) + IP_V_A(ip, IPVERSION); + if (!ip->ip_id) + ip->ip_id = htons(id++); + if (!ip->ip_ttl) + ip->ip_ttl = 60; + } + + if (ip->ip_src.s_addr != local_ip.s_addr) { + (void) arp((char *)&ip->ip_src, (char *) &local_arp); + bcopy(local_arp, (char *) &eh->ether_shost,sizeof(last_arp)); + local_ip = ip->ip_src; + } else + bcopy(local_arp, (char *) &eh->ether_shost, 6); + + if (!frag || (sizeof(*eh) + iplen < mtu)) + { + ip->ip_sum = 0; + ip->ip_sum = chksum((u_short *)ip, IP_HL(ip) << 2); + + bcopy((char *)ip, ipbuf + sizeof(*eh), iplen); + err = sendip(nfd, ipbuf, sizeof(*eh) + iplen); + } + 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, sent = 0, ts, hlen, olen; + + hlen = IP_HL(ip) << 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_HL(ip) << 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((u_short *)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_HL_A(ip, (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; + tcphdr_t *t, *t2; + int thlen, i, iplen, hlen; + u_32_t lbuf[20]; + ip_t *ip2; + + iplen = ip->ip_len; + hlen = IP_HL(ip) << 2; + t = (tcphdr_t *)((char *)ip + hlen); + ip2 = (struct ip *)lbuf; + t2 = (tcphdr_t *)((char *)ip2 + hlen); + thlen = TCP_OFF(t) << 2; + if (!thlen) + thlen = sizeof(tcphdr_t); + bzero((char *)ip2, sizeof(*ip2) + sizeof(*t2)); + ip->ip_p = IPPROTO_TCP; + ip2->ip_p = ip->ip_p; + ip2->ip_src = ip->ip_src; + ip2->ip_dst = ip->ip_dst; + bcopy((char *)ip + hlen, (char *)t2, thlen); + + if (!t2->th_win) + t2->th_win = htons(4096); + iss += 63; + + i = sizeof(struct tcpiphdr) / sizeof(long); + + if ((t2->th_flags == TH_SYN) && !ntohs(ip->ip_off) && + (lbuf[i] != htonl(0x020405b4))) { + lbuf[i] = htonl(0x020405b4); + bcopy((char *)ip + hlen + thlen, (char *)ip + hlen + thlen + 4, + iplen - thlen - hlen); + thlen += 4; + } + TCP_OFF_A(t2, thlen >> 2); + ip2->ip_len = htons(thlen); + ip->ip_len = hlen + thlen; + t2->th_sum = 0; + t2->th_sum = chksum((u_short *)ip2, thlen + sizeof(ip_t)); + + bcopy((char *)t2, (char *)ip + hlen, thlen); + return send_ip(nfd, mtu, ip, gwip, 1); +} + + +/* + * 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; + 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_HL(ip) << 2), + (char *)&ti->ti_sport, sizeof(udphdr_t)); + + ti->ti_len = htons(thlen); + ip->ip_len = (IP_HL(ip) << 2) + thlen; + ti->ti_sum = 0; + ti->ti_sum = chksum((u_short *)ti, thlen + sizeof(ip_t)); + + bcopy((char *)&ti->ti_sport, + (char *)ip + (IP_HL(ip) << 2), sizeof(udphdr_t)); + return send_ip(nfd, mtu, ip, gwip, 1); +} + + +/* + * 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_HL(ip) << 2)); + + ic->icmp_cksum = 0; + ic->icmp_cksum = chksum((u_short *)ic, sizeof(struct icmp)); + + return send_ip(nfd, mtu, ip, gwip, 1); +} + + +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, 1); + } +} diff --git a/sbin/ipf/ipsend/ipresend.1 b/sbin/ipf/ipsend/ipresend.1 new file mode 100644 index 000000000000..6761a183caea --- /dev/null +++ b/sbin/ipf/ipsend/ipresend.1 @@ -0,0 +1,108 @@ +.\" $FreeBSD$ +.\" +.TH IPRESEND 1 +.SH NAME +ipresend \- resend IP packets out to network +.SH SYNOPSIS +.B ipresend +[ +.B \-EHPRSTX +] [ +.B \-d +<device> +] [ +.B \-g +<\fIgateway\fP> +] [ +.B \-m +<\fIMTU\fP> +] [ +.B \-r +<\fIfilename\fP> +] +.SH DESCRIPTION +.PP +\fBipresend\fP was designed to allow packets to be resent, once captured, +back out onto the network for use in testing. \fIipresend\fP supports a +number of different file formats as input, including saved snoop/tcpdump +binary data. +.SH OPTIONS +.TP +.BR \-d \0<interface> +Set the interface name 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. +.TP +.BR \-g \0<gateway> +Specify the hostname of the gateway through which to route packets. This +is required whenever the destination host isn't directly attached to the +same network as the host from which you're sending. +.TP +.BR \-m \0<MTU> +Specify the MTU to be used when sending out packets. This option allows you +to set a fake MTU, allowing the simulation of network interfaces with small +MTU's without setting them so. +.TP +.BR \-r \0<filename> +Specify the filename from which to take input. Default is stdin. +.TP +.B \-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 +.TP +.B \-H +The input file is to be hex digits, representing the binary makeup of the +packet. No length correction is made, if an incorrect length is put in +the IP header. +.TP +.B \-P +The input file specified by \fB\-i\fP is a binary file produced using libpcap +(i.e., tcpdump version 3). Packets are read from this file as being input +(for rule purposes). +.TP +.B \-R +When sending packets out, send them out "raw" (the way they came in). The +only real significance here is that it will expect the link layer (i.e. +ethernet) headers to be prepended to the IP packet being output. +.TP +.B \-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. +.TP +.B \-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 +.TP +.B \-X +The input file is composed of text descriptions of IP packets. +.DT +.SH SEE ALSO +snoop(1m), tcpdump(8), etherfind(8c), ipftest(1), ipresend(1), iptest(1), bpf(4), dlpi(7p) +.SH DIAGNOSTICS +.PP +Needs to be run as root. +.SH BUGS +.PP +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. +If you find any, please send email to me at darrenr@pobox.com + diff --git a/sbin/ipf/ipsend/ipresend.c b/sbin/ipf/ipsend/ipresend.c new file mode 100644 index 000000000000..d9a5f92cbe2c --- /dev/null +++ b/sbin/ipf/ipsend/ipresend.c @@ -0,0 +1,133 @@ +/* $FreeBSD$ */ + +/* + * ipresend.c (C) 1995-1998 Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +#if !defined(lint) +static const char sccsid[] = "%W% %G% (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <netdb.h> +#include <string.h> +#include "ipsend.h" + + +extern char *optarg; +extern int optind; +#ifndef NO_IPF +extern struct ipread pcap, iphex, iptext; +#endif + +int opts = 0; +#ifndef DEFAULT_DEVICE +# ifdef sun +char default_device[] = "le0"; +# else +char default_device[] = "lan0"; +# endif +#else +char default_device[] = DEFAULT_DEVICE; +#endif + + +static void usage(char *); +int main(int, char **); + + +static void usage(prog) + char *prog; +{ + fprintf(stderr, "Usage: %s [options] <-r filename|-R filename>\n\ +\t\t-r filename\tsnoop data file to resend\n\ +\t\t-R filename\tlibpcap data file to resend\n\ +\toptions:\n\ +\t\t-d device\tSend out on this device\n\ +\t\t-g gateway\tIP gateway to use if non-local dest.\n\ +\t\t-m mtu\t\tfake MTU to use when sending out\n\ +", prog); + exit(1); +} + + +int main(argc, argv) + int argc; + char **argv; +{ + struct in_addr gwip; + struct ipread *ipr = NULL; + char *name = argv[0], *gateway = NULL, *dev = NULL; + char *resend = NULL; + int mtu = 1500, c; + + while ((c = getopt(argc, argv, "EHPRSTXd:g:m:r:")) != -1) + switch (c) + { + case 'd' : + dev = optarg; + break; + case 'g' : + gateway = optarg; + break; + case 'm' : + mtu = atoi(optarg); + if (mtu < 28) + { + fprintf(stderr, "mtu must be > 28\n"); + exit(1); + } + case 'r' : + resend = optarg; + break; + case 'R' : + opts |= OPT_RAW; + break; +#ifndef NO_IPF + case 'H' : + ipr = &iphex; + break; + case 'P' : + ipr = &pcap; + break; + case 'X' : + ipr = &iptext; + break; +#endif + default : + fprintf(stderr, "Unknown option \"%c\"\n", c); + usage(name); + } + + if (!ipr || !resend) + usage(name); + + gwip.s_addr = 0; + if (gateway && resolve(gateway, (char *)&gwip) == -1) + { + fprintf(stderr,"Cant resolve %s\n", gateway); + exit(2); + } + + if (!dev) + dev = default_device; + + printf("Device: %s\n", dev); + printf("Gateway: %s\n", inet_ntoa(gwip)); + printf("mtu: %d\n", mtu); + + return ip_resend(dev, mtu, ipr, gwip, resend); +} diff --git a/sbin/ipf/ipsend/ipsend.1 b/sbin/ipf/ipsend/ipsend.1 new file mode 100644 index 000000000000..7f0a8e39538a --- /dev/null +++ b/sbin/ipf/ipsend/ipsend.1 @@ -0,0 +1,111 @@ +.\" $FreeBSD$ +.\" +.TH IPSEND 1 +.SH NAME +ipsend \- sends IP packets +.SH SYNOPSIS +.B ipsend +[ +.B \-dITUv +] [ +.B \-i +<interface> +] [ +.B \-f +<\fIoffset\fP> +] [ +.B \-g +<\fIgateway\fP> +] [ +.B \-m +<\fIMTU\fP> +] [ +.B \-o +<\fIoption\fP> +] [ +.B \-P +<protocol> +] [ +.B \-s +<\fIsource\fP> +] [ +.B \-t +<\fIdest. port\fP> +] [ +.B \-w +<\fIwindow\fP> +] <destination> [TCP-flags] +.SH DESCRIPTION +.PP +\fBipsend\fP can be compiled in two ways. The first is used to send one-off +packets to a destination host, using command line options to specify various +attributes present in the headers. The \fIdestination\fP must be given as +the last command line option, except for when TCP flags are specified as +a combination of A, S, F, U, P and R, last. +.PP +The other way it may be compiled, with DOSOCKET defined, is to allow an +attempt at making a TCP connection using a with ipsend resending the SYN +packet as per the command line options. +.SH OPTIONS +.TP +.BR \-d +enable debugging mode. +.TP +.BR \-f \0<offset> +The \fI-f\fP allows the IP offset field in the IP header to be set to an +arbitrary value, which can be specified in decimal or hexadecimal. +.TP +.BR \-g \0<gateway> +Specify the hostname of the gateway through which to route packets. This +is required whenever the destination host isn't directly attached to the +same network as the host from which you're sending. +.TP +.BR \-i \0<interface> +Set the interface name to be the name supplied. +.TP +.TP +.BR \-m \0<MTU> +Specify the MTU to be used when sending out packets. This option allows you +to set a fake MTU, allowing the simulation of network interfaces with small +MTU's without setting them so. +.TP +.BR \-o \0<option> +Specify options to be included at the end of the IP header. An EOL option +is automatically appended and need not be given. If an option would also +have data associated with it (source as an IP# for a lsrr option), then +this will not be initialised. +.TP +.BR \-s \0<source> +Set the source address in the packet to that provided - maybe either a +hostname or IP#. +.TP +.BR \-t \0<dest. port> +Set the destination port for TCP/UDP packets. +.TP +.BR \-w \0<window> +Set the window size for TCP packets. +.TP +.B \-I +Set the protocol to ICMP. +.TP +.B \-P <protocol> +Set the protocol to the value given. If the parameter is a name, the name +is looked up in the \fI/etc/protocols\fP file. +.TP +.B \-T +Set the protocol to TCP. +.TP +.B \-U +Set the protocol to UDP. +.TP +.BR \-v +enable verbose mode. +.DT +.SH SEE ALSO +ipsend(1), ipresend(1), iptest(1), protocols(4), bpf(4), dlpi(7p) +.SH DIAGNOSTICS +.PP +Needs to be run as root. +.SH BUGS +.PP +If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipsend/ipsend.5 b/sbin/ipf/ipsend/ipsend.5 new file mode 100644 index 000000000000..fc8691198247 --- /dev/null +++ b/sbin/ipf/ipsend/ipsend.5 @@ -0,0 +1,402 @@ +.\" $FreeBSD$ +.TH IPSEND 5 +.SH NAME +ipsend \- IP packet description language +.SH DESCRIPTION +The \fBipsend\fP program expects, with the \fB-L\fP option, input to be a +text file which fits the grammar described below. The purpose of this +grammar is to allow IP packets to be described in an arbitary way which +also allows encapsulation to be so done to an arbitary level. +.SH GRAMMAR +.LP +.nf +line ::= iface | arp | send | defrouter | ipv4line . + +iface ::= ifhdr "{" ifaceopts "}" ";" . +ifhdr ::= "interface" | "iface" . +ifaceopts ::= "ifname" name | "mtu" mtu | "v4addr" ipaddr | + "eaddr" eaddr . + +send ::= "send" ";" | "send" "{" sendbodyopts "}" ";" . +sendbodyopts ::= sendbody [ sendbodyopts ] . +sendbody ::= "ifname" name | "via" ipaddr . + +defrouter ::= "router" ipaddr . + +arp ::= "arp" "{" arpbodyopts "}" ";" . +arpbodyopts ::= arpbody [ arpbodyopts ] . +arpbody ::= "v4addr" ipaddr | "eaddr" eaddr . + +bodyline ::= ipv4line | tcpline | udpline | icmpline | dataline . + +ipv4line ::= "ipv4" "{" ipv4bodyopts "}" ";" . +ipv4bodyopts ::= ipv4body [ ipv4bodyopts ] | bodyline . +ipv4body ::= "proto" protocol | "src" ipaddr | "dst" ipaddr | + "off" number | "v" number | "hl" number| "id" number | + "ttl" number | "tos" number | "sum" number | "len" number | + "opt" "{" ipv4optlist "}" ";" . +ipv4optlist ::= ipv4option [ ipv4optlist ] . +ipv4optlist = "nop" | "rr" | "zsu" | "mtup" | "mtur" | "encode" | "ts" | + "tr" | "sec" | "lsrr" | "e-sec" | "cipso" | "satid" | + "ssrr" | "addext" | "visa" | "imitd" | "eip" | "finn" | + "secclass" ipv4secclass. +ipv4secclass := "unclass" | "confid" | "reserv-1" | "reserv-2" | + "reserv-3" | "reserv-4" | "secret" | "topsecret" . + +tcpline ::= "tcp" "{" tcpbodyopts "}" ";" . +tcpbodyopts ::= tcpbody [ tcpbodyopts ] | bodyline . +tcpbody ::= "sport" port | "dport" port | "seq" number | "ack" number | + "off" number | "urp" number | "win" number | "sum" number | + "flags" tcpflags | data . + +udpline ::= "udp" "{" udpbodyopts "}" ";" . +udpbodyopts ::= udpbody [ udpbodyopts ] | bodyline . +udpbody ::= "sport" port | "dport" port | "len" number | "sum" number | + data . + +icmpline ::= "icmp" "{" icmpbodyopts "}" ";" . +icmpbodyopts ::= icmpbody [ icmpbodyopts ] | bodyline . +icmpbody ::= "type" icmptype [ "code" icmpcode ] . +icmptype ::= "echorep" | "echorep" "{" echoopts "}" ";" | "unreach" | + "unreach" "{" unreachtype "}" ";" | "squench" | "redir" | + "redir" "{" redirtype "}" ";" | "echo" "{" echoopts "}" ";" | + "echo" | "routerad" | "routersol" | "timex" | + "timex" "{" timextype "}" ";" | "paramprob" | + "paramprob" "{" parapptype "}" ";" | "timest" | "timestrep" | + "inforeq" | "inforep" | "maskreq" | "maskrep" . + +echoopts ::= echoopts [ icmpechoopts ] . +unreachtype ::= "net-unr" | "host-unr" | "proto-unr" | "port-unr" | + "needfrag" | "srcfail" | "net-unk" | "host-unk" | "isolate" | + "net-prohib" | "host-prohib" | "net-tos" | "host-tos" | + "filter-prohib" | "host-preced" | "cutoff-preced" . +redirtype ::= "net-redir" | "host-redir" | "tos-net-redir" | + "tos-host-redir" . +timextype ::= "intrans" | "reass" . +paramptype ::= "optabsent" . + +data ::= "data" "{" databodyopts "}" ";" . +databodyopts ::= "len" number | "value" string | "file" filename . + +icmpechoopts ::= "icmpseq" number | "icmpid" number . +.fi +.SH COMMANDS +.PP +Before sending any packets or defining any packets, it is necessary to +describe the interface(s) which will be used to send packets out. +.TP +.B interface +is used to describe a network interface. The description included need +not match the actual configuration currently employed by the operating +system. +.TP +.B send +is used to actually send out a packet across the network. If the +destination is not specified, it will attempt to send the packet +directly out on the network to the destination without routing it. +.TP +.B router +configures the default router for ipsend, as distinct from the default +route installed in the kernel. +.TP +.B ipv4 +is used to describe an IP (version 4) packet. IP header fields can be +specified, including options, followed by a data section which may contain +further protocol headers. +.SH IPv4 +.TP +.B hl <number> +manually specifies the IP header length (automatically adjusts with the +presence of IP options and defaults to 5); +.TP +.B v <number> +set the IP version. Default is 4. +.TP +.B tos <number> +set the type of service (TOS) field in the IP header. Default is 0. +.TP +.B len <number> +manually specifies the length of the IP packet. The length will automatically +be adjusted to accommodate data or further protocol headers. +.TP +.B off <number> +sets the fragment offset field of the IP packet. Default is 0. +.TP +.B ttl <number> +sets the time to live (TTL) field of the IP header. Default is 60. +.TP +.B proto <protocol> +sets the protocol field of the IP header. The protocol can either be a +number or a name found in \fB/etc/protocols\fP. +.TP +.B sum +manually specifies the checksum for the IP header. If left unset (0), it +will be calculated prior to being sent. +.TP +.B src +manually specifies the source address of the IP header. If left unset, it +will default to the host's IP address. +.TP +.B dst +sets the destination of the IP packet. The default is 0.0.0.0. +.TP +.B opt +is used to include IP options in the IP header. +.TP +.B tcp +is used to indicate the a TCP protocol header is to follow. See the \fBTCP\fP +section for TCP header options. +.TP +.B udp +is used to indicate the a UDP protocol header is to follow. See the \fBUDP\fP +section for UDP header options. +.TP +.B icmp +is used to indicate the a ICMP protocol header is to follow. See the +\fBICMP\fP section for ICMP header options. +.TP +.B data +is used to indicate that raw data is to be included in the IP packet. See the +\fBDATA\fP section for details on options available. +.SH "IPv4 Options" +these keywords indicate that the relevant IP option should be added to the +IP header (the header length field will be adjusted appropriately). +.TP +.B nop +No Operation [RFC 791] (space filler). +.TP +.B rr <number> +Record Router [RFC 791]. The number given specifies the number of +\fBbytes\fP to be used for storage. This should be a multiple of 4 for +proper operation. +.TP +.B zsu +Experimental Measurement. +.TP +.B mtup [RFC 1191]. +MTU Probe. +.TP +.B mtur [RFC 1191]. +MTU Ready. +.TP +.B encode +.TP +.B ts +Timestamp [RFC 791]. +.TP +.B tr +Traceroute [RFC 1393]. +.TP +.B "sec-class <security-level>, sec" +Security [RFC 1108]. This option specifies the security label for the packet. +Using \fBsec\fP sets up the framework of the security option but unless +\fBsec-class\fP is given, the level may not be set. +.TP +.B "lsrr <ip-address>" +Loose Source Route [RFC 791]. +.TP +.B e-sec +Extended Security [RFC 1108]. +.TP +.B cipso +Commercial Security. +.TP +.B satid +Stream ID [RFC 791]. +.TP +.B "ssrr <ip-address>" +Strict Source Route [RFC 791]. +.TP +.B addext +Address Extension +.TP +.B visa +Experimental Access Control. +.TP +.B imitd +IMI Traffic Descriptor. +.TP +.B eip +[RFC 1358]. +.TP +.B finn +Experimental Flow Control. +.SH TCP +.TP +.B sport <port> +sets the source port to the number/name given. Default is 0. +.TP +.B dport <port> +sets the destination port to the number/name given. Default is 0. +.TP +.B seq <number> +sets the sequence number to the number specified. Default is 0. +.TP +.B ack <number> +sets the acknowledge number to the number specified. Default is 0. +.TP +.B off <number> +sets the offset value for the start of data to the number specified. This +implies the size of the TCP header. It is automatically adjusted if TCP +options are included and defaults to 5. +.TP +.B urp <number> +sets the value of the urgent data pointer to the number specified. Default +is 0. +.TP +.B win <number> +sets the size of the TCP window to the number specified. Default is 4096. +.TP +.B sum <number> +manually specifies the checksum for the TCP pseudo-header and data. If left +unset, it defaults to 0 and is automatically calculated. +.TP +.B flags <tcp-flags> +sets the TCP flags field to match the flags specified. Valid flags are +"S" (SYN), "A" (ACK), "R" (RST), "F" (FIN), "U" (URG), "P" (PUSH). +.TP +.B opt +indicates that TCP header options follow. As TCP options are added to the +TCP header, the \fBoff\fP field is updated to match. +.TP +.B data +indicates that a data section is to follow and is to be included as raw +data, being appended to the header. +.SH "TCP options" +With a TCP header, it is possible to append a number of header options. +The TCP header offset will be updated automatically to reflect the change +in size. The valid options are: \fBnop\fP No Operation, +\fBeol\fP End Of (option) List, \fBmss [ size ]\fP Maximum Segment Size - this +sets the maximum receivable size of a packet containing data, +\fBwscale\fP Window Scale, \fBts\fP Timestamp. +.SH UDP +.TP +.B sport <port> +sets the source port to the number/name given. Default is 0. +.TP +.B dport <port> +sets the destination port to the number/name given. Default is 0. +.TP +.B len <number> +manually specifies the length of the UDP header and data. If left unset, +it is automatically adjusted to match the header presence and any data if +present. +.TP +.B sum <number> +manually specifies the checksum for the UDP pseudo-header and data. If left +unset, it defaults to 0 and is automatically calculated. +.TP +.B data +indicates that a data section is to follow and is to be included as raw +data, being appended to the header. +.SH ICMP +.TP +.B type <icmptype> +sets the ICMP type according the to the icmptype tag. This may either be +a number or one of the recognised tags (see the \fBICMP TYPES\fP section for a +list of names recognised). +.TP +.B code <icmpcode> +sets the ICMP code. +.TP +.B data +indicates that a data section is to follow and is to be included as raw +data, being appended to the header. +.SH DATA +Each of the following extend the packet in a different way. \fBLen\fP just +increases the length (without adding any content), \fBvalue\fP uses a string +and \fBfile\fP a file. +.TP +.B len <number> +extend the length of the packet by \fBnumber\fP bytes (without filling those +bytes with any particular data). +.TP +.B value <string> +indicates that the string provided should be added to the current packet as +data. A string may be a consecutive list of characters and numbers (with +no white spaces) or bounded by "'s (may not contain them, even if \\'d). +The \\ character is recognised with the appropriate C escaped values, including +octal numbers. +.TP +.B file <filename> +reads data in from the specified file and appends it to the current packet. +If the new total length would exceed 64k, an error will be reported. +.SH "ICMP TYPES" +.TP +.B echorep +Echo Reply. +.TP +.B "unreach [ unreachable-code ]" +Generic Unreachable error. This is used to indicate that an error has +occurred whilst trying to send the packet across the network and that the +destination cannot be reached. The unreachable code names are: +\fBnet-unr\fP network unreachable, \fBhost-unr\fP host unreachable, +\fBproto-unr\fP protocol unreachable, \fBport-unr\fP port unreachable, +\fBneedfrag\fP, \fBsrcfail\fP source route failed, +\fBnet-unk\fP network unknown, \fBhost-unk\fP host unknown, +\fBisolate\fP, \fBnet-prohib\fP administratively prohibited contact with +network, +\fBhost-prohib\fP administratively prohibited contact with host, +\fBnet-tos\fP network unreachable with given TOS, +\fBhost-tos\fP host unreachable with given TOS, +\fBfilter-prohib\fP packet prohibited by packet filter, +\fBhost-preced\fP, +\fBcutoff-preced\fP. +.TP +.B squench +Source Quence. +.TP +.B "redir [ redirect-code ]" +Redirect (routing). This is used to indicate that the route being chosen +for forwarding the packet is suboptimal and that the sender of the packet +should be routing packets via another route. The redirect code names are: +\fBnet-redir\fP redirect packets for a network, +\fBhost-redir\fP redirect packets for a host, +\fBtos-net-redir\fP redirect packets for a network with a given TOS, +\fBtos-host-redir\fP redirect packets for a host with a given TOS. +.TP +.B echo +Echo. +.TP +.B routerad +Router Advertisement. +.TP +.B routersol +Router solicitation. +.TP +.B "timex [ timexceed-code ]" +Time Exceeded. This is used to indicate that the packet failed to reach the +destination because it was in transit too long (i.e. ttl reached 0). The +valid code names are: \fBintrans\fP, +\fBreass\fP could not reassemble packet from fragments within a given time. +.TP +.B "paramprob [ paramprob-code ]" +Parameter problem. There is only one available parameter problem code name: +\fBoptabsent\fP. +.TP +.B timest +Time stamp request. +.TP +.B "timestrep [ { timestamp-code } ]" +Time stamp reply. In a timestamp reply, it is possible to supply the +following values: \fBrtime\fP, \fBotime\fP, \fBttime\fP. +.TP +.B inforeq +Information request. +.TP +.B inforep +Information reply. +.TP +.B maskreq +Address mask request. +.TP +.B maskrep +Address mask reply. +.SH FILES +/etc/hosts +.br +/etc/protocols +.br +/etc/services +.SH SEE ALSO +ipsend(1), iptest(1), hosts(5), protocols(5), services(5) diff --git a/sbin/ipf/ipsend/ipsend.c b/sbin/ipf/ipsend/ipsend.c new file mode 100644 index 000000000000..d77081bd8b71 --- /dev/null +++ b/sbin/ipf/ipsend/ipsend.c @@ -0,0 +1,416 @@ +/* $FreeBSD$ */ +/* + * ipsend.c (C) 1995-1998 Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ipsend.c 1.5 12/10/95 (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/in_systm.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <netdb.h> +#include <string.h> +#include <netinet/ip.h> +# include <netinet/ip_var.h> +#include "ipsend.h" +#include "ipf.h" +# include <netinet/udp_var.h> + + +extern char *optarg; +extern int optind; +extern void iplang(FILE *); + +char options[68]; +int opts; +char default_device[] = "le0"; + + +static void usage(char *); +static void do_icmp(ip_t *, char *); +void udpcksum(ip_t *, struct udphdr *, int); +int main(int, char **); + + +static void usage(prog) + char *prog; +{ + fprintf(stderr, "Usage: %s [options] dest [flags]\n\ +\toptions:\n\ +\t\t-d\tdebug mode\n\ +\t\t-i 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\ +\t\t-v\tverbose mode\n\ +\t\t-w <window>\tSet the TCP window size\n\ +", prog); + fprintf(stderr, "Usage: %s [-dv] -L <filename>\n\ +\toptions:\n\ +\t\t-d\tdebug mode\n\ +\t\t-L filename\tUse IP language for sending packets\n\ +\t\t-v\tverbose mode\n\ +", prog); + exit(1); +} + + +static 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; +{ + int wfd; + + wfd = initdevice(dev, 5); + if (wfd == -1) + return -1; + return send_packet(wfd, mtu, ip, gwip); +} + +void +udpcksum(ip_t *ip, struct udphdr *udp, int len) +{ + union pseudoh { + struct hdr { + u_short len; + u_char ttl; + u_char proto; + u_32_t src; + u_32_t dst; + } h; + u_short w[6]; + } ph; + u_32_t temp32; + u_short *opts; + + ph.h.len = htons(len); + ph.h.ttl = 0; + ph.h.proto = IPPROTO_UDP; + ph.h.src = ip->ip_src.s_addr; + ph.h.dst = ip->ip_dst.s_addr; + temp32 = 0; + opts = &ph.w[0]; + temp32 += opts[0] + opts[1] + opts[2] + opts[3] + opts[4] + opts[5]; + temp32 = (temp32 >> 16) + (temp32 & 65535); + temp32 += (temp32 >> 16); + udp->uh_sum = temp32 & 65535; + udp->uh_sum = chksum((u_short *)udp, len); + if (udp->uh_sum == 0) + udp->uh_sum = 0xffff; +} + +int main(argc, argv) + int argc; + char **argv; +{ + FILE *langfile = NULL; + struct in_addr gwip; + tcphdr_t *tcp; + udphdr_t *udp; + ip_t *ip; + char *name = argv[0], host[MAXHOSTNAMELEN + 1]; + char *gateway = NULL, *dev = NULL; + char *src = NULL, *dst, *s; + int mtu = 1500, olen = 0, c, nonl = 0; + + /* + * 65535 is maximum packet size...you never know... + */ + ip = (ip_t *)calloc(1, 65536); + tcp = (tcphdr_t *)(ip + 1); + udp = (udphdr_t *)tcp; + ip->ip_len = sizeof(*ip); + IP_HL_A(ip, sizeof(*ip) >> 2); + + while ((c = getopt(argc, argv, "I:L:P:TUdf:i:g:m:o:s:t:vw:")) != -1) { + switch (c) + { + case 'I' : + nonl++; + if (ip->ip_p) + { + fprintf(stderr, "Protocol already set: %d\n", + ip->ip_p); + break; + } + do_icmp(ip, optarg); + break; + case 'L' : + if (nonl) { + fprintf(stderr, + "Incorrect usage of -L option.\n"); + usage(name); + } + if (!strcmp(optarg, "-")) + langfile = stdin; + else if (!(langfile = fopen(optarg, "r"))) { + fprintf(stderr, "can't open file %s\n", + optarg); + exit(1); + } + iplang(langfile); + return 0; + case 'P' : + { + struct protoent *p; + + nonl++; + 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 'T' : + nonl++; + 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' : + nonl++; + 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' : + opts |= OPT_DEBUG; + break; + case 'f' : + nonl++; + ip->ip_off = strtol(optarg, NULL, 0); + break; + case 'g' : + nonl++; + gateway = optarg; + break; + case 'i' : + nonl++; + dev = optarg; + break; + case 'm' : + nonl++; + mtu = atoi(optarg); + if (mtu < 28) + { + fprintf(stderr, "mtu must be > 28\n"); + exit(1); + } + break; + case 'o' : + nonl++; + olen = buildopts(optarg, options, (IP_HL(ip) - 5) << 2); + break; + case 's' : + nonl++; + src = optarg; + break; + case 't' : + nonl++; + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + tcp->th_dport = htons(atoi(optarg)); + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + case 'w' : + nonl++; + 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 (argc - optind < 1) + 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", gateway); + exit(2); + } + + if (olen) + { + int hlen; + char *p; + + printf("Options: %d\n", olen); + hlen = sizeof(*ip) + olen; + IP_HL_A(ip, hlen >> 2); + ip->ip_len += olen; + p = (char *)malloc(65536); + if (p == NULL) + { + fprintf(stderr, "malloc failed\n"); + exit(2); + } + + bcopy(ip, p, sizeof(*ip)); + bcopy(options, p + sizeof(*ip), olen); + bcopy(ip + 1, p + hlen, ip->ip_len - hlen); + ip = (ip_t *)p; + + if (ip->ip_p == IPPROTO_TCP) { + tcp = (tcphdr_t *)(p + hlen); + } else if (ip->ip_p == IPPROTO_UDP) { + udp = (udphdr_t *)(p + hlen); + } + } + + if (ip->ip_p == IPPROTO_TCP) + for (s = argv[optind]; s && (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) + dev = default_device; + 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 (ip->ip_p == IPPROTO_UDP) { + udp->uh_sum = 0; + udpcksum(ip, udp, ip->ip_len - (IP_HL(ip) << 2)); + } +#ifdef DOSOCKET + if (ip->ip_p == IPPROTO_TCP && tcp->th_dport) + return do_socket(dev, mtu, ip, gwip); +#endif + return send_packets(dev, mtu, ip, gwip); +} diff --git a/sbin/ipf/ipsend/ipsend.h b/sbin/ipf/ipsend/ipsend.h new file mode 100644 index 000000000000..bfec90f1c5b3 --- /dev/null +++ b/sbin/ipf/ipsend/ipsend.h @@ -0,0 +1,62 @@ +/* $FreeBSD$ */ + +/* + * ipsend.h (C) 1997-1998 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. + * + */ +#ifndef __P +# define __P(x) x +#endif + +#include <net/if.h> + +#include "ipf.h" +/* XXX: The following is needed by tcpip.h */ +#include <netinet/ip_var.h> +#include "netinet/tcpip.h" +#include "ipt.h" + +extern int resolve(char *, char *); +extern int arp(char *, char *); +extern u_short chksum(u_short *, int); +extern int send_ether(int, char *, int, struct in_addr); +extern int send_ip(int, int, ip_t *, struct in_addr, int); +extern int send_tcp(int, int, ip_t *, struct in_addr); +extern int send_udp(int, int, ip_t *, struct in_addr); +extern int send_icmp(int, int, ip_t *, struct in_addr); +extern int send_packet(int, int, ip_t *, struct in_addr); +extern int send_packets(char *, int, ip_t *, struct in_addr); +extern u_short ipseclevel(char *); +extern u_32_t buildopts(char *, char *, int); +extern int addipopt(char *, struct ipopt_names *, int, char *); +extern int initdevice(char *, int); +extern int sendip(int, char *, int); +extern struct tcpcb *find_tcp(int, struct tcpiphdr *); +extern int ip_resend(char *, int, struct ipread *, struct in_addr, char *); + +extern void ip_test1(char *, int, ip_t *, struct in_addr, int); +extern void ip_test2(char *, int, ip_t *, struct in_addr, int); +extern void ip_test3(char *, int, ip_t *, struct in_addr, int); +extern void ip_test4(char *, int, ip_t *, struct in_addr, int); +extern void ip_test5(char *, int, ip_t *, struct in_addr, int); +extern void ip_test6(char *, int, ip_t *, struct in_addr, int); +extern void ip_test7(char *, int, ip_t *, struct in_addr, int); +extern int do_socket(char *, int, struct tcpiphdr *, struct in_addr); +extern int kmemcpy(char *, void *, int); + +#define KMCPY(a,b,c) kmemcpy((char *)(a), (void *)(b), (int)(c)) + +#ifndef OPT_RAW +#define OPT_RAW 0x80000 +#endif diff --git a/sbin/ipf/ipsend/ipsopt.c b/sbin/ipf/ipsend/ipsopt.c new file mode 100644 index 000000000000..ce6616525ca1 --- /dev/null +++ b/sbin/ipf/ipsend/ipsopt.c @@ -0,0 +1,194 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ipsopt.c 1.2 1/11/96 (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/param.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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include "ipsend.h" + + +#ifndef __P +# define __P(x) x +#endif + + +struct ipopt_names ionames[] = { + { IPOPT_EOL, 0x01, 1, "eol" }, + { IPOPT_NOP, 0x02, 1, "nop" }, + { IPOPT_RR, 0x04, 3, "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, 0, 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 ipseclevel(slevel) + char *slevel; +{ + struct ipopt_names *so; + + 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; +} + + +int addipopt(op, io, len, class) + char *op; + struct ipopt_names *io; + int len; + char *class; +{ + struct in_addr ipadr; + int olen = len, srr = 0; + u_short val; + u_char lvl; + char *s = op, *t; + + 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) { + /* + * Allow option to specify RR buffer length in bytes. + */ + if (io->on_value == IPOPT_RR) { + val = (class && *class) ? atoi(class) : 4; + *op++ = val + io->on_siz; + len += val; + } else + *op++ = io->on_siz; + if (io->on_value == IPOPT_TS) + *op++ = IPOPT_MINOFF + 1; + else + *op++ = IPOPT_MINOFF; + + while (class && *class) { + t = NULL; + switch (io->on_value) + { + case IPOPT_SECURITY : + lvl = ipseclevel(class); + *(op - 1) = lvl; + break; + case IPOPT_LSRR : + case IPOPT_SSRR : + if ((t = strchr(class, ','))) + *t = '\0'; + ipadr.s_addr = inet_addr(class); + srr++; + bcopy((char *)&ipadr, op, sizeof(ipadr)); + op += sizeof(ipadr); + break; + case IPOPT_SATID : + val = atoi(class); + bcopy((char *)&val, op, 2); + break; + } + + if (t) + *t++ = ','; + class = t; + } + if (srr) + s[IPOPT_OLEN] = IPOPT_MINOFF - 1 + 4 * srr; + if (io->on_value == IPOPT_RR) + op += val; + else + op += io->on_siz - 3; + } + return len - olen; +} + + +u_32_t buildopts(cp, op, len) + char *cp, *op; + int len; +{ + struct ipopt_names *io; + u_32_t msk = 0; + char *s, *t; + int inc, lastop = -1; + + 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; + lastop = io->on_value; + if ((inc = addipopt(op, io, len, t))) { + op += inc; + len += inc; + } + msk |= io->on_bit; + break; + } + if (!io->on_name) { + fprintf(stderr, "unknown IP option name %s\n", s); + return 0; + } + } + + if (len & 3) { + while (len & 3) { + *op++ = ((len & 3) == 3) ? IPOPT_EOL : IPOPT_NOP; + len++; + } + } else { + if (lastop != IPOPT_EOL) { + if (lastop == IPOPT_NOP) + *(op - 1) = IPOPT_EOL; + else { + *op++ = IPOPT_NOP; + *op++ = IPOPT_NOP; + *op++ = IPOPT_NOP; + *op = IPOPT_EOL; + len += 4; + } + } + } + return len; +} diff --git a/sbin/ipf/ipsend/iptest.1 b/sbin/ipf/ipsend/iptest.1 new file mode 100644 index 000000000000..8f25f4abf256 --- /dev/null +++ b/sbin/ipf/ipsend/iptest.1 @@ -0,0 +1,103 @@ +.\" $FreeBSD$ +.\" +.TH IPTEST 1 +.SH NAME +iptest \- automatically generate a packets to test IP functionality +.SH SYNOPSIS +.B iptest +[ +.B \-1234567 +] [ +.B \-d +<device> +] [ +.B \-g +<gateway> +] [ +.B \-m +<\fIMTU\fP> +] [ +.B \-p +<\fIpointtest\fP> +] [ +.B \-s +<\fIsource\fP> +] <destination> +.SH DESCRIPTION +.PP +\fBiptest\fP ... +.SH OPTIONS +.TP +.B \-1 +Run IP test group #1. This group of tests generates packets with the IP +header fields set to invalid values given other packet characteristics. +The point tests are: 1 (ip_hl < ip_len), 2 (ip_hl > ip_len), +3 (ip_v < 4), 4 (ip_v > 4), 5 (ip_len < packetsize, long packets), +6 (ip_len > packet size, short packets), 7 (Zero length fragments), +8 (packet > 64k after reassembly), 9 (IP offset with MSB set), 10 (ttl +variations). +.TP +.B \-2 +Run IP test group #2. This group of tests generates packets with the IP +options constructed with invalid values given other packet characteristics. +The point tests are: 1 (option length > packet length), 2 (option length = 0). +.TP +.B \-3 +Run IP test group #3. This group of tests generates packets with the ICMP +header fields set to non-standard values. The point tests are: 1 (ICMP types +0-31 & 255), 2 (type 3 & code 0 - 31), 3 (type 4 & code 0, 127, 128, 255), +4 (type 5 & code 0, 127, 128, 255), 5 (types 8-10,13-18 with codes 0, 127, +128 and 255), 6 (type 12 & code 0, 127, 128, 129, 255) and 7 (type 3 & codes +9-10, 13-14 and 17-18 - shortened packets). +.TP +.B \-4 +Run IP test group #4. This group of tests generates packets with the UDP +header fields set to non-standard values. The point tests are: 1 (UDP length +> packet size), 2 (UDP length < packetsize), 3 (sport = 0, 1, 32767, 32768, +65535), 4 (dport = 0, 1, 32767, 32768, 65535) and 5 (sizeof(struct ip) <= MTU +<= sizeof(struct udphdr) + sizeof(struct ip)). +.TP +.B \-5 +Run IP test group #5. This group of tests generates packets with the TCP +header fields set to non-standard values. The point tests are: 1 (TCP flags +variations, all combinations), 2 (seq = 0, 0x7fffffff, 0x8000000, 0xa0000000, +0xffffffff), 3 (ack = 0, 0x7fffffff, 0x8000000, 0xa0000000, 0xffffffff), +4 (SYN packet with window of 0, 32768, 65535), 5 (set urgent pointer to 1, +0x7fff, 0x8000, 0xffff), 6 (data offset), 7 (sport = 0, 1, 32767, 32768, +65535) and 8 (dport = 0, 1, 32767, 32768, 65535). +.TP +.B \-6 +Run IP test group #6. This test generates a large number of fragments in +an attempt to exhaust the network buffers used for holding packets for later +reassembly. WARNING: this may crash or cause serious performance degradation +to the target host. +.TP +.B \-7 +Run IP test group #7. This test generates 1024 random IP packets with only +the IP version, checksum, length and IP offset field correct. +.TP +.BR \-d \0<interface> +Set the interface name to be the name supplied. +.TP +.BR \-g \0<gateway> +Specify the hostname of the gateway through which to route packets. This +is required whenever the destination host isn't directly attached to the +same network as the host from which you're sending. +.TP +.BR \-m \0<MTU> +Specify the MTU to be used when sending out packets. This option allows you +to set a fake MTU, allowing the simulation of network interfaces with small +MTU's without setting them so. +.TP +.B \-p <test> +Run a... +.DT +.SH SEE ALSO +ipsend(1), ipresend(1), bpf(4), ipsend(5), dlpi(7p) +.SH DIAGNOSTICS +Only one of the numeric test options may be given when \fIiptest\fP is run. +.PP +Needs to be run as root. +.SH BUGS +.PP +If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipsend/iptest.c b/sbin/ipf/ipsend/iptest.c new file mode 100644 index 000000000000..df2efb96a32a --- /dev/null +++ b/sbin/ipf/ipsend/iptest.c @@ -0,0 +1,197 @@ +/* $FreeBSD$ */ + +/* + * ipsend.c (C) 1995-1998 Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +#if !defined(lint) +static const char sccsid[] = "%W% %G% (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <stdio.h> +#include <netdb.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "ipsend.h" + + +extern char *optarg; +extern int optind; + +char options[68]; +# ifdef sun +char default_device[] = "le0"; +# else +char default_device[] = "lan0"; +# endif + +static void usage(char *); +int main(int, char **); + + +static void usage(prog) + char *prog; +{ + fprintf(stderr, "Usage: %s [options] dest\n\ +\toptions:\n\ +\t\t-d device\tSend out on this device\n\ +\t\t-g gateway\tIP gateway to use if non-local dest.\n\ +\t\t-m mtu\t\tfake MTU to use when sending out\n\ +\t\t-p pointtest\t\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\ +\t\t-6 \t\tPerform test 6 (overlapping fragments)\n\ +\t\t-7 \t\tPerform test 7 (random packets)\n\ +", prog); + exit(1); +} + + +int main(argc, argv) + int argc; + char **argv; +{ + struct tcpiphdr *ti; + struct in_addr gwip; + ip_t *ip; + char *name = argv[0], host[MAXHOSTNAMELEN + 1]; + char *gateway = NULL, *dev = NULL; + char *src = NULL, *dst; + int mtu = 1500, tests = 0, pointtest = 0, c; + + /* + * 65535 is maximum packet size...you never know... + */ + ip = (ip_t *)calloc(1, 65536); + ti = (struct tcpiphdr *)ip; + ip->ip_len = sizeof(*ip); + IP_HL_A(ip, sizeof(*ip) >> 2); + + while ((c = getopt(argc, argv, "1234567d:g:m:p:s:")) != -1) + switch (c) + { + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + case '6' : + case '7' : + tests = c - '0'; + break; + case 'd' : + dev = optarg; + 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 'p' : + pointtest = atoi(optarg); + break; + case 's' : + src = optarg; + break; + default : + fprintf(stderr, "Unknown option \"%c\"\n", c); + usage(name); + } + + if ((argc <= optind) || !argv[optind]) + usage(name); + dst = argv[optind++]; + + if (!src) + { + gethostname(host, sizeof(host)); + host[sizeof(host) - 1] = '\0'; + src = host; + } + + if (resolve(dst, (char *)&ip->ip_dst) == -1) + { + fprintf(stderr,"Cant resolve %s\n", dst); + exit(2); + } + + if (resolve(src, (char *)&ip->ip_src) == -1) + { + fprintf(stderr,"Cant resolve %s\n", src); + exit(2); + } + + if (!gateway) + gwip = ip->ip_dst; + else if (resolve(gateway, (char *)&gwip) == -1) + { + fprintf(stderr,"Cant resolve %s\n", gateway); + exit(2); + } + + + if (!dev) + dev = default_device; + 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)); + printf("mtu: %d\n", mtu); + + switch (tests) + { + case 1 : + ip_test1(dev, mtu, (ip_t *)ti, gwip, pointtest); + break; + case 2 : + ip_test2(dev, mtu, (ip_t *)ti, gwip, pointtest); + break; + case 3 : + ip_test3(dev, mtu, (ip_t *)ti, gwip, pointtest); + break; + case 4 : + ip_test4(dev, mtu, (ip_t *)ti, gwip, pointtest); + break; + case 5 : + ip_test5(dev, mtu, (ip_t *)ti, gwip, pointtest); + break; + case 6 : + ip_test6(dev, mtu, (ip_t *)ti, gwip, pointtest); + break; + case 7 : + ip_test7(dev, mtu, (ip_t *)ti, gwip, pointtest); + break; + default : + ip_test1(dev, mtu, (ip_t *)ti, gwip, pointtest); + ip_test2(dev, mtu, (ip_t *)ti, gwip, pointtest); + ip_test3(dev, mtu, (ip_t *)ti, gwip, pointtest); + ip_test4(dev, mtu, (ip_t *)ti, gwip, pointtest); + ip_test5(dev, mtu, (ip_t *)ti, gwip, pointtest); + ip_test6(dev, mtu, (ip_t *)ti, gwip, pointtest); + ip_test7(dev, mtu, (ip_t *)ti, gwip, pointtest); + break; + } + return 0; +} diff --git a/sbin/ipf/ipsend/iptests.c b/sbin/ipf/ipsend/iptests.c new file mode 100644 index 000000000000..a4e1a99b2885 --- /dev/null +++ b/sbin/ipf/ipsend/iptests.c @@ -0,0 +1,1397 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +#if !defined(lint) +static const char sccsid[] = "%W% %G% (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/param.h> +#include <sys/types.h> +#if defined(__NetBSD__) && defined(__vax__) +/* + * XXX need to declare boolean_t for _KERNEL <sys/files.h> + * which ends up including <sys/device.h> for vax. See PR#32907 + * for further details. + */ +typedef int boolean_t; +#endif +#include <sys/time.h> +# ifdef __NetBSD__ +# include <machine/lock.h> +# include <machine/mutex.h> +# endif +# define _KERNEL +# define KERNEL +# if !defined(solaris) +# include <sys/file.h> +# else +# ifdef solaris +# include <sys/dditypes.h> +# endif +# endif +# undef _KERNEL +# undef KERNEL +#if !defined(solaris) +# include <nlist.h> +# include <sys/user.h> +# include <sys/proc.h> +#endif +# include <kvm.h> +# include <sys/socket.h> +#if defined(solaris) +# include <sys/stream.h> +#else +# include <sys/socketvar.h> +#endif +#ifdef sun +#include <sys/systm.h> +#include <sys/session.h> +#endif +# include <sys/sysctl.h> +# include <sys/filedesc.h> +# include <paths.h> +#include <netinet/in_systm.h> +#include <sys/socket.h> +#include <net/if.h> +# if defined(__FreeBSD__) +# include "radix_ipf.h" +# endif +# if !defined(solaris) +# include <net/route.h> +# endif +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#if defined(__SVR4) || defined(__svr4__) +# include <sys/sysmacros.h> +#endif +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +# include <netinet/ip_var.h> +# if !defined(solaris) +# include <netinet/in_pcb.h> +# endif +#include "ipsend.h" +# include <netinet/tcp_timer.h> +# include <netinet/tcp_var.h> +#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 106000000) +# define USE_NANOSLEEP +#endif + + +#ifdef USE_NANOSLEEP +# define PAUSE() ts.tv_sec = 0; ts.tv_nsec = 10000000; \ + (void) nanosleep(&ts, NULL) +#else +# define PAUSE() tv.tv_sec = 0; tv.tv_usec = 10000; \ + (void) select(0, NULL, NULL, NULL, &tv) +#endif + + +void ip_test1(dev, mtu, ip, gwip, ptest) + char *dev; + int mtu; + ip_t *ip; + struct in_addr gwip; + int ptest; +{ +#ifdef USE_NANOSLEEP + struct timespec ts; +#else + struct timeval tv; +#endif + udphdr_t *u; + int nfd, i = 0, len, id = getpid(); + + IP_HL_A(ip, sizeof(*ip) >> 2); + IP_V_A(ip, 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 = htons(1); + u->uh_dport = htons(9); + u->uh_sum = 0; + u->uh_ulen = htons(sizeof(*u) + 4); + ip->ip_len = sizeof(*ip) + ntohs(u->uh_ulen); + len = ip->ip_len; + + nfd = initdevice(dev, 1); + if (nfd == -1) + return; + + 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) + ntohs(u->uh_ulen)) >> 2); i++) { + IP_HL_A(ip, i >> 2); + (void) send_ip(nfd, 1500, ip, gwip, 1); + 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 + ntohs(u->uh_ulen)) >> 2); i++) { + IP_HL_A(ip, i >> 2); + (void) send_ip(nfd, 1500, ip, gwip, 1); + 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_HL_A(ip, sizeof(*ip) >> 2); + for (i = 0; i < 4; i++) { + IP_V_A(ip, i); + (void) send_ip(nfd, 1500, ip, gwip, 1); + 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_V_A(ip, i); + (void) send_ip(nfd, 1500, ip, gwip, 1); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + } + + if (!ptest || (ptest == 5)) { + /* + * Part5: len < packet + */ + ip->ip_id = 0; + IP_V_A(ip, IPVERSION); + i = ip->ip_len + 1; + 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((u_short *)ip, IP_HL(ip) << 2); + (void) send_ether(nfd, (char *)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 = i; + ip->ip_sum = 0; + ip->ip_sum = chksum((u_short *)ip, IP_HL(ip) << 2); + (void) send_ether(nfd, (char *)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 = i; + ip->ip_sum = 0; + ip->ip_sum = chksum((u_short *)ip, IP_HL(ip) << 2); + (void) send_ether(nfd, (char *)ip, len, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + ip->ip_len = 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((u_short *)ip, IP_HL(ip) << 2); + (void) send_ether(nfd, (char *)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, 1); + 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, 1); + 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, 1); + 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, 1); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 8)) { + struct timeval tv; + + gettimeofday(&tv, NULL); + srand(tv.tv_sec ^ getpid() ^ tv.tv_usec); + /* + * Part8.1: 63k packet + 1k fragment at offset 0x1ffe + * Mark it as being ICMP (so it doesn't get junked), but + * don't bother about the ICMP header, we're not worrying + * about that here. + */ + ip->ip_p = IPPROTO_ICMP; + ip->ip_off = htons(IP_MF); + u->uh_dport = htons(9); + ip->ip_id = htons(id++); + printf("1.8.1 63k packet + 1k fragment at offset 0x1ffe\n"); + ip->ip_len = 768 + 20 + 8; + (void) send_ip(nfd, mtu, ip, gwip, 1); + printf("%d\r", i); + + ip->ip_len = MIN(768 + 20, mtu - 68); + i = 512; + for (; i < (63 * 1024 + 768); i += 768) { + ip->ip_off = htons(IP_MF | (i >> 3)); + (void) send_ip(nfd, mtu, ip, gwip, 1); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + ip->ip_len = 896 + 20; + ip->ip_off = htons(i >> 3); + (void) send_ip(nfd, mtu, ip, gwip, 1); + printf("%d\r", i); + putchar('\n'); + fflush(stdout); + + /* + * Part8.2: 63k packet + 1k fragment at offset 0x1ffe + * Mark it as being ICMP (so it doesn't get junked), but + * don't bother about the ICMP header, we're not worrying + * about that here. (Lossage here) + */ + ip->ip_p = IPPROTO_ICMP; + ip->ip_off = htons(IP_MF); + u->uh_dport = htons(9); + ip->ip_id = htons(id++); + printf("1.8.2 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, 1); + 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 = htons(IP_MF | (i >> 3)); + if ((rand() & 0x1f) != 0) { + (void) send_ip(nfd, mtu, ip, gwip, 1); + printf("%d\r", i); + } else + printf("skip %d\n", i); + fflush(stdout); + PAUSE(); + } + ip->ip_len = 896 + 20; + ip->ip_off = htons(i >> 3); + if ((rand() & 0x1f) != 0) { + (void) send_ip(nfd, mtu, ip, gwip, 1); + printf("%d\r", i); + } else + printf("skip\n"); + putchar('\n'); + fflush(stdout); + + /* + * Part8.3: 33k packet - test for not dealing with -ve length + * Mark it as being ICMP (so it doesn't get junked), but + * don't bother about the ICMP header, we're not worrying + * about that here. + */ + ip->ip_p = IPPROTO_ICMP; + ip->ip_off = htons(IP_MF); + u->uh_dport = htons(9); + ip->ip_id = htons(id++); + printf("1.8.3 33k packet\n"); + ip->ip_len = 768 + 20 + 8; + (void) send_ip(nfd, mtu, ip, gwip, 1); + printf("%d\r", i); + + ip->ip_len = MIN(768 + 20, mtu - 68); + i = 512; + for (; i < (32 * 1024 + 768); i += 768) { + ip->ip_off = htons(IP_MF | (i >> 3)); + (void) send_ip(nfd, mtu, ip, gwip, 1); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + ip->ip_len = 896 + 20; + ip->ip_off = htons(i >> 3); + (void) send_ip(nfd, mtu, ip, gwip, 1); + printf("%d\r", i); + 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 = htons(0x8000); + printf("1.9. ip_off & 0x8000 == 0x8000\n"); + (void) send_ip(nfd, mtu, ip, gwip, 1); + 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, 1); + fflush(stdout); + PAUSE(); + + ip->ip_ttl = 128; + printf("1.10.1 ip_ttl = 128\n"); + (void) send_ip(nfd, mtu, ip, gwip, 1); + fflush(stdout); + PAUSE(); + + ip->ip_ttl = 0; + printf("1.10.2 ip_ttl = 0\n"); + (void) send_ip(nfd, mtu, ip, gwip, 1); + 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; +{ +#ifdef USE_NANOSLEEP + struct timespec ts; +#else + struct timeval tv; +#endif + int nfd; + u_char *s; + + + nfd = initdevice(dev, 1); + if (nfd == -1) + return; + + IP_HL_A(ip, 6); + ip->ip_len = IP_HL(ip) << 2; + s = (u_char *)(ip + 1); + 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, 1); + fflush(stdout); + PAUSE(); + } + + IP_HL_A(ip, 7); + ip->ip_len = IP_HL(ip) << 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, 1); + 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, 1); + 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, 1); + 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, 1); + 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, 1); + 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, 1); + 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 }; +#ifdef USE_NANOSLEEP + struct timespec ts; +#else + struct timeval tv; +#endif + struct icmp *icp; + int nfd, i; + + IP_HL_A(ip, sizeof(*ip) >> 2); + IP_V_A(ip, 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_HL(ip) << 2)); + + nfd = initdevice(dev, 1); + if (nfd == -1) + return; + + 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; +{ +#ifdef USE_NANOSLEEP + struct timespec ts; +#else + struct timeval tv; +#endif + udphdr_t *u; + int nfd, i; + + + IP_HL_A(ip, sizeof(*ip) >> 2); + IP_V_A(ip, 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_HL(ip) << 2)); + u->uh_sport = htons(1); + u->uh_dport = htons(1); + u->uh_ulen = htons(sizeof(*u) + 4); + + nfd = initdevice(dev, 1); + if (nfd == -1) + return; + + if (!ptest || (ptest == 1)) { + /* + * Test 1. ulen > packet + */ + u->uh_ulen = htons(sizeof(*u) + 4); + ip->ip_len = (IP_HL(ip) << 2) + ntohs(u->uh_ulen); + printf("4.1 UDP uh_ulen > packet size - short packets\n"); + for (i = ntohs(u->uh_ulen) * 2; i > sizeof(*u) + 4; i--) { + u->uh_ulen = htons(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 = htons(sizeof(*u) + 4); + ip->ip_len = (IP_HL(ip) << 2) + ntohs(u->uh_ulen); + printf("4.2 UDP uh_ulen < packet size - short packets\n"); + for (i = ntohs(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_HL(ip) << 2) + ntohs(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 = htons(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 = htons(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 = htons(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 = htons(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 = ntohs(sizeof(*u) + 4); + u->uh_sport = htons(1); + ip->ip_len = (IP_HL(ip) << 2) + ntohs(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 = htons(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 = htons(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 = htons(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 = htons(65535); + (void) send_udp(nfd, 1500, ip, gwip); + printf("65535\n"); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 5)) { + /* + * Test 5: sizeof(ip_t) <= MTU <= sizeof(udphdr_t) + + * sizeof(ip_t) + */ + printf("4.5 UDP 20 <= MTU <= 32\n"); + for (i = sizeof(*ip); i <= ntohs(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; +{ +#ifdef USE_NANOSLEEP + struct timespec ts; +#else + struct timeval tv; +#endif + tcphdr_t *t; + int nfd, i; + + t = (tcphdr_t *)((char *)ip + (IP_HL(ip) << 2)); + t->th_x2 = 0; + TCP_OFF_A(t, 0); + t->th_sport = htons(1); + t->th_dport = htons(1); + t->th_win = htons(4096); + t->th_urp = 0; + t->th_sum = 0; + t->th_seq = htonl(1); + t->th_ack = 0; + ip->ip_len = sizeof(ip_t) + sizeof(tcphdr_t); + + nfd = initdevice(dev, 1); + if (nfd == -1) + return; + + if (!ptest || (ptest == 1)) { + /* + * Test 1: flags variations, 0 - 3f + */ + TCP_OFF_A(t, 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 = htonl(0); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.2 TCP seq = 1\n"); + t->th_seq = htonl(1); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.3 TCP seq = 0x7fffffff\n"); + t->th_seq = htonl(0x7fffffff); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.4 TCP seq = 0x80000000\n"); + t->th_seq = htonl(0x80000000); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.5 TCP seq = 0xc0000000\n"); + t->th_seq = htonl(0xc0000000); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.2.6 TCP seq = 0xffffffff\n"); + t->th_seq = htonl(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 = htonl(1); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.3 TCP ack = 0x7fffffff\n"); + t->th_ack = htonl(0x7fffffff); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.4 TCP ack = 0x80000000\n"); + t->th_ack = htonl(0x80000000); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.5 TCP ack = 0xc0000000\n"); + t->th_ack = htonl(0xc0000000); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.3.6 TCP ack = 0xffffffff\n"); + t->th_ack = htonl(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 = htonl(0); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.4.2 TCP win = 32768\n"); + t->th_seq = htonl(0x7fff); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.4.3 TCP win = 65535\n"); + t->th_win = htons(0xffff); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + +#if !defined(linux) && !defined(__SVR4) && !defined(__svr4__) && \ + !defined(__sgi) && !defined(__hpux) && !defined(__osf__) + { + struct tcpcb *tcbp, tcb; + struct tcpiphdr ti; + struct sockaddr_in sin; + int fd; + socklen_t slen; + + bzero((char *)&sin, sizeof(sin)); + + for (i = 1; i < 63; i++) { + fd = socket(AF_INET, SOCK_STREAM, 0); + bzero((char *)&sin, sizeof(sin)); + sin.sin_addr.s_addr = ip->ip_dst.s_addr; + sin.sin_port = htons(i); + sin.sin_family = AF_INET; + if (!connect(fd, (struct sockaddr *)&sin, sizeof(sin))) + break; + close(fd); + } + + if (i == 63) { + printf("Couldn't open a TCP socket between ports 1 and 63\n"); + printf("to host %s for test 5 and 6 - skipping.\n", + inet_ntoa(ip->ip_dst)); + goto skip_five_and_six; + } + + bcopy((char *)ip, (char *)&ti, sizeof(*ip)); + t->th_dport = htons(i); + slen = sizeof(sin); + if (!getsockname(fd, (struct sockaddr *)&sin, &slen)) + t->th_sport = sin.sin_port; + if (!(tcbp = find_tcp(fd, &ti))) { + printf("Can't find PCB\n"); + goto skip_five_and_six; + } + KMCPY(&tcb, tcbp, sizeof(tcb)); + ti.ti_win = tcb.rcv_adv; + ti.ti_seq = htonl(tcb.snd_nxt - 1); + ti.ti_ack = tcb.rcv_nxt; + + if (!ptest || (ptest == 5)) { + /* + * Test 5: urp + */ + t->th_flags = TH_ACK|TH_URG; + printf("5.5.1 TCP Urgent pointer, sport %hu dport %hu\n", + ntohs(t->th_sport), ntohs(t->th_dport)); + t->th_urp = htons(1); + (void) send_tcp(nfd, mtu, ip, gwip); + PAUSE(); + + t->th_seq = htonl(tcb.snd_nxt); + ip->ip_len = sizeof(ip_t) + sizeof(tcphdr_t) + 1; + t->th_urp = htons(0x7fff); + (void) send_tcp(nfd, mtu, ip, gwip); + PAUSE(); + t->th_urp = htons(0x8000); + (void) send_tcp(nfd, mtu, ip, gwip); + PAUSE(); + t->th_urp = htons(0xffff); + (void) send_tcp(nfd, mtu, ip, gwip); + PAUSE(); + t->th_urp = 0; + t->th_flags &= ~TH_URG; + ip->ip_len = sizeof(ip_t) + sizeof(tcphdr_t); + } + + if (!ptest || (ptest == 6)) { + /* + * Test 6: data offset, off = 0, off is inside, off is outside + */ + t->th_flags = TH_ACK; + printf("5.6.1 TCP off = 1-15, len = 40\n"); + for (i = 1; i < 16; i++) { + TCP_OFF_A(t, ntohs(i)); + (void) send_tcp(nfd, mtu, ip, gwip); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + ip->ip_len = sizeof(ip_t) + sizeof(tcphdr_t); + } + + (void) close(fd); + } +skip_five_and_six: +#endif + t->th_seq = htonl(1); + t->th_ack = htonl(1); + TCP_OFF_A(t, 0); + + if (!ptest || (ptest == 7)) { + 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 = htons(1); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.7.3 TCP sport = 32767\n"); + t->th_sport = htons(32767); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.7.4 TCP sport = 32768\n"); + t->th_sport = htons(32768); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.7.5 TCP sport = 65535\n"); + t->th_sport = htons(65535); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + if (!ptest || (ptest == 8)) { + t->th_sport = htons(1); + t->th_flags = TH_SYN; + /* + * 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 = htons(1); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.8.3 TCP dport = 32767\n"); + t->th_dport = htons(32767); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.8.4 TCP dport = 32768\n"); + t->th_dport = htons(32768); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + + printf("5.8.5 TCP dport = 65535\n"); + t->th_dport = htons(65535); + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + /* LAND attack - self connect, so make src & dst ip/port the same */ + if (!ptest || (ptest == 9)) { + printf("5.9 TCP LAND attack. sport = 25, dport = 25\n"); + /* chose SMTP port 25 */ + t->th_sport = htons(25); + t->th_dport = htons(25); + t->th_flags = TH_SYN; + ip->ip_src = ip->ip_dst; + (void) send_tcp(nfd, mtu, ip, gwip); + fflush(stdout); + PAUSE(); + } + + /* TCP options header checking */ + /* 0 length options, etc */ +} + + +/* Perform test 6 (exhaust mbuf test) */ + +void ip_test6(dev, mtu, ip, gwip, ptest) + char *dev; + int mtu; + ip_t *ip; + struct in_addr gwip; + int ptest; +{ +#ifdef USE_NANOSLEEP + struct timespec ts; +#else + struct timeval tv; +#endif + udphdr_t *u; + int nfd, i, j, k; + + IP_V_A(ip, 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 = htons(1); + u->uh_dport = htons(9); + u->uh_sum = 0; + + nfd = initdevice(dev, 1); + if (nfd == -1) + return; + + u->uh_ulen = htons(7168); + + printf("6. Exhaustive mbuf test.\n"); + printf(" Send 7k packet in 768 & 128 byte fragments, 128 times.\n"); + printf(" Total of around 8,900 packets\n"); + for (i = 0; i < 128; i++) { + /* + * First send the entire packet in 768 byte chunks. + */ + ip->ip_len = sizeof(*ip) + 768 + sizeof(*u); + IP_HL_A(ip, sizeof(*ip) >> 2); + ip->ip_off = htons(IP_MF); + (void) send_ip(nfd, 1500, ip, gwip, 1); + printf("%d %d\r", i, 0); + fflush(stdout); + PAUSE(); + /* + * And again using 128 byte chunks. + */ + ip->ip_len = sizeof(*ip) + 128 + sizeof(*u); + ip->ip_off = htons(IP_MF); + (void) send_ip(nfd, 1500, ip, gwip, 1); + printf("%d %d\r", i, 0); + fflush(stdout); + PAUSE(); + + for (j = 768; j < 3584; j += 768) { + ip->ip_len = sizeof(*ip) + 768; + ip->ip_off = htons(IP_MF|(j>>3)); + (void) send_ip(nfd, 1500, ip, gwip, 1); + printf("%d %d\r", i, j); + fflush(stdout); + PAUSE(); + + ip->ip_len = sizeof(*ip) + 128; + for (k = j - 768; k < j; k += 128) { + ip->ip_off = htons(IP_MF|(k>>3)); + (void) send_ip(nfd, 1500, ip, gwip, 1); + printf("%d %d\r", i, k); + fflush(stdout); + PAUSE(); + } + } + } + putchar('\n'); +} + + +/* Perform test 7 (random packets) */ + +static u_long tbuf[64]; + +void ip_test7(dev, mtu, ip, gwip, ptest) + char *dev; + int mtu; + ip_t *ip; + struct in_addr gwip; + int ptest; +{ + ip_t *pip; +#ifdef USE_NANOSLEEP + struct timespec ts; +#else + struct timeval tv; +#endif + int nfd, i, j; + u_char *s; + + nfd = initdevice(dev, 1); + if (nfd == -1) + return; + + pip = (ip_t *)tbuf; + + srand(time(NULL) ^ (getpid() * getppid())); + + printf("7. send 1024 random IP packets.\n"); + + for (i = 0; i < 512; i++) { + for (s = (u_char *)pip, j = 0; j < sizeof(tbuf); j++, s++) + *s = (rand() >> 13) & 0xff; + IP_V_A(pip, IPVERSION); + bcopy((char *)&ip->ip_dst, (char *)&pip->ip_dst, + sizeof(struct in_addr)); + pip->ip_sum = 0; + pip->ip_len &= 0xff; + (void) send_ip(nfd, mtu, pip, gwip, 0); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); + + for (i = 0; i < 512; i++) { + for (s = (u_char *)pip, j = 0; j < sizeof(tbuf); j++, s++) + *s = (rand() >> 13) & 0xff; + IP_V_A(pip, IPVERSION); + pip->ip_off &= htons(0xc000); + bcopy((char *)&ip->ip_dst, (char *)&pip->ip_dst, + sizeof(struct in_addr)); + pip->ip_sum = 0; + pip->ip_len &= 0xff; + (void) send_ip(nfd, mtu, pip, gwip, 0); + printf("%d\r", i); + fflush(stdout); + PAUSE(); + } + putchar('\n'); +} diff --git a/sbin/ipf/ipsend/resend.c b/sbin/ipf/ipsend/resend.c new file mode 100644 index 000000000000..5d01bb78e5e5 --- /dev/null +++ b/sbin/ipf/ipsend/resend.c @@ -0,0 +1,141 @@ +/* $FreeBSD$ */ + +/* + * resend.c (C) 1995-1998 Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +#if !defined(lint) +static const char sccsid[] = "@(#)resend.c 1.3 1/11/96 (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +# include <netinet/ip_var.h> +# include <netinet/if_ether.h> +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include "ipsend.h" + +extern int opts; + +void dumppacket(ip_t *); + + +void dumppacket(ip) + ip_t *ip; +{ + tcphdr_t *t; + int i, j; + + t = (tcphdr_t *)((char *)ip + (IP_HL(ip) << 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_dport); + if (ip->ip_p == IPPROTO_TCP) { + printf(" seq %lu:%lu flags ", + (u_long)t->th_seq, (u_long)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; + int mtu; + struct in_addr gwip; + struct ipread *r; + char *datain; +{ + ether_header_t *eh; + char dhost[6]; + ip_t *ip; + int fd, wfd = initdevice(dev, 5), len, i; + mb_t mb; + + if (wfd == -1) + return -1; + + if (datain) + fd = (*r->r_open)(datain); + else + fd = (*r->r_open)("-"); + + if (fd < 0) + exit(-1); + + ip = (struct ip *)mb.mb_buf; + eh = (ether_header_t *)malloc(sizeof(*eh)); + if(!eh) + { + perror("malloc failed"); + return -2; + } + + bzero((char *) &eh->ether_shost, sizeof(eh->ether_shost)); + if (gwip.s_addr && (arp((char *)&gwip, dhost) == -1)) + { + perror("arp"); + free(eh); + return -2; + } + + while ((i = (*r->r_readip)(&mb, NULL, NULL)) > 0) + { + if (!(opts & OPT_RAW)) { + len = ntohs(ip->ip_len); + eh = (ether_header_t *)realloc((char *)eh, sizeof(*eh) + len); + eh->ether_type = htons((u_short)ETHERTYPE_IP); + if (!gwip.s_addr) { + if (arp((char *)&gwip, + (char *) &eh->ether_dhost) == -1) { + perror("arp"); + continue; + } + } else + bcopy(dhost, (char *) &eh->ether_dhost, + sizeof(dhost)); + if (!ip->ip_sum) + ip->ip_sum = chksum((u_short *)ip, + IP_HL(ip) << 2); + bcopy(ip, (char *)(eh + 1), len); + len += sizeof(*eh); + dumppacket(ip); + } else { + eh = (ether_header_t *)mb.mb_buf; + len = i; + } + + if (sendip(wfd, (char *)eh, len) == -1) + { + perror("send_packet"); + break; + } + } + (*r->r_close)(); + free(eh); + return 0; +} diff --git a/sbin/ipf/ipsend/sbpf.c b/sbin/ipf/ipsend/sbpf.c new file mode 100644 index 000000000000..27f239185d37 --- /dev/null +++ b/sbin/ipf/ipsend/sbpf.c @@ -0,0 +1,150 @@ +/* $FreeBSD$ */ +/* + * (C)opyright 1995-1998 Darren Reed. (from tcplog) + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/mbuf.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#ifdef __FreeBSD__ +# include <sys/dirent.h> +#else +# include <sys/dir.h> +#endif +#include <net/bpf.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> + +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#ifdef __NetBSD__ +# include <paths.h> +#endif +#include <ctype.h> +#include <signal.h> +#include <errno.h> + +#include "ipsend.h" + +#if !defined(lint) +static const char sccsid[] = "@(#)sbpf.c 1.3 8/25/95 (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +/* + * the code herein is dervied from libpcap. + */ +static u_char *buf = NULL; +static int bufsize = 0, timeout = 1; + + +int initdevice(device, tout) + char *device; + int tout; +{ + struct bpf_version bv; + struct timeval to; + struct ifreq ifr; +#ifdef _PATH_BPF + char *bpfname = _PATH_BPF; + int fd; + + if ((fd = open(bpfname, O_RDWR)) < 0) + { + fprintf(stderr, "no bpf devices available as /dev/bpfxx\n"); + return -1; + } +#else + char bpfname[16]; + int fd = 0, i; + + for (i = 0; i < 16; i++) + { + (void) snprintf(bpfname, sizeof(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; + } +#endif + + 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/sbin/ipf/ipsend/sdlpi.c b/sbin/ipf/ipsend/sdlpi.c new file mode 100644 index 000000000000..cd540337b2fa --- /dev/null +++ b/sbin/ipf/ipsend/sdlpi.c @@ -0,0 +1,166 @@ +/* $FreeBSD$ */ + +/* + * (C)opyright 1992-1998 Darren Reed. (from tcplog) + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ + +#include <stdio.h> +#include <netdb.h> +#include <ctype.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/timeb.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/stropts.h> + +#ifdef sun +# include <sys/pfmod.h> +# include <sys/bufmod.h> +#endif +# include <sys/dlpi.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 "ipsend.h" + +#if !defined(lint) +static const char sccsid[] = "@(#)sdlpi.c 1.3 10/30/95 (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +#define CHUNKSIZE 8192 +#define BUFSPACE (4*CHUNKSIZE) + + +/* + * Be careful to only include those defined in the flags option for the + * interface are included in the header size. + */ +int initdevice(device, tout) + char *device; + int tout; +{ + char devname[16], *s, buf[256]; + int i, fd; + + (void) strcpy(devname, "/dev/"); + (void) strncat(devname, device, sizeof(devname) - strlen(devname)); + + s = devname + 5; + while (*s && !ISDIGIT(*s)) + s++; + if (!*s) + { + fprintf(stderr, "bad device name %s\n", devname); + exit(-1); + } + i = atoi(s); + *s = '\0'; + /* + * For writing + */ + if ((fd = open(devname, O_RDWR)) < 0) + { + fprintf(stderr, "O_RDWR(1) "); + perror(devname); + exit(-1); + } + + if (dlattachreq(fd, i) == -1) + { + fprintf(stderr, "dlattachreq: DLPI error\n"); + exit(-1); + } + else if (dlokack(fd, buf) == -1) + { + fprintf(stderr, "dlokack(attach): DLPI error\n"); + exit(-1); + } +#ifdef DL_HP_RAWDLS + if (dlpromisconreq(fd, DL_PROMISC_SAP) < 0) + { + fprintf(stderr, "dlpromisconreq: DL_PROMISC_PHYS error\n"); + exit(-1); + } + else if (dlokack(fd, buf) < 0) + { + fprintf(stderr, "dlokack(promisc): DLPI error\n"); + exit(-1); + } + /* 22 is INSAP as per the HP-UX DLPI Programmer's Guide */ + + dlbindreq(fd, 22, 1, DL_HP_RAWDLS, 0, 0); +#else + dlbindreq(fd, ETHERTYPE_IP, 0, DL_CLDLS, 0, 0); +#endif + dlbindack(fd, buf); + /* + * write full headers + */ +#ifdef DLIOCRAW /* we require RAW DLPI mode, which is a Sun extension */ + if (strioctl(fd, DLIOCRAW, -1, 0, NULL) == -1) + { + fprintf(stderr, "DLIOCRAW error\n"); + exit(-1); + } +#endif + return fd; +} + + +/* + * output an IP packet onto a fd opened for /dev/nit + */ +int sendip(fd, pkt, len) + int fd, len; + char *pkt; +{ + struct strbuf dbuf, *dp = &dbuf, *cp = NULL; + int pri = 0; +#ifdef DL_HP_RAWDLS + struct strbuf cbuf; + dl_hp_rawdata_req_t raw; + + cp = &cbuf; + raw.dl_primitive = DL_HP_RAWDATA_REQ; + cp->len = sizeof(raw); + cp->buf = (char *)&raw; + cp->maxlen = cp->len; + pri = MSG_HIPRI; +#endif + /* + * construct NIT STREAMS messages, first control then data. + */ + dp->buf = pkt; + dp->len = len; + dp->maxlen = dp->len; + + if (putmsg(fd, cp, dp, pri) == -1) + { + perror("putmsg"); + return -1; + } + if (ioctl(fd, I_FLUSH, FLUSHW) == -1) + { + perror("I_FLUSHW"); + return -1; + } + return len; +} + diff --git a/sbin/ipf/ipsend/snit.c b/sbin/ipf/ipsend/snit.c new file mode 100644 index 000000000000..0d75b4e616f5 --- /dev/null +++ b/sbin/ipf/ipsend/snit.c @@ -0,0 +1,160 @@ +/* $FreeBSD$ */ + +/* + * (C)opyright 1992-1998 Darren Reed. (from tcplog) + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ + +#include <stdio.h> +#include <netdb.h> +#include <ctype.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/timeb.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <net/nit.h> +#include <sys/fcntlcom.h> +#include <sys/dir.h> +#include <net/nit_if.h> +#include <net/nit_pf.h> +#include <net/nit_buf.h> +#include <net/packetfilt.h> +#include <sys/stropts.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 "ipsend.h" + +#if !defined(lint) +static const char sccsid[] = "@(#)snit.c 1.5 1/11/96 (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +#define CHUNKSIZE 8192 +#define BUFSPACE (4*CHUNKSIZE) + +/* + * Be careful to only include those defined in the flags option for the + * interface are included in the header size. + */ +#define BUFHDR_SIZE (sizeof(struct nit_bufhdr)) +#define NIT_HDRSIZE (BUFHDR_SIZE) + +static int timeout; + + +int initdevice(device, tout) + char *device; + int tout; +{ + struct strioctl si; + struct timeval to; + struct ifreq ifr; + int fd; + + if ((fd = open("/dev/nit", O_RDWR)) < 0) + { + perror("/dev/nit"); + exit(-1); + } + + /* + * arrange to get messages from the NIT STREAM and use NIT_BUF option + */ + ioctl(fd, I_SRDOPT, (char*)RMSGD); + ioctl(fd, I_PUSH, "nbuf"); + + /* + * set the timeout + */ + timeout = tout; + si.ic_timout = 1; + to.tv_sec = 1; + to.tv_usec = 0; + si.ic_cmd = NIOCSTIME; + si.ic_len = sizeof(to); + si.ic_dp = (char*)&to; + if (ioctl(fd, I_STR, (char*)&si) == -1) + { + perror("ioctl: NIT timeout"); + exit(-1); + } + + /* + * request the interface + */ + strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = ' '; + si.ic_cmd = NIOCBIND; + si.ic_len = sizeof(ifr); + si.ic_dp = (char*)𝔦 + if (ioctl(fd, I_STR, (char*)&si) == -1) + { + perror(ifr.ifr_name); + exit(1); + } + return fd; +} + + +/* + * output an IP packet onto a fd opened for /dev/nit + */ +int sendip(fd, pkt, len) + int fd, len; + char *pkt; +{ + struct sockaddr sk, *sa = &sk; + struct strbuf cbuf, *cp = &cbuf, dbuf, *dp = &dbuf; + + /* + * For ethernet, need at least 802.3 header and IP header. + */ + if (len < (sizeof(sa->sa_data) + sizeof(struct ip))) + return -1; + /* + * to avoid any output processing for IP, say we're not. + */ + sa->sa_family = AF_UNSPEC; + bcopy(pkt, sa->sa_data, sizeof(sa->sa_data)); + pkt += sizeof(sa->sa_data); + len -= sizeof(sa->sa_data); + + /* + * construct NIT STREAMS messages, first control then data. + */ + cp->len = sizeof(*sa); + cp->maxlen = sizeof(*sa); + cp->buf = (char *)sa; + + dp->buf = pkt; + dp->len = len; + dp->maxlen = dp->len; + + if (putmsg(fd, cp, dp, 0) == -1) + { + perror("putmsg"); + return -1; + } + + if (ioctl(fd, I_FLUSH, FLUSHW) == -1) + { + perror("I_FLUSH"); + return -1; + } + return len; +} diff --git a/sbin/ipf/ipsend/sock.c b/sbin/ipf/ipsend/sock.c new file mode 100644 index 000000000000..51418d64d1fe --- /dev/null +++ b/sbin/ipf/ipsend/sock.c @@ -0,0 +1,319 @@ +/* $FreeBSD$ */ +/* + * sock.c (C) 1995-1998 Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +#if !defined(lint) +static const char sccsid[] = "@(#)sock.c 1.2 1/11/96 (C)1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#if defined(__NetBSD__) && defined(__vax__) +/* + * XXX need to declare boolean_t for _KERNEL <sys/files.h> + * which ends up including <sys/device.h> for vax. See PR#32907 + * for further details. + */ +typedef int boolean_t; +#endif +#include <fcntl.h> +# include <sys/dirent.h> +# ifdef __NetBSD__ +# include <machine/lock.h> +# endif +# ifdef __FreeBSD__ +# define _WANT_FILE +# else +# define _KERNEL +# define KERNEL +# endif +# include <sys/file.h> +# ifdef __FreeBSD__ +# undef _WANT_FILE +# else +# undef _KERNEL +# undef KERNEL +# endif +#include <nlist.h> +#include <sys/user.h> +#include <sys/socket.h> +#define _WANT_SOCKET +#include <sys/socketvar.h> +#include <sys/proc.h> +# include <kvm.h> +#ifdef sun +#include <sys/systm.h> +#include <sys/session.h> +#endif +#include <sys/sysctl.h> +#include <sys/filedesc.h> +#include <paths.h> +#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> +#define _WANT_INPCB +#include <netinet/in_pcb.h> +#include <netinet/tcp_timer.h> +#define _WANT_TCPCB +#include <netinet/tcp_var.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <pwd.h> +#include "ipsend.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 + + +static struct kinfo_proc *getproc(void); + + +int kmemcpy(buf, pos, n) + char *buf; + void *pos; + int n; +{ + static int kfd = -1; + off_t offset = (u_long)pos; + + if (kfd == -1) + kfd = open(KMEM, O_RDONLY); + + if (lseek(kfd, offset, SEEK_SET) == -1) + { + perror("lseek"); + return -1; + } + if (read(kfd, buf, n) == -1) + { + perror("read"); + return -1; + } + return n; +} + +struct nlist names[4] = { + { "_proc" }, + { "_nproc" }, + { NULL }, + { NULL } + }; + +static struct kinfo_proc *getproc() +{ + static struct kinfo_proc kp; + pid_t pid = getpid(); + int mib[4]; + size_t n; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + n = sizeof(kp); + 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 (fd == NULL) + return NULL; +#if defined( __FreeBSD__) + if (KMCPY(fd, p->ki_fd, sizeof(*fd)) == -1) + { + fprintf(stderr, "read(%#lx,%#lx) failed\n", + (u_long)p, (u_long)p->ki_fd); + free(fd); + return NULL; + } +#else + if (KMCPY(fd, p->kp_proc.p_fd, sizeof(*fd)) == -1) + { + fprintf(stderr, "read(%#lx,%#lx) failed\n", + (u_long)p, (u_long)p->kp_proc.p_fd); + free(fd); + return NULL; + } +#endif + + o = NULL; + f = NULL; + s = NULL; + i = NULL; + t = NULL; + + o = (struct file **)calloc(fd->fd_lastfile + 1, sizeof(*o)); + if (KMCPY(o, fd->fd_ofiles, (fd->fd_lastfile + 1) * sizeof(*o)) == -1) + { + fprintf(stderr, "read(%#lx,%#lx,%lu) - u_ofile - failed\n", + (u_long)fd->fd_ofiles, (u_long)o, (u_long)sizeof(*o)); + goto finderror; + } + f = (struct file *)calloc(1, sizeof(*f)); + if (KMCPY(f, o[tfd], sizeof(*f)) == -1) + { + fprintf(stderr, "read(%#lx,%#lx,%lu) - o[tfd] - failed\n", + (u_long)o[tfd], (u_long)f, (u_long)sizeof(*f)); + goto finderror; + } + + s = (struct socket *)calloc(1, sizeof(*s)); + if (KMCPY(s, f->f_data, sizeof(*s)) == -1) + { + fprintf(stderr, "read(%#lx,%#lx,%lu) - f_data - failed\n", + (u_long)f->f_data, (u_long)s, (u_long)sizeof(*s)); + goto finderror; + } + + i = (struct inpcb *)calloc(1, sizeof(*i)); + if (KMCPY(i, s->so_pcb, sizeof(*i)) == -1) + { + fprintf(stderr, "kvm_read(%#lx,%#lx,%lu) - so_pcb - failed\n", + (u_long)s->so_pcb, (u_long)i, (u_long)sizeof(*i)); + goto finderror; + } + + t = (struct tcpcb *)calloc(1, sizeof(*t)); + if (KMCPY(t, i->inp_ppcb, sizeof(*t)) == -1) + { + fprintf(stderr, "read(%#lx,%#lx,%lu) - inp_ppcb - failed\n", + (u_long)i->inp_ppcb, (u_long)t, (u_long)sizeof(*t)); + goto finderror; + } + return (struct tcpcb *)i->inp_ppcb; + +finderror: + if (o != NULL) + free(o); + if (f != NULL) + free(f); + if (s != NULL) + free(s); + if (i != NULL) + free(i); + if (t != NULL) + free(t); + return NULL; +} + +int do_socket(dev, mtu, ti, gwip) + char *dev; + int mtu; + struct tcpiphdr *ti; + struct in_addr gwip; +{ + struct sockaddr_in rsin, lsin; + struct tcpcb *t, tcb; + int fd, nfd; + socklen_t 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, 1); + if (nfd == -1) + return -1; + + 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; + } + KMCPY(&tcb, 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, (ip_t *)ti, gwip) == -1) + return -1; + (void)write(fd, "Hello World\n", 12); + sleep(2); + close(fd); + return 0; +} diff --git a/sbin/ipf/ipsend/sockraw.c b/sbin/ipf/ipsend/sockraw.c new file mode 100644 index 000000000000..c9232273ee86 --- /dev/null +++ b/sbin/ipf/ipsend/sockraw.c @@ -0,0 +1,89 @@ +/* $FreeBSD$ */ + +/* + * (C)opyright 2000 Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * WARNING: Attempting to use this .c file on HP-UX 11.00 will cause the + * system to crash. + */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.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 <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include "ipsend.h" + +#if !defined(lint) && defined(LIBC_SCCS) +static char sirix[] = "@(#)sirix.c 1.0 10/9/97 (C)1997 Marc Boucher"; +#endif + + +int initdevice(char *device, int tout) +{ + struct sockaddr s; + struct ifreq ifr; + int fd; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name); + + if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + { + perror("socket(AF_INET, SOCK_RAW, IPPROTO_RAW)"); + return -1; + } + + if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) + { + perror("ioctl SIOCGIFADDR"); + return -1; + } + + bzero((char *)&s, sizeof(s)); + s.sa_family = AF_INET; + bcopy(&ifr.ifr_addr, s.sa_data, 4); + if (bind(fd, &s, sizeof(s)) == -1) + perror("bind"); + return fd; +} + + +/* + * output an IP packet + */ +int sendip(int fd, char *pkt, int len) +{ + struct ether_header *eh; + struct sockaddr_in sin; + + eh = (struct ether_header *)pkt; + bzero((char *)&sin, sizeof(sin)); + sin.sin_family = AF_INET; + pkt += 14; + len -= 14; + bcopy(pkt + 12, (char *)&sin.sin_addr, 4); + + if (sendto(fd, pkt, len, 0, &sin, sizeof(sin)) == -1) + { + perror("send"); + return -1; + } + + return len; +} diff --git a/sbin/ipf/libipf/addicmp.c b/sbin/ipf/libipf/addicmp.c new file mode 100644 index 000000000000..da52f1caacfe --- /dev/null +++ b/sbin/ipf/libipf/addicmp.c @@ -0,0 +1,21 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <ctype.h> + +#include "ipf.h" + + +char *icmptypes[MAX_ICMPTYPE + 1] = { + "echorep", (char *)NULL, (char *)NULL, "unreach", "squench", + "redir", (char *)NULL, (char *)NULL, "echo", "routerad", + "routersol", "timex", "paramprob", "timest", "timestrep", + "inforeq", "inforep", "maskreq", "maskrep", "END" +}; diff --git a/sbin/ipf/libipf/addipopt.c b/sbin/ipf/libipf/addipopt.c new file mode 100644 index 000000000000..26aff83f31b6 --- /dev/null +++ b/sbin/ipf/libipf/addipopt.c @@ -0,0 +1,65 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +int addipopt(op, io, len, class) + char *op; + struct ipopt_names *io; + int len; + char *class; +{ + int olen = len; + struct in_addr ipadr; + u_short val; + u_char lvl; + char *s; + + 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) { + s = op; + *op++ = io->on_siz; + *op++ = IPOPT_MINOFF; + + if (class) { + switch (io->on_value) + { + case IPOPT_SECURITY : + lvl = seclevel(class); + *(op - 1) = lvl; + break; + case IPOPT_RR : + case IPOPT_TS : + s[IPOPT_OLEN] = IPOPT_MINOFF - 1 + 4; + break; + case IPOPT_LSRR : + case IPOPT_SSRR : + ipadr.s_addr = inet_addr(class); + s[IPOPT_OLEN] = IPOPT_MINOFF - 1 + 4; + bcopy((char *)&ipadr, op, sizeof(ipadr)); + break; + case IPOPT_SATID : + val = atoi(class); + bcopy((char *)&val, op, 2); + break; + } + } + } + if (opts & OPT_DEBUG) + fprintf(stderr, "bo: %s %d %#x: %d\n", + io->on_name, io->on_value, io->on_bit, len); + return len - olen; +} diff --git a/sbin/ipf/libipf/alist_free.c b/sbin/ipf/libipf/alist_free.c new file mode 100644 index 000000000000..44dea1330f81 --- /dev/null +++ b/sbin/ipf/libipf/alist_free.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: alist_free.c,v 1.3.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ +#include "ipf.h" + +void +alist_free(hosts) + alist_t *hosts; +{ + alist_t *a, *next; + + for (a = hosts; a != NULL; a = next) { + next = a->al_next; + free(a); + } +} diff --git a/sbin/ipf/libipf/alist_new.c b/sbin/ipf/libipf/alist_new.c new file mode 100644 index 000000000000..73bc03073990 --- /dev/null +++ b/sbin/ipf/libipf/alist_new.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: alist_new.c,v 1.5.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" +#include <ctype.h> + +alist_t * +alist_new(int family, char *host) +{ + int a, b, c, d, bits; + char *slash; + alist_t *al; + u_int mask; + + if (family == AF_UNSPEC) { + if (strchr(host, ':') != NULL) + family = AF_INET6; + else + family = AF_INET; + } + if (family != AF_INET && family != AF_INET6) + return NULL; + + al = calloc(1, sizeof(*al)); + if (al == NULL) { + fprintf(stderr, "alist_new out of memory\n"); + return NULL; + } + + while (ISSPACE(*host)) + host++; + + if (*host == '!') { + al->al_not = 1; + host++; + while (ISSPACE(*host)) + host++; + } + + bits = -1; + slash = strchr(host, '/'); + if (slash != NULL) { + *slash = '\0'; + bits = atoi(slash + 1); + } + + if (family == AF_INET) { + if (bits > 32) + goto bad; + + a = b = c = d = -1; + sscanf(host, "%d.%d.%d.%d", &a, &b, &c, &d); + + if (bits > 0 && bits < 33) { + mask = 0xffffffff << (32 - bits); + } else if (b == -1) { + mask = 0xff000000; + b = c = d = 0; + } else if (c == -1) { + mask = 0xffff0000; + c = d = 0; + } else if (d == -1) { + mask = 0xffffff00; + d = 0; + } else { + mask = 0xffffffff; + } + al->al_mask = htonl(mask); + } else { + if (bits > 128) + goto bad; + fill6bits(bits, al->al_i6mask.i6); + } + + if (gethost(family, host, &al->al_i6addr) == -1) { + if (slash != NULL) + *slash = '/'; + fprintf(stderr, "Cannot parse hostname\n"); + goto bad; + } + al->al_family = family; + if (slash != NULL) + *slash = '/'; + return al; +bad: + free(al); + return NULL; +} diff --git a/sbin/ipf/libipf/allocmbt.c b/sbin/ipf/libipf/allocmbt.c new file mode 100644 index 000000000000..df776842736c --- /dev/null +++ b/sbin/ipf/libipf/allocmbt.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: allocmbt.c,v 1.1.4.1 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + +mb_t *allocmbt(size_t len) +{ + mb_t *m; + + m = (mb_t *)malloc(sizeof(mb_t)); + if (m == NULL) + return NULL; + m->mb_len = len; + m->mb_next = NULL; + m->mb_data = (char *)m->mb_buf; + return m; +} diff --git a/sbin/ipf/libipf/assigndefined.c b/sbin/ipf/libipf/assigndefined.c new file mode 100644 index 000000000000..34f8d9af3acc --- /dev/null +++ b/sbin/ipf/libipf/assigndefined.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: assigndefined.c,v 1.4.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + +void assigndefined(env) + char *env; +{ + char *s, *t; + + if (env == NULL) + return; + + for (s = strtok(env, ";"); s != NULL; s = strtok(NULL, ";")) { + t = strchr(s, '='); + if (t == NULL) + continue; + *t++ = '\0'; + set_variable(s, t); + *--t = '='; + } +} diff --git a/sbin/ipf/libipf/bcopywrap.c b/sbin/ipf/libipf/bcopywrap.c new file mode 100644 index 000000000000..453c0464846f --- /dev/null +++ b/sbin/ipf/libipf/bcopywrap.c @@ -0,0 +1,20 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +int bcopywrap(from, to, size) + void *from, *to; + size_t size; +{ + bcopy((caddr_t)from, (caddr_t)to, size); + return 0; +} + diff --git a/sbin/ipf/libipf/binprint.c b/sbin/ipf/libipf/binprint.c new file mode 100644 index 000000000000..f826721e0d21 --- /dev/null +++ b/sbin/ipf/libipf/binprint.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void binprint(ptr, size) + void *ptr; + size_t size; +{ + u_char *s; + int i, j; + + for (i = size, j = 0, s = (u_char *)ptr; i; i--, s++) { + j++; + printf("%02x ", *s); + if (j == 16) { + printf("\n"); + j = 0; + } + } + putchar('\n'); + (void)fflush(stdout); +} diff --git a/sbin/ipf/libipf/buildopts.c b/sbin/ipf/libipf/buildopts.c new file mode 100644 index 000000000000..1d1de8c36784 --- /dev/null +++ b/sbin/ipf/libipf/buildopts.c @@ -0,0 +1,50 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +u_32_t buildopts(cp, op, len) + char *cp, *op; + int len; +{ + struct ipopt_names *io; + u_32_t msk = 0; + char *s, *t; + int inc; + + for (s = strtok(cp, ","); s; s = strtok(NULL, ",")) { + if ((t = strchr(s, '='))) + *t++ = '\0'; + else + t = ""; + for (io = ionames; io->on_name; io++) { + if (strcasecmp(s, io->on_name) || (msk & io->on_bit)) + continue; + if ((inc = addipopt(op, io, len, t))) { + op += inc; + len += inc; + } + msk |= io->on_bit; + break; + } + if (!io->on_name) { + fprintf(stderr, "unknown IP option name %s\n", s); + return 0; + } + } + while ((len & 3) != 3) { + *op++ = IPOPT_NOP; + len++; + } + *op++ = IPOPT_EOL; + len++; + return len; +} diff --git a/sbin/ipf/libipf/checkrev.c b/sbin/ipf/libipf/checkrev.c new file mode 100644 index 000000000000..b6f8eeec1fc2 --- /dev/null +++ b/sbin/ipf/libipf/checkrev.c @@ -0,0 +1,46 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <sys/ioctl.h> +#include <fcntl.h> + +#include "ipf.h" +#include "netinet/ipl.h" + +int checkrev(ipfname) + char *ipfname; +{ + static int vfd = -1; + struct friostat fio; + ipfobj_t obj; + + bzero((caddr_t)&obj, sizeof(obj)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = sizeof(fio); + obj.ipfo_ptr = (void *)&fio; + obj.ipfo_type = IPFOBJ_IPFSTAT; + + if ((vfd == -1) && ((vfd = open(ipfname, O_RDONLY)) == -1)) { + perror("open device"); + return -1; + } + + if (ioctl(vfd, SIOCGETFS, &obj)) { + ipferror(vfd, "ioctl(SIOCGETFS)"); + close(vfd); + vfd = -1; + return -1; + } + + if (strncmp(IPL_VERSION, fio.f_version, sizeof(fio.f_version))) { + return -1; + } + return 0; +} diff --git a/sbin/ipf/libipf/connecttcp.c b/sbin/ipf/libipf/connecttcp.c new file mode 100644 index 000000000000..2bab2afe0daa --- /dev/null +++ b/sbin/ipf/libipf/connecttcp.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: connecttcp.c,v 1.3.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" +#include <ctype.h> + +/* + * Format expected is one addres per line, at the start of each line. + */ +int +connecttcp(char *server, int port) +{ + struct sockaddr_in sin; + struct hostent *host; + int fd; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port & 65535); + + if (ISDIGIT(*server)) { + if (inet_aton(server, &sin.sin_addr) == -1) { + return -1; + } + } else { + host = gethostbyname(server); + if (host == NULL) + return -1; + memcpy(&sin.sin_addr, host->h_addr_list[0], + sizeof(sin.sin_addr)); + } + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) + return -1; + + if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + close(fd); + return -1; + } + + return fd; +} diff --git a/sbin/ipf/libipf/count4bits.c b/sbin/ipf/libipf/count4bits.c new file mode 100644 index 000000000000..a847388fad8e --- /dev/null +++ b/sbin/ipf/libipf/count4bits.c @@ -0,0 +1,40 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +/* + * 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 count4bits(ip) + u_int ip; +{ + int cnt = 0, i, j; + u_int ipn; + + 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; +} diff --git a/sbin/ipf/libipf/count6bits.c b/sbin/ipf/libipf/count6bits.c new file mode 100644 index 000000000000..b8f43206a8bd --- /dev/null +++ b/sbin/ipf/libipf/count6bits.c @@ -0,0 +1,29 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +int count6bits(msk) + u_32_t *msk; +{ + int i = 0, k; + u_32_t j; + + for (k = 3; k >= 0; k--) + if (msk[k] == 0xffffffff) + i += 32; + else { + for (j = msk[k]; j; j <<= 1) + if (j & 0x80000000) + i++; + } + return i; +} diff --git a/sbin/ipf/libipf/debug.c b/sbin/ipf/libipf/debug.c new file mode 100644 index 000000000000..0e3276e21705 --- /dev/null +++ b/sbin/ipf/libipf/debug.c @@ -0,0 +1,43 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +# include <stdarg.h> +#include <stdio.h> + +#include "ipf.h" +#include "opts.h" + +int debuglevel = 0; + + +void +debug(int level, char *fmt, ...) +{ + va_list pvar; + + va_start(pvar, fmt); + + if ((debuglevel > 0) && (level <= debuglevel)) + vfprintf(stderr, fmt, pvar); + va_end(pvar); +} + + +void +ipfkdebug(char *fmt, ...) +{ + va_list pvar; + + va_start(pvar, fmt); + + if (opts & OPT_DEBUG) + debug(0x1fffffff, fmt, pvar); + va_end(pvar); +} diff --git a/sbin/ipf/libipf/dupmbt.c b/sbin/ipf/libipf/dupmbt.c new file mode 100644 index 000000000000..0929eeb54229 --- /dev/null +++ b/sbin/ipf/libipf/dupmbt.c @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: dupmbt.c,v 1.3.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + +mb_t *dupmbt(orig) + mb_t *orig; +{ + mb_t *m; + + m = (mb_t *)malloc(sizeof(mb_t)); + if (m == NULL) + return NULL; + m->mb_len = orig->mb_len; + m->mb_next = NULL; + m->mb_data = (char *)m->mb_buf + (orig->mb_data - (char *)orig->mb_buf); + bcopy(orig->mb_data, m->mb_data, m->mb_len); + return m; +} diff --git a/sbin/ipf/libipf/facpri.c b/sbin/ipf/libipf/facpri.c new file mode 100644 index 000000000000..c9b47746b3ac --- /dev/null +++ b/sbin/ipf/libipf/facpri.c @@ -0,0 +1,153 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#if !defined(__SVR4) && !defined(__svr4__) +#include <strings.h> +#endif +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <syslog.h> +#include "facpri.h" + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + + +typedef struct table { + char *name; + int value; +} table_t; + +table_t facs[] = { + { "kern", LOG_KERN }, { "user", LOG_USER }, + { "mail", LOG_MAIL }, { "daemon", LOG_DAEMON }, + { "auth", LOG_AUTH }, { "syslog", LOG_SYSLOG }, + { "lpr", LOG_LPR }, { "news", LOG_NEWS }, + { "uucp", LOG_UUCP }, +#if LOG_CRON == LOG_CRON2 + { "cron2", LOG_CRON1 }, +#else + { "cron", LOG_CRON1 }, +#endif +#ifdef LOG_FTP + { "ftp", LOG_FTP }, +#endif +#ifdef LOG_AUTHPRIV + { "authpriv", LOG_AUTHPRIV }, +#endif +#ifdef LOG_AUDIT + { "audit", LOG_AUDIT }, +#endif +#ifdef LOG_LFMT + { "logalert", LOG_LFMT }, +#endif +#if LOG_CRON == LOG_CRON1 + { "cron", LOG_CRON2 }, +#else + { "cron2", LOG_CRON2 }, +#endif +#ifdef LOG_SECURITY + { "security", LOG_SECURITY }, +#endif + { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, + { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, + { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, + { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, + { NULL, 0 } +}; + + +/* + * map a facility number to its name + */ +char * +fac_toname(facpri) + int facpri; +{ + int i, j, fac; + + fac = facpri & LOG_FACMASK; + j = fac >> 3; + if (j < (sizeof(facs)/sizeof(facs[0]))) { + if (facs[j].value == fac) + return facs[j].name; + } + for (i = 0; facs[i].name; i++) + if (fac == facs[i].value) + return facs[i].name; + + return NULL; +} + + +/* + * map a facility name to its number + */ +int +fac_findname(name) + char *name; +{ + int i; + + for (i = 0; facs[i].name; i++) + if (!strcmp(facs[i].name, name)) + return facs[i].value; + return -1; +} + + +table_t pris[] = { + { "emerg", LOG_EMERG }, { "alert", LOG_ALERT }, + { "crit", LOG_CRIT }, { "err", LOG_ERR }, + { "warn", LOG_WARNING }, { "notice", LOG_NOTICE }, + { "info", LOG_INFO }, { "debug", LOG_DEBUG }, + { NULL, 0 } +}; + + +/* + * map a facility name to its number + */ +int +pri_findname(name) + char *name; +{ + int i; + + for (i = 0; pris[i].name; i++) + if (!strcmp(pris[i].name, name)) + return pris[i].value; + return -1; +} + + +/* + * map a priority number to its name + */ +char * +pri_toname(facpri) + int facpri; +{ + int i, pri; + + pri = facpri & LOG_PRIMASK; + if (pris[pri].value == pri) + return pris[pri].name; + for (i = 0; pris[i].name; i++) + if (pri == pris[i].value) + return pris[i].name; + return NULL; +} diff --git a/sbin/ipf/libipf/facpri.h b/sbin/ipf/libipf/facpri.h new file mode 100644 index 000000000000..5698c0ebe047 --- /dev/null +++ b/sbin/ipf/libipf/facpri.h @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#ifndef __FACPRI_H__ +#define __FACPRI_H__ + +#ifndef __P +# define P_DEF +# define __P(x) x +#endif + +extern char *fac_toname(int); +extern int fac_findname(char *); + +extern char *pri_toname(int); +extern int pri_findname(char *); + +#ifdef P_DEF +# undef __P +# undef P_DEF +#endif + +#if LOG_CRON == (9<<3) +# define LOG_CRON1 LOG_CRON +# define LOG_CRON2 (15<<3) +#endif +#if LOG_CRON == (15<<3) +# define LOG_CRON1 (9<<3) +# define LOG_CRON2 LOG_CRON +#endif + +#endif /* __FACPRI_H__ */ diff --git a/sbin/ipf/libipf/familyname.c b/sbin/ipf/libipf/familyname.c new file mode 100644 index 000000000000..891c6715ed0b --- /dev/null +++ b/sbin/ipf/libipf/familyname.c @@ -0,0 +1,12 @@ +#include "ipf.h" + +const char *familyname(int family) +{ + if (family == AF_INET) + return "inet"; +#ifdef USE_INET6 + if (family == AF_INET6) + return "inet6"; +#endif + return "unknown"; +} diff --git a/sbin/ipf/libipf/fill6bits.c b/sbin/ipf/libipf/fill6bits.c new file mode 100644 index 000000000000..39ec735f8ee9 --- /dev/null +++ b/sbin/ipf/libipf/fill6bits.c @@ -0,0 +1,48 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void fill6bits(bits, msk) + int bits; + u_int *msk; +{ + if (bits == 0) { + msk[0] = 0; + msk[1] = 0; + msk[2] = 0; + msk[3] = 0; + return; + } + + msk[0] = 0xffffffff; + msk[1] = 0xffffffff; + msk[2] = 0xffffffff; + msk[3] = 0xffffffff; + + if (bits == 128) + return; + if (bits > 96) { + msk[3] = htonl(msk[3] << (128 - bits)); + } else if (bits > 64) { + msk[3] = 0; + msk[2] = htonl(msk[2] << (96 - bits)); + } else if (bits > 32) { + msk[3] = 0; + msk[2] = 0; + msk[1] = htonl(msk[1] << (64 - bits)); + } else { + msk[3] = 0; + msk[2] = 0; + msk[1] = 0; + msk[0] = htonl(msk[0] << (32 - bits)); + } +} diff --git a/sbin/ipf/libipf/findword.c b/sbin/ipf/libipf/findword.c new file mode 100644 index 000000000000..e06f213c0540 --- /dev/null +++ b/sbin/ipf/libipf/findword.c @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: findword.c,v 1.3.4.1 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + + +wordtab_t *findword(words, name) + wordtab_t *words; + char *name; +{ + wordtab_t *w; + + for (w = words; w->w_word != NULL; w++) + if (!strcmp(name, w->w_word)) + break; + if (w->w_word == NULL) + return NULL; + + return w; +} diff --git a/sbin/ipf/libipf/flags.c b/sbin/ipf/libipf/flags.c new file mode 100644 index 000000000000..05fcc9874866 --- /dev/null +++ b/sbin/ipf/libipf/flags.c @@ -0,0 +1,25 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +/* + * ECN is a new addition to TCP - RFC 2481 + */ +#ifndef TH_ECN +# define TH_ECN 0x40 +#endif +#ifndef TH_CWR +# define TH_CWR 0x80 +#endif + +char flagset[] = "FSRPAUEC"; +u_char flags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, TH_ACK, TH_URG, + TH_ECN, TH_CWR }; diff --git a/sbin/ipf/libipf/freembt.c b/sbin/ipf/libipf/freembt.c new file mode 100644 index 000000000000..0fc748decd65 --- /dev/null +++ b/sbin/ipf/libipf/freembt.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: freembt.c,v 1.3.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + +void freembt(m) + mb_t *m; +{ + + free(m); +} diff --git a/sbin/ipf/libipf/ftov.c b/sbin/ipf/libipf/ftov.c new file mode 100644 index 000000000000..cb9715de450f --- /dev/null +++ b/sbin/ipf/libipf/ftov.c @@ -0,0 +1,16 @@ +#include "ipf.h" + +int +ftov(version) + int version; +{ +#ifdef USE_INET6 + if (version == AF_INET6) + return 6; +#endif + if (version == AF_INET) + return 4; + if (version == AF_UNSPEC) + return 0; + return -1; +} diff --git a/sbin/ipf/libipf/gethost.c b/sbin/ipf/libipf/gethost.c new file mode 100644 index 000000000000..14099e25c372 --- /dev/null +++ b/sbin/ipf/libipf/gethost.c @@ -0,0 +1,76 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +int gethost(family, name, hostp) + int family; + char *name; + i6addr_t *hostp; +{ + struct hostent *h; + struct netent *n; + u_32_t addr; + + bzero(hostp, sizeof(*hostp)); + if (!strcmp(name, "test.host.dots")) { + if (family == AF_INET) { + hostp->in4.s_addr = htonl(0xfedcba98); + } +#ifdef USE_INET6 + if (family == AF_INET6) { + hostp->i6[0] = htonl(0xfe80aa55); + hostp->i6[1] = htonl(0x12345678); + hostp->i6[2] = htonl(0x5a5aa5a5); + hostp->i6[3] = htonl(0xfedcba98); + } +#endif + return 0; + } + + if (!strcmp(name, "<thishost>")) + name = thishost; + + if (family == AF_INET) { + h = gethostbyname(name); + if (h != NULL) { + if ((h->h_addr != NULL) && + (h->h_length == sizeof(addr))) { + bcopy(h->h_addr, (char *)&addr, sizeof(addr)); + hostp->in4.s_addr = addr; + return 0; + } + } + + n = getnetbyname(name); + if (n != NULL) { + hostp->in4.s_addr = htonl(n->n_net & 0xffffffff); + return 0; + } + } +#ifdef USE_INET6 + if (family == AF_INET6) { + struct addrinfo hints, *res; + struct sockaddr_in6 *sin6; + + bzero((char *)&hints, sizeof(hints)); + hints.ai_family = PF_INET6; + + getaddrinfo(name, NULL, &hints, &res); + if (res != NULL) { + sin6 = (struct sockaddr_in6 *)res->ai_addr; + hostp->in6 = sin6->sin6_addr; + freeaddrinfo(res); + return 0; + } + } +#endif + return -1; +} diff --git a/sbin/ipf/libipf/geticmptype.c b/sbin/ipf/libipf/geticmptype.c new file mode 100644 index 000000000000..5c962e949526 --- /dev/null +++ b/sbin/ipf/libipf/geticmptype.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + +int geticmptype(family, name) + int family; + char *name; +{ + icmptype_t *i; + + for (i = icmptypelist; i->it_name != NULL; i++) { + if (!strcmp(name, i->it_name)) { + if (family == AF_INET) + return i->it_v4; +#ifdef USE_INET6 + if (family == AF_INET6) + return i->it_v6; +#endif + return -1; + } + } + + return -1; +} diff --git a/sbin/ipf/libipf/getifname.c b/sbin/ipf/libipf/getifname.c new file mode 100644 index 000000000000..518950acd1a2 --- /dev/null +++ b/sbin/ipf/libipf/getifname.c @@ -0,0 +1,54 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +#include "kmem.h" + +/* + * Given a pointer to an interface in the kernel, return a pointer to a + * string which is the interface name. + */ +char *getifname(ptr) + struct ifnet *ptr; +{ +#if SOLARIS +# include <sys/mutex.h> +# include <sys/condvar.h> +# include "../pfil/qif.h" + char *ifname; + qif_t qif; + + if ((void *)ptr == (void *)-1) + return "!"; + if (ptr == NULL) + return "-"; + + if (kmemcpy((char *)&qif, (u_long)ptr, sizeof(qif)) == -1) + return "X"; + ifname = strdup(qif.qf_name); + if ((ifname != NULL) && (*ifname == '\0')) { + free(ifname); + return "!"; + } + return ifname; +#else + struct ifnet netif; + + if ((void *)ptr == (void *)-1) + return "!"; + if (ptr == NULL) + return "-"; + + if (kmemcpy((char *)&netif, (u_long)ptr, sizeof(netif)) == -1) + return "X"; + return strdup(netif.if_xname); +#endif +} diff --git a/sbin/ipf/libipf/getnattype.c b/sbin/ipf/libipf/getnattype.c new file mode 100644 index 000000000000..81364738e94a --- /dev/null +++ b/sbin/ipf/libipf/getnattype.c @@ -0,0 +1,70 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) + */ +#include "ipf.h" +#include "kmem.h" + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + + +/* + * Get a nat filter type given its kernel address. + */ +char * +getnattype(nat) + nat_t *nat; +{ + static char unknownbuf[20]; + char *which; + + if (!nat) + return "???"; + + switch (nat->nat_redir) + { + case NAT_MAP : + which = "MAP"; + break; + case NAT_MAPBLK : + which = "MAP-BLOCK"; + break; + case NAT_REDIRECT : + which = "RDR"; + break; + case NAT_MAP|NAT_REWRITE : + which = "RWR-MAP"; + break; + case NAT_REDIRECT|NAT_REWRITE : + which = "RWR-RDR"; + break; + case NAT_BIMAP : + which = "BIMAP"; + break; + case NAT_REDIRECT|NAT_DIVERTUDP : + which = "DIV-RDR"; + break; + case NAT_MAP|NAT_DIVERTUDP : + which = "DIV-MAP"; + break; + case NAT_REDIRECT|NAT_ENCAP : + which = "ENC-RDR"; + break; + case NAT_MAP|NAT_ENCAP : + which = "ENC-MAP"; + break; + default : + snprintf(unknownbuf, sizeof(unknownbuf), "unknown(%04x)", + nat->nat_redir & 0xffffffff); + which = unknownbuf; + break; + } + return which; +} diff --git a/sbin/ipf/libipf/getport.c b/sbin/ipf/libipf/getport.c new file mode 100644 index 000000000000..0981ff172b96 --- /dev/null +++ b/sbin/ipf/libipf/getport.c @@ -0,0 +1,90 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" +#include <ctype.h> + +int getport(fr, name, port, proto) + frentry_t *fr; + char *name, *proto; + u_short *port; +{ + struct protoent *p; + struct servent *s; + u_short p1; + + if (fr == NULL || fr->fr_type != FR_T_IPF) { + s = getservbyname(name, proto); + if (s != NULL) { + *port = s->s_port; + return 0; + } + + if (ISDIGIT(*name)) { + int portval = atoi(name); + if (portval < 0 || portval > 65535) + return -1; + *port = htons((u_short)portval); + return 0; + } + return -1; + } + + /* + * Some people will use port names in rules without specifying + * either TCP or UDP because it is implied by the group head. + * If we don't know the protocol, then the best we can do here is + * to take either only the TCP or UDP mapping (if one or the other + * is missing) or make sure both of them agree. + */ + if (fr->fr_proto == 0) { + s = getservbyname(name, "tcp"); + if (s != NULL) + p1 = s->s_port; + else + p1 = 0; + s = getservbyname(name, "udp"); + if (s != NULL) { + if (p1 != s->s_port) + return -1; + } + if ((p1 == 0) && (s == NULL)) + return -1; + if (p1) + *port = p1; + else + *port = s->s_port; + return 0; + } + + if ((fr->fr_flx & FI_TCPUDP) != 0) { + /* + * If a rule is "tcp/udp" then check that both TCP and UDP + * mappings for this protocol name match ports. + */ + s = getservbyname(name, "tcp"); + if (s == NULL) + return -1; + p1 = s->s_port; + s = getservbyname(name, "udp"); + if (s == NULL || s->s_port != p1) + return -1; + *port = p1; + return 0; + } + + p = getprotobynumber(fr->fr_proto); + s = getservbyname(name, p ? p->p_name : NULL); + if (s != NULL) { + *port = s->s_port; + return 0; + } + return -1; +} diff --git a/sbin/ipf/libipf/getportproto.c b/sbin/ipf/libipf/getportproto.c new file mode 100644 index 000000000000..69fecff157fd --- /dev/null +++ b/sbin/ipf/libipf/getportproto.c @@ -0,0 +1,40 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <ctype.h> +#include "ipf.h" + +int getportproto(name, proto) + char *name; + int proto; +{ + struct servent *s; + struct protoent *p; + + if (ISDIGIT(*name)) { + int number; + char *s; + + for (s = name; *s != '\0'; s++) + if (!ISDIGIT(*s)) + return -1; + + number = atoi(name); + if (number < 0 || number > 65535) + return -1; + return htons(number); + } + + p = getprotobynumber(proto); + s = getservbyname(name, p ? p->p_name : NULL); + if (s != NULL) + return s->s_port; + return -1; +} diff --git a/sbin/ipf/libipf/getproto.c b/sbin/ipf/libipf/getproto.c new file mode 100644 index 000000000000..f57fe06358fb --- /dev/null +++ b/sbin/ipf/libipf/getproto.c @@ -0,0 +1,33 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" +#include <ctype.h> + +int getproto(name) + char *name; +{ + struct protoent *p; + char *s; + + for (s = name; *s != '\0'; s++) + if (!ISDIGIT(*s)) + break; + if (*s == '\0') + return atoi(name); + + if (!strcasecmp(name, "ip")) + return 0; + + p = getprotobyname(name); + if (p != NULL) + return p->p_proto; + return -1; +} diff --git a/sbin/ipf/libipf/getsumd.c b/sbin/ipf/libipf/getsumd.c new file mode 100644 index 000000000000..53869131e694 --- /dev/null +++ b/sbin/ipf/libipf/getsumd.c @@ -0,0 +1,23 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +char *getsumd(sum) + u_32_t sum; +{ + static char sumdbuf[17]; + + if (sum & NAT_HW_CKSUM) + snprintf(sumdbuf, sizeof(sumdbuf), "hw(%#0x)", sum & 0xffff); + else + snprintf(sumdbuf, sizeof(sumdbuf), "%#0x", sum); + return sumdbuf; +} diff --git a/sbin/ipf/libipf/hostname.c b/sbin/ipf/libipf/hostname.c new file mode 100644 index 000000000000..3b179954bbff --- /dev/null +++ b/sbin/ipf/libipf/hostname.c @@ -0,0 +1,59 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +char * +hostname(int family, void *ip) +{ + static char hostbuf[MAXHOSTNAMELEN+1]; + struct hostent *hp; + struct in_addr ipa; + struct netent *np; + + memset(&ipa, 0, sizeof(ipa)); /* XXX gcc */ + + if (family == AF_INET) { + ipa.s_addr = *(u_32_t *)ip; + if (ipa.s_addr == htonl(0xfedcba98)) + return ("test.host.dots"); + } + + if ((opts & OPT_NORESOLVE) == 0) { + if (family == AF_INET) { + hp = gethostbyaddr(ip, 4, AF_INET); + if (hp != NULL && hp->h_name != NULL && + *hp->h_name != '\0') { + strncpy(hostbuf, hp->h_name, sizeof(hostbuf)); + hostbuf[sizeof(hostbuf) - 1] = '\0'; + return (hostbuf); + } + + np = getnetbyaddr(ipa.s_addr, AF_INET); + if (np != NULL && np->n_name != NULL && + *np->n_name != '\0') { + strncpy(hostbuf, np->n_name, sizeof(hostbuf)); + hostbuf[sizeof(hostbuf) - 1] = '\0'; + return (hostbuf); + } + } + } + + if (family == AF_INET) { + return (inet_ntoa(ipa)); + } +#ifdef USE_INET6 + (void) inet_ntop(AF_INET6, ip, hostbuf, sizeof(hostbuf) - 1); + hostbuf[MAXHOSTNAMELEN] = '\0'; + return (hostbuf); +#else + return ("IPv6"); +#endif +} diff --git a/sbin/ipf/libipf/icmpcode.c b/sbin/ipf/libipf/icmpcode.c new file mode 100644 index 000000000000..e898ebfa39a5 --- /dev/null +++ b/sbin/ipf/libipf/icmpcode.c @@ -0,0 +1,24 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <ctype.h> + +#include "ipf.h" + +#ifndef MIN +# define MIN(a,b) ((a) > (b) ? (b) : (a)) +#endif + + +char *icmpcodes[MAX_ICMPCODE + 1] = { + "net-unr", "host-unr", "proto-unr", "port-unr", "needfrag", "srcfail", + "net-unk", "host-unk", "isolate", "net-prohib", "host-prohib", + "net-tos", "host-tos", "filter-prohib", "host-preced", "preced-cutoff", + NULL }; diff --git a/sbin/ipf/libipf/icmptypename.c b/sbin/ipf/libipf/icmptypename.c new file mode 100644 index 000000000000..d7eb3bd3ab73 --- /dev/null +++ b/sbin/ipf/libipf/icmptypename.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + +char *icmptypename(family, type) + int family, type; +{ + icmptype_t *i; + + if ((type < 0) || (type > 255)) + return NULL; + + for (i = icmptypelist; i->it_name != NULL; i++) { + if ((family == AF_INET) && (i->it_v4 == type)) + return i->it_name; +#ifdef USE_INET6 + if ((family == AF_INET6) && (i->it_v6 == type)) + return i->it_name; +#endif + } + + return NULL; +} diff --git a/sbin/ipf/libipf/icmptypes.c b/sbin/ipf/libipf/icmptypes.c new file mode 100644 index 000000000000..c1123ff5e04d --- /dev/null +++ b/sbin/ipf/libipf/icmptypes.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + +#ifndef USE_INET6 +# undef ICMP6_ECHO_REQUEST +# define ICMP6_ECHO_REQUEST 0 +# undef ICMP6_ECHO_REPLY +# define ICMP6_ECHO_REPLY 0 +# undef ICMP6_NI_QUERY +# define ICMP6_NI_QUERY 0 +# undef ICMP6_NI_REPLY +# define ICMP6_NI_REPLY 0 +# undef ICMP6_PARAM_PROB +# define ICMP6_PARAM_PROB 0 +# undef ND_ROUTER_ADVERT +# define ND_ROUTER_ADVERT 0 +# undef ND_ROUTER_SOLICIT +# define ND_ROUTER_SOLICIT 0 +# undef ICMP6_TIME_EXCEEDED +# define ICMP6_TIME_EXCEEDED 0 +# undef ICMP6_DST_UNREACH +# define ICMP6_DST_UNREACH 0 +# undef ICMP6_PACKET_TOO_BIG +# define ICMP6_PACKET_TOO_BIG 0 +# undef MLD_LISTENER_QUERY +# define MLD_LISTENER_QUERY 0 +# undef MLD_LISTENER_REPORT +# define MLD_LISTENER_REPORT 0 +# undef MLD_LISTENER_DONE +# define MLD_LISTENER_DONE 0 +# undef ICMP6_MEMBERSHIP_QUERY +# define ICMP6_MEMBERSHIP_QUERY 0 +# undef ICMP6_MEMBERSHIP_REPORT +# define ICMP6_MEMBERSHIP_REPORT 0 +# undef ICMP6_MEMBERSHIP_REDUCTION +# define ICMP6_MEMBERSHIP_REDUCTION 0 +# undef ND_NEIGHBOR_ADVERT +# define ND_NEIGHBOR_ADVERT 0 +# undef ND_NEIGHBOR_SOLICIT +# define ND_NEIGHBOR_SOLICIT 0 +# undef ICMP6_ROUTER_RENUMBERING +# define ICMP6_ROUTER_RENUMBERING 0 +# undef ICMP6_WRUREQUEST +# define ICMP6_WRUREQUEST 0 +# undef ICMP6_WRUREPLY +# define ICMP6_WRUREPLY 0 +# undef ICMP6_FQDN_QUERY +# define ICMP6_FQDN_QUERY 0 +# undef ICMP6_FQDN_REPLY +# define ICMP6_FQDN_REPLY 0 +#else +# if !defined(MLD_LISTENER_QUERY) +# define MLD_LISTENER_QUERY 130 +# endif +# if !defined(MLD_LISTENER_REPORT) +# define MLD_LISTENER_REPORT 131 +# endif +# if !defined(MLD_LISTENER_DONE) +# define MLD_LISTENER_DONE 132 +# endif +# if defined(MLD_LISTENER_REDUCTION) && !defined(MLD_LISTENER_DONE) +# define MLD_LISTENER_DONE MLD_LISTENER_REDUCTION +# endif +#endif + +icmptype_t icmptypelist[] = { + { "echo", ICMP_ECHO, ICMP6_ECHO_REQUEST }, + { "echorep", ICMP_ECHOREPLY, ICMP6_ECHO_REPLY }, + { "fqdnquery", -1, ICMP6_FQDN_QUERY }, + { "fqdnreply", -1, ICMP6_FQDN_REPLY }, + { "infoqry", -1, ICMP6_NI_QUERY }, + { "inforeq", ICMP_IREQ, ICMP6_NI_QUERY }, + { "inforep", ICMP_IREQREPLY, ICMP6_NI_REPLY }, + { "listendone", -1, MLD_LISTENER_DONE }, + { "listenqry", -1, MLD_LISTENER_QUERY }, + { "listenrep", -1, MLD_LISTENER_REPORT }, + { "maskrep", ICMP_MASKREPLY, -1 }, + { "maskreq", ICMP_MASKREQ, -1 }, + { "memberqry", -1, ICMP6_MEMBERSHIP_QUERY }, + { "memberred", -1, ICMP6_MEMBERSHIP_REDUCTION }, + { "memberreply",-1, ICMP6_MEMBERSHIP_REPORT }, + { "neighadvert", -1, ND_NEIGHBOR_ADVERT }, + { "neighborsol", -1, ND_NEIGHBOR_SOLICIT }, + { "neighborsolicit", -1, ND_NEIGHBOR_SOLICIT }, + { "paramprob", ICMP_PARAMPROB, ICMP6_PARAM_PROB }, + { "redir", ICMP_REDIRECT, ND_REDIRECT }, + { "renumber", -1, ICMP6_ROUTER_RENUMBERING }, + { "routerad", ICMP_ROUTERADVERT, ND_ROUTER_ADVERT }, + { "routeradvert",ICMP_ROUTERADVERT, ND_ROUTER_ADVERT }, + { "routersol", ICMP_ROUTERSOLICIT, ND_ROUTER_SOLICIT }, + { "routersolcit",ICMP_ROUTERSOLICIT, ND_ROUTER_SOLICIT }, + { "squench", ICMP_SOURCEQUENCH, -1 }, + { "timest", ICMP_TSTAMP, -1 }, + { "timestrep", ICMP_TSTAMPREPLY, -1 }, + { "timex", ICMP_TIMXCEED, ICMP6_TIME_EXCEEDED }, + { "toobig", -1, ICMP6_PACKET_TOO_BIG }, + { "unreach", ICMP_UNREACH, ICMP6_DST_UNREACH }, + { "whorep", -1, ICMP6_WRUREPLY }, + { "whoreq", -1, ICMP6_WRUREQUEST }, + { NULL, -1, -1 } +}; diff --git a/sbin/ipf/libipf/inet_addr.c b/sbin/ipf/libipf/inet_addr.c new file mode 100644 index 000000000000..2b192ae19eff --- /dev/null +++ b/sbin/ipf/libipf/inet_addr.c @@ -0,0 +1,204 @@ +/* $FreeBSD$ */ + +/* + * ++Copyright++ 1983, 1990, 1993 + * - + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#if !defined(lint) +static const char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93"; +static const char rcsid[] = "@(#)$Id: inet_addr.c,v 1.8.2.3 2004/12/09 19:41:20 darrenr Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> + +#ifndef __P +# define __P(x) x +#endif +int inet_aton(const char *, struct in_addr *); + +/* + * Because the ctype(3) posix definition, if used "safely" in code everywhere, + * would mean all normal code that walks through strings needed casts. Yuck. + */ +#define ISALNUM(x) isalnum((u_char)(x)) +#define ISALPHA(x) isalpha((u_char)(x)) +#define ISASCII(x) isascii((u_char)(x)) +#define ISDIGIT(x) isdigit((u_char)(x)) +#define ISPRINT(x) isprint((u_char)(x)) +#define ISSPACE(x) isspace((u_char)(x)) +#define ISUPPER(x) isupper((u_char)(x)) +#define ISXDIGIT(x) isxdigit((u_char)(x)) +#define ISLOWER(x) islower((u_char)(x)) + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(cp, addr) + register const char *cp; + struct in_addr *addr; +{ + register u_long val; + register int base, n; + register char c; + u_int parts[4]; + register u_int *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!ISDIGIT(c)) + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + for (;;) { + if (ISASCII(c) && ISDIGIT(c)) { + val = (val * base) + (c - '0'); + c = *++cp; + } else if (base == 16 && ISASCII(c) && ISXDIGIT(c)) { + val = (val << 4) | + (c + 10 - (ISLOWER(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!ISASCII(c) || !ISSPACE(c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return (1); +} + +/* these are compatibility routines, not needed on recent BSD releases */ + +/* + * Ascii internet address interpretation routine. + * The value returned is in network order. + */ +#if 0 +inet_addr(cp) + const char *cp; +{ + struct in_addr val; + + if (inet_aton(cp, &val)) + return (val.s_addr); + return (0xffffffff); +} +#endif diff --git a/sbin/ipf/libipf/initparse.c b/sbin/ipf/libipf/initparse.c new file mode 100644 index 000000000000..c85d6d3ed69d --- /dev/null +++ b/sbin/ipf/libipf/initparse.c @@ -0,0 +1,20 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + + +char thishost[MAXHOSTNAMELEN]; + + +void initparse(void) +{ + gethostname(thishost, sizeof(thishost)); + thishost[sizeof(thishost) - 1] = '\0'; +} diff --git a/sbin/ipf/libipf/interror.c b/sbin/ipf/libipf/interror.c new file mode 100644 index 000000000000..78ae4bf37849 --- /dev/null +++ b/sbin/ipf/libipf/interror.c @@ -0,0 +1,582 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: interror.c,v 1.9.2.12 2012/07/22 08:03:39 darren_r Exp $ + */ + +#include "ipf.h" +#include <fcntl.h> +#include <sys/ioctl.h> + +typedef struct { + int iee_number; + char *iee_text; +} ipf_error_entry_t; + +static ipf_error_entry_t *find_error(int); + +#define IPF_NUM_ERRORS 475 + +/* + * NO REUSE OF NUMBERS! + * + * IF YOU WANT TO ADD AN ERROR TO THIS TABLE, _ADD_ A NEW NUMBER. + * DO _NOT_ USE AN EMPTY NUMBER OR FILL IN A GAP. + */ +static ipf_error_entry_t ipf_errors[IPF_NUM_ERRORS] = { + { 1, "auth table locked/full" }, + { 2, "" }, + { 3, "copyinptr received bad address" }, + { 4, "copyoutptr received bad address" }, + { 5, "" }, + { 6, "cannot load a rule with FR_T_BUILTIN flag set" }, + { 7, "internal rule without FR_T_BUILDINT flag set" }, + { 8, "no data provided with filter rule" }, + { 9, "invalid ioctl for rule" }, + { 10, "rule protocol is not 4 or 6" }, + { 11, "cannot find rule function" }, + { 12, "cannot find rule group" }, + { 13, "group in/out does not match rule in/out" }, + { 14, "rule without in/out does not belong to a group" }, + { 15, "cannot determine where to append rule" }, + { 16, "malloc for rule data failed" }, + { 17, "copyin for rule data failed" }, + { 18, "" }, + { 19, "zero data size for BPF rule" }, + { 20, "BPF validation failed" }, + { 21, "incorrect data size for IPF rule" }, + { 22, "'keep state' rule included 'with oow'" }, + { 23, "bad interface index with dynamic source address" }, + { 24, "bad interface index with dynamic dest. address" }, + { 25, "match array verif failed for filter rule" }, + { 26, "bad filter rule type" }, + { 27, "rule not found for zero'stats" }, + { 28, "copyout failed for zero'ing stats" }, + { 29, "rule not found for removing" }, + { 30, "cannot remove internal rule" }, + { 31, "rule in use" }, + { 32, "rule already exists" }, + { 33, "no memory for another rule" }, + { 34, "could not find function" }, + { 35, "copyout failed for resolving function name -> addr" }, + { 36, "copyout failed for resolving function addr -> name" }, + { 37, "function name/addr resolving search failed" }, + { 38, "group map cannot find it's hash table" }, + { 39, "group map hash-table in/out do not match rule" }, + { 40, "bcopyout failed for SIOCIPFINTERROR" }, + { 41, "" }, + { 42, "ipfilter not enabled for NAT ioctl" }, + { 43, "ipfilter not enabled for state ioctl" }, + { 44, "ipfilter not enabled for auth ioctl" }, + { 45, "ipfilter not enbaled for sync ioctl" }, + { 46, "ipfilter not enabled for scan ioctl" }, + { 47, "ipfilter not enabled for lookup ioctl" }, + { 48, "unrecognised device minor number for ioctl" }, + { 49, "unrecognised object type for copying in ipfobj" }, + { 50, "mismatching object type for copying in ipfobj" }, + { 51, "object size too small for copying in ipfobj" }, + { 52, "object size mismatch for copying in ipfobj" }, + { 53, "compat object size too small for copying in ipfobj" }, + { 54, "compat object size mismatch for copying in ipfobj" }, + { 55, "error doing copyin of data for in ipfobj" }, + { 56, "unrecognised object type for size copy in ipfobj" }, + { 57, "object size too small for size copy in ipfobj" }, + { 58, "mismatching object type for size copy in ipfobj" }, + { 59, "object size mismatch for size copy in ipfobj" }, + { 60, "compat object size mismatch for size copy in ipfobj" }, + { 61, "error doing size copyin of data for in ipfobj" }, + { 62, "bad object type for size copy out ipfobj" }, + { 63, "mismatching object type for size copy out ipfobj" }, + { 64, "object size mismatch for size copy out ipfobj" }, + { 65, "compat object size wrong for size copy out ipfobj" }, + { 66, "error doing size copyout of data for out ipfobj" }, + { 67, "unrecognised object type for copying out ipfobj" }, + { 68, "mismatching object type for copying out ipfobj" }, + { 69, "object size too small for copying out ipfobj" }, + { 70, "object size mismatch for copying out ipfobj" }, + { 71, "compat object size too small for copying out ipfobj" }, + { 72, "compat object size mismatch for copying out ipfobj" }, + { 73, "error doing copyout of data for out ipfobj" }, + { 74, "attempt to add existing tunable name" }, + { 75, "cannot find tunable name to delete" }, + { 76, "internal data too big for next tunable" }, + { 77, "could not find tunable" }, + { 78, "tunable can only be changed when ipfilter disabled" }, + { 79, "new tunable value outside accepted range" }, + { 80, "ipftune called for unrecognised ioctl" }, + { 81, "" }, + { 82, "could not find token to delete" }, + { 83, "" }, + { 84, "attempt to get next rule when no more exist" }, + { 85, "value for iri_inout outside accepted range" }, + { 86, "value for iri_active outside accepted range" }, + { 87, "value for iri_nrules is 0" }, + { 88, "NULL pointer specified for where to copy rule to" }, + { 89, "copyout of rule failed" }, + { 90, "" }, + { 91, "could not get token for rule iteration" }, + { 92, "unrecognised generic iterator" }, + { 93, "could not find token for generic iterator" }, + { 94, "need write permissions to disable/enable ipfilter" }, + { 95, "error copying in enable/disable value" }, + { 96, "need write permissions to set ipf tunable" }, + { 97, "need write permissions to set ipf flags" }, + { 98, "error doing copyin of ipf flags" }, + { 99, "error doing copyout of ipf flags" }, + { 100, "need write permissions to add another rule" }, + { 101, "need write permissions to insert another rule" }, + { 102, "need write permissions to swap active rule set" }, + { 103, "error copying out current active rule set" }, + { 104, "need write permissions to zero ipf stats" }, + { 105, "need write permissions to flush ipf v4 rules" }, + { 106, "error copying out v4 flush results" }, + { 107, "error copying in v4 flush command" }, + { 108, "need write permissions to flush ipf v6 rules" }, + { 109, "error copying out v6 flush results" }, + { 110, "error copying in v6 flush command" }, + { 111, "error copying in new lock state for ipfilter" }, + { 112, "need write permissions to flush ipf logs" }, + { 113, "error copying out results of log flush" }, + { 114, "need write permissions to resync ipf" }, + { 115, "unrecognised ipf ioctl" }, + { 116, "error copying in match array" }, + { 117, "match array type is not IPFOBJ_IPFEXPR" }, + { 118, "bad size for match array" }, + { 119, "cannot allocate memory for match aray" }, + { 120, "error copying in match array" }, + { 121, "error verifying contents of match array" }, + { 122, "need write permissions to set ipf lock status" }, + { 123, "error copying in data for function resolution" }, + { 124, "error copying in ipfobj structure" }, + { 125, "error copying in ipfobj structure" }, + { 126, "error copying in ipfobj structure" }, + { 127, "error copying in ipfobj structure" }, + { 128, "no memory for filter rule comment" }, + { 129, "error copying in filter rule comment" }, + { 130, "error copying out filter rule comment" }, + { 131, "no memory for new rule alloc buffer" }, + { 132, "cannot find source lookup pool" }, + { 133, "unknown source address type" }, + { 134, "cannot find destination lookup pool" }, + { 135, "unknown destination address type" }, + { 136, "icmp head group name index incorrect" }, + { 137, "group head name index incorrect" }, + { 138, "group name index incorrect" }, + { 139, "to interface name index incorrect" }, + { 140, "dup-to interface name index incorrect" }, + { 141, "reply-to interface name index incorrect" }, + { 142, "could not initialise call now function" }, + { 143, "could not initialise call function" }, + { 144, "could not find destination list" }, + { 145, "auth rules cannot have dup/to/fastroute" }, + { 146, "incorrect size for object to copy out" }, + { 147, "object type out of bounds for kernel copyout" }, + { 148, "object size too small for kernel copyout" }, + { 149, "object size validation failed for kernel copyout" }, + { 150, "error copying data out for kernel copyout" }, + { 151, "version mismatch for kernel copyout" }, +/* -------------------------------------------------------------------------- */ + { 10001, "could not find token for auth iterator" }, + { 10002, "write permissions require to add/remove auth rule" }, + { 10003, "need write permissions to set auth lock" }, + { 10004, "error copying out results of auth flush" }, + { 10005, "unknown auth ioctl" }, + { 10006, "can only append or remove preauth rules" }, + { 10007, "NULL pointers passed in for preauth remove" }, + { 10008, "preauth rule not found to remove" }, + { 10009, "could not malloc memory for preauth entry" }, + { 10010, "unrecognised preauth rule ioctl command" }, + { 10011, "iterator data supplied with NULL pointer" }, + { 10012, "unknown auth iterator type" }, + { 10013, "iterator error copying out auth data" }, + { 10014, "sleep waiting for auth packet interrupted" }, + { 10015, "bad index supplied in auth reply" }, + { 10016, "error injecting outbound packet back into kernel" }, + { 10017, "error injecting inbound packet back into kernel" }, + { 10018, "could not attempt to inject packet back into kernel" }, + { 10019, "packet id does not match" }, +/* -------------------------------------------------------------------------- */ + { 20001, "invalid frag token data pointer supplied" }, + { 20002, "error copying out frag token data" }, + { 20003, "can only copy one fragment state entry at a time" }, +/* -------------------------------------------------------------------------- */ + { 30001, "incorrect object size to get hash table stats" }, + { 30002, "could not malloc memory for new hash table" }, + { 30003, "error coping in hash table structure" }, + { 30004, "hash table already exists" }, + { 30005, "mismach between new hash table and operation unit" }, + { 30006, "could not malloc memory for hash table base" }, + { 30007, "could not find hash table" }, + { 30008, "mismatch between hash table and operation unit" }, + { 30009, "could not find hash table for iterators next node" }, + { 30010, "unknown iterator tpe" }, + { 30011, "iterator error copying out hash table" }, + { 30012, "iterator error copying out hash table entry" }, + { 30013, "error copying out hash table statistics" }, + { 30014, "table node delete structure wrong size" }, + { 30015, "error copying in node to delete" }, + { 30016, "table to delete node from does not exist" }, + { 30017, "could not find table to remove node from" }, + { 30018, "table node add structure wrong size" }, + { 30019, "error copying in node to add" }, + { 30020, "could not find table to add node to" }, + { 30021, "node already exists in the table" }, + { 30022, "could not find node to delete in table" }, + { 30023, "uid mismatch on node to delete" }, + { 30024, "object size incorrect for hash table" }, + { 30025, "hash table size must be at least 1"}, + { 30026, "cannot allocate memory for hash table context" }, +/* -------------------------------------------------------------------------- */ + { 40001, "invalid minor device numebr for log read" }, + { 40002, "read size too small" }, + { 40003, "interrupted waiting for log data to read" }, + { 40004, "interrupted waiting for log data to read" }, + { 40005, "read size too large" }, + { 40006, "uiomove for read operation failed" }, +/* -------------------------------------------------------------------------- */ + { 50001, "unknown lookup ioctl" }, + { 50002, "error copying in object data for add node" }, + { 50003, "invalid unit for lookup add node" }, + { 50004, "incorrect size for adding a pool node" }, + { 50005, "error copying in pool node structure" }, + { 50006, "mismatch in pool node address/mask families" }, + { 50007, "could not find pool name" }, + { 50008, "node already exists in pool" }, + { 50009, "incorrect size for adding a hash node" }, + { 50010, "error copying in hash node structure" }, + { 50011, "could not find hash table name" }, + { 50012, "unrecognised object type for lookup add node" }, + { 50013, "invalid unit for lookup delete node" }, + { 50014, "incorrect size for deleting a pool node" }, + { 50015, "error copying in pool node structure" }, + { 50016, "could not find pool name" }, + { 50017, "could not find pool node" }, + { 50018, "incorrect size for removing a hash node" }, + { 50019, "error copying in hash node structure" }, + { 50020, "could not find hash table name" }, + { 50021, "unrecognised object type for lookup delete node" }, + { 50022, "error copying in add table data" }, + { 50023, "invalid unit for lookup add table" }, + { 50024, "pool name already exists" }, + { 50025, "hash table name already exists" }, + { 50026, "unrecognised object type for lookup add table" }, + { 50027, "error copying table data back out" }, + { 50028, "error copying in remove table data" }, + { 50029, "invalid unit for lookup remove table" }, + { 50030, "unrecognised object type for lookup remove table" }, + { 50031, "error copying in lookup stats structure" }, + { 50032, "invalid unit for lookup stats" }, + { 50033, "unrecognised object type for lookup stats" }, + { 50034, "error copying in flush lookup data" }, + { 50035, "invalid unit for lookup flush" }, + { 50036, "incorrect table type for lookup flush" }, + { 50037, "error copying out lookup flush results" }, + { 50038, "invalid unit for lookup iterator" }, + { 50039, "invalid unit for lookup iterator" }, + { 50040, "could not find token for lookup iterator" }, + { 50041, "unrecognised object type for lookup interator" }, + { 50042, "error copying in lookup delete node operation" }, +/* -------------------------------------------------------------------------- */ + { 60001, "insufficient privilege for NAT write operation" }, + { 60002, "need write permissions to flush NAT logs" }, + { 60003, "need write permissions to turn NAT logging on/off" }, + { 60004, "error copying out current NAT log setting" }, + { 60005, "error copying out bytes waiting to be read in NAT \ +log" }, + { 60006, "need write permissions to add NAT rule" }, + { 60007, "NAT rule already exists" }, + { 60008, "could not allocate memory for NAT rule" }, + { 60009, "need write permissions to remove NAT rule" }, + { 60010, "NAT rule could not be found" }, + { 60011, "could not find NAT entry for redirect lookup" }, + { 60012, "need write permissions to flush NAT table" }, + { 60013, "error copying in NAT flush command" }, + { 60014, "need write permissions to do matching NAT flush" }, + { 60015, "need write permissions to set NAT lock" }, + { 60016, "need write permissions to add entry to NAT table" }, + { 60017, "NAT not locked for size retrieval" }, + { 60018, "NAT not locked for fetching NAT table entry" }, + { 60019, "error copying in NAT token data for deletion" }, + { 60020, "unknown NAT ioctl" }, + { 60021, "" }, + { 60022, "resolving proxy name in NAT rule failed" }, + { 60023, "only reply age specified in NAT rule" }, + { 60024, "error doing copyin to determine NAT entry size" }, + { 60025, "error copying out NAT size of 0" }, + { 60026, "NAT entry not found" }, + { 60027, "error doing copyout of NAT entry size" }, + { 60028, "invalid data size for getting NAT entry" }, + { 60029, "could not malloc temporary space for NAT entry" }, + { 60030, "no NAT table entries present" }, + { 60031, "NAT entry to get next from not found" }, + { 60032, "not enough space for proxy structure" }, + { 60033, "not enough space for private proxy data" }, + { 60034, "NAT entry size is too large" }, + { 60035, "could not malloc memory for NAT entry sratch space" }, + { 60036, "" }, + { 60037, "could not malloc memory for NAT entry" }, + { 60038, "could not malloc memory for NAT entry rule" }, + { 60039, "could not resolve NAT entry rule's proxy" }, + { 60040, "cannot add outbound duplicate NAT entry" }, + { 60041, "cannot add inbound duplicate NAT entry" }, + { 60042, "cannot add NAT entry that is neither IN nor OUT" }, + { 60043, "could not malloc memory for NAT proxy data" }, + { 60044, "proxy data size too big" }, + { 60045, "could not malloc proxy private data for NAT entry" }, + { 60046, "could not malloc memory for new NAT filter rule" }, + { 60047, "could not find existing filter rule for NAT entry" }, + { 60048, "insertion into NAT table failed" }, + { 60049, "iterator error copying out hostmap data" }, + { 60050, "iterator error copying out NAT rule data" }, + { 60051, "iterator error copying out NAT entry data" }, + { 60052, "iterator data supplied with NULL pointer" }, + { 60053, "unknown NAT iterator type" }, + { 60054, "unknwon next address type" }, + { 60055, "iterator suppled with unknown type for get-next" }, + { 60056, "unknown lookup group for next address" }, + { 60057, "error copying out NAT log flush results" }, + { 60058, "bucket table type is incorrect" }, + { 60059, "error copying out NAT bucket table" }, + { 60060, "function not found for lookup" }, + { 60061, "address family not supported with SIOCSTPUT" }, + { 60062, "unknown timeout name" }, + { 60063, "cannot allocate new inbound NAT entry table" }, + { 60064, "cannot allocate new outbound NAT entry table" }, + { 60065, "cannot allocate new inbound NAT bucketlen table" }, + { 60066, "cannot allocate new outbound NAT bucketlen table" }, + { 60067, "cannot allocate new NAT rules table" }, + { 60068, "cannot allocate new NAT hostmap table" }, + { 60069, "new source lookup type is not dstlist" }, + { 60070, "cannot allocate NAT rule scratch space" }, + { 60071, "new destination lookup type is not dstlist" }, + { 60072, "function not found for lookup (ipv6)" }, + { 60073, "unknown lookup group for next address (ipv6)" }, + { 60074, "unknown next address type (ipv6)" }, + { 60075, "one object at a time must be copied" }, +/* -------------------------------------------------------------------------- */ + { 70001, "incorrect object size to get pool stats" }, + { 70002, "could not malloc memory for new pool node" }, + { 70003, "invalid address length for new pool node" }, + { 70004, "invalid mask length for new pool node" }, + { 70005, "error adding node to pool" }, + { 70006, "pool already exists" }, + { 70007, "could not malloc memory for new pool" }, + { 70008, "could not allocate radix tree for new pool" }, + { 70009, "could not find pool" }, + { 70010, "unknown pool name for iteration" }, + { 70011, "unknown pool iterator" }, + { 70012, "error copying out pool head" }, + { 70013, "error copying out pool node" }, + { 70014, "add node size incorrect" }, + { 70015, "error copying in pool node" }, + { 70016, "" }, + { 70017, "cannot find pool for node" }, + { 70018, "node entry already present in pool" }, + { 70019, "delete node size incorrect" }, + { 70020, "error copying in node to delete" }, + { 70021, "cannot find pool to delete node from" }, + { 70022, "cannot find node to delete in pool" }, + { 70023, "pool name already exists" }, + { 70024, "uid mismatch for node removal" }, + { 70025, "stats device unit is invalid" }, + { 70026, "error copying out statistics" }, + { 70027, "could not remove node from radix tree" }, + { 70028, "incorrect address length in pool node add" }, + { 70029, "incorrect mask length in pool node add" }, + { 70030, "incorrect address length in pool node remove" }, + { 70031, "incorrect mask length in pool node remove" }, + { 70032, "cannot allocate memory for pool context" }, + { 70033, "cannot allocate memory for radix tree context" }, + { 70034, "adding IPv6 node with incorrect address length" }, + { 70035, "IPv4 address not masked" }, + { 70036, "IPv6 address not masked" }, + { 70037, "removing IPv6 node with incorrect address length" }, +/* -------------------------------------------------------------------------- */ + { 80001, "could not find proxy" }, + { 80002, "proxy does not support control operations" }, + { 80003, "could not allocate data to hold proxy operation" }, + { 80004, "unknown proxy ioctl" }, + { 80005, "could not copyin proxy control structure" }, + { 80006, "DNS proxy could not find rule to delete" }, + { 80007, "DNS proxy found existing matching rule" }, + { 80008, "DNS proxy could not allocate memory for new rule" }, + { 80009, "DNS proxy unknown command request" }, +/* -------------------------------------------------------------------------- */ + { 90001, "could not malloc space for new scan structure" }, + { 90002, "scan tag already exists" }, + { 90003, "scan structure in use" }, + { 90004, "could not find matching scan tag for filter rule" }, + { 90005, "could not copyout scan statistics" }, +/* -------------------------------------------------------------------------- */ + { 100001, "cannot find matching state entry to remove" }, + { 100002, "error copying in v4 state flush command" }, + { 100003, "error copying out v4 state flush results" }, + { 100004, "error copying in v6 state flush command" }, + { 100005, "error copying out v6 state flush results" }, + { 100006, "" }, + { 100007, "" }, + { 100008, "need write permissions to flush state log" }, + { 100009, "erorr copyout results of flushing state log" }, + { 100010, "need write permissions to turn state logging on/off" }, + { 100011, "error copying in new state logging state" }, + { 100012, "error copying out current state logging state" }, + { 100013, "error copying out bytes waiting to be read in state \ +log" }, + { 100014, "need write permissions to set state lock" }, + { 100015, "need write permissions to add entry to state table" }, + { 100016, "state not locked for size retrieval" }, + { 100017, "error copying out hash table bucket lengths" }, + { 100018, "could not find token for state iterator" }, + { 100019, "error copying in state token data for deletion" }, + { 100020, "unknown state ioctl" }, + { 100021, "no state table entries present" }, + { 100022, "state entry to get next from not found" }, + { 100023, "could not malloc memory for state entry" }, + { 100024, "could not malloc memory for state entry rule" }, + { 100025, "could not copy back state entry to user space" }, + { 100026, "iterator data supplied with NULL pointer" }, + { 100027, "iterator supplied with 0 item count" }, + { 100028, "iterator type is incorrect" }, + { 100029, "invalid state token data pointer supplied" }, + { 100030, "error copying out next state entry" }, + { 100031, "unrecognised table request" }, + { 100032, "error copying out bucket length data" }, + { 100033, "could not find existing filter rule for state entry" }, + { 100034, "could not find timeout name" }, + { 100035, "could not allocate new state table" }, + { 100036, "could not allocate new state bucket length table" }, +/* -------------------------------------------------------------------------- */ + { 110001, "sync write header magic number is incorrect" }, + { 110002, "sync write header protocol is incorrect" }, + { 110003, "sync write header command is incorrect" }, + { 110004, "sync write header table number is incorrect" }, + { 110005, "data structure too small for sync write operation" }, + { 110006, "zero length data with sync write header" }, + { 110007, "insufficient data for sync write" }, + { 110008, "bad sync read size" }, + { 110009, "interrupted sync read (solaris)" }, + { 110010, "interrupted sync read (hpux)" }, + { 110011, "interrupted sync read (osf)" }, + { 110012, "interrupted sync read" }, + { 110013, "could not malloc memory for sync'd state" }, + { 110014, "could not malloc memory for sync-state list item" }, + { 110015, "sync update could not find state" }, + { 110016, "unrecognised sync state command" }, + { 110017, "could not malloc memory for new sync'd NAT entry" }, + { 110018, "could not malloc memory for sync-NAT list item" }, + { 110019, "sync update could not find NAT entry" }, + { 110020, "unrecognised sync NAT command" }, + { 110021, "ioctls are not handled with sync" }, +/* -------------------------------------------------------------------------- */ + { 120001, "null data pointer for iterator" }, + { 120002, "unit outside of acceptable range" }, + { 120003, "unknown iterator subtype" }, + { 120004, "cannot find dest. list for iteration" }, + { 120005, "error copying out destination iteration list" }, + { 120006, "error copying out destination iteration node" }, + { 120007, "wrong size for frdest_t structure" }, + { 120008, "cannot allocate memory for new destination node" }, + { 120009, "error copying in destination node to add" }, + { 120010, "could not find destination list to add node to" }, + { 120011, "error copying in destination node to remove" }, + { 120012, "could not find dest. list to remove node from" }, + { 120013, "destination list already exists" }, + { 120014, "could not allocate new destination table" }, + { 120015, "could not find destination list to remove" }, + { 120016, "destination list cannot be removed - it is busy" }, + { 120017, "error copying in names for destination" }, + { 120018, "destination name is too long/short" }, + { 120019, "unrecognised address family in destination" }, + { 120020, "" }, + { 120021, "error copying in new destination table" }, + { 120022, "cannot allocate memory for node table" }, + { 120023, "stats object size is incorrect for dest. lists" }, + { 120024, "stats device unit is invalid for dest. lists" }, + { 120025, "error copying out dest. list statistics" }, + { 120026, "cannot allocate memory for destination node" }, + { 120027, "error copying in destination node" }, + { 120028, "cannot allocate memory for destination context " }, +/* -------------------------------------------------------------------------- */ + { 130001, "ioctl denied by system security level" }, + { 130002, "ioctl operation on invalid minor device" }, + { 130003, "ioctl on device denied, ipfitler is disabled" }, + { 130004, "ioctl command not allowed when disabled" }, + { 130005, "ioctl denied due to insufficient authorisation" }, + { 130006, "cannot read while ipfilter is disabled" }, + { 130007, "read on minor device not supported" }, + { 130008, "cannot write while ipfilter is disabled" }, + { 130009, "write on minor device not supported" }, + { 130010, "poll on minor device is not supported" }, + { 130011, "error removing IPv4 filter hooks" }, + { 130012, "error removing IPv6 filter hooks" }, + { 130013, "attaching IPv4 hook failed" }, + { 130014, "attaching IPv6 hook failed" }, + { 130015, "ipf_init_all failed" }, + { 130016, "finding pfil head failed" }, + { 130017, "ipfilter is already initialised and running" }, +}; + + +static ipf_error_entry_t * +find_error(errnum) + int errnum; +{ + ipf_error_entry_t *ie; + + int l = -1, r = IPF_NUM_ERRORS + 1, step; + step = (r - l) / 2;; + + while (step != 0) { + ie = ipf_errors + l + step; + if (ie->iee_number == errnum) + return ie; + step = l + step; + if (ie->iee_number > errnum) + r = step; + else + l = step; + step = (r - l) / 2;; + } + + return NULL; +} + +char * +ipf_geterror(fd, func) + int fd; + ioctlfunc_t *func; +{ + static char text[80]; + ipf_error_entry_t *ie; + int errnum; + + if ((*func)(fd, SIOCIPFINTERROR, &errnum) == 0) { + + ie = find_error(errnum); + if (ie != NULL) + return ie->iee_text; + snprintf(text, sizeof(text), "unknown error %d", errnum); + } else { + snprintf(text, sizeof(text), "retrieving error number failed (%d)", errno); + } + return text; +} + + +char * +ipf_strerror(errnum) + int errnum; +{ + static char text[80]; + ipf_error_entry_t *ie; + + + ie = find_error(errnum); + if (ie != NULL) + return ie->iee_text; + + snprintf(text, sizeof(text), "unknown error %d", errnum); + return text; +} diff --git a/sbin/ipf/libipf/ionames.c b/sbin/ipf/libipf/ionames.c new file mode 100644 index 000000000000..9b586422a392 --- /dev/null +++ b/sbin/ipf/libipf/ionames.c @@ -0,0 +1,41 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + + +struct ipopt_names ionames[] ={ + { IPOPT_NOP, 0x000001, 1, "nop" }, /* RFC791 */ + { IPOPT_RR, 0x000002, 8, "rr" }, /* 1 route */ + { IPOPT_ZSU, 0x000004, 4, "zsu" }, /* size ?? */ + { IPOPT_MTUP, 0x000008, 4, "mtup" }, /* RFC1191 */ + { IPOPT_MTUR, 0x000010, 4, "mtur" }, /* RFC1191 */ + { IPOPT_ENCODE, 0x000020, 4, "encode" }, /* size ?? */ + { IPOPT_TS, 0x000040, 8, "ts" }, /* 1 TS */ + { IPOPT_TR, 0x000080, 4, "tr" }, /* RFC1393 */ + { IPOPT_SECURITY,0x000100, 12, "sec" }, /* RFC1108 */ + { IPOPT_SECURITY,0x000100, 12, "sec-class" }, /* RFC1108 */ + { IPOPT_LSRR, 0x000200, 8, "lsrr" }, /* 1 route */ + { IPOPT_E_SEC, 0x000400, 8, "e-sec" }, /* RFC1108 */ + { IPOPT_CIPSO, 0x000800, 8, "cipso" }, /* size ?? */ + { IPOPT_SATID, 0x001000, 4, "satid" }, /* RFC791 */ + { IPOPT_SSRR, 0x002000, 8, "ssrr" }, /* 1 route */ + { IPOPT_ADDEXT, 0x004000, 4, "addext" }, /* IPv7 ?? */ + { IPOPT_VISA, 0x008000, 4, "visa" }, /* size ?? */ + { IPOPT_IMITD, 0x010000, 4, "imitd" }, /* size ?? */ + { IPOPT_EIP, 0x020000, 4, "eip" }, /* RFC1385 */ + { IPOPT_FINN, 0x040000, 4, "finn" }, /* size ?? */ + { IPOPT_DPS, 0x080000, 4, "dps" }, /* size ?? */ + { IPOPT_SDB, 0x100000, 4, "sdb" }, /* size ?? */ + { IPOPT_NSAPA, 0x200000, 4, "nsapa" }, /* size ?? */ + { IPOPT_RTRALRT,0x400000, 4, "rtralrt" }, /* RFC2113 */ + { IPOPT_UMP, 0x800000, 4, "ump" }, /* size ?? */ + { IPOPT_AH, 0x1000000, 0, "ah" }, /* IPPROTO_AH */ + { 0, 0, 0, (char *)NULL } /* must be last */ +}; diff --git a/sbin/ipf/libipf/ipf_dotuning.c b/sbin/ipf/libipf/ipf_dotuning.c new file mode 100644 index 000000000000..b0ac8b42a2f8 --- /dev/null +++ b/sbin/ipf/libipf/ipf_dotuning.c @@ -0,0 +1,74 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" +#include "netinet/ipl.h" +#include <sys/ioctl.h> + +void ipf_dotuning(fd, tuneargs, iocfn) + int fd; + char *tuneargs; + ioctlfunc_t iocfn; +{ + ipfobj_t obj; + ipftune_t tu; + char *s, *t; + + bzero((char *)&tu, sizeof(tu)); + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_size = sizeof(tu);; + obj.ipfo_ptr = (void *)&tu; + obj.ipfo_type = IPFOBJ_TUNEABLE; + + for (s = strtok(tuneargs, ","); s != NULL; s = strtok(NULL, ",")) { + if (!strcmp(s, "list")) { + while (1) { + if ((*iocfn)(fd, SIOCIPFGETNEXT, &obj) == -1) { + ipf_perror_fd(fd, iocfn, + "ioctl(SIOCIPFGETNEXT)"); + break; + } + if (tu.ipft_cookie == NULL) + break; + + tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0'; + printtunable(&tu); + } + } else if ((t = strchr(s, '=')) != NULL) { + tu.ipft_cookie = NULL; + *t++ = '\0'; + strncpy(tu.ipft_name, s, sizeof(tu.ipft_name)); + if (sscanf(t, "%lu", &tu.ipft_vlong) == 1) { + if ((*iocfn)(fd, SIOCIPFSET, &obj) == -1) { + ipf_perror_fd(fd, iocfn, + "ioctl(SIOCIPFSET)"); + return; + } + } else { + fprintf(stderr, "invalid value '%s'\n", s); + return; + } + } else { + tu.ipft_cookie = NULL; + strncpy(tu.ipft_name, s, sizeof(tu.ipft_name)); + if ((*iocfn)(fd, SIOCIPFGET, &obj) == -1) { + ipf_perror_fd(fd, iocfn, "ioctl(SIOCIPFGET)"); + return; + } + if (tu.ipft_cookie == NULL) { + fprintf(stderr, "Null cookie for %s\n", s); + return; + } + + tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0'; + printtunable(&tu); + } + } +} diff --git a/sbin/ipf/libipf/ipf_perror.c b/sbin/ipf/libipf/ipf_perror.c new file mode 100644 index 000000000000..08e31648b053 --- /dev/null +++ b/sbin/ipf/libipf/ipf_perror.c @@ -0,0 +1,47 @@ +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" + +void +ipf_perror(err, string) + int err; + char *string; +{ + if (err == 0) + fprintf(stderr, "%s\n", string); + else + fprintf(stderr, "%s: %s\n", string, ipf_strerror(err)); +} + +int +ipf_perror_fd(fd, iocfunc, string) + int fd; + ioctlfunc_t iocfunc; + char *string; +{ + int save; + int realerr; + + save = errno; + if ((*iocfunc)(fd, SIOCIPFINTERROR, &realerr) == -1) + realerr = 0; + + errno = save; + fprintf(stderr, "%d:", realerr); + ipf_perror(realerr, string); + return realerr ? realerr : save; + +} + +void +ipferror(fd, msg) + int fd; + char *msg; +{ + if (fd >= 0) { + ipf_perror_fd(fd, ioctl, msg); + } else { + fprintf(stderr, "0:"); + perror(msg); + } +} diff --git a/sbin/ipf/libipf/ipft_hx.c b/sbin/ipf/libipf/ipft_hx.c new file mode 100644 index 000000000000..e424c3929c79 --- /dev/null +++ b/sbin/ipf/libipf/ipft_hx.c @@ -0,0 +1,183 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ipft_hx.c 1.1 3/9/96 (C) 1996 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +#include <ctype.h> + +#include "ipf.h" +#include "ipt.h" + + +extern int opts; + +static int hex_open(char *); +static int hex_close(void); +static int hex_readip(mb_t *, char **, int *); +static char *readhex(char *, char *); + +struct ipread iphex = { hex_open, hex_close, hex_readip, 0 }; +static FILE *tfp = NULL; +static int tfd = -1; + +static int hex_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 hex_close() +{ + int cfd = tfd; + + tfd = -1; + return close(cfd); +} + + +static int hex_readip(mb, ifn, dir) + mb_t *mb; + char **ifn; + int *dir; +{ + register char *s, *t, *u; + char line[513]; + ip_t *ip; + char *buf; + + buf = (char *)mb->mb_buf; + /* + * interpret start of line as possibly "[ifname]" or + * "[in/out,ifname]". + */ + if (ifn) + *ifn = NULL; + if (dir) + *dir = 0; + ip = (ip_t *)buf; + while (fgets(line, sizeof(line)-1, tfp)) { + if ((s = strchr(line, '\n'))) { + if (s == line) { + mb->mb_len = (char *)ip - buf; + return mb->mb_len; + } + *s = '\0'; + } + if ((s = strchr(line, '#'))) + *s = '\0'; + if (!*line) + continue; + if ((opts & OPT_DEBUG) != 0) { + printf("input: %s", line); + } + + if ((*line == '[') && (s = strchr(line, ']'))) { + t = line + 1; + if (s - t > 0) { + *s++ = '\0'; + if ((u = strchr(t, ',')) && (u < s)) { + u++; + if (ifn) + *ifn = strdup(u); + if (dir) { + if (*t == 'i') + *dir = 0; + else if (*t == 'o') + *dir = 1; + } + } else if (ifn) + *ifn = t; + } + + while (*s++ == '+') { + if (!strncasecmp(s, "mcast", 5)) { + mb->mb_flags |= M_MCAST; + s += 5; + } + if (!strncasecmp(s, "bcast", 5)) { + mb->mb_flags |= M_BCAST; + s += 5; + } + if (!strncasecmp(s, "mbcast", 6)) { + mb->mb_flags |= M_MBCAST; + s += 6; + } + } + while (ISSPACE(*s)) + s++; + } else + s = line; + t = (char *)ip; + ip = (ip_t *)readhex(s, (char *)ip); + if ((opts & OPT_DEBUG) != 0) { + if (opts & OPT_ASCII) { + int c = *t; + if (t < (char *)ip) + putchar('\t'); + while (t < (char *)ip) { + if (isprint(c) && isascii(c)) + putchar(c); + else + putchar('.'); + t++; + } + } + putchar('\n'); + fflush(stdout); + } + } + if (feof(tfp)) + return 0; + return -1; +} + + +static char *readhex(src, dst) +register char *src, *dst; +{ + int state = 0; + char c; + + while ((c = *src++)) { + if (ISSPACE(c)) { + if (state) { + dst++; + state = 0; + } + continue; + } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')) { + c = ISDIGIT(c) ? (c - '0') : (TOUPPER(c) - 55); + if (state == 0) { + *dst = (c << 4); + state++; + } else { + *dst++ |= c; + state = 0; + } + } else + break; + } + return dst; +} diff --git a/sbin/ipf/libipf/ipft_pc.c b/sbin/ipf/libipf/ipft_pc.c new file mode 100644 index 000000000000..65eee078bd06 --- /dev/null +++ b/sbin/ipf/libipf/ipft_pc.c @@ -0,0 +1,251 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" +#include "ipt.h" + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + +struct llc { + int lc_type; + 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[] = { + { 0, 0, 0, 0 }, /* DLT_NULL */ + { 1, 14, 12, 2 }, /* DLT_Ethernet */ + { 10, 0, 0, 0 }, /* DLT_FDDI */ + { 12, 0, 0, 0 }, /* DLT_RAW */ + { -1, -1, -1, -1 } +}; + +typedef struct { + u_int id; + u_short major; + u_short minor; + u_int timezone; + u_int sigfigs; + u_int snaplen; + u_int type; +} fileheader_t; + +typedef struct { + u_32_t seconds; + u_32_t microseconds; + u_32_t caplen; + u_32_t wirelen; +} packetheader_t; + +static int ipcap_open(char *); +static int ipcap_close(void); +static int ipcap_readip(mb_t *, char **, int *); +static int ipcap_read_rec(packetheader_t *); +static void iswap_hdr(fileheader_t *); + +static int pfd = -1, swapped = 0; +static struct llc *llcp = NULL; + +struct ipread pcap = { ipcap_open, ipcap_close, ipcap_readip, 0 }; + +#define SWAPLONG(y) \ + ((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff)) +#define SWAPSHORT(y) \ + ( (((y)&0xff)<<8) | (((y)&0xff00)>>8) ) + +static void iswap_hdr(p) + fileheader_t *p; +{ + p->major = SWAPSHORT(p->major); + p->minor = SWAPSHORT(p->minor); + p->timezone = SWAPLONG(p->timezone); + p->sigfigs = SWAPLONG(p->sigfigs); + p->snaplen = SWAPLONG(p->snaplen); + p->type = SWAPLONG(p->type); +} + +static int ipcap_open(fname) + char *fname; +{ + fileheader_t ph; + int fd, i; + + 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.id != 0xa1b2c3d4) { + if (SWAPLONG(ph.id) != 0xa1b2c3d4) { + (void) close(fd); + return -2; + } + swapped = 1; + iswap_hdr(&ph); + } + + for (i = 0; llcs[i].lc_type != -1; i++) + if (llcs[i].lc_type == ph.type) { + llcp = llcs + i; + break; + } + + if (llcp == NULL) { + (void) close(fd); + return -2; + } + + pfd = fd; + printf("opened pcap file %s:\n", fname); + printf("\tid: %08x version: %d.%d type: %d snap %d\n", + ph.id, ph.major, ph.minor, ph.type, ph.snaplen); + + return fd; +} + + +static int ipcap_close() +{ + return close(pfd); +} + + +/* + * read in the header (and validate) which should be the first record + * in a pcap file. + */ +static int ipcap_read_rec(rec) + packetheader_t *rec; +{ + int n, p, i; + + n = sizeof(*rec); + + while (n > 0) { + i = read(pfd, (char *)rec, sizeof(*rec)); + if (i <= 0) + return -2; + n -= i; + } + + if (swapped) { + rec->caplen = SWAPLONG(rec->caplen); + rec->wirelen = SWAPLONG(rec->wirelen); + rec->seconds = SWAPLONG(rec->seconds); + rec->microseconds = SWAPLONG(rec->microseconds); + } + p = rec->caplen; + n = MIN(p, rec->wirelen); + if (!n || n < 0) + return -3; + + if (p < 0 || p > 65536) + return -4; + return p; +} + + +#ifdef notyet +/* + * 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 ipcap_read(buf, cnt) + char *buf; + int cnt; +{ + packetheader_t rec; + static char *bufp = NULL; + int i, n; + + if ((i = ipcap_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; +} +#endif + + +/* + * return only an IP packet read into buf + */ +static int ipcap_readip(mb, ifn, dir) + mb_t *mb; + char **ifn; + int *dir; +{ + static char *bufp = NULL; + packetheader_t rec; + struct llc *l; + char *s, ty[4]; + int i, j, n; + char *buf; + int cnt; + +#if 0 + ifn = ifn; /* gcc -Wextra */ + dir = dir; /* gcc -Wextra */ +#endif + buf = (char *)mb->mb_buf; + cnt = sizeof(mb->mb_buf); + l = llcp; + + /* do { */ + if ((i = ipcap_read_rec(&rec)) <= 0) + return i; + + if (!bufp) + bufp = malloc(i); + else + bufp = realloc(bufp, i); + s = bufp; + + for (j = i, n = 0; j > 0; ) { + n = read(pfd, s, j); + if (n <= 0) + return -2; + j -= n; + s += n; + } + s = bufp; + + 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); + mb->mb_len = n; + return n; +} diff --git a/sbin/ipf/libipf/ipft_tx.c b/sbin/ipf/libipf/ipft_tx.c new file mode 100644 index 000000000000..3499afedf854 --- /dev/null +++ b/sbin/ipf/libipf/ipft_tx.c @@ -0,0 +1,508 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ipft_tx.c 1.7 6/5/96 (C) 1993 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + +#include <ctype.h> + +#include "ipf.h" +#include "ipt.h" + +extern int opts; + +static char *tx_proto = ""; + +static int text_open(char *), text_close(void); +static int text_readip(mb_t *, char **, int *); +static int parseline(char *, ip_t *, char **, int *); + +static char myflagset[] = "FSRPAUEC"; +static u_char myflags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, + TH_ACK, TH_URG, TH_ECN, TH_CWR }; + +struct ipread iptext = { text_open, text_close, text_readip, R_DO_CKSUM }; +static FILE *tfp = NULL; +static int tfd = -1; + +static u_32_t tx_hostnum(char *, int *); +static u_short tx_portnum(char *); + +#ifdef USE_INET6 +int parseipv6(char **, ip6_t *, char **, int *); +#endif + +/* + * returns an ip address as a long var as a result of either a DNS lookup or + * straight inet_addr() call + */ +static u_32_t tx_hostnum(host, resolved) + char *host; + int *resolved; +{ + i6addr_t ipa; + + *resolved = 0; + if (!strcasecmp("any", host)) + return 0L; + if (ISDIGIT(*host)) + return inet_addr(host); + + if (gethost(AF_INET, host, &ipa) == -1) { + *resolved = -1; + fprintf(stderr, "can't resolve hostname: %s\n", host); + return 0; + } + return ipa.in4.s_addr; +} + + +/* + * find the port number given by the name, either from getservbyname() or + * straight atoi() + */ +static u_short tx_portnum(name) + char *name; +{ + struct servent *sp; + + if (ISDIGIT(*name)) + return (u_short)atoi(name); + sp = getservbyname(name, tx_proto); + if (sp) + return ntohs(sp->s_port); + (void) fprintf(stderr, "unknown service \"%s\".\n", name); + return 0; +} + + +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(mb, ifn, dir) + mb_t *mb; + char **ifn; + int *dir; +{ + register char *s; + char line[513]; + ip_t *ip; + char *buf; + + buf = (char *)mb->mb_buf; + + *ifn = NULL; + while (fgets(line, sizeof(line)-1, tfp)) { + if ((s = strchr(line, '\n'))) + *s = '\0'; + if ((s = strchr(line, '\r'))) + *s = '\0'; + if ((s = strchr(line, '#'))) + *s = '\0'; + if (!*line) + continue; + if ((opts & OPT_DEBUG) != 0) + printf("input: %s\n", line); + *ifn = NULL; + *dir = 0; + if (!parseline(line, (ip_t *)buf, ifn, dir)) { + ip = (ip_t *)buf; + if (IP_V(ip) == 6) { +#ifdef USE_INET6 + mb->mb_len = ntohs(((ip6_t *)ip)->ip6_plen) + + sizeof(ip6_t); +#else + mb->mb_len = 0; +#endif + } else { + mb->mb_len = ntohs(ip->ip_len); + } + return mb->mb_len; + } + } + if (feof(tfp)) + return 0; + return -1; +} + +static int parseline(line, ip, ifn, out) + char *line; + ip_t *ip; + char **ifn; + int *out; +{ + tcphdr_t th, *tcp = &th; + struct icmp icmp, *ic = &icmp; + char *cps[20], **cpp, c, ipopts[68]; + int i, r; + + if (*ifn) + free(*ifn); + bzero((char *)ip, MAX(sizeof(*tcp), sizeof(*ic)) + sizeof(*ip)); + bzero((char *)tcp, sizeof(*tcp)); + bzero((char *)ic, sizeof(*ic)); + bzero(ipopts, sizeof(ipopts)); + IP_HL_A(ip, sizeof(*ip) >> 2); + IP_V_A(ip, IPVERSION); + ip->ip_ttl = 63; + for (i = 0, cps[0] = strtok(line, " \b\t\r\n"); cps[i] && i < 19; ) + cps[++i] = strtok(NULL, " \b\t\r\n"); + + cpp = cps; + if (!*cpp) + return 1; + + c = **cpp; + if (!ISALPHA(c) || (TOLOWER(c) != 'o' && TOLOWER(c) != 'i')) { + fprintf(stderr, "bad direction \"%s\"\n", *cpp); + return 1; + } + +#ifdef USE_INET6 + if (!strcasecmp(*cpp, "out6") || !strcasecmp(*cpp, "in6")) { + return parseipv6(cpp, (ip6_t *)ip, ifn, out); + } +#endif + + *out = (TOLOWER(c) == 'o') ? 1 : 0; + cpp++; + if (!*cpp) + return 1; + + if (!strcasecmp(*cpp, "on")) { + cpp++; + if (!*cpp) + return 1; + *ifn = strdup(*cpp++); + if (!*cpp) + return 1; + } + + c = **cpp; + ip->ip_len = sizeof(ip_t); + if (!strcasecmp(*cpp, "tcp") || !strcasecmp(*cpp, "udp") || + !strcasecmp(*cpp, "icmp")) { + if (c == 't') { + ip->ip_p = IPPROTO_TCP; + ip->ip_len += sizeof(struct tcphdr); + tx_proto = "tcp"; + } else if (c == 'u') { + ip->ip_p = IPPROTO_UDP; + ip->ip_len += sizeof(struct udphdr); + tx_proto = "udp"; + } else { + ip->ip_p = IPPROTO_ICMP; + ip->ip_len += ICMPERR_IPICMPHLEN; + tx_proto = "icmp"; + } + cpp++; + } else if (ISDIGIT(**cpp) && !index(*cpp, '.')) { + ip->ip_p = atoi(*cpp); + 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 = strchr(*cpp, ','); + if (!last) { + fprintf(stderr, "tcp/udp with no source port\n"); + return 1; + } + *last++ = '\0'; + tcp->th_sport = htons(tx_portnum(last)); + if (ip->ip_p == IPPROTO_TCP) { + tcp->th_win = htons(4096); + TCP_OFF_A(tcp, sizeof(*tcp) >> 2); + } + } + ip->ip_src.s_addr = tx_hostnum(*cpp, &r); + cpp++; + if (!*cpp) + return 1; + + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) { + char *last; + + last = strchr(*cpp, ','); + if (!last) { + fprintf(stderr, "tcp/udp with no destination port\n"); + return 1; + } + *last++ = '\0'; + tcp->th_dport = htons(tx_portnum(last)); + } + ip->ip_dst.s_addr = tx_hostnum(*cpp, &r); + cpp++; + if (ip->ip_p == IPPROTO_TCP) { + if (*cpp != NULL) { + char *s, *t; + + tcp->th_flags = 0; + for (s = *cpp; *s; s++) + if ((t = strchr(myflagset, *s))) + tcp->th_flags |= myflags[t-myflagset]; + if (tcp->th_flags) + cpp++; + } + + if (tcp->th_flags & TH_URG) + tcp->th_urp = htons(1); + + if (*cpp && !strncasecmp(*cpp, "seq=", 4)) { + tcp->th_seq = htonl(atoi(*cpp + 4)); + cpp++; + } + + if (*cpp && !strncasecmp(*cpp, "ack=", 4)) { + tcp->th_ack = htonl(atoi(*cpp + 4)); + cpp++; + } + } else if (*cpp && ip->ip_p == IPPROTO_ICMP) { + char *t; + + t = strchr(*cpp, ','); + if (t != NULL) + *t = '\0'; + + ic->icmp_type = geticmptype(AF_INET, *cpp); + if (t != NULL) + ic->icmp_code = atoi(t + 1); + cpp++; + + if (ic->icmp_type == ICMP_ECHO || + ic->icmp_type == ICMP_ECHOREPLY) + ic->icmp_id = htons(getpid()); + if (t != NULL) + *t = ','; + } + + if (*cpp && !strcasecmp(*cpp, "opt")) { + u_long olen; + + cpp++; + olen = buildopts(*cpp, ipopts, (IP_HL(ip) - 5) << 2); + if (olen) { + bcopy(ipopts, (char *)(ip + 1), olen); + IP_HL_A(ip, IP_HL(ip) + (olen >> 2)); + ip->ip_len += olen; + } + } + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + bcopy((char *)tcp, ((char *)ip) + (IP_HL(ip) << 2), + sizeof(*tcp)); + else if (ip->ip_p == IPPROTO_ICMP) + bcopy((char *)ic, ((char *)ip) + (IP_HL(ip) << 2), + sizeof(*ic)); + ip->ip_len = htons(ip->ip_len); + return 0; +} + + +#ifdef USE_INET6 +int parseipv6(cpp, ip6, ifn, out) + char **cpp; + ip6_t *ip6; + char **ifn; + int *out; +{ + tcphdr_t th, *tcp = &th; + struct icmp6_hdr icmp, *ic6 = &icmp; + + bzero((char *)ip6, MAX(sizeof(*tcp), sizeof(*ic6)) + sizeof(*ip6)); + bzero((char *)tcp, sizeof(*tcp)); + bzero((char *)ic6, sizeof(*ic6)); + ip6->ip6_vfc = 0x60; + + *out = (**cpp == 'o') ? 1 : 0; + cpp++; + if (!*cpp) + return 1; + + if (!strcasecmp(*cpp, "on")) { + cpp++; + if (!*cpp) + return 1; + *ifn = strdup(*cpp++); + if (!*cpp) + return 1; + } + + if (!strcasecmp(*cpp, "tcp")) { + ip6->ip6_nxt = IPPROTO_TCP; + tx_proto = "tcp"; + cpp++; + } else if (!strcasecmp(*cpp, "udp")) { + ip6->ip6_nxt = IPPROTO_UDP; + tx_proto = "udp"; + cpp++; + } else if (!strcasecmp(*cpp, "icmpv6")) { + ip6->ip6_nxt = IPPROTO_ICMPV6; + tx_proto = "icmpv6"; + cpp++; + } else if (ISDIGIT(**cpp) && !index(*cpp, ':')) { + ip6->ip6_nxt = atoi(*cpp); + cpp++; + } else + ip6->ip6_nxt = IPPROTO_IPV6; + + if (!*cpp) + return 1; + + switch (ip6->ip6_nxt) + { + case IPPROTO_TCP : + ip6->ip6_plen = sizeof(struct tcphdr); + break; + case IPPROTO_UDP : + ip6->ip6_plen = sizeof(struct udphdr); + break; + case IPPROTO_ICMPV6 : + ip6->ip6_plen = ICMP6ERR_IPICMPHLEN; + break; + default : + break; + } + + if (ip6->ip6_nxt == IPPROTO_TCP || ip6->ip6_nxt == IPPROTO_UDP) { + char *last; + + last = strchr(*cpp, ','); + if (!last) { + fprintf(stderr, "tcp/udp with no source port\n"); + return 1; + } + *last++ = '\0'; + tcp->th_sport = htons(tx_portnum(last)); + if (ip6->ip6_nxt == IPPROTO_TCP) { + tcp->th_win = htons(4096); + TCP_OFF_A(tcp, sizeof(*tcp) >> 2); + } + } + + if (inet_pton(AF_INET6, *cpp, &ip6->ip6_src) != 1) { + fprintf(stderr, "cannot parse source address '%s'\n", *cpp); + return 1; + } + + cpp++; + if (!*cpp) + return 1; + + if (ip6->ip6_nxt == IPPROTO_TCP || ip6->ip6_nxt == IPPROTO_UDP) { + char *last; + + last = strchr(*cpp, ','); + if (!last) { + fprintf(stderr, "tcp/udp with no destination port\n"); + return 1; + } + *last++ = '\0'; + tcp->th_dport = htons(tx_portnum(last)); + } + + if (inet_pton(AF_INET6, *cpp, &ip6->ip6_dst) != 1) { + fprintf(stderr, "cannot parse destination address '%s'\n", + *cpp); + return 1; + } + + cpp++; + if (ip6->ip6_nxt == IPPROTO_TCP) { + if (*cpp != NULL) { + char *s, *t; + + tcp->th_flags = 0; + for (s = *cpp; *s; s++) + if ((t = strchr(myflagset, *s))) + tcp->th_flags |= myflags[t-myflagset]; + if (tcp->th_flags) + cpp++; + } + + if (tcp->th_flags & TH_URG) + tcp->th_urp = htons(1); + + if (*cpp && !strncasecmp(*cpp, "seq=", 4)) { + tcp->th_seq = htonl(atoi(*cpp + 4)); + cpp++; + } + + if (*cpp && !strncasecmp(*cpp, "ack=", 4)) { + tcp->th_ack = htonl(atoi(*cpp + 4)); + cpp++; + } + } else if (*cpp && ip6->ip6_nxt == IPPROTO_ICMPV6) { + char *t; + + t = strchr(*cpp, ','); + if (t != NULL) + *t = '\0'; + + ic6->icmp6_type = geticmptype(AF_INET6, *cpp); + if (t != NULL) + ic6->icmp6_code = atoi(t + 1); + + if (ic6->icmp6_type == ICMP6_ECHO_REQUEST || + ic6->icmp6_type == ICMP6_ECHO_REPLY) + ic6->icmp6_id = htons(getpid()); + + if (t != NULL) + *t = ','; + } + + if (ip6->ip6_nxt == IPPROTO_TCP || ip6->ip6_nxt == IPPROTO_UDP) { + bcopy((char *)tcp, (char *)ip6 + sizeof(*ip6), + sizeof(*tcp)); + } else if (ip6->ip6_nxt == IPPROTO_ICMPV6) { + bcopy((char *)ic6, (char *)ip6 + sizeof(*ip6), + sizeof(*ic6)); + } + + /* + * Because a length of 0 == jumbo gram... + */ + if (ip6->ip6_plen == 0) { + ip6->ip6_plen++; + } + ip6->ip6_plen = htons(ip6->ip6_plen); + return 0; +} +#endif diff --git a/sbin/ipf/libipf/ipoptsec.c b/sbin/ipf/libipf/ipoptsec.c new file mode 100644 index 000000000000..5e585ba57ef8 --- /dev/null +++ b/sbin/ipf/libipf/ipoptsec.c @@ -0,0 +1,61 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +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, 0x80, 0, "reserv-1" }, + { 0, 0, 0, NULL } /* must be last */ +}; + + +u_char seclevel(slevel) + char *slevel; +{ + struct ipopt_names *so; + + if (slevel == NULL || *slevel == '\0') + return 0; + + 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_char secbit(class) + int class; +{ + struct ipopt_names *so; + + for (so = secclass; so->on_name; so++) + if (so->on_value == class) + break; + + if (!so->on_name) { + fprintf(stderr, "no such security class: %d.\n", class); + return 0; + } + return (u_char)so->on_bit; +} diff --git a/sbin/ipf/libipf/kmem.c b/sbin/ipf/libipf/kmem.c new file mode 100644 index 000000000000..26252a02f0bf --- /dev/null +++ b/sbin/ipf/libipf/kmem.c @@ -0,0 +1,118 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +/* + * kmemcpy() - copies n bytes from kernel memory into user buffer. + * returns 0 on success, -1 on error. + */ + +#include <stdio.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/file.h> +#include <kvm.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <net/if.h> + +#include "kmem.h" + +#if !defined(lint) +static const char sccsid[] = "@(#)kmem.c 1.4 1/12/96 (C) 1992 Darren Reed"; +static const char rcsid[] = "@(#)$Id$"; +#endif + + + +static kvm_t *kvm_f = NULL; + + +int openkmem(kern, core) + char *kern, *core; +{ + kvm_f = kvm_open(kern, core, NULL, O_RDONLY, NULL); + if (kvm_f == NULL) + { + perror("openkmem:open"); + return -1; + } + return kvm_f != NULL; +} + +int kmemcpy(buf, pos, n) + register char *buf; + long pos; + register int n; +{ + register int r; + + if (!n) + return 0; + + if (kvm_f == NULL) + if (openkmem(NULL, NULL) == -1) + return -1; + + while ((r = kvm_read(kvm_f, pos, buf, n)) < n) + if (r <= 0) + { + fprintf(stderr, "pos=0x%lx ", (u_long)pos); + perror("kmemcpy:read"); + return -1; + } + else + { + buf += r; + pos += r; + n -= r; + } + return 0; +} + +int kstrncpy(buf, pos, n) + register char *buf; + long pos; + register int n; +{ + register int r; + + if (!n) + return 0; + + if (kvm_f == NULL) + if (openkmem(NULL, NULL) == -1) + return -1; + + while (n > 0) + { + r = kvm_read(kvm_f, pos, buf, 1); + if (r <= 0) + { + fprintf(stderr, "pos=0x%lx ", (u_long)pos); + perror("kmemcpy:read"); + return -1; + } + else + { + if (*buf == '\0') + break; + buf++; + pos++; + n--; + } + } + return 0; +} diff --git a/sbin/ipf/libipf/kmem.h b/sbin/ipf/libipf/kmem.h new file mode 100644 index 000000000000..bcf6a0be7e27 --- /dev/null +++ b/sbin/ipf/libipf/kmem.h @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * $Id$ + */ + +#ifndef __KMEM_H__ +#define __KMEM_H__ + +#ifndef __P +# define __P(x) x +#endif +extern int openkmem(char *, char *); +extern int kmemcpy(char *, long, int); +extern int kstrncpy(char *, long, int); + +#if defined(__NetBSD__) +# include <paths.h> +#endif + +#ifdef _PATH_KMEM +# define KMEM _PATH_KMEM +#else +# define KMEM "/dev/kmem" +#endif + +#endif /* __KMEM_H__ */ diff --git a/sbin/ipf/libipf/kmemcpywrap.c b/sbin/ipf/libipf/kmemcpywrap.c new file mode 100644 index 000000000000..6c398d6d39f3 --- /dev/null +++ b/sbin/ipf/libipf/kmemcpywrap.c @@ -0,0 +1,23 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" +#include "kmem.h" + +int kmemcpywrap(from, to, size) + void *from, *to; + size_t size; +{ + int ret; + + ret = kmemcpy((caddr_t)to, (u_long)from, size); + return ret; +} + diff --git a/sbin/ipf/libipf/kvatoname.c b/sbin/ipf/libipf/kvatoname.c new file mode 100644 index 000000000000..65b524082df4 --- /dev/null +++ b/sbin/ipf/libipf/kvatoname.c @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +#include <fcntl.h> +#include <sys/ioctl.h> + +char *kvatoname(func, iocfunc) + ipfunc_t func; + ioctlfunc_t iocfunc; +{ + static char funcname[40]; + ipfunc_resolve_t res; + int fd; + + res.ipfu_addr = func; + res.ipfu_name[0] = '\0'; + fd = -1; + + if ((opts & OPT_DONTOPEN) == 0) { + fd = open(IPL_NAME, O_RDONLY); + if (fd == -1) + return NULL; + } + (void) (*iocfunc)(fd, SIOCFUNCL, &res); + if (fd >= 0) + close(fd); + strncpy(funcname, res.ipfu_name, sizeof(funcname)); + funcname[sizeof(funcname) - 1] = '\0'; + return funcname; +} diff --git a/sbin/ipf/libipf/load_dstlist.c b/sbin/ipf/libipf/load_dstlist.c new file mode 100644 index 000000000000..760699dafeae --- /dev/null +++ b/sbin/ipf/libipf/load_dstlist.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: load_dstlist.c,v 1.1.2.5 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" + + +int +load_dstlist(dst, iocfunc, nodes) + ippool_dst_t *dst; + ioctlfunc_t iocfunc; + ipf_dstnode_t *nodes; +{ + iplookupop_t op; + ipf_dstnode_t *a; + ippool_dst_t dest; + + if (dst->ipld_name[0] == '\0') + return -1; + + if (pool_open() == -1) + return -1; + + op.iplo_unit = dst->ipld_unit; + op.iplo_type = IPLT_DSTLIST; + op.iplo_arg = 0; + strncpy(op.iplo_name, dst->ipld_name, sizeof(op.iplo_name)); + op.iplo_size = sizeof(dest); + op.iplo_struct = &dest; + bzero((char *)&dest, sizeof(dest)); + dest.ipld_unit = dst->ipld_unit; + dest.ipld_policy = dst->ipld_policy; + dest.ipld_flags = dst->ipld_flags; + strncpy(dest.ipld_name, dst->ipld_name, sizeof(dest.ipld_name)); + + if ((opts & OPT_REMOVE) == 0) { + if (pool_ioctl(iocfunc, SIOCLOOKUPADDTABLE, &op)) + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "add destination list table"); + } + } + + if ((opts & OPT_VERBOSE) != 0) { + dest.ipld_dests = dst->ipld_dests; + printdstlist(&dest, bcopywrap, dest.ipld_name, opts, nodes, NULL); + dest.ipld_dests = NULL; + } + + for (a = nodes; a != NULL; a = a->ipfd_next) + load_dstlistnode(dst->ipld_unit, dest.ipld_name, a, iocfunc); + + if ((opts & OPT_REMOVE) != 0) { + if (pool_ioctl(iocfunc, SIOCLOOKUPDELTABLE, &op)) + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "delete destination list table"); + } + } + return 0; +} diff --git a/sbin/ipf/libipf/load_dstlistnode.c b/sbin/ipf/libipf/load_dstlistnode.c new file mode 100644 index 000000000000..d8160ebaea9c --- /dev/null +++ b/sbin/ipf/libipf/load_dstlistnode.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: load_dstlistnode.c,v 1.1.2.5 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_pool.h" + + +int +load_dstlistnode(role, name, node, iocfunc) + int role; + char *name; + ipf_dstnode_t *node; + ioctlfunc_t iocfunc; +{ + iplookupop_t op; + frdest_t *dst; + char *what; + int err; + + if (pool_open() == -1) + return -1; + + dst = calloc(1, sizeof(*dst) + node->ipfd_dest.fd_name); + if (dst == NULL) + return -1; + + op.iplo_unit = role; + op.iplo_type = IPLT_DSTLIST; + op.iplo_arg = 0; + op.iplo_struct = dst; + op.iplo_size = sizeof(*dst); + if (node->ipfd_dest.fd_name >= 0) + op.iplo_size += node->ipfd_dest.fd_name; + (void) strncpy(op.iplo_name, name, sizeof(op.iplo_name)); + + dst->fd_addr = node->ipfd_dest.fd_addr; + dst->fd_type = node->ipfd_dest.fd_type; + dst->fd_name = node->ipfd_dest.fd_name; + if (node->ipfd_dest.fd_name >= 0) + bcopy(node->ipfd_names, (char *)dst + sizeof(*dst), + node->ipfd_dest.fd_name); + + if ((opts & OPT_REMOVE) == 0) { + what = "add"; + err = pool_ioctl(iocfunc, SIOCLOOKUPADDNODE, &op); + } else { + what = "delete"; + err = pool_ioctl(iocfunc, SIOCLOOKUPDELNODE, &op); + } + free(dst); + + if (err != 0) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + (void) snprintf(msg, sizeof(msg), "%s lookup node", what); + return ipf_perror_fd(pool_fd(), iocfunc, msg); + } + } + + return 0; +} diff --git a/sbin/ipf/libipf/load_file.c b/sbin/ipf/libipf/load_file.c new file mode 100644 index 000000000000..a1d1f70b5c33 --- /dev/null +++ b/sbin/ipf/libipf/load_file.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: load_file.c,v 1.6.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" +#include <ctype.h> + +alist_t * +load_file(char *filename) +{ + alist_t *a, *rtop, *rbot; + char *s, line[1024], *t; + int linenum, not; + FILE *fp; + + fp = fopen(filename + 7, "r"); + if (fp == NULL) { + fprintf(stderr, "load_file cannot open '%s'\n", filename); + return NULL; + } + + a = NULL; + rtop = NULL; + rbot = NULL; + linenum = 0; + + while (fgets(line, sizeof(line) - 1, fp)) { + line[sizeof(line) - 1] = '\0'; + linenum++; + /* + * Hunt for CR/LF. If no LF, stop processing. + */ + s = strchr(line, '\n'); + if (s == NULL) { + fprintf(stderr, "%d:%s: line too long\n", + linenum, filename); + fclose(fp); + alist_free(rtop); + return NULL; + } + + /* + * Remove trailing spaces + */ + for (; ISSPACE(*s); s--) + *s = '\0'; + + s = strchr(line, '\r'); + if (s != NULL) + *s = '\0'; + for (t = line; ISSPACE(*t); t++) + ; + if (*t == '!') { + not = 1; + t++; + } else + not = 0; + + /* + * Remove comment markers + */ + s = strchr(t, '#'); + if (s != NULL) { + *s = '\0'; + if (s == t) + continue; + } + + /* + * Trim off tailing white spaces + */ + s = strlen(t) + t - 1; + while (ISSPACE(*s)) + *s-- = '\0'; + + a = alist_new(AF_UNSPEC, t); + if (a != NULL) { + a->al_not = not; + if (rbot != NULL) + rbot->al_next = a; + else + rtop = a; + rbot = a; + } else { + fprintf(stderr, "%s:%d unrecognised content :%s\n", + filename, linenum, t); + } + } + fclose(fp); + + return rtop; +} diff --git a/sbin/ipf/libipf/load_hash.c b/sbin/ipf/libipf/load_hash.c new file mode 100644 index 000000000000..7ec79a91258f --- /dev/null +++ b/sbin/ipf/libipf/load_hash.c @@ -0,0 +1,103 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_htable.h" + + +int +load_hash(iphp, list, iocfunc) + iphtable_t *iphp; + iphtent_t *list; + ioctlfunc_t iocfunc; +{ + iplookupop_t op; + iphtable_t iph; + iphtent_t *a; + size_t size; + int n; + + if (pool_open() == -1) + return -1; + + for (n = 0, a = list; a != NULL; a = a->ipe_next) + n++; + + bzero((char *)&iph, sizeof(iph)); + op.iplo_arg = 0; + op.iplo_type = IPLT_HASH; + op.iplo_unit = iphp->iph_unit; + strncpy(op.iplo_name, iphp->iph_name, sizeof(op.iplo_name)); + if (*op.iplo_name == '\0') + op.iplo_arg = IPHASH_ANON; + op.iplo_size = sizeof(iph); + op.iplo_struct = &iph; + iph = *iphp; + if (n <= 0) + n = 1; + if (iphp->iph_size == 0) + size = n * 2 - 1; + else + size = iphp->iph_size; + if ((list == NULL) && (size == 1)) { + fprintf(stderr, + "WARNING: empty hash table %s, recommend setting %s\n", + iphp->iph_name, "size to match expected use"); + } + iph.iph_size = size; + iph.iph_table = NULL; + iph.iph_list = NULL; + iph.iph_ref = 0; + + if ((opts & OPT_REMOVE) == 0) { + if (pool_ioctl(iocfunc, SIOCLOOKUPADDTABLE, &op)) + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "add lookup hash table"); + } + } + + strncpy(iph.iph_name, op.iplo_name, sizeof(op.iplo_name)); + strncpy(iphp->iph_name, op.iplo_name, sizeof(op.iplo_name)); + + if (opts & OPT_VERBOSE) { + iph.iph_table = calloc(size, sizeof(*iph.iph_table)); + if (iph.iph_table == NULL) { + perror("calloc(size, sizeof(*iph.iph_table))"); + return -1; + } + iph.iph_list = list; + printhash(&iph, bcopywrap, iph.iph_name, opts, NULL); + free(iph.iph_table); + + for (a = list; a != NULL; a = a->ipe_next) { + a->ipe_addr.in4_addr = htonl(a->ipe_addr.in4_addr); + a->ipe_mask.in4_addr = htonl(a->ipe_mask.in4_addr); + } + } + + if (opts & OPT_DEBUG) + printf("Hash %s:\n", iph.iph_name); + + for (a = list; a != NULL; a = a->ipe_next) + load_hashnode(iphp->iph_unit, iph.iph_name, a, 0, iocfunc); + + if ((opts & OPT_REMOVE) != 0) { + if (pool_ioctl(iocfunc, SIOCLOOKUPDELTABLE, &op)) + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "delete lookup hash table"); + } + } + return 0; +} diff --git a/sbin/ipf/libipf/load_hashnode.c b/sbin/ipf/libipf/load_hashnode.c new file mode 100644 index 000000000000..203d75484ec3 --- /dev/null +++ b/sbin/ipf/libipf/load_hashnode.c @@ -0,0 +1,67 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_htable.h" + + +int +load_hashnode(unit, name, node, ttl, iocfunc) + int unit; + char *name; + iphtent_t *node; + int ttl; + ioctlfunc_t iocfunc; +{ + iplookupop_t op; + iphtent_t ipe; + char *what; + int err; + + if (pool_open() == -1) + return -1; + + op.iplo_type = IPLT_HASH; + op.iplo_unit = unit; + op.iplo_arg = 0; + op.iplo_size = sizeof(ipe); + op.iplo_struct = &ipe; + strncpy(op.iplo_name, name, sizeof(op.iplo_name)); + + bzero((char *)&ipe, sizeof(ipe)); + ipe.ipe_family = node->ipe_family; + ipe.ipe_die = ttl; + bcopy((char *)&node->ipe_addr, (char *)&ipe.ipe_addr, + sizeof(ipe.ipe_addr)); + bcopy((char *)&node->ipe_mask, (char *)&ipe.ipe_mask, + sizeof(ipe.ipe_mask)); + bcopy((char *)&node->ipe_group, (char *)&ipe.ipe_group, + sizeof(ipe.ipe_group)); + + if ((opts & OPT_REMOVE) == 0) { + what = "add"; + err = pool_ioctl(iocfunc, SIOCLOOKUPADDNODE, &op); + } else { + what = "delete"; + err = pool_ioctl(iocfunc, SIOCLOOKUPDELNODE, &op); + } + + if (err != 0) + if (!(opts & OPT_DONOTHING)) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%s node from lookup hash table", what); + return ipf_perror_fd(pool_fd(), iocfunc, msg); + } + return 0; +} diff --git a/sbin/ipf/libipf/load_http.c b/sbin/ipf/libipf/load_http.c new file mode 100644 index 000000000000..88fc1e37cb53 --- /dev/null +++ b/sbin/ipf/libipf/load_http.c @@ -0,0 +1,208 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: load_http.c,v 1.5.2.5 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" +#include <ctype.h> + +/* + * Because the URL can be included twice into the buffer, once as the + * full path for the "GET" and once as the "Host:", the buffer it is + * put in needs to be larger than 512*2 to make room for the supporting + * text. Why not just use snprintf and truncate? The warning about the + * URL being too long tells you something is wrong and does not fetch + * any data - just truncating the URL (with snprintf, etc) and sending + * that to the server is allowing an unknown and unintentioned action + * to happen. + */ +#define MAX_URL_LEN 512 +#define LOAD_BUFSIZE (MAX_URL_LEN * 2 + 128) + +/* + * Format expected is one addres per line, at the start of each line. + */ +alist_t * +load_http(char *url) +{ + int fd, len, left, port, endhdr, removed, linenum = 0; + char *s, *t, *u, buffer[LOAD_BUFSIZE], *myurl; + alist_t *a, *rtop, *rbot; + size_t avail; + int error; + + /* + * More than this would just be absurd. + */ + if (strlen(url) > MAX_URL_LEN) { + fprintf(stderr, "load_http has a URL > %d bytes?!\n", + MAX_URL_LEN); + return NULL; + } + + fd = -1; + rtop = NULL; + rbot = NULL; + + avail = sizeof(buffer); + error = snprintf(buffer, avail, "GET %s HTTP/1.0\r\n", url); + + /* + * error is always less then avail due to the constraint on + * the url length above. + */ + avail -= error; + + myurl = strdup(url); + if (myurl == NULL) + goto done; + + s = myurl + 7; /* http:// */ + t = strchr(s, '/'); + if (t == NULL) { + fprintf(stderr, "load_http has a malformed URL '%s'\n", url); + free(myurl); + return NULL; + } + *t++ = '\0'; + + /* + * 10 is the length of 'Host: \r\n\r\n' below. + */ + if (strlen(s) + strlen(buffer) + 10 > sizeof(buffer)) { + fprintf(stderr, "load_http has a malformed URL '%s'\n", url); + free(myurl); + return NULL; + } + + u = strchr(s, '@'); + if (u != NULL) + s = u + 1; /* AUTH */ + + error = snprintf(buffer + strlen(buffer), avail, "Host: %s\r\n\r\n", s); + if (error >= avail) { + fprintf(stderr, "URL is too large: %s\n", url); + goto done; + } + + u = strchr(s, ':'); + if (u != NULL) { + *u++ = '\0'; + port = atoi(u); + if (port < 0 || port > 65535) + goto done; + } else { + port = 80; + } + + + fd = connecttcp(s, port); + if (fd == -1) + goto done; + + + len = strlen(buffer); + if (write(fd, buffer, len) != len) + goto done; + + s = buffer; + endhdr = 0; + left = sizeof(buffer) - 1; + + while ((len = read(fd, s, left)) > 0) { + s[len] = '\0'; + left -= len; + s += len; + + if (endhdr >= 0) { + if (endhdr == 0) { + t = strchr(buffer, ' '); + if (t == NULL) + continue; + t++; + if (*t != '2') + break; + } + + u = buffer; + while ((t = strchr(u, '\r')) != NULL) { + if (t == u) { + if (*(t + 1) == '\n') { + u = t + 2; + endhdr = -1; + break; + } else + t++; + } else if (*(t + 1) == '\n') { + endhdr++; + u = t + 2; + } else + u = t + 1; + } + if (endhdr >= 0) + continue; + removed = (u - buffer) + 1; + memmove(buffer, u, (sizeof(buffer) - left) - removed); + s -= removed; + left += removed; + } + + do { + t = strchr(buffer, '\n'); + if (t == NULL) + break; + + linenum++; + *t = '\0'; + + /* + * Remove comment and continue to the next line if + * the comment is at the start of the line. + */ + u = strchr(buffer, '#'); + if (u != NULL) { + *u = '\0'; + if (u == buffer) + continue; + } + + /* + * Trim off tailing white spaces, will include \r + */ + for (u = t - 1; (u >= buffer) && ISSPACE(*u); u--) + *u = '\0'; + + a = alist_new(AF_UNSPEC, buffer); + if (a != NULL) { + if (rbot != NULL) + rbot->al_next = a; + else + rtop = a; + rbot = a; + } else { + fprintf(stderr, + "%s:%d unrecognised content:%s\n", + url, linenum, buffer); + } + + t++; + removed = t - buffer; + memmove(buffer, t, sizeof(buffer) - left - removed); + s -= removed; + left += removed; + + } while (1); + } + +done: + if (myurl != NULL) + free(myurl); + if (fd != -1) + close(fd); + return rtop; +} diff --git a/sbin/ipf/libipf/load_pool.c b/sbin/ipf/libipf/load_pool.c new file mode 100644 index 000000000000..190a2dff2520 --- /dev/null +++ b/sbin/ipf/libipf/load_pool.c @@ -0,0 +1,72 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_pool.h" + + +int +load_pool(plp, iocfunc) + ip_pool_t *plp; + ioctlfunc_t iocfunc; +{ + iplookupop_t op; + ip_pool_node_t *a; + ip_pool_t pool; + + if (pool_open() == -1) + return -1; + + op.iplo_unit = plp->ipo_unit; + op.iplo_type = IPLT_POOL; + op.iplo_arg = 0; + strncpy(op.iplo_name, plp->ipo_name, sizeof(op.iplo_name)); + op.iplo_size = sizeof(pool); + op.iplo_struct = &pool; + bzero((char *)&pool, sizeof(pool)); + pool.ipo_unit = plp->ipo_unit; + strncpy(pool.ipo_name, plp->ipo_name, sizeof(pool.ipo_name)); + if (plp->ipo_name[0] == '\0') + op.iplo_arg |= IPOOL_ANON; + + if ((opts & OPT_REMOVE) == 0) { + if (pool_ioctl(iocfunc, SIOCLOOKUPADDTABLE, &op)) { + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "add lookup table"); + } + } + } + + if (op.iplo_arg & IPOOL_ANON) + strncpy(pool.ipo_name, op.iplo_name, sizeof(pool.ipo_name)); + + if ((opts & OPT_VERBOSE) != 0) { + pool.ipo_list = plp->ipo_list; + (void) printpool(&pool, bcopywrap, pool.ipo_name, opts, NULL); + pool.ipo_list = NULL; + } + + for (a = plp->ipo_list; a != NULL; a = a->ipn_next) + load_poolnode(plp->ipo_unit, pool.ipo_name, + a, 0, iocfunc); + + if ((opts & OPT_REMOVE) != 0) { + if (pool_ioctl(iocfunc, SIOCLOOKUPDELTABLE, &op)) + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "delete lookup table"); + } + } + return 0; +} diff --git a/sbin/ipf/libipf/load_poolnode.c b/sbin/ipf/libipf/load_poolnode.c new file mode 100644 index 000000000000..0dfc1d25a8f7 --- /dev/null +++ b/sbin/ipf/libipf/load_poolnode.c @@ -0,0 +1,70 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_pool.h" + + +int +load_poolnode(role, name, node, ttl, iocfunc) + int role; + char *name; + ip_pool_node_t *node; + int ttl; + ioctlfunc_t iocfunc; +{ + ip_pool_node_t pn; + iplookupop_t op; + char *what; + int err; + + if (pool_open() == -1) + return -1; + + op.iplo_unit = role; + op.iplo_type = IPLT_POOL; + op.iplo_arg = 0; + op.iplo_struct = &pn; + op.iplo_size = sizeof(pn); + strncpy(op.iplo_name, name, sizeof(op.iplo_name)); + + bzero((char *)&pn, sizeof(pn)); + bcopy((char *)&node->ipn_addr, (char *)&pn.ipn_addr, + sizeof(pn.ipn_addr)); + bcopy((char *)&node->ipn_mask, (char *)&pn.ipn_mask, + sizeof(pn.ipn_mask)); + pn.ipn_info = node->ipn_info; + pn.ipn_die = ttl; + strncpy(pn.ipn_name, node->ipn_name, sizeof(pn.ipn_name)); + + if ((opts & OPT_REMOVE) == 0) { + what = "add"; + err = pool_ioctl(iocfunc, SIOCLOOKUPADDNODE, &op); + } else { + what = "delete"; + err = pool_ioctl(iocfunc, SIOCLOOKUPDELNODE, &op); + } + + if (err != 0) { + if ((opts & OPT_DONOTHING) == 0) { + char msg[80]; + + snprintf(msg, sizeof(msg), "%s pool node(%s/", what, + inet_ntoa(pn.ipn_addr.adf_addr.in4)); + strcat(msg, inet_ntoa(pn.ipn_mask.adf_addr.in4)); + return ipf_perror_fd(pool_fd(), iocfunc, msg); + } + } + + return 0; +} diff --git a/sbin/ipf/libipf/load_url.c b/sbin/ipf/libipf/load_url.c new file mode 100644 index 000000000000..dcda4c07fdf8 --- /dev/null +++ b/sbin/ipf/libipf/load_url.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: load_url.c,v 1.3.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + +alist_t * +load_url(char *url) +{ + alist_t *hosts = NULL; + + if (strncmp(url, "file://", 7) == 0) { + /* + * file:///etc/passwd + * ^------------s + */ + hosts = load_file(url); + + } else if (*url == '/' || *url == '.') { + hosts = load_file(url); + + } else if (strncmp(url, "http://", 7) == 0) { + hosts = load_http(url); + } + + return hosts; +} diff --git a/sbin/ipf/libipf/mb_hexdump.c b/sbin/ipf/libipf/mb_hexdump.c new file mode 100644 index 000000000000..6da65633191c --- /dev/null +++ b/sbin/ipf/libipf/mb_hexdump.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: mb_hexdump.c,v 1.1.2.3 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + +void +mb_hexdump(m, fp) + mb_t *m; + FILE *fp; +{ + u_char *s; + int len; + int i; + + for (; m != NULL; m = m->mb_next) { + len = m->mb_len; + for (s = (u_char *)m->mb_data, i = 0; i < len; i++) { + fprintf(fp, "%02x", *s++ & 0xff); + if (len - i > 1) { + i++; + fprintf(fp, "%02x", *s++ & 0xff); + } + fputc(' ', fp); + } + } + fputc('\n', fp); +} diff --git a/sbin/ipf/libipf/msgdsize.c b/sbin/ipf/libipf/msgdsize.c new file mode 100644 index 000000000000..9bdc584bc008 --- /dev/null +++ b/sbin/ipf/libipf/msgdsize.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: msgdsize.c,v 1.2.4.3 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + +size_t msgdsize(orig) + mb_t *orig; +{ + size_t sz = 0; + mb_t *m; + + for (m = orig; m != NULL; m = m->mb_next) + sz += m->mb_len; + return sz; +} diff --git a/sbin/ipf/libipf/mutex_emul.c b/sbin/ipf/libipf/mutex_emul.c new file mode 100644 index 000000000000..1846701fa40a --- /dev/null +++ b/sbin/ipf/libipf/mutex_emul.c @@ -0,0 +1,133 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +#define EMM_MAGIC 0x9d7adba3 + +static int mutex_debug = 0; +static FILE *mutex_file = NULL; +static int initcount = 0; + +void +eMmutex_enter(mtx, file, line) + eMmutex_t *mtx; + char *file; + int line; +{ + if (mutex_debug & 2) + fprintf(mutex_file, "%s:%d:eMmutex_enter(%s)\n", file, line, + mtx->eMm_owner); + if (mtx->eMm_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMmutex_enter(%p): bad magic: %#x\n", + mtx->eMm_owner, mtx, mtx->eMm_magic); + abort(); + } + if (mtx->eMm_held != 0) { + fprintf(stderr, "%s:eMmutex_enter(%p): already locked: %d\n", + mtx->eMm_owner, mtx, mtx->eMm_held); + abort(); + } + mtx->eMm_held++; + mtx->eMm_heldin = file; + mtx->eMm_heldat = line; +} + + +void +eMmutex_exit(mtx, file, line) + eMmutex_t *mtx; + char *file; + int line; +{ + if (mutex_debug & 2) + fprintf(mutex_file, "%s:%d:eMmutex_exit(%s)\n", file, line, + mtx->eMm_owner); + if (mtx->eMm_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMmutex_exit(%p): bad magic: %#x\n", + mtx->eMm_owner, mtx, mtx->eMm_magic); + abort(); + } + if (mtx->eMm_held != 1) { + fprintf(stderr, "%s:eMmutex_exit(%p): not locked: %d\n", + mtx->eMm_owner, mtx, mtx->eMm_held); + abort(); + } + mtx->eMm_held--; + mtx->eMm_heldin = NULL; + mtx->eMm_heldat = 0; +} + + +void +eMmutex_init(mtx, who, file, line) + eMmutex_t *mtx; + char *who; + char *file; + int line; +{ + if (mutex_file == NULL && mutex_debug) + mutex_file = fopen("ipf_mutex_log", "w"); + if (mutex_debug & 1) + fprintf(mutex_file, "%s:%d:eMmutex_init(%p,%s)\n", + file, line, mtx, who); + if (mtx->eMm_magic == EMM_MAGIC) { /* safe bet ? */ + fprintf(stderr, + "%s:eMmutex_init(%p): already initialised?: %#x\n", + mtx->eMm_owner, mtx, mtx->eMm_magic); + abort(); + } + mtx->eMm_magic = EMM_MAGIC; + mtx->eMm_held = 0; + if (who != NULL) + mtx->eMm_owner = strdup(who); + else + mtx->eMm_owner = NULL; + initcount++; +} + + +void +eMmutex_destroy(mtx, file, line) + eMmutex_t *mtx; + char *file; + int line; +{ + if (mutex_debug & 1) + fprintf(mutex_file, + "%s:%d:eMmutex_destroy(%p,%s)\n", file, line, + mtx, mtx->eMm_owner); + if (mtx->eMm_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMmutex_destroy(%p): bad magic: %#x\n", + mtx->eMm_owner, mtx, mtx->eMm_magic); + abort(); + } + if (mtx->eMm_held != 0) { + fprintf(stderr, + "%s:eMmutex_enter(%p): still locked: %d\n", + mtx->eMm_owner, mtx, mtx->eMm_held); + abort(); + } + if (mtx->eMm_owner != NULL) + free(mtx->eMm_owner); + memset(mtx, 0xa5, sizeof(*mtx)); + initcount--; +} + + +void +ipf_mutex_clean() +{ + if (initcount != 0) { + if (mutex_file) + fprintf(mutex_file, "initcount %d\n", initcount); + abort(); + } +} diff --git a/sbin/ipf/libipf/nametokva.c b/sbin/ipf/libipf/nametokva.c new file mode 100644 index 000000000000..8e7af944d508 --- /dev/null +++ b/sbin/ipf/libipf/nametokva.c @@ -0,0 +1,38 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +#include <sys/ioctl.h> +#include <fcntl.h> + +ipfunc_t nametokva(name, iocfunc) + char *name; + ioctlfunc_t iocfunc; +{ + ipfunc_resolve_t res; + int fd; + + strncpy(res.ipfu_name, name, sizeof(res.ipfu_name)); + res.ipfu_addr = NULL; + fd = -1; + + if ((opts & OPT_DONTOPEN) == 0) { + fd = open(IPL_NAME, O_RDONLY); + if (fd == -1) + return NULL; + } + (void) (*iocfunc)(fd, SIOCFUNCL, &res); + if (fd >= 0) + close(fd); + if (res.ipfu_addr == NULL) + res.ipfu_addr = (ipfunc_t)-1; + return res.ipfu_addr; +} diff --git a/sbin/ipf/libipf/nat_setgroupmap.c b/sbin/ipf/libipf/nat_setgroupmap.c new file mode 100644 index 000000000000..15c21f6ced12 --- /dev/null +++ b/sbin/ipf/libipf/nat_setgroupmap.c @@ -0,0 +1,34 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + +#include "ipf.h" + +void nat_setgroupmap(n) + ipnat_t *n; +{ + if (n->in_nsrcmsk == n->in_osrcmsk) + n->in_ippip = 1; + else if (n->in_flags & IPN_AUTOPORTMAP) { + n->in_ippip = ~ntohl(n->in_osrcmsk); + if (n->in_nsrcmsk != 0xffffffff) + n->in_ippip /= (~ntohl(n->in_nsrcmsk) + 1); + n->in_ippip++; + if (n->in_ippip == 0) + n->in_ippip = 1; + n->in_ppip = USABLE_PORTS / n->in_ippip; + } else { + n->in_space = USABLE_PORTS * ~ntohl(n->in_nsrcmsk); + n->in_snip = 0; + if (!(n->in_ppip = n->in_spmin)) + n->in_ppip = 1; + n->in_ippip = USABLE_PORTS / n->in_ppip; + } +} diff --git a/sbin/ipf/libipf/ntomask.c b/sbin/ipf/libipf/ntomask.c new file mode 100644 index 000000000000..98e3b26119b0 --- /dev/null +++ b/sbin/ipf/libipf/ntomask.c @@ -0,0 +1,47 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +int ntomask(family, nbits, ap) + int family, nbits; + u_32_t *ap; +{ + u_32_t mask; + + if (nbits < 0) + return -1; + + switch (family) + { + case AF_INET : + if (nbits > 32 || use_inet6 == 1) + return -1; + if (nbits == 0) { + mask = 0; + } else { + mask = 0xffffffff; + mask <<= (32 - nbits); + } + *ap = htonl(mask); + break; + + case 0 : + case AF_INET6 : + if ((nbits > 128) || (use_inet6 == -1)) + return -1; + fill6bits(nbits, ap); + break; + + default : + return -1; + } + return 0; +} diff --git a/sbin/ipf/libipf/optname.c b/sbin/ipf/libipf/optname.c new file mode 100644 index 000000000000..2bc811b8dbe5 --- /dev/null +++ b/sbin/ipf/libipf/optname.c @@ -0,0 +1,65 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +u_32_t optname(cp, sp, linenum) + char ***cp; + u_short *sp; + int linenum; +{ + 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, "%d: unknown IP option name %s\n", + linenum, s); + return 0; + } + if (!strcasecmp(s, "sec-class")) + sec = 1; + } + + if (sec && !*(*cp + 1)) { + fprintf(stderr, "%d: missing security level after sec-class\n", + linenum); + 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, + "%d: no such security level: %s\n", + linenum, s); + return 0; + } + } + if (smsk) + *sp = smsk; + } + return msk; +} diff --git a/sbin/ipf/libipf/optprint.c b/sbin/ipf/libipf/optprint.c new file mode 100644 index 000000000000..8b1f5cd9d042 --- /dev/null +++ b/sbin/ipf/libipf/optprint.c @@ -0,0 +1,83 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + + +void optprint(sec, optmsk, optbits) + u_short *sec; + u_long optmsk, optbits; +{ + u_short secmsk = sec[0], secbits = sec[1]; + struct ipopt_names *io, *so; + char *s; + + s = " opt "; + for (io = ionames; io->on_name; io++) + if ((io->on_bit & optmsk) && + ((io->on_bit & optmsk) == (io->on_bit & optbits))) { + if ((io->on_value != IPOPT_SECURITY) || + (!secmsk && !secbits)) { + printf("%s%s", s, io->on_name); + /* + * Because the ionames table has this entry + * twice. + */ + if (io->on_value == IPOPT_SECURITY) + io++; + s = ","; + } + } + + + if (secmsk & secbits) { + printf("%ssec-class", s); + s = " "; + for (so = secclass; so->on_name; so++) + if ((secmsk & so->on_bit) && + ((so->on_bit & secmsk) == (so->on_bit & secbits))) { + printf("%s%s", s, so->on_name); + s = ","; + } + } + + if ((optmsk && (optmsk != optbits)) || + (secmsk && (secmsk != secbits))) { + s = " "; + printf(" not opt"); + if (optmsk != optbits) { + for (io = ionames; io->on_name; io++) + if ((io->on_bit & optmsk) && + ((io->on_bit & optmsk) != + (io->on_bit & optbits))) { + if ((io->on_value != IPOPT_SECURITY) || + (!secmsk && !secbits)) { + printf("%s%s", s, io->on_name); + s = ","; + if (io->on_value == + IPOPT_SECURITY) + io++; + } else + io++; + } + } + + if (secmsk != secbits) { + printf("%ssec-class", s); + s = " "; + for (so = secclass; so->on_name; so++) + if ((so->on_bit & secmsk) && + ((so->on_bit & secmsk) != + (so->on_bit & secbits))) { + printf("%s%s", s, so->on_name); + s = ","; + } + } + } +} diff --git a/sbin/ipf/libipf/optprintv6.c b/sbin/ipf/libipf/optprintv6.c new file mode 100644 index 000000000000..752d1b353485 --- /dev/null +++ b/sbin/ipf/libipf/optprintv6.c @@ -0,0 +1,47 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + + +#ifdef USE_INET6 + +void optprintv6(sec, optmsk, optbits) + u_short *sec; + u_long optmsk, optbits; +{ + u_short secmsk = sec[0], secbits = sec[1]; + struct ipopt_names *io; + char *s; + + s = " v6hdr "; + for (io = v6ionames; io->on_name; io++) + if ((io->on_bit & optmsk) && + ((io->on_bit & optmsk) == (io->on_bit & optbits))) { + printf("%s%s", s, io->on_name); + s = ","; + } + + if ((optmsk && (optmsk != optbits)) || + (secmsk && (secmsk != secbits))) { + s = " "; + printf(" not v6hdrs"); + if (optmsk != optbits) { + for (io = v6ionames; io->on_name; io++) + if ((io->on_bit & optmsk) && + ((io->on_bit & optmsk) != + (io->on_bit & optbits))) { + printf("%s%s", s, io->on_name); + s = ","; + } + } + + } +} +#endif diff --git a/sbin/ipf/libipf/optvalue.c b/sbin/ipf/libipf/optvalue.c new file mode 100644 index 000000000000..5bc1f4298a25 --- /dev/null +++ b/sbin/ipf/libipf/optvalue.c @@ -0,0 +1,34 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + + +u_32_t getoptbyname(optname) + char *optname; +{ + struct ipopt_names *io; + + for (io = ionames; io->on_name; io++) + if (!strcasecmp(optname, io->on_name)) + return io->on_bit; + return -1; +} + + +u_32_t getoptbyvalue(optval) + int optval; +{ + struct ipopt_names *io; + + for (io = ionames; io->on_name; io++) + if (io->on_value == optval) + return io->on_bit; + return -1; +} diff --git a/sbin/ipf/libipf/parsefields.c b/sbin/ipf/libipf/parsefields.c new file mode 100644 index 000000000000..93c3601c9557 --- /dev/null +++ b/sbin/ipf/libipf/parsefields.c @@ -0,0 +1,53 @@ +#include "ipf.h" +#include <err.h> + +extern int nohdrfields; + +wordtab_t *parsefields(table, arg) + wordtab_t *table; + char *arg; +{ + wordtab_t *f, *fields; + char *s, *t; + int num; + + fields = NULL; + num = 0; + + for (s = strtok(arg, ","); s != NULL; s = strtok(NULL, ",")) { + t = strchr(s, '='); + if (t != NULL) { + *t++ = '\0'; + if (*t == '\0') + nohdrfields = 1; + } + + f = findword(table, s); + if (f == NULL) { + fprintf(stderr, "Unknown field '%s'\n", s); + exit(1); + } + + num++; + if (fields == NULL) { + fields = malloc(2 * sizeof(*fields)); + } else { + fields = reallocarray(fields, num + 1, sizeof(*fields)); + if (fields == NULL) { + warnx("memory allocation error at %d in %s in %s", __LINE__, __FUNCTION__, __FILE__); + abort(); + } + } + + if (t == NULL) { + fields[num - 1].w_word = f->w_word; + } else { + fields[num - 1].w_word = t; + } + fields[num - 1].w_value = f->w_value; + fields[num].w_word = NULL; + fields[num].w_value = 0; + } + + return fields; +} diff --git a/sbin/ipf/libipf/parseipfexpr.c b/sbin/ipf/libipf/parseipfexpr.c new file mode 100644 index 000000000000..b4b00f91bfca --- /dev/null +++ b/sbin/ipf/libipf/parseipfexpr.c @@ -0,0 +1,283 @@ +#include "ipf.h" +#include <ctype.h> + + +typedef struct ipfopentry { + int ipoe_cmd; + int ipoe_nbasearg; + int ipoe_maxarg; + int ipoe_argsize; + char *ipoe_word; +} ipfopentry_t; + +static ipfopentry_t opwords[17] = { + { IPF_EXP_IP_ADDR, 2, 0, 1, "ip.addr" }, + { IPF_EXP_IP6_ADDR, 2, 0, 4, "ip6.addr" }, + { IPF_EXP_IP_PR, 1, 0, 1, "ip.p" }, + { IPF_EXP_IP_SRCADDR, 2, 0, 1, "ip.src" }, + { IPF_EXP_IP_DSTADDR, 2, 0, 1, "ip.dst" }, + { IPF_EXP_IP6_SRCADDR, 2, 0, 4, "ip6.src" }, + { IPF_EXP_IP6_DSTADDR, 2, 0, 4, "ip6.dst" }, + { IPF_EXP_TCP_PORT, 1, 0, 1, "tcp.port" }, + { IPF_EXP_TCP_DPORT, 1, 0, 1, "tcp.dport" }, + { IPF_EXP_TCP_SPORT, 1, 0, 1, "tcp.sport" }, + { IPF_EXP_TCP_FLAGS, 2, 0, 1, "tcp.flags" }, + { IPF_EXP_UDP_PORT, 1, 0, 1, "udp.port" }, + { IPF_EXP_UDP_DPORT, 1, 0, 1, "udp.dport" }, + { IPF_EXP_UDP_SPORT, 1, 0, 1, "udp.sport" }, + { IPF_EXP_TCP_STATE, 1, 0, 1, "tcp.state" }, + { IPF_EXP_IDLE_GT, 1, 1, 1, "idle-gt" }, + { -1, 0, 0, 0, NULL } +}; + + +int * +parseipfexpr(line, errorptr) + char *line; + char **errorptr; +{ + int not, items, asize, *oplist, osize, i; + char *temp, *arg, *s, *t, *ops, *error; + ipfopentry_t *e; + ipfexp_t *ipfe; + + asize = 0; + error = NULL; + oplist = NULL; + + temp = strdup(line); + if (temp == NULL) { + error = "strdup failed"; + goto parseerror; + } + + /* + * Eliminate any white spaces to make parsing easier. + */ + for (s = temp; *s != '\0'; ) { + if (ISSPACE(*s)) + strcpy(s, s + 1); + else + s++; + } + + /* + * Parse the string. + * It should be sets of "ip.dst=1.2.3.4/32;" things. + * There must be a "=" or "!=" and it must end in ";". + */ + if (temp[strlen(temp) - 1] != ';') { + error = "last character not ';'"; + goto parseerror; + } + + /* + * Work through the list of complete operands present. + */ + for (ops = strtok(temp, ";"); ops != NULL; ops = strtok(NULL, ";")) { + arg = strchr(ops, '='); + if ((arg < ops + 2) || (arg == NULL)) { + error = "bad 'arg' vlaue"; + goto parseerror; + } + + if (*(arg - 1) == '!') { + *(arg - 1) = '\0'; + not = 1; + } else { + not = 0; + } + *arg++ = '\0'; + + + for (e = opwords; e->ipoe_word; e++) { + if (strcmp(ops, e->ipoe_word) == 0) + break; + } + if (e->ipoe_word == NULL) { + error = malloc(32); + if (error != NULL) { + snprintf(error, sizeof(error), "keyword (%.10s) not found", + ops); + } + goto parseerror; + } + + /* + * Count the number of commas so we know how big to + * build the array + */ + for (s = arg, items = 1; *s != '\0'; s++) + if (*s == ',') + items++; + + if ((e->ipoe_maxarg != 0) && (items > e->ipoe_maxarg)) { + error = "too many items"; + goto parseerror; + } + + /* + * osize will mark the end of where we have filled up to + * and is thus where we start putting new data. + */ + osize = asize; + asize += 4 + (items * e->ipoe_nbasearg * e->ipoe_argsize); + if (oplist == NULL) + oplist = calloc(asize + 2, sizeof(int)); + else + oplist = reallocarray(oplist, asize + 2, sizeof(int)); + if (oplist == NULL) { + error = "oplist alloc failed"; + goto parseerror; + } + ipfe = (ipfexp_t *)(oplist + osize); + osize += 4; + ipfe->ipfe_cmd = e->ipoe_cmd; + ipfe->ipfe_not = not; + ipfe->ipfe_narg = items * e->ipoe_nbasearg; + ipfe->ipfe_size = items * e->ipoe_nbasearg * e->ipoe_argsize; + ipfe->ipfe_size += 4; + + for (s = arg; (*s != '\0') && (osize < asize); s = t) { + /* + * Look for the end of this arg or the ',' to say + * there is another following. + */ + for (t = s; (*t != '\0') && (*t != ','); t++) + ; + if (*t == ',') + *t++ = '\0'; + + if (!strcasecmp(ops, "ip.addr") || + !strcasecmp(ops, "ip.src") || + !strcasecmp(ops, "ip.dst")) { + i6addr_t mask, addr; + char *delim; + + delim = strchr(s, '/'); + if (delim != NULL) { + *delim++ = '\0'; + if (genmask(AF_INET, delim, + &mask) == -1) { + error = "genmask failed"; + goto parseerror; + } + } else { + mask.in4.s_addr = 0xffffffff; + } + if (gethost(AF_INET, s, &addr) == -1) { + error = "gethost failed"; + goto parseerror; + } + + oplist[osize++] = addr.in4.s_addr; + oplist[osize++] = mask.in4.s_addr; + +#ifdef USE_INET6 + } else if (!strcasecmp(ops, "ip6.addr") || + !strcasecmp(ops, "ip6.src") || + !strcasecmp(ops, "ip6.dst")) { + i6addr_t mask, addr; + char *delim; + + delim = strchr(s, '/'); + if (delim != NULL) { + *delim++ = '\0'; + if (genmask(AF_INET6, delim, + &mask) == -1) { + error = "genmask failed"; + goto parseerror; + } + } else { + mask.i6[0] = 0xffffffff; + mask.i6[1] = 0xffffffff; + mask.i6[2] = 0xffffffff; + mask.i6[3] = 0xffffffff; + } + if (gethost(AF_INET6, s, &addr) == -1) { + error = "gethost failed"; + goto parseerror; + } + + oplist[osize++] = addr.i6[0]; + oplist[osize++] = addr.i6[1]; + oplist[osize++] = addr.i6[2]; + oplist[osize++] = addr.i6[3]; + oplist[osize++] = mask.i6[0]; + oplist[osize++] = mask.i6[1]; + oplist[osize++] = mask.i6[2]; + oplist[osize++] = mask.i6[3]; +#endif + + } else if (!strcasecmp(ops, "ip.p")) { + int p; + + p = getproto(s); + if (p == -1) + goto parseerror; + oplist[osize++] = p; + + } else if (!strcasecmp(ops, "tcp.flags")) { + u_32_t mask, flags; + char *delim; + + delim = strchr(s, '/'); + if (delim != NULL) { + *delim++ = '\0'; + mask = tcpflags(delim); + } else { + mask = 0xff; + } + flags = tcpflags(s); + + oplist[osize++] = flags; + oplist[osize++] = mask; + + + } else if (!strcasecmp(ops, "tcp.port") || + !strcasecmp(ops, "tcp.sport") || + !strcasecmp(ops, "tcp.dport") || + !strcasecmp(ops, "udp.port") || + !strcasecmp(ops, "udp.sport") || + !strcasecmp(ops, "udp.dport")) { + char proto[4]; + u_short port; + + strncpy(proto, ops, 3); + proto[3] = '\0'; + if (getport(NULL, s, &port, proto) == -1) + goto parseerror; + oplist[osize++] = port; + + } else if (!strcasecmp(ops, "tcp.state")) { + oplist[osize++] = atoi(s); + + } else { + error = "unknown word"; + goto parseerror; + } + } + } + + free(temp); + + if (errorptr != NULL) + *errorptr = NULL; + + for (i = asize; i > 0; i--) + oplist[i] = oplist[i - 1]; + + oplist[0] = asize + 2; + oplist[asize + 1] = IPF_EXP_END; + + return oplist; + +parseerror: + if (errorptr != NULL) + *errorptr = error; + if (oplist != NULL) + free(oplist); + if (temp != NULL) + free(temp); + return NULL; +} diff --git a/sbin/ipf/libipf/parsewhoisline.c b/sbin/ipf/libipf/parsewhoisline.c new file mode 100644 index 000000000000..526935ca23a5 --- /dev/null +++ b/sbin/ipf/libipf/parsewhoisline.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: parsewhoisline.c,v 1.2.2.5 2012/07/22 08:04:24 darren_r Exp $ + */ +#include "ipf.h" + +/* +Microsoft Corp MICROSOFT19 (NET-198-136-97-0-1) 198.137.97.0 - 198.137.97.255 +Microsoft Corp SAVV-S233053-6 (NET-206-79-74-32-1) 206.79.74.32 - 206.79.74.47 + */ +int +parsewhoisline(line, addrp, maskp) + char *line; + addrfamily_t *addrp; + addrfamily_t *maskp; +{ + struct in_addr a1, a2; + char *src = line; + char *s = NULL; + + if (line == NULL) + return -1; + + while (*src != '\0') { + s = strchr(src, '('); + if (s == NULL) + break; + + if (strncmp(s, "(NET", 4)) { + src = s + 1; + } + break; + } + + if (s == NULL) + return -1; + + memset(addrp, 0x00, sizeof(*maskp)); + memset(maskp, 0x00, sizeof(*maskp)); + + if (*(s + 4) == '6') { +#ifdef USE_INET6 + i6addr_t a61, a62; + + s = strchr(s, ')'); + if (s == NULL || *++s != ' ') + return -1; + /* + * Parse the IPv6 + */ + if (inet_pton(AF_INET6, s, &a61.in6) != 1) + return -1; + + s = strchr(s, ' '); + if (s == NULL || strncmp(s, " - ", 3)) + return -1; + + s += 3; + if (inet_pton(AF_INET6, s, &a62) != 1) + return -1; + + addrp->adf_addr = a61; + addrp->adf_family = AF_INET6; + addrp->adf_len = offsetof(addrfamily_t, adf_addr) + + sizeof(struct in6_addr); + + maskp->adf_addr.i6[0] = ~(a62.i6[0] ^ a61.i6[0]); + maskp->adf_addr.i6[1] = ~(a62.i6[1] ^ a61.i6[1]); + maskp->adf_addr.i6[2] = ~(a62.i6[2] ^ a61.i6[2]); + maskp->adf_addr.i6[3] = ~(a62.i6[3] ^ a61.i6[3]); + + /* + * If the mask that's been generated isn't a consecutive mask + * then we can't add it into a pool. + */ + if (count6bits(maskp->adf_addr.i6) == -1) + return -1; + + maskp->adf_family = AF_INET6; + maskp->adf_len = addrp->adf_len; + + if (IP6_MASKNEQ(&addrp->adf_addr.in6, &maskp->adf_addr.in6, + &addrp->adf_addr.in6)) { + return -1; + } + return 0; +#else + return -1; +#endif + } + + s = strchr(s, ')'); + if (s == NULL || *++s != ' ') + return -1; + + s++; + + if (inet_aton(s, &a1) != 1) + return -1; + + s = strchr(s, ' '); + if (s == NULL || strncmp(s, " - ", 3)) + return -1; + + s += 3; + if (inet_aton(s, &a2) != 1) + return -1; + + addrp->adf_addr.in4 = a1; + addrp->adf_family = AF_INET; + addrp->adf_len = offsetof(addrfamily_t, adf_addr) + + sizeof(struct in_addr); + maskp->adf_addr.in4.s_addr = ~(a2.s_addr ^ a1.s_addr); + + /* + * If the mask that's been generated isn't a consecutive mask then + * we can't add it into a pool. + */ + if (count4bits(maskp->adf_addr.in4.s_addr) == -1) + return -1; + + maskp->adf_family = AF_INET; + maskp->adf_len = addrp->adf_len; + bzero((char *)maskp + maskp->adf_len, sizeof(*maskp) - maskp->adf_len); + if ((addrp->adf_addr.in4.s_addr & maskp->adf_addr.in4.s_addr) != + addrp->adf_addr.in4.s_addr) + return -1; + return 0; +} diff --git a/sbin/ipf/libipf/poolio.c b/sbin/ipf/libipf/poolio.c new file mode 100644 index 000000000000..18cf698222a8 --- /dev/null +++ b/sbin/ipf/libipf/poolio.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: poolio.c,v 1.1.2.3 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_pool.h" + +static int poolfd = -1; + + +int +pool_open() +{ + + if ((opts & OPT_DONTOPEN) != 0) + return 0; + + if (poolfd == -1) + poolfd = open(IPLOOKUP_NAME, O_RDWR); + return poolfd; +} + +int +pool_ioctl(iocfunc, cmd, ptr) + ioctlfunc_t iocfunc; + ioctlcmd_t cmd; + void *ptr; +{ + return (*iocfunc)(poolfd, cmd, ptr); +} + + +void +pool_close() +{ + if (poolfd != -1) { + close(poolfd); + poolfd = -1; + } +} + +int +pool_fd() +{ + return poolfd; +} diff --git a/sbin/ipf/libipf/portname.c b/sbin/ipf/libipf/portname.c new file mode 100644 index 000000000000..f567b26fc3fd --- /dev/null +++ b/sbin/ipf/libipf/portname.c @@ -0,0 +1,43 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + + +char * +portname(int pr, int port) +{ + static char buf[32]; + struct protoent *p = NULL; + struct servent *sv = NULL; + struct servent *sv1 = NULL; + + if ((opts & OPT_NORESOLVE) == 0) { + 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"); + sv = strncasecmp(buf, sv->s_name, strlen(buf)) ? + NULL : sv1; + } + if (sv) + return (buf); + } else if ((pr != -2) && (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) snprintf(buf, sizeof(buf), "%d", port); + return (buf); +} diff --git a/sbin/ipf/libipf/prependmbt.c b/sbin/ipf/libipf/prependmbt.c new file mode 100644 index 000000000000..4f7220ba236a --- /dev/null +++ b/sbin/ipf/libipf/prependmbt.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: prependmbt.c,v 1.3.2.3 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + +int prependmbt(fin, m) + fr_info_t *fin; + mb_t *m; +{ + m->mb_next = *fin->fin_mp; + *fin->fin_mp = m; + return 0; +} diff --git a/sbin/ipf/libipf/print_toif.c b/sbin/ipf/libipf/print_toif.c new file mode 100644 index 000000000000..fb4a266318b4 --- /dev/null +++ b/sbin/ipf/libipf/print_toif.c @@ -0,0 +1,50 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void +print_toif(family, tag, base, fdp) + int family; + char *tag; + char *base; + frdest_t *fdp; +{ + switch (fdp->fd_type) + { + case FRD_NORMAL : + PRINTF("%s %s%s", tag, base + fdp->fd_name, + (fdp->fd_ptr || (long)fdp->fd_ptr == -1) ? "" : "(!)"); +#ifdef USE_INET6 + if (family == AF_INET6) { + if (IP6_NOTZERO(&fdp->fd_ip6)) { + char ipv6addr[80]; + + inet_ntop(AF_INET6, &fdp->fd_ip6, ipv6addr, + sizeof(fdp->fd_ip6)); + PRINTF(":%s", ipv6addr); + } + } else +#endif + if (fdp->fd_ip.s_addr) + PRINTF(":%s", inet_ntoa(fdp->fd_ip)); + putchar(' '); + break; + + case FRD_DSTLIST : + PRINTF("%s dstlist/%s ", tag, base + fdp->fd_name); + break; + + default : + PRINTF("%s <%d>", tag, fdp->fd_type); + break; + } +} diff --git a/sbin/ipf/libipf/printactiveaddr.c b/sbin/ipf/libipf/printactiveaddr.c new file mode 100644 index 000000000000..531cdc1fc782 --- /dev/null +++ b/sbin/ipf/libipf/printactiveaddr.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) + */ + +#include "ipf.h" + + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id: printactiveaddr.c,v 1.3.2.2 2012/07/22 08:04:24 darren_r Exp $"; +#endif + + +void +printactiveaddress(v, fmt, addr, ifname) + int v; + char *fmt, *ifname; + i6addr_t *addr; +{ + switch (v) + { + case 4 : + PRINTF(fmt, inet_ntoa(addr->in4)); + break; +#ifdef USE_INET6 + case 6 : + printaddr(AF_INET6, FRI_NORMAL, ifname, 0, + (u_32_t *)&addr->in6, NULL); + break; +#endif + default : + break; + } +} diff --git a/sbin/ipf/libipf/printactivenat.c b/sbin/ipf/libipf/printactivenat.c new file mode 100644 index 000000000000..c696c0b2cacd --- /dev/null +++ b/sbin/ipf/libipf/printactivenat.c @@ -0,0 +1,149 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) + */ + +#include "ipf.h" + + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + + +void +printactivenat(nat, opts, ticks) + nat_t *nat; + int opts; + u_long ticks; +{ + + PRINTF("%s", getnattype(nat)); + + if (nat->nat_flags & SI_CLONE) + PRINTF(" CLONE"); + if (nat->nat_phnext[0] == NULL && nat->nat_phnext[1] == NULL) + PRINTF(" ORPHAN"); + + putchar(' '); + if (nat->nat_redir & NAT_REWRITE) { + printactiveaddress(nat->nat_v[0], "%-15s", &nat->nat_osrc6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %-5hu", ntohs(nat->nat_osport)); + + putchar(' '); + printactiveaddress(nat->nat_v[0], "%-15s", &nat->nat_odst6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %-5hu", ntohs(nat->nat_odport)); + + PRINTF("<- -> "); + printactiveaddress(nat->nat_v[1], "%-15s", &nat->nat_nsrc6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %-5hu", ntohs(nat->nat_nsport)); + + putchar(' '); + printactiveaddress(nat->nat_v[1], "%-15s", &nat->nat_ndst6, + nat->nat_ifnames[0]); + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %-5hu", ntohs(nat->nat_ndport)); + + } else if (nat->nat_dir == NAT_OUTBOUND) { + printactiveaddress(nat->nat_v[0], "%-15s", &nat->nat_osrc6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %-5hu", ntohs(nat->nat_osport)); + + PRINTF(" <- -> "); + printactiveaddress(nat->nat_v[1], "%-15s", &nat->nat_nsrc6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %-5hu", ntohs(nat->nat_nsport)); + + PRINTF(" ["); + printactiveaddress(nat->nat_v[0], "%s", &nat->nat_odst6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %hu", ntohs(nat->nat_odport)); + PRINTF("]"); + } else { + printactiveaddress(nat->nat_v[1], "%-15s", &nat->nat_ndst6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %-5hu", ntohs(nat->nat_ndport)); + + PRINTF(" <- -> "); + printactiveaddress(nat->nat_v[0], "%-15s", &nat->nat_odst6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %-5hu", ntohs(nat->nat_odport)); + + PRINTF(" ["); + printactiveaddress(nat->nat_v[0], "%s", &nat->nat_osrc6, + nat->nat_ifnames[0]); + + if ((nat->nat_flags & IPN_TCPUDP) != 0) + PRINTF(" %hu", ntohs(nat->nat_osport)); + PRINTF("]"); + } + + if (opts & OPT_VERBOSE) { + PRINTF("\n\tttl %lu use %hu sumd %s/", + nat->nat_age - ticks, nat->nat_use, + getsumd(nat->nat_sumd[0])); + PRINTF("%s pr %u/%u hash %u/%u flags %x\n", + getsumd(nat->nat_sumd[1]), + nat->nat_pr[0], nat->nat_pr[1], + nat->nat_hv[0], nat->nat_hv[1], nat->nat_flags); + PRINTF("\tifp %s", getifname(nat->nat_ifps[0])); + PRINTF(",%s ", getifname(nat->nat_ifps[1])); +#ifdef USE_QUAD_T + PRINTF("bytes %"PRIu64"/%"PRIu64" pkts %"PRIu64"/%"PRIu64"", + (unsigned long long)nat->nat_bytes[0], + (unsigned long long)nat->nat_bytes[1], + (unsigned long long)nat->nat_pkts[0], + (unsigned long long)nat->nat_pkts[1]); +#else + PRINTF("bytes %lu/%lu pkts %lu/%lu", nat->nat_bytes[0], + nat->nat_bytes[1], nat->nat_pkts[0], nat->nat_pkts[1]); +#endif + PRINTF(" ipsumd %x", nat->nat_ipsumd); + } + + if (opts & OPT_DEBUG) { + PRINTF("\n\tnat_next %p _pnext %p _hm %p\n", + nat->nat_next, nat->nat_pnext, nat->nat_hm); + PRINTF("\t_hnext %p/%p _phnext %p/%p\n", + nat->nat_hnext[0], nat->nat_hnext[1], + nat->nat_phnext[0], nat->nat_phnext[1]); + PRINTF("\t_data %p _me %p _state %p _aps %p\n", + nat->nat_data, nat->nat_me, nat->nat_state, + nat->nat_aps); + PRINTF("\tfr %p ptr %p ifps %p/%p sync %p\n", + nat->nat_fr, nat->nat_ptr, nat->nat_ifps[0], + nat->nat_ifps[1], nat->nat_sync); + PRINTF("\ttqe:pnext %p next %p ifq %p parent %p/%p\n", + nat->nat_tqe.tqe_pnext, nat->nat_tqe.tqe_next, + nat->nat_tqe.tqe_ifq, nat->nat_tqe.tqe_parent, nat); + PRINTF("\ttqe:die %d touched %d flags %x state %d/%d\n", + nat->nat_tqe.tqe_die, nat->nat_tqe.tqe_touched, + nat->nat_tqe.tqe_flags, nat->nat_tqe.tqe_state[0], + nat->nat_tqe.tqe_state[1]); + } + putchar('\n'); +} diff --git a/sbin/ipf/libipf/printaddr.c b/sbin/ipf/libipf/printaddr.c new file mode 100644 index 000000000000..03fbacbcce74 --- /dev/null +++ b/sbin/ipf/libipf/printaddr.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +void +printaddr(family, type, base, ifidx, addr, mask) + int family, type, ifidx; + char *base; + u_32_t *addr, *mask; +{ + char *suffix; + + switch (type) + { + case FRI_BROADCAST : + suffix = "bcast"; + break; + + case FRI_DYNAMIC : + PRINTF("%s", base + ifidx); + printmask(family, mask); + suffix = NULL; + break; + + case FRI_NETWORK : + suffix = "net"; + break; + + case FRI_NETMASKED : + suffix = "netmasked"; + break; + + case FRI_PEERADDR : + suffix = "peer"; + break; + + case FRI_LOOKUP : + suffix = NULL; + printlookup(base, (i6addr_t *)addr, (i6addr_t *)mask); + break; + + case FRI_NONE : + case FRI_NORMAL : + printhostmask(family, addr, mask); + suffix = NULL; + break; + case FRI_RANGE : + printhost(family, addr); + putchar('-'); + printhost(family, mask); + suffix = NULL; + break; + case FRI_SPLIT : + printhost(family, addr); + putchar(','); + printhost(family, mask); + suffix = NULL; + break; + default : + PRINTF("<%d>", type); + printmask(family, mask); + suffix = NULL; + break; + } + + if (suffix != NULL) { + PRINTF("%s/%s", base + ifidx, suffix); + } +} diff --git a/sbin/ipf/libipf/printaps.c b/sbin/ipf/libipf/printaps.c new file mode 100644 index 000000000000..47c8def6106b --- /dev/null +++ b/sbin/ipf/libipf/printaps.c @@ -0,0 +1,113 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) + */ + +#include "ipf.h" +#include "kmem.h" + + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + + +void +printaps(aps, opts, proto) + ap_session_t *aps; + int opts, proto; +{ + ipsec_pxy_t ipsec; + ap_session_t ap; + ftpinfo_t ftp; + aproxy_t apr; + raudio_t ra; + + if (kmemcpy((char *)&ap, (long)aps, sizeof(ap))) + return; + if (kmemcpy((char *)&apr, (long)ap.aps_apr, sizeof(apr))) + return; + PRINTF("\tproxy %s/%d use %d flags %x\n", apr.apr_label, + apr.apr_p, apr.apr_ref, apr.apr_flags); +#ifdef USE_QUAD_T + PRINTF("\tbytes %"PRIu64" pkts %"PRIu64"", + (unsigned long long)ap.aps_bytes, + (unsigned long long)ap.aps_pkts); +#else + PRINTF("\tbytes %lu pkts %lu", ap.aps_bytes, ap.aps_pkts); +#endif + PRINTF(" data %s\n", ap.aps_data ? "YES" : "NO"); + if ((proto == IPPROTO_TCP) && (opts & OPT_VERBOSE)) { + PRINTF("\t\tstate[%u,%u], sel[%d,%d]\n", + ap.aps_state[0], ap.aps_state[1], + ap.aps_sel[0], ap.aps_sel[1]); +#if (defined(NetBSD) && (NetBSD >= 199905) && (NetBSD < 1991011)) || \ + defined(__FreeBSD__) + PRINTF("\t\tseq: off %hd/%hd min %x/%x\n", + ap.aps_seqoff[0], ap.aps_seqoff[1], + ap.aps_seqmin[0], ap.aps_seqmin[1]); + PRINTF("\t\tack: off %hd/%hd min %x/%x\n", + ap.aps_ackoff[0], ap.aps_ackoff[1], + ap.aps_ackmin[0], ap.aps_ackmin[1]); +#else + PRINTF("\t\tseq: off %hd/%hd min %lx/%lx\n", + ap.aps_seqoff[0], ap.aps_seqoff[1], + ap.aps_seqmin[0], ap.aps_seqmin[1]); + PRINTF("\t\tack: off %hd/%hd min %lx/%lx\n", + ap.aps_ackoff[0], ap.aps_ackoff[1], + ap.aps_ackmin[0], ap.aps_ackmin[1]); +#endif + } + + if (!strcmp(apr.apr_label, "raudio") && ap.aps_psiz == sizeof(ra)) { + if (kmemcpy((char *)&ra, (long)ap.aps_data, sizeof(ra))) + return; + PRINTF("\tReal Audio Proxy:\n"); + PRINTF("\t\tSeen PNA: %d\tVersion: %d\tEOS: %d\n", + ra.rap_seenpna, ra.rap_version, ra.rap_eos); + PRINTF("\t\tMode: %#x\tSBF: %#x\n", ra.rap_mode, ra.rap_sbf); + PRINTF("\t\tPorts:pl %hu, pr %hu, sr %hu\n", + ra.rap_plport, ra.rap_prport, ra.rap_srport); + } else if (!strcmp(apr.apr_label, "ftp") && + (ap.aps_psiz == sizeof(ftp))) { + if (kmemcpy((char *)&ftp, (long)ap.aps_data, sizeof(ftp))) + return; + PRINTF("\tFTP Proxy:\n"); + PRINTF("\t\tpassok: %d\n", ftp.ftp_passok); + ftp.ftp_side[0].ftps_buf[FTP_BUFSZ - 1] = '\0'; + ftp.ftp_side[1].ftps_buf[FTP_BUFSZ - 1] = '\0'; + PRINTF("\tClient:\n"); + PRINTF("\t\tseq %x (ack %x) len %d junk %d cmds %d\n", + ftp.ftp_side[0].ftps_seq[0], + ftp.ftp_side[0].ftps_seq[1], + ftp.ftp_side[0].ftps_len, ftp.ftp_side[0].ftps_junk, + ftp.ftp_side[0].ftps_cmds); + PRINTF("\t\tbuf ["); + printbuf(ftp.ftp_side[0].ftps_buf, FTP_BUFSZ, 1); + PRINTF("]\n\tServer:\n"); + PRINTF("\t\tseq %x (ack %x) len %d junk %d cmds %d\n", + ftp.ftp_side[1].ftps_seq[0], + ftp.ftp_side[1].ftps_seq[1], + ftp.ftp_side[1].ftps_len, ftp.ftp_side[1].ftps_junk, + ftp.ftp_side[1].ftps_cmds); + PRINTF("\t\tbuf ["); + printbuf(ftp.ftp_side[1].ftps_buf, FTP_BUFSZ, 1); + PRINTF("]\n"); + } else if (!strcmp(apr.apr_label, "ipsec") && + (ap.aps_psiz == sizeof(ipsec))) { + if (kmemcpy((char *)&ipsec, (long)ap.aps_data, sizeof(ipsec))) + return; + PRINTF("\tIPSec Proxy:\n"); + PRINTF("\t\tICookie %08x%08x RCookie %08x%08x %s\n", + (u_int)ntohl(ipsec.ipsc_icookie[0]), + (u_int)ntohl(ipsec.ipsc_icookie[1]), + (u_int)ntohl(ipsec.ipsc_rcookie[0]), + (u_int)ntohl(ipsec.ipsc_rcookie[1]), + ipsec.ipsc_rckset ? "(Set)" : "(Not set)"); + } +} diff --git a/sbin/ipf/libipf/printbuf.c b/sbin/ipf/libipf/printbuf.c new file mode 100644 index 000000000000..4e9236f0d02d --- /dev/null +++ b/sbin/ipf/libipf/printbuf.c @@ -0,0 +1,34 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <ctype.h> + +#include "ipf.h" + + +void +printbuf(buf, len, zend) + char *buf; + int len, zend; +{ + char *s; + int c; + int i; + + for (s = buf, i = len; i; i--) { + c = *s++; + if (isprint(c)) + putchar(c); + else + PRINTF("\\%03o", c); + if ((c == '\0') && zend) + break; + } +} diff --git a/sbin/ipf/libipf/printdstl_live.c b/sbin/ipf/libipf/printdstl_live.c new file mode 100644 index 000000000000..c8741ed4005a --- /dev/null +++ b/sbin/ipf/libipf/printdstl_live.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ipl.h" + + +/* + * Because the ipf_dstnode_t can vary in size because of the interface name, + * the size may be larger than just sizeof(). + */ +ippool_dst_t * +printdstl_live(d, fd, name, opts, fields) + ippool_dst_t *d; + int fd; + char *name; + int opts; + wordtab_t *fields; +{ + ipf_dstnode_t *entry, *zero; + ipflookupiter_t iter; + int printed, last; + ipfobj_t obj; + + if ((name != NULL) && strncmp(name, d->ipld_name, FR_GROUPLEN)) + return d->ipld_next; + + entry = calloc(1, sizeof(*entry) + 64); + if (entry == NULL) + return d->ipld_next; + zero = calloc(1, sizeof(*zero) + 64); + if (zero == NULL) { + free(entry); + return d->ipld_next; + } + + if (fields == NULL) + printdstlistdata(d, opts); + + if ((d->ipld_flags & IPHASH_DELETE) != 0) + PRINTF("# "); + + if ((opts & OPT_DEBUG) == 0) + PRINTF("\t{"); + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_LOOKUPITER; + obj.ipfo_ptr = &iter; + obj.ipfo_size = sizeof(iter); + + iter.ili_data = entry; + iter.ili_type = IPLT_DSTLIST; + iter.ili_otype = IPFLOOKUPITER_NODE; + iter.ili_ival = IPFGENITER_LOOKUP; + iter.ili_unit = d->ipld_unit; + strncpy(iter.ili_name, d->ipld_name, FR_GROUPLEN); + + last = 0; + printed = 0; + + while (!last && (ioctl(fd, SIOCLOOKUPITER, &obj) == 0)) { + if (entry->ipfd_next == NULL) + last = 1; + if (bcmp((char *)zero, (char *)entry, sizeof(*zero)) == 0) + break; + (void) printdstlistnode(entry, bcopywrap, opts, fields); + printed++; + } + + (void) ioctl(fd, SIOCIPFDELTOK, &iter.ili_key); + free(entry); + free(zero); + + if (printed == 0) + putchar(';'); + + if ((opts & OPT_DEBUG) == 0) + PRINTF(" };\n"); + return d->ipld_next; +} diff --git a/sbin/ipf/libipf/printdstlist.c b/sbin/ipf/libipf/printdstlist.c new file mode 100644 index 000000000000..829a1d2e69ce --- /dev/null +++ b/sbin/ipf/libipf/printdstlist.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +ippool_dst_t * +printdstlist(pp, copyfunc, name, opts, nodes, fields) + ippool_dst_t *pp; + copyfunc_t copyfunc; + char *name; + int opts; + ipf_dstnode_t *nodes; + wordtab_t *fields; +{ + ipf_dstnode_t *node; + ippool_dst_t dst; + + if ((*copyfunc)(pp, &dst, sizeof(dst))) + return NULL; + + if ((name != NULL) && strncmp(name, dst.ipld_name, FR_GROUPLEN)) + return dst.ipld_next; + + if (fields == NULL) + printdstlistdata(&dst, opts); + + if ((dst.ipld_flags & IPDST_DELETE) != 0) + PRINTF("# "); + if ((opts & OPT_DEBUG) == 0) + PRINTF("\t{"); + + if (nodes == NULL) { + putchar(';'); + } else { + for (node = nodes; node != NULL; ) { + ipf_dstnode_t *n; + + n = calloc(1, node->ipfd_size); + if (n == NULL) + break; + if ((*copyfunc)(node, n, node->ipfd_size)) { + free(n); + return NULL; + } + + node = printdstlistnode(n, bcopywrap, opts, fields); + + free(n); + } + } + + if ((opts & OPT_DEBUG) == 0) + PRINTF(" };\n"); + + return dst.ipld_next; +} diff --git a/sbin/ipf/libipf/printdstlistdata.c b/sbin/ipf/libipf/printdstlistdata.c new file mode 100644 index 000000000000..8b55afdb57c7 --- /dev/null +++ b/sbin/ipf/libipf/printdstlistdata.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" +#include <ctype.h> + + +void +printdstlistdata(pool, opts) + ippool_dst_t *pool; + int opts; +{ + + if ((opts & OPT_DEBUG) == 0) { + if ((pool->ipld_flags & IPDST_DELETE) != 0) + PRINTF("# "); + PRINTF("pool "); + } else { + if ((pool->ipld_flags & IPDST_DELETE) != 0) + PRINTF("# "); + PRINTF("Name: %s\tRole: ", pool->ipld_name); + } + + printunit(pool->ipld_unit); + + if ((opts & OPT_DEBUG) == 0) { + PRINTF("/dstlist (name %s;", pool->ipld_name); + if (pool->ipld_policy != IPLDP_NONE) { + PRINTF(" policy "); + printdstlistpolicy(pool->ipld_policy); + putchar(';'); + } + PRINTF(")\n"); + } else { + putchar(' '); + + PRINTF("\tReferences: %d\n", pool->ipld_ref); + if ((pool->ipld_flags & IPDST_DELETE) != 0) + PRINTF("# "); + PRINTF("Policy: \n"); + printdstlistpolicy(pool->ipld_policy); + PRINTF("\n\tNodes Starting at %p\n", pool->ipld_dests); + } +} diff --git a/sbin/ipf/libipf/printdstlistnode.c b/sbin/ipf/libipf/printdstlistnode.c new file mode 100644 index 000000000000..898986d1c066 --- /dev/null +++ b/sbin/ipf/libipf/printdstlistnode.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +ipf_dstnode_t * +printdstlistnode(inp, copyfunc, opts, fields) + ipf_dstnode_t *inp; + copyfunc_t copyfunc; + int opts; + wordtab_t *fields; +{ + ipf_dstnode_t node, *np; + int i; +#ifdef USE_INET6 + char buf[INET6_ADDRSTRLEN+1]; + const char *str; +#endif + + if ((*copyfunc)(inp, &node, sizeof(node))) + return NULL; + + np = calloc(1, node.ipfd_size); + if (np == NULL) + return node.ipfd_next; + if ((*copyfunc)(inp, np, node.ipfd_size)) + return NULL; + + if (fields != NULL) { + for (i = 0; fields[i].w_value != 0; i++) { + printpoolfield(np, IPLT_DSTLIST, i); + if (fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else if ((opts & OPT_DEBUG) == 0) { + putchar(' '); + if (np->ipfd_dest.fd_name >= 0) + PRINTF("%s:", np->ipfd_names); + if (np->ipfd_dest.fd_addr.adf_family == AF_INET) { + printip(AF_INET, (u_32_t *)&np->ipfd_dest.fd_ip); + } else { +#ifdef USE_INET6 + str = inet_ntop(AF_INET6, &np->ipfd_dest.fd_ip6, + buf, sizeof(buf) - 1); + if (str != NULL) + PRINTF("%s", str); +#endif + } + putchar(';'); + } else { + PRINTF("Interface: [%s]/%d\n", np->ipfd_names, + np->ipfd_dest.fd_name); +#ifdef USE_INET6 + str = inet_ntop(np->ipfd_dest.fd_addr.adf_family, + &np->ipfd_dest.fd_ip6, buf, sizeof(buf) - 1); + if (str != NULL) { + PRINTF("\tAddress: %s\n", str); + } +#else + PRINTF("\tAddress: %s\n", inet_ntoa(np->ipfd_dest.fd_ip)); +#endif + PRINTF( +#ifdef USE_QUAD_T + "\t\tStates %d\tRef %d\tName [%s]\tUid %d\n", +#else + "\t\tStates %d\tRef %d\tName [%s]\tUid %d\n", +#endif + np->ipfd_states, np->ipfd_ref, + np->ipfd_names, np->ipfd_uid); + } + free(np); + return node.ipfd_next; +} diff --git a/sbin/ipf/libipf/printdstlistpolicy.c b/sbin/ipf/libipf/printdstlistpolicy.c new file mode 100644 index 000000000000..4873b95e207f --- /dev/null +++ b/sbin/ipf/libipf/printdstlistpolicy.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +void +printdstlistpolicy(policy) + ippool_policy_t policy; +{ + switch (policy) + { + case IPLDP_NONE : + PRINTF("none"); + break; + case IPLDP_ROUNDROBIN : + PRINTF("round-robin"); + break; + case IPLDP_CONNECTION : + PRINTF("weighting connection"); + break; + case IPLDP_RANDOM : + PRINTF("random"); + break; + default : + break; + } +} diff --git a/sbin/ipf/libipf/printfieldhdr.c b/sbin/ipf/libipf/printfieldhdr.c new file mode 100644 index 000000000000..f796f6fed62f --- /dev/null +++ b/sbin/ipf/libipf/printfieldhdr.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: printfieldhdr.c,v 1.5.2.3 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" +#include <ctype.h> + + +void +printfieldhdr(words, field) + wordtab_t *words, *field; +{ + wordtab_t *w; + char *s, *t; + + if (field->w_value == -2) { + for (w = words; w->w_word != NULL; ) { + if (w->w_value > 0) { + printfieldhdr(words, w); + w++; + if (w->w_value > 0) + putchar('\t'); + } else { + w++; + } + } + return; + } + + for (w = words; w->w_word != NULL; w++) { + if (w->w_value == field->w_value) { + if (w->w_word == field->w_word) { + s = strdup(w->w_word); + } else { + s = NULL; + } + + if ((w->w_word != field->w_word) || (s == NULL)) { + PRINTF("%s", field->w_word); + } else { + for (t = s; *t != '\0'; t++) { + if (ISALPHA(*t) && ISLOWER(*t)) + *t = TOUPPER(*t); + } + PRINTF("%s", s); + free(s); + } + } + } +} diff --git a/sbin/ipf/libipf/printfr.c b/sbin/ipf/libipf/printfr.c new file mode 100644 index 000000000000..9883df48f8f4 --- /dev/null +++ b/sbin/ipf/libipf/printfr.c @@ -0,0 +1,473 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +/* + * print the filter structure in a useful way + */ +void +printfr(fp, iocfunc) + struct frentry *fp; + ioctlfunc_t iocfunc; +{ + struct protoent *p; + u_short sec[2]; + u_32_t type; + int pr, af; + char *s; + int hash; + + pr = -2; + type = fp->fr_type & ~FR_T_BUILTIN; + + if ((fp->fr_type & FR_T_BUILTIN) != 0) + PRINTF("# Builtin: "); + + if (fp->fr_collect != 0) + PRINTF("%u ", fp->fr_collect); + + if (fp->fr_type == FR_T_CALLFUNC) { + ; + } else if (fp->fr_func != NULL) { + PRINTF("call"); + if ((fp->fr_flags & FR_CALLNOW) != 0) + PRINTF(" now"); + s = kvatoname(fp->fr_func, iocfunc); + PRINTF(" %s/%u", s ? s : "?", fp->fr_arg); + } else if (FR_ISPASS(fp->fr_flags)) + PRINTF("pass"); + else if (FR_ISBLOCK(fp->fr_flags)) { + PRINTF("block"); + } else if ((fp->fr_flags & FR_LOGMASK) == FR_LOG) { + printlog(fp); + } else if (FR_ISACCOUNT(fp->fr_flags)) + PRINTF("count"); + else if (FR_ISAUTH(fp->fr_flags)) + PRINTF("auth"); + else if (FR_ISPREAUTH(fp->fr_flags)) + PRINTF("preauth"); + else if (FR_ISNOMATCH(fp->fr_flags)) + PRINTF("nomatch"); + else if (FR_ISDECAPS(fp->fr_flags)) + PRINTF("decapsulate"); + else if (FR_ISSKIP(fp->fr_flags)) + PRINTF("skip %u", fp->fr_arg); + else { + PRINTF("%x", fp->fr_flags); + } + if (fp->fr_flags & FR_RETICMP) { + if ((fp->fr_flags & FR_RETMASK) == FR_FAKEICMP) + PRINTF(" return-icmp-as-dest"); + else if ((fp->fr_flags & FR_RETMASK) == FR_RETICMP) + PRINTF(" return-icmp"); + if (fp->fr_icode) { + if (fp->fr_icode <= MAX_ICMPCODE) + PRINTF("(%s)", + icmpcodes[(int)fp->fr_icode]); + else + PRINTF("(%d)", fp->fr_icode); + } + } else if ((fp->fr_flags & FR_RETMASK) == FR_RETRST) + PRINTF(" return-rst"); + + if (fp->fr_flags & FR_OUTQUE) + PRINTF(" out "); + else if (fp->fr_flags & FR_INQUE) + PRINTF(" in "); + + if (((fp->fr_flags & FR_LOGB) == FR_LOGB) || + ((fp->fr_flags & FR_LOGP) == FR_LOGP)) { + printlog(fp); + putchar(' '); + } + + if (fp->fr_flags & FR_QUICK) + PRINTF("quick "); + + if (fp->fr_ifnames[0] != -1) { + printifname("on ", fp->fr_names + fp->fr_ifnames[0], + fp->fr_ifa); + if (fp->fr_ifnames[1] != -1 && + strcmp(fp->fr_names + fp->fr_ifnames[1], "*")) + printifname(",", fp->fr_names + fp->fr_ifnames[1], + fp->fr_ifas[1]); + putchar(' '); + } + + if (fp->fr_tif.fd_name != -1) + print_toif(fp->fr_family, "to", fp->fr_names, &fp->fr_tif); + if (fp->fr_dif.fd_name != -1) + print_toif(fp->fr_family, "dup-to", fp->fr_names, + &fp->fr_dif); + if (fp->fr_rif.fd_name != -1) + print_toif(fp->fr_family, "reply-to", fp->fr_names, + &fp->fr_rif); + if (fp->fr_flags & FR_FASTROUTE) + PRINTF("fastroute "); + + if ((fp->fr_ifnames[2] != -1 && + strcmp(fp->fr_names + fp->fr_ifnames[2], "*")) || + (fp->fr_ifnames[3] != -1 && + strcmp(fp->fr_names + fp->fr_ifnames[3], "*"))) { + if (fp->fr_flags & FR_OUTQUE) + PRINTF("in-via "); + else + PRINTF("out-via "); + + if (fp->fr_ifnames[2] != -1) { + printifname("", fp->fr_names + fp->fr_ifnames[2], + fp->fr_ifas[2]); + if (fp->fr_ifnames[3] != -1) { + printifname(",", + fp->fr_names + fp->fr_ifnames[3], + fp->fr_ifas[3]); + } + putchar(' '); + } + } + + if (fp->fr_family == AF_INET) { + PRINTF("inet "); + af = AF_INET; +#ifdef USE_INET6 + } else if (fp->fr_family == AF_INET6) { + PRINTF("inet6 "); + af = AF_INET6; +#endif + } else { + af = -1; + } + + if (type == FR_T_IPF) { + if (fp->fr_mip.fi_tos) + PRINTF("tos %#x ", fp->fr_tos); + if (fp->fr_mip.fi_ttl) + PRINTF("ttl %d ", fp->fr_ttl); + if (fp->fr_flx & FI_TCPUDP) { + PRINTF("proto tcp/udp "); + pr = -1; + } else if (fp->fr_mip.fi_p) { + pr = fp->fr_ip.fi_p; + p = getprotobynumber(pr); + PRINTF("proto "); + printproto(p, pr, NULL); + putchar(' '); + } + } + + switch (type) + { + case FR_T_NONE : + PRINTF("all"); + break; + + case FR_T_IPF : + PRINTF("from %s", fp->fr_flags & FR_NOTSRCIP ? "!" : ""); + printaddr(af, fp->fr_satype, fp->fr_names, fp->fr_ifnames[0], + &fp->fr_src.s_addr, &fp->fr_smsk.s_addr); + if (fp->fr_scmp) + printportcmp(pr, &fp->fr_tuc.ftu_src); + + PRINTF(" to %s", fp->fr_flags & FR_NOTDSTIP ? "!" : ""); + printaddr(af, fp->fr_datype, fp->fr_names, fp->fr_ifnames[0], + &fp->fr_dst.s_addr, &fp->fr_dmsk.s_addr); + if (fp->fr_dcmp) + printportcmp(pr, &fp->fr_tuc.ftu_dst); + + if (((fp->fr_proto == IPPROTO_ICMP) || + (fp->fr_proto == IPPROTO_ICMPV6)) && fp->fr_icmpm) { + int type = fp->fr_icmp, code; + char *name; + + type = ntohs(fp->fr_icmp); + code = type & 0xff; + type /= 256; + name = icmptypename(fp->fr_family, type); + if (name == NULL) + PRINTF(" icmp-type %d", type); + else + PRINTF(" icmp-type %s", name); + if (ntohs(fp->fr_icmpm) & 0xff) + PRINTF(" code %d", code); + } + if ((fp->fr_proto == IPPROTO_TCP) && + (fp->fr_tcpf || fp->fr_tcpfm)) { + PRINTF(" flags "); + printtcpflags(fp->fr_tcpf, fp->fr_tcpfm); + } + break; + + case FR_T_BPFOPC : + { + fakebpf_t *fb; + int i; + + PRINTF("bpf-v%d { \"", fp->fr_family); + i = fp->fr_dsize / sizeof(*fb); + + for (fb = fp->fr_data, s = ""; i; i--, fb++, s = " ") + PRINTF("%s%#x %#x %#x %#x", s, fb->fb_c, fb->fb_t, + fb->fb_f, fb->fb_k); + + PRINTF("\" }"); + break; + } + + case FR_T_COMPIPF : + break; + + case FR_T_CALLFUNC : + PRINTF("call function at %p", fp->fr_data); + break; + + case FR_T_IPFEXPR : + PRINTF("exp { \""); + printipfexpr(fp->fr_data); + PRINTF("\" } "); + break; + + default : + PRINTF("[unknown filter type %#x]", fp->fr_type); + break; + } + + if ((type == FR_T_IPF) && + ((fp->fr_flx & FI_WITH) || (fp->fr_mflx & FI_WITH) || + fp->fr_optbits || fp->fr_optmask || + fp->fr_secbits || fp->fr_secmask)) { + char *comma = " "; + + PRINTF(" with"); + if (fp->fr_optbits || fp->fr_optmask || + fp->fr_secbits || fp->fr_secmask) { + sec[0] = fp->fr_secmask; + sec[1] = fp->fr_secbits; + if (fp->fr_family == AF_INET) + optprint(sec, fp->fr_optmask, fp->fr_optbits); +#ifdef USE_INET6 + else + optprintv6(sec, fp->fr_optmask, + fp->fr_optbits); +#endif + } else if (fp->fr_mflx & FI_OPTIONS) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_OPTIONS)) + PRINTF("not "); + PRINTF("ipopts"); + comma = ","; + } + if (fp->fr_mflx & FI_SHORT) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_SHORT)) + PRINTF("not "); + PRINTF("short"); + comma = ","; + } + if (fp->fr_mflx & FI_FRAG) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_FRAG)) + PRINTF("not "); + PRINTF("frag"); + comma = ","; + } + if (fp->fr_mflx & FI_FRAGBODY) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_FRAGBODY)) + PRINTF("not "); + PRINTF("frag-body"); + comma = ","; + } + if (fp->fr_mflx & FI_NATED) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_NATED)) + PRINTF("not "); + PRINTF("nat"); + comma = ","; + } + if (fp->fr_mflx & FI_LOWTTL) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_LOWTTL)) + PRINTF("not "); + PRINTF("lowttl"); + comma = ","; + } + if (fp->fr_mflx & FI_BAD) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_BAD)) + PRINTF("not "); + PRINTF("bad"); + comma = ","; + } + if (fp->fr_mflx & FI_BADSRC) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_BADSRC)) + PRINTF("not "); + PRINTF("bad-src"); + comma = ","; + } + if (fp->fr_mflx & FI_BADNAT) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_BADNAT)) + PRINTF("not "); + PRINTF("bad-nat"); + comma = ","; + } + if (fp->fr_mflx & FI_OOW) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_OOW)) + PRINTF("not "); + PRINTF("oow"); + comma = ","; + } + if (fp->fr_mflx & FI_MBCAST) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_MBCAST)) + PRINTF("not "); + PRINTF("mbcast"); + comma = ","; + } + if (fp->fr_mflx & FI_BROADCAST) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_BROADCAST)) + PRINTF("not "); + PRINTF("bcast"); + comma = ","; + } + if (fp->fr_mflx & FI_MULTICAST) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_MULTICAST)) + PRINTF("not "); + PRINTF("mcast"); + comma = ","; + } + if (fp->fr_mflx & FI_STATE) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_STATE)) + PRINTF("not "); + PRINTF("state"); + comma = ","; + } + if (fp->fr_mflx & FI_V6EXTHDR) { + fputs(comma, stdout); + if (!(fp->fr_flx & FI_V6EXTHDR)) + PRINTF("not "); + PRINTF("v6hdrs"); + comma = ","; + } + } + + if (fp->fr_flags & FR_KEEPSTATE) { + host_track_t *src = &fp->fr_srctrack; + PRINTF(" keep state"); + if ((fp->fr_flags & (FR_STSTRICT|FR_NEWISN| + FR_NOICMPERR|FR_STATESYNC)) || + (fp->fr_statemax != 0) || (fp->fr_age[0] != 0) || + (src->ht_max_nodes != 0)) { + char *comma = ""; + PRINTF(" ("); + if (fp->fr_statemax != 0) { + PRINTF("limit %u", fp->fr_statemax); + comma = ","; + } + if (src->ht_max_nodes != 0) { + PRINTF("%smax-nodes %d", comma, + src->ht_max_nodes); + if (src->ht_max_per_node) + PRINTF(", max-per-src %d/%d", + src->ht_max_per_node, + src->ht_netmask); + comma = ","; + } + if (fp->fr_flags & FR_STSTRICT) { + PRINTF("%sstrict", comma); + comma = ","; + } + if (fp->fr_flags & FR_STLOOSE) { + PRINTF("%sloose", comma); + comma = ","; + } + if (fp->fr_flags & FR_NEWISN) { + PRINTF("%snewisn", comma); + comma = ","; + } + if (fp->fr_flags & FR_NOICMPERR) { + PRINTF("%sno-icmp-err", comma); + comma = ","; + } + if (fp->fr_flags & FR_STATESYNC) { + PRINTF("%ssync", comma); + comma = ","; + } + if (fp->fr_age[0] || fp->fr_age[1]) + PRINTF("%sage %d/%d", comma, fp->fr_age[0], + fp->fr_age[1]); + PRINTF(")"); + } + } + if (fp->fr_flags & FR_KEEPFRAG) { + PRINTF(" keep frags"); + if (fp->fr_flags & (FR_FRSTRICT)) { + PRINTF(" ("); + if (fp->fr_flags & FR_FRSTRICT) + PRINTF("strict"); + PRINTF(")"); + + } + } + if (fp->fr_isc != (struct ipscan *)-1) { + if (fp->fr_isctag != -1) + PRINTF(" scan %s", fp->fr_isctag + fp->fr_names); + else + PRINTF(" scan *"); + } + if (fp->fr_grhead != -1) + PRINTF(" head %s", fp->fr_names + fp->fr_grhead); + if (fp->fr_group != -1) + PRINTF(" group %s", fp->fr_names + fp->fr_group); + if (fp->fr_logtag != FR_NOLOGTAG || *fp->fr_nattag.ipt_tag) { + char *s = ""; + + PRINTF(" set-tag("); + if (fp->fr_logtag != FR_NOLOGTAG) { + PRINTF("log=%u", fp->fr_logtag); + s = ", "; + } + if (*fp->fr_nattag.ipt_tag) { + PRINTF("%snat=%-.*s", s, IPFTAG_LEN, + fp->fr_nattag.ipt_tag); + } + PRINTF(")"); + } + + if (fp->fr_pps) + PRINTF(" pps %d", fp->fr_pps); + + if (fp->fr_comment != -1) + PRINTF(" comment \"%s\"", fp->fr_names + fp->fr_comment); + + hash = 0; + if ((fp->fr_flags & FR_KEEPSTATE) && (opts & OPT_VERBOSE)) { + PRINTF(" # count %d", fp->fr_statecnt); + if (fp->fr_die != 0) + PRINTF(" rule-ttl %u", fp->fr_die); + hash = 1; + } else if (fp->fr_die != 0) { + PRINTF(" # rule-ttl %u", fp->fr_die); + hash = 1; + } + if (opts & OPT_DEBUG) { + if (hash == 0) + putchar('#'); + PRINTF(" ref %d", fp->fr_ref); + } + (void)putchar('\n'); +} diff --git a/sbin/ipf/libipf/printfraginfo.c b/sbin/ipf/libipf/printfraginfo.c new file mode 100644 index 000000000000..dd2966fc05b5 --- /dev/null +++ b/sbin/ipf/libipf/printfraginfo.c @@ -0,0 +1,42 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" +#include "kmem.h" + + +void +printfraginfo(prefix, ifr) + char *prefix; + struct ipfr *ifr; +{ + frentry_t fr; + int family; + + PRINTF("%s", prefix); + if (ifr->ipfr_v == 6) { + PRINTF("inet6"); + family = AF_INET6; + } else { + PRINTF("inet"); + family = AF_INET; + } + fr.fr_flags = 0xffffffff; + + PRINTF(" %s -> ", hostname(family, &ifr->ipfr_src)); +/* + if (kmemcpy((char *)&fr, (u_long)ifr->ipfr_rule, + sizeof(fr)) == -1) + return; + */ + PRINTF("%s id %x ttl %lu pr %d pkts %u bytes %u seen0 %d ref %d\n", + hostname(family, &ifr->ipfr_dst), ifr->ipfr_id, + ifr->ipfr_ttl, ifr->ipfr_p, ifr->ipfr_pkts, ifr->ipfr_bytes, + ifr->ipfr_seen0, ifr->ipfr_ref); +} diff --git a/sbin/ipf/libipf/printhash.c b/sbin/ipf/libipf/printhash.c new file mode 100644 index 000000000000..37796620fc1d --- /dev/null +++ b/sbin/ipf/libipf/printhash.c @@ -0,0 +1,58 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +iphtable_t * +printhash(hp, copyfunc, name, opts, fields) + iphtable_t *hp; + copyfunc_t copyfunc; + char *name; + int opts; + wordtab_t *fields; +{ + iphtent_t *ipep, **table; + iphtable_t iph; + int printed; + size_t sz; + + if ((*copyfunc)((char *)hp, (char *)&iph, sizeof(iph))) + return NULL; + + if ((name != NULL) && strncmp(name, iph.iph_name, FR_GROUPLEN)) + return iph.iph_next; + + if (fields == NULL) + printhashdata(hp, opts); + + if ((hp->iph_flags & IPHASH_DELETE) != 0) + PRINTF("# "); + + if ((opts & OPT_DEBUG) == 0) + PRINTF("\t{"); + + sz = iph.iph_size * sizeof(*table); + table = malloc(sz); + if ((*copyfunc)((char *)iph.iph_table, (char *)table, sz)) + return NULL; + + for (printed = 0, ipep = iph.iph_list; ipep != NULL; ) { + ipep = printhashnode(&iph, ipep, copyfunc, opts, fields); + printed++; + } + if (printed == 0) + putchar(';'); + + free(table); + + if ((opts & OPT_DEBUG) == 0) + PRINTF(" };\n"); + + return iph.iph_next; +} diff --git a/sbin/ipf/libipf/printhash_live.c b/sbin/ipf/libipf/printhash_live.c new file mode 100644 index 000000000000..6003a63f60d2 --- /dev/null +++ b/sbin/ipf/libipf/printhash_live.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ipl.h" + + +iphtable_t * +printhash_live(hp, fd, name, opts, fields) + iphtable_t *hp; + int fd; + char *name; + int opts; + wordtab_t *fields; +{ + iphtent_t entry, zero; + ipflookupiter_t iter; + int last, printed; + ipfobj_t obj; + + if ((name != NULL) && strncmp(name, hp->iph_name, FR_GROUPLEN)) + return hp->iph_next; + + if (fields == NULL) + printhashdata(hp, opts); + + if ((hp->iph_flags & IPHASH_DELETE) != 0) + PRINTF("# "); + + if ((opts & OPT_DEBUG) == 0) + PRINTF("\t{"); + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_LOOKUPITER; + obj.ipfo_ptr = &iter; + obj.ipfo_size = sizeof(iter); + + iter.ili_data = &entry; + iter.ili_type = IPLT_HASH; + iter.ili_otype = IPFLOOKUPITER_NODE; + iter.ili_ival = IPFGENITER_LOOKUP; + iter.ili_unit = hp->iph_unit; + strncpy(iter.ili_name, hp->iph_name, FR_GROUPLEN); + + last = 0; + printed = 0; + bzero((char *)&zero, sizeof(zero)); + + while (!last && (ioctl(fd, SIOCLOOKUPITER, &obj) == 0)) { + if (entry.ipe_next == NULL) + last = 1; + if (bcmp(&zero, &entry, sizeof(zero)) == 0) + break; + (void) printhashnode(hp, &entry, bcopywrap, opts, fields); + printed++; + } + if (last == 0) + ipferror(fd, "walking hash nodes"); + + if (printed == 0) + putchar(';'); + + if ((opts & OPT_DEBUG) == 0) + PRINTF(" };\n"); + return hp->iph_next; +} diff --git a/sbin/ipf/libipf/printhashdata.c b/sbin/ipf/libipf/printhashdata.c new file mode 100644 index 000000000000..ea2d41636e46 --- /dev/null +++ b/sbin/ipf/libipf/printhashdata.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" +#include <ctype.h> + + +void +printhashdata(hp, opts) + iphtable_t *hp; + int opts; +{ + + if ((opts & OPT_DEBUG) == 0) { + if ((hp->iph_type & IPHASH_ANON) == IPHASH_ANON) + PRINTF("# 'anonymous' table refs %d\n", hp->iph_ref); + if ((hp->iph_flags & IPHASH_DELETE) == IPHASH_DELETE) + PRINTF("# "); + switch (hp->iph_type & ~IPHASH_ANON) + { + case IPHASH_LOOKUP : + PRINTF("table"); + break; + case IPHASH_GROUPMAP : + PRINTF("group-map"); + if (hp->iph_flags & FR_INQUE) + PRINTF(" in"); + else if (hp->iph_flags & FR_OUTQUE) + PRINTF(" out"); + else + PRINTF(" ???"); + break; + default : + PRINTF("%#x", hp->iph_type); + break; + } + PRINTF(" role="); + } else { + PRINTF("Hash Table %s: %s", + ISDIGIT(*hp->iph_name) ? "Number" : "Name", + hp->iph_name); + if ((hp->iph_type & IPHASH_ANON) == IPHASH_ANON) + PRINTF("(anon)"); + putchar(' '); + PRINTF("Role: "); + } + + printunit(hp->iph_unit); + + if ((opts & OPT_DEBUG) == 0) { + if ((hp->iph_type & ~IPHASH_ANON) == IPHASH_LOOKUP) + PRINTF(" type=hash"); + PRINTF(" %s=%s size=%lu", + ISDIGIT(*hp->iph_name) ? "number" : "name", + hp->iph_name, (u_long)hp->iph_size); + if (hp->iph_seed != 0) + PRINTF(" seed=%lu", hp->iph_seed); + putchar('\n'); + } else { + PRINTF(" Type: "); + switch (hp->iph_type & ~IPHASH_ANON) + { + case IPHASH_LOOKUP : + PRINTF("lookup"); + break; + case IPHASH_GROUPMAP : + PRINTF("groupmap Group. %s", hp->iph_name); + break; + default : + break; + } + + putchar('\n'); + PRINTF("\t\tSize: %lu\tSeed: %lu", + (u_long)hp->iph_size, hp->iph_seed); + PRINTF("\tRef. Count: %d\tMasks: %#x\n", hp->iph_ref, + hp->iph_maskset[0]); + } + + if ((opts & OPT_DEBUG) != 0) { + struct in_addr m; + int i; + + for (i = 0; i < 32; i++) { + if ((1 << i) & hp->iph_maskset[0]) { + ntomask(AF_INET, i, &m.s_addr); + PRINTF("\t\tMask: %s\n", inet_ntoa(m)); + } + } + } +} diff --git a/sbin/ipf/libipf/printhashnode.c b/sbin/ipf/libipf/printhashnode.c new file mode 100644 index 000000000000..8a45d86c5134 --- /dev/null +++ b/sbin/ipf/libipf/printhashnode.c @@ -0,0 +1,98 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +iphtent_t * +printhashnode(iph, ipep, copyfunc, opts, fields) + iphtable_t *iph; + iphtent_t *ipep; + copyfunc_t copyfunc; + int opts; + wordtab_t *fields; +{ + iphtent_t ipe; + u_int hv; + int i; + + if ((*copyfunc)(ipep, &ipe, sizeof(ipe))) + return NULL; + + hv = IPE_V4_HASH_FN(ipe.ipe_addr.i6[0], ipe.ipe_mask.i6[0], + iph->iph_size); + + if (fields != NULL) { + for (i = 0; fields[i].w_value != 0; i++) { + printpoolfield(&ipe, IPLT_HASH, i); + if (fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else if ((opts & OPT_DEBUG) != 0) { +#ifdef USE_INET6 + if (ipe.ipe_family == AF_INET6) { + char buf[INET6_ADDRSTRLEN + 1]; + const char *str; + + buf[0] = '\0'; + str = inet_ntop(AF_INET6, &ipe.ipe_addr.in6, + buf, sizeof(buf) - 1); + if (str == NULL) + str = "???"; + PRINTF("\t%d\tAddress: %s", hv, str); + printmask(ipe.ipe_family, (u_32_t *)&ipe.ipe_mask.in4_addr); + PRINTF("\tRef. Count: %d\tGroup: %s\n", ipe.ipe_ref, + ipe.ipe_group); +#ifdef USE_QUAD_T + PRINTF("\tHits: %"PRIu64"\tBytes: %"PRIu64"\n", + ipe.ipe_hits, ipe.ipe_bytes); +#else + PRINTF("\tHits: %lu\tBytes: %lu\n", + ipe.ipe_hits, ipe.ipe_bytes); +#endif /* USE_QUAD_T */ + } else if (ipe.ipe_family == AF_INET) { +#else + if (ipe.ipe_family == AF_INET) { +#endif /* USE_INET6 */ + PRINTF("\t%d\tAddress: %s", hv, + inet_ntoa(ipe.ipe_addr.in4)); + printmask(ipe.ipe_family, (u_32_t *)&ipe.ipe_mask.in4_addr); + PRINTF("\tRef. Count: %d\tGroup: %s\n", ipe.ipe_ref, + ipe.ipe_group); +#ifdef USE_QUAD_T + PRINTF("\tHits: %"PRIu64"\tBytes: %"PRIu64"\n", + ipe.ipe_hits, ipe.ipe_bytes); +#else + PRINTF("\tHits: %lu\tBytes: %lu\n", + ipe.ipe_hits, ipe.ipe_bytes); +#endif /* USE_QUAD_T */ + } else { + PRINTF("\tAddress: family: %d\n", + ipe.ipe_family); + } + } else { + putchar(' '); + printip(ipe.ipe_family, (u_32_t *)&ipe.ipe_addr.in4_addr); + printmask(ipe.ipe_family, (u_32_t *)&ipe.ipe_mask.in4_addr); + if (ipe.ipe_value != 0) { + switch (iph->iph_type & ~IPHASH_ANON) + { + case IPHASH_GROUPMAP : + if (strncmp(ipe.ipe_group, iph->iph_name, + FR_GROUPLEN)) + PRINTF(", group=%s", ipe.ipe_group); + break; + } + } + putchar(';'); + } + + ipep = ipe.ipe_next; + return ipep; +} diff --git a/sbin/ipf/libipf/printhost.c b/sbin/ipf/libipf/printhost.c new file mode 100644 index 000000000000..009a9bb1803e --- /dev/null +++ b/sbin/ipf/libipf/printhost.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: printhost.c,v 1.3.2.2 2012/07/22 08:04:24 darren_r Exp $ + */ + +#include "ipf.h" + + +void +printhost(family, addr) + int family; + u_32_t *addr; +{ +#ifdef USE_INET6 + char ipbuf[64]; +#else + struct in_addr ipa; +#endif + + if ((family == -1) || !*addr) + PRINTF("any"); + else { +#ifdef USE_INET6 + void *ptr = addr; + + PRINTF("%s", inet_ntop(family, ptr, ipbuf, sizeof(ipbuf))); +#else + ipa.s_addr = *addr; + PRINTF("%s", inet_ntoa(ipa)); +#endif + } +} diff --git a/sbin/ipf/libipf/printhostmap.c b/sbin/ipf/libipf/printhostmap.c new file mode 100644 index 000000000000..714bc416932b --- /dev/null +++ b/sbin/ipf/libipf/printhostmap.c @@ -0,0 +1,31 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +void +printhostmap(hmp, hv) + hostmap_t *hmp; + u_int hv; +{ + + printactiveaddress(hmp->hm_v, "%s", &hmp->hm_osrcip6, NULL); + putchar(','); + printactiveaddress(hmp->hm_v, "%s", &hmp->hm_odstip6, NULL); + PRINTF(" -> "); + printactiveaddress(hmp->hm_v, "%s", &hmp->hm_nsrcip6, NULL); + putchar(','); + printactiveaddress(hmp->hm_v, "%s", &hmp->hm_ndstip6, NULL); + putchar(' '); + PRINTF("(use = %d", hmp->hm_ref); + if (opts & OPT_VERBOSE) + PRINTF(" hv = %u", hv); + printf(")\n"); +} diff --git a/sbin/ipf/libipf/printhostmask.c b/sbin/ipf/libipf/printhostmask.c new file mode 100644 index 000000000000..b1e41f9c0a87 --- /dev/null +++ b/sbin/ipf/libipf/printhostmask.c @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void +printhostmask(family, addr, mask) + int family; + u_32_t *addr, *mask; +{ +#ifdef USE_INET6 + char ipbuf[64]; +#else + struct in_addr ipa; +#endif + + if ((family == -1) || ((!addr || !*addr) && (!mask || !*mask))) + PRINTF("any"); + else { +#ifdef USE_INET6 + void *ptr = addr; + + PRINTF("%s", inet_ntop(family, ptr, ipbuf, sizeof(ipbuf))); +#else + ipa.s_addr = *addr; + PRINTF("%s", inet_ntoa(ipa)); +#endif + if (mask != NULL) + printmask(family, mask); + } +} diff --git a/sbin/ipf/libipf/printifname.c b/sbin/ipf/libipf/printifname.c new file mode 100644 index 000000000000..2e554d950aed --- /dev/null +++ b/sbin/ipf/libipf/printifname.c @@ -0,0 +1,22 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void +printifname(format, name, ifp) + char *format, *name; + void *ifp; +{ + PRINTF("%s%s", format, name); + if ((ifp == NULL) && strcmp(name, "-") && strcmp(name, "*")) + PRINTF("(!)"); +} diff --git a/sbin/ipf/libipf/printip.c b/sbin/ipf/libipf/printip.c new file mode 100644 index 000000000000..a0b8bd37f277 --- /dev/null +++ b/sbin/ipf/libipf/printip.c @@ -0,0 +1,43 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void +printip(family, addr) + int family; + u_32_t *addr; +{ + struct in_addr ipa; + + if (family == AF_INET) { + ipa.s_addr = *addr; + if (ntohl(ipa.s_addr) < 256) + PRINTF("%lu", (u_long)ntohl(ipa.s_addr)); + else + PRINTF("%s", inet_ntoa(ipa)); + } +#ifdef USE_INET6 + else if (family == AF_INET6) { + char buf[INET6_ADDRSTRLEN + 1]; + const char *str; + + buf[0] = '\0'; + str = inet_ntop(AF_INET6, addr, buf, sizeof(buf) - 1); + if (str != NULL) + PRINTF("%s", str); + else + PRINTF("???"); + } +#endif + else + PRINTF("?(%d)?", family); +} diff --git a/sbin/ipf/libipf/printipfexpr.c b/sbin/ipf/libipf/printipfexpr.c new file mode 100644 index 000000000000..6fb74c1e2e26 --- /dev/null +++ b/sbin/ipf/libipf/printipfexpr.c @@ -0,0 +1,199 @@ +#include "ipf.h" + +static void printport(int *); +static void printhosts(int *); +static void printsingle(int *); +#ifdef USE_INET6 +static void printhostsv6(int *); +#endif + +void +printipfexpr(array) + int *array; +{ + int i, nelems, j, not; + ipfexp_t *ipfe; + + nelems = array[0]; + + for (i = 1; i < nelems; ) { + ipfe = (ipfexp_t *)(array + i); + if (ipfe->ipfe_cmd == IPF_EXP_END) + break; + + not = ipfe->ipfe_not; + + switch (ipfe->ipfe_cmd) + { + case IPF_EXP_IP_ADDR : + PRINTF("ip.addr %s= ", not ? "!" : ""); + printhosts(array + i); + break; + + case IPF_EXP_IP_PR : + PRINTF("ip.p %s= ", not ? "!" : ""); + printsingle(array + i); + break; + + case IPF_EXP_IP_SRCADDR : + PRINTF("ip.src %s= ", not ? "!" : ""); + printhosts(array + i); + break; + + case IPF_EXP_IP_DSTADDR : + PRINTF("ip.dst %s= ", not ? "!" : ""); + printhosts(array + i); + break; + + case IPF_EXP_TCP_PORT : + PRINTF("tcp.port %s= ", not ? "!" : ""); + printport(array + i); + break; + + case IPF_EXP_TCP_DPORT : + PRINTF("tcp.dport %s= ", not ? "!" : ""); + printport(array + i); + break; + + case IPF_EXP_TCP_SPORT : + PRINTF("tcp.sport %s= ", not ? "!" : ""); + printport(array + i); + break; + + case IPF_EXP_TCP_FLAGS : + PRINTF("tcp.flags %s= ", not ? "!" : ""); + + for (j = 0; j < ipfe->ipfe_narg; ) { + printtcpflags(array[i + 4], array[i + 5]); + j += 2; + if (j < array[4]) + putchar(','); + } + break; + + case IPF_EXP_UDP_PORT : + PRINTF("udp.port %s= ", not ? "!" : ""); + printport(array + i); + break; + + case IPF_EXP_UDP_DPORT : + PRINTF("udp.dport %s= ", not ? "!" : ""); + printport(array + i); + break; + + case IPF_EXP_UDP_SPORT : + PRINTF("udp.sport %s= ", not ? "!" : ""); + printport(array + i); + break; + + case IPF_EXP_IDLE_GT : + PRINTF("idle-gt %s= ", not ? "!" : ""); + printsingle(array + i); + break; + + case IPF_EXP_TCP_STATE : + PRINTF("tcp-state %s= ", not ? "!" : ""); + printsingle(array + i); + break; + +#ifdef USE_INET6 + case IPF_EXP_IP6_ADDR : + PRINTF("ip6.addr %s= ", not ? "!" : ""); + printhostsv6(array + i); + break; + + case IPF_EXP_IP6_SRCADDR : + PRINTF("ip6.src %s= ", not ? "!" : ""); + printhostsv6(array + i); + break; + + case IPF_EXP_IP6_DSTADDR : + PRINTF("ip6.dst %s= ", not ? "!" : ""); + printhostsv6(array + i); + break; +#endif + + case IPF_EXP_END : + break; + + default : + PRINTF("#%#x,len=%d;", + ipfe->ipfe_cmd, ipfe->ipfe_narg); + } + + if (array[i] != IPF_EXP_END) + putchar(';'); + + i += ipfe->ipfe_size; + if (array[i] != IPF_EXP_END) + putchar(' '); + } +} + + +static void +printsingle(array) + int *array; +{ + ipfexp_t *ipfe = (ipfexp_t *)array; + int i; + + for (i = 0; i < ipfe->ipfe_narg; ) { + PRINTF("%d", array[i + 4]); + i++; + if (i < ipfe->ipfe_narg) + putchar(','); + } +} + + +static void +printport(array) + int *array; +{ + ipfexp_t *ipfe = (ipfexp_t *)array; + int i; + + for (i = 0; i < ipfe->ipfe_narg; ) { + PRINTF("%d", ntohs(array[i + 4])); + i++; + if (i < ipfe->ipfe_narg) + putchar(','); + } +} + + +static void +printhosts(array) + int *array; +{ + ipfexp_t *ipfe = (ipfexp_t *)array; + int i, j; + + for (i = 0, j = 0; i < ipfe->ipfe_narg; j++) { + printhostmask(AF_INET, (u_32_t *)ipfe->ipfe_arg0 + j * 2, + (u_32_t *)ipfe->ipfe_arg0 + j * 2 + 1); + i += 2; + if (i < ipfe->ipfe_narg) + putchar(','); + } +} + + +#ifdef USE_INET6 +static void +printhostsv6(array) + int *array; +{ + ipfexp_t *ipfe = (ipfexp_t *)array; + int i, j; + + for (i = 4, j= 0; i < ipfe->ipfe_size; j++) { + printhostmask(AF_INET6, (u_32_t *)ipfe->ipfe_arg0 + j * 8, + (u_32_t *)ipfe->ipfe_arg0 + j * 8 + 4); + i += 8; + if (i < ipfe->ipfe_size) + putchar(','); + } +} +#endif diff --git a/sbin/ipf/libipf/printiphdr.c b/sbin/ipf/libipf/printiphdr.c new file mode 100644 index 000000000000..fdf0f75f9079 --- /dev/null +++ b/sbin/ipf/libipf/printiphdr.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: printiphdr.c,v 1.1 2009/03/01 12:48:32 darren_r Exp $ + */ + +#include "ipf.h" + + +void +printiphdr(ip) + ip_t *ip; +{ + PRINTF("ip(v=%d,hl=%d,len=%d,tos=%#x,off=%#x,sum=%#x,src=%#x,dst=%#x", + ip->ip_v, ip->ip_hl, ntohs(ip->ip_len), ip->ip_tos, + ntohs(ip->ip_off), ntohs(ip->ip_sum), ntohl(ip->ip_src.s_addr), + ntohl(ip->ip_dst.s_addr)); +} diff --git a/sbin/ipf/libipf/printlog.c b/sbin/ipf/libipf/printlog.c new file mode 100644 index 000000000000..c5278cdfdd6a --- /dev/null +++ b/sbin/ipf/libipf/printlog.c @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +#include <syslog.h> + + +void +printlog(fp) + frentry_t *fp; +{ + char *s, *u; + + PRINTF("log"); + if (fp->fr_flags & FR_LOGBODY) + PRINTF(" body"); + if (fp->fr_flags & FR_LOGFIRST) + PRINTF(" first"); + if (fp->fr_flags & FR_LOGORBLOCK) + PRINTF(" or-block"); + if (fp->fr_loglevel != 0xffff) { + PRINTF(" level "); + s = fac_toname(fp->fr_loglevel); + if (s == NULL || *s == '\0') + s = "!!!"; + u = pri_toname(fp->fr_loglevel); + if (u == NULL || *u == '\0') + u = "!!!"; + PRINTF("%s.%s", s, u); + } +} diff --git a/sbin/ipf/libipf/printlookup.c b/sbin/ipf/libipf/printlookup.c new file mode 100644 index 000000000000..51f8d6e3b2df --- /dev/null +++ b/sbin/ipf/libipf/printlookup.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void +printlookup(base, addr, mask) + char *base; + i6addr_t *addr, *mask; +{ + char name[32]; + + switch (addr->iplookuptype) + { + case IPLT_POOL : + PRINTF("pool/"); + break; + case IPLT_HASH : + PRINTF("hash/"); + break; + case IPLT_DSTLIST : + PRINTF("dstlist/"); + break; + default : + PRINTF("lookup(%x)=", addr->iplookuptype); + break; + } + + if (addr->iplookupsubtype == 0) + PRINTF("%u", addr->iplookupnum); + else if (addr->iplookupsubtype == 1) { + strncpy(name, base + addr->iplookupname, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + PRINTF("%s", name); + } +} diff --git a/sbin/ipf/libipf/printmask.c b/sbin/ipf/libipf/printmask.c new file mode 100644 index 000000000000..365d7ffeba14 --- /dev/null +++ b/sbin/ipf/libipf/printmask.c @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void +printmask(family, mask) + int family; + u_32_t *mask; +{ + struct in_addr ipa; + int ones; + + if (family == AF_INET6) { + PRINTF("/%d", count6bits(mask)); + } else if ((ones = count4bits(*mask)) == -1) { + ipa.s_addr = *mask; + PRINTF("/%s", inet_ntoa(ipa)); + } else { + PRINTF("/%d", ones); + } +} diff --git a/sbin/ipf/libipf/printnat.c b/sbin/ipf/libipf/printnat.c new file mode 100644 index 000000000000..a94d4ee6105e --- /dev/null +++ b/sbin/ipf/libipf/printnat.c @@ -0,0 +1,353 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) + */ + +#include "ipf.h" +#include "kmem.h" + + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + + +/* + * Print out a NAT rule + */ +void +printnat(np, opts) + ipnat_t *np; + int opts; +{ + struct protoent *pr; + char *base; + int family; + int proto; + + if (np->in_v[0] == 4) + family = AF_INET; +#ifdef USE_INET6 + else if (np->in_v[0] == 6) + family = AF_INET6; +#endif + else + family = AF_UNSPEC; + + if (np->in_flags & IPN_NO) + PRINTF("no "); + + switch (np->in_redir) + { + case NAT_REDIRECT|NAT_ENCAP : + PRINTF("encap in on"); + proto = np->in_pr[0]; + break; + case NAT_MAP|NAT_ENCAP : + PRINTF("encap out on"); + proto = np->in_pr[1]; + break; + case NAT_REDIRECT|NAT_DIVERTUDP : + PRINTF("divert in on"); + proto = np->in_pr[0]; + break; + case NAT_MAP|NAT_DIVERTUDP : + PRINTF("divert out on"); + proto = np->in_pr[1]; + break; + case NAT_REDIRECT|NAT_REWRITE : + PRINTF("rewrite in on"); + proto = np->in_pr[0]; + break; + case NAT_MAP|NAT_REWRITE : + PRINTF("rewrite out on"); + proto = np->in_pr[1]; + break; + case NAT_REDIRECT : + PRINTF("rdr"); + proto = np->in_pr[0]; + break; + case NAT_MAP : + PRINTF("map"); + proto = np->in_pr[1]; + break; + case NAT_MAPBLK : + PRINTF("map-block"); + proto = np->in_pr[1]; + break; + case NAT_BIMAP : + PRINTF("bimap"); + proto = np->in_pr[0]; + break; + default : + FPRINTF(stderr, "unknown value for in_redir: %#x\n", + np->in_redir); + proto = np->in_pr[0]; + break; + } + + pr = getprotobynumber(proto); + + base = np->in_names; + if (!strcmp(base + np->in_ifnames[0], "-")) + PRINTF(" \"%s\"", base + np->in_ifnames[0]); + else + PRINTF(" %s", base + np->in_ifnames[0]); + if ((np->in_ifnames[1] != -1) && + (strcmp(base + np->in_ifnames[0], base + np->in_ifnames[1]) != 0)) { + if (!strcmp(base + np->in_ifnames[1], "-")) + PRINTF(",\"%s\"", base + np->in_ifnames[1]); + else + PRINTF(",%s", base + np->in_ifnames[1]); + } + putchar(' '); + + if (family == AF_INET6) + PRINTF("inet6 "); + + if (np->in_redir & (NAT_REWRITE|NAT_ENCAP|NAT_DIVERTUDP)) { + if ((proto != 0) || (np->in_flags & IPN_TCPUDP)) { + PRINTF("proto "); + printproto(pr, proto, np); + putchar(' '); + } + } + + if (np->in_flags & IPN_FILTER) { + if (np->in_flags & IPN_NOTSRC) + PRINTF("! "); + PRINTF("from "); + printnataddr(np->in_v[0], np->in_names, &np->in_osrc, + np->in_ifnames[0]); + if (np->in_scmp) + printportcmp(proto, &np->in_tuc.ftu_src); + + if (np->in_flags & IPN_NOTDST) + PRINTF(" !"); + PRINTF(" to "); + printnataddr(np->in_v[0], np->in_names, &np->in_odst, + np->in_ifnames[0]); + if (np->in_dcmp) + printportcmp(proto, &np->in_tuc.ftu_dst); + } + + if (np->in_redir & (NAT_ENCAP|NAT_DIVERTUDP)) { + PRINTF(" -> src "); + printnataddr(np->in_v[1], np->in_names, &np->in_nsrc, + np->in_ifnames[0]); + if ((np->in_redir & NAT_DIVERTUDP) != 0) + PRINTF(",%u", np->in_spmin); + PRINTF(" dst "); + printnataddr(np->in_v[1], np->in_names, &np->in_ndst, + np->in_ifnames[0]); + if ((np->in_redir & NAT_DIVERTUDP) != 0) + PRINTF(",%u udp", np->in_dpmin); + if ((np->in_flags & IPN_PURGE) != 0) + PRINTF(" purge"); + PRINTF(";\n"); + + } else if (np->in_redir & NAT_REWRITE) { + PRINTF(" -> src "); + if (np->in_nsrc.na_atype == FRI_LOOKUP && + np->in_nsrc.na_type == IPLT_DSTLIST) { + PRINTF("dstlist/"); + if (np->in_nsrc.na_subtype == 0) + PRINTF("%d", np->in_nsrc.na_num); + else + PRINTF("%s", base + np->in_nsrc.na_num); + } else { + printnataddr(np->in_v[1], np->in_names, &np->in_nsrc, + np->in_ifnames[0]); + } + if ((((np->in_flags & IPN_TCPUDP) != 0)) && + (np->in_spmin != 0)) { + if ((np->in_flags & IPN_FIXEDSPORT) != 0) { + PRINTF(",port = %u", np->in_spmin); + } else { + PRINTF(",%u", np->in_spmin); + if (np->in_spmax != np->in_spmin) + PRINTF("-%u", np->in_spmax); + } + } + PRINTF(" dst "); + if (np->in_ndst.na_atype == FRI_LOOKUP && + np->in_ndst.na_type == IPLT_DSTLIST) { + PRINTF("dstlist/"); + if (np->in_ndst.na_subtype == 0) + PRINTF("%d", np->in_nsrc.na_num); + else + PRINTF("%s", base + np->in_ndst.na_num); + } else { + printnataddr(np->in_v[1], np->in_names, &np->in_ndst, + np->in_ifnames[0]); + } + if ((((np->in_flags & IPN_TCPUDP) != 0)) && + (np->in_dpmin != 0)) { + if ((np->in_flags & IPN_FIXEDDPORT) != 0) { + PRINTF(",port = %u", np->in_dpmin); + } else { + PRINTF(",%u", np->in_dpmin); + if (np->in_dpmax != np->in_dpmin) + PRINTF("-%u", np->in_dpmax); + } + } + if ((np->in_flags & IPN_PURGE) != 0) + PRINTF(" purge"); + PRINTF(";\n"); + + } else if (np->in_redir == NAT_REDIRECT) { + if (!(np->in_flags & IPN_FILTER)) { + printnataddr(np->in_v[0], np->in_names, &np->in_odst, + np->in_ifnames[0]); + if (np->in_flags & IPN_TCPUDP) { + PRINTF(" port %d", np->in_odport); + if (np->in_odport != np->in_dtop) + PRINTF("-%d", np->in_dtop); + } + } + if (np->in_flags & IPN_NO) { + putchar(' '); + printproto(pr, proto, np); + PRINTF(";\n"); + return; + } + PRINTF(" -> "); + printnataddr(np->in_v[1], np->in_names, &np->in_ndst, + np->in_ifnames[0]); + if (np->in_flags & IPN_TCPUDP) { + if ((np->in_flags & IPN_FIXEDDPORT) != 0) + PRINTF(" port = %d", np->in_dpmin); + else { + PRINTF(" port %d", np->in_dpmin); + if (np->in_dpmin != np->in_dpmax) + PRINTF("-%d", np->in_dpmax); + } + } + putchar(' '); + printproto(pr, proto, np); + if (np->in_flags & IPN_ROUNDR) + PRINTF(" round-robin"); + if (np->in_flags & IPN_FRAG) + PRINTF(" frag"); + if (np->in_age[0] != 0 || np->in_age[1] != 0) { + PRINTF(" age %d/%d", np->in_age[0], np->in_age[1]); + } + if (np->in_flags & IPN_STICKY) + PRINTF(" sticky"); + if (np->in_mssclamp != 0) + PRINTF(" mssclamp %d", np->in_mssclamp); + if (np->in_plabel != -1) + PRINTF(" proxy %s", np->in_names + np->in_plabel); + if (np->in_tag.ipt_tag[0] != '\0') + PRINTF(" tag %-.*s", IPFTAG_LEN, np->in_tag.ipt_tag); + if ((np->in_flags & IPN_PURGE) != 0) + PRINTF(" purge"); + PRINTF("\n"); + if (opts & OPT_DEBUG) + PRINTF("\tpmax %u\n", np->in_dpmax); + + } else { + int protoprinted = 0; + + if (!(np->in_flags & IPN_FILTER)) { + printnataddr(np->in_v[0], np->in_names, &np->in_osrc, + np->in_ifnames[0]); + } + if (np->in_flags & IPN_NO) { + putchar(' '); + printproto(pr, proto, np); + PRINTF(";\n"); + return; + } + PRINTF(" -> "); + if (np->in_flags & IPN_SIPRANGE) { + PRINTF("range "); + printnataddr(np->in_v[1], np->in_names, &np->in_nsrc, + np->in_ifnames[0]); + } else { + printnataddr(np->in_v[1], np->in_names, &np->in_nsrc, + np->in_ifnames[0]); + } + if (np->in_plabel != -1) { + PRINTF(" proxy port "); + if (np->in_odport != 0) { + char *s; + + s = portname(proto, np->in_odport); + if (s != NULL) + fputs(s, stdout); + else + fputs("???", stdout); + } + PRINTF(" %s/", np->in_names + np->in_plabel); + printproto(pr, proto, NULL); + protoprinted = 1; + } else if (np->in_redir == NAT_MAPBLK) { + if ((np->in_spmin == 0) && + (np->in_flags & IPN_AUTOPORTMAP)) + PRINTF(" ports auto"); + else + PRINTF(" ports %d", np->in_spmin); + if (opts & OPT_DEBUG) + PRINTF("\n\tip modulous %d", np->in_spmax); + + } else if (np->in_spmin || np->in_spmax) { + if (np->in_flags & IPN_ICMPQUERY) { + PRINTF(" icmpidmap "); + } else { + PRINTF(" portmap "); + } + printproto(pr, proto, np); + protoprinted = 1; + if (np->in_flags & IPN_AUTOPORTMAP) { + PRINTF(" auto"); + if (opts & OPT_DEBUG) + PRINTF(" [%d:%d %d %d]", + np->in_spmin, np->in_spmax, + np->in_ippip, np->in_ppip); + } else { + PRINTF(" %d:%d", np->in_spmin, np->in_spmax); + } + if (np->in_flags & IPN_SEQUENTIAL) + PRINTF(" sequential"); + } + + if (np->in_flags & IPN_FRAG) + PRINTF(" frag"); + if (np->in_age[0] != 0 || np->in_age[1] != 0) { + PRINTF(" age %d/%d", np->in_age[0], np->in_age[1]); + } + if (np->in_mssclamp != 0) + PRINTF(" mssclamp %d", np->in_mssclamp); + if (np->in_tag.ipt_tag[0] != '\0') + PRINTF(" tag %s", np->in_tag.ipt_tag); + if (!protoprinted && (np->in_flags & IPN_TCPUDP || proto)) { + putchar(' '); + printproto(pr, proto, np); + } + if ((np->in_flags & IPN_PURGE) != 0) + PRINTF(" purge"); + PRINTF("\n"); + if (opts & OPT_DEBUG) { + PRINTF("\tnextip "); + printip(family, &np->in_snip); + PRINTF(" pnext %d\n", np->in_spnext); + } + } + + if (opts & OPT_DEBUG) { + PRINTF("\tspace %lu use %u hits %lu flags %#x proto %d/%d", + np->in_space, np->in_use, np->in_hits, + np->in_flags, np->in_pr[0], np->in_pr[1]); + PRINTF(" hv %u/%u\n", np->in_hv[0], np->in_hv[1]); + PRINTF("\tifp[0] %p ifp[1] %p apr %p\n", + np->in_ifps[0], np->in_ifps[1], np->in_apr); + PRINTF("\ttqehead %p/%p comment %p\n", + np->in_tqehead[0], np->in_tqehead[1], np->in_comment); + } +} diff --git a/sbin/ipf/libipf/printnataddr.c b/sbin/ipf/libipf/printnataddr.c new file mode 100644 index 000000000000..89faa624193c --- /dev/null +++ b/sbin/ipf/libipf/printnataddr.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) + */ + +#include "ipf.h" +#include "kmem.h" + + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id: printnataddr.c,v 1.4.2.2 2012/07/22 08:04:24 darren_r Exp $"; +#endif + + +void +printnataddr(v, base, addr, ifidx) + int v; + char *base; + nat_addr_t *addr; + int ifidx; +{ + switch (v) + { + case 4 : + if (addr->na_atype == FRI_NORMAL && + addr->na_addr[0].in4.s_addr == 0) { + PRINTF("0/%d", count4bits(addr->na_addr[1].in4.s_addr)); + } else { + printaddr(AF_INET, addr->na_atype, base, ifidx, + (u_32_t *)&addr->na_addr[0].in4.s_addr, + (u_32_t *)&addr->na_addr[1].in4.s_addr); + } + break; +#ifdef USE_INET6 + case 6 : + printaddr(AF_INET6, addr->na_atype, base, ifidx, + (u_32_t *)&addr->na_addr[0].in6, + (u_32_t *)&addr->na_addr[1].in6); + break; +#endif + default : + printf("{v=%d}", v); + break; + } +} diff --git a/sbin/ipf/libipf/printnatfield.c b/sbin/ipf/libipf/printnatfield.c new file mode 100644 index 000000000000..49596f607170 --- /dev/null +++ b/sbin/ipf/libipf/printnatfield.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: printnatfield.c,v 1.6.2.2 2012/01/26 05:44:26 darren_r Exp $ + */ + +#include "ipf.h" + +wordtab_t natfields[] = { + { "all", -2 }, + { "ifp0", 1 }, + { "ifp1", 2 }, + { "mtu0", 3 }, + { "mtu1", 4 }, + { "ifname0", 5 }, + { "ifname1", 6 }, + { "sumd0", 7 }, + { "sumd1", 8 }, + { "pkts0", 9 }, + { "pkts1", 10 }, + { "bytes0", 11 }, + { "bytes1", 12 }, + { "proto0", 13 }, + { "proto1", 14 }, + { "hash0", 15 }, + { "hash1", 16 }, + { "ref", 17 }, + { "rev", 18 }, + { "v0", 19 }, + { "redir", 20 }, + { "use", 21 }, + { "ipsumd", 22 }, + { "dir", 23 }, + { "olddstip", 24 }, + { "oldsrcip", 25 }, + { "newdstip", 26 }, + { "newsrcip", 27 }, + { "olddport", 28 }, + { "oldsport", 29 }, + { "newdport", 30 }, + { "newsport", 31 }, + { "age", 32 }, + { "v1", 33 }, + { NULL, 0 } +}; + + +void +printnatfield(n, fieldnum) + nat_t *n; + int fieldnum; +{ + int i; + + switch (fieldnum) + { + case -2 : + for (i = 1; natfields[i].w_word != NULL; i++) { + if (natfields[i].w_value > 0) { + printnatfield(n, i); + if (natfields[i + 1].w_value > 0) + putchar('\t'); + } + } + break; + + case 1: + PRINTF("%#lx", (u_long)n->nat_ifps[0]); + break; + + case 2: + PRINTF("%#lx", (u_long)n->nat_ifps[1]); + break; + + case 3: + PRINTF("%d", n->nat_mtu[0]); + break; + + case 4: + PRINTF("%d", n->nat_mtu[1]); + break; + + case 5: + PRINTF("%s", n->nat_ifnames[0]); + break; + + case 6: + PRINTF("%s", n->nat_ifnames[1]); + break; + + case 7: + PRINTF("%d", n->nat_sumd[0]); + break; + + case 8: + PRINTF("%d", n->nat_sumd[1]); + break; + + case 9: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", n->nat_pkts[0]); +#else + PRINTF("%lu", n->nat_pkts[0]); +#endif + break; + + case 10: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", n->nat_pkts[1]); +#else + PRINTF("%lu", n->nat_pkts[1]); +#endif + break; + + case 11: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", n->nat_bytes[0]); +#else + PRINTF("%lu", n->nat_bytes[0]); +#endif + break; + + case 12: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", n->nat_bytes[1]); +#else + PRINTF("%lu", n->nat_bytes[1]); +#endif + break; + + case 13: + PRINTF("%d", n->nat_pr[0]); + break; + + case 14: + PRINTF("%d", n->nat_pr[1]); + break; + + case 15: + PRINTF("%u", n->nat_hv[0]); + break; + + case 16: + PRINTF("%u", n->nat_hv[1]); + break; + + case 17: + PRINTF("%d", n->nat_ref); + break; + + case 18: + PRINTF("%d", n->nat_rev); + break; + + case 19: + PRINTF("%d", n->nat_v[0]); + break; + + case 33: + PRINTF("%d", n->nat_v[0]); + break; + + case 20: + PRINTF("%d", n->nat_redir); + break; + + case 21: + PRINTF("%d", n->nat_use); + break; + + case 22: + PRINTF("%u", n->nat_ipsumd); + break; + + case 23: + PRINTF("%d", n->nat_dir); + break; + + case 24: + PRINTF("%s", hostname(n->nat_v[0], &n->nat_odstip)); + break; + + case 25: + PRINTF("%s", hostname(n->nat_v[0], &n->nat_osrcip)); + break; + + case 26: + PRINTF("%s", hostname(n->nat_v[1], &n->nat_ndstip)); + break; + + case 27: + PRINTF("%s", hostname(n->nat_v[1], &n->nat_nsrcip)); + break; + + case 28: + PRINTF("%hu", ntohs(n->nat_odport)); + break; + + case 29: + PRINTF("%hu", ntohs(n->nat_osport)); + break; + + case 30: + PRINTF("%hu", ntohs(n->nat_ndport)); + break; + + case 31: + PRINTF("%hu", ntohs(n->nat_nsport)); + break; + + case 32: + PRINTF("%u", n->nat_age); + break; + + default: + break; + } +} diff --git a/sbin/ipf/libipf/printnatside.c b/sbin/ipf/libipf/printnatside.c new file mode 100644 index 000000000000..37e1cb8d1e3a --- /dev/null +++ b/sbin/ipf/libipf/printnatside.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: printnatside.c,v 1.2.2.6 2012/07/22 08:04:24 darren_r Exp $ + */ +#include "ipf.h" + +void +printnatside(side, ns) + char *side; + nat_stat_side_t *ns; +{ + PRINTF("%lu\tproxy create fail %s\n", ns->ns_appr_fail, side); + PRINTF("%lu\tproxy fail %s\n", ns->ns_ipf_proxy_fail, side); + PRINTF("%lu\tbad nat %s\n", ns->ns_badnat, side); + PRINTF("%lu\tbad nat new %s\n", ns->ns_badnatnew, side); + PRINTF("%lu\tbad next addr %s\n", ns->ns_badnextaddr, side); + PRINTF("%lu\tbucket max %s\n", ns->ns_bucket_max, side); + PRINTF("%lu\tclone nomem %s\n", ns->ns_clone_nomem, side); + PRINTF("%lu\tdecap bad %s\n", ns->ns_decap_bad, side); + PRINTF("%lu\tdecap fail %s\n", ns->ns_decap_fail, side); + PRINTF("%lu\tdecap pullup %s\n", ns->ns_decap_pullup, side); + PRINTF("%lu\tdivert dup %s\n", ns->ns_divert_dup, side); + PRINTF("%lu\tdivert exist %s\n", ns->ns_divert_exist, side); + PRINTF("%lu\tdrop %s\n", ns->ns_drop, side); + PRINTF("%lu\texhausted %s\n", ns->ns_exhausted, side); + PRINTF("%lu\ticmp address %s\n", ns->ns_icmp_address, side); + PRINTF("%lu\ticmp basic %s\n", ns->ns_icmp_basic, side); + PRINTF("%lu\tinuse %s\n", ns->ns_inuse, side); + PRINTF("%lu\ticmp mbuf wrong size %s\n", ns->ns_icmp_mbuf, side); + PRINTF("%lu\ticmp header unmatched %s\n", ns->ns_icmp_notfound, side); + PRINTF("%lu\ticmp rebuild failures %s\n", ns->ns_icmp_rebuild, side); + PRINTF("%lu\ticmp short %s\n", ns->ns_icmp_short, side); + PRINTF("%lu\ticmp packet size wrong %s\n", ns->ns_icmp_size, side); + PRINTF("%lu\tIFP address fetch failures %s\n", + ns->ns_ifpaddrfail, side); + PRINTF("%lu\tpackets untranslated %s\n", ns->ns_ignored, side); + PRINTF("%lu\tNAT insert failures %s\n", ns->ns_insert_fail, side); + PRINTF("%lu\tNAT lookup misses %s\n", ns->ns_lookup_miss, side); + PRINTF("%lu\tNAT lookup nowild %s\n", ns->ns_lookup_nowild, side); + PRINTF("%lu\tnew ifpaddr failed %s\n", ns->ns_new_ifpaddr, side); + PRINTF("%lu\tmemory requests failed %s\n", ns->ns_memfail, side); + PRINTF("%lu\ttable max reached %s\n", ns->ns_table_max, side); + PRINTF("%lu\tpackets translated %s\n", ns->ns_translated, side); + PRINTF("%lu\tfinalised failed %s\n", ns->ns_unfinalised, side); + PRINTF("%lu\tsearch wraps %s\n", ns->ns_wrap, side); + PRINTF("%lu\tnull translations %s\n", ns->ns_xlate_null, side); + PRINTF("%lu\ttranslation exists %s\n", ns->ns_xlate_exists, side); + PRINTF("%lu\tno memory %s\n", ns->ns_memfail, side); + + if (opts & OPT_VERBOSE) + PRINTF("%p table %s\n", ns->ns_table, side); +} diff --git a/sbin/ipf/libipf/printpacket.c b/sbin/ipf/libipf/printpacket.c new file mode 100644 index 000000000000..5c4a74975cb6 --- /dev/null +++ b/sbin/ipf/libipf/printpacket.c @@ -0,0 +1,110 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +#ifndef IP_OFFMASK +# define IP_OFFMASK 0x3fff +#endif + + +void +printpacket(dir, m) + int dir; + mb_t *m; +{ + u_short len, off; + tcphdr_t *tcp; + ip_t *ip; + + ip = MTOD(m, ip_t *); + + if (IP_V(ip) == 6) { +#ifdef USE_INET6 + len = ntohs(((ip6_t *)ip)->ip6_plen); +#else + len = ntohs(((u_short *)ip)[2]); +#endif + len += 40; + } else { + len = ntohs(ip->ip_len); + } + ASSERT(len == msgdsize(m)); + + if ((opts & OPT_HEX) == OPT_HEX) { + u_char *s; + int i; + + for (; m != NULL; m = m->mb_next) { + len = m->mb_len; + for (s = (u_char *)m->mb_data, i = 0; i < len; i++) { + PRINTF("%02x", *s++ & 0xff); + if (len - i > 1) { + i++; + PRINTF("%02x", *s++ & 0xff); + } + putchar(' '); + } + } + putchar('\n'); + putchar('\n'); + return; + } + + if (IP_V(ip) == 6) { + printpacket6(dir, m); + return; + } + + if (dir) + PRINTF("> "); + else + PRINTF("< "); + + PRINTF("%s ", IFNAME(m->mb_ifp)); + + off = ntohs(ip->ip_off); + tcp = (struct tcphdr *)((char *)ip + (IP_HL(ip) << 2)); + PRINTF("ip #%d %d(%d) %d", ntohs(ip->ip_id), ntohs(ip->ip_len), + IP_HL(ip) << 2, ip->ip_p); + if (off & IP_OFFMASK) + PRINTF(" @%d", off << 3); + PRINTF(" %s", inet_ntoa(ip->ip_src)); + if (!(off & IP_OFFMASK)) + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + PRINTF(",%d", ntohs(tcp->th_sport)); + PRINTF(" > "); + PRINTF("%s", inet_ntoa(ip->ip_dst)); + if (!(off & IP_OFFMASK)) { + if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) + PRINTF(",%d", ntohs(tcp->th_dport)); + if ((ip->ip_p == IPPROTO_TCP) && (tcp->th_flags != 0)) { + putchar(' '); + if (tcp->th_flags & TH_FIN) + putchar('F'); + if (tcp->th_flags & TH_SYN) + putchar('S'); + if (tcp->th_flags & TH_RST) + putchar('R'); + if (tcp->th_flags & TH_PUSH) + putchar('P'); + if (tcp->th_flags & TH_ACK) + putchar('A'); + if (tcp->th_flags & TH_URG) + putchar('U'); + if (tcp->th_flags & TH_ECN) + putchar('E'); + if (tcp->th_flags & TH_CWR) + putchar('C'); + } + } + + putchar('\n'); +} diff --git a/sbin/ipf/libipf/printpacket6.c b/sbin/ipf/libipf/printpacket6.c new file mode 100644 index 000000000000..6363e55fc76d --- /dev/null +++ b/sbin/ipf/libipf/printpacket6.c @@ -0,0 +1,60 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +/* + * This is meant to work without the IPv6 header files being present or + * the inet_ntop() library. + */ +void +printpacket6(dir, m) + int dir; + mb_t *m; +{ + u_char *buf, p; + u_short plen, *addrs; + tcphdr_t *tcp; + u_32_t flow; + + buf = (u_char *)m->mb_data; + tcp = (tcphdr_t *)(buf + 40); + p = buf[6]; + flow = ntohl(*(u_32_t *)buf); + flow &= 0xfffff; + plen = ntohs(*((u_short *)buf +2)); + addrs = (u_short *)buf + 4; + + if (dir) + PRINTF("> "); + else + PRINTF("< "); + + PRINTF("%s ", IFNAME(m->mb_ifp)); + + PRINTF("ip6/%d %d %#x %d", buf[0] & 0xf, plen, flow, p); + PRINTF(" %x:%x:%x:%x:%x:%x:%x:%x", + ntohs(addrs[0]), ntohs(addrs[1]), ntohs(addrs[2]), + ntohs(addrs[3]), ntohs(addrs[4]), ntohs(addrs[5]), + ntohs(addrs[6]), ntohs(addrs[7])); + if (plen >= 4) + if (p == IPPROTO_TCP || p == IPPROTO_UDP) + (void)PRINTF(",%d", ntohs(tcp->th_sport)); + PRINTF(" >"); + addrs += 8; + PRINTF(" %x:%x:%x:%x:%x:%x:%x:%x", + ntohs(addrs[0]), ntohs(addrs[1]), ntohs(addrs[2]), + ntohs(addrs[3]), ntohs(addrs[4]), ntohs(addrs[5]), + ntohs(addrs[6]), ntohs(addrs[7])); + if (plen >= 4) + if (p == IPPROTO_TCP || p == IPPROTO_UDP) + PRINTF(",%d", ntohs(tcp->th_dport)); + putchar('\n'); +} diff --git a/sbin/ipf/libipf/printpool.c b/sbin/ipf/libipf/printpool.c new file mode 100644 index 000000000000..8d8cdccea59f --- /dev/null +++ b/sbin/ipf/libipf/printpool.c @@ -0,0 +1,65 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +ip_pool_t * +printpool(pp, copyfunc, name, opts, fields) + ip_pool_t *pp; + copyfunc_t copyfunc; + char *name; + int opts; + wordtab_t *fields; +{ + ip_pool_node_t *ipnp, *ipnpn, ipn, **pnext; + ip_pool_t ipp; + + if ((*copyfunc)(pp, &ipp, sizeof(ipp))) + return NULL; + + if ((name != NULL) && strncmp(name, ipp.ipo_name, FR_GROUPLEN)) + return ipp.ipo_next; + + printpooldata(&ipp, opts); + + if ((ipp.ipo_flags & IPOOL_DELETE) != 0) + PRINTF("# "); + if ((opts & OPT_DEBUG) == 0) + PRINTF("\t{"); + + ipnpn = ipp.ipo_list; + ipp.ipo_list = NULL; + pnext = &ipp.ipo_list; + while (ipnpn != NULL) { + ipnp = (ip_pool_node_t *)malloc(sizeof(*ipnp)); + (*copyfunc)(ipnpn, ipnp, sizeof(ipn)); + ipnpn = ipnp->ipn_next; + *pnext = ipnp; + pnext = &ipnp->ipn_next; + ipnp->ipn_next = NULL; + } + + if (ipp.ipo_list == NULL) { + putchar(';'); + } else { + for (ipnp = ipp.ipo_list; ipnp != NULL; ipnp = ipnpn) { + ipnpn = printpoolnode(ipnp, opts, fields); + free(ipnp); + + if ((opts & OPT_DEBUG) == 0) { + putchar(';'); + } + } + } + + if ((opts & OPT_DEBUG) == 0) + PRINTF(" };\n"); + + return ipp.ipo_next; +} diff --git a/sbin/ipf/libipf/printpool_live.c b/sbin/ipf/libipf/printpool_live.c new file mode 100644 index 000000000000..2aabf32bc14a --- /dev/null +++ b/sbin/ipf/libipf/printpool_live.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ipl.h" + + +ip_pool_t * +printpool_live(pool, fd, name, opts, fields) + ip_pool_t *pool; + int fd; + char *name; + int opts; + wordtab_t *fields; +{ + ip_pool_node_t entry; + ipflookupiter_t iter; + int printed, last; + ipfobj_t obj; + + if ((name != NULL) && strncmp(name, pool->ipo_name, FR_GROUPLEN)) + return pool->ipo_next; + + if (fields == NULL) + printpooldata(pool, opts); + + if ((pool->ipo_flags & IPOOL_DELETE) != 0) + PRINTF("# "); + if ((opts & OPT_DEBUG) == 0) + PRINTF("\t{"); + + obj.ipfo_rev = IPFILTER_VERSION; + obj.ipfo_type = IPFOBJ_LOOKUPITER; + obj.ipfo_ptr = &iter; + obj.ipfo_size = sizeof(iter); + + iter.ili_data = &entry; + iter.ili_type = IPLT_POOL; + iter.ili_otype = IPFLOOKUPITER_NODE; + iter.ili_ival = IPFGENITER_LOOKUP; + iter.ili_unit = pool->ipo_unit; + strncpy(iter.ili_name, pool->ipo_name, FR_GROUPLEN); + + last = 0; + printed = 0; + + if (pool->ipo_list != NULL) { + while (!last && (ioctl(fd, SIOCLOOKUPITER, &obj) == 0)) { + if (entry.ipn_next == NULL) + last = 1; + (void) printpoolnode(&entry, opts, fields); + if ((opts & OPT_DEBUG) == 0) + putchar(';'); + printed++; + } + } + + if (printed == 0) + putchar(';'); + + if ((opts & OPT_DEBUG) == 0) + PRINTF(" };\n"); + + (void) ioctl(fd,SIOCIPFDELTOK, &iter.ili_key); + + return pool->ipo_next; +} diff --git a/sbin/ipf/libipf/printpooldata.c b/sbin/ipf/libipf/printpooldata.c new file mode 100644 index 000000000000..a1591774b4df --- /dev/null +++ b/sbin/ipf/libipf/printpooldata.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" +#include <ctype.h> + + +void +printpooldata(pool, opts) + ip_pool_t *pool; + int opts; +{ + + if ((opts & OPT_DEBUG) == 0) { + if ((pool->ipo_flags & IPOOL_ANON) != 0) + PRINTF("# 'anonymous' tree %s\n", pool->ipo_name); + if ((pool->ipo_flags & IPOOL_DELETE) != 0) + PRINTF("# "); + PRINTF("table role="); + } else { + if ((pool->ipo_flags & IPOOL_DELETE) != 0) + PRINTF("# "); + PRINTF("%s: %s", + ISDIGIT(*pool->ipo_name) ? "Number" : "Name", + pool->ipo_name); + if ((pool->ipo_flags & IPOOL_ANON) == IPOOL_ANON) + PRINTF("(anon)"); + putchar(' '); + PRINTF("Role: "); + } + + printunit(pool->ipo_unit); + + if ((opts & OPT_DEBUG) == 0) { + PRINTF(" type=tree %s=%s\n", + (!*pool->ipo_name || ISDIGIT(*pool->ipo_name)) ? \ + "number" : "name", pool->ipo_name); + } else { + putchar(' '); + + PRINTF("\tReferences: %d\tHits: %lu\n", pool->ipo_ref, + pool->ipo_hits); + if ((pool->ipo_flags & IPOOL_DELETE) != 0) + PRINTF("# "); + PRINTF("\tNodes Starting at %p\n", pool->ipo_list); + } +} diff --git a/sbin/ipf/libipf/printpoolfield.c b/sbin/ipf/libipf/printpoolfield.c new file mode 100644 index 000000000000..9254ab844615 --- /dev/null +++ b/sbin/ipf/libipf/printpoolfield.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: printpoolfield.c,v 1.1.2.4 2012/01/26 05:44:26 darren_r Exp $ + */ + +#include "ipf.h" + +wordtab_t poolfields[] = { + { "all", -2 }, + { "address", 1 }, + { "mask", 2 }, + { "ifname", 3 }, + { "pkts", 4 }, + { "bytes", 5 }, + { "family", 6 }, + { NULL, 0 } +}; + + +void +printpoolfield(p, ptype, fieldnum) + void *p; + int ptype; + int fieldnum; +{ + addrfamily_t *a; + char abuf[80]; + int i; + + switch (fieldnum) + { + case -2 : + for (i = 1; poolfields[i].w_word != NULL; i++) { + if (poolfields[i].w_value > 0) { + printpoolfield(p, ptype, i); + if (poolfields[i + 1].w_value > 0) + putchar('\t'); + } + } + break; + + case 1: + if (ptype == IPLT_POOL) { + ip_pool_node_t *node = (ip_pool_node_t *)p; + + if (node->ipn_info) + PRINTF("!"); + a = &node->ipn_addr; + PRINTF("%s", inet_ntop(a->adf_family, &a->adf_addr, + abuf, sizeof(abuf))); + } else if (ptype == IPLT_HASH) { + iphtent_t *node = (iphtent_t *)p; + + PRINTF("%s", inet_ntop(node->ipe_family, + &node->ipe_addr, + abuf, sizeof(abuf))); + } else if (ptype == IPLT_DSTLIST) { + ipf_dstnode_t *node = (ipf_dstnode_t *)p; + + a = &node->ipfd_dest.fd_addr; + PRINTF("%s", inet_ntop(a->adf_family, &a->adf_addr, + abuf, sizeof(abuf))); + } + break; + + case 2: + if (ptype == IPLT_POOL) { + ip_pool_node_t *node = (ip_pool_node_t *)p; + + a = &node->ipn_mask; + PRINTF("%s", inet_ntop(a->adf_family, &a->adf_addr, + abuf, sizeof(abuf))); + } else if (ptype == IPLT_HASH) { + iphtent_t *node = (iphtent_t *)p; + + PRINTF("%s", inet_ntop(node->ipe_family, + &node->ipe_mask, + abuf, sizeof(abuf))); + } else if (ptype == IPLT_DSTLIST) { + PRINTF("%s", ""); + } + break; + + case 3: + if (ptype == IPLT_POOL) { + PRINTF("%s", ""); + } else if (ptype == IPLT_HASH) { + PRINTF("%s", ""); + } else if (ptype == IPLT_DSTLIST) { + ipf_dstnode_t *node = (ipf_dstnode_t *)p; + + if (node->ipfd_dest.fd_name == -1) { + PRINTF("%s", ""); + } else { + PRINTF("%s", node->ipfd_names + + node->ipfd_dest.fd_name); + } + } + break; + + case 4: + if (ptype == IPLT_POOL) { + ip_pool_node_t *node = (ip_pool_node_t *)p; + +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", node->ipn_hits); +#else + PRINTF("%lu", node->ipn_hits); +#endif + } else if (ptype == IPLT_HASH) { + iphtent_t *node = (iphtent_t *)p; + +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", node->ipe_hits); +#else + PRINTF("%lu", node->ipe_hits); +#endif + } else if (ptype == IPLT_DSTLIST) { + printf("0"); + } + break; + + case 5: + if (ptype == IPLT_POOL) { + ip_pool_node_t *node = (ip_pool_node_t *)p; + +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", node->ipn_bytes); +#else + PRINTF("%lu", node->ipn_bytes); +#endif + } else if (ptype == IPLT_HASH) { + iphtent_t *node = (iphtent_t *)p; + +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", node->ipe_bytes); +#else + PRINTF("%lu", node->ipe_bytes); +#endif + } else if (ptype == IPLT_DSTLIST) { + printf("0"); + } + break; + + case 6: + if (ptype == IPLT_POOL) { + ip_pool_node_t *node = (ip_pool_node_t *)p; + + PRINTF("%s", familyname(node->ipn_addr.adf_family)); + } else if (ptype == IPLT_HASH) { + iphtent_t *node = (iphtent_t *)p; + + PRINTF("%s", familyname(node->ipe_family)); + } else if (ptype == IPLT_DSTLIST) { + ipf_dstnode_t *node = (ipf_dstnode_t *)p; + + a = &node->ipfd_dest.fd_addr; + PRINTF("%s", familyname(a->adf_family)); + } + break; + + default : + break; + } +} diff --git a/sbin/ipf/libipf/printpoolnode.c b/sbin/ipf/libipf/printpoolnode.c new file mode 100644 index 000000000000..6b58b8459327 --- /dev/null +++ b/sbin/ipf/libipf/printpoolnode.c @@ -0,0 +1,71 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +ip_pool_node_t * +printpoolnode(np, opts, fields) + ip_pool_node_t *np; + int opts; + wordtab_t *fields; +{ + int i; + + if (fields != NULL) { + for (i = 0; fields[i].w_value != 0; i++) { + printpoolfield(np, IPLT_POOL, i); + if (fields[i + 1].w_value != 0) + printf("\t"); + } + printf("\n"); + } else if ((opts & OPT_DEBUG) == 0) { + putchar(' '); + if (np->ipn_info == 1) + PRINTF("! "); + printip(np->ipn_addr.adf_family, + (u_32_t *)&np->ipn_addr.adf_addr.in4); + printmask(np->ipn_addr.adf_family, + (u_32_t *)&np->ipn_mask.adf_addr); + } else { +#ifdef USE_INET6 + if (np->ipn_addr.adf_family == AF_INET6) { + char buf[INET6_ADDRSTRLEN + 1]; + const char *str; + + buf[0] = '\0'; + str = inet_ntop(AF_INET6, &np->ipn_addr.adf_addr.in6, + buf, sizeof(buf) - 1); + if (str == NULL) + str = "???"; + PRINTF("\tAddress: %s%s", np->ipn_info ? "! " : "", + str); + } else if (np->ipn_addr.adf_family == AF_INET) { +#else + if (np->ipn_addr.adf_family == AF_INET) { +#endif + PRINTF("\tAddress: %s%s", np->ipn_info ? "! " : "", + inet_ntoa(np->ipn_addr.adf_addr.in4)); + } else { + PRINTF("\tAddress: family: %d\n", + np->ipn_addr.adf_family); + } + printmask(np->ipn_addr.adf_family, + (u_32_t *)&np->ipn_mask.adf_addr); +#ifdef USE_QUAD_T + PRINTF("\n\t\tHits %"PRIu64"\tBytes %"PRIu64"\tName %s\tRef %d\n", + np->ipn_hits, np->ipn_bytes, + np->ipn_name, np->ipn_ref); +#else + PRINTF("\n\t\tHits %lu\tBytes %lu\tName %s\tRef %d\n", + np->ipn_hits, np->ipn_bytes, + np->ipn_name, np->ipn_ref); +#endif + } + return np->ipn_next; +} diff --git a/sbin/ipf/libipf/printportcmp.c b/sbin/ipf/libipf/printportcmp.c new file mode 100644 index 000000000000..2a5bd0229201 --- /dev/null +++ b/sbin/ipf/libipf/printportcmp.c @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +void +printportcmp(pr, frp) + int pr; + frpcmp_t *frp; +{ + static char *pcmp1[] = { "*", "=", "!=", "<", ">", "<=", ">=", + "<>", "><", ":" }; + + if (frp->frp_cmp == FR_INRANGE || frp->frp_cmp == FR_OUTRANGE) + PRINTF(" port %d %s %d", frp->frp_port, + pcmp1[frp->frp_cmp], frp->frp_top); + else if (frp->frp_cmp == FR_INCRANGE) + PRINTF(" port %d:%d", frp->frp_port, frp->frp_top); + else + PRINTF(" port %s %s", pcmp1[frp->frp_cmp], + portname(pr, frp->frp_port)); +} diff --git a/sbin/ipf/libipf/printproto.c b/sbin/ipf/libipf/printproto.c new file mode 100644 index 000000000000..879da12d7857 --- /dev/null +++ b/sbin/ipf/libipf/printproto.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id$"; +#endif + + +void +printproto(pr, p, np) + struct protoent *pr; + int p; + ipnat_t *np; +{ + if (np != NULL) { + if ((np->in_flags & IPN_TCPUDP) == IPN_TCPUDP) + PRINTF("tcp/udp"); + else if (np->in_flags & IPN_TCP) + PRINTF("tcp"); + else if (np->in_flags & IPN_UDP) + PRINTF("udp"); + else if (np->in_flags & IPN_ICMPQUERY) + PRINTF("icmp"); + else if (np->in_pr[0] == 0) + PRINTF("ip"); + else if (pr != NULL) + PRINTF("%s", pr->p_name); + else + PRINTF("%d", np->in_pr[0]); + } else { + if (pr != NULL) + PRINTF("%s", pr->p_name); + else + PRINTF("%d", p); + } +} diff --git a/sbin/ipf/libipf/printsbuf.c b/sbin/ipf/libipf/printsbuf.c new file mode 100644 index 000000000000..efda99e856b8 --- /dev/null +++ b/sbin/ipf/libipf/printsbuf.c @@ -0,0 +1,42 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#ifdef IPFILTER_SCAN + +#include <ctype.h> +#include <stdio.h> +#include "ipf.h" +#include "netinet/ip_scan.h" + +void +printsbuf(buf) + char *buf; +{ + u_char *s; + int i; + + for (s = (u_char *)buf, i = ISC_TLEN; i; i--, s++) { + if (ISPRINT(*s)) + putchar(*s); + else + PRINTF("\\%o", *s); + } +} +#else +void printsbuf(char *buf); + +void printsbuf(buf) + char *buf; +{ +#if 0 + buf = buf; /* gcc -Wextra */ +#endif +} +#endif diff --git a/sbin/ipf/libipf/printstate.c b/sbin/ipf/libipf/printstate.c new file mode 100644 index 000000000000..8832a723e9f1 --- /dev/null +++ b/sbin/ipf/libipf/printstate.c @@ -0,0 +1,221 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" +#include "kmem.h" + + +ipstate_t * +printstate(ipstate_t *sp, int opts, u_long now) +{ + struct protoent *pr; + synclist_t ipsync; + + if ((opts & OPT_NORESOLVE) == 0) + pr = getprotobynumber(sp->is_p); + else + pr = NULL; + + PRINTF("%d:", sp->is_v); + if (pr != NULL) + PRINTF("%s", pr->p_name); + else + PRINTF("%d", sp->is_p); + + PRINTF(" src:%s", hostname(sp->is_family, &sp->is_src.in4)); + if (sp->is_p == IPPROTO_UDP || sp->is_p == IPPROTO_TCP) { + if (sp->is_flags & IS_WSPORT) + PRINTF(",*"); + else + PRINTF(",%d", ntohs(sp->is_sport)); + } + + PRINTF(" dst:%s", hostname(sp->is_family, &sp->is_dst.in4)); + if (sp->is_p == IPPROTO_UDP || sp->is_p == IPPROTO_TCP) { + if (sp->is_flags & IS_WDPORT) + PRINTF(",*"); + else + PRINTF(",%d", ntohs(sp->is_dport)); + } + + if (sp->is_p == IPPROTO_TCP) { + PRINTF(" state:%d/%d", sp->is_state[0], sp->is_state[1]); + } + + PRINTF(" %ld", sp->is_die - now); + if (sp->is_phnext == NULL) + PRINTF(" ORPHAN"); + if (sp->is_flags & IS_CLONE) + PRINTF(" CLONE"); + putchar('\n'); + + if (sp->is_p == IPPROTO_TCP) { + PRINTF("\t%x:%x %hu<<%d:%hu<<%d\n", + sp->is_send, sp->is_dend, + sp->is_maxswin, sp->is_swinscale, + sp->is_maxdwin, sp->is_dwinscale); + if ((opts & OPT_VERBOSE) != 0) { + PRINTF("\tcmsk %04x smsk %04x isc %p s0 %08x/%08x\n", + sp->is_smsk[0], sp->is_smsk[1], sp->is_isc, + sp->is_s0[0], sp->is_s0[1]); + PRINTF("\tFWD: ISN inc %x sumd %x\n", + sp->is_isninc[0], sp->is_sumd[0]); + PRINTF("\tREV: ISN inc %x sumd %x\n", + sp->is_isninc[1], sp->is_sumd[1]); +#ifdef IPFILTER_SCAN + PRINTF("\tsbuf[0] ["); + printsbuf(sp->is_sbuf[0]); + PRINTF("] sbuf[1] ["); + printsbuf(sp->is_sbuf[1]); + PRINTF("]\n"); +#endif + } + } else if (sp->is_p == IPPROTO_GRE) { + PRINTF("\tcall %hx/%hx\n", ntohs(sp->is_gre.gs_call[0]), + ntohs(sp->is_gre.gs_call[1])); + } else if (sp->is_p == IPPROTO_ICMP +#ifdef USE_INET6 + || sp->is_p == IPPROTO_ICMPV6 +#endif + ) { + PRINTF("\tid %hu seq %hu type %d\n", sp->is_icmp.ici_id, + sp->is_icmp.ici_seq, sp->is_icmp.ici_type); + } + +#ifdef USE_QUAD_T + PRINTF("\tFWD: IN pkts %"PRIu64" bytes %"PRIu64" OUT pkts %"PRIu64" bytes %"PRIu64"\n\tREV: IN pkts %"PRIu64" bytes %"PRIu64" OUT pkts %"PRIu64" bytes %"PRIu64"\n", + sp->is_pkts[0], sp->is_bytes[0], + sp->is_pkts[1], sp->is_bytes[1], + sp->is_pkts[2], sp->is_bytes[2], + sp->is_pkts[3], sp->is_bytes[3]); +#else + PRINTF("\tFWD: IN pkts %lu bytes %lu OUT pkts %lu bytes %lu\n\tREV: IN pkts %lu bytes %lu OUT pkts %lu bytes %lu\n", + sp->is_pkts[0], sp->is_bytes[0], + sp->is_pkts[1], sp->is_bytes[1], + sp->is_pkts[2], sp->is_bytes[2], + sp->is_pkts[3], sp->is_bytes[3]); +#endif + + PRINTF("\ttag %u pass %#x = ", sp->is_tag, sp->is_pass); + + /* + * Print out bits set in the result code for the state being + * kept as they would for a rule. + */ + if (FR_ISPASS(sp->is_pass)) { + PRINTF("pass"); + } else if (FR_ISBLOCK(sp->is_pass)) { + PRINTF("block"); + switch (sp->is_pass & FR_RETMASK) + { + case FR_RETICMP : + PRINTF(" return-icmp"); + break; + case FR_FAKEICMP : + PRINTF(" return-icmp-as-dest"); + break; + case FR_RETRST : + PRINTF(" return-rst"); + break; + default : + break; + } + } else if ((sp->is_pass & FR_LOGMASK) == FR_LOG) { + PRINTF("log"); + if (sp->is_pass & FR_LOGBODY) + PRINTF(" body"); + if (sp->is_pass & FR_LOGFIRST) + PRINTF(" first"); + } else if (FR_ISACCOUNT(sp->is_pass)) { + PRINTF("count"); + } else if (FR_ISPREAUTH(sp->is_pass)) { + PRINTF("preauth"); + } else if (FR_ISAUTH(sp->is_pass)) + PRINTF("auth"); + + if (sp->is_pass & FR_OUTQUE) + PRINTF(" out"); + else + PRINTF(" in"); + + if ((sp->is_pass & FR_LOG) != 0) { + PRINTF(" log"); + if (sp->is_pass & FR_LOGBODY) + PRINTF(" body"); + if (sp->is_pass & FR_LOGFIRST) + PRINTF(" first"); + if (sp->is_pass & FR_LOGORBLOCK) + PRINTF(" or-block"); + } + if (sp->is_pass & FR_QUICK) + PRINTF(" quick"); + if (sp->is_pass & FR_KEEPFRAG) + PRINTF(" keep frags"); + /* a given; no? */ + if (sp->is_pass & FR_KEEPSTATE) { + PRINTF(" keep state"); + if (sp->is_pass & (FR_STATESYNC|FR_STSTRICT|FR_STLOOSE)) { + PRINTF(" ("); + if (sp->is_pass & FR_STATESYNC) + PRINTF(" sync"); + if (sp->is_pass & FR_STSTRICT) + PRINTF(" strict"); + if (sp->is_pass & FR_STLOOSE) + PRINTF(" loose"); + PRINTF(" )"); + } + } + PRINTF("\n"); + + if ((opts & OPT_VERBOSE) != 0) { + PRINTF("\tref %d", sp->is_ref); + PRINTF(" pkt_flags & %x(%x) = %x\n", + sp->is_flags & 0xf, sp->is_flags, sp->is_flags >> 4); + PRINTF("\tpkt_options & %x = %x, %x = %x \n", sp->is_optmsk[0], + sp->is_opt[0], sp->is_optmsk[1], sp->is_opt[1]); + PRINTF("\tpkt_security & %x = %x, pkt_auth & %x = %x\n", + sp->is_secmsk, sp->is_sec, sp->is_authmsk, + sp->is_auth); + PRINTF("\tis_flx %#x %#x %#x %#x\n", sp->is_flx[0][0], + sp->is_flx[0][1], sp->is_flx[1][0], sp->is_flx[1][1]); + } + PRINTF("\tinterfaces: in %s[%s", getifname(sp->is_ifp[0]), + sp->is_ifname[0]); + if (opts & OPT_DEBUG) + PRINTF("/%p", sp->is_ifp[0]); + putchar(']'); + PRINTF(",%s[%s", getifname(sp->is_ifp[1]), sp->is_ifname[1]); + if (opts & OPT_DEBUG) + PRINTF("/%p", sp->is_ifp[1]); + putchar(']'); + PRINTF(" out %s[%s", getifname(sp->is_ifp[2]), sp->is_ifname[2]); + if (opts & OPT_DEBUG) + PRINTF("/%p", sp->is_ifp[2]); + putchar(']'); + PRINTF(",%s[%s", getifname(sp->is_ifp[3]), sp->is_ifname[3]); + if (opts & OPT_DEBUG) + PRINTF("/%p", sp->is_ifp[3]); + PRINTF("]\n"); + + PRINTF("\tSync status: "); + if (sp->is_sync != NULL) { + if (kmemcpy((char *)&ipsync, (u_long)sp->is_sync, + sizeof(ipsync))) { + PRINTF("status could not be retrieved\n"); + return (NULL); + } + + PRINTF("idx %d num %d v %d pr %d rev %d\n", + ipsync.sl_idx, ipsync.sl_num, ipsync.sl_v, + ipsync.sl_p, ipsync.sl_rev); + } else { + PRINTF("not synchronized\n"); + } + + return (sp->is_next); +} diff --git a/sbin/ipf/libipf/printstatefields.c b/sbin/ipf/libipf/printstatefields.c new file mode 100644 index 000000000000..5632d8416c47 --- /dev/null +++ b/sbin/ipf/libipf/printstatefields.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: printstatefields.c,v 1.4.2.2 2012/01/26 05:44:26 darren_r Exp $ + */ + +#include "ipf.h" + +wordtab_t statefields[] = { + { "all", -2 }, + { "ifp0", 1 }, + { "ifp1", 2 }, + { "ifp2", 3 }, + { "ifp3", 4 }, + { "ifname0", 5 }, + { "ifname1", 6 }, + { "ifname2", 7 }, + { "ifname3", 8 }, + { "pkts0", 9 }, + { "pkts1", 10 }, + { "pkts2", 11 }, + { "pkts3", 12 }, + { "bytes0", 13 }, + { "bytes1", 14 }, + { "bytes2", 15 }, + { "bytes3", 16 }, + { "state0", 17 }, + { "state1", 18 }, + { "age0", 19 }, + { "age1", 20 }, + { "ref", 21 }, + { "isn0", 22 }, + { "isn1", 23 }, + { "sumd0", 24 }, + { "sumd1", 25 }, + { "src", 26 }, + { "dst", 27 }, + { "sport", 28 }, + { "dport", 29 }, + { "icmptype", 30 }, + { "-", 31 }, + { "pass", 32 }, + { "proto", 33 }, + { "version", 34 }, + { "hash", 35 }, + { "tag", 36 }, + { "flags", 37 }, + { "rulen", 38 }, + { "group", 39 }, + { "flx0", 40 }, + { "flx1", 41 }, + { "flx2", 42 }, + { "flx3", 43 }, + { "opt0", 44 }, + { "opt1", 45 }, + { "optmsk0", 46 }, + { "optmsk1", 47 }, + { "sec", 48 }, + { "secmsk", 49 }, + { "auth", 50 }, + { "authmsk", 51 }, + { "icmppkts0", 52 }, + { "icmppkts1", 53 }, + { "icmppkts2", 54 }, + { "icmppkts3", 55 }, + { NULL, 0 } +}; + + +void +printstatefield(sp, fieldnum) + ipstate_t *sp; + int fieldnum; +{ + int i; + + switch (fieldnum) + { + case -2 : + for (i = 1; statefields[i].w_word != NULL; i++) { + if (statefields[i].w_value > 0) { + printstatefield(sp, i); + if (statefields[i + 1].w_value > 0) + putchar('\t'); + } + } + break; + + case 1: + PRINTF("%#lx", (u_long)sp->is_ifp[0]); + break; + + case 2: + PRINTF("%#lx", (u_long)sp->is_ifp[1]); + break; + + case 3: + PRINTF("%#lx", (u_long)sp->is_ifp[2]); + break; + + case 4: + PRINTF("%#lx", (u_long)sp->is_ifp[3]); + break; + + case 5: + PRINTF("%s", sp->is_ifname[0]); + break; + + case 6: + PRINTF("%s", sp->is_ifname[1]); + break; + + case 7: + PRINTF("%s", sp->is_ifname[2]); + break; + + case 8: + PRINTF("%s", sp->is_ifname[3]); + break; + + case 9: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_pkts[0]); +#else + PRINTF("%lu", sp->is_pkts[0]); +#endif + break; + + case 10: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_pkts[1]); +#else + PRINTF("%lu", sp->is_pkts[1]); +#endif + break; + + case 11: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_pkts[2]); +#else + PRINTF("%lu", sp->is_pkts[2]); +#endif + break; + + case 12: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_pkts[3]); +#else + PRINTF("%lu", sp->is_pkts[3]); +#endif + break; + + case 13: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_bytes[0]); +#else + PRINTF("%lu", sp->is_bytes[0]); +#endif + break; + + case 14: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_bytes[1]); +#else + PRINTF("%lu", sp->is_bytes[1]); +#endif + break; + + case 15: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_bytes[2]); +#else + PRINTF("%lu", sp->is_bytes[2]); +#endif + break; + + case 16: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_bytes[3]); +#else + PRINTF("%lu", sp->is_bytes[3]); +#endif + break; + + case 17: + PRINTF("%d", sp->is_state[0]); + break; + + case 18: + PRINTF("%d", sp->is_state[1]); + break; + + case 19: + PRINTF("%d", sp->is_frage[0]); + break; + + case 20: + PRINTF("%d", sp->is_frage[1]); + break; + + case 21: + PRINTF("%d", sp->is_ref); + break; + + case 22: + PRINTF("%d", sp->is_isninc[0]); + break; + + case 23: + PRINTF("%d", sp->is_isninc[1]); + break; + + case 24: + PRINTF("%hd", sp->is_sumd[0]); + break; + + case 25: + PRINTF("%hd", sp->is_sumd[1]); + break; + + case 26: + PRINTF("%s", hostname(sp->is_v, &sp->is_src.in4)); + break; + + case 27: + PRINTF("%s", hostname(sp->is_v, &sp->is_dst.in4)); + break; + + case 28: + PRINTF("%hu", ntohs(sp->is_sport)); + break; + + case 29: + PRINTF("%hu", ntohs(sp->is_dport)); + break; + + case 30: + PRINTF("%d", sp->is_type); + break; + + case 32: + PRINTF("%#x", sp->is_pass); + break; + + case 33: + PRINTF("%d", sp->is_p); + break; + + case 34: + PRINTF("%d", sp->is_v); + break; + + case 35: + PRINTF("%d", sp->is_hv); + break; + + case 36: + PRINTF("%d", sp->is_tag); + break; + + case 37: + PRINTF("%#x", sp->is_flags); + break; + + case 38: + PRINTF("%d", sp->is_rulen); + break; + + case 39: + PRINTF("%s", sp->is_group); + break; + + case 40: + PRINTF("%#x", sp->is_flx[0][0]); + break; + + case 41: + PRINTF("%#x", sp->is_flx[0][1]); + break; + + case 42: + PRINTF("%#x", sp->is_flx[1][0]); + break; + + case 43: + PRINTF("%#x", sp->is_flx[1][1]); + break; + + case 44: + PRINTF("%#x", sp->is_opt[0]); + break; + + case 45: + PRINTF("%#x", sp->is_opt[1]); + break; + + case 46: + PRINTF("%#x", sp->is_optmsk[0]); + break; + + case 47: + PRINTF("%#x", sp->is_optmsk[1]); + break; + + case 48: + PRINTF("%#x", sp->is_sec); + break; + + case 49: + PRINTF("%#x", sp->is_secmsk); + break; + + case 50: + PRINTF("%#x", sp->is_auth); + break; + + case 51: + PRINTF("%#x", sp->is_authmsk); + break; + + case 52: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_icmppkts[0]); +#else + PRINTF("%lu", sp->is_icmppkts[0]); +#endif + break; + + case 53: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_icmppkts[1]); +#else + PRINTF("%lu", sp->is_icmppkts[1]); +#endif + break; + + case 54: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_icmppkts[2]); +#else + PRINTF("%lu", sp->is_icmppkts[2]); +#endif + break; + + case 55: +#ifdef USE_QUAD_T + PRINTF("%"PRIu64"", sp->is_icmppkts[3]); +#else + PRINTF("%lu", sp->is_icmppkts[3]); +#endif + break; + + default: + break; + } +} diff --git a/sbin/ipf/libipf/printtcpflags.c b/sbin/ipf/libipf/printtcpflags.c new file mode 100644 index 000000000000..9860780307a8 --- /dev/null +++ b/sbin/ipf/libipf/printtcpflags.c @@ -0,0 +1,30 @@ +#include "ipf.h" + + +void +printtcpflags(tcpf, tcpfm) + u_32_t tcpf, tcpfm; +{ + u_char *t; + char *s; + + if (tcpf & ~TCPF_ALL) { + PRINTF("0x%x", tcpf); + } else { + for (s = flagset, t = flags; *s; s++, t++) { + if (tcpf & *t) + (void)putchar(*s); + } + } + + if (tcpfm) { + (void)putchar('/'); + if (tcpfm & ~TCPF_ALL) { + PRINTF("0x%x", tcpfm); + } else { + for (s = flagset, t = flags; *s; s++, t++) + if (tcpfm & *t) + (void)putchar(*s); + } + } +} diff --git a/sbin/ipf/libipf/printtqtable.c b/sbin/ipf/libipf/printtqtable.c new file mode 100644 index 000000000000..ffb512dac42e --- /dev/null +++ b/sbin/ipf/libipf/printtqtable.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" + + +void +printtqtable(table) + ipftq_t *table; +{ + int i; + + PRINTF("TCP Entries per state\n"); + for (i = 0; i < IPF_TCP_NSTATES; i++) + PRINTF(" %5d", i); + PRINTF("\n"); + + for (i = 0; i < IPF_TCP_NSTATES; i++) + PRINTF(" %5d", table[i].ifq_ref - 1); + PRINTF("\n"); +} diff --git a/sbin/ipf/libipf/printtunable.c b/sbin/ipf/libipf/printtunable.c new file mode 100644 index 000000000000..aa82841b2fe1 --- /dev/null +++ b/sbin/ipf/libipf/printtunable.c @@ -0,0 +1,30 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +void +printtunable(tup) + ipftune_t *tup; +{ + PRINTF("%s\tmin %lu\tmax %lu\tcurrent ", + tup->ipft_name, tup->ipft_min, tup->ipft_max); + if (tup->ipft_sz == sizeof(u_long)) + PRINTF("%lu\n", tup->ipft_vlong); + else if (tup->ipft_sz == sizeof(u_int)) + PRINTF("%u\n", tup->ipft_vint); + else if (tup->ipft_sz == sizeof(u_short)) + PRINTF("%hu\n", tup->ipft_vshort); + else if (tup->ipft_sz == sizeof(u_char)) + PRINTF("%u\n", (u_int)tup->ipft_vchar); + else { + PRINTF("sz = %d\n", tup->ipft_sz); + } +} diff --git a/sbin/ipf/libipf/printunit.c b/sbin/ipf/libipf/printunit.c new file mode 100644 index 000000000000..bac3d45d34c5 --- /dev/null +++ b/sbin/ipf/libipf/printunit.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ + +#include "ipf.h" + + +void +printunit(unit) + int unit; +{ + + switch (unit) + { + case IPL_LOGIPF : + PRINTF("ipf"); + break; + case IPL_LOGNAT : + PRINTF("nat"); + break; + case IPL_LOGSTATE : + PRINTF("state"); + break; + case IPL_LOGAUTH : + PRINTF("auth"); + break; + case IPL_LOGSYNC : + PRINTF("sync"); + break; + case IPL_LOGSCAN : + PRINTF("scan"); + break; + case IPL_LOGLOOKUP : + PRINTF("lookup"); + break; + case IPL_LOGCOUNT : + PRINTF("count"); + break; + case IPL_LOGALL : + PRINTF("all"); + break; + default : + PRINTF("unknown(%d)", unit); + } +} diff --git a/sbin/ipf/libipf/remove_hash.c b/sbin/ipf/libipf/remove_hash.c new file mode 100644 index 000000000000..a60c1fddc7e9 --- /dev/null +++ b/sbin/ipf/libipf/remove_hash.c @@ -0,0 +1,50 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_htable.h" + + +int +remove_hash(iphp, iocfunc) + iphtable_t *iphp; + ioctlfunc_t iocfunc; +{ + iplookupop_t op; + iphtable_t iph; + + if (pool_open() == -1) + return -1; + + op.iplo_type = IPLT_HASH; + op.iplo_unit = iphp->iph_unit; + strncpy(op.iplo_name, iphp->iph_name, sizeof(op.iplo_name)); + if (*op.iplo_name == '\0') + op.iplo_arg = IPHASH_ANON; + op.iplo_size = sizeof(iph); + op.iplo_struct = &iph; + + bzero((char *)&iph, sizeof(iph)); + iph.iph_unit = iphp->iph_unit; + iph.iph_type = iphp->iph_type; + strncpy(iph.iph_name, iphp->iph_name, sizeof(iph.iph_name)); + iph.iph_flags = iphp->iph_flags; + + if (pool_ioctl(iocfunc, SIOCLOOKUPDELTABLE, &op)) { + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "remove lookup hash table"); + } + } + return 0; +} diff --git a/sbin/ipf/libipf/remove_hashnode.c b/sbin/ipf/libipf/remove_hashnode.c new file mode 100644 index 000000000000..58e9125015b6 --- /dev/null +++ b/sbin/ipf/libipf/remove_hashnode.c @@ -0,0 +1,56 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_htable.h" + + +int +remove_hashnode(unit, name, node, iocfunc) + int unit; + char *name; + iphtent_t *node; + ioctlfunc_t iocfunc; +{ + iplookupop_t op; + iphtent_t ipe; + + if (pool_open() == -1) + return -1; + + op.iplo_type = IPLT_HASH; + op.iplo_unit = unit; + op.iplo_size = sizeof(ipe); + op.iplo_struct = &ipe; + op.iplo_arg = 0; + strncpy(op.iplo_name, name, sizeof(op.iplo_name)); + + bzero((char *)&ipe, sizeof(ipe)); + bcopy((char *)&node->ipe_addr, (char *)&ipe.ipe_addr, + sizeof(ipe.ipe_addr)); + bcopy((char *)&node->ipe_mask, (char *)&ipe.ipe_mask, + sizeof(ipe.ipe_mask)); + + if (opts & OPT_DEBUG) { + printf("\t%s - ", inet_ntoa(ipe.ipe_addr.in4)); + printf("%s\n", inet_ntoa(ipe.ipe_mask.in4)); + } + + if (pool_ioctl(iocfunc, SIOCLOOKUPDELNODE, &op)) { + if (!(opts & OPT_DONOTHING)) { + return ipf_perror_fd(pool_fd(), iocfunc, + "remove lookup hash node"); + } + } + return 0; +} diff --git a/sbin/ipf/libipf/remove_pool.c b/sbin/ipf/libipf/remove_pool.c new file mode 100644 index 000000000000..8e7554963afd --- /dev/null +++ b/sbin/ipf/libipf/remove_pool.c @@ -0,0 +1,47 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_htable.h" + + +int +remove_pool(poolp, iocfunc) + ip_pool_t *poolp; + ioctlfunc_t iocfunc; +{ + iplookupop_t op; + ip_pool_t pool; + + if (pool_open() == -1) + return -1; + + op.iplo_type = IPLT_POOL; + op.iplo_unit = poolp->ipo_unit; + strncpy(op.iplo_name, poolp->ipo_name, sizeof(op.iplo_name)); + op.iplo_size = sizeof(pool); + op.iplo_struct = &pool; + + bzero((char *)&pool, sizeof(pool)); + pool.ipo_unit = poolp->ipo_unit; + strncpy(pool.ipo_name, poolp->ipo_name, sizeof(pool.ipo_name)); + pool.ipo_flags = poolp->ipo_flags; + + if (pool_ioctl(iocfunc, SIOCLOOKUPDELTABLE, &op)) { + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "delete lookup pool"); + } + } + return 0; +} diff --git a/sbin/ipf/libipf/remove_poolnode.c b/sbin/ipf/libipf/remove_poolnode.c new file mode 100644 index 000000000000..0b78118ec582 --- /dev/null +++ b/sbin/ipf/libipf/remove_poolnode.c @@ -0,0 +1,54 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <fcntl.h> +#include <sys/ioctl.h> +#include "ipf.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_pool.h" + + +int +remove_poolnode(unit, name, node, iocfunc) + int unit; + char *name; + ip_pool_node_t *node; + ioctlfunc_t iocfunc; +{ + ip_pool_node_t pn; + iplookupop_t op; + + if (pool_open() == -1) + return -1; + + op.iplo_unit = unit; + op.iplo_type = IPLT_POOL; + op.iplo_arg = 0; + strncpy(op.iplo_name, name, sizeof(op.iplo_name)); + op.iplo_struct = &pn; + op.iplo_size = sizeof(pn); + + bzero((char *)&pn, sizeof(pn)); + bcopy((char *)&node->ipn_addr, (char *)&pn.ipn_addr, + sizeof(pn.ipn_addr)); + bcopy((char *)&node->ipn_mask, (char *)&pn.ipn_mask, + sizeof(pn.ipn_mask)); + pn.ipn_info = node->ipn_info; + strncpy(pn.ipn_name, node->ipn_name, sizeof(pn.ipn_name)); + + if (pool_ioctl(iocfunc, SIOCLOOKUPDELNODE, &op)) { + if ((opts & OPT_DONOTHING) == 0) { + return ipf_perror_fd(pool_fd(), iocfunc, + "remove lookup pool node"); + } + } + + return 0; +} diff --git a/sbin/ipf/libipf/resetlexer.c b/sbin/ipf/libipf/resetlexer.c new file mode 100644 index 000000000000..558db98603ed --- /dev/null +++ b/sbin/ipf/libipf/resetlexer.c @@ -0,0 +1,25 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +long string_start = -1; +long string_end = -1; +char *string_val = NULL; +long pos = 0; + + +void resetlexer() +{ + string_start = -1; + string_end = -1; + string_val = NULL; + pos = 0; +} diff --git a/sbin/ipf/libipf/rwlock_emul.c b/sbin/ipf/libipf/rwlock_emul.c new file mode 100644 index 000000000000..f2f2ed19532a --- /dev/null +++ b/sbin/ipf/libipf/rwlock_emul.c @@ -0,0 +1,166 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + +#define EMM_MAGIC 0x97dd8b3a + +void eMrwlock_read_enter(rw, file, line) + eMrwlock_t *rw; + char *file; + int line; +{ + if (rw->eMrw_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMrwlock_read_enter(%p): bad magic: %#x\n", + rw->eMrw_owner, rw, rw->eMrw_magic); + abort(); + } + if (rw->eMrw_read != 0 || rw->eMrw_write != 0) { + fprintf(stderr, + "%s:eMrwlock_read_enter(%p): already locked: %d/%d\n", + rw->eMrw_owner, rw, rw->eMrw_read, rw->eMrw_write); + abort(); + } + rw->eMrw_read++; + rw->eMrw_heldin = file; + rw->eMrw_heldat = line; +} + + +void eMrwlock_write_enter(rw, file, line) + eMrwlock_t *rw; + char *file; + int line; +{ + if (rw->eMrw_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMrwlock_write_enter(%p): bad magic: %#x\n", + rw->eMrw_owner, rw, rw->eMrw_magic); + abort(); + } + if (rw->eMrw_read != 0 || rw->eMrw_write != 0) { + fprintf(stderr, + "%s:eMrwlock_write_enter(%p): already locked: %d/%d\n", + rw->eMrw_owner, rw, rw->eMrw_read, rw->eMrw_write); + abort(); + } + rw->eMrw_write++; + rw->eMrw_heldin = file; + rw->eMrw_heldat = line; +} + + +void eMrwlock_try_upgrade(rw, file, line) + eMrwlock_t *rw; + char *file; + int line; +{ + if (rw->eMrw_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMrwlock_write_enter(%p): bad magic: %#x\n", + rw->eMrw_owner, rw, rw->eMrw_magic); + abort(); + } + if (rw->eMrw_read != 0 || rw->eMrw_write != 0) { + fprintf(stderr, + "%s:eMrwlock_try_upgrade(%p): already locked: %d/%d\n", + rw->eMrw_owner, rw, rw->eMrw_read, rw->eMrw_write); + abort(); + } + rw->eMrw_write++; + rw->eMrw_heldin = file; + rw->eMrw_heldat = line; +} + +void eMrwlock_downgrade(rw, file, line) + eMrwlock_t *rw; + char *file; + int line; +{ + if (rw->eMrw_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMrwlock_write_enter(%p): bad magic: %#x\n", + rw->eMrw_owner, rw, rw->eMrw_magic); + abort(); + } + if (rw->eMrw_read != 0 || rw->eMrw_write != 1) { + fprintf(stderr, + "%s:eMrwlock_write_enter(%p): already locked: %d/%d\n", + rw->eMrw_owner, rw, rw->eMrw_read, rw->eMrw_write); + abort(); + } + rw->eMrw_write--; + rw->eMrw_read++; + rw->eMrw_heldin = file; + rw->eMrw_heldat = line; +} + + +void eMrwlock_exit(rw) + eMrwlock_t *rw; +{ + if (rw->eMrw_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMrwlock_exit(%p): bad magic: %#x\n", + rw->eMrw_owner, rw, rw->eMrw_magic); + abort(); + } + if (rw->eMrw_read != 1 && rw->eMrw_write != 1) { + fprintf(stderr, "%s:eMrwlock_exit(%p): not locked: %d/%d\n", + rw->eMrw_owner, rw, rw->eMrw_read, rw->eMrw_write); + abort(); + } + if (rw->eMrw_read == 1) + rw->eMrw_read--; + else if (rw->eMrw_write == 1) + rw->eMrw_write--; + rw->eMrw_heldin = NULL; + rw->eMrw_heldat = 0; +} + + +static int initcount = 0; + +void eMrwlock_init(rw, who) + eMrwlock_t *rw; + char *who; +{ + if (rw->eMrw_magic == EMM_MAGIC) { /* safe bet ? */ + fprintf(stderr, + "%s:eMrwlock_init(%p): already initialised?: %#x\n", + rw->eMrw_owner, rw, rw->eMrw_magic); + abort(); + } + rw->eMrw_magic = EMM_MAGIC; + rw->eMrw_read = 0; + rw->eMrw_write = 0; + if (who != NULL) + rw->eMrw_owner = strdup(who); + else + rw->eMrw_owner = NULL; + initcount++; +} + + +void eMrwlock_destroy(rw) + eMrwlock_t *rw; +{ + if (rw->eMrw_magic != EMM_MAGIC) { + fprintf(stderr, "%s:eMrwlock_destroy(%p): bad magic: %#x\n", + rw->eMrw_owner, rw, rw->eMrw_magic); + abort(); + } + if (rw->eMrw_owner != NULL) + free(rw->eMrw_owner); + memset(rw, 0xa5, sizeof(*rw)); + initcount--; +} + +void ipf_rwlock_clean() +{ + if (initcount != 0) + abort(); +} diff --git a/sbin/ipf/libipf/save_execute.c b/sbin/ipf/libipf/save_execute.c new file mode 100644 index 000000000000..68a3a3754900 --- /dev/null +++ b/sbin/ipf/libipf/save_execute.c @@ -0,0 +1,80 @@ +#include "ipf.h" +#include "ipmon.h" + +static void *execute_parse(char **); +static void execute_destroy(void *); +static int execute_send(void *, ipmon_msg_t *); +static void execute_print(void *); + +typedef struct execute_opts_s { + char *path; +} execute_opts_t; + +ipmon_saver_t executesaver = { + "execute", + execute_destroy, + NULL, /* dup */ + NULL, /* match */ + execute_parse, + execute_print, + execute_send +}; + + +static void * +execute_parse(char **strings) +{ + execute_opts_t *ctx; + + ctx = calloc(1, sizeof(*ctx)); + + if (ctx != NULL && strings[0] != NULL && strings[0][0] != '\0') { + ctx->path = strdup(strings[0]); + + } else { + free(ctx); + return NULL; + } + + return ctx; +} + + +static void +execute_print(ctx) + void *ctx; +{ + execute_opts_t *exe = ctx; + + printf("%s", exe->path); +} + + +static void +execute_destroy(ctx) + void *ctx; +{ + execute_opts_t *exe = ctx; + + if (exe != NULL) + free(exe->path); + free(exe); +} + + +static int +execute_send(ctx, msg) + void *ctx; + ipmon_msg_t *msg; +{ + execute_opts_t *exe = ctx; + FILE *fp; + + fp = popen(exe->path, "w"); + if (fp != NULL) { + fwrite(msg->imm_msg, msg->imm_msglen, 1, fp); + pclose(fp); + } + return 0; +} + diff --git a/sbin/ipf/libipf/save_file.c b/sbin/ipf/libipf/save_file.c new file mode 100644 index 000000000000..84ef157009f5 --- /dev/null +++ b/sbin/ipf/libipf/save_file.c @@ -0,0 +1,130 @@ +#include "ipf.h" +#include "ipmon.h" + +static void *file_parse(char **); +static void file_destroy(void *); +static int file_send(void *, ipmon_msg_t *); +static void file_print(void *); +static int file_match(void *, void *); +static void *file_dup(void *); + +typedef struct file_opts_s { + FILE *fp; + int raw; + char *path; + int ref; +} file_opts_t; + +ipmon_saver_t filesaver = { + "file", + file_destroy, + file_dup, + file_match, + file_parse, + file_print, + file_send +}; + + +static void * +file_parse(strings) + char **strings; +{ + file_opts_t *ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + if (strings[0] != NULL && strings[0][0] != '\0') { + ctx->ref = 1; + if (!strncmp(strings[0], "raw://", 6)) { + ctx->raw = 1; + ctx->path = strdup(strings[0] + 6); + ctx->fp = fopen(ctx->path, "ab"); + } else if (!strncmp(strings[0], "file://", 7)) { + ctx->path = strdup(strings[0] + 7); + ctx->fp = fopen(ctx->path, "a"); + } else { + free(ctx); + ctx = NULL; + } + } else { + free(ctx); + ctx = NULL; + } + + return ctx; +} + + +static int +file_match(ctx1, ctx2) + void *ctx1, *ctx2; +{ + file_opts_t *f1 = ctx1, *f2 = ctx2; + + if (f1->raw != f2->raw) + return 1; + if (strcmp(f1->path, f2->path)) + return 1; + return 0; +} + + +static void * +file_dup(ctx) + void *ctx; +{ + file_opts_t *f = ctx; + + f->ref++; + return f; +} + + +static void +file_print(ctx) + void *ctx; +{ + file_opts_t *file = ctx; + + if (file->raw) + printf("raw://"); + else + printf("file://"); + printf("%s", file->path); +} + + +static void +file_destroy(ctx) + void *ctx; +{ + file_opts_t *file = ctx; + + file->ref--; + if (file->ref > 0) + return; + + if (file->path != NULL) + free(file->path); + free(file); +} + + +static int +file_send(ctx, msg) + void *ctx; + ipmon_msg_t *msg; +{ + file_opts_t *file = ctx; + + if (file->raw) { + fwrite(msg->imm_data, msg->imm_dsize, 1, file->fp); + } else { + fprintf(file->fp, "%s", msg->imm_msg); + } + return 0; +} + diff --git a/sbin/ipf/libipf/save_nothing.c b/sbin/ipf/libipf/save_nothing.c new file mode 100644 index 000000000000..39158cf51ddb --- /dev/null +++ b/sbin/ipf/libipf/save_nothing.c @@ -0,0 +1,62 @@ +#include "ipf.h" +#include "ipmon.h" + +static void *nothing_parse(char **); +static void nothing_destroy(void *); +static int nothing_send(void *, ipmon_msg_t *); + +typedef struct nothing_opts_s { + FILE *fp; + int raw; + char *path; +} nothing_opts_t; + +ipmon_saver_t nothingsaver = { + "nothing", + nothing_destroy, + NULL, /* dup */ + NULL, /* match */ + nothing_parse, + NULL, /* print */ + nothing_send +}; + + +static void * +nothing_parse(char **strings) +{ + void *ctx; + +#if 0 + strings = strings; /* gcc -Wextra */ +#endif + + ctx = calloc(1, sizeof(void *)); + + return ctx; +} + + +static void +nothing_destroy(ctx) + void *ctx; +{ + free(ctx); +} + + +static int +nothing_send(ctx, msg) + void *ctx; + ipmon_msg_t *msg; +{ +#if 0 + ctx = ctx; /* gcc -Wextra */ + msg = msg; /* gcc -Wextra */ +#endif + /* + * Do nothing + */ + return 0; +} + diff --git a/sbin/ipf/libipf/save_syslog.c b/sbin/ipf/libipf/save_syslog.c new file mode 100644 index 000000000000..37d428bdb4aa --- /dev/null +++ b/sbin/ipf/libipf/save_syslog.c @@ -0,0 +1,137 @@ +#include "ipf.h" +#include "ipmon.h" +#include <syslog.h> + +static void *syslog_parse(char **); +static void syslog_destroy(void *); +static int syslog_send(void *, ipmon_msg_t *); +static void syslog_print(void *); + +typedef struct syslog_opts_s { + int facpri; + int fac; + int pri; +} syslog_opts_t; + +ipmon_saver_t syslogsaver = { + "syslog", + syslog_destroy, + NULL, /* dup */ + NULL, /* match */ + syslog_parse, + syslog_print, + syslog_send +}; + + +static void * +syslog_parse(char **strings) +{ + syslog_opts_t *ctx; + char *str; + char *s; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->facpri = -1; + + if (strings[0] != NULL && strings[0][0] != '\0') { + str = strdup(*strings); + if (str != NULL && *str != '\0') { + int fac = -1, pri = -1; + + s = strchr(str, '.'); + if (s != NULL) + *s++ = '\0'; + + if (*str != '\0') { + fac = fac_findname(str); + if (fac == -1) { + free(str); + free(ctx); + return NULL; + } + } + + if (s != NULL && *s != '\0') { + pri = pri_findname(s); + if (pri == -1) { + free(str); + free(ctx); + return NULL; + } + } + free(str); + + ctx->fac = fac; + ctx->pri = pri; + if (pri == -1) + ctx->facpri = fac; + else if (fac == -1) + ctx->facpri = pri; + else + ctx->facpri = fac | pri; + } else { + if (str != NULL) + free(str); + free(ctx); + ctx = NULL; + } + } + + return ctx; +} + + +static void +syslog_print(ctx) + void *ctx; +{ + syslog_opts_t *sys = ctx; + + if (sys->facpri == -1) + return; + + if (sys->fac == -1) { + printf(".%s", pri_toname(sys->pri)); + } else if (sys->pri == -1) { + printf("%s.", fac_toname(sys->fac)); + } else { + printf("%s.%s", fac_toname(sys->facpri & LOG_FACMASK), + pri_toname(sys->facpri & LOG_PRIMASK)); + } +} + + +static void +syslog_destroy(ctx) + void *ctx; +{ + free(ctx); +} + + +static int +syslog_send(ctx, msg) + void *ctx; + ipmon_msg_t *msg; +{ + syslog_opts_t *sys = ctx; + int facpri; + + if (sys->facpri == -1) { + facpri = msg->imm_loglevel; + } else { + if (sys->pri == -1) { + facpri = sys->fac | (msg->imm_loglevel & LOG_PRIMASK); + } else if (sys->fac == -1) { + facpri = sys->pri | (msg->imm_loglevel & LOG_FACMASK); + } else { + facpri = sys->facpri; + } + } + syslog(facpri, "%s", msg->imm_msg); + return 0; +} diff --git a/sbin/ipf/libipf/save_v1trap.c b/sbin/ipf/libipf/save_v1trap.c new file mode 100644 index 000000000000..cca61ac600e5 --- /dev/null +++ b/sbin/ipf/libipf/save_v1trap.c @@ -0,0 +1,463 @@ +#include "ipf.h" +#include "netinet/ipl.h" +#include "ipmon.h" +#include <ctype.h> + +#define IPF_ENTERPRISE 9932 +/* + * Enterprise number OID: + * 1.3.6.1.4.1.9932 + */ +static u_char ipf_enterprise[] = { 6, 7, 0x2b, 6, 1, 4, 1, 0xcd, 0x4c }; +static u_char ipf_trap0_1[] = { 6, 10, 0x2b, 6, 1, 4, 1, 0xcd, 0x4c, 1, 1, 1 }; +static u_char ipf_trap0_2[] = { 6, 10, 0x2b, 6, 1, 4, 1, 0xcd, 0x4c, 1, 1, 2 }; + +static int writeint(u_char *, int); +static int writelength(u_char *, u_int); +static int maketrap_v1(char *, u_char *, int, u_char *, int, u_32_t, + time_t); +static void snmpv1_destroy(void *); +static void *snmpv1_dup(void *); +static int snmpv1_match(void *, void *); +static void *snmpv1_parse(char **); +static void snmpv1_print(void *); +static int snmpv1_send(void *, ipmon_msg_t *); + +typedef struct snmpv1_opts_s { + char *community; + int fd; + int v6; + int ref; +#ifdef USE_INET6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin; +} snmpv1_opts_t; + +ipmon_saver_t snmpv1saver = { + "snmpv1", + snmpv1_destroy, + snmpv1_dup, /* dup */ + snmpv1_match, /* match */ + snmpv1_parse, + snmpv1_print, + snmpv1_send +}; + + +static int +snmpv1_match(ctx1, ctx2) + void *ctx1, *ctx2; +{ + snmpv1_opts_t *s1 = ctx1, *s2 = ctx2; + + if (s1->v6 != s2->v6) + return 1; + + if (strcmp(s1->community, s2->community)) + return 1; + +#ifdef USE_INET6 + if (s1->v6 == 1) { + if (memcmp(&s1->sin6, &s2->sin6, sizeof(s1->sin6))) + return 1; + } else +#endif + { + if (memcmp(&s1->sin, &s2->sin, sizeof(s1->sin))) + return 1; + } + + return 0; +} + + +static void * +snmpv1_dup(ctx) + void *ctx; +{ + snmpv1_opts_t *s = ctx; + + s->ref++; + return s; +} + + +static void +snmpv1_print(ctx) + void *ctx; +{ + snmpv1_opts_t *snmpv1 = ctx; + + printf("%s ", snmpv1->community); +#ifdef USE_INET6 + if (snmpv1->v6 == 1) { + char buf[80]; + + printf("%s", inet_ntop(AF_INET6, &snmpv1->sin6.sin6_addr, buf, + sizeof(snmpv1->sin6.sin6_addr))); + } else +#endif + { + printf("%s", inet_ntoa(snmpv1->sin.sin_addr)); + } +} + + +static void * +snmpv1_parse(char **strings) +{ + snmpv1_opts_t *ctx; + int result; + char *str; + char *s; + + if (strings[0] == NULL || strings[0][0] == '\0') + return NULL; + + if (strchr(*strings, ' ') == NULL) + return NULL; + + str = strdup(*strings); + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->fd = -1; + + s = strchr(str, ' '); + *s++ = '\0'; + ctx->community = str; + + while (ISSPACE(*s)) + s++; + if (!*s) { + free(str); + free(ctx); + return NULL; + } + +#ifdef USE_INET6 + if (strchr(s, ':') == NULL) { + result = inet_pton(AF_INET, s, &ctx->sin.sin_addr); + if (result == 1) { + ctx->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ctx->fd >= 0) { + ctx->sin.sin_family = AF_INET; + ctx->sin.sin_port = htons(162); + if (connect(ctx->fd, + (struct sockaddr *)&ctx->sin, + sizeof(ctx->sin)) != 0) { + snmpv1_destroy(ctx); + return NULL; + } + } + } + } else { + result = inet_pton(AF_INET6, s, &ctx->sin6.sin6_addr); + if (result == 1) { + ctx->v6 = 1; + ctx->fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (ctx->fd >= 0) { + ctx->sin6.sin6_family = AF_INET6; + ctx->sin6.sin6_port = htons(162); + if (connect(ctx->fd, + (struct sockaddr *)&ctx->sin6, + sizeof(ctx->sin6)) != 0) { + snmpv1_destroy(ctx); + return NULL; + } + } + } + } +#else + result = inet_aton(s, &ctx->sin.sin_addr); + if (result == 1) { + ctx->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ctx->fd >= 0) { + ctx->sin.sin_family = AF_INET; + ctx->sin.sin_port = htons(162); + if (connect(ctx->fd, (struct sockaddr *)&ctx->sin, + sizeof(ctx->sin)) != 0) { + snmpv1_destroy(ctx); + return NULL; + } + } + } +#endif + + if (result != 1) { + free(str); + free(ctx); + return NULL; + } + + ctx->ref = 1; + + return ctx; +} + + +static void +snmpv1_destroy(ctx) + void *ctx; +{ + snmpv1_opts_t *v1 = ctx; + + v1->ref--; + if (v1->ref > 0) + return; + + if (v1->community) + free(v1->community); + if (v1->fd >= 0) + close(v1->fd); + free(v1); +} + + +static int +snmpv1_send(ctx, msg) + void *ctx; + ipmon_msg_t *msg; +{ + snmpv1_opts_t *v1 = ctx; + + return sendtrap_v1_0(v1->fd, v1->community, + msg->imm_msg, msg->imm_msglen, msg->imm_when); +} + +static char def_community[] = "public"; /* ublic */ + +static int +writelength(buffer, value) + u_char *buffer; + u_int value; +{ + u_int n = htonl(value); + int len; + + if (value < 128) { + *buffer = value; + return 1; + } + if (value > 0xffffff) + len = 4; + else if (value > 0xffff) + len = 3; + else if (value > 0xff) + len = 2; + else + len = 1; + + *buffer = 0x80 | len; + + bcopy((u_char *)&n + 4 - len, buffer + 1, len); + + return len + 1; +} + + +static int +writeint(buffer, value) + u_char *buffer; + int value; +{ + u_char *s = buffer; + u_int n = value; + + if (value == 0) { + *buffer = 0; + return 1; + } + + if (n > 4194304) { + *s++ = 0x80 | (n / 4194304); + n -= 4194304 * (n / 4194304); + } + if (n > 32768) { + *s++ = 0x80 | (n / 32768); + n -= 32768 * (n / 327678); + } + if (n > 128) { + *s++ = 0x80 | (n / 128); + n -= (n / 128) * 128; + } + *s++ = (u_char)n; + + return s - buffer; +} + + + +/* + * First style of traps is: + * 1.3.6.1.4.1.9932.1.1 + */ +static int +maketrap_v1(community, buffer, bufsize, msg, msglen, ipaddr, when) + char *community; + u_char *buffer; + int bufsize; + u_char *msg; + int msglen; + u_32_t ipaddr; + time_t when; +{ + u_char *s = buffer, *t, *pdulen, *varlen; + int basesize = 73; + u_short len; + int trapmsglen; + int pdulensz; + int varlensz; + int baselensz; + int n; + + if (community == NULL || *community == '\0') + community = def_community; + basesize += strlen(community) + msglen; + + if (basesize + 8 > bufsize) + return 0; + + memset(buffer, 0xff, bufsize); + *s++ = 0x30; /* Sequence */ + if (basesize - 1 >= 128) { + baselensz = 2; + basesize++; + } else { + baselensz = 1; + } + s += baselensz; + *s++ = 0x02; /* Integer32 */ + *s++ = 0x01; /* length 1 */ + *s++ = 0x00; /* version 1 */ + *s++ = 0x04; /* octet string */ + *s++ = strlen(community); /* length of "public" */ + bcopy(community, s, s[-1]); + s += s[-1]; + *s++ = 0xA4; /* PDU(4) */ + pdulen = s++; + if (basesize - (s - buffer) >= 128) { + pdulensz = 2; + basesize++; + s++; + } else { + pdulensz = 1; + } + + /* enterprise */ + bcopy(ipf_enterprise, s, sizeof(ipf_enterprise)); + s += sizeof(ipf_enterprise); + + /* Agent address */ + *s++ = 0x40; + *s++ = 0x4; + bcopy(&ipaddr, s, 4); + s += 4; + + /* Generic Trap code */ + *s++ = 0x2; + n = writeint(s + 1, 6); + if (n == 0) + return 0; + *s = n; + s += n + 1; + + /* Specific Trap code */ + *s++ = 0x2; + n = writeint(s + 1, 0); + if (n == 0) + return 0; + *s = n; + s += n + 1; + + /* Time stamp */ + *s++ = 0x43; /* TimeTicks */ + *s++ = 0x04; /* TimeTicks */ + s[0] = when >> 24; + s[1] = when >> 16; + s[2] = when >> 8; + s[3] = when & 0xff; + s += 4; + + /* + * The trap0 message is "ipfilter_version" followed by the message + */ + *s++ = 0x30; + varlen = s; + if (basesize - (s - buffer) >= 128) { + varlensz = 2; + basesize++; + } else { + varlensz = 1; + } + s += varlensz; + + *s++ = 0x30; + t = s + 1; + bcopy(ipf_trap0_1, t, sizeof(ipf_trap0_1)); + t += sizeof(ipf_trap0_1); + + *t++ = 0x2; /* Integer */ + n = writeint(t + 1, IPFILTER_VERSION); + *t = n; + t += n + 1; + + len = t - s - 1; + writelength(s, len); + + s = t; + *s++ = 0x30; + if (basesize - (s - buffer) >= 128) { + trapmsglen = 2; + basesize++; + } else { + trapmsglen = 1; + } + t = s + trapmsglen; + bcopy(ipf_trap0_2, t, sizeof(ipf_trap0_2)); + t += sizeof(ipf_trap0_2); + + *t++ = 0x4; /* Octet string */ + n = writelength(t, msglen); + t += n; + bcopy(msg, t, msglen); + t += msglen; + + len = t - s - trapmsglen; + writelength(s, len); + + len = t - varlen - varlensz; + writelength(varlen, len); /* pdu length */ + + len = t - pdulen - pdulensz; + writelength(pdulen, len); /* pdu length */ + + len = t - buffer - baselensz - 1; + writelength(buffer + 1, len); /* length of trap */ + + return t - buffer; +} + + +int +sendtrap_v1_0(fd, community, msg, msglen, when) + int fd; + char *community, *msg; + int msglen; + time_t when; +{ + + u_char buffer[1500]; + int n; + + n = maketrap_v1(community, buffer, sizeof(buffer), + (u_char *)msg, msglen, 0, when); + if (n > 0) { + return send(fd, buffer, n, 0); + } + + return 0; +} diff --git a/sbin/ipf/libipf/save_v2trap.c b/sbin/ipf/libipf/save_v2trap.c new file mode 100644 index 000000000000..480f4290851d --- /dev/null +++ b/sbin/ipf/libipf/save_v2trap.c @@ -0,0 +1,461 @@ +#include "ipf.h" +#include "netinet/ipl.h" +#include "ipmon.h" +#include <ctype.h> + +static u_char sysuptime[] = { 6, 8, 0x2b, 6, 1, 2, 1, 1, 3, 0 }; +/* + * Enterprise number OID: + * 1.3.6.1.4.1.9932 + */ +static u_char ipf_trap0_1[] = { 6, 10, 0x2b, 6, 1, 4, 1, 0xcd, 0x4c, 1, 1, 1 }; +static u_char ipf_trap0_2[] = { 6, 10, 0x2b, 6, 1, 4, 1, 0xcd, 0x4c, 1, 1, 2 }; + +static int writeint(u_char *, int); +static int writelength(u_char *, u_int); +static int maketrap_v2(char *, u_char *, int, u_char *, int); +static void snmpv2_destroy(void *); +static void *snmpv2_dup(void *); +static int snmpv2_match(void *, void *); +static void *snmpv2_parse(char **); +static void snmpv2_print(void *); +static int snmpv2_send(void *, ipmon_msg_t *); + + +int sendtrap_v2_0(int, char *, char *, int); + +static char def_community[] = "public"; /* ublic */ + +typedef struct snmpv2_opts_s { + char *community; + char *server; + int fd; + int v6; + int ref; +#ifdef USE_INET6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin; +} snmpv2_opts_t; + +ipmon_saver_t snmpv2saver = { + "snmpv2", + snmpv2_destroy, + snmpv2_dup, /* dup */ + snmpv2_match, /* match */ + snmpv2_parse, + snmpv2_print, + snmpv2_send +}; + + +static int +snmpv2_match(ctx1, ctx2) + void *ctx1, *ctx2; +{ + snmpv2_opts_t *s1 = ctx1, *s2 = ctx2; + + if (s1->v6 != s2->v6) + return 1; + + if (strcmp(s1->community, s2->community)) + return 1; + +#ifdef USE_INET6 + if (s1->v6 == 1) { + if (memcmp(&s1->sin6, &s2->sin6, sizeof(s1->sin6))) + return 1; + } else +#endif + { + if (memcmp(&s1->sin, &s2->sin, sizeof(s1->sin))) + return 1; + } + + return 0; +} + + +static void * +snmpv2_dup(ctx) + void *ctx; +{ + snmpv2_opts_t *s = ctx; + + s->ref++; + return s; +} + + +static void +snmpv2_print(ctx) + void *ctx; +{ + snmpv2_opts_t *snmpv2 = ctx; + + printf("%s ", snmpv2->community); +#ifdef USE_INET6 + if (snmpv2->v6 == 1) { + char buf[80]; + + printf("%s", inet_ntop(AF_INET6, &snmpv2->sin6.sin6_addr, buf, + sizeof(snmpv2->sin6.sin6_addr))); + } else +#endif + { + printf("%s", inet_ntoa(snmpv2->sin.sin_addr)); + } +} + + +static void * +snmpv2_parse(char **strings) +{ + snmpv2_opts_t *ctx; + int result; + char *str; + char *s; + + if (strings[0] == NULL || strings[0][0] == '\0') + return NULL; + if (strchr(*strings, ' ') == NULL) + return NULL; + + str = strdup(*strings); + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + free(str); + return NULL; + } + + ctx->fd = -1; + + s = strchr(str, ' '); + *s++ = '\0'; + ctx->community = str; + + while (ISSPACE(*s)) + s++; + if (!*s) { + free(str); + free(ctx); + return NULL; + } + +#ifdef USE_INET6 + if (strchr(s, ':') == NULL) { + result = inet_pton(AF_INET, s, &ctx->sin.sin_addr); + if (result == 1) { + ctx->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ctx->fd >= 0) { + ctx->sin.sin_family = AF_INET; + ctx->sin.sin_port = htons(162); + if (connect(ctx->fd, + (struct sockaddr *)&ctx->sin, + sizeof(ctx->sin)) != 0) { + snmpv2_destroy(ctx); + return NULL; + } + } + } + } else { + result = inet_pton(AF_INET6, s, &ctx->sin6.sin6_addr); + if (result == 1) { + ctx->v6 = 1; + ctx->fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (ctx->fd >= 0) { + ctx->sin6.sin6_family = AF_INET6; + ctx->sin6.sin6_port = htons(162); + if (connect(ctx->fd, + (struct sockaddr *)&ctx->sin6, + sizeof(ctx->sin6)) != 0) { + snmpv2_destroy(ctx); + return NULL; + } + } + } + } +#else + result = inet_aton(s, &ctx->sin.sin_addr); + if (result == 1) { + ctx->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ctx->fd >= 0) { + ctx->sin.sin_family = AF_INET; + ctx->sin.sin_port = htons(162); + if (connect(ctx->fd, (struct sockaddr *)&ctx->sin, + sizeof(ctx->sin)) != 0) { + snmpv2_destroy(ctx); + return NULL; + } + } + } +#endif + + if (result != 1) { + free(str); + free(ctx); + return NULL; + } + + ctx->ref = 1; + + return ctx; +} + + +static void +snmpv2_destroy(ctx) + void *ctx; +{ + snmpv2_opts_t *v2 = ctx; + + v2->ref--; + if (v2->ref > 0) + return; + + if (v2->community) + free(v2->community); + if (v2->fd >= 0) + close(v2->fd); + free(v2); +} + + +static int +snmpv2_send(ctx, msg) + void *ctx; + ipmon_msg_t *msg; +{ + snmpv2_opts_t *v2 = ctx; + + return sendtrap_v2_0(v2->fd, v2->community, + msg->imm_msg, msg->imm_msglen); +} +static int +writelength(buffer, value) + u_char *buffer; + u_int value; +{ + u_int n = htonl(value); + int len; + + if (value < 128) { + *buffer = value; + return 1; + } + if (value > 0xffffff) + len = 4; + else if (value > 0xffff) + len = 3; + else if (value > 0xff) + len = 2; + else + len = 1; + + *buffer = 0x80 | len; + + bcopy((u_char *)&n + 4 - len, buffer + 1, len); + + return len + 1; +} + + +static int +writeint(buffer, value) + u_char *buffer; + int value; +{ + u_char *s = buffer; + u_int n = value; + + if (value == 0) { + *buffer = 0; + return 1; + } + + if (n > 4194304) { + *s++ = 0x80 | (n / 4194304); + n -= 4194304 * (n / 4194304); + } + if (n > 32768) { + *s++ = 0x80 | (n / 32768); + n -= 32768 * (n / 327678); + } + if (n > 128) { + *s++ = 0x80 | (n / 128); + n -= (n / 128) * 128; + } + *s++ = (u_char)n; + + return s - buffer; +} + + + +/* + * First style of traps is: + * 1.3.6.1.4.1.9932.1.1 + */ +static int +maketrap_v2(community, buffer, bufsize, msg, msglen) + char *community; + u_char *buffer; + int bufsize; + u_char *msg; + int msglen; +{ + u_char *s = buffer, *t, *pdulen; + u_char *varlen; + int basesize = 77; + u_short len; + int trapmsglen; + int pdulensz; + int varlensz; + int baselensz; + int n; + + if (community == NULL || *community == '\0') + community = def_community; + basesize += strlen(community) + msglen; + + if (basesize + 8 > bufsize) + return 0; + + memset(buffer, 0xff, bufsize); + *s++ = 0x30; /* Sequence */ + + if (basesize - 1 >= 128) { + baselensz = 2; + basesize++; + } else { + baselensz = 1; + } + s += baselensz; + *s++ = 0x02; /* Integer32 */ + *s++ = 0x01; /* length 1 */ + *s++ = 0x01; /* version 2 */ + *s++ = 0x04; /* octet string */ + *s++ = strlen(community); /* length of "public" */ + bcopy(community, s, s[-1]); + s += s[-1]; + *s++ = 0xA7; /* PDU(7) */ + pdulen = s++; + if (basesize - (s - buffer) >= 128) { + pdulensz = 2; + basesize++; + s++; + } else { + pdulensz = 1; + } + /* request id */ + *s++ = 0x2; /* integer */ + *s++ = 0x4; /* len 4 */ + *s++ = 0x0; /* noError */ + *s++ = 0x0; /* noError */ + *s++ = 0x0; /* noError */ + *s++ = 0x0; /* noError */ + + /* error status */ + *s++ = 0x2; /* integer */ + *s++ = 0x1; /* len 1 */ + *s++ = 0x0; /* noError */ + + /* error-index */ + *s++ = 0x2; /* integer */ + *s++ = 0x1; /* len 1 */ + *s++ = 0x0; /* noError */ + + *s++ = 0x30; /* sequence */ + varlen = s++; + if (basesize - (s - buffer) >= 128) { + varlensz = 2; + basesize++; + s++; + } else { + varlensz = 1; + } + + *s++ = 0x30; /* sequence */ + *s++ = sizeof(sysuptime) + 6; + + bcopy(sysuptime, s, sizeof(sysuptime)); + s += sizeof(sysuptime); + + *s++ = 0x43; /* Timestamp */ + *s++ = 0x04; /* TimeTicks */ + *s++ = 0x0; + *s++ = 0x0; + *s++ = 0x0; + *s++ = 0x0; + + *s++ = 0x30; + t = s + 1; + bcopy(ipf_trap0_1, t, sizeof(ipf_trap0_1)); + t += sizeof(ipf_trap0_1); + + *t++ = 0x2; /* Integer */ + n = writeint(t + 1, IPFILTER_VERSION); + *t = n; + t += n + 1; + + len = t - s - 1; + writelength(s, len); + + s = t; + *s++ = 0x30; + if (msglen < 128) { + if (msglen + 1 + 1 + sizeof(ipf_trap0_2) >= 128) + trapmsglen = 2; + else + trapmsglen = 1; + } else { + if (msglen + 2 + 1 + sizeof(ipf_trap0_2) >= 128) + trapmsglen = 2; + else + trapmsglen = 1; + } + t = s + trapmsglen; + bcopy(ipf_trap0_2, t, sizeof(ipf_trap0_2)); + t += sizeof(ipf_trap0_2); + + *t++ = 0x4; /* Octet string */ + n = writelength(t, msglen); + t += n; + bcopy(msg, t, msglen); + t += msglen; + + len = t - s - trapmsglen; + writelength(s, len); + + len = t - varlen - varlensz; + writelength(varlen, len); /* pdu length */ + + len = t - pdulen - pdulensz; + writelength(pdulen, len); /* pdu length */ + + len = t - buffer - baselensz - 1; + writelength(buffer + 1, len); /* length of trap */ + + return t - buffer; +} + + +int +sendtrap_v2_0(fd, community, msg, msglen) + int fd; + char *community, *msg; + int msglen; +{ + + u_char buffer[1500]; + int n; + + n = maketrap_v2(community, buffer, sizeof(buffer), + (u_char *)msg, msglen); + if (n > 0) { + return send(fd, buffer, n, 0); + } + + return 0; +} diff --git a/sbin/ipf/libipf/tcp_flags.c b/sbin/ipf/libipf/tcp_flags.c new file mode 100644 index 000000000000..0b602e66ab30 --- /dev/null +++ b/sbin/ipf/libipf/tcp_flags.c @@ -0,0 +1,50 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2000-2004 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id: tcp_flags.c,v 1.8.2.1 2006/06/16 17:21:17 darrenr Exp $ + */ + +#include "ipf.h" + +extern char flagset[]; +extern u_char flags[]; + + +u_char tcp_flags(flgs, mask, linenum) +char *flgs; +u_char *mask; +int linenum; +{ + u_char tcpf = 0, tcpfm = 0; + char *s; + + s = strchr(flgs, '/'); + if (s) + *s++ = '\0'; + + if (*flgs == '0') { + tcpf = strtol(flgs, NULL, 0); + } else { + tcpf = tcpflags(flgs); + } + + if (s != NULL) { + if (*s == '0') + tcpfm = strtol(s, NULL, 0); + else + tcpfm = tcpflags(s); + } + + if (!tcpfm) { + if (tcpf == TH_SYN) + tcpfm = 0xff & ~(TH_ECN|TH_CWR); + else + tcpfm = 0xff & ~(TH_ECN); + } + *mask = tcpfm; + return tcpf; +} diff --git a/sbin/ipf/libipf/tcpflags.c b/sbin/ipf/libipf/tcpflags.c new file mode 100644 index 000000000000..feb3e8af04ac --- /dev/null +++ b/sbin/ipf/libipf/tcpflags.c @@ -0,0 +1,45 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +/* + * ECN is a new addition to TCP - RFC 2481 + */ +#ifndef TH_ECN +# define TH_ECN 0x40 +#endif +#ifndef TH_CWR +# define TH_CWR 0x80 +#endif + +extern char flagset[]; +extern u_char flags[]; + + +u_char tcpflags(flgs) + char *flgs; +{ + u_char tcpf = 0; + char *s, *t; + + for (s = flgs; *s; s++) { + if (*s == 'W') + tcpf |= TH_CWR; + else { + if (!(t = strchr(flagset, *s))) { + return 0; + } + tcpf |= flags[t - flagset]; + } + } + return tcpf; +} diff --git a/sbin/ipf/libipf/tcpoptnames.c b/sbin/ipf/libipf/tcpoptnames.c new file mode 100644 index 000000000000..24e41bb18b8b --- /dev/null +++ b/sbin/ipf/libipf/tcpoptnames.c @@ -0,0 +1,22 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include "ipf.h" + + +struct ipopt_names tcpoptnames[] ={ + { TCPOPT_NOP, 0x000001, 1, "nop" }, + { TCPOPT_MAXSEG, 0x000002, 4, "maxseg" }, + { TCPOPT_WINDOW, 0x000004, 3, "wscale" }, + { TCPOPT_SACK_PERMITTED, 0x000008, 2, "sackok" }, + { TCPOPT_SACK, 0x000010, 3, "sack" }, + { TCPOPT_TIMESTAMP, 0x000020, 10, "tstamp" }, + { 0, 0, 0, (char *)NULL } /* must be last */ +}; diff --git a/sbin/ipf/libipf/v6ionames.c b/sbin/ipf/libipf/v6ionames.c new file mode 100644 index 000000000000..9f1207f13431 --- /dev/null +++ b/sbin/ipf/libipf/v6ionames.c @@ -0,0 +1,28 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + + +#ifdef USE_INET6 + +struct ipopt_names v6ionames[] ={ + { IPPROTO_HOPOPTS, 0x000001, 0, "hopopts" }, + { IPPROTO_IPV6, 0x000002, 0, "ipv6" }, + { IPPROTO_ROUTING, 0x000004, 0, "routing" }, + { IPPROTO_FRAGMENT, 0x000008, 0, "frag" }, + { IPPROTO_ESP, 0x000010, 0, "esp" }, + { IPPROTO_AH, 0x000020, 0, "ah" }, + { IPPROTO_NONE, 0x000040, 0, "none" }, + { IPPROTO_DSTOPTS, 0x000080, 0, "dstopts" }, + { IPPROTO_MOBILITY, 0x000100, 0, "mobility" }, + { 0, 0, 0, (char *)NULL } +}; + +#endif diff --git a/sbin/ipf/libipf/v6optvalue.c b/sbin/ipf/libipf/v6optvalue.c new file mode 100644 index 000000000000..a6eff9221256 --- /dev/null +++ b/sbin/ipf/libipf/v6optvalue.c @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ +#include "ipf.h" + + + +u_32_t getv6optbyname(optname) + char *optname; +{ +#ifdef USE_INET6 + struct ipopt_names *io; + + for (io = v6ionames; io->on_name; io++) + if (!strcasecmp(optname, io->on_name)) + return io->on_bit; +#endif + return -1; +} + + +u_32_t getv6optbyvalue(optval) + int optval; +{ +#ifdef USE_INET6 + struct ipopt_names *io; + + for (io = v6ionames; io->on_name; io++) + if (io->on_value == optval) + return io->on_bit; +#endif + return -1; +} diff --git a/sbin/ipf/libipf/var.c b/sbin/ipf/libipf/var.c new file mode 100644 index 000000000000..22d5b2ac1b1a --- /dev/null +++ b/sbin/ipf/libipf/var.c @@ -0,0 +1,179 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +#include <ctype.h> + +#include "ipf.h" + +typedef struct variable { + struct variable *v_next; + char *v_name; + char *v_value; +} variable_t; + +static variable_t *vtop = NULL; + +static variable_t *find_var(char *); +static char *expand_string(char *, int); + + +static variable_t *find_var(name) + char *name; +{ + variable_t *v; + + for (v = vtop; v != NULL; v = v->v_next) + if (!strcmp(name, v->v_name)) + return v; + return NULL; +} + + +char *get_variable(string, after, line) + char *string, **after; + int line; +{ + char c, *s, *t, *value; + variable_t *v; + + s = string; + + if (*s == '{') { + s++; + for (t = s; *t != '\0'; t++) + if (*t == '}') + break; + if (*t == '\0') { + fprintf(stderr, "%d: { without }\n", line); + return NULL; + } + } else if (ISALPHA(*s)) { + for (t = s + 1; *t != '\0'; t++) + if (!ISALPHA(*t) && !ISDIGIT(*t) && (*t != '_')) + break; + } else { + fprintf(stderr, "%d: variables cannot start with '%c'\n", + line, *s); + return NULL; + } + + if (after != NULL) + *after = t; + c = *t; + *t = '\0'; + v = find_var(s); + *t = c; + if (v == NULL) { + fprintf(stderr, "%d: unknown variable '%s'\n", line, s); + return NULL; + } + + s = strdup(v->v_value); + value = expand_string(s, line); + if (value != s) + free(s); + return value; +} + + +static char *expand_string(oldstring, line) + char *oldstring; + int line; +{ + char c, *s, *p1, *p2, *p3, *newstring, *value; + int len; + + p3 = NULL; + newstring = oldstring; + + for (s = oldstring; *s != '\0'; s++) + if (*s == '$') { + *s = '\0'; + s++; + + switch (*s) + { + case '$' : + bcopy(s, s - 1, strlen(s)); + break; + default : + c = *s; + if (c == '\0') + return newstring; + + value = get_variable(s, &p3, line); + if (value == NULL) + return NULL; + + p2 = expand_string(value, line); + if (p2 == NULL) + return NULL; + + len = strlen(newstring) + strlen(p2); + if (p3 != NULL) { + if (c == '{' && *p3 == '}') + p3++; + len += strlen(p3); + } + p1 = malloc(len + 1); + if (p1 == NULL) + return NULL; + + *(s - 1) = '\0'; + strcpy(p1, newstring); + strcat(p1, p2); + if (p3 != NULL) + strcat(p1, p3); + + s = p1 + len - strlen(p3) - 1; + if (newstring != oldstring) + free(newstring); + newstring = p1; + break; + } + } + return newstring; +} + + +void set_variable(name, value) + char *name; + char *value; +{ + variable_t *v; + int len; + + if (name == NULL || value == NULL || *name == '\0') + return; + + v = find_var(name); + if (v != NULL) { + free(v->v_value); + v->v_value = strdup(value); + return; + } + + len = strlen(value); + + if ((*value == '"' && value[len - 1] == '"') || + (*value == '\'' && value[len - 1] == '\'')) { + value[len - 1] = '\0'; + value++; + len -=2; + } + + v = (variable_t *)malloc(sizeof(*v)); + if (v == NULL) + return; + v->v_name = strdup(name); + v->v_value = strdup(value); + v->v_next = vtop; + vtop = v; +} diff --git a/sbin/ipf/libipf/verbose.c b/sbin/ipf/libipf/verbose.c new file mode 100644 index 000000000000..47988c084516 --- /dev/null +++ b/sbin/ipf/libipf/verbose.c @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ + +# include <stdarg.h> +#include <stdio.h> + +#include "ipf.h" +#include "opts.h" + + +void verbose(int level, char *fmt, ...) +{ + va_list pvar; + + va_start(pvar, fmt); + + if (opts & OPT_VERBOSE) + vprintf(fmt, pvar); + va_end(pvar); +} + + +void ipfkverbose(char *fmt, ...) +{ + va_list pvar; + + va_start(pvar, fmt); + + if (opts & OPT_VERBOSE) + verbose(0x1fffffff, fmt, pvar); + va_end(pvar); +} diff --git a/sbin/ipf/libipf/vtof.c b/sbin/ipf/libipf/vtof.c new file mode 100644 index 000000000000..fd1a98432aa8 --- /dev/null +++ b/sbin/ipf/libipf/vtof.c @@ -0,0 +1,16 @@ +#include "ipf.h" + +int +vtof(version) + int version; +{ +#ifdef USE_INET6 + if (version == 6) + return AF_INET6; +#endif + if (version == 4) + return AF_INET; + if (version == 0) + return AF_UNSPEC; + return -1; +} |