diff --git a/contrib/ipfilter/bpf_filter.c b/contrib/ipfilter/bpf_filter.c index bd465761a34e..85a38a88bf57 100644 --- a/contrib/ipfilter/bpf_filter.c +++ b/contrib/ipfilter/bpf_filter.c @@ -1,595 +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 #include #include #include #include #include #include "netinet/ip_compat.h" #include "bpf-ipf.h" #if (defined(__hpux) || SOLARIS) && (defined(_KERNEL) || defined(KERNEL)) # include # include #endif #include "pcap-ipf.h" #if !defined(KERNEL) && !defined(_KERNEL) #include #endif #define int32 bpf_int32 #define u_int32 bpf_u_int32 -static int m_xword __P((mb_t *, int, int *)); -static int m_xhalf __P((mb_t *, int, int *)); +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/contrib/ipfilter/ip_dstlist.c b/contrib/ipfilter/ip_dstlist.c index 99c7a22668df..14b071b0b95f 100644 --- a/contrib/ipfilter/ip_dstlist.c +++ b/contrib/ipfilter/ip_dstlist.c @@ -1,1343 +1,1343 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # include # undef _KERNEL #else # include # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) # include # endif #endif #include # include #include #if defined(_KERNEL) && !defined(__SVR4) # include #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD_version) # include #endif #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" /* END OF INCLUDES */ #ifdef HAS_SYS_MD5_H # include #else # include "md5.h" #endif #if !defined(lint) static const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $"; #endif typedef struct ipf_dstl_softc_s { ippool_dst_t *dstlist[LOOKUP_POOL_SZ]; ippool_dst_t **tails[LOOKUP_POOL_SZ]; ipf_dstl_stat_t stats; } ipf_dstl_softc_t; -static void *ipf_dstlist_soft_create __P((ipf_main_softc_t *)); -static void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *)); -static int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *)); -static void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *)); -static int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int, - void *, u_int)); -static size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *, - iplookupflush_t *)); -static int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int, - void *)); -static int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, - ipflookupiter_t *)); -static int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *, - iplookupop_t *, int)); -static int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *, - iplookupop_t *, int)); -static int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *)); -static void *ipf_dstlist_table_find __P((void *, int, char *)); -static void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *)); -static void ipf_dstlist_table_remove __P((ipf_main_softc_t *, - ipf_dstl_softc_t *, ippool_dst_t *)); -static void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *, - ippool_dst_t *)); -static ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *)); -static void *ipf_dstlist_select_ref __P((void *, int, char *)); -static void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *)); -static int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *)); -static void ipf_dstlist_expire __P((ipf_main_softc_t *, void *)); -static void ipf_dstlist_sync __P((ipf_main_softc_t *, void *)); +static void *ipf_dstlist_soft_create(ipf_main_softc_t *); +static void ipf_dstlist_soft_destroy(ipf_main_softc_t *, void *); +static int ipf_dstlist_soft_init(ipf_main_softc_t *, void *); +static void ipf_dstlist_soft_fini(ipf_main_softc_t *, void *); +static int ipf_dstlist_addr_find(ipf_main_softc_t *, void *, int, + void *, u_int); +static size_t ipf_dstlist_flush(ipf_main_softc_t *, void *, + iplookupflush_t *); +static int ipf_dstlist_iter_deref(ipf_main_softc_t *, void *, int, int, + void *); +static int ipf_dstlist_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, + ipflookupiter_t *); +static int ipf_dstlist_node_add(ipf_main_softc_t *, void *, + iplookupop_t *, int); +static int ipf_dstlist_node_del(ipf_main_softc_t *, void *, + iplookupop_t *, int); +static int ipf_dstlist_stats_get(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_dstlist_table_add(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_dstlist_table_del(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_dstlist_table_deref(ipf_main_softc_t *, void *, void *); +static void *ipf_dstlist_table_find(void *, int, char *); +static void ipf_dstlist_table_free(ipf_dstl_softc_t *, ippool_dst_t *); +static void ipf_dstlist_table_remove(ipf_main_softc_t *, + ipf_dstl_softc_t *, ippool_dst_t *); +static void ipf_dstlist_table_clearnodes(ipf_dstl_softc_t *, + ippool_dst_t *); +static ipf_dstnode_t *ipf_dstlist_select(fr_info_t *, ippool_dst_t *); +static void *ipf_dstlist_select_ref(void *, int, char *); +static void ipf_dstlist_node_free(ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *); +static int ipf_dstlist_node_deref(void *, ipf_dstnode_t *); +static void ipf_dstlist_expire(ipf_main_softc_t *, void *); +static void ipf_dstlist_sync(ipf_main_softc_t *, void *); ipf_lookup_t ipf_dstlist_backend = { IPLT_DSTLIST, ipf_dstlist_soft_create, ipf_dstlist_soft_destroy, ipf_dstlist_soft_init, ipf_dstlist_soft_fini, ipf_dstlist_addr_find, ipf_dstlist_flush, ipf_dstlist_iter_deref, ipf_dstlist_iter_next, ipf_dstlist_node_add, ipf_dstlist_node_del, ipf_dstlist_stats_get, ipf_dstlist_table_add, ipf_dstlist_table_del, ipf_dstlist_table_deref, ipf_dstlist_table_find, ipf_dstlist_select_ref, ipf_dstlist_select_node, ipf_dstlist_expire, ipf_dstlist_sync }; /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_create */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocating a chunk of memory filled with 0's is enough for the current */ /* soft context used with destination lists. */ /* ------------------------------------------------------------------------ */ static void * ipf_dstlist_soft_create(softc) ipf_main_softc_t *softc; { ipf_dstl_softc_t *softd; int i; KMALLOC(softd, ipf_dstl_softc_t *); if (softd == NULL) { IPFERROR(120028); return NULL; } bzero((char *)softd, sizeof(*softd)); for (i = 0; i <= IPL_LOGMAX; i++) softd->tails[i] = &softd->dstlist[i]; return softd; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* For destination lists, the only thing we have to do when destroying the */ /* soft context is free it! */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_dstl_softc_t *softd = arg; KFREE(softd); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There is currently no soft context for destination list management. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There is currently no soft context for destination list management. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_dstl_softc_t *softd = arg; int i; for (i = -1; i <= IPL_LOGMAX; i++) { while (softd->dstlist[i + 1] != NULL) { ipf_dstlist_table_remove(softc, softd, softd->dstlist[i + 1]); } } ASSERT(softd->stats.ipls_numderefnodes == 0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_addr_find */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg1(I) - pointer to local context to use */ /* arg2(I) - pointer to local context to use */ /* arg3(I) - pointer to local context to use */ /* arg4(I) - pointer to local context to use */ /* */ /* There is currently no such thing as searching a destination list for an */ /* address so this function becomes a no-op. Its presence is required as */ /* ipf_lookup_res_name() stores the "addr_find" function pointer in the */ /* pointer passed in to it as funcptr, although it could be a generic null- */ /* op function rather than a specific one. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int ipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4) ipf_main_softc_t *softc; void *arg1, *arg3; int arg2; u_int arg4; { return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_flush */ /* Returns: int - number of objects deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* fop(I) - pointer to lookup flush operation data */ /* */ /* Flush all of the destination tables that match the data passed in with */ /* the iplookupflush_t. There are two ways to match objects: the device for */ /* which they are to be used with and their name. */ /* ------------------------------------------------------------------------ */ static size_t ipf_dstlist_flush(softc, arg, fop) ipf_main_softc_t *softc; void *arg; iplookupflush_t *fop; { ipf_dstl_softc_t *softd = arg; ippool_dst_t *node, *next; int n, i; for (n = 0, i = -1; i <= IPL_LOGMAX; i++) { if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i) continue; for (node = softd->dstlist[i + 1]; node != NULL; node = next) { next = node->ipld_next; if ((*fop->iplf_name != '\0') && strncmp(fop->iplf_name, node->ipld_name, FR_GROUPLEN)) continue; ipf_dstlist_table_remove(softc, softd, node); n++; } } return n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_iter_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* otype(I) - type of data structure to iterate through */ /* unit(I) - device we are working with */ /* data(I) - address of object in kernel space */ /* */ /* This function is called when the iteration token is being free'd and is */ /* responsible for dropping the reference count of the structure it points */ /* to. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_iter_deref(softc, arg, otype, unit, data) ipf_main_softc_t *softc; void *arg; int otype, unit; void *data; { if (data == NULL) { IPFERROR(120001); return EINVAL; } if (unit < -1 || unit > IPL_LOGMAX) { IPFERROR(120002); return EINVAL; } switch (otype) { case IPFLOOKUPITER_LIST : ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data); break; case IPFLOOKUPITER_NODE : ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data); break; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_iter_next */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* */ /* This function is responsible for either selecting the next destination */ /* list or node on a destination list to be returned as a user process */ /* iterates through the list of destination lists or nodes. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_iter_next(softc, arg, token, iter) ipf_main_softc_t *softc; void *arg; ipftoken_t *token; ipflookupiter_t *iter; { ipf_dstnode_t zn, *nextnode = NULL, *node = NULL; ippool_dst_t zero, *next = NULL, *dsttab = NULL; ipf_dstl_softc_t *softd = arg; int err = 0; void *hint; switch (iter->ili_otype) { case IPFLOOKUPITER_LIST : dsttab = token->ipt_data; if (dsttab == NULL) { next = softd->dstlist[(int)iter->ili_unit + 1]; } else { next = dsttab->ipld_next; } if (next != NULL) { ATOMIC_INC32(next->ipld_ref); token->ipt_data = next; hint = next->ipld_next; } else { bzero((char *)&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; hint = NULL; } break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { dsttab = ipf_dstlist_table_find(arg, iter->ili_unit, iter->ili_name); if (dsttab == NULL) { IPFERROR(120004); err = ESRCH; nextnode = NULL; } else { if (dsttab->ipld_dests == NULL) nextnode = NULL; else nextnode = *dsttab->ipld_dests; dsttab = NULL; } } else { nextnode = node->ipfd_next; } if (nextnode != NULL) { MUTEX_ENTER(&nextnode->ipfd_lock); nextnode->ipfd_ref++; MUTEX_EXIT(&nextnode->ipfd_lock); token->ipt_data = nextnode; hint = nextnode->ipfd_next; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; hint = NULL; } break; default : IPFERROR(120003); err = EINVAL; break; } if (err != 0) return err; switch (iter->ili_otype) { case IPFLOOKUPITER_LIST : if (dsttab != NULL) ipf_dstlist_table_deref(softc, arg, dsttab); err = COPYOUT(next, iter->ili_data, sizeof(*next)); if (err != 0) { IPFERROR(120005); err = EFAULT; } break; case IPFLOOKUPITER_NODE : if (node != NULL) ipf_dstlist_node_deref(arg, node); err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode)); if (err != 0) { IPFERROR(120006); err = EFAULT; } break; } if (hint == NULL) ipf_token_mark_complete(token); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Add a new node to a destination list. To do this, we only copy in the */ /* frdest_t structure because that contains the only data required from the */ /* application to create a new node. The frdest_t doesn't contain the name */ /* itself. When loading filter rules, fd_name is a 'pointer' to the name. */ /* In this case, the 'pointer' does not work, instead it is the length of */ /* the name and the name is immediately following the frdest_t structure. */ /* fd_name must include the trailing \0, so it should be strlen(str) + 1. */ /* For simple sanity checking, an upper bound on the size of fd_name is */ /* imposed - 128. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_node_add(softc, arg, op, uid) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; int uid; { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node, **nodes; ippool_dst_t *d; frdest_t dest; int err; if (op->iplo_size < sizeof(frdest_t)) { IPFERROR(120007); return EINVAL; } err = COPYIN(op->iplo_struct, &dest, sizeof(dest)); if (err != 0) { IPFERROR(120009); return EFAULT; } d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120010); return ESRCH; } switch (dest.fd_addr.adf_family) { case AF_INET : case AF_INET6 : break; default : IPFERROR(120019); return EINVAL; } if (dest.fd_name < -1 || dest.fd_name > 128) { IPFERROR(120018); return EINVAL; } KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name); if (node == NULL) { softd->stats.ipls_nomem++; IPFERROR(120008); return ENOMEM; } bzero((char *)node, sizeof(*node) + dest.fd_name); bcopy(&dest, &node->ipfd_dest, sizeof(dest)); node->ipfd_size = sizeof(*node) + dest.fd_name; if (dest.fd_name > 0) { /* * fd_name starts out as the length of the string to copy * in (including \0) and ends up being the offset from * fd_names (0). */ err = COPYIN((char *)op->iplo_struct + sizeof(dest), node->ipfd_names, dest.fd_name); if (err != 0) { IPFERROR(120017); KFREES(node, node->ipfd_size); return EFAULT; } node->ipfd_dest.fd_name = 0; } else { node->ipfd_dest.fd_name = -1; } if (d->ipld_nodes == d->ipld_maxnodes) { KMALLOCS(nodes, ipf_dstnode_t **, sizeof(*nodes) * (d->ipld_maxnodes + 1)); if (nodes == NULL) { softd->stats.ipls_nomem++; IPFERROR(120022); KFREES(node, node->ipfd_size); return ENOMEM; } if (d->ipld_dests != NULL) { bcopy(d->ipld_dests, nodes, sizeof(*nodes) * d->ipld_maxnodes); KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes); nodes[0]->ipfd_pnext = nodes; } d->ipld_dests = nodes; d->ipld_maxnodes++; } d->ipld_dests[d->ipld_nodes] = node; d->ipld_nodes++; if (d->ipld_nodes == 1) { node->ipfd_pnext = d->ipld_dests; } else if (d->ipld_nodes > 1) { node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next; } *node->ipfd_pnext = node; MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock"); node->ipfd_uid = uid; node->ipfd_ref = 1; if (node->ipfd_dest.fd_name == 0) (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET); #ifdef USE_INET6 if (node->ipfd_dest.fd_name == 0 && node->ipfd_dest.fd_ptr == (void *)-1) (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET6); #endif softd->stats.ipls_numnodes++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* node(I) - pointer to destionation node to free */ /* */ /* Dereference the use count by one. If it drops to zero then we can assume */ /* that it has been removed from any lists/tables and is ripe for freeing. */ /* The pointer to context is required for the purpose of maintaining */ /* statistics. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_node_deref(arg, node) void *arg; ipf_dstnode_t *node; { ipf_dstl_softc_t *softd = arg; int ref; MUTEX_ENTER(&node->ipfd_lock); ref = --node->ipfd_ref; MUTEX_EXIT(&node->ipfd_lock); if (ref > 0) return 0; if ((node->ipfd_flags & IPDST_DELETE) != 0) softd->stats.ipls_numderefnodes--; MUTEX_DESTROY(&node->ipfd_lock); KFREES(node, node->ipfd_size); softd->stats.ipls_numnodes--; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* */ /* Look for a matching destination node on the named table and free it if */ /* found. Because the name embedded in the frdest_t is variable in length, */ /* it is necessary to allocate some memory locally, to complete this op. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_node_del(softc, arg, op, uid) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; int uid; { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node; frdest_t frd, *temp; ippool_dst_t *d; size_t size; int err; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120012); return ESRCH; } err = COPYIN(op->iplo_struct, &frd, sizeof(frd)); if (err != 0) { IPFERROR(120011); return EFAULT; } size = sizeof(*temp) + frd.fd_name; KMALLOCS(temp, frdest_t *, size); if (temp == NULL) { softd->stats.ipls_nomem++; IPFERROR(120026); return ENOMEM; } err = COPYIN(op->iplo_struct, temp, size); if (err != 0) { IPFERROR(120027); return EFAULT; } MUTEX_ENTER(&d->ipld_lock); for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) { if ((uid != 0) && (node->ipfd_uid != uid)) continue; if (node->ipfd_size != size) continue; if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6, size - offsetof(frdest_t, fd_ip6))) { ipf_dstlist_node_free(softd, d, node); MUTEX_EXIT(&d->ipld_lock); KFREES(temp, size); return 0; } } MUTEX_EXIT(&d->ipld_lock); KFREES(temp, size); return ESRCH; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_free */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* node(I) - pointer to node to free */ /* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */ /* */ /* Free the destination node by first removing it from any lists and then */ /* checking if this was the last reference held to the object. While the */ /* array of pointers to nodes is compacted, its size isn't reduced (by way */ /* of allocating a new smaller one and copying) because the belief is that */ /* it is likely the array will again reach that size. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_node_free(softd, d, node) ipf_dstl_softc_t *softd; ippool_dst_t *d; ipf_dstnode_t *node; { int i; /* * Compact the array of pointers to nodes. */ for (i = 0; i < d->ipld_nodes; i++) if (d->ipld_dests[i] == node) break; if (d->ipld_nodes - i > 1) { bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i], sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1)); } d->ipld_nodes--; if (node->ipfd_pnext != NULL) *node->ipfd_pnext = node->ipfd_next; if (node->ipfd_next != NULL) node->ipfd_next->ipfd_pnext = node->ipfd_pnext; node->ipfd_pnext = NULL; node->ipfd_next = NULL; if ((node->ipfd_flags & IPDST_DELETE) == 0) { softd->stats.ipls_numderefnodes++; node->ipfd_flags |= IPDST_DELETE; } ipf_dstlist_node_deref(softd, node); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_stats_get */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Return the current statistics for destination lists. This may be for all */ /* of them or just information pertaining to a particular table. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int ipf_dstlist_stats_get(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ipf_dstl_softc_t *softd = arg; ipf_dstl_stat_t stats; int unit, i, err = 0; if (op->iplo_size != sizeof(ipf_dstl_stat_t)) { IPFERROR(120023); return EINVAL; } stats = softd->stats; unit = op->iplo_unit; if (unit == IPL_LOGALL) { for (i = 0; i <= IPL_LOGMAX; i++) stats.ipls_list[i] = softd->dstlist[i]; } else if (unit >= 0 && unit <= IPL_LOGMAX) { void *ptr; if (op->iplo_name[0] != '\0') ptr = ipf_dstlist_table_find(softd, unit, op->iplo_name); else ptr = softd->dstlist[unit + 1]; stats.ipls_list[unit] = ptr; } else { IPFERROR(120024); err = EINVAL; } if (err == 0) { err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); if (err != 0) { IPFERROR(120025); return EFAULT; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Add a new destination table to the list of those available for the given */ /* device. Because we seldom operate on these objects (find/add/delete), */ /* they are just kept in a simple linked list. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_table_add(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ipf_dstl_softc_t *softd = arg; ippool_dst_t user, *d, *new; int unit, err; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d != NULL) { IPFERROR(120013); return EEXIST; } err = COPYIN(op->iplo_struct, &user, sizeof(user)); if (err != 0) { IPFERROR(120021); return EFAULT; } KMALLOC(new, ippool_dst_t *); if (new == NULL) { softd->stats.ipls_nomem++; IPFERROR(120014); return ENOMEM; } bzero((char *)new, sizeof(*new)); MUTEX_INIT(&new->ipld_lock, "ipf dst table lock"); strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN); unit = op->iplo_unit; new->ipld_unit = unit; new->ipld_policy = user.ipld_policy; new->ipld_seed = ipf_random(); new->ipld_ref = 1; new->ipld_pnext = softd->tails[unit + 1]; *softd->tails[unit + 1] = new; softd->tails[unit + 1] = &new->ipld_next; softd->stats.ipls_numlists++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Find a named destinstion list table and delete it. If there are other */ /* references to it, the caller isn't told. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_table_del(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ippool_dst_t *d; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120015); return ESRCH; } if (d->ipld_dests != NULL) { IPFERROR(120016); return EBUSY; } ipf_dstlist_table_remove(softc, arg, d); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_remove */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* */ /* Remove a given destination list from existance. While the IPDST_DELETE */ /* flag is set every time we call this function and the reference count is */ /* non-zero, the "numdereflists" counter is always incremented because the */ /* decision about whether it will be freed or not is not made here. This */ /* means that the only action the code can take here is to treat it as if */ /* it will become a detached. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_table_remove(softc, softd, d) ipf_main_softc_t *softc; ipf_dstl_softc_t *softd; ippool_dst_t *d; { if (softd->tails[d->ipld_unit + 1] == &d->ipld_next) softd->tails[d->ipld_unit + 1] = d->ipld_pnext; if (d->ipld_pnext != NULL) *d->ipld_pnext = d->ipld_next; if (d->ipld_next != NULL) d->ipld_next->ipld_pnext = d->ipld_pnext; d->ipld_pnext = NULL; d->ipld_next = NULL; ipf_dstlist_table_clearnodes(softd, d); softd->stats.ipls_numdereflists++; d->ipld_flags |= IPDST_DELETE; ipf_dstlist_table_deref(softc, softd, d); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_free */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* */ /* Free up a destination list data structure and any other memory that was */ /* directly allocated as part of creating it. Individual destination list */ /* nodes are not freed. It is assumed the caller will have already emptied */ /* the destination list. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_table_free(softd, d) ipf_dstl_softc_t *softd; ippool_dst_t *d; { MUTEX_DESTROY(&d->ipld_lock); if ((d->ipld_flags & IPDST_DELETE) != 0) softd->stats.ipls_numdereflists--; softd->stats.ipls_numlists--; if (d->ipld_dests != NULL) { KFREES(d->ipld_dests, d->ipld_maxnodes * sizeof(*d->ipld_dests)); } KFREE(d); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Drops the reference count on a destination list table object and free's */ /* it if 0 has been reached. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_table_deref(softc, arg, table) ipf_main_softc_t *softc; void *arg; void *table; { ippool_dst_t *d = table; d->ipld_ref--; if (d->ipld_ref > 0) return d->ipld_ref; ipf_dstlist_table_free(arg, d); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_clearnodes */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* dst(I) - pointer to destination list */ /* */ /* Free all of the destination nodes attached to the given table. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_table_clearnodes(softd, dst) ipf_dstl_softc_t *softd; ippool_dst_t *dst; { ipf_dstnode_t *node; if (dst->ipld_dests == NULL) return; while ((node = *dst->ipld_dests) != NULL) { ipf_dstlist_node_free(softd, dst, node); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_find */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - device we are working with */ /* name(I) - destination table name to find */ /* */ /* Return a pointer to a destination table that matches the unit+name that */ /* is passed in. */ /* ------------------------------------------------------------------------ */ static void * ipf_dstlist_table_find(arg, unit, name) void *arg; int unit; char *name; { ipf_dstl_softc_t *softd = arg; ippool_dst_t *d; for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) { if ((d->ipld_unit == unit) && !strncmp(d->ipld_name, name, FR_GROUPLEN)) { return d; } } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select_ref */ /* Returns: void * - NULL = failure, else pointer to table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - device we are working with */ /* name(I) - destination table name to find */ /* */ /* Attempt to find a destination table that matches the name passed in and */ /* if successful, bump up the reference count on it because we intend to */ /* store the pointer to it somewhere else. */ /* ------------------------------------------------------------------------ */ static void * ipf_dstlist_select_ref(arg, unit, name) void *arg; int unit; char *name; { ippool_dst_t *d; d = ipf_dstlist_table_find(arg, unit, name); if (d != NULL) { MUTEX_ENTER(&d->ipld_lock); d->ipld_ref++; MUTEX_EXIT(&d->ipld_lock); } return d; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select */ /* Returns: void * - NULL = failure, else pointer to table */ /* Parameters: fin(I) - pointer to packet information */ /* d(I) - pointer to destination list */ /* */ /* Find the next node in the destination list to be used according to the */ /* defined policy. Of these, "connection" is the most expensive policy to */ /* implement as it always looks for the node with the least number of */ /* connections associated with it. */ /* */ /* The hashes exclude the port numbers so that all protocols map to the */ /* same destination. Otherwise, someone doing a ping would target a */ /* different server than their TCP connection, etc. MD-5 is used to */ /* transform the addressese into something random that the other end could */ /* not easily guess and use in an attack. ipld_seed introduces an unknown */ /* into the hash calculation to increase the difficult of an attacker */ /* guessing the bucket. */ /* */ /* One final comment: mixing different address families in a single pool */ /* will currently result in failures as the address family of the node is */ /* only matched up with that in the packet as the last step. While this can */ /* be coded around for the weighted connection and round-robin models, it */ /* cannot be supported for the hash/random models as they do not search and */ /* nor is the algorithm conducive to searching. */ /* ------------------------------------------------------------------------ */ static ipf_dstnode_t * ipf_dstlist_select(fin, d) fr_info_t *fin; ippool_dst_t *d; { ipf_dstnode_t *node, *sel; int connects; u_32_t hash[4]; MD5_CTX ctx; int family; int x; if (d->ipld_dests == NULL || *d->ipld_dests == NULL) return NULL; family = fin->fin_family; MUTEX_ENTER(&d->ipld_lock); switch (d->ipld_policy) { case IPLDP_ROUNDROBIN: sel = d->ipld_selected; if (sel == NULL) { sel = *d->ipld_dests; } else { sel = sel->ipfd_next; if (sel == NULL) sel = *d->ipld_dests; } break; case IPLDP_CONNECTION: if (d->ipld_selected == NULL) { sel = *d->ipld_dests; break; } sel = d->ipld_selected; connects = 0x7fffffff; node = sel->ipfd_next; if (node == NULL) node = *d->ipld_dests; while (node != d->ipld_selected) { if (node->ipfd_states == 0) { sel = node; break; } if (node->ipfd_states < connects) { sel = node; connects = node->ipfd_states; } node = node->ipfd_next; if (node == NULL) node = *d->ipld_dests; } break; case IPLDP_RANDOM : x = ipf_random() % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_HASHED : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_src6, sizeof(fin->fin_src6)); MD5Update(&ctx, (u_char *)&fin->fin_dst6, sizeof(fin->fin_dst6)); MD5Final((u_char *)hash, &ctx); x = hash[0] % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_SRCHASH : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_src6, sizeof(fin->fin_src6)); MD5Final((u_char *)hash, &ctx); x = hash[0] % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_DSTHASH : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_dst6, sizeof(fin->fin_dst6)); MD5Final((u_char *)hash, &ctx); x = hash[0] % d->ipld_nodes; sel = d->ipld_dests[x]; break; default : sel = NULL; break; } if (sel->ipfd_dest.fd_addr.adf_family != family) sel = NULL; d->ipld_selected = sel; MUTEX_EXIT(&d->ipld_lock); return sel; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select_node */ /* Returns: int - -1 == failure, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* group(I) - destination pool to search */ /* addr(I) - pointer to store selected address */ /* pfdp(O) - pointer to storage for selected destination node */ /* */ /* This function is only responsible for obtaining the next IP address for */ /* use and storing it in the caller's address space (addr). "addr" is only */ /* used for storage if pfdp is NULL. No permanent reference is currently */ /* kept on the node. */ /* ------------------------------------------------------------------------ */ int ipf_dstlist_select_node(fin, group, addr, pfdp) fr_info_t *fin; void *group; u_32_t *addr; frdest_t *pfdp; { #ifdef USE_MUTEXES ipf_main_softc_t *softc = fin->fin_main_soft; #endif ippool_dst_t *d = group; ipf_dstnode_t *node; frdest_t *fdp; READ_ENTER(&softc->ipf_poolrw); node = ipf_dstlist_select(fin, d); if (node == NULL) { RWLOCK_EXIT(&softc->ipf_poolrw); return -1; } if (pfdp != NULL) { bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp)); } else { if (fin->fin_family == AF_INET) { addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; } else if (fin->fin_family == AF_INET6) { addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1]; addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2]; addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3]; } } fdp = &node->ipfd_dest; if (fdp->fd_ptr == NULL) fdp->fd_ptr = fin->fin_ifp; MUTEX_ENTER(&node->ipfd_lock); node->ipfd_states++; MUTEX_EXIT(&node->ipfd_lock); RWLOCK_EXIT(&softc->ipf_poolrw); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There are currently no objects to expire in destination lists. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_expire(softc, arg) ipf_main_softc_t *softc; void *arg; { return; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_sync */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* When a network interface appears or disappears, we need to revalidate */ /* all of the network interface names that have been configured as a target */ /* in a destination list. */ /* ------------------------------------------------------------------------ */ void ipf_dstlist_sync(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node; ippool_dst_t *list; int i; int j; for (i = 0; i < IPL_LOGMAX; i++) { for (list = softd->dstlist[i]; list != NULL; list = list->ipld_next) { for (j = 0; j < list->ipld_maxnodes; j++) { node = list->ipld_dests[j]; if (node == NULL) continue; if (node->ipfd_dest.fd_name == -1) continue; (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET); } } } } diff --git a/contrib/ipfilter/ip_dstlist.h b/contrib/ipfilter/ip_dstlist.h index e2885e5c47ad..c4acd78dfd94 100644 --- a/contrib/ipfilter/ip_dstlist.h +++ b/contrib/ipfilter/ip_dstlist.h @@ -1,68 +1,68 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_dstlist.h,v 1.5.2.6 2012/07/22 08:04:23 darren_r Exp $ */ #ifndef __IP_DSTLIST_H__ #define __IP_DSTLIST_H__ typedef struct ipf_dstnode { struct ipf_dstnode *ipfd_next; struct ipf_dstnode **ipfd_pnext; ipfmutex_t ipfd_lock; frdest_t ipfd_dest; u_long ipfd_syncat; int ipfd_flags; int ipfd_size; int ipfd_states; int ipfd_ref; int ipfd_uid; char ipfd_names[1]; } ipf_dstnode_t; typedef enum ippool_policy_e { IPLDP_NONE = 0, IPLDP_ROUNDROBIN, IPLDP_CONNECTION, IPLDP_RANDOM, IPLDP_HASHED, IPLDP_SRCHASH, IPLDP_DSTHASH } ippool_policy_t; typedef struct ippool_dst { struct ippool_dst *ipld_next; struct ippool_dst **ipld_pnext; ipfmutex_t ipld_lock; int ipld_seed; int ipld_unit; int ipld_ref; int ipld_flags; int ipld_nodes; int ipld_maxnodes; ippool_policy_t ipld_policy; ipf_dstnode_t **ipld_dests; ipf_dstnode_t *ipld_selected; char ipld_name[FR_GROUPLEN]; } ippool_dst_t; #define IPDST_DELETE 0x01 typedef struct dstlist_stat_s { void *ipls_list[LOOKUP_POOL_SZ]; int ipls_numlists; u_long ipls_nomem; int ipls_numnodes; int ipls_numdereflists; int ipls_numderefnodes; } ipf_dstl_stat_t; extern ipf_lookup_t ipf_dstlist_backend; -extern int ipf_dstlist_select_node __P((fr_info_t *, void *, u_32_t *, - frdest_t *)); +extern int ipf_dstlist_select_node(fr_info_t *, void *, u_32_t *, + frdest_t *); #endif /* __IP_DSTLIST_H__ */ diff --git a/contrib/ipfilter/ip_fil.c b/contrib/ipfilter/ip_fil.c index 794d7e205bb3..327f90fc356c 100644 --- a/contrib/ipfilter/ip_fil.c +++ b/contrib/ipfilter/ip_fil.c @@ -1,812 +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 __P((struct ifnet *, char *)); -void init_ifp __P((void)); -static int no_output __P((struct ifnet *, struct mbuf *, - struct sockaddr *, struct rtentry *)); -static int write_output __P((struct ifnet *, struct mbuf *, - struct sockaddr *, 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/contrib/ipfilter/ip_fil_compat.c b/contrib/ipfilter/ip_fil_compat.c index 271c2e065738..fbcfc8a0088f 100644 --- a/contrib/ipfilter/ip_fil_compat.c +++ b/contrib/ipfilter/ip_fil_compat.c @@ -1,4811 +1,4811 @@ /* * Copyright (C) 2002-2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if defined(__FreeBSD_version) && defined(_KERNEL) # include # include #else # include #endif #if !defined(_KERNEL) # include # define _KERNEL # include # undef _KERNEL #endif #include #include #if defined(__FreeBSD__) # include # include #endif #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #include "netinet/ip_lookup.h" #include "netinet/ip_nat.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" /* END OF INCLUDES */ /* * NetBSD has moved to 64bit time_t for all architectures. * For some, such as sparc64, there is no change because long is already * 64bit, but for others (i386), there is... */ #ifdef IPFILTER_COMPAT # ifdef __NetBSD__ typedef struct timeval_l { long tv_sec; long tv_usec; } timeval_l_t; # endif /* ------------------------------------------------------------------------ */ typedef struct tcpinfo4 { u_short ts_sport; u_short ts_dport; tcpdata_t ts_data[2]; } tcpinfo4_t; -static void ipf_v5tcpinfoto4 __P((tcpinfo_t *, tcpinfo4_t *)); +static void ipf_v5tcpinfoto4(tcpinfo_t *, tcpinfo4_t *); static void ipf_v5tcpinfoto4(v5, v4) tcpinfo_t *v5; tcpinfo4_t *v4; { v4->ts_sport = v5->ts_sport; v4->ts_dport = v5->ts_dport; v4->ts_data[0] = v5->ts_data[0]; v4->ts_data[1] = v5->ts_data[1]; } typedef struct fr_ip4 { u_32_t fi_v:4; u_32_t fi_xx:4; u_32_t fi_tos:8; u_32_t fi_ttl:8; u_32_t fi_p:8; u_32_t fi_optmsk; i6addr_t fi_src; i6addr_t fi_dst; u_short ofi_secmsk; u_short ofi_auth; u_32_t fi_flx; u_32_t fi_tcpmsk; u_32_t fi_res1; } frip4_t; typedef struct frpcmp4 { int frp_cmp; u_short frp_port; u_short frp_top; } frpcmp4_t; typedef struct frtuc4 { u_char ftu_tcpfm; u_char ftu_tcpf; frpcmp4_t ftu_src; frpcmp4_t ftu_dst; } frtuc4_t; typedef struct fripf4 { frip4_t fri_ip; frip4_t fri_mip; u_short fri_icmpm; u_short fri_icmp; frtuc4_t fri_tuc; int fri_satype; int fri_datype; int fri_sifpidx; int fri_difpidx; } fripf4_t; typedef struct frdest_4 { void *fd_ifp; i6addr_t ofd_ip6; char fd_ifname[LIFNAMSIZ]; } frdest_4_t; /* ------------------------------------------------------------------------ */ /* 5.1.0 new release (current) * 4.1.34 changed the size of the time structure used for pps * 4.1.16 moved the location of fr_flineno * 4.1.0 base version */ typedef struct frentry_4_1_34 { ipfmutex_t fr_lock; struct frentry *fr_next; struct frentry **fr_grp; struct ipscan *fr_isc; void *fr_ifas[4]; void *fr_ptr; /* for use with fr_arg */ char *fr_comment; /* text comment for rule */ int fr_ref; /* reference count - for grouping */ int fr_statecnt; /* state count - for limit rules */ int fr_flineno; /* line number from conf file */ U_QUAD_T fr_hits; U_QUAD_T fr_bytes; union { struct timeval frp_lastpkt; char frp_bytes[12]; } fr_lpu; int fr_curpps; union { void *fru_data; char *fru_caddr; fripf4_t *fru_ipf; frentfunc_t fru_func; } fr_dun; ipfunc_t fr_func; /* call this function */ int fr_dsize; int fr_pps; int fr_statemax; /* max reference count */ u_32_t fr_type; u_32_t fr_flags; /* per-rule flags && options (see below) */ u_32_t fr_logtag; /* user defined log tag # */ u_32_t fr_collect; /* collection number */ u_int fr_arg; /* misc. numeric arg for rule */ u_int fr_loglevel; /* syslog log facility + priority */ u_int fr_age[2]; /* non-TCP timeouts */ u_char fr_v; u_char fr_icode; /* return ICMP code */ char fr_group[FR_GROUPLEN]; /* group to which this rule belongs */ char fr_grhead[FR_GROUPLEN]; /* group # which this rule starts */ ipftag_t fr_nattag; char fr_ifnames[4][LIFNAMSIZ]; char fr_isctag[16]; frdest_4_t fr_tifs[2]; /* "to"/"reply-to" interface */ frdest_4_t fr_dif; /* duplicate packet interface */ u_int fr_cksum; /* checksum on filter rules for performance */ } frentry_4_1_34_t; typedef struct frentry_4_1_16 { ipfmutex_t fr_lock; struct frentry *fr_next; struct frentry **fr_grp; struct ipscan *fr_isc; void *fr_ifas[4]; void *fr_ptr; char *fr_comment; int fr_ref; int fr_statecnt; int fr_flineno; U_QUAD_T fr_hits; U_QUAD_T fr_bytes; union { #ifdef __NetBSD__ timeval_l_t frp_lastpkt; #else struct timeval frp_lastpkt; #endif } fr_lpu; int fr_curpps; union { void *fru_data; caddr_t fru_caddr; fripf4_t *fru_ipf; frentfunc_t fru_func; } fr_dun; ipfunc_t fr_func; int fr_dsize; int fr_pps; int fr_statemax; u_32_t fr_type; u_32_t fr_flags; u_32_t fr_logtag; u_32_t fr_collect; u_int fr_arg; u_int fr_loglevel; u_int fr_age[2]; u_char fr_v; u_char fr_icode; char fr_group[FR_GROUPLEN]; char fr_grhead[FR_GROUPLEN]; ipftag_t fr_nattag; char fr_ifnames[4][LIFNAMSIZ]; char fr_isctag[16]; frdest_4_t fr_tifs[2]; frdest_4_t fr_dif; u_int fr_cksum; } frentry_4_1_16_t; typedef struct frentry_4_1_0 { ipfmutex_t fr_lock; struct frentry *fr_next; struct frentry **fr_grp; struct ipscan *fr_isc; void *fr_ifas[4]; void *fr_ptr; char *fr_comment; int fr_ref; int fr_statecnt; U_QUAD_T fr_hits; U_QUAD_T fr_bytes; union { #ifdef __NetBSD__ timeval_l_t frp_lastpkt; #else struct timeval frp_lastpkt; #endif } fr_lpu; int fr_curpps; union { void *fru_data; caddr_t fru_caddr; fripf4_t *fru_ipf; frentfunc_t fru_func; } fr_dun; /* * Fields after this may not change whilst in the kernel. */ ipfunc_t fr_func; int fr_dsize; int fr_pps; int fr_statemax; int fr_flineno; u_32_t fr_type; u_32_t fr_flags; u_32_t fr_logtag; u_32_t fr_collect; u_int fr_arg; u_int fr_loglevel; u_int fr_age[2]; u_char fr_v; u_char fr_icode; char fr_group[FR_GROUPLEN]; char fr_grhead[FR_GROUPLEN]; ipftag_t fr_nattag; char fr_ifnames[4][LIFNAMSIZ]; char fr_isctag[16]; frdest_4_t fr_tifs[2]; frdest_4_t fr_dif; u_int fr_cksum; } frentry_4_1_0_t; /* ------------------------------------------------------------------------ */ /* * 5.1.0 new release (current) * 4.1.32 removed both fin_state and fin_nat, added fin_pktnum * 4.1.24 added fin_cksum * 4.1.23 added fin_exthdr * 4.1.11 added fin_ifname * 4.1.4 added fin_hbuf */ typedef struct fr_info_4_1_32 { void *fin_ifp; /* interface packet is `on' */ frip4_t fin_fi; /* IP Packet summary */ union { u_short fid_16[2]; /* TCP/UDP ports, ICMP code/type */ u_32_t fid_32; } fin_dat; int fin_out; /* in or out ? 1 == out, 0 == in */ int fin_rev; /* state only: 1 = reverse */ u_short fin_hlen; /* length of IP header in bytes */ u_char ofin_tcpf; /* TCP header flags (SYN, ACK, etc) */ u_char fin_icode; /* ICMP error to return */ u_32_t fin_rule; /* rule # last matched */ char fin_group[FR_GROUPLEN]; /* group number, -1 for none */ struct frentry *fin_fr; /* last matching rule */ void *fin_dp; /* start of data past IP header */ int fin_dlen; /* length of data portion of packet */ int fin_plen; int fin_ipoff; /* # bytes from buffer start to hdr */ u_short fin_id; /* IP packet id field */ u_short fin_off; int fin_depth; /* Group nesting depth */ int fin_error; /* Error code to return */ int fin_cksum; /* -1 bad, 1 good, 0 not done */ u_int fin_pktnum; void *fin_nattag; void *fin_exthdr; ip_t *ofin_ip; mb_t **fin_mp; /* pointer to pointer to mbuf */ mb_t *fin_m; /* pointer to mbuf */ #ifdef MENTAT mb_t *fin_qfm; /* pointer to mblk where pkt starts */ void *fin_qpi; char fin_ifname[LIFNAMSIZ]; #endif } fr_info_4_1_32_t; typedef struct fr_info_4_1_24 { void *fin_ifp; frip4_t fin_fi; union { u_short fid_16[2]; u_32_t fid_32; } fin_dat; int fin_out; int fin_rev; u_short fin_hlen; u_char ofin_tcpf; u_char fin_icode; u_32_t fin_rule; char fin_group[FR_GROUPLEN]; struct frentry *fin_fr; void *fin_dp; int fin_dlen; int fin_plen; int fin_ipoff; u_short fin_id; u_short fin_off; int fin_depth; int fin_error; int fin_cksum; void *fin_state; void *fin_nat; void *fin_nattag; void *fin_exthdr; ip_t *ofin_ip; mb_t **fin_mp; mb_t *fin_m; #ifdef MENTAT mb_t *fin_qfm; void *fin_qpi; char fin_ifname[LIFNAMSIZ]; #endif } fr_info_4_1_24_t; typedef struct fr_info_4_1_23 { void *fin_ifp; frip4_t fin_fi; union { u_short fid_16[2]; u_32_t fid_32; } fin_dat; int fin_out; int fin_rev; u_short fin_hlen; u_char ofin_tcpf; u_char fin_icode; u_32_t fin_rule; char fin_group[FR_GROUPLEN]; struct frentry *fin_fr; void *fin_dp; int fin_dlen; int fin_plen; int fin_ipoff; u_short fin_id; u_short fin_off; int fin_depth; int fin_error; void *fin_state; void *fin_nat; void *fin_nattag; void *fin_exthdr; ip_t *ofin_ip; mb_t **fin_mp; mb_t *fin_m; #ifdef MENTAT mb_t *fin_qfm; void *fin_qpi; char fin_ifname[LIFNAMSIZ]; #endif } fr_info_4_1_23_t; typedef struct fr_info_4_1_11 { void *fin_ifp; frip4_t fin_fi; union { u_short fid_16[2]; u_32_t fid_32; } fin_dat; int fin_out; int fin_rev; u_short fin_hlen; u_char ofin_tcpf; u_char fin_icode; u_32_t fin_rule; char fin_group[FR_GROUPLEN]; struct frentry *fin_fr; void *fin_dp; int fin_dlen; int fin_plen; int fin_ipoff; u_short fin_id; u_short fin_off; int fin_depth; int fin_error; void *fin_state; void *fin_nat; void *fin_nattag; ip_t *ofin_ip; mb_t **fin_mp; mb_t *fin_m; #ifdef MENTAT mb_t *fin_qfm; void *fin_qpi; char fin_ifname[LIFNAMSIZ]; #endif } fr_info_4_1_11_t; /* ------------------------------------------------------------------------ */ typedef struct filterstats_4_1 { 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_short; /* packets which are short */ 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_tcpbad; /* TCP checksum check failures */ u_long fr_pull[2]; /* good and bad pullup attempts */ u_long fr_badsrc; /* source received doesn't match route */ u_long fr_badttl; /* TTL in packet doesn't reach minimum */ u_long fr_bad; /* bad IP packets to the filter */ u_long fr_ipv6; /* IPv6 packets in/out */ u_long fr_ppshit; /* dropped because of pps ceiling */ u_long fr_ipud; /* IP id update failures */ } filterstats_4_1_t; /* * 5.1.0 new release (current) * 4.1.33 changed the size of f_locks from IPL_LOGMAX to IPL_LOGSIZE */ typedef struct friostat_4_1_33 { struct filterstats_4_1 of_st[2]; struct frentry *f_ipf[2][2]; struct frentry *f_acct[2][2]; struct frentry *f_ipf6[2][2]; struct frentry *f_acct6[2][2]; struct frentry *f_auth; struct frgroup *f_groups[IPL_LOGSIZE][2]; u_long f_froute[2]; u_long f_ticks; int f_locks[IPL_LOGSIZE]; size_t f_kmutex_sz; size_t f_krwlock_sz; int f_defpass; /* default pass - from fr_pass */ int f_active; /* 1 or 0 - active rule set */ int f_running; /* 1 if running, else 0 */ int f_logging; /* 1 if enabled, else 0 */ int f_features; char f_version[32]; /* version string */ } friostat_4_1_33_t; typedef struct friostat_4_1_0 { struct filterstats_4_1 of_st[2]; struct frentry *f_ipf[2][2]; struct frentry *f_acct[2][2]; struct frentry *f_ipf6[2][2]; struct frentry *f_acct6[2][2]; struct frentry *f_auth; struct frgroup *f_groups[IPL_LOGSIZE][2]; u_long f_froute[2]; u_long f_ticks; int f_locks[IPL_LOGMAX]; size_t f_kmutex_sz; size_t f_krwlock_sz; int f_defpass; int f_active; int f_running; int f_logging; int f_features; char f_version[32]; } friostat_4_1_0_t; /* ------------------------------------------------------------------------ */ /* * 5.1.0 new release (current) * 4.1.14 added in_lock */ typedef struct ipnat_4_1_14 { ipfmutex_t in_lock; struct ipnat *in_next; /* NAT rule list next */ struct ipnat *in_rnext; /* rdr rule hash next */ struct ipnat **in_prnext; /* prior rdr next ptr */ struct ipnat *in_mnext; /* map rule hash next */ struct ipnat **in_pmnext; /* prior map next ptr */ struct ipftq *in_tqehead[2]; void *in_ifps[2]; void *in_apr; char *in_comment; i6addr_t in_next6; u_long in_space; u_long in_hits; u_int in_use; u_int in_hv; int in_flineno; /* conf. file line number */ u_short in_pnext; u_char in_v; u_char in_xxx; /* From here to the end is covered by IPN_CMPSIZ */ u_32_t in_flags; u_32_t in_mssclamp; /* if != 0 clamp MSS to this */ u_int in_age[2]; int in_redir; /* see below for values */ int in_p; /* protocol. */ i6addr_t in_in[2]; i6addr_t in_out[2]; i6addr_t in_src[2]; frtuc4_t in_tuc; u_short in_port[2]; u_short in_ppip; /* ports per IP. */ u_short in_ippip; /* IP #'s per IP# */ char in_ifnames[2][LIFNAMSIZ]; char in_plabel[APR_LABELLEN]; /* proxy label. */ ipftag_t in_tag; } ipnat_4_1_14_t; typedef struct ipnat_4_1_0 { struct ipnat *in_next; struct ipnat *in_rnext; struct ipnat **in_prnext; struct ipnat *in_mnext; struct ipnat **in_pmnext; struct ipftq *in_tqehead[2]; void *in_ifps[2]; void *in_apr; char *in_comment; i6addr_t in_next6; u_long in_space; u_long in_hits; u_int in_use; u_int in_hv; int in_flineno; u_short in_pnext; u_char in_v; u_char in_xxx; u_32_t in_flags; u_32_t in_mssclamp; u_int in_age[2]; int in_redir; int in_p; i6addr_t in_in[2]; i6addr_t in_out[2]; i6addr_t in_src[2]; frtuc4_t in_tuc; u_short in_port[2]; u_short in_ppip; u_short in_ippip; char in_ifnames[2][LIFNAMSIZ]; char in_plabel[APR_LABELLEN]; ipftag_t in_tag; } ipnat_4_1_0_t; /* ------------------------------------------------------------------------ */ typedef struct natlookup_4_1_1 { struct in_addr onl_inip; struct in_addr onl_outip; struct in_addr onl_realip; int nl_flags; u_short nl_inport; u_short nl_outport; u_short nl_realport; } natlookup_4_1_1_t; /* ------------------------------------------------------------------------ */ /* * 4.1.25 added nat_seqnext (current) * 4.1.14 added nat_redir * 4.1.3 moved nat_rev * 4.1.2 added nat_rev */ typedef struct nat_4_1_25 { ipfmutex_t nat_lock; struct nat_4_1_25 *nat_next; struct nat_4_1_25 **nat_pnext; struct nat_4_1_25 *nat_hnext[2]; struct nat_4_1_25 **nat_phnext[2]; struct hostmap *nat_hm; void *nat_data; struct nat_4_1_25 **nat_me; struct ipstate *nat_state; struct ap_session *nat_aps; frentry_t *nat_fr; struct ipnat_4_1_14 *nat_ptr; void *nat_ifps[2]; void *nat_sync; ipftqent_t nat_tqe; u_32_t nat_flags; u_32_t nat_sumd[2]; u_32_t nat_ipsumd; u_32_t nat_mssclamp; i6addr_t nat_inip6; i6addr_t nat_outip6; i6addr_t nat_oip6; U_QUAD_T nat_pkts[2]; U_QUAD_T nat_bytes[2]; union { udpinfo_t nat_unu; tcpinfo4_t nat_unt; icmpinfo_t nat_uni; greinfo_t nat_ugre; } nat_un; u_short nat_oport; u_short nat_use; u_char nat_p; int nat_dir; int nat_ref; int nat_hv[2]; char nat_ifnames[2][LIFNAMSIZ]; int nat_rev; int nat_redir; u_32_t nat_seqnext[2]; } nat_4_1_25_t; typedef struct nat_4_1_14 { ipfmutex_t nat_lock; struct nat *nat_next; struct nat **nat_pnext; struct nat *nat_hnext[2]; struct nat **nat_phnext[2]; struct hostmap *nat_hm; void *nat_data; struct nat **nat_me; struct ipstate *nat_state; struct ap_session *nat_aps; frentry_t *nat_fr; struct ipnat *nat_ptr; void *nat_ifps[2]; void *nat_sync; ipftqent_t nat_tqe; u_32_t nat_flags; u_32_t nat_sumd[2]; u_32_t nat_ipsumd; u_32_t nat_mssclamp; i6addr_t nat_inip6; i6addr_t nat_outip6; i6addr_t nat_oip6; U_QUAD_T nat_pkts[2]; U_QUAD_T nat_bytes[2]; union { udpinfo_t nat_unu; tcpinfo4_t nat_unt; icmpinfo_t nat_uni; greinfo_t nat_ugre; } nat_un; u_short nat_oport; u_short nat_use; u_char nat_p; int nat_dir; int nat_ref; int nat_hv[2]; char nat_ifnames[2][LIFNAMSIZ]; int nat_rev; int nat_redir; } nat_4_1_14_t; typedef struct nat_4_1_3 { ipfmutex_t nat_lock; struct nat *nat_next; struct nat **nat_pnext; struct nat *nat_hnext[2]; struct nat **nat_phnext[2]; struct hostmap *nat_hm; void *nat_data; struct nat **nat_me; struct ipstate *nat_state; struct ap_session *nat_aps; frentry_t *nat_fr; struct ipnat *nat_ptr; void *nat_ifps[2]; void *nat_sync; ipftqent_t nat_tqe; u_32_t nat_flags; u_32_t nat_sumd[2]; u_32_t nat_ipsumd; u_32_t nat_mssclamp; i6addr_t nat_inip6; i6addr_t nat_outip6; i6addr_t nat_oip6; U_QUAD_T nat_pkts[2]; U_QUAD_T nat_bytes[2]; union { udpinfo_t nat_unu; tcpinfo4_t nat_unt; icmpinfo_t nat_uni; greinfo_t nat_ugre; } nat_un; u_short nat_oport; u_short nat_use; u_char nat_p; int nat_dir; int nat_ref; int nat_hv[2]; char nat_ifnames[2][LIFNAMSIZ]; int nat_rev; } nat_4_1_3_t; typedef struct nat_save_4_1_34 { void *ipn_next; struct nat_4_1_25 ipn_nat; struct ipnat_4_1_14 ipn_ipnat; struct frentry_4_1_34 ipn_fr; int ipn_dsize; char ipn_data[4]; } nat_save_4_1_34_t; typedef struct nat_save_4_1_16 { void *ipn_next; nat_4_1_14_t ipn_nat; ipnat_t ipn_ipnat; frentry_4_1_16_t ipn_fr; int ipn_dsize; char ipn_data[4]; } nat_save_4_1_16_t; typedef struct nat_save_4_1_14 { void *ipn_next; nat_4_1_14_t ipn_nat; ipnat_t ipn_ipnat; frentry_4_1_0_t ipn_fr; int ipn_dsize; char ipn_data[4]; } nat_save_4_1_14_t; typedef struct nat_save_4_1_3 { void *ipn_next; nat_4_1_3_t ipn_nat; ipnat_4_1_0_t ipn_ipnat; frentry_4_1_0_t ipn_fr; int ipn_dsize; char ipn_data[4]; } nat_save_4_1_3_t; /* ------------------------------------------------------------------------ */ /* * 5.1.0 new release (current) * 4.1.32 added ns_uncreate * 4.1.27 added ns_orphans * 4.1.16 added ns_ticks */ typedef struct natstat_4_1_32 { u_long ns_mapped[2]; u_long ns_rules; u_long ns_added; u_long ns_expire; u_long ns_inuse; u_long ns_logged; u_long ns_logfail; u_long ns_memfail; u_long ns_badnat; u_long ns_addtrpnt; nat_t **ns_table[2]; hostmap_t **ns_maptable; ipnat_t *ns_list; void *ns_apslist; u_int ns_wilds; u_int ns_nattab_sz; u_int ns_nattab_max; u_int ns_rultab_sz; u_int ns_rdrtab_sz; u_int ns_trpntab_sz; u_int ns_hostmap_sz; nat_t *ns_instances; hostmap_t *ns_maplist; u_long *ns_bucketlen[2]; u_long ns_ticks; u_int ns_orphans; u_long ns_uncreate[2][2]; } natstat_4_1_32_t; typedef struct natstat_4_1_27 { u_long ns_mapped[2]; u_long ns_rules; u_long ns_added; u_long ns_expire; u_long ns_inuse; u_long ns_logged; u_long ns_logfail; u_long ns_memfail; u_long ns_badnat; u_long ns_addtrpnt; nat_t **ns_table[2]; hostmap_t **ns_maptable; ipnat_t *ns_list; void *ns_apslist; u_int ns_wilds; u_int ns_nattab_sz; u_int ns_nattab_max; u_int ns_rultab_sz; u_int ns_rdrtab_sz; u_int ns_trpntab_sz; u_int ns_hostmap_sz; nat_t *ns_instances; hostmap_t *ns_maplist; u_long *ns_bucketlen[2]; u_long ns_ticks; u_int ns_orphans; } natstat_4_1_27_t; typedef struct natstat_4_1_16 { u_long ns_mapped[2]; u_long ns_rules; u_long ns_added; u_long ns_expire; u_long ns_inuse; u_long ns_logged; u_long ns_logfail; u_long ns_memfail; u_long ns_badnat; u_long ns_addtrpnt; nat_t **ns_table[2]; hostmap_t **ns_maptable; ipnat_t *ns_list; void *ns_apslist; u_int ns_wilds; u_int ns_nattab_sz; u_int ns_nattab_max; u_int ns_rultab_sz; u_int ns_rdrtab_sz; u_int ns_trpntab_sz; u_int ns_hostmap_sz; nat_t *ns_instances; hostmap_t *ns_maplist; u_long *ns_bucketlen[2]; u_long ns_ticks; } natstat_4_1_16_t; typedef struct natstat_4_1_0 { u_long ns_mapped[2]; u_long ns_rules; u_long ns_added; u_long ns_expire; u_long ns_inuse; u_long ns_logged; u_long ns_logfail; u_long ns_memfail; u_long ns_badnat; u_long ns_addtrpnt; nat_t **ns_table[2]; hostmap_t **ns_maptable; ipnat_t *ns_list; void *ns_apslist; u_int ns_wilds; u_int ns_nattab_sz; u_int ns_nattab_max; u_int ns_rultab_sz; u_int ns_rdrtab_sz; u_int ns_trpntab_sz; u_int ns_hostmap_sz; nat_t *ns_instances; hostmap_t *ns_maplist; u_long *ns_bucketlen[2]; } natstat_4_1_0_t; /* ------------------------------------------------------------------------ */ /* * 5.1.0 new release (current) * 4.1.32 fra_info:removed both fin_state & fin_nat, added fin_pktnum * 4.1.29 added fra_flx * 4.1.24 fra_info:added fin_cksum * 4.1.23 fra_info:added fin_exthdr * 4.1.11 fra_info:added fin_ifname * 4.1.4 fra_info:added fin_hbuf */ typedef struct frauth_4_1_32 { int fra_age; int fra_len; int fra_index; u_32_t fra_pass; fr_info_4_1_32_t fra_info; char *fra_buf; u_32_t fra_flx; #ifdef MENTAT queue_t *fra_q; mb_t *fra_m; #endif } frauth_4_1_32_t; typedef struct frauth_4_1_29 { int fra_age; int fra_len; int fra_index; u_32_t fra_pass; fr_info_4_1_24_t fra_info; char *fra_buf; u_32_t fra_flx; #ifdef MENTAT queue_t *fra_q; mb_t *fra_m; #endif } frauth_4_1_29_t; typedef struct frauth_4_1_24 { int fra_age; int fra_len; int fra_index; u_32_t fra_pass; fr_info_4_1_24_t fra_info; char *fra_buf; #ifdef MENTAT queue_t *fra_q; mb_t *fra_m; #endif } frauth_4_1_24_t; typedef struct frauth_4_1_23 { int fra_age; int fra_len; int fra_index; u_32_t fra_pass; fr_info_4_1_23_t fra_info; char *fra_buf; #ifdef MENTAT queue_t *fra_q; mb_t *fra_m; #endif } frauth_4_1_23_t; typedef struct frauth_4_1_11 { int fra_age; int fra_len; int fra_index; u_32_t fra_pass; fr_info_4_1_11_t fra_info; char *fra_buf; #ifdef MENTAT queue_t *fra_q; mb_t *fra_m; #endif } frauth_4_1_11_t; /* ------------------------------------------------------------------------ */ /* * 5.1.0 new release (current) * 4.1.16 removed is_nat */ typedef struct ipstate_4_1_16 { ipfmutex_t is_lock; struct ipstate *is_next; struct ipstate **is_pnext; struct ipstate *is_hnext; struct ipstate **is_phnext; struct ipstate **is_me; void *is_ifp[4]; void *is_sync; frentry_t *is_rule; struct ipftq *is_tqehead[2]; struct ipscan *is_isc; U_QUAD_T is_pkts[4]; U_QUAD_T is_bytes[4]; U_QUAD_T is_icmppkts[4]; struct ipftqent is_sti; u_int is_frage[2]; int is_ref; /* reference count */ int is_isninc[2]; u_short is_sumd[2]; i6addr_t is_src; i6addr_t is_dst; u_int is_pass; u_char is_p; /* Protocol */ u_char is_v; u_32_t is_hv; u_32_t is_tag; u_32_t is_opt[2]; /* packet options set */ u_32_t is_optmsk[2]; /* " " mask */ u_short is_sec; /* security options set */ u_short is_secmsk; /* " " mask */ u_short is_auth; /* authentication options set */ u_short is_authmsk; /* " " mask */ union { icmpinfo_t is_ics; tcpinfo4_t is_ts; udpinfo_t is_us; greinfo_t is_ug; } is_ps; u_32_t is_flags; int is_flx[2][2]; u_32_t is_rulen; /* rule number when created */ u_32_t is_s0[2]; u_short is_smsk[2]; char is_group[FR_GROUPLEN]; char is_sbuf[2][16]; char is_ifname[4][LIFNAMSIZ]; } ipstate_4_1_16_t; typedef struct ipstate_4_1_0 { ipfmutex_t is_lock; struct ipstate *is_next; struct ipstate **is_pnext; struct ipstate *is_hnext; struct ipstate **is_phnext; struct ipstate **is_me; void *is_ifp[4]; void *is_sync; void *is_nat[2]; frentry_t *is_rule; struct ipftq *is_tqehead[2]; struct ipscan *is_isc; U_QUAD_T is_pkts[4]; U_QUAD_T is_bytes[4]; U_QUAD_T is_icmppkts[4]; struct ipftqent is_sti; u_int is_frage[2]; int is_ref; int is_isninc[2]; u_short is_sumd[2]; i6addr_t is_src; i6addr_t is_dst; u_int is_pass; u_char is_p; u_char is_v; u_32_t is_hv; u_32_t is_tag; u_32_t is_opt[2]; u_32_t is_optmsk[2]; u_short is_sec; u_short is_secmsk; u_short is_auth; u_short is_authmsk; union { icmpinfo_t is_ics; tcpinfo4_t is_ts; udpinfo_t is_us; greinfo_t is_ug; } is_ps; u_32_t is_flags; int is_flx[2][2]; u_32_t is_rulen; u_32_t is_s0[2]; u_short is_smsk[2]; char is_group[FR_GROUPLEN]; char is_sbuf[2][16]; char is_ifname[4][LIFNAMSIZ]; } ipstate_4_1_0_t; typedef struct ipstate_save_4_1_34 { void *ips_next; struct ipstate_4_1_16 ips_is; struct frentry_4_1_34 ips_fr; } ipstate_save_4_1_34_t; typedef struct ipstate_save_4_1_16 { void *ips_next; ipstate_4_1_0_t ips_is; frentry_4_1_16_t ips_fr; } ipstate_save_4_1_16_t; typedef struct ipstate_save_4_1_0 { void *ips_next; ipstate_4_1_0_t ips_is; frentry_4_1_0_t ips_fr; } ipstate_save_4_1_0_t; /* ------------------------------------------------------------------------ */ /* * 5.1.0 new release (current) * 4.1.21 added iss_tcptab */ typedef struct ips_stat_4_1_21 { u_long iss_hits; u_long iss_miss; u_long iss_max; u_long iss_maxref; u_long iss_tcp; u_long iss_udp; u_long iss_icmp; u_long iss_nomem; u_long iss_expire; u_long iss_fin; u_long iss_active; u_long iss_logged; u_long iss_logfail; u_long iss_inuse; u_long iss_wild; u_long iss_killed; u_long iss_ticks; u_long iss_bucketfull; int iss_statesize; int iss_statemax; ipstate_t **iss_table; ipstate_t *iss_list; u_long *iss_bucketlen; ipftq_t *iss_tcptab; } ips_stat_4_1_21_t; typedef struct ips_stat_4_1_0 { u_long iss_hits; u_long iss_miss; u_long iss_max; u_long iss_maxref; u_long iss_tcp; u_long iss_udp; u_long iss_icmp; u_long iss_nomem; u_long iss_expire; u_long iss_fin; u_long iss_active; u_long iss_logged; u_long iss_logfail; u_long iss_inuse; u_long iss_wild; u_long iss_killed; u_long iss_ticks; u_long iss_bucketfull; int iss_statesize; int iss_statemax; ipstate_t **iss_table; ipstate_t *iss_list; u_long *iss_bucketlen; } ips_stat_4_1_0_t; /* ------------------------------------------------------------------------ */ typedef struct ipfrstat_4_1_1 { u_long ifs_exists; /* add & already exists */ u_long ifs_nomem; u_long ifs_new; u_long ifs_hits; u_long ifs_expire; u_long ifs_inuse; u_long ifs_retrans0; u_long ifs_short; struct ipfr **ifs_table; struct ipfr **ifs_nattab; } ipfrstat_4_1_1_t; /* ------------------------------------------------------------------------ */ -static int ipf_addfrstr __P((char *, int, char *, int)); -static void ipf_v4iptov5 __P((frip4_t *, fr_ip_t *)); -static void ipf_v5iptov4 __P((fr_ip_t *, frip4_t *)); -static void ipfv4tuctov5 __P((frtuc4_t *, frtuc_t *)); -static void ipfv5tuctov4 __P((frtuc_t *, frtuc4_t *)); -static int ipf_v4fripftov5 __P((fripf4_t *, char *)); -static void ipf_v5fripftov4 __P((fripf_t *, fripf4_t *)); -static int fr_frflags4to5 __P((u_32_t)); -static int fr_frflags5to4 __P((u_32_t)); - -static void friostat_current_to_4_1_0 __P((void *, friostat_4_1_0_t *, int)); -static void friostat_current_to_4_1_33 __P((void *, friostat_4_1_33_t *, int)); -static void ipstate_current_to_4_1_0 __P((void *, ipstate_4_1_0_t *)); -static void ipstate_current_to_4_1_16 __P((void *, ipstate_4_1_16_t *)); -static void ipnat_current_to_4_1_0 __P((void *, ipnat_4_1_0_t *)); -static void ipnat_current_to_4_1_14 __P((void *, ipnat_4_1_14_t *)); -static void frauth_current_to_4_1_11 __P((void *, frauth_4_1_11_t *)); -static void frauth_current_to_4_1_23 __P((void *, frauth_4_1_23_t *)); -static void frauth_current_to_4_1_24 __P((void *, frauth_4_1_24_t *)); -static void frauth_current_to_4_1_29 __P((void *, frauth_4_1_29_t *)); -static void frentry_current_to_4_1_0 __P((void *, frentry_4_1_0_t *)); -static void frentry_current_to_4_1_16 __P((void *, frentry_4_1_16_t *)); -static void frentry_current_to_4_1_34 __P((void *, frentry_4_1_34_t *)); -static void fr_info_current_to_4_1_11 __P((void *, fr_info_4_1_11_t *)); -static void fr_info_current_to_4_1_23 __P((void *, fr_info_4_1_23_t *)); -static void fr_info_current_to_4_1_24 __P((void *, fr_info_4_1_24_t *)); -static void nat_save_current_to_4_1_3 __P((void *, nat_save_4_1_3_t *)); -static void nat_save_current_to_4_1_14 __P((void *, nat_save_4_1_14_t *)); -static void nat_save_current_to_4_1_16 __P((void *, nat_save_4_1_16_t *)); -static void ipstate_save_current_to_4_1_0 __P((void *, ipstate_save_4_1_0_t *)); -static void ipstate_save_current_to_4_1_16 __P((void *, ipstate_save_4_1_16_t *)); -static void ips_stat_current_to_4_1_0 __P((void *, ips_stat_4_1_0_t *)); -static void ips_stat_current_to_4_1_21 __P((void *, ips_stat_4_1_21_t *)); -static void natstat_current_to_4_1_0 __P((void *, natstat_4_1_0_t *)); -static void natstat_current_to_4_1_16 __P((void *, natstat_4_1_16_t *)); -static void natstat_current_to_4_1_27 __P((void *, natstat_4_1_27_t *)); -static void natstat_current_to_4_1_32 __P((void *, natstat_4_1_32_t *)); -static void nat_current_to_4_1_3 __P((void *, nat_4_1_3_t *)); -static void nat_current_to_4_1_14 __P((void *, nat_4_1_14_t *)); -static void nat_current_to_4_1_25 __P((void *, nat_4_1_25_t *)); - -static void friostat_4_1_0_to_current __P((friostat_4_1_0_t *, void *)); -static void friostat_4_1_33_to_current __P((friostat_4_1_33_t *, void *)); -static void ipnat_4_1_0_to_current __P((ipnat_4_1_0_t *, void *, int)); -static void ipnat_4_1_14_to_current __P((ipnat_4_1_14_t *, void *, int)); -static void frauth_4_1_11_to_current __P((frauth_4_1_11_t *, void *)); -static void frauth_4_1_23_to_current __P((frauth_4_1_23_t *, void *)); -static void frauth_4_1_24_to_current __P((frauth_4_1_24_t *, void *)); -static void frauth_4_1_29_to_current __P((frauth_4_1_29_t *, void *)); -static void frauth_4_1_32_to_current __P((frauth_4_1_32_t *, void *)); -static void frentry_4_1_0_to_current __P((ipf_main_softc_t *, frentry_4_1_0_t *, void *, int)); -static void frentry_4_1_16_to_current __P((ipf_main_softc_t *, frentry_4_1_16_t *, void *, int)); -static void frentry_4_1_34_to_current __P((ipf_main_softc_t *, frentry_4_1_34_t *, void *, int)); -static void fr_info_4_1_11_to_current __P((fr_info_4_1_11_t *, void *)); -static void fr_info_4_1_23_to_current __P((fr_info_4_1_23_t *, void *)); -static void fr_info_4_1_24_to_current __P((fr_info_4_1_24_t *, void *)); -static void fr_info_4_1_32_to_current __P((fr_info_4_1_32_t *, void *)); -static void nat_save_4_1_3_to_current __P((ipf_main_softc_t *, nat_save_4_1_3_t *, void *)); -static void nat_save_4_1_14_to_current __P((ipf_main_softc_t *, nat_save_4_1_14_t *, void *)); -static void nat_save_4_1_16_to_current __P((ipf_main_softc_t *, nat_save_4_1_16_t *, void *)); +static int ipf_addfrstr(char *, int, char *, int); +static void ipf_v4iptov5(frip4_t *, fr_ip_t *); +static void ipf_v5iptov4(fr_ip_t *, frip4_t *); +static void ipfv4tuctov5(frtuc4_t *, frtuc_t *); +static void ipfv5tuctov4(frtuc_t *, frtuc4_t *); +static int ipf_v4fripftov5(fripf4_t *, char *); +static void ipf_v5fripftov4(fripf_t *, fripf4_t *); +static int fr_frflags4to5(u_32_t); +static int fr_frflags5to4(u_32_t); + +static void friostat_current_to_4_1_0(void *, friostat_4_1_0_t *, int); +static void friostat_current_to_4_1_33(void *, friostat_4_1_33_t *, int); +static void ipstate_current_to_4_1_0(void *, ipstate_4_1_0_t *); +static void ipstate_current_to_4_1_16(void *, ipstate_4_1_16_t *); +static void ipnat_current_to_4_1_0(void *, ipnat_4_1_0_t *); +static void ipnat_current_to_4_1_14(void *, ipnat_4_1_14_t *); +static void frauth_current_to_4_1_11(void *, frauth_4_1_11_t *); +static void frauth_current_to_4_1_23(void *, frauth_4_1_23_t *); +static void frauth_current_to_4_1_24(void *, frauth_4_1_24_t *); +static void frauth_current_to_4_1_29(void *, frauth_4_1_29_t *); +static void frentry_current_to_4_1_0(void *, frentry_4_1_0_t *); +static void frentry_current_to_4_1_16(void *, frentry_4_1_16_t *); +static void frentry_current_to_4_1_34(void *, frentry_4_1_34_t *); +static void fr_info_current_to_4_1_11(void *, fr_info_4_1_11_t *); +static void fr_info_current_to_4_1_23(void *, fr_info_4_1_23_t *); +static void fr_info_current_to_4_1_24(void *, fr_info_4_1_24_t *); +static void nat_save_current_to_4_1_3(void *, nat_save_4_1_3_t *); +static void nat_save_current_to_4_1_14(void *, nat_save_4_1_14_t *); +static void nat_save_current_to_4_1_16(void *, nat_save_4_1_16_t *); +static void ipstate_save_current_to_4_1_0(void *, ipstate_save_4_1_0_t *); +static void ipstate_save_current_to_4_1_16(void *, ipstate_save_4_1_16_t *); +static void ips_stat_current_to_4_1_0(void *, ips_stat_4_1_0_t *); +static void ips_stat_current_to_4_1_21(void *, ips_stat_4_1_21_t *); +static void natstat_current_to_4_1_0(void *, natstat_4_1_0_t *); +static void natstat_current_to_4_1_16(void *, natstat_4_1_16_t *); +static void natstat_current_to_4_1_27(void *, natstat_4_1_27_t *); +static void natstat_current_to_4_1_32(void *, natstat_4_1_32_t *); +static void nat_current_to_4_1_3(void *, nat_4_1_3_t *); +static void nat_current_to_4_1_14(void *, nat_4_1_14_t *); +static void nat_current_to_4_1_25(void *, nat_4_1_25_t *); + +static void friostat_4_1_0_to_current(friostat_4_1_0_t *, void *); +static void friostat_4_1_33_to_current(friostat_4_1_33_t *, void *); +static void ipnat_4_1_0_to_current(ipnat_4_1_0_t *, void *, int); +static void ipnat_4_1_14_to_current(ipnat_4_1_14_t *, void *, int); +static void frauth_4_1_11_to_current(frauth_4_1_11_t *, void *); +static void frauth_4_1_23_to_current(frauth_4_1_23_t *, void *); +static void frauth_4_1_24_to_current(frauth_4_1_24_t *, void *); +static void frauth_4_1_29_to_current(frauth_4_1_29_t *, void *); +static void frauth_4_1_32_to_current(frauth_4_1_32_t *, void *); +static void frentry_4_1_0_to_current(ipf_main_softc_t *, frentry_4_1_0_t *, void *, int); +static void frentry_4_1_16_to_current(ipf_main_softc_t *, frentry_4_1_16_t *, void *, int); +static void frentry_4_1_34_to_current(ipf_main_softc_t *, frentry_4_1_34_t *, void *, int); +static void fr_info_4_1_11_to_current(fr_info_4_1_11_t *, void *); +static void fr_info_4_1_23_to_current(fr_info_4_1_23_t *, void *); +static void fr_info_4_1_24_to_current(fr_info_4_1_24_t *, void *); +static void fr_info_4_1_32_to_current(fr_info_4_1_32_t *, void *); +static void nat_save_4_1_3_to_current(ipf_main_softc_t *, nat_save_4_1_3_t *, void *); +static void nat_save_4_1_14_to_current(ipf_main_softc_t *, nat_save_4_1_14_t *, void *); +static void nat_save_4_1_16_to_current(ipf_main_softc_t *, nat_save_4_1_16_t *, void *); /* ------------------------------------------------------------------------ */ /* In this section is a series of short routines that deal with translating */ /* the smaller data structures used above as their internal changes make */ /* them inappropriate for simple assignment. */ /* ------------------------------------------------------------------------ */ static int ipf_addfrstr(char *names, int namelen, char *str, int maxlen) { char *t; int i; for (i = maxlen, t = str; (*t != '\0') && (i > 0); i--) { names[namelen++] = *t++; } names[namelen++] = '\0'; return namelen; } static void ipf_v4iptov5(v4, v5) frip4_t *v4; fr_ip_t *v5; { v5->fi_v = v4->fi_v; v5->fi_p = v4->fi_p; v5->fi_xx = v4->fi_xx; v5->fi_tos = v4->fi_tos; v5->fi_ttl = v4->fi_ttl; v5->fi_p = v4->fi_p; v5->fi_optmsk = v4->fi_optmsk; v5->fi_src = v4->fi_src; v5->fi_dst = v4->fi_dst; v5->fi_secmsk = v4->ofi_secmsk; v5->fi_auth = v4->ofi_auth; v5->fi_flx = v4->fi_flx; v5->fi_tcpmsk = v4->fi_tcpmsk; } static void ipf_v5iptov4(v5, v4) fr_ip_t *v5; frip4_t *v4; { v4->fi_v = v5->fi_v; v4->fi_p = v5->fi_p; v4->fi_xx = v5->fi_xx; v4->fi_tos = v5->fi_tos; v4->fi_ttl = v5->fi_ttl; v4->fi_p = v5->fi_p; v4->fi_optmsk = v5->fi_optmsk; v4->fi_src = v5->fi_src; v4->fi_dst = v5->fi_dst; v4->ofi_secmsk = v5->fi_secmsk; v4->ofi_auth = v5->fi_auth; v4->fi_flx = v5->fi_flx; v4->fi_tcpmsk = v5->fi_tcpmsk; } static void ipfv4tuctov5(v4, v5) frtuc4_t *v4; frtuc_t *v5; { v5->ftu_src.frp_cmp = v4->ftu_src.frp_cmp; v5->ftu_src.frp_port = v4->ftu_src.frp_port; v5->ftu_src.frp_top = v4->ftu_src.frp_top; v5->ftu_dst.frp_cmp = v4->ftu_dst.frp_cmp; v5->ftu_dst.frp_port = v4->ftu_dst.frp_port; v5->ftu_dst.frp_top = v4->ftu_dst.frp_top; } static void ipfv5tuctov4(v5, v4) frtuc_t *v5; frtuc4_t *v4; { v4->ftu_src.frp_cmp = v5->ftu_src.frp_cmp; v4->ftu_src.frp_port = v5->ftu_src.frp_port; v4->ftu_src.frp_top = v5->ftu_src.frp_top; v4->ftu_dst.frp_cmp = v5->ftu_dst.frp_cmp; v4->ftu_dst.frp_port = v5->ftu_dst.frp_port; v4->ftu_dst.frp_top = v5->ftu_dst.frp_top; } static int ipf_v4fripftov5(frp4, dst) fripf4_t *frp4; char *dst; { fripf_t *frp; frp = (fripf_t *)dst; ipf_v4iptov5(&frp4->fri_ip, &frp->fri_ip); ipf_v4iptov5(&frp4->fri_mip, &frp->fri_mip); frp->fri_icmpm = frp4->fri_icmpm; frp->fri_icmp = frp4->fri_icmp; frp->fri_tuc.ftu_tcpfm = frp4->fri_tuc.ftu_tcpfm; frp->fri_tuc.ftu_tcpf = frp4->fri_tuc.ftu_tcpf; ipfv4tuctov5(&frp4->fri_tuc, &frp->fri_tuc); frp->fri_satype = frp4->fri_satype; frp->fri_datype = frp4->fri_datype; frp->fri_sifpidx = frp4->fri_sifpidx; frp->fri_difpidx = frp4->fri_difpidx; return 0; } static void ipf_v5fripftov4(frp, frp4) fripf_t *frp; fripf4_t *frp4; { ipf_v5iptov4(&frp->fri_ip, &frp4->fri_ip); ipf_v5iptov4(&frp->fri_mip, &frp4->fri_mip); frp4->fri_icmpm = frp->fri_icmpm; frp4->fri_icmp = frp->fri_icmp; frp4->fri_tuc.ftu_tcpfm = frp->fri_tuc.ftu_tcpfm; frp4->fri_tuc.ftu_tcpf = frp->fri_tuc.ftu_tcpf; ipfv5tuctov4(&frp->fri_tuc, &frp4->fri_tuc); frp4->fri_satype = frp->fri_satype; frp4->fri_datype = frp->fri_datype; frp4->fri_sifpidx = frp->fri_sifpidx; frp4->fri_difpidx = frp->fri_difpidx; } /* ------------------------------------------------------------------------ */ /* ipf_in_compat is the first of two service routines. It is responsible for*/ /* converting data structures from user space into what's required by the */ /* kernel module. */ /* ------------------------------------------------------------------------ */ int ipf_in_compat(softc, obj, ptr, size) ipf_main_softc_t *softc; ipfobj_t *obj; void *ptr; int size; { int error; int sz; IPFERROR(140000); error = EINVAL; switch (obj->ipfo_type) { default : break; case IPFOBJ_FRENTRY : if (obj->ipfo_rev >= 4013400) { frentry_4_1_34_t *old; KMALLOC(old, frentry_4_1_34_t *); if (old == NULL) { IPFERROR(140001); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error == 0) { if (old->fr_type != FR_T_NONE && old->fr_type != FR_T_IPF) { IPFERROR(140002); error = EINVAL; KFREE(old); break; } frentry_4_1_34_to_current(softc, old, ptr, size); } else { IPFERROR(140003); } KFREE(old); } else if (obj->ipfo_rev >= 4011600) { frentry_4_1_16_t *old; KMALLOC(old, frentry_4_1_16_t *); if (old == NULL) { IPFERROR(140004); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error == 0) { if (old->fr_type != FR_T_NONE && old->fr_type != FR_T_IPF) { IPFERROR(140005); error = EINVAL; KFREE(old); break; } frentry_4_1_16_to_current(softc, old, ptr, size); } else { IPFERROR(140006); } KFREE(old); } else { frentry_4_1_0_t *old; KMALLOC(old, frentry_4_1_0_t *); if (old == NULL) { IPFERROR(140007); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error == 0) { if (old->fr_type != FR_T_NONE && old->fr_type != FR_T_IPF) { IPFERROR(140008); error = EINVAL; KFREE(old); break; } frentry_4_1_0_to_current(softc, old, ptr, size); } else { IPFERROR(140009); } KFREE(old); } break; case IPFOBJ_IPFSTAT : if (obj->ipfo_rev >= 4013300) { friostat_4_1_33_t *old; KMALLOC(old, friostat_4_1_33_t *); if (old == NULL) { IPFERROR(140010); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error == 0) { friostat_4_1_33_to_current(old, ptr); } else { IPFERROR(140011); } } else { friostat_4_1_0_t *old; KMALLOC(old, friostat_4_1_0_t *); if (old == NULL) { IPFERROR(140012); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error == 0) { friostat_4_1_0_to_current(old, ptr); } else { IPFERROR(140013); } } break; case IPFOBJ_IPFINFO : /* unused */ break; case IPFOBJ_IPNAT : if (obj->ipfo_rev >= 4011400) { ipnat_4_1_14_t *old; KMALLOC(old, ipnat_4_1_14_t *); if (old == NULL) { IPFERROR(140014); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error == 0) { ipnat_4_1_14_to_current(old, ptr, size); } else { IPFERROR(140015); } KFREE(old); } else { ipnat_4_1_0_t *old; KMALLOC(old, ipnat_4_1_0_t *); if (old == NULL) { IPFERROR(140016); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error == 0) { ipnat_4_1_0_to_current(old, ptr, size); } else { IPFERROR(140017); } KFREE(old); } break; case IPFOBJ_NATSTAT : /* * Statistics are not copied in. */ break; case IPFOBJ_NATSAVE : if (obj->ipfo_rev >= 4011600) { nat_save_4_1_16_t *old16; KMALLOC(old16, nat_save_4_1_16_t *); if (old16 == NULL) { IPFERROR(140018); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old16, sizeof(*old16)); if (error == 0) { nat_save_4_1_16_to_current(softc, old16, ptr); } else { IPFERROR(140019); } KFREE(old16); } else if (obj->ipfo_rev >= 4011400) { nat_save_4_1_14_t *old14; KMALLOC(old14, nat_save_4_1_14_t *); if (old14 == NULL) { IPFERROR(140020); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old14, sizeof(*old14)); if (error == 0) { nat_save_4_1_14_to_current(softc, old14, ptr); } else { IPFERROR(140021); } KFREE(old14); } else if (obj->ipfo_rev >= 4010300) { nat_save_4_1_3_t *old3; KMALLOC(old3, nat_save_4_1_3_t *); if (old3 == NULL) { IPFERROR(140022); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old3, sizeof(*old3)); if (error == 0) { nat_save_4_1_3_to_current(softc, old3, ptr); } else { IPFERROR(140023); } KFREE(old3); } break; case IPFOBJ_STATESAVE : if (obj->ipfo_rev >= 4013400) { ipstate_save_4_1_34_t *old; KMALLOC(old, ipstate_save_4_1_34_t *); if (old == NULL) { IPFERROR(140024); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error != 0) { IPFERROR(140025); } KFREE(old); } else if (obj->ipfo_rev >= 4011600) { ipstate_save_4_1_16_t *old; KMALLOC(old, ipstate_save_4_1_16_t *); if (old == NULL) { IPFERROR(140026); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error != 0) { IPFERROR(140027); } KFREE(old); } else { ipstate_save_4_1_0_t *old; KMALLOC(old, ipstate_save_4_1_0_t *); if (old == NULL) { IPFERROR(140028); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error != 0) { IPFERROR(140029); } KFREE(old); } break; case IPFOBJ_IPSTATE : /* * This structure is not copied in by itself. */ break; case IPFOBJ_STATESTAT : /* * Statistics are not copied in. */ break; case IPFOBJ_FRAUTH : if (obj->ipfo_rev >= 4013200) { frauth_4_1_32_t *old32; KMALLOC(old32, frauth_4_1_32_t *); if (old32 == NULL) { IPFERROR(140030); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old32, sizeof(*old32)); if (error == 0) { frauth_4_1_32_to_current(old32, ptr); } else { IPFERROR(140031); } KFREE(old32); } else if (obj->ipfo_rev >= 4012900) { frauth_4_1_29_t *old29; KMALLOC(old29, frauth_4_1_29_t *); if (old29 == NULL) { IPFERROR(140032); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old29, sizeof(*old29)); if (error == 0) { frauth_4_1_29_to_current(old29, ptr); } else { IPFERROR(140033); } KFREE(old29); } else if (obj->ipfo_rev >= 4012400) { frauth_4_1_24_t *old24; KMALLOC(old24, frauth_4_1_24_t *); if (old24 == NULL) { IPFERROR(140034); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old24, sizeof(*old24)); if (error == 0) { frauth_4_1_24_to_current(old24, ptr); } else { IPFERROR(140035); } KFREE(old24); } else if (obj->ipfo_rev >= 4012300) { frauth_4_1_23_t *old23; KMALLOC(old23, frauth_4_1_23_t *); if (old23 == NULL) { IPFERROR(140036); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old23, sizeof(*old23)); if (error == 0) frauth_4_1_23_to_current(old23, ptr); KFREE(old23); } else if (obj->ipfo_rev >= 4011100) { frauth_4_1_11_t *old11; KMALLOC(old11, frauth_4_1_11_t *); if (old11 == NULL) { IPFERROR(140037); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old11, sizeof(*old11)); if (error == 0) { frauth_4_1_11_to_current(old11, ptr); } else { IPFERROR(140038); } KFREE(old11); } break; case IPFOBJ_NAT : if (obj->ipfo_rev >= 4011400) { sz = sizeof(nat_4_1_14_t); } else if (obj->ipfo_rev >= 4010300) { sz = sizeof(nat_4_1_3_t); } else { break; } bzero(ptr, sizeof(nat_t)); error = COPYIN(obj->ipfo_ptr, ptr, sz); if (error != 0) { IPFERROR(140039); } break; case IPFOBJ_FRIPF : if (obj->ipfo_rev < 5000000) { fripf4_t *old; KMALLOC(old, fripf4_t *); if (old == NULL) { IPFERROR(140040); error = ENOMEM; break; } error = COPYIN(obj->ipfo_ptr, old, sizeof(*old)); if (error == 0) { ipf_v4fripftov5(old, ptr); } else { IPFERROR(140041); } KFREE(old); } break; } return error; } /* ------------------------------------------------------------------------ */ /* * flags is v4 flags, returns v5 flags. */ static int fr_frflags4to5(flags) u_32_t flags; { u_32_t nflags = 0; switch (flags & 0xf) { case 0x0 : nflags |= FR_CALL; break; case 0x1 : nflags |= FR_BLOCK; break; case 0x2 : nflags |= FR_PASS; break; case 0x3 : nflags |= FR_AUTH; break; case 0x4 : nflags |= FR_PREAUTH; break; case 0x5 : nflags |= FR_ACCOUNT; break; case 0x6 : nflags |= FR_SKIP; break; default : break; } if (flags & 0x00010) nflags |= FR_LOG; if (flags & 0x00020) nflags |= FR_CALLNOW; if (flags & 0x00080) nflags |= FR_NOTSRCIP; if (flags & 0x00040) nflags |= FR_NOTDSTIP; if (flags & 0x00100) nflags |= FR_QUICK; if (flags & 0x00200) nflags |= FR_KEEPFRAG; if (flags & 0x00400) nflags |= FR_KEEPSTATE; if (flags & 0x00800) nflags |= FR_FASTROUTE; if (flags & 0x01000) nflags |= FR_RETRST; if (flags & 0x02000) nflags |= FR_RETICMP; if (flags & 0x03000) nflags |= FR_FAKEICMP; if (flags & 0x04000) nflags |= FR_OUTQUE; if (flags & 0x08000) nflags |= FR_INQUE; if (flags & 0x10000) nflags |= FR_LOGBODY; if (flags & 0x20000) nflags |= FR_LOGFIRST; if (flags & 0x40000) nflags |= FR_LOGORBLOCK; if (flags & 0x100000) nflags |= FR_FRSTRICT; if (flags & 0x200000) nflags |= FR_STSTRICT; if (flags & 0x400000) nflags |= FR_NEWISN; if (flags & 0x800000) nflags |= FR_NOICMPERR; if (flags & 0x1000000) nflags |= FR_STATESYNC; if (flags & 0x8000000) nflags |= FR_NOMATCH; if (flags & 0x40000000) nflags |= FR_COPIED; if (flags & 0x80000000) nflags |= FR_INACTIVE; return nflags; } static void frentry_4_1_34_to_current(softc, old, current, size) ipf_main_softc_t *softc; frentry_4_1_34_t *old; void *current; int size; { frentry_t *fr = (frentry_t *)current; fr->fr_comment = -1; fr->fr_ref = old->fr_ref; fr->fr_statecnt = old->fr_statecnt; fr->fr_hits = old->fr_hits; fr->fr_bytes = old->fr_bytes; fr->fr_lastpkt.tv_sec = old->fr_lastpkt.tv_sec; fr->fr_lastpkt.tv_usec = old->fr_lastpkt.tv_usec; bcopy(&old->fr_dun, &fr->fr_dun, sizeof(old->fr_dun)); fr->fr_func = old->fr_func; fr->fr_dsize = old->fr_dsize; fr->fr_pps = old->fr_pps; fr->fr_statemax = old->fr_statemax; fr->fr_flineno = old->fr_flineno; fr->fr_type = old->fr_type; fr->fr_flags = fr_frflags4to5(old->fr_flags); fr->fr_logtag = old->fr_logtag; fr->fr_collect = old->fr_collect; fr->fr_arg = old->fr_arg; fr->fr_loglevel = old->fr_loglevel; fr->fr_age[0] = old->fr_age[0]; fr->fr_age[1] = old->fr_age[1]; fr->fr_tifs[0].fd_ip6 = old->fr_tifs[0].ofd_ip6; fr->fr_tifs[0].fd_type = FRD_NORMAL; fr->fr_tifs[1].fd_ip6 = old->fr_tifs[1].ofd_ip6; fr->fr_tifs[1].fd_type = FRD_NORMAL; fr->fr_dif.fd_ip6 = old->fr_dif.ofd_ip6; fr->fr_dif.fd_type = FRD_NORMAL; if (old->fr_v == 4) fr->fr_family = AF_INET; if (old->fr_v == 6) fr->fr_family = AF_INET6; fr->fr_icode = old->fr_icode; fr->fr_cksum = old->fr_cksum; fr->fr_namelen = 0; fr->fr_ifnames[0] = -1; fr->fr_ifnames[1] = -1; fr->fr_ifnames[2] = -1; fr->fr_ifnames[3] = -1; fr->fr_dif.fd_name = -1; fr->fr_tifs[0].fd_name = -1; fr->fr_tifs[1].fd_name = -1; fr->fr_group = -1; fr->fr_grhead = -1; fr->fr_icmphead = -1; if (size == 0) { fr->fr_size = sizeof(*fr) + LIFNAMSIZ * 7 + FR_GROUPLEN * 2; fr->fr_size += sizeof(fripf_t) + 16; fr->fr_size += 9; /* room for \0's */ } else { char *names = fr->fr_names; int nlen = fr->fr_namelen; fr->fr_size = size; if (old->fr_ifnames[0][0] != '\0') { fr->fr_ifnames[0] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[0], LIFNAMSIZ); } if (old->fr_ifnames[1][0] != '\0') { fr->fr_ifnames[1] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[1], LIFNAMSIZ); } if (old->fr_ifnames[2][0] != '\0') { fr->fr_ifnames[2] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[2], LIFNAMSIZ); } if (old->fr_ifnames[3][0] != '\0') { fr->fr_ifnames[3] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[3], LIFNAMSIZ); } if (old->fr_tifs[0].fd_ifname[0] != '\0') { fr->fr_tifs[0].fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_tifs[0].fd_ifname, LIFNAMSIZ); } if (old->fr_tifs[1].fd_ifname[0] != '\0') { fr->fr_tifs[1].fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_tifs[1].fd_ifname, LIFNAMSIZ); } if (old->fr_dif.fd_ifname[0] != '\0') { fr->fr_dif.fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_dif.fd_ifname, LIFNAMSIZ); } if (old->fr_group[0] != '\0') { fr->fr_group = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_group, LIFNAMSIZ); } if (old->fr_grhead[0] != '\0') { fr->fr_grhead = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_grhead, LIFNAMSIZ); } fr->fr_namelen = nlen; if (old->fr_type == FR_T_IPF) { int offset = fr->fr_namelen; ipfobj_t obj; int error; obj.ipfo_type = IPFOBJ_FRIPF; obj.ipfo_rev = 4010100; obj.ipfo_ptr = old->fr_data; if ((offset & 7) != 0) offset += 8 - (offset & 7); error = ipf_in_compat(softc, &obj, fr->fr_names + offset, 0); if (error == 0) { fr->fr_data = fr->fr_names + offset; fr->fr_dsize = sizeof(fripf_t); } } } } static void frentry_4_1_16_to_current(softc, old, current, size) ipf_main_softc_t *softc; frentry_4_1_16_t *old; void *current; int size; { frentry_t *fr = (frentry_t *)current; fr->fr_comment = -1; fr->fr_ref = old->fr_ref; fr->fr_statecnt = old->fr_statecnt; fr->fr_hits = old->fr_hits; fr->fr_bytes = old->fr_bytes; fr->fr_lastpkt.tv_sec = old->fr_lastpkt.tv_sec; fr->fr_lastpkt.tv_usec = old->fr_lastpkt.tv_usec; bcopy(&old->fr_dun, &fr->fr_dun, sizeof(old->fr_dun)); fr->fr_func = old->fr_func; fr->fr_dsize = old->fr_dsize; fr->fr_pps = old->fr_pps; fr->fr_statemax = old->fr_statemax; fr->fr_flineno = old->fr_flineno; fr->fr_type = old->fr_type; fr->fr_flags = fr_frflags4to5(old->fr_flags); fr->fr_logtag = old->fr_logtag; fr->fr_collect = old->fr_collect; fr->fr_arg = old->fr_arg; fr->fr_loglevel = old->fr_loglevel; fr->fr_age[0] = old->fr_age[0]; fr->fr_age[1] = old->fr_age[1]; fr->fr_tifs[0].fd_ip6 = old->fr_tifs[0].ofd_ip6; fr->fr_tifs[0].fd_type = FRD_NORMAL; fr->fr_tifs[1].fd_ip6 = old->fr_tifs[1].ofd_ip6; fr->fr_tifs[1].fd_type = FRD_NORMAL; fr->fr_dif.fd_ip6 = old->fr_dif.ofd_ip6; fr->fr_dif.fd_type = FRD_NORMAL; if (old->fr_v == 4) fr->fr_family = AF_INET; if (old->fr_v == 6) fr->fr_family = AF_INET6; fr->fr_icode = old->fr_icode; fr->fr_cksum = old->fr_cksum; fr->fr_namelen = 0; fr->fr_ifnames[0] = -1; fr->fr_ifnames[1] = -1; fr->fr_ifnames[2] = -1; fr->fr_ifnames[3] = -1; fr->fr_dif.fd_name = -1; fr->fr_tifs[0].fd_name = -1; fr->fr_tifs[1].fd_name = -1; fr->fr_group = -1; fr->fr_grhead = -1; fr->fr_icmphead = -1; if (size == 0) { fr->fr_size = sizeof(*fr) + LIFNAMSIZ * 7 + FR_GROUPLEN * 2; fr->fr_size += 9; /* room for \0's */ } else { char *names = fr->fr_names; int nlen = fr->fr_namelen; fr->fr_size = size; if (old->fr_ifnames[0][0] != '\0') { fr->fr_ifnames[0] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[0], LIFNAMSIZ); } if (old->fr_ifnames[1][0] != '\0') { fr->fr_ifnames[1] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[1], LIFNAMSIZ); } if (old->fr_ifnames[2][0] != '\0') { fr->fr_ifnames[2] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[2], LIFNAMSIZ); } if (old->fr_ifnames[3][0] != '\0') { fr->fr_ifnames[3] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[3], LIFNAMSIZ); } if (old->fr_tifs[0].fd_ifname[0] != '\0') { fr->fr_tifs[0].fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_tifs[0].fd_ifname, LIFNAMSIZ); } if (old->fr_tifs[1].fd_ifname[0] != '\0') { fr->fr_tifs[1].fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_tifs[1].fd_ifname, LIFNAMSIZ); } if (old->fr_dif.fd_ifname[0] != '\0') { fr->fr_dif.fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_dif.fd_ifname, LIFNAMSIZ); } if (old->fr_group[0] != '\0') { fr->fr_group = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_group, LIFNAMSIZ); } if (old->fr_grhead[0] != '\0') { fr->fr_grhead = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_grhead, LIFNAMSIZ); } fr->fr_namelen = nlen; if (old->fr_type == FR_T_IPF) { int offset = fr->fr_namelen; ipfobj_t obj; int error; obj.ipfo_type = IPFOBJ_FRIPF; obj.ipfo_rev = 4010100; obj.ipfo_ptr = old->fr_data; if ((offset & 7) != 0) offset += 8 - (offset & 7); error = ipf_in_compat(softc, &obj, fr->fr_names + offset, 0); if (error == 0) { fr->fr_data = fr->fr_names + offset; fr->fr_dsize = sizeof(fripf_t); } } } } static void frentry_4_1_0_to_current(softc, old, current, size) ipf_main_softc_t *softc; frentry_4_1_0_t *old; void *current; int size; { frentry_t *fr = (frentry_t *)current; fr->fr_size = sizeof(*fr); fr->fr_comment = -1; fr->fr_ref = old->fr_ref; fr->fr_statecnt = old->fr_statecnt; fr->fr_hits = old->fr_hits; fr->fr_bytes = old->fr_bytes; fr->fr_lastpkt.tv_sec = old->fr_lastpkt.tv_sec; fr->fr_lastpkt.tv_usec = old->fr_lastpkt.tv_usec; bcopy(&old->fr_dun, &fr->fr_dun, sizeof(old->fr_dun)); fr->fr_func = old->fr_func; fr->fr_dsize = old->fr_dsize; fr->fr_pps = old->fr_pps; fr->fr_statemax = old->fr_statemax; fr->fr_flineno = old->fr_flineno; fr->fr_type = old->fr_type; fr->fr_flags = fr_frflags4to5(old->fr_flags); fr->fr_logtag = old->fr_logtag; fr->fr_collect = old->fr_collect; fr->fr_arg = old->fr_arg; fr->fr_loglevel = old->fr_loglevel; fr->fr_age[0] = old->fr_age[0]; fr->fr_age[1] = old->fr_age[1]; fr->fr_tifs[0].fd_ip6 = old->fr_tifs[0].ofd_ip6; fr->fr_tifs[0].fd_type = FRD_NORMAL; fr->fr_tifs[1].fd_ip6 = old->fr_tifs[1].ofd_ip6; fr->fr_tifs[1].fd_type = FRD_NORMAL; fr->fr_dif.fd_ip6 = old->fr_dif.ofd_ip6; fr->fr_dif.fd_type = FRD_NORMAL; if (old->fr_v == 4) fr->fr_family = AF_INET; if (old->fr_v == 6) fr->fr_family = AF_INET6; fr->fr_icode = old->fr_icode; fr->fr_cksum = old->fr_cksum; fr->fr_namelen = 0; fr->fr_ifnames[0] = -1; fr->fr_ifnames[1] = -1; fr->fr_ifnames[2] = -1; fr->fr_ifnames[3] = -1; fr->fr_dif.fd_name = -1; fr->fr_tifs[0].fd_name = -1; fr->fr_tifs[1].fd_name = -1; fr->fr_group = -1; fr->fr_grhead = -1; fr->fr_icmphead = -1; if (size == 0) { fr->fr_size = sizeof(*fr) + LIFNAMSIZ * 7 + FR_GROUPLEN * 2; fr->fr_size += 9; /* room for \0's */ } else { char *names = fr->fr_names; int nlen = fr->fr_namelen; fr->fr_size = size; if (old->fr_ifnames[0][0] != '\0') { fr->fr_ifnames[0] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[0], LIFNAMSIZ); } if (old->fr_ifnames[1][0] != '\0') { fr->fr_ifnames[1] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[1], LIFNAMSIZ); } if (old->fr_ifnames[2][0] != '\0') { fr->fr_ifnames[2] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[2], LIFNAMSIZ); } if (old->fr_ifnames[3][0] != '\0') { fr->fr_ifnames[3] = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_ifnames[3], LIFNAMSIZ); } if (old->fr_tifs[0].fd_ifname[0] != '\0') { fr->fr_tifs[0].fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_tifs[0].fd_ifname, LIFNAMSIZ); } if (old->fr_tifs[1].fd_ifname[0] != '\0') { fr->fr_tifs[1].fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_tifs[1].fd_ifname, LIFNAMSIZ); } if (old->fr_dif.fd_ifname[0] != '\0') { fr->fr_dif.fd_name = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_dif.fd_ifname, LIFNAMSIZ); } if (old->fr_group[0] != '\0') { fr->fr_group = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_group, LIFNAMSIZ); } if (old->fr_grhead[0] != '\0') { fr->fr_grhead = nlen; nlen = ipf_addfrstr(names, nlen, old->fr_grhead, LIFNAMSIZ); } fr->fr_namelen = nlen; if (old->fr_type == FR_T_IPF) { int offset = fr->fr_namelen; ipfobj_t obj; int error; obj.ipfo_type = IPFOBJ_FRIPF; obj.ipfo_rev = 4010100; obj.ipfo_ptr = old->fr_data; if ((offset & 7) != 0) offset += 8 - (offset & 7); offset += 8 - (offset & 7); error = ipf_in_compat(softc, &obj, fr->fr_names + offset, 0); if (error == 0) { fr->fr_data = fr->fr_names + offset; fr->fr_dsize = sizeof(fripf_t); } } } } static void friostat_4_1_33_to_current(old, current) friostat_4_1_33_t *old; void *current; { friostat_t *fiop = (friostat_t *)current; bcopy(&old->of_st[0], &fiop->f_st[0].fr_pass, sizeof(old->of_st[0])); bcopy(&old->of_st[1], &fiop->f_st[1].fr_pass, sizeof(old->of_st[1])); fiop->f_ipf[0][0] = old->f_ipf[0][0]; fiop->f_ipf[0][1] = old->f_ipf[0][1]; fiop->f_ipf[1][0] = old->f_ipf[1][0]; fiop->f_ipf[1][1] = old->f_ipf[1][1]; fiop->f_acct[0][0] = old->f_acct[0][0]; fiop->f_acct[0][1] = old->f_acct[0][1]; fiop->f_acct[1][0] = old->f_acct[1][0]; fiop->f_acct[1][1] = old->f_acct[1][1]; fiop->f_auth = fiop->f_auth; bcopy(&old->f_groups, &fiop->f_groups, sizeof(old->f_groups)); bcopy(&old->f_froute, &fiop->f_froute, sizeof(old->f_froute)); fiop->f_ticks = old->f_ticks; bcopy(&old->f_locks, &fiop->f_locks, sizeof(old->f_locks)); fiop->f_defpass = old->f_defpass; fiop->f_active = old->f_active; fiop->f_running = old->f_running; fiop->f_logging = old->f_logging; fiop->f_features = old->f_features; bcopy(old->f_version, fiop->f_version, sizeof(old->f_version)); } static void friostat_4_1_0_to_current(old, current) friostat_4_1_0_t *old; void *current; { friostat_t *fiop = (friostat_t *)current; bcopy(&old->of_st[0], &fiop->f_st[0].fr_pass, sizeof(old->of_st[0])); bcopy(&old->of_st[1], &fiop->f_st[1].fr_pass, sizeof(old->of_st[1])); fiop->f_ipf[0][0] = old->f_ipf[0][0]; fiop->f_ipf[0][1] = old->f_ipf[0][1]; fiop->f_ipf[1][0] = old->f_ipf[1][0]; fiop->f_ipf[1][1] = old->f_ipf[1][1]; fiop->f_acct[0][0] = old->f_acct[0][0]; fiop->f_acct[0][1] = old->f_acct[0][1]; fiop->f_acct[1][0] = old->f_acct[1][0]; fiop->f_acct[1][1] = old->f_acct[1][1]; fiop->f_auth = fiop->f_auth; bcopy(&old->f_groups, &fiop->f_groups, sizeof(old->f_groups)); bcopy(&old->f_froute, &fiop->f_froute, sizeof(old->f_froute)); fiop->f_ticks = old->f_ticks; bcopy(&old->f_locks, &fiop->f_locks, sizeof(old->f_locks)); fiop->f_defpass = old->f_defpass; fiop->f_active = old->f_active; fiop->f_running = old->f_running; fiop->f_logging = old->f_logging; fiop->f_features = old->f_features; bcopy(old->f_version, fiop->f_version, sizeof(old->f_version)); } static void ipnat_4_1_14_to_current(old, current, size) ipnat_4_1_14_t *old; void *current; int size; { ipnat_t *np = (ipnat_t *)current; np->in_space = old->in_space; np->in_hv[0] = old->in_hv; np->in_hv[1] = old->in_hv; np->in_flineno = old->in_flineno; if (old->in_redir == NAT_REDIRECT) np->in_dpnext = old->in_pnext; else np->in_spnext = old->in_pnext; np->in_v[0] = old->in_v; np->in_v[1] = old->in_v; np->in_flags = old->in_flags; np->in_mssclamp = old->in_mssclamp; np->in_age[0] = old->in_age[0]; np->in_age[1] = old->in_age[1]; np->in_redir = old->in_redir; np->in_pr[0] = old->in_p; np->in_pr[1] = old->in_p; if (np->in_redir == NAT_REDIRECT) { np->in_ndst.na_nextaddr = old->in_next6; np->in_ndst.na_addr[0] = old->in_in[0]; np->in_ndst.na_addr[1] = old->in_in[1]; np->in_ndst.na_atype = FRI_NORMAL; np->in_odst.na_addr[0] = old->in_out[0]; np->in_odst.na_addr[1] = old->in_out[1]; np->in_odst.na_atype = FRI_NORMAL; np->in_osrc.na_addr[0] = old->in_src[0]; np->in_osrc.na_addr[1] = old->in_src[1]; np->in_osrc.na_atype = FRI_NORMAL; } else { np->in_nsrc.na_nextaddr = old->in_next6; np->in_nsrc.na_addr[0] = old->in_out[0]; np->in_nsrc.na_addr[1] = old->in_out[1]; np->in_nsrc.na_atype = FRI_NORMAL; np->in_osrc.na_addr[0] = old->in_in[0]; np->in_osrc.na_addr[1] = old->in_in[1]; np->in_osrc.na_atype = FRI_NORMAL; np->in_odst.na_addr[0] = old->in_src[0]; np->in_odst.na_addr[1] = old->in_src[1]; np->in_odst.na_atype = FRI_NORMAL; } ipfv4tuctov5(&old->in_tuc, &np->in_tuc); if (np->in_redir == NAT_REDIRECT) { np->in_dpmin = old->in_port[0]; np->in_dpmax = old->in_port[1]; } else { np->in_spmin = old->in_port[0]; np->in_spmax = old->in_port[1]; } np->in_ppip = old->in_ppip; np->in_ippip = old->in_ippip; np->in_tag = old->in_tag; np->in_namelen = 0; np->in_plabel = -1; np->in_ifnames[0] = -1; np->in_ifnames[1] = -1; if (size == 0) { np->in_size = sizeof(*np); np->in_size += LIFNAMSIZ * 2 + APR_LABELLEN; np->in_size += 3; } else { int nlen = np->in_namelen; char *names = np->in_names; if (old->in_ifnames[0][0] != '\0') { np->in_ifnames[0] = nlen; nlen = ipf_addfrstr(names, nlen, old->in_ifnames[0], LIFNAMSIZ); } if (old->in_ifnames[1][0] != '\0') { np->in_ifnames[0] = nlen; nlen = ipf_addfrstr(names, nlen, old->in_ifnames[1], LIFNAMSIZ); } if (old->in_plabel[0] != '\0') { np->in_plabel = nlen; nlen = ipf_addfrstr(names, nlen, old->in_plabel, LIFNAMSIZ); } np->in_namelen = nlen; np->in_size = size; } } static void ipnat_4_1_0_to_current(old, current, size) ipnat_4_1_0_t *old; void *current; int size; { ipnat_t *np = (ipnat_t *)current; np->in_space = old->in_space; np->in_hv[0] = old->in_hv; np->in_hv[1] = old->in_hv; np->in_flineno = old->in_flineno; if (old->in_redir == NAT_REDIRECT) np->in_dpnext = old->in_pnext; else np->in_spnext = old->in_pnext; np->in_v[0] = old->in_v; np->in_v[1] = old->in_v; np->in_flags = old->in_flags; np->in_mssclamp = old->in_mssclamp; np->in_age[0] = old->in_age[0]; np->in_age[1] = old->in_age[1]; np->in_redir = old->in_redir; np->in_pr[0] = old->in_p; np->in_pr[1] = old->in_p; if (np->in_redir == NAT_REDIRECT) { np->in_ndst.na_nextaddr = old->in_next6; bcopy(&old->in_in, &np->in_ndst.na_addr, sizeof(old->in_in)); bcopy(&old->in_out, &np->in_odst.na_addr, sizeof(old->in_out)); bcopy(&old->in_src, &np->in_osrc.na_addr, sizeof(old->in_src)); } else { np->in_nsrc.na_nextaddr = old->in_next6; bcopy(&old->in_in, &np->in_osrc.na_addr, sizeof(old->in_in)); bcopy(&old->in_out, &np->in_nsrc.na_addr, sizeof(old->in_out)); bcopy(&old->in_src, &np->in_odst.na_addr, sizeof(old->in_src)); } ipfv4tuctov5(&old->in_tuc, &np->in_tuc); if (np->in_redir == NAT_REDIRECT) { np->in_dpmin = old->in_port[0]; np->in_dpmax = old->in_port[1]; } else { np->in_spmin = old->in_port[0]; np->in_spmax = old->in_port[1]; } np->in_ppip = old->in_ppip; np->in_ippip = old->in_ippip; bcopy(&old->in_tag, &np->in_tag, sizeof(np->in_tag)); np->in_namelen = 0; np->in_plabel = -1; np->in_ifnames[0] = -1; np->in_ifnames[1] = -1; if (size == 0) { np->in_size = sizeof(*np); np->in_size += LIFNAMSIZ * 2 + APR_LABELLEN; np->in_size += 3; } else { int nlen = np->in_namelen; char *names = np->in_names; if (old->in_ifnames[0][0] != '\0') { np->in_ifnames[0] = nlen; nlen = ipf_addfrstr(names, nlen, old->in_ifnames[0], LIFNAMSIZ); } if (old->in_ifnames[1][0] != '\0') { np->in_ifnames[0] = nlen; nlen = ipf_addfrstr(names, nlen, old->in_ifnames[1], LIFNAMSIZ); } if (old->in_plabel[0] != '\0') { np->in_plabel = nlen; nlen = ipf_addfrstr(names, nlen, old->in_plabel, LIFNAMSIZ); } np->in_namelen = nlen; np->in_size = size; } } static void frauth_4_1_32_to_current(old, current) frauth_4_1_32_t *old; void *current; { frauth_t *fra = (frauth_t *)current; fra->fra_age = old->fra_age; fra->fra_len = old->fra_len; fra->fra_index = old->fra_index; fra->fra_pass = old->fra_pass; fr_info_4_1_32_to_current(&old->fra_info, &fra->fra_info); fra->fra_buf = old->fra_buf; fra->fra_flx = old->fra_flx; #ifdef MENTAT fra->fra_q = old->fra_q; fra->fra_m = old->fra_m; #endif } static void frauth_4_1_29_to_current(old, current) frauth_4_1_29_t *old; void *current; { frauth_t *fra = (frauth_t *)current; fra->fra_age = old->fra_age; fra->fra_len = old->fra_len; fra->fra_index = old->fra_index; fra->fra_pass = old->fra_pass; fr_info_4_1_24_to_current(&old->fra_info, &fra->fra_info); fra->fra_buf = old->fra_buf; fra->fra_flx = old->fra_flx; #ifdef MENTAT fra->fra_q = old->fra_q; fra->fra_m = old->fra_m; #endif } static void frauth_4_1_24_to_current(old, current) frauth_4_1_24_t *old; void *current; { frauth_t *fra = (frauth_t *)current; fra->fra_age = old->fra_age; fra->fra_len = old->fra_len; fra->fra_index = old->fra_index; fra->fra_pass = old->fra_pass; fr_info_4_1_24_to_current(&old->fra_info, &fra->fra_info); fra->fra_buf = old->fra_buf; #ifdef MENTAT fra->fra_q = old->fra_q; fra->fra_m = old->fra_m; #endif } static void frauth_4_1_23_to_current(old, current) frauth_4_1_23_t *old; void *current; { frauth_t *fra = (frauth_t *)current; fra->fra_age = old->fra_age; fra->fra_len = old->fra_len; fra->fra_index = old->fra_index; fra->fra_pass = old->fra_pass; fr_info_4_1_23_to_current(&old->fra_info, &fra->fra_info); fra->fra_buf = old->fra_buf; #ifdef MENTAT fra->fra_q = old->fra_q; fra->fra_m = old->fra_m; #endif } static void frauth_4_1_11_to_current(old, current) frauth_4_1_11_t *old; void *current; { frauth_t *fra = (frauth_t *)current; fra->fra_age = old->fra_age; fra->fra_len = old->fra_len; fra->fra_index = old->fra_index; fra->fra_pass = old->fra_pass; fr_info_4_1_11_to_current(&old->fra_info, &fra->fra_info); fra->fra_buf = old->fra_buf; #ifdef MENTAT fra->fra_q = old->fra_q; fra->fra_m = old->fra_m; #endif } static void fr_info_4_1_32_to_current(old, current) fr_info_4_1_32_t *old; void *current; { fr_info_t *fin = (fr_info_t *)current; fin->fin_ifp = old->fin_ifp; ipf_v4iptov5(&old->fin_fi, &fin->fin_fi); bcopy(&old->fin_dat, &fin->fin_dat, sizeof(old->fin_dat)); fin->fin_out = old->fin_out; fin->fin_rev = old->fin_rev; fin->fin_hlen = old->fin_hlen; fin->fin_tcpf = old->ofin_tcpf; fin->fin_icode = old->fin_icode; fin->fin_rule = old->fin_rule; bcopy(old->fin_group, fin->fin_group, sizeof(old->fin_group)); fin->fin_fr = old->fin_fr; fin->fin_dp = old->fin_dp; fin->fin_dlen = old->fin_dlen; fin->fin_plen = old->fin_plen; fin->fin_ipoff = old->fin_ipoff; fin->fin_id = old->fin_id; fin->fin_off = old->fin_off; fin->fin_depth = old->fin_depth; fin->fin_error = old->fin_error; fin->fin_cksum = old->fin_cksum; fin->fin_nattag = old->fin_nattag; fin->fin_ip = old->ofin_ip; fin->fin_mp = old->fin_mp; fin->fin_m = old->fin_m; #ifdef MENTAT fin->fin_qfm = old->fin_qfm; fin->fin_qpi = old->fin_qpi; #endif } static void fr_info_4_1_24_to_current(old, current) fr_info_4_1_24_t *old; void *current; { fr_info_t *fin = (fr_info_t *)current; fin->fin_ifp = old->fin_ifp; ipf_v4iptov5(&old->fin_fi, &fin->fin_fi); bcopy(&old->fin_dat, &fin->fin_dat, sizeof(old->fin_dat)); fin->fin_out = old->fin_out; fin->fin_rev = old->fin_rev; fin->fin_hlen = old->fin_hlen; fin->fin_tcpf = old->ofin_tcpf; fin->fin_icode = old->fin_icode; fin->fin_rule = old->fin_rule; bcopy(old->fin_group, fin->fin_group, sizeof(old->fin_group)); fin->fin_fr = old->fin_fr; fin->fin_dp = old->fin_dp; fin->fin_dlen = old->fin_dlen; fin->fin_plen = old->fin_plen; fin->fin_ipoff = old->fin_ipoff; fin->fin_id = old->fin_id; fin->fin_off = old->fin_off; fin->fin_depth = old->fin_depth; fin->fin_error = old->fin_error; fin->fin_cksum = old->fin_cksum; fin->fin_nattag = old->fin_nattag; fin->fin_ip = old->ofin_ip; fin->fin_mp = old->fin_mp; fin->fin_m = old->fin_m; #ifdef MENTAT fin->fin_qfm = old->fin_qfm; fin->fin_qpi = old->fin_qpi; #endif } static void fr_info_4_1_23_to_current(old, current) fr_info_4_1_23_t *old; void *current; { fr_info_t *fin = (fr_info_t *)current; fin->fin_ifp = old->fin_ifp; ipf_v4iptov5(&old->fin_fi, &fin->fin_fi); bcopy(&old->fin_dat, &fin->fin_dat, sizeof(old->fin_dat)); fin->fin_out = old->fin_out; fin->fin_rev = old->fin_rev; fin->fin_hlen = old->fin_hlen; fin->fin_tcpf = old->ofin_tcpf; fin->fin_icode = old->fin_icode; fin->fin_rule = old->fin_rule; bcopy(old->fin_group, fin->fin_group, sizeof(old->fin_group)); fin->fin_fr = old->fin_fr; fin->fin_dp = old->fin_dp; fin->fin_dlen = old->fin_dlen; fin->fin_plen = old->fin_plen; fin->fin_ipoff = old->fin_ipoff; fin->fin_id = old->fin_id; fin->fin_off = old->fin_off; fin->fin_depth = old->fin_depth; fin->fin_error = old->fin_error; fin->fin_nattag = old->fin_nattag; fin->fin_ip = old->ofin_ip; fin->fin_mp = old->fin_mp; fin->fin_m = old->fin_m; #ifdef MENTAT fin->fin_qfm = old->fin_qfm; fin->fin_qpi = old->fin_qpi; #endif } static void fr_info_4_1_11_to_current(old, current) fr_info_4_1_11_t *old; void *current; { fr_info_t *fin = (fr_info_t *)current; fin->fin_ifp = old->fin_ifp; ipf_v4iptov5(&old->fin_fi, &fin->fin_fi); bcopy(&old->fin_dat, &fin->fin_dat, sizeof(old->fin_dat)); fin->fin_out = old->fin_out; fin->fin_rev = old->fin_rev; fin->fin_hlen = old->fin_hlen; fin->fin_tcpf = old->ofin_tcpf; fin->fin_icode = old->fin_icode; fin->fin_rule = old->fin_rule; bcopy(old->fin_group, fin->fin_group, sizeof(old->fin_group)); fin->fin_fr = old->fin_fr; fin->fin_dp = old->fin_dp; fin->fin_dlen = old->fin_dlen; fin->fin_plen = old->fin_plen; fin->fin_ipoff = old->fin_ipoff; fin->fin_id = old->fin_id; fin->fin_off = old->fin_off; fin->fin_depth = old->fin_depth; fin->fin_error = old->fin_error; fin->fin_nattag = old->fin_nattag; fin->fin_ip = old->ofin_ip; fin->fin_mp = old->fin_mp; fin->fin_m = old->fin_m; #ifdef MENTAT fin->fin_qfm = old->fin_qfm; fin->fin_qpi = old->fin_qpi; #endif } static void nat_4_1_3_to_current(nat_4_1_3_t *old, nat_t *current) { bzero((void *)current, sizeof(*current)); bcopy((void *)old, (void *)current, sizeof(*old)); } static void nat_4_1_14_to_current(nat_4_1_14_t *old, nat_t *current) { bzero((void *)current, sizeof(*current)); bcopy((void *)old, (void *)current, sizeof(*old)); } static void nat_save_4_1_16_to_current(softc, old, current) ipf_main_softc_t *softc; nat_save_4_1_16_t *old; void *current; { nat_save_t *nats = (nat_save_t *)current; nats->ipn_next = old->ipn_next; nat_4_1_14_to_current(&old->ipn_nat, &nats->ipn_nat); bcopy(&old->ipn_ipnat, &nats->ipn_ipnat, sizeof(old->ipn_ipnat)); frentry_4_1_16_to_current(softc, &old->ipn_fr, &nats->ipn_fr, 0); nats->ipn_dsize = old->ipn_dsize; bcopy(old->ipn_data, nats->ipn_data, sizeof(nats->ipn_data)); } static void nat_save_4_1_14_to_current(softc, old, current) ipf_main_softc_t *softc; nat_save_4_1_14_t *old; void *current; { nat_save_t *nats = (nat_save_t *)current; nats->ipn_next = old->ipn_next; nat_4_1_14_to_current(&old->ipn_nat, &nats->ipn_nat); bcopy(&old->ipn_ipnat, &nats->ipn_ipnat, sizeof(old->ipn_ipnat)); frentry_4_1_0_to_current(softc, &old->ipn_fr, &nats->ipn_fr, 0); nats->ipn_dsize = old->ipn_dsize; bcopy(old->ipn_data, nats->ipn_data, sizeof(nats->ipn_data)); } static void nat_save_4_1_3_to_current(softc, old, current) ipf_main_softc_t *softc; nat_save_4_1_3_t *old; void *current; { nat_save_t *nats = (nat_save_t *)current; nats->ipn_next = old->ipn_next; nat_4_1_3_to_current(&old->ipn_nat, &nats->ipn_nat); ipnat_4_1_0_to_current(&old->ipn_ipnat, &nats->ipn_ipnat, 0); frentry_4_1_0_to_current(softc, &old->ipn_fr, &nats->ipn_fr, 0); nats->ipn_dsize = old->ipn_dsize; bcopy(old->ipn_data, nats->ipn_data, sizeof(nats->ipn_data)); } static void natstat_current_to_4_1_32(current, old) void *current; natstat_4_1_32_t *old; { natstat_t *ns = (natstat_t *)current; old->ns_mapped[0] = ns->ns_side[0].ns_translated; old->ns_mapped[1] = ns->ns_side[1].ns_translated; old->ns_rules = ns->ns_side[0].ns_inuse + ns->ns_side[1].ns_inuse; old->ns_added = ns->ns_side[0].ns_added + ns->ns_side[1].ns_added; old->ns_expire = ns->ns_expire; old->ns_inuse = ns->ns_side[0].ns_inuse + ns->ns_side[1].ns_inuse; old->ns_logged = ns->ns_log_ok; old->ns_logfail = ns->ns_log_fail; old->ns_memfail = ns->ns_side[0].ns_memfail + ns->ns_side[1].ns_memfail; old->ns_badnat = ns->ns_side[0].ns_badnat + ns->ns_side[1].ns_badnat; old->ns_addtrpnt = ns->ns_addtrpnt; old->ns_table[0] = ns->ns_side[0].ns_table; old->ns_table[1] = ns->ns_side[1].ns_table; old->ns_maptable = NULL; old->ns_list = ns->ns_list; old->ns_apslist = NULL; old->ns_wilds = ns->ns_wilds; old->ns_nattab_sz = ns->ns_nattab_sz; old->ns_nattab_max = ns->ns_nattab_max; old->ns_rultab_sz = ns->ns_rultab_sz; old->ns_rdrtab_sz = ns->ns_rdrtab_sz; old->ns_trpntab_sz = ns->ns_trpntab_sz; old->ns_hostmap_sz = 0; old->ns_instances = ns->ns_instances; old->ns_maplist = ns->ns_maplist; old->ns_bucketlen[0] = (u_long *)ns->ns_side[0].ns_bucketlen; old->ns_bucketlen[1] = (u_long *)ns->ns_side[1].ns_bucketlen; old->ns_ticks = ns->ns_ticks; old->ns_orphans = ns->ns_orphans; old->ns_uncreate[0][0] = ns->ns_side[0].ns_uncreate[0]; old->ns_uncreate[0][1] = ns->ns_side[0].ns_uncreate[1]; old->ns_uncreate[1][0] = ns->ns_side[1].ns_uncreate[0]; old->ns_uncreate[1][1] = ns->ns_side[1].ns_uncreate[1]; } static void natstat_current_to_4_1_27(current, old) void *current; natstat_4_1_27_t *old; { natstat_t *ns = (natstat_t *)current; old->ns_mapped[0] = ns->ns_side[0].ns_translated; old->ns_mapped[1] = ns->ns_side[1].ns_translated; old->ns_rules = ns->ns_side[0].ns_inuse + ns->ns_side[1].ns_inuse; old->ns_added = ns->ns_side[0].ns_added + ns->ns_side[1].ns_added; old->ns_expire = ns->ns_expire; old->ns_inuse = ns->ns_side[0].ns_inuse + ns->ns_side[1].ns_inuse; old->ns_logged = ns->ns_log_ok; old->ns_logfail = ns->ns_log_fail; old->ns_memfail = ns->ns_side[0].ns_memfail + ns->ns_side[1].ns_memfail; old->ns_badnat = ns->ns_side[0].ns_badnat + ns->ns_side[1].ns_badnat; old->ns_addtrpnt = ns->ns_addtrpnt; old->ns_table[0] = ns->ns_side[0].ns_table; old->ns_table[1] = ns->ns_side[1].ns_table; old->ns_maptable = NULL; old->ns_list = ns->ns_list; old->ns_apslist = NULL; old->ns_wilds = ns->ns_wilds; old->ns_nattab_sz = ns->ns_nattab_sz; old->ns_nattab_max = ns->ns_nattab_max; old->ns_rultab_sz = ns->ns_rultab_sz; old->ns_rdrtab_sz = ns->ns_rdrtab_sz; old->ns_trpntab_sz = ns->ns_trpntab_sz; old->ns_hostmap_sz = 0; old->ns_instances = ns->ns_instances; old->ns_maplist = ns->ns_maplist; old->ns_bucketlen[0] = (u_long *)ns->ns_side[0].ns_bucketlen; old->ns_bucketlen[1] = (u_long *)ns->ns_side[1].ns_bucketlen; old->ns_ticks = ns->ns_ticks; old->ns_orphans = ns->ns_orphans; } static void natstat_current_to_4_1_16(current, old) void *current; natstat_4_1_16_t *old; { natstat_t *ns = (natstat_t *)current; old->ns_mapped[0] = ns->ns_side[0].ns_translated; old->ns_mapped[1] = ns->ns_side[1].ns_translated; old->ns_rules = ns->ns_side[0].ns_inuse + ns->ns_side[1].ns_inuse; old->ns_added = ns->ns_side[0].ns_added + ns->ns_side[1].ns_added; old->ns_expire = ns->ns_expire; old->ns_inuse = ns->ns_side[0].ns_inuse + ns->ns_side[1].ns_inuse; old->ns_logged = ns->ns_log_ok; old->ns_logfail = ns->ns_log_fail; old->ns_memfail = ns->ns_side[0].ns_memfail + ns->ns_side[1].ns_memfail; old->ns_badnat = ns->ns_side[0].ns_badnat + ns->ns_side[1].ns_badnat; old->ns_addtrpnt = ns->ns_addtrpnt; old->ns_table[0] = ns->ns_side[0].ns_table; old->ns_table[1] = ns->ns_side[1].ns_table; old->ns_maptable = NULL; old->ns_list = ns->ns_list; old->ns_apslist = NULL; old->ns_wilds = ns->ns_wilds; old->ns_nattab_sz = ns->ns_nattab_sz; old->ns_nattab_max = ns->ns_nattab_max; old->ns_rultab_sz = ns->ns_rultab_sz; old->ns_rdrtab_sz = ns->ns_rdrtab_sz; old->ns_trpntab_sz = ns->ns_trpntab_sz; old->ns_hostmap_sz = 0; old->ns_instances = ns->ns_instances; old->ns_maplist = ns->ns_maplist; old->ns_bucketlen[0] = (u_long *)ns->ns_side[0].ns_bucketlen; old->ns_bucketlen[1] = (u_long *)ns->ns_side[1].ns_bucketlen; old->ns_ticks = ns->ns_ticks; } static void natstat_current_to_4_1_0(current, old) void *current; natstat_4_1_0_t *old; { natstat_t *ns = (natstat_t *)current; old->ns_mapped[0] = ns->ns_side[0].ns_translated; old->ns_mapped[1] = ns->ns_side[1].ns_translated; old->ns_rules = ns->ns_side[0].ns_inuse + ns->ns_side[1].ns_inuse; old->ns_added = ns->ns_side[0].ns_added + ns->ns_side[1].ns_added; old->ns_expire = ns->ns_expire; old->ns_inuse = ns->ns_side[0].ns_inuse + ns->ns_side[1].ns_inuse; old->ns_logged = ns->ns_log_ok; old->ns_logfail = ns->ns_log_fail; old->ns_memfail = ns->ns_side[0].ns_memfail + ns->ns_side[1].ns_memfail; old->ns_badnat = ns->ns_side[0].ns_badnat + ns->ns_side[1].ns_badnat; old->ns_addtrpnt = ns->ns_addtrpnt; old->ns_table[0] = ns->ns_side[0].ns_table; old->ns_table[1] = ns->ns_side[1].ns_table; old->ns_maptable = NULL; old->ns_list = ns->ns_list; old->ns_apslist = NULL; old->ns_wilds = ns->ns_wilds; old->ns_nattab_sz = ns->ns_nattab_sz; old->ns_nattab_max = ns->ns_nattab_max; old->ns_rultab_sz = ns->ns_rultab_sz; old->ns_rdrtab_sz = ns->ns_rdrtab_sz; old->ns_trpntab_sz = ns->ns_trpntab_sz; old->ns_hostmap_sz = 0; old->ns_instances = ns->ns_instances; old->ns_maplist = ns->ns_maplist; old->ns_bucketlen[0] = (u_long *)ns->ns_side[0].ns_bucketlen; old->ns_bucketlen[1] = (u_long *)ns->ns_side[1].ns_bucketlen; } static void ipstate_save_current_to_4_1_16(current, old) void *current; ipstate_save_4_1_16_t *old; { ipstate_save_t *ips = (ipstate_save_t *)current; old->ips_next = ips->ips_next; ipstate_current_to_4_1_0(&ips->ips_is, &old->ips_is); frentry_current_to_4_1_16(&ips->ips_fr, &old->ips_fr); } static void ipstate_save_current_to_4_1_0(current, old) void *current; ipstate_save_4_1_0_t *old; { ipstate_save_t *ips = (ipstate_save_t *)current; old->ips_next = ips->ips_next; ipstate_current_to_4_1_0(&ips->ips_is, &old->ips_is); frentry_current_to_4_1_0(&ips->ips_fr, &old->ips_fr); } int ipf_out_compat(softc, obj, ptr) ipf_main_softc_t *softc; ipfobj_t *obj; void *ptr; { frentry_t *fr; int error; IPFERROR(140042); error = EINVAL; switch (obj->ipfo_type) { default : break; case IPFOBJ_FRENTRY : if (obj->ipfo_rev >= 4013400) { frentry_4_1_34_t *old; KMALLOC(old, frentry_4_1_34_t *); if (old == NULL) { IPFERROR(140043); error = ENOMEM; break; } frentry_current_to_4_1_34(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error == 0 && old->fr_dsize > 0) { char *dst = obj->ipfo_ptr; fr = ptr; dst += sizeof(*old); error = COPYOUT(fr->fr_data, dst, old->fr_dsize); if (error != 0) { IPFERROR(140044); } } KFREE(old); obj->ipfo_size = sizeof(*old); } else if (obj->ipfo_rev >= 4011600) { frentry_4_1_16_t *old; KMALLOC(old, frentry_4_1_16_t *); if (old == NULL) { IPFERROR(140045); error = ENOMEM; break; } frentry_current_to_4_1_16(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140046); } KFREE(old); obj->ipfo_size = sizeof(*old); } else { frentry_4_1_0_t *old; KMALLOC(old, frentry_4_1_0_t *); if (old == NULL) { IPFERROR(140047); error = ENOMEM; break; } frentry_current_to_4_1_0(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140048); } KFREE(old); obj->ipfo_size = sizeof(*old); } break; case IPFOBJ_IPFSTAT : if (obj->ipfo_rev >= 4013300) { friostat_4_1_33_t *old; KMALLOC(old, friostat_4_1_33_t *); if (old == NULL) { IPFERROR(140049); error = ENOMEM; break; } friostat_current_to_4_1_33(ptr, old, obj->ipfo_rev); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140050); } KFREE(old); } else { friostat_4_1_0_t *old; KMALLOC(old, friostat_4_1_0_t *); if (old == NULL) { IPFERROR(140051); error = ENOMEM; break; } friostat_current_to_4_1_0(ptr, old, obj->ipfo_rev); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140052); } KFREE(old); } break; case IPFOBJ_IPFINFO : /* unused */ break; case IPFOBJ_IPNAT : if (obj->ipfo_rev >= 4011400) { ipnat_4_1_14_t *old; KMALLOC(old, ipnat_4_1_14_t *); if (old == NULL) { IPFERROR(140053); error = ENOMEM; break; } ipnat_current_to_4_1_14(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140054); } KFREE(old); } else { ipnat_4_1_0_t *old; KMALLOC(old, ipnat_4_1_0_t *); if (old == NULL) { IPFERROR(140055); error = ENOMEM; break; } ipnat_current_to_4_1_0(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140056); } KFREE(old); } break; case IPFOBJ_NATSTAT : if (obj->ipfo_rev >= 4013200) { natstat_4_1_32_t *old; KMALLOC(old, natstat_4_1_32_t *); if (old == NULL) { IPFERROR(140057); error = ENOMEM; break; } natstat_current_to_4_1_32(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140058); } KFREE(old); } else if (obj->ipfo_rev >= 4012700) { natstat_4_1_27_t *old; KMALLOC(old, natstat_4_1_27_t *); if (old == NULL) { IPFERROR(140059); error = ENOMEM; break; } natstat_current_to_4_1_27(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140060); } KFREE(old); } else if (obj->ipfo_rev >= 4011600) { natstat_4_1_16_t *old; KMALLOC(old, natstat_4_1_16_t *); if (old == NULL) { IPFERROR(140061); error = ENOMEM; break; } natstat_current_to_4_1_16(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140062); } KFREE(old); } else { natstat_4_1_0_t *old; KMALLOC(old, natstat_4_1_0_t *); if (old == NULL) { IPFERROR(140063); error = ENOMEM; break; } natstat_current_to_4_1_0(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140064); } KFREE(old); } break; case IPFOBJ_STATESAVE : if (obj->ipfo_rev >= 4011600) { ipstate_save_4_1_16_t *old; KMALLOC(old, ipstate_save_4_1_16_t *); if (old == NULL) { IPFERROR(140065); error = ENOMEM; break; } ipstate_save_current_to_4_1_16(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140066); } KFREE(old); } else { ipstate_save_4_1_0_t *old; KMALLOC(old, ipstate_save_4_1_0_t *); if (old == NULL) { IPFERROR(140067); error = ENOMEM; break; } ipstate_save_current_to_4_1_0(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140068); } KFREE(old); } break; case IPFOBJ_NATSAVE : if (obj->ipfo_rev >= 4011600) { nat_save_4_1_16_t *old16; KMALLOC(old16, nat_save_4_1_16_t *); if (old16 == NULL) { IPFERROR(140069); error = ENOMEM; break; } nat_save_current_to_4_1_16(ptr, old16); error = COPYOUT(&old16, obj->ipfo_ptr, sizeof(*old16)); if (error != 0) { IPFERROR(140070); } KFREE(old16); } else if (obj->ipfo_rev >= 4011400) { nat_save_4_1_14_t *old14; KMALLOC(old14, nat_save_4_1_14_t *); if (old14 == NULL) { IPFERROR(140071); error = ENOMEM; break; } nat_save_current_to_4_1_14(ptr, old14); error = COPYOUT(&old14, obj->ipfo_ptr, sizeof(*old14)); if (error != 0) { IPFERROR(140072); } KFREE(old14); } else if (obj->ipfo_rev >= 4010300) { nat_save_4_1_3_t *old3; KMALLOC(old3, nat_save_4_1_3_t *); if (old3 == NULL) { IPFERROR(140073); error = ENOMEM; break; } nat_save_current_to_4_1_3(ptr, old3); error = COPYOUT(&old3, obj->ipfo_ptr, sizeof(*old3)); if (error != 0) { IPFERROR(140074); } KFREE(old3); } break; case IPFOBJ_IPSTATE : if (obj->ipfo_rev >= 4011600) { ipstate_4_1_16_t *old; KMALLOC(old, ipstate_4_1_16_t *); if (old == NULL) { IPFERROR(140075); error = ENOMEM; break; } ipstate_current_to_4_1_16(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140076); } KFREE(old); } else { ipstate_4_1_0_t *old; KMALLOC(old, ipstate_4_1_0_t *); if (old == NULL) { IPFERROR(140077); error = ENOMEM; break; } ipstate_current_to_4_1_0(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140078); } KFREE(old); } break; case IPFOBJ_STATESTAT : if (obj->ipfo_rev >= 4012100) { ips_stat_4_1_21_t *old; KMALLOC(old, ips_stat_4_1_21_t *); if (old == NULL) { IPFERROR(140079); error = ENOMEM; break; } ips_stat_current_to_4_1_21(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140080); } KFREE(old); } else { ips_stat_4_1_0_t *old; KMALLOC(old, ips_stat_4_1_0_t *); if (old == NULL) { IPFERROR(140081); error = ENOMEM; break; } ips_stat_current_to_4_1_0(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140082); } KFREE(old); } break; case IPFOBJ_FRAUTH : if (obj->ipfo_rev >= 4012900) { frauth_4_1_29_t *old29; KMALLOC(old29, frauth_4_1_29_t *); if (old29 == NULL) { IPFERROR(140083); error = ENOMEM; break; } frauth_current_to_4_1_29(ptr, old29); error = COPYOUT(old29, obj->ipfo_ptr, sizeof(*old29)); if (error != 0) { IPFERROR(140084); } KFREE(old29); } else if (obj->ipfo_rev >= 4012400) { frauth_4_1_24_t *old24; KMALLOC(old24, frauth_4_1_24_t *); if (old24 == NULL) { IPFERROR(140085); error = ENOMEM; break; } frauth_current_to_4_1_24(ptr, old24); error = COPYOUT(old24, obj->ipfo_ptr, sizeof(*old24)); if (error != 0) { IPFERROR(140086); } KFREE(old24); } else if (obj->ipfo_rev >= 4012300) { frauth_4_1_23_t *old23; KMALLOC(old23, frauth_4_1_23_t *); if (old23 == NULL) { IPFERROR(140087); error = ENOMEM; break; } frauth_current_to_4_1_23(ptr, old23); error = COPYOUT(old23, obj->ipfo_ptr, sizeof(*old23)); if (error != 0) { IPFERROR(140088); } KFREE(old23); } else if (obj->ipfo_rev >= 4011100) { frauth_4_1_11_t *old11; KMALLOC(old11, frauth_4_1_11_t *); if (old11 == NULL) { IPFERROR(140089); error = ENOMEM; break; } frauth_current_to_4_1_11(ptr, old11); error = COPYOUT(old11, obj->ipfo_ptr, sizeof(*old11)); if (error != 0) { IPFERROR(140090); } KFREE(old11); } break; case IPFOBJ_NAT : if (obj->ipfo_rev >= 4012500) { nat_4_1_25_t *old; KMALLOC(old, nat_4_1_25_t *); if (old == NULL) { IPFERROR(140091); error = ENOMEM; break; } nat_current_to_4_1_25(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140092); } KFREE(old); } else if (obj->ipfo_rev >= 4011400) { nat_4_1_14_t *old; KMALLOC(old, nat_4_1_14_t *); if (old == NULL) { IPFERROR(140093); error = ENOMEM; break; } nat_current_to_4_1_14(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140094); } KFREE(old); } else if (obj->ipfo_rev >= 4010300) { nat_4_1_3_t *old; KMALLOC(old, nat_4_1_3_t *); if (old == NULL) { IPFERROR(140095); error = ENOMEM; break; } nat_current_to_4_1_3(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140096); } KFREE(old); } break; case IPFOBJ_FRIPF : if (obj->ipfo_rev < 5000000) { fripf4_t *old; KMALLOC(old, fripf4_t *); if (old == NULL) { IPFERROR(140097); error = ENOMEM; break; } ipf_v5fripftov4(ptr, old); error = COPYOUT(old, obj->ipfo_ptr, sizeof(*old)); if (error != 0) { IPFERROR(140098); } KFREE(old); } break; } return error; } static void friostat_current_to_4_1_33(current, old, rev) void *current; friostat_4_1_33_t *old; int rev; { friostat_t *fiop = (friostat_t *)current; bcopy(&fiop->f_st[0].fr_pass, &old->of_st[0], sizeof(old->of_st[0])); bcopy(&fiop->f_st[1].fr_pass, &old->of_st[1], sizeof(old->of_st[1])); old->f_ipf[0][0] = fiop->f_ipf[0][0]; old->f_ipf[0][1] = fiop->f_ipf[0][1]; old->f_ipf[1][0] = fiop->f_ipf[1][0]; old->f_ipf[1][1] = fiop->f_ipf[1][1]; old->f_acct[0][0] = fiop->f_acct[0][0]; old->f_acct[0][1] = fiop->f_acct[0][1]; old->f_acct[1][0] = fiop->f_acct[1][0]; old->f_acct[1][1] = fiop->f_acct[1][1]; old->f_ipf6[0][0] = NULL; old->f_ipf6[0][1] = NULL; old->f_ipf6[1][0] = NULL; old->f_ipf6[1][1] = NULL; old->f_acct6[0][0] = NULL; old->f_acct6[0][1] = NULL; old->f_acct6[1][0] = NULL; old->f_acct6[1][1] = NULL; old->f_auth = fiop->f_auth; bcopy(&fiop->f_groups, &old->f_groups, sizeof(old->f_groups)); bcopy(&fiop->f_froute, &old->f_froute, sizeof(old->f_froute)); old->f_ticks = fiop->f_ticks; bcopy(&fiop->f_locks, &old->f_locks, sizeof(old->f_locks)); old->f_kmutex_sz = 0; old->f_krwlock_sz = 0; old->f_defpass = fiop->f_defpass; old->f_active = fiop->f_active; old->f_running = fiop->f_running; old->f_logging = fiop->f_logging; old->f_features = fiop->f_features; sprintf(old->f_version, "IP Filter: v%d.%d.%d", (rev / 1000000) % 100, (rev / 10000) % 100, (rev / 100) % 100); } static void friostat_current_to_4_1_0(current, old, rev) void *current; friostat_4_1_0_t *old; int rev; { friostat_t *fiop = (friostat_t *)current; bcopy(&fiop->f_st[0].fr_pass, &old->of_st[0], sizeof(old->of_st[0])); bcopy(&fiop->f_st[1].fr_pass, &old->of_st[1], sizeof(old->of_st[1])); old->f_ipf[0][0] = fiop->f_ipf[0][0]; old->f_ipf[0][1] = fiop->f_ipf[0][1]; old->f_ipf[1][0] = fiop->f_ipf[1][0]; old->f_ipf[1][1] = fiop->f_ipf[1][1]; old->f_acct[0][0] = fiop->f_acct[0][0]; old->f_acct[0][1] = fiop->f_acct[0][1]; old->f_acct[1][0] = fiop->f_acct[1][0]; old->f_acct[1][1] = fiop->f_acct[1][1]; old->f_ipf6[0][0] = NULL; old->f_ipf6[0][1] = NULL; old->f_ipf6[1][0] = NULL; old->f_ipf6[1][1] = NULL; old->f_acct6[0][0] = NULL; old->f_acct6[0][1] = NULL; old->f_acct6[1][0] = NULL; old->f_acct6[1][1] = NULL; old->f_auth = fiop->f_auth; bcopy(&fiop->f_groups, &old->f_groups, sizeof(old->f_groups)); bcopy(&fiop->f_froute, &old->f_froute, sizeof(old->f_froute)); old->f_ticks = fiop->f_ticks; old->f_ipf[0][0] = fiop->f_ipf[0][0]; old->f_ipf[0][1] = fiop->f_ipf[0][1]; old->f_ipf[1][0] = fiop->f_ipf[1][0]; old->f_ipf[1][1] = fiop->f_ipf[1][1]; old->f_acct[0][0] = fiop->f_acct[0][0]; old->f_acct[0][1] = fiop->f_acct[0][1]; old->f_acct[1][0] = fiop->f_acct[1][0]; old->f_acct[1][1] = fiop->f_acct[1][1]; old->f_ipf6[0][0] = NULL; old->f_ipf6[0][1] = NULL; old->f_ipf6[1][0] = NULL; old->f_ipf6[1][1] = NULL; old->f_acct6[0][0] = NULL; old->f_acct6[0][1] = NULL; old->f_acct6[1][0] = NULL; old->f_acct6[1][1] = NULL; old->f_auth = fiop->f_auth; bcopy(&fiop->f_groups, &old->f_groups, sizeof(old->f_groups)); bcopy(&fiop->f_froute, &old->f_froute, sizeof(old->f_froute)); old->f_ticks = fiop->f_ticks; bcopy(&fiop->f_locks, &old->f_locks, sizeof(old->f_locks)); old->f_kmutex_sz = 0; old->f_krwlock_sz = 0; old->f_defpass = fiop->f_defpass; old->f_active = fiop->f_active; old->f_running = fiop->f_running; old->f_logging = fiop->f_logging; old->f_features = fiop->f_features; sprintf(old->f_version, "IP Filter: v%d.%d.%d", (rev / 1000000) % 100, (rev / 10000) % 100, (rev / 100) % 100); } /* * nflags is v5 flags, returns v4 flags. */ static int fr_frflags5to4(nflags) u_32_t nflags; { u_32_t oflags = 0; switch (nflags & FR_CMDMASK) { case FR_CALL : oflags = 0x0; break; case FR_BLOCK : oflags = 0x1; break; case FR_PASS : oflags = 0x2; break; case FR_AUTH : oflags = 0x3; break; case FR_PREAUTH : oflags = 0x4; break; case FR_ACCOUNT : oflags = 0x5; break; case FR_SKIP : oflags = 0x6; break; default : break; } if (nflags & FR_LOG) oflags |= 0x00010; if (nflags & FR_CALLNOW) oflags |= 0x00020; if (nflags & FR_NOTSRCIP) oflags |= 0x00080; if (nflags & FR_NOTDSTIP) oflags |= 0x00040; if (nflags & FR_QUICK) oflags |= 0x00100; if (nflags & FR_KEEPFRAG) oflags |= 0x00200; if (nflags & FR_KEEPSTATE) oflags |= 0x00400; if (nflags & FR_FASTROUTE) oflags |= 0x00800; if (nflags & FR_RETRST) oflags |= 0x01000; if (nflags & FR_RETICMP) oflags |= 0x02000; if (nflags & FR_FAKEICMP) oflags |= 0x03000; if (nflags & FR_OUTQUE) oflags |= 0x04000; if (nflags & FR_INQUE) oflags |= 0x08000; if (nflags & FR_LOGBODY) oflags |= 0x10000; if (nflags & FR_LOGFIRST) oflags |= 0x20000; if (nflags & FR_LOGORBLOCK) oflags |= 0x40000; if (nflags & FR_FRSTRICT) oflags |= 0x100000; if (nflags & FR_STSTRICT) oflags |= 0x200000; if (nflags & FR_NEWISN) oflags |= 0x400000; if (nflags & FR_NOICMPERR) oflags |= 0x800000; if (nflags & FR_STATESYNC) oflags |= 0x1000000; if (nflags & FR_NOMATCH) oflags |= 0x8000000; if (nflags & FR_COPIED) oflags |= 0x40000000; if (nflags & FR_INACTIVE) oflags |= 0x80000000; return oflags; } static void frentry_current_to_4_1_34(current, old) void *current; frentry_4_1_34_t *old; { frentry_t *fr = (frentry_t *)current; old->fr_lock = fr->fr_lock; old->fr_next = fr->fr_next; old->fr_grp = (void *)fr->fr_grp; old->fr_isc = fr->fr_isc; old->fr_ifas[0] = fr->fr_ifas[0]; old->fr_ifas[1] = fr->fr_ifas[1]; old->fr_ifas[2] = fr->fr_ifas[2]; old->fr_ifas[3] = fr->fr_ifas[3]; old->fr_ptr = fr->fr_ptr; old->fr_comment = NULL; old->fr_ref = fr->fr_ref; old->fr_statecnt = fr->fr_statecnt; old->fr_hits = fr->fr_hits; old->fr_bytes = fr->fr_bytes; old->fr_lastpkt.tv_sec = fr->fr_lastpkt.tv_sec; old->fr_lastpkt.tv_usec = fr->fr_lastpkt.tv_usec; old->fr_curpps = fr->fr_curpps; old->fr_dun.fru_data = fr->fr_dun.fru_data; old->fr_func = fr->fr_func; old->fr_dsize = fr->fr_dsize; old->fr_pps = fr->fr_pps; old->fr_statemax = fr->fr_statemax; old->fr_flineno = fr->fr_flineno; old->fr_type = fr->fr_type; old->fr_flags = fr_frflags5to4(fr->fr_flags); old->fr_logtag = fr->fr_logtag; old->fr_collect = fr->fr_collect; old->fr_arg = fr->fr_arg; old->fr_loglevel = fr->fr_loglevel; old->fr_age[0] = fr->fr_age[0]; old->fr_age[1] = fr->fr_age[1]; if (fr->fr_family == AF_INET) old->fr_v = 4; if (fr->fr_family == AF_INET6) old->fr_v = 6; old->fr_icode = fr->fr_icode; old->fr_cksum = fr->fr_cksum; old->fr_tifs[0].ofd_ip6 = fr->fr_tifs[0].fd_ip6; old->fr_tifs[1].ofd_ip6 = fr->fr_tifs[0].fd_ip6; old->fr_dif.ofd_ip6 = fr->fr_dif.fd_ip6; if (fr->fr_ifnames[0] >= 0) { strncpy(old->fr_ifnames[0], fr->fr_names + fr->fr_ifnames[0], LIFNAMSIZ); old->fr_ifnames[0][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[1] >= 0) { strncpy(old->fr_ifnames[1], fr->fr_names + fr->fr_ifnames[1], LIFNAMSIZ); old->fr_ifnames[1][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[2] >= 0) { strncpy(old->fr_ifnames[2], fr->fr_names + fr->fr_ifnames[2], LIFNAMSIZ); old->fr_ifnames[2][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[3] >= 0) { strncpy(old->fr_ifnames[3], fr->fr_names + fr->fr_ifnames[3], LIFNAMSIZ); old->fr_ifnames[3][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_tifs[0].fd_name >= 0) { strncpy(old->fr_tifs[0].fd_ifname, fr->fr_names + fr->fr_tifs[0].fd_name, LIFNAMSIZ); old->fr_tifs[0].fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_tifs[1].fd_name >= 0) { strncpy(old->fr_tifs[1].fd_ifname, fr->fr_names + fr->fr_tifs[1].fd_name, LIFNAMSIZ); old->fr_tifs[1].fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_dif.fd_name >= 0) { strncpy(old->fr_dif.fd_ifname, fr->fr_names + fr->fr_dif.fd_name, LIFNAMSIZ); old->fr_dif.fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_group >= 0) { strncpy(old->fr_group, fr->fr_names + fr->fr_group, FR_GROUPLEN); old->fr_group[FR_GROUPLEN - 1] = '\0'; } if (fr->fr_grhead >= 0) { strncpy(old->fr_grhead, fr->fr_names + fr->fr_grhead, FR_GROUPLEN); old->fr_grhead[FR_GROUPLEN - 1] = '\0'; } } static void frentry_current_to_4_1_16(current, old) void *current; frentry_4_1_16_t *old; { frentry_t *fr = (frentry_t *)current; old->fr_lock = fr->fr_lock; old->fr_next = fr->fr_next; old->fr_grp = (void *)fr->fr_grp; old->fr_isc = fr->fr_isc; old->fr_ifas[0] = fr->fr_ifas[0]; old->fr_ifas[1] = fr->fr_ifas[1]; old->fr_ifas[2] = fr->fr_ifas[2]; old->fr_ifas[3] = fr->fr_ifas[3]; old->fr_ptr = fr->fr_ptr; old->fr_comment = NULL; old->fr_ref = fr->fr_ref; old->fr_statecnt = fr->fr_statecnt; old->fr_hits = fr->fr_hits; old->fr_bytes = fr->fr_bytes; old->fr_lastpkt.tv_sec = fr->fr_lastpkt.tv_sec; old->fr_lastpkt.tv_usec = fr->fr_lastpkt.tv_usec; old->fr_curpps = fr->fr_curpps; old->fr_dun.fru_data = fr->fr_dun.fru_data; old->fr_func = fr->fr_func; old->fr_dsize = fr->fr_dsize; old->fr_pps = fr->fr_pps; old->fr_statemax = fr->fr_statemax; old->fr_flineno = fr->fr_flineno; old->fr_type = fr->fr_type; old->fr_flags = fr_frflags5to4(fr->fr_flags); old->fr_logtag = fr->fr_logtag; old->fr_collect = fr->fr_collect; old->fr_arg = fr->fr_arg; old->fr_loglevel = fr->fr_loglevel; old->fr_age[0] = fr->fr_age[0]; old->fr_age[1] = fr->fr_age[1]; if (old->fr_v == 4) fr->fr_family = AF_INET; if (old->fr_v == 6) fr->fr_family = AF_INET6; old->fr_icode = fr->fr_icode; old->fr_cksum = fr->fr_cksum; old->fr_tifs[0].ofd_ip6 = fr->fr_tifs[0].fd_ip6; old->fr_tifs[1].ofd_ip6 = fr->fr_tifs[0].fd_ip6; old->fr_dif.ofd_ip6 = fr->fr_dif.fd_ip6; if (fr->fr_ifnames[0] >= 0) { strncpy(old->fr_ifnames[0], fr->fr_names + fr->fr_ifnames[0], LIFNAMSIZ); old->fr_ifnames[0][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[1] >= 0) { strncpy(old->fr_ifnames[1], fr->fr_names + fr->fr_ifnames[1], LIFNAMSIZ); old->fr_ifnames[1][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[2] >= 0) { strncpy(old->fr_ifnames[2], fr->fr_names + fr->fr_ifnames[2], LIFNAMSIZ); old->fr_ifnames[2][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[3] >= 0) { strncpy(old->fr_ifnames[3], fr->fr_names + fr->fr_ifnames[3], LIFNAMSIZ); old->fr_ifnames[3][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_tifs[0].fd_name >= 0) { strncpy(old->fr_tifs[0].fd_ifname, fr->fr_names + fr->fr_tifs[0].fd_name, LIFNAMSIZ); old->fr_tifs[0].fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_tifs[1].fd_name >= 0) { strncpy(old->fr_tifs[1].fd_ifname, fr->fr_names + fr->fr_tifs[1].fd_name, LIFNAMSIZ); old->fr_tifs[1].fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_dif.fd_name >= 0) { strncpy(old->fr_dif.fd_ifname, fr->fr_names + fr->fr_dif.fd_name, LIFNAMSIZ); old->fr_dif.fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_group >= 0) { strncpy(old->fr_group, fr->fr_names + fr->fr_group, FR_GROUPLEN); old->fr_group[FR_GROUPLEN - 1] = '\0'; } if (fr->fr_grhead >= 0) { strncpy(old->fr_grhead, fr->fr_names + fr->fr_grhead, FR_GROUPLEN); old->fr_grhead[FR_GROUPLEN - 1] = '\0'; } } static void frentry_current_to_4_1_0(current, old) void *current; frentry_4_1_0_t *old; { frentry_t *fr = (frentry_t *)current; old->fr_lock = fr->fr_lock; old->fr_next = fr->fr_next; old->fr_grp = (void *)fr->fr_grp; old->fr_isc = fr->fr_isc; old->fr_ifas[0] = fr->fr_ifas[0]; old->fr_ifas[1] = fr->fr_ifas[1]; old->fr_ifas[2] = fr->fr_ifas[2]; old->fr_ifas[3] = fr->fr_ifas[3]; old->fr_ptr = fr->fr_ptr; old->fr_comment = NULL; old->fr_ref = fr->fr_ref; old->fr_statecnt = fr->fr_statecnt; old->fr_hits = fr->fr_hits; old->fr_bytes = fr->fr_bytes; old->fr_lastpkt.tv_sec = fr->fr_lastpkt.tv_sec; old->fr_lastpkt.tv_usec = fr->fr_lastpkt.tv_usec; old->fr_curpps = fr->fr_curpps; old->fr_dun.fru_data = fr->fr_dun.fru_data; old->fr_func = fr->fr_func; old->fr_dsize = fr->fr_dsize; old->fr_pps = fr->fr_pps; old->fr_statemax = fr->fr_statemax; old->fr_flineno = fr->fr_flineno; old->fr_type = fr->fr_type; old->fr_flags = fr_frflags5to4(fr->fr_flags); old->fr_logtag = fr->fr_logtag; old->fr_collect = fr->fr_collect; old->fr_arg = fr->fr_arg; old->fr_loglevel = fr->fr_loglevel; old->fr_age[0] = fr->fr_age[0]; old->fr_age[1] = fr->fr_age[1]; if (old->fr_v == 4) fr->fr_family = AF_INET; if (old->fr_v == 6) fr->fr_family = AF_INET6; old->fr_icode = fr->fr_icode; old->fr_cksum = fr->fr_cksum; old->fr_tifs[0].ofd_ip6 = fr->fr_tifs[0].fd_ip6; old->fr_tifs[1].ofd_ip6 = fr->fr_tifs[0].fd_ip6; old->fr_dif.ofd_ip6 = fr->fr_dif.fd_ip6; if (fr->fr_ifnames[0] >= 0) { strncpy(old->fr_ifnames[0], fr->fr_names + fr->fr_ifnames[0], LIFNAMSIZ); old->fr_ifnames[0][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[1] >= 0) { strncpy(old->fr_ifnames[1], fr->fr_names + fr->fr_ifnames[1], LIFNAMSIZ); old->fr_ifnames[1][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[2] >= 0) { strncpy(old->fr_ifnames[2], fr->fr_names + fr->fr_ifnames[2], LIFNAMSIZ); old->fr_ifnames[2][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_ifnames[3] >= 0) { strncpy(old->fr_ifnames[3], fr->fr_names + fr->fr_ifnames[3], LIFNAMSIZ); old->fr_ifnames[3][LIFNAMSIZ - 1] = '\0'; } if (fr->fr_tifs[0].fd_name >= 0) { strncpy(old->fr_tifs[0].fd_ifname, fr->fr_names + fr->fr_tifs[0].fd_name, LIFNAMSIZ); old->fr_tifs[0].fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_tifs[1].fd_name >= 0) { strncpy(old->fr_tifs[1].fd_ifname, fr->fr_names + fr->fr_tifs[1].fd_name, LIFNAMSIZ); old->fr_tifs[1].fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_dif.fd_name >= 0) { strncpy(old->fr_dif.fd_ifname, fr->fr_names + fr->fr_dif.fd_name, LIFNAMSIZ); old->fr_dif.fd_ifname[LIFNAMSIZ - 1] = '\0'; } if (fr->fr_group >= 0) { strncpy(old->fr_group, fr->fr_names + fr->fr_group, FR_GROUPLEN); old->fr_group[FR_GROUPLEN - 1] = '\0'; } if (fr->fr_grhead >= 0) { strncpy(old->fr_grhead, fr->fr_names + fr->fr_grhead, FR_GROUPLEN); old->fr_grhead[FR_GROUPLEN - 1] = '\0'; } } static void fr_info_current_to_4_1_24(current, old) void *current; fr_info_4_1_24_t *old; { fr_info_t *fin = (fr_info_t *)current; old->fin_ifp = fin->fin_ifp; ipf_v5iptov4(&fin->fin_fi, &old->fin_fi); bcopy(&fin->fin_dat, &old->fin_dat, sizeof(fin->fin_dat)); old->fin_out = fin->fin_out; old->fin_rev = fin->fin_rev; old->fin_hlen = fin->fin_hlen; old->ofin_tcpf = fin->fin_tcpf; old->fin_icode = fin->fin_icode; old->fin_rule = fin->fin_rule; bcopy(fin->fin_group, old->fin_group, sizeof(fin->fin_group)); old->fin_fr = fin->fin_fr; old->fin_dp = fin->fin_dp; old->fin_dlen = fin->fin_dlen; old->fin_plen = fin->fin_plen; old->fin_ipoff = fin->fin_ipoff; old->fin_id = fin->fin_id; old->fin_off = fin->fin_off; old->fin_depth = fin->fin_depth; old->fin_error = fin->fin_error; old->fin_cksum = fin->fin_cksum; old->fin_state = NULL; old->fin_nat = NULL; old->fin_nattag = fin->fin_nattag; old->fin_exthdr = NULL; old->ofin_ip = fin->fin_ip; old->fin_mp = fin->fin_mp; old->fin_m = fin->fin_m; #ifdef MENTAT old->fin_qfm = fin->fin_qfm; old->fin_qpi = fin->fin_qpi; old->fin_ifname[0] = '\0'; #endif } static void fr_info_current_to_4_1_23(current, old) void *current; fr_info_4_1_23_t *old; { fr_info_t *fin = (fr_info_t *)current; old->fin_ifp = fin->fin_ifp; ipf_v5iptov4(&fin->fin_fi, &old->fin_fi); bcopy(&fin->fin_dat, &old->fin_dat, sizeof(fin->fin_dat)); old->fin_out = fin->fin_out; old->fin_rev = fin->fin_rev; old->fin_hlen = fin->fin_hlen; old->ofin_tcpf = fin->fin_tcpf; old->fin_icode = fin->fin_icode; old->fin_rule = fin->fin_rule; bcopy(fin->fin_group, old->fin_group, sizeof(fin->fin_group)); old->fin_fr = fin->fin_fr; old->fin_dp = fin->fin_dp; old->fin_dlen = fin->fin_dlen; old->fin_plen = fin->fin_plen; old->fin_ipoff = fin->fin_ipoff; old->fin_id = fin->fin_id; old->fin_off = fin->fin_off; old->fin_depth = fin->fin_depth; old->fin_error = fin->fin_error; old->fin_state = NULL; old->fin_nat = NULL; old->fin_nattag = fin->fin_nattag; old->ofin_ip = fin->fin_ip; old->fin_mp = fin->fin_mp; old->fin_m = fin->fin_m; #ifdef MENTAT old->fin_qfm = fin->fin_qfm; old->fin_qpi = fin->fin_qpi; old->fin_ifname[0] = '\0'; #endif } static void fr_info_current_to_4_1_11(current, old) void *current; fr_info_4_1_11_t *old; { fr_info_t *fin = (fr_info_t *)current; old->fin_ifp = fin->fin_ifp; ipf_v5iptov4(&fin->fin_fi, &old->fin_fi); bcopy(&fin->fin_dat, &old->fin_dat, sizeof(fin->fin_dat)); old->fin_out = fin->fin_out; old->fin_rev = fin->fin_rev; old->fin_hlen = fin->fin_hlen; old->ofin_tcpf = fin->fin_tcpf; old->fin_icode = fin->fin_icode; old->fin_rule = fin->fin_rule; bcopy(fin->fin_group, old->fin_group, sizeof(fin->fin_group)); old->fin_fr = fin->fin_fr; old->fin_dp = fin->fin_dp; old->fin_dlen = fin->fin_dlen; old->fin_plen = fin->fin_plen; old->fin_ipoff = fin->fin_ipoff; old->fin_id = fin->fin_id; old->fin_off = fin->fin_off; old->fin_depth = fin->fin_depth; old->fin_error = fin->fin_error; old->fin_state = NULL; old->fin_nat = NULL; old->fin_nattag = fin->fin_nattag; old->ofin_ip = fin->fin_ip; old->fin_mp = fin->fin_mp; old->fin_m = fin->fin_m; #ifdef MENTAT old->fin_qfm = fin->fin_qfm; old->fin_qpi = fin->fin_qpi; old->fin_ifname[0] = '\0'; #endif } static void frauth_current_to_4_1_29(current, old) void *current; frauth_4_1_29_t *old; { frauth_t *fra = (frauth_t *)current; old->fra_age = fra->fra_age; old->fra_len = fra->fra_len; old->fra_index = fra->fra_index; old->fra_pass = fra->fra_pass; fr_info_current_to_4_1_24(&fra->fra_info, &old->fra_info); old->fra_buf = fra->fra_buf; old->fra_flx = fra->fra_flx; #ifdef MENTAT old->fra_q = fra->fra_q; old->fra_m = fra->fra_m; #endif } static void frauth_current_to_4_1_24(current, old) void *current; frauth_4_1_24_t *old; { frauth_t *fra = (frauth_t *)current; old->fra_age = fra->fra_age; old->fra_len = fra->fra_len; old->fra_index = fra->fra_index; old->fra_pass = fra->fra_pass; fr_info_current_to_4_1_24(&fra->fra_info, &old->fra_info); old->fra_buf = fra->fra_buf; #ifdef MENTAT old->fra_q = fra->fra_q; old->fra_m = fra->fra_m; #endif } static void frauth_current_to_4_1_23(current, old) void *current; frauth_4_1_23_t *old; { frauth_t *fra = (frauth_t *)current; old->fra_age = fra->fra_age; old->fra_len = fra->fra_len; old->fra_index = fra->fra_index; old->fra_pass = fra->fra_pass; fr_info_current_to_4_1_23(&fra->fra_info, &old->fra_info); old->fra_buf = fra->fra_buf; #ifdef MENTAT old->fra_q = fra->fra_q; old->fra_m = fra->fra_m; #endif } static void frauth_current_to_4_1_11(current, old) void *current; frauth_4_1_11_t *old; { frauth_t *fra = (frauth_t *)current; old->fra_age = fra->fra_age; old->fra_len = fra->fra_len; old->fra_index = fra->fra_index; old->fra_pass = fra->fra_pass; fr_info_current_to_4_1_11(&fra->fra_info, &old->fra_info); old->fra_buf = fra->fra_buf; #ifdef MENTAT old->fra_q = fra->fra_q; old->fra_m = fra->fra_m; #endif } static void ipnat_current_to_4_1_14(current, old) void *current; ipnat_4_1_14_t *old; { ipnat_t *np = (ipnat_t *)current; old->in_next = np->in_next; old->in_rnext = np->in_rnext; old->in_prnext = np->in_prnext; old->in_mnext = np->in_mnext; old->in_pmnext = np->in_pmnext; old->in_tqehead[0] = np->in_tqehead[0]; old->in_tqehead[1] = np->in_tqehead[1]; old->in_ifps[0] = np->in_ifps[0]; old->in_ifps[1] = np->in_ifps[1]; old->in_apr = np->in_apr; old->in_comment = np->in_comment; old->in_space = np->in_space; old->in_hits = np->in_hits; old->in_use = np->in_use; old->in_hv = np->in_hv[0]; old->in_flineno = np->in_flineno; if (old->in_redir == NAT_REDIRECT) old->in_pnext = np->in_dpnext; else old->in_pnext = np->in_spnext; old->in_v = np->in_v[0]; old->in_flags = np->in_flags; old->in_mssclamp = np->in_mssclamp; old->in_age[0] = np->in_age[0]; old->in_age[1] = np->in_age[1]; old->in_redir = np->in_redir; old->in_p = np->in_pr[0]; if (np->in_redir == NAT_REDIRECT) { old->in_next6 = np->in_ndst.na_nextaddr; old->in_in[0] = np->in_ndst.na_addr[0]; old->in_in[1] = np->in_ndst.na_addr[1]; old->in_out[0] = np->in_odst.na_addr[0]; old->in_out[1] = np->in_odst.na_addr[1]; old->in_src[0] = np->in_osrc.na_addr[0]; old->in_src[1] = np->in_osrc.na_addr[1]; } else { old->in_next6 = np->in_nsrc.na_nextaddr; old->in_out[0] = np->in_nsrc.na_addr[0]; old->in_out[1] = np->in_nsrc.na_addr[1]; old->in_in[0] = np->in_osrc.na_addr[0]; old->in_in[1] = np->in_osrc.na_addr[1]; old->in_src[0] = np->in_odst.na_addr[0]; old->in_src[1] = np->in_odst.na_addr[1]; } ipfv5tuctov4(&np->in_tuc, &old->in_tuc); if (np->in_redir == NAT_REDIRECT) { old->in_port[0] = np->in_dpmin; old->in_port[1] = np->in_dpmax; } else { old->in_port[0] = np->in_spmin; old->in_port[1] = np->in_spmax; } old->in_ppip = np->in_ppip; old->in_ippip = np->in_ippip; bcopy(&np->in_tag, &old->in_tag, sizeof(np->in_tag)); if (np->in_ifnames[0] >= 0) { strncpy(old->in_ifnames[0], np->in_names + np->in_ifnames[0], LIFNAMSIZ); old->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; } if (np->in_ifnames[1] >= 0) { strncpy(old->in_ifnames[1], np->in_names + np->in_ifnames[1], LIFNAMSIZ); old->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; } if (np->in_plabel >= 0) { strncpy(old->in_plabel, np->in_names + np->in_plabel, APR_LABELLEN); old->in_plabel[APR_LABELLEN - 1] = '\0'; } } static void ipnat_current_to_4_1_0(current, old) void *current; ipnat_4_1_0_t *old; { ipnat_t *np = (ipnat_t *)current; old->in_next = np->in_next; old->in_rnext = np->in_rnext; old->in_prnext = np->in_prnext; old->in_mnext = np->in_mnext; old->in_pmnext = np->in_pmnext; old->in_tqehead[0] = np->in_tqehead[0]; old->in_tqehead[1] = np->in_tqehead[1]; old->in_ifps[0] = np->in_ifps[0]; old->in_ifps[1] = np->in_ifps[1]; old->in_apr = np->in_apr; old->in_comment = np->in_comment; old->in_space = np->in_space; old->in_hits = np->in_hits; old->in_use = np->in_use; old->in_hv = np->in_hv[0]; old->in_flineno = np->in_flineno; if (old->in_redir == NAT_REDIRECT) old->in_pnext = np->in_dpnext; else old->in_pnext = np->in_spnext; old->in_v = np->in_v[0]; old->in_flags = np->in_flags; old->in_mssclamp = np->in_mssclamp; old->in_age[0] = np->in_age[0]; old->in_age[1] = np->in_age[1]; old->in_redir = np->in_redir; old->in_p = np->in_pr[0]; if (np->in_redir == NAT_REDIRECT) { old->in_next6 = np->in_ndst.na_nextaddr; old->in_in[0] = np->in_ndst.na_addr[0]; old->in_in[1] = np->in_ndst.na_addr[1]; old->in_out[0] = np->in_odst.na_addr[0]; old->in_out[1] = np->in_odst.na_addr[1]; old->in_src[0] = np->in_osrc.na_addr[0]; old->in_src[1] = np->in_osrc.na_addr[1]; } else { old->in_next6 = np->in_nsrc.na_nextaddr; old->in_out[0] = np->in_nsrc.na_addr[0]; old->in_out[1] = np->in_nsrc.na_addr[1]; old->in_in[0] = np->in_osrc.na_addr[0]; old->in_in[1] = np->in_osrc.na_addr[1]; old->in_src[0] = np->in_odst.na_addr[0]; old->in_src[1] = np->in_odst.na_addr[1]; } ipfv5tuctov4(&np->in_tuc, &old->in_tuc); if (np->in_redir == NAT_REDIRECT) { old->in_port[0] = np->in_dpmin; old->in_port[1] = np->in_dpmax; } else { old->in_port[0] = np->in_spmin; old->in_port[1] = np->in_spmax; } old->in_ppip = np->in_ppip; old->in_ippip = np->in_ippip; bcopy(&np->in_tag, &old->in_tag, sizeof(np->in_tag)); if (np->in_ifnames[0] >= 0) { strncpy(old->in_ifnames[0], np->in_names + np->in_ifnames[0], LIFNAMSIZ); old->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; } if (np->in_ifnames[1] >= 0) { strncpy(old->in_ifnames[1], np->in_names + np->in_ifnames[1], LIFNAMSIZ); old->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; } if (np->in_plabel >= 0) { strncpy(old->in_plabel, np->in_names + np->in_plabel, APR_LABELLEN); old->in_plabel[APR_LABELLEN - 1] = '\0'; } } static void ipstate_current_to_4_1_16(current, old) void *current; ipstate_4_1_16_t *old; { ipstate_t *is = (ipstate_t *)current; old->is_lock = is->is_lock; old->is_next = is->is_next; old->is_pnext = is->is_pnext; old->is_hnext = is->is_hnext; old->is_phnext = is->is_phnext; old->is_me = is->is_me; old->is_ifp[0] = is->is_ifp[0]; old->is_ifp[1] = is->is_ifp[1]; old->is_sync = is->is_sync; old->is_rule = is->is_rule; old->is_tqehead[0] = is->is_tqehead[0]; old->is_tqehead[1] = is->is_tqehead[1]; old->is_isc = is->is_isc; old->is_pkts[0] = is->is_pkts[0]; old->is_pkts[1] = is->is_pkts[1]; old->is_pkts[2] = is->is_pkts[2]; old->is_pkts[3] = is->is_pkts[3]; old->is_bytes[0] = is->is_bytes[0]; old->is_bytes[1] = is->is_bytes[1]; old->is_bytes[2] = is->is_bytes[2]; old->is_bytes[3] = is->is_bytes[3]; old->is_icmppkts[0] = is->is_icmppkts[0]; old->is_icmppkts[1] = is->is_icmppkts[1]; old->is_icmppkts[2] = is->is_icmppkts[2]; old->is_icmppkts[3] = is->is_icmppkts[3]; old->is_sti = is->is_sti; old->is_frage[0] = is->is_frage[0]; old->is_frage[1] = is->is_frage[1]; old->is_ref = is->is_ref; old->is_isninc[0] = is->is_isninc[0]; old->is_isninc[1] = is->is_isninc[1]; old->is_sumd[0] = is->is_sumd[0]; old->is_sumd[1] = is->is_sumd[1]; old->is_src = is->is_src; old->is_dst = is->is_dst; old->is_pass = is->is_pass; old->is_p = is->is_p; old->is_v = is->is_v; old->is_hv = is->is_hv; old->is_tag = is->is_tag; old->is_opt[0] = is->is_opt[0]; old->is_opt[1] = is->is_opt[1]; old->is_optmsk[0] = is->is_optmsk[0]; old->is_optmsk[1] = is->is_optmsk[1]; old->is_sec = is->is_sec; old->is_secmsk = is->is_secmsk; old->is_auth = is->is_auth; old->is_authmsk = is->is_authmsk; ipf_v5tcpinfoto4(&is->is_tcp, &old->is_tcp); old->is_flags = is->is_flags; old->is_flx[0][0] = is->is_flx[0][0]; old->is_flx[0][1] = is->is_flx[0][1]; old->is_flx[1][0] = is->is_flx[1][0]; old->is_flx[1][1] = is->is_flx[1][1]; old->is_rulen = is->is_rulen; old->is_s0[0] = is->is_s0[0]; old->is_s0[1] = is->is_s0[1]; old->is_smsk[0] = is->is_smsk[0]; old->is_smsk[1] = is->is_smsk[1]; bcopy(is->is_group, old->is_group, sizeof(is->is_group)); bcopy(is->is_sbuf, old->is_sbuf, sizeof(is->is_sbuf)); bcopy(is->is_ifname, old->is_ifname, sizeof(is->is_ifname)); } static void ipstate_current_to_4_1_0(current, old) void *current; ipstate_4_1_0_t *old; { ipstate_t *is = (ipstate_t *)current; old->is_lock = is->is_lock; old->is_next = is->is_next; old->is_pnext = is->is_pnext; old->is_hnext = is->is_hnext; old->is_phnext = is->is_phnext; old->is_me = is->is_me; old->is_ifp[0] = is->is_ifp[0]; old->is_ifp[1] = is->is_ifp[1]; old->is_sync = is->is_sync; bzero(&old->is_nat, sizeof(old->is_nat)); old->is_rule = is->is_rule; old->is_tqehead[0] = is->is_tqehead[0]; old->is_tqehead[1] = is->is_tqehead[1]; old->is_isc = is->is_isc; old->is_pkts[0] = is->is_pkts[0]; old->is_pkts[1] = is->is_pkts[1]; old->is_pkts[2] = is->is_pkts[2]; old->is_pkts[3] = is->is_pkts[3]; old->is_bytes[0] = is->is_bytes[0]; old->is_bytes[1] = is->is_bytes[1]; old->is_bytes[2] = is->is_bytes[2]; old->is_bytes[3] = is->is_bytes[3]; old->is_icmppkts[0] = is->is_icmppkts[0]; old->is_icmppkts[1] = is->is_icmppkts[1]; old->is_icmppkts[2] = is->is_icmppkts[2]; old->is_icmppkts[3] = is->is_icmppkts[3]; old->is_sti = is->is_sti; old->is_frage[0] = is->is_frage[0]; old->is_frage[1] = is->is_frage[1]; old->is_ref = is->is_ref; old->is_isninc[0] = is->is_isninc[0]; old->is_isninc[1] = is->is_isninc[1]; old->is_sumd[0] = is->is_sumd[0]; old->is_sumd[1] = is->is_sumd[1]; old->is_src = is->is_src; old->is_dst = is->is_dst; old->is_pass = is->is_pass; old->is_p = is->is_p; old->is_v = is->is_v; old->is_hv = is->is_hv; old->is_tag = is->is_tag; old->is_opt[0] = is->is_opt[0]; old->is_opt[1] = is->is_opt[1]; old->is_optmsk[0] = is->is_optmsk[0]; old->is_optmsk[1] = is->is_optmsk[1]; old->is_sec = is->is_sec; old->is_secmsk = is->is_secmsk; old->is_auth = is->is_auth; old->is_authmsk = is->is_authmsk; ipf_v5tcpinfoto4(&is->is_tcp, &old->is_tcp); old->is_flags = is->is_flags; old->is_flx[0][0] = is->is_flx[0][0]; old->is_flx[0][1] = is->is_flx[0][1]; old->is_flx[1][0] = is->is_flx[1][0]; old->is_flx[1][1] = is->is_flx[1][1]; old->is_rulen = is->is_rulen; old->is_s0[0] = is->is_s0[0]; old->is_s0[1] = is->is_s0[1]; old->is_smsk[0] = is->is_smsk[0]; old->is_smsk[1] = is->is_smsk[1]; bcopy(is->is_group, old->is_group, sizeof(is->is_group)); bcopy(is->is_sbuf, old->is_sbuf, sizeof(is->is_sbuf)); bcopy(is->is_ifname, old->is_ifname, sizeof(is->is_ifname)); } static void ips_stat_current_to_4_1_21(current, old) void *current; ips_stat_4_1_21_t *old; { ips_stat_t *st = (ips_stat_t *)current; old->iss_hits = st->iss_hits; old->iss_miss = st->iss_check_miss; old->iss_max = st->iss_max; old->iss_maxref = st->iss_max_ref; old->iss_tcp = st->iss_proto[IPPROTO_TCP]; old->iss_udp = st->iss_proto[IPPROTO_UDP]; old->iss_icmp = st->iss_proto[IPPROTO_ICMP]; old->iss_nomem = st->iss_nomem; old->iss_expire = st->iss_expire; old->iss_fin = st->iss_fin; old->iss_active = st->iss_active; old->iss_logged = st->iss_log_ok; old->iss_logfail = st->iss_log_fail; old->iss_inuse = st->iss_inuse; old->iss_wild = st->iss_wild; old->iss_ticks = st->iss_ticks; old->iss_bucketfull = st->iss_bucket_full; old->iss_statesize = st->iss_state_size; old->iss_statemax = st->iss_state_max; old->iss_table = st->iss_table; old->iss_list = st->iss_list; old->iss_bucketlen = (void *)st->iss_bucketlen; old->iss_tcptab = st->iss_tcptab; } static void ips_stat_current_to_4_1_0(current, old) void *current; ips_stat_4_1_0_t *old; { ips_stat_t *st = (ips_stat_t *)current; old->iss_hits = st->iss_hits; old->iss_miss = st->iss_check_miss; old->iss_max = st->iss_max; old->iss_maxref = st->iss_max_ref; old->iss_tcp = st->iss_proto[IPPROTO_TCP]; old->iss_udp = st->iss_proto[IPPROTO_UDP]; old->iss_icmp = st->iss_proto[IPPROTO_ICMP]; old->iss_nomem = st->iss_nomem; old->iss_expire = st->iss_expire; old->iss_fin = st->iss_fin; old->iss_active = st->iss_active; old->iss_logged = st->iss_log_ok; old->iss_logfail = st->iss_log_fail; old->iss_inuse = st->iss_inuse; old->iss_wild = st->iss_wild; old->iss_ticks = st->iss_ticks; old->iss_bucketfull = st->iss_bucket_full; old->iss_statesize = st->iss_state_size; old->iss_statemax = st->iss_state_max; old->iss_table = st->iss_table; old->iss_list = st->iss_list; old->iss_bucketlen = (void *)st->iss_bucketlen; } static void nat_save_current_to_4_1_16(current, old) void *current; nat_save_4_1_16_t *old; { nat_save_t *nats = (nat_save_t *)current; old->ipn_next = nats->ipn_next; bcopy(&nats->ipn_nat, &old->ipn_nat, sizeof(old->ipn_nat)); bcopy(&nats->ipn_ipnat, &old->ipn_ipnat, sizeof(old->ipn_ipnat)); frentry_current_to_4_1_16(&nats->ipn_fr, &old->ipn_fr); old->ipn_dsize = nats->ipn_dsize; bcopy(nats->ipn_data, old->ipn_data, sizeof(nats->ipn_data)); } static void nat_save_current_to_4_1_14(current, old) void *current; nat_save_4_1_14_t *old; { nat_save_t *nats = (nat_save_t *)current; old->ipn_next = nats->ipn_next; bcopy(&nats->ipn_nat, &old->ipn_nat, sizeof(old->ipn_nat)); bcopy(&nats->ipn_ipnat, &old->ipn_ipnat, sizeof(old->ipn_ipnat)); frentry_current_to_4_1_0(&nats->ipn_fr, &old->ipn_fr); old->ipn_dsize = nats->ipn_dsize; bcopy(nats->ipn_data, old->ipn_data, sizeof(nats->ipn_data)); } static void nat_save_current_to_4_1_3(current, old) void *current; nat_save_4_1_3_t *old; { nat_save_t *nats = (nat_save_t *)current; old->ipn_next = nats->ipn_next; bcopy(&nats->ipn_nat, &old->ipn_nat, sizeof(old->ipn_nat)); bcopy(&nats->ipn_ipnat, &old->ipn_ipnat, sizeof(old->ipn_ipnat)); frentry_current_to_4_1_0(&nats->ipn_fr, &old->ipn_fr); old->ipn_dsize = nats->ipn_dsize; bcopy(nats->ipn_data, old->ipn_data, sizeof(nats->ipn_data)); } static void nat_current_to_4_1_25(current, old) void *current; nat_4_1_25_t *old; { nat_t *nat = (nat_t *)current; old->nat_lock = nat->nat_lock; old->nat_next = (void *)nat->nat_next; old->nat_pnext = (void *)nat->nat_pnext; old->nat_hnext[0] = (void *)nat->nat_hnext[0]; old->nat_hnext[1] = (void *)nat->nat_hnext[1]; old->nat_phnext[0] = (void *)nat->nat_phnext[0]; old->nat_phnext[1] = (void *)nat->nat_phnext[1]; old->nat_hm = nat->nat_hm; old->nat_data = nat->nat_data; old->nat_me = (void *)nat->nat_me; old->nat_state = nat->nat_state; old->nat_aps = nat->nat_aps; old->nat_fr = nat->nat_fr; old->nat_ptr = (void *)nat->nat_ptr; old->nat_ifps[0] = nat->nat_ifps[0]; old->nat_ifps[1] = nat->nat_ifps[1]; old->nat_sync = nat->nat_sync; old->nat_tqe = nat->nat_tqe; old->nat_flags = nat->nat_flags; old->nat_sumd[0] = nat->nat_sumd[0]; old->nat_sumd[1] = nat->nat_sumd[1]; old->nat_ipsumd = nat->nat_ipsumd; old->nat_mssclamp = nat->nat_mssclamp; old->nat_pkts[0] = nat->nat_pkts[0]; old->nat_pkts[1] = nat->nat_pkts[1]; old->nat_bytes[0] = nat->nat_bytes[0]; old->nat_bytes[1] = nat->nat_bytes[1]; old->nat_ref = nat->nat_ref; old->nat_dir = nat->nat_dir; old->nat_p = nat->nat_pr[0]; old->nat_use = nat->nat_use; old->nat_hv[0] = nat->nat_hv[0]; old->nat_hv[1] = nat->nat_hv[1]; old->nat_rev = nat->nat_rev; old->nat_redir = nat->nat_redir; bcopy(nat->nat_ifnames[0], old->nat_ifnames[0], LIFNAMSIZ); bcopy(nat->nat_ifnames[1], old->nat_ifnames[1], LIFNAMSIZ); if (nat->nat_redir == NAT_REDIRECT) { old->nat_inip6 = nat->nat_ndst6; old->nat_outip6 = nat->nat_odst6; old->nat_oip6 = nat->nat_osrc6; old->nat_un.nat_unt.ts_sport = nat->nat_ndport; old->nat_un.nat_unt.ts_dport = nat->nat_odport; } else { old->nat_inip6 = nat->nat_osrc6; old->nat_outip6 = nat->nat_nsrc6; old->nat_oip6 = nat->nat_odst6; old->nat_un.nat_unt.ts_sport = nat->nat_osport; old->nat_un.nat_unt.ts_dport = nat->nat_nsport; } } static void nat_current_to_4_1_14(current, old) void *current; nat_4_1_14_t *old; { nat_t *nat = (nat_t *)current; old->nat_lock = nat->nat_lock; old->nat_next = nat->nat_next; old->nat_pnext = NULL; old->nat_hnext[0] = NULL; old->nat_hnext[1] = NULL; old->nat_phnext[0] = NULL; old->nat_phnext[1] = NULL; old->nat_hm = nat->nat_hm; old->nat_data = nat->nat_data; old->nat_me = (void *)nat->nat_me; old->nat_state = nat->nat_state; old->nat_aps = nat->nat_aps; old->nat_fr = nat->nat_fr; old->nat_ptr = nat->nat_ptr; old->nat_ifps[0] = nat->nat_ifps[0]; old->nat_ifps[1] = nat->nat_ifps[1]; old->nat_sync = nat->nat_sync; old->nat_tqe = nat->nat_tqe; old->nat_flags = nat->nat_flags; old->nat_sumd[0] = nat->nat_sumd[0]; old->nat_sumd[1] = nat->nat_sumd[1]; old->nat_ipsumd = nat->nat_ipsumd; old->nat_mssclamp = nat->nat_mssclamp; old->nat_pkts[0] = nat->nat_pkts[0]; old->nat_pkts[1] = nat->nat_pkts[1]; old->nat_bytes[0] = nat->nat_bytes[0]; old->nat_bytes[1] = nat->nat_bytes[1]; old->nat_ref = nat->nat_ref; old->nat_dir = nat->nat_dir; old->nat_p = nat->nat_pr[0]; old->nat_use = nat->nat_use; old->nat_hv[0] = nat->nat_hv[0]; old->nat_hv[1] = nat->nat_hv[1]; old->nat_rev = nat->nat_rev; bcopy(nat->nat_ifnames[0], old->nat_ifnames[0], LIFNAMSIZ); bcopy(nat->nat_ifnames[1], old->nat_ifnames[1], LIFNAMSIZ); if (nat->nat_redir == NAT_REDIRECT) { old->nat_inip6 = nat->nat_ndst6; old->nat_outip6 = nat->nat_odst6; old->nat_oip6 = nat->nat_osrc6; old->nat_un.nat_unt.ts_sport = nat->nat_ndport; old->nat_un.nat_unt.ts_dport = nat->nat_odport; } else { old->nat_inip6 = nat->nat_osrc6; old->nat_outip6 = nat->nat_nsrc6; old->nat_oip6 = nat->nat_odst6; old->nat_un.nat_unt.ts_sport = nat->nat_osport; old->nat_un.nat_unt.ts_dport = nat->nat_nsport; } } static void nat_current_to_4_1_3(current, old) void *current; nat_4_1_3_t *old; { nat_t *nat = (nat_t *)current; old->nat_lock = nat->nat_lock; old->nat_next = nat->nat_next; old->nat_pnext = NULL; old->nat_hnext[0] = NULL; old->nat_hnext[1] = NULL; old->nat_phnext[0] = NULL; old->nat_phnext[1] = NULL; old->nat_hm = nat->nat_hm; old->nat_data = nat->nat_data; old->nat_me = (void *)nat->nat_me; old->nat_state = nat->nat_state; old->nat_aps = nat->nat_aps; old->nat_fr = nat->nat_fr; old->nat_ptr = nat->nat_ptr; old->nat_ifps[0] = nat->nat_ifps[0]; old->nat_ifps[1] = nat->nat_ifps[1]; old->nat_sync = nat->nat_sync; old->nat_tqe = nat->nat_tqe; old->nat_flags = nat->nat_flags; old->nat_sumd[0] = nat->nat_sumd[0]; old->nat_sumd[1] = nat->nat_sumd[1]; old->nat_ipsumd = nat->nat_ipsumd; old->nat_mssclamp = nat->nat_mssclamp; old->nat_pkts[0] = nat->nat_pkts[0]; old->nat_pkts[1] = nat->nat_pkts[1]; old->nat_bytes[0] = nat->nat_bytes[0]; old->nat_bytes[1] = nat->nat_bytes[1]; old->nat_ref = nat->nat_ref; old->nat_dir = nat->nat_dir; old->nat_p = nat->nat_pr[0]; old->nat_use = nat->nat_use; old->nat_hv[0] = nat->nat_hv[0]; old->nat_hv[1] = nat->nat_hv[1]; old->nat_rev = nat->nat_rev; bcopy(nat->nat_ifnames[0], old->nat_ifnames[0], LIFNAMSIZ); bcopy(nat->nat_ifnames[1], old->nat_ifnames[1], LIFNAMSIZ); if (nat->nat_redir == NAT_REDIRECT) { old->nat_inip6 = nat->nat_ndst6; old->nat_outip6 = nat->nat_odst6; old->nat_oip6 = nat->nat_osrc6; old->nat_un.nat_unt.ts_sport = nat->nat_ndport; old->nat_un.nat_unt.ts_dport = nat->nat_odport; } else { old->nat_inip6 = nat->nat_osrc6; old->nat_outip6 = nat->nat_nsrc6; old->nat_oip6 = nat->nat_odst6; old->nat_un.nat_unt.ts_sport = nat->nat_osport; old->nat_un.nat_unt.ts_dport = nat->nat_nsport; } } #endif /* IPFILTER_COMPAT */ diff --git a/contrib/ipfilter/ip_msnrpc_pxy.c b/contrib/ipfilter/ip_msnrpc_pxy.c index 40bc084cbb5d..9cddd398edde 100644 --- a/contrib/ipfilter/ip_msnrpc_pxy.c +++ b/contrib/ipfilter/ip_msnrpc_pxy.c @@ -1,328 +1,328 @@ /* $FreeBSD$ */ /* * Copyright (C) 2000-2003 by Darren Reed * * See the IPFILTER.LICENCE file for details on licencing. * * Simple DCE transparent proxy for MSN RPC. * * ******* NOTE: THIS PROXY DOES NOT DO ADDRESS TRANSLATION ******** * * Id: ip_msnrpc_pxy.c,v 2.17.2.1 2005/02/04 10:22:55 darrenr Exp */ #define IPF_MSNRPC_PROXY #define IPF_MINMSNRPCLEN 24 #define IPF_MSNRPCSKIP (2 + 19 + 2 + 2 + 2 + 19 + 2 + 2) typedef struct msnrpchdr { u_char mrh_major; /* major # == 5 */ u_char mrh_minor; /* minor # == 0 */ u_char mrh_type; u_char mrh_flags; u_32_t mrh_endian; u_short mrh_dlen; /* data size */ u_short mrh_alen; /* authentication length */ u_32_t mrh_cid; /* call identifier */ u_32_t mrh_hint; /* allocation hint */ u_short mrh_ctxt; /* presentation context hint */ u_char mrh_ccnt; /* cancel count */ u_char mrh_ans; } msnrpchdr_t; -int ippr_msnrpc_init __P((void)); -void ippr_msnrpc_fini __P((void)); -int ippr_msnrpc_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_msnrpc_out __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_msnrpc_in __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_msnrpc_check __P((ip_t *, msnrpchdr_t *)); +int ippr_msnrpc_init(void); +void ippr_msnrpc_fini(void); +int ippr_msnrpc_new(fr_info_t *, ap_session_t *, nat_t *); +int ippr_msnrpc_out(fr_info_t *, ap_session_t *, nat_t *); +int ippr_msnrpc_in(fr_info_t *, ap_session_t *, nat_t *); +int ippr_msnrpc_check(ip_t *, msnrpchdr_t *); static frentry_t msnfr; int msn_proxy_init = 0; /* * Initialize local structures. */ int ippr_msnrpc_init() { bzero((char *)&msnfr, sizeof(msnfr)); msnfr.fr_ref = 1; msnfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&msnfr.fr_lock, "MSN RPC proxy rule lock"); msn_proxy_init = 1; return 0; } void ippr_msnrpc_fini() { if (msn_proxy_init == 1) { MUTEX_DESTROY(&msnfr.fr_lock); msn_proxy_init = 0; } } int ippr_msnrpc_new(fin, aps, nat) fr_info_t *fin; ap_session_t *aps; nat_t *nat; { msnrpcinfo_t *mri; KMALLOC(mri, msnrpcinfo_t *); if (mri == NULL) return -1; aps->aps_data = mri; aps->aps_psiz = sizeof(msnrpcinfo_t); bzero((char *)mri, sizeof(*mri)); mri->mri_cmd[0] = 0xff; mri->mri_cmd[1] = 0xff; return 0; } int ippr_msnrpc_check(ip, mrh) ip_t *ip; msnrpchdr_t *mrh; { if (mrh->mrh_major != 5) return -1; if (mrh->mrh_minor != 0) return -1; if (mrh->mrh_alen != 0) return -1; if (mrh->mrh_endian == 0x10) { /* Both gateway and packet match endian */ if (mrh->mrh_dlen > ip->ip_len) return -1; if (mrh->mrh_type == 0 || mrh->mrh_type == 2) if (mrh->mrh_hint > ip->ip_len) return -1; } else if (mrh->mrh_endian == 0x10000000) { /* XXX - Endian mismatch - should be swapping! */ return -1; } else { return -1; } return 0; } int ippr_msnrpc_out(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { msnrpcinfo_t *mri; msnrpchdr_t *mrh; tcphdr_t *tcp; int dlen; mri = aps->aps_data; if (mri == NULL) return 0; tcp = (tcphdr_t *)fin->fin_dp; dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); if (dlen < IPF_MINMSNRPCLEN) return 0; mrh = (msnrpchdr_t *)((char *)tcp + (TCP_OFF(tcp) << 2)); if (ippr_msnrpc_check(ip, mrh)) return 0; mri->mri_valid++; switch (mrh->mrh_type) { case 0x0b : /* BIND */ case 0x00 : /* REQUEST */ break; case 0x0c : /* BIND ACK */ case 0x02 : /* RESPONSE */ default: return 0; } mri->mri_cmd[1] = mrh->mrh_type; return 0; } int ippr_msnrpc_in(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { tcphdr_t *tcp, tcph, *tcp2 = &tcph; int dlen, sz, sz2, i; msnrpcinfo_t *mri; msnrpchdr_t *mrh; fr_info_t fi; u_short len; char *s; mri = aps->aps_data; if (mri == NULL) return 0; tcp = (tcphdr_t *)fin->fin_dp; dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); if (dlen < IPF_MINMSNRPCLEN) return 0; mrh = (msnrpchdr_t *)((char *)tcp + (TCP_OFF(tcp) << 2)); if (ippr_msnrpc_check(ip, mrh)) return 0; mri->mri_valid++; switch (mrh->mrh_type) { case 0x0c : /* BIND ACK */ if (mri->mri_cmd[1] != 0x0b) return 0; break; case 0x02 : /* RESPONSE */ if (mri->mri_cmd[1] != 0x00) return 0; break; case 0x0b : /* BIND */ case 0x00 : /* REQUEST */ default: return 0; } mri->mri_cmd[0] = mrh->mrh_type; dlen -= sizeof(*mrh); /* * Only processes RESPONSE's */ if (mrh->mrh_type != 0x02) return 0; /* * Skip over some bytes...what are these really ? */ if (dlen <= 44) return 0; s = (char *)(mrh + 1) + 20; dlen -= 20; bcopy(s, (char *)&len, sizeof(len)); if (len == 1) { s += 20; dlen -= 20; } else if (len == 2) { s += 24; dlen -= 24; } else return 0; if (dlen <= 10) return 0; dlen -= 10; bcopy(s, (char *)&sz, sizeof(sz)); s += sizeof(sz); bcopy(s, (char *)&sz2, sizeof(sz2)); s += sizeof(sz2); if (sz2 != sz) return 0; if (sz > dlen) return 0; if (*s++ != 5) return 0; if (*s++ != 0) return 0; sz -= IPF_MSNRPCSKIP; s += IPF_MSNRPCSKIP; dlen -= IPF_MSNRPCSKIP; do { if (sz < 7 || dlen < 7) break; bcopy(s, (char *)&len, sizeof(len)); if (dlen < len) break; if (sz < len) break; if (len != 1) break; sz -= 3; i = *(s + 2); s += 3; dlen -= 3; bcopy(s, (char *)&len, sizeof(len)); if (dlen < len) break; if (sz < len) break; s += sizeof(len); switch (i) { case 7 : if (len == 2) { bcopy(s, (char *)&mri->mri_rport, 2); mri->mri_flags |= 1; } break; case 9 : if (len == 4) { bcopy(s, (char *)&mri->mri_raddr, 4); mri->mri_flags |= 2; } break; default : break; } sz -= len; s += len; dlen -= len; } while (sz > 0); if (mri->mri_flags == 3) { int slen; bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); slen = ip->ip_len; ip->ip_len = fin->fin_hlen + sizeof(*tcp2); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); TCP_OFF_A(tcp2, 5); fi.fin_data[0] = htons(mri->mri_rport); tcp2->th_sport = mri->mri_rport; fi.fin_data[1] = 0; tcp2->th_dport = 0; fi.fin_state = NULL; fi.fin_nat = NULL; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); fi.fin_dp = (char *)tcp2; fi.fin_fi.fi_daddr = ip->ip_dst.s_addr; fi.fin_fi.fi_saddr = mri->mri_raddr.s_addr; if (!fi.fin_fr) fi.fin_fr = &msnfr; if (fr_stlookup(&fi, NULL, NULL)) { RWLOCK_EXIT(&ipf_state); } else { (void) fr_addstate(&fi, NULL, SI_W_DPORT|SI_CLONE); if (fi.fin_state != NULL) fr_statederef(&fi, (ipstate_t **)&fi.fin_state); } ip->ip_len = slen; } mri->mri_flags = 0; return 0; } diff --git a/contrib/ipfilter/ipf.h b/contrib/ipfilter/ipf.h index 834ba83f51f0..c7f7308a9ba9 100644 --- a/contrib/ipfilter/ipf.h +++ b/contrib/ipfilter/ipf.h @@ -1,377 +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 #include #include /* * This is a workaround for troubles on FreeBSD, HPUX, OpenBSD. * Needed here because on some systems gets included by things * like */ #ifndef _KERNEL # define ADD_KERNEL # define _KERNEL # define KERNEL #endif #include #ifdef ADD_KERNEL # undef _KERNEL # undef KERNEL #endif #include #include #include #include #include #include #include # include #include #include #include #include #include #include #include #include #if !defined(__SVR4) && !defined(__svr4__) && defined(sun) # include #endif #include #include #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_version) || \ SOLARIS # include -typedef int (* ioctlfunc_t) __P((int, ioctlcmd_t, ...)); +typedef int (* ioctlfunc_t)(int, ioctlcmd_t, ...); #else -typedef int (* ioctlfunc_t) __P((dev_t, ioctlcmd_t, void *)); +typedef int (* ioctlfunc_t)(dev_t, ioctlcmd_t, void *); #endif -typedef int (* addfunc_t) __P((int, ioctlfunc_t, void *)); -typedef int (* copyfunc_t) __P((void *, void *, size_t)); +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 __P((char ***, struct frentry *, int)); -extern int addipopt __P((char *, struct ipopt_names *, int, char *)); -extern int addkeep __P((char ***, struct frentry *, int)); -extern alist_t *alist_new __P((int, char *)); -extern void alist_free __P((alist_t *)); -extern void assigndefined __P((char *)); -extern void binprint __P((void *, size_t)); -extern u_32_t buildopts __P((char *, char *, int)); -extern int checkrev __P((char *)); -extern int connecttcp __P((char *, int)); -extern int count6bits __P((u_32_t *)); -extern int count4bits __P((u_32_t)); -extern char *fac_toname __P((int)); -extern int fac_findname __P((char *)); -extern const char *familyname __P((const int)); -extern void fill6bits __P((int, u_int *)); -extern wordtab_t *findword __P((wordtab_t *, char *)); -extern int ftov __P((int)); -extern char *ipf_geterror __P((int, ioctlfunc_t *)); -extern int genmask __P((int, char *, i6addr_t *)); -extern int gethost __P((int, char *, i6addr_t *)); -extern int geticmptype __P((int, char *)); -extern int getport __P((struct frentry *, char *, u_short *, char *)); -extern int getportproto __P((char *, int)); -extern int getproto __P((char *)); -extern char *getnattype __P((struct nat *)); -extern char *getsumd __P((u_32_t)); -extern u_32_t getoptbyname __P((char *)); -extern u_32_t getoptbyvalue __P((int)); -extern u_32_t getv6optbyname __P((char *)); -extern u_32_t getv6optbyvalue __P((int)); -extern char *icmptypename __P((int, int)); -extern void initparse __P((void)); -extern void ipf_dotuning __P((int, char *, ioctlfunc_t)); -extern int ipf_addrule __P((int, ioctlfunc_t, void *)); -extern void ipf_mutex_clean __P((void)); -extern int ipf_parsefile __P((int, addfunc_t, ioctlfunc_t *, char *)); -extern int ipf_parsesome __P((int, addfunc_t, ioctlfunc_t *, FILE *)); -extern void ipf_perror __P((int, char *)); -extern int ipf_perror_fd __P(( int, ioctlfunc_t, char *)); -extern void ipf_rwlock_clean __P((void)); -extern char *ipf_strerror __P((int)); -extern void ipferror __P((int, char *)); -extern int ipmon_parsefile __P((char *)); -extern int ipmon_parsesome __P((FILE *)); -extern int ipnat_addrule __P((int, ioctlfunc_t, void *)); -extern int ipnat_parsefile __P((int, addfunc_t, ioctlfunc_t, char *)); -extern int ipnat_parsesome __P((int, addfunc_t, ioctlfunc_t, FILE *)); -extern int ippool_parsefile __P((int, char *, ioctlfunc_t)); -extern int ippool_parsesome __P((int, FILE *, ioctlfunc_t)); -extern int kmemcpywrap __P((void *, void *, size_t)); -extern char *kvatoname __P((ipfunc_t, ioctlfunc_t)); -extern int load_dstlist __P((struct ippool_dst *, ioctlfunc_t, - ipf_dstnode_t *)); -extern int load_dstlistnode __P((int, char *, struct ipf_dstnode *, - ioctlfunc_t)); -extern alist_t *load_file __P((char *)); -extern int load_hash __P((struct iphtable_s *, struct iphtent_s *, - ioctlfunc_t)); -extern int load_hashnode __P((int, char *, struct iphtent_s *, int, - ioctlfunc_t)); -extern alist_t *load_http __P((char *)); -extern int load_pool __P((struct ip_pool_s *list, ioctlfunc_t)); -extern int load_poolnode __P((int, char *, ip_pool_node_t *, int, ioctlfunc_t)); -extern alist_t *load_url __P((char *)); -extern alist_t *make_range __P((int, struct in_addr, struct in_addr)); -extern void mb_hexdump __P((mb_t *, FILE *)); -extern ipfunc_t nametokva __P((char *, ioctlfunc_t)); -extern void nat_setgroupmap __P((struct ipnat *)); -extern int ntomask __P((int, int, u_32_t *)); -extern u_32_t optname __P((char ***, u_short *, int)); -extern wordtab_t *parsefields __P((wordtab_t *, char *)); -extern int *parseipfexpr __P((char *, char **)); -extern int parsewhoisline __P((char *, addrfamily_t *, addrfamily_t *)); -extern void pool_close __P((void)); -extern int pool_fd __P((void)); -extern int pool_ioctl __P((ioctlfunc_t, ioctlcmd_t, void *)); -extern int pool_open __P((void)); -extern char *portname __P((int, int)); -extern int pri_findname __P((char *)); -extern char *pri_toname __P((int)); -extern void print_toif __P((int, char *, char *, struct frdest *)); -extern void printaps __P((ap_session_t *, int, int)); -extern void printaddr __P((int, int, char *, int, u_32_t *, u_32_t *)); -extern void printbuf __P((char *, int, int)); -extern void printfieldhdr __P((wordtab_t *, wordtab_t *)); -extern void printfr __P((struct frentry *, ioctlfunc_t)); -extern struct iphtable_s *printhash __P((struct iphtable_s *, copyfunc_t, - char *, int, wordtab_t *)); -extern struct iphtable_s *printhash_live __P((iphtable_t *, int, char *, - int, wordtab_t *)); -extern ippool_dst_t *printdstl_live __P((ippool_dst_t *, int, char *, - int, wordtab_t *)); -extern void printhashdata __P((iphtable_t *, int)); -extern struct iphtent_s *printhashnode __P((struct iphtable_s *, +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 __P((int, u_32_t *)); -extern void printhostmask __P((int, u_32_t *, u_32_t *)); -extern void printip __P((int, u_32_t *)); -extern void printlog __P((struct frentry *)); -extern void printlookup __P((char *, i6addr_t *addr, i6addr_t *mask)); -extern void printmask __P((int, u_32_t *)); -extern void printnataddr __P((int, char *, nat_addr_t *, int)); -extern void printnatfield __P((nat_t *, int)); -extern void printnatside __P((char *, nat_stat_side_t *)); -extern void printpacket __P((int, mb_t *)); -extern void printpacket6 __P((int, mb_t *)); -extern struct ippool_dst *printdstlist __P((struct ippool_dst *, copyfunc_t, + 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 __P((ippool_dst_t *, int)); -extern ipf_dstnode_t *printdstlistnode __P((ipf_dstnode_t *, copyfunc_t, - int, wordtab_t *)); -extern void printdstlistpolicy __P((ippool_policy_t)); -extern struct ip_pool_s *printpool __P((struct ip_pool_s *, copyfunc_t, - char *, int, wordtab_t *)); -extern struct ip_pool_s *printpool_live __P((struct ip_pool_s *, int, - char *, int, wordtab_t *)); -extern void printpooldata __P((ip_pool_t *, int)); -extern void printpoolfield __P((void *, int, int)); -extern struct ip_pool_node *printpoolnode __P((struct ip_pool_node *, - int, wordtab_t *)); -extern void printproto __P((struct protoent *, int, struct ipnat *)); -extern void printportcmp __P((int, struct frpcmp *)); -extern void printstatefield __P((ipstate_t *, int)); -extern void printtqtable __P((ipftq_t *)); -extern void printtunable __P((ipftune_t *)); -extern void printunit __P((int)); -extern void optprint __P((u_short *, u_long, u_long)); + 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 __P((u_short *, u_long, u_long)); +extern void optprintv6(u_short *, u_long, u_long); #endif -extern int remove_hash __P((struct iphtable_s *, ioctlfunc_t)); -extern int remove_hashnode __P((int, char *, struct iphtent_s *, ioctlfunc_t)); -extern int remove_pool __P((ip_pool_t *, ioctlfunc_t)); -extern int remove_poolnode __P((int, char *, ip_pool_node_t *, ioctlfunc_t)); -extern u_char tcpflags __P((char *)); -extern void printc __P((struct frentry *)); -extern void printC __P((int)); -extern void emit __P((int, int, void *, struct frentry *)); -extern u_char secbit __P((int)); -extern u_char seclevel __P((char *)); -extern void printfraginfo __P((char *, struct ipfr *)); -extern void printifname __P((char *, char *, void *)); -extern char *hostname __P((int, void *)); -extern struct ipstate *printstate __P((struct ipstate *, int, u_long)); -extern void printsbuf __P((char *)); -extern void printnat __P((struct ipnat *, int)); -extern void printactiveaddress __P((int, char *, i6addr_t *, char *)); -extern void printactivenat __P((struct nat *, int, u_long)); -extern void printhostmap __P((struct hostmap *, u_int)); -extern void printtcpflags __P((u_32_t, u_32_t)); -extern void printipfexpr __P((int *)); -extern void printstatefield __P((ipstate_t *, int)); -extern void printstatefieldhdr __P((int)); -extern int sendtrap_v1_0 __P((int, char *, char *, int, time_t)); -extern int sendtrap_v2_0 __P((int, char *, char *, int)); -extern int vtof __P((int)); - -extern void set_variable __P((char *, char *)); -extern char *get_variable __P((char *, char **, int)); -extern void resetlexer __P((void)); - -extern void debug __P((int, char *, ...)); -extern void verbose __P((int, char *, ...)); -extern void ipfkdebug __P((char *, ...)); -extern void ipfkverbose __P((char *, ...)); +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 __P((char *, int )); -extern void sync __P((void)); +extern int gethostname(char *, int ); +extern void sync(void); #endif #endif /* __IPF_H__ */ diff --git a/contrib/ipfilter/iplang/iplang.h b/contrib/ipfilter/iplang/iplang.h index 63cc078322a3..f38ef9671701 100644 --- a/contrib/ipfilter/iplang/iplang.h +++ b/contrib/ipfilter/iplang/iplang.h @@ -1,54 +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 __P((char *, char *)); +extern int get_arpipv4(char *, char *); diff --git a/contrib/ipfilter/iplang/iplang_l.l b/contrib/ipfilter/iplang/iplang_l.l index e66867e2a455..f8b1b82d4707 100644 --- a/contrib/ipfilter/iplang/iplang_l.l +++ b/contrib/ipfilter/iplang/iplang_l.l @@ -1,319 +1,319 @@ /* $FreeBSD$ */ %{ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ */ #include #include #include #if defined(__SVR4) || defined(__sysv__) #include #endif #include #include #include #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 __P((char *)); -void push_proto __P((void)); -void pop_proto __P((void)); -int next_state __P((int, int)); -int next_item __P((int)); -int save_token __P((void)); -void swallow __P((void)); -int yylex __P((void)); +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/contrib/ipfilter/iplang/iplang_y.y b/contrib/ipfilter/iplang/iplang_y.y index b4570d390530..f3335636cee1 100644 --- a/contrib/ipfilter/iplang/iplang_y.y +++ b/contrib/ipfilter/iplang/iplang_y.y @@ -1,1819 +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 #include #include #if !defined(__SVR4) && !defined(__svr4__) # include #else # include #endif #include #include #include #include #include #include #include #include #include #include #include #include # include # include # include #include #include #include #include #include #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 __P((u_short *, u_int, u_long)); -u_long p_chksum __P((u_short *, u_int)); +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 __P((char *arg)); -u_short getportnum __P((char *, char *)); -struct ether_addr *geteaddr __P((char *, struct ether_addr *)); -void *new_header __P((int)); -void free_aniplist __P((void)); -void inc_anipheaders __P((int)); -void new_data __P((void)); -void set_datalen __P((char **)); -void set_datafile __P((char **)); -void set_data __P((char **)); -void new_packet __P((void)); -void set_ipv4proto __P((char **)); -void set_ipv4src __P((char **)); -void set_ipv4dst __P((char **)); -void set_ipv4off __P((char **)); -void set_ipv4v __P((char **)); -void set_ipv4hl __P((char **)); -void set_ipv4ttl __P((char **)); -void set_ipv4tos __P((char **)); -void set_ipv4id __P((char **)); -void set_ipv4sum __P((char **)); -void set_ipv4len __P((char **)); -void new_tcpheader __P((void)); -void set_tcpsport __P((char **)); -void set_tcpdport __P((char **)); -void set_tcpseq __P((char **)); -void set_tcpack __P((char **)); -void set_tcpoff __P((char **)); -void set_tcpurp __P((char **)); -void set_tcpwin __P((char **)); -void set_tcpsum __P((char **)); -void set_tcpflags __P((char **)); -void set_tcpopt __P((int, char **)); -void end_tcpopt __P((void)); -void new_udpheader __P((void)); -void set_udplen __P((char **)); -void set_udpsum __P((char **)); -void prep_packet __P((void)); -void packet_done __P((void)); -void new_interface __P((void)); -void check_interface __P((void)); -void set_ifname __P((char **)); -void set_ifmtu __P((int)); -void set_ifv4addr __P((char **)); -void set_ifeaddr __P((char **)); -void new_arp __P((void)); -void set_arpeaddr __P((char **)); -void set_arpv4addr __P((char **)); -void reset_send __P((void)); -void set_sendif __P((char **)); -void set_sendvia __P((char **)); -void set_defaultrouter __P((char **)); -void new_icmpheader __P((void)); -void set_icmpcode __P((int)); -void set_icmptype __P((int)); -void set_icmpcodetok __P((char **)); -void set_icmptypetok __P((char **)); -void set_icmpid __P((int)); -void set_icmpseq __P((int)); -void set_icmpotime __P((int)); -void set_icmprtime __P((int)); -void set_icmpttime __P((int)); -void set_icmpmtu __P((int)); -void set_redir __P((int, char **)); -void new_ipv4opt __P((void)); -void set_icmppprob __P((int)); -void add_ipopt __P((int, void *)); -void end_ipopt __P((void)); -void set_secclass __P((char **)); -void free_anipheader __P((void)); -void end_ipv4 __P((void)); -void end_icmp __P((void)); -void end_udp __P((void)); -void end_tcp __P((void)); -void end_data __P((void)); -void yyerror __P((char *)); -void iplang __P((FILE *)); -int arp_getipv4 __P((char *, char *)); -int yyparse __P((void)); +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 IL_NUMBER %type number digits optnumber %token IL_TOKEN %type 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 IL_IPS_RESERV4 IL_IPS_TOPSECRET IL_IPS_SECRET IL_IPS_RESERV3 %token 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) sprintf(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/contrib/ipfilter/ipmon.h b/contrib/ipfilter/ipmon.h index b469cc80d6b5..4807299c49d2 100644 --- a/contrib/ipfilter/ipmon.h +++ b/contrib/ipfilter/ipmon.h @@ -1,142 +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 __P((void)); -extern int load_config __P((char *)); -extern void unload_config __P((void)); -extern void dumphex __P((FILE *, int, char *, int)); -extern int check_action __P((char *, char *, int, int)); -extern char *getword __P((int)); -extern void *add_doing __P((ipmon_saver_t *)); +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/contrib/ipfilter/ipsend/ipresend.c b/contrib/ipfilter/ipsend/ipresend.c index 1e7b9049169a..d9a5f92cbe2c 100644 --- a/contrib/ipfilter/ipsend/ipresend.c +++ b/contrib/ipfilter/ipsend/ipresend.c @@ -1,133 +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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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 __P((char *)); -int main __P((int, char **)); +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/contrib/ipfilter/ipsend/ipsend.c b/contrib/ipfilter/ipsend/ipsend.c index b7617734f66e..d77081bd8b71 100644 --- a/contrib/ipfilter/ipsend/ipsend.c +++ b/contrib/ipfilter/ipsend/ipsend.c @@ -1,416 +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 #include #include #include #include #include #include #include #include #include #include #include #include # include #include "ipsend.h" #include "ipf.h" # include extern char *optarg; extern int optind; -extern void iplang __P((FILE *)); +extern void iplang(FILE *); char options[68]; int opts; char default_device[] = "le0"; -static void usage __P((char *)); -static void do_icmp __P((ip_t *, char *)); +static void usage(char *); +static void do_icmp(ip_t *, char *); void udpcksum(ip_t *, struct udphdr *, int); -int main __P((int, char **)); +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 \tSet the TCP window size\n\ ", prog); fprintf(stderr, "Usage: %s [-dv] -L \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/contrib/ipfilter/ipsend/ipsend.h b/contrib/ipfilter/ipsend/ipsend.h index 22f85ff07f14..bfec90f1c5b3 100644 --- a/contrib/ipfilter/ipsend/ipsend.h +++ b/contrib/ipfilter/ipsend/ipsend.h @@ -1,62 +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 #include "ipf.h" /* XXX: The following is needed by tcpip.h */ #include #include "netinet/tcpip.h" #include "ipt.h" -extern int resolve __P((char *, char *)); -extern int arp __P((char *, char *)); -extern u_short chksum __P((u_short *, int)); -extern int send_ether __P((int, char *, int, struct in_addr)); -extern int send_ip __P((int, int, ip_t *, struct in_addr, int)); -extern int send_tcp __P((int, int, ip_t *, struct in_addr)); -extern int send_udp __P((int, int, ip_t *, struct in_addr)); -extern int send_icmp __P((int, int, ip_t *, struct in_addr)); -extern int send_packet __P((int, int, ip_t *, struct in_addr)); -extern int send_packets __P((char *, int, ip_t *, struct in_addr)); -extern u_short ipseclevel __P((char *)); -extern u_32_t buildopts __P((char *, char *, int)); -extern int addipopt __P((char *, struct ipopt_names *, int, char *)); -extern int initdevice __P((char *, int)); -extern int sendip __P((int, char *, int)); -extern struct tcpcb *find_tcp __P((int, struct tcpiphdr *)); -extern int ip_resend __P((char *, int, struct ipread *, struct in_addr, char *)); +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 __P((char *, int, ip_t *, struct in_addr, int)); -extern void ip_test2 __P((char *, int, ip_t *, struct in_addr, int)); -extern void ip_test3 __P((char *, int, ip_t *, struct in_addr, int)); -extern void ip_test4 __P((char *, int, ip_t *, struct in_addr, int)); -extern void ip_test5 __P((char *, int, ip_t *, struct in_addr, int)); -extern void ip_test6 __P((char *, int, ip_t *, struct in_addr, int)); -extern void ip_test7 __P((char *, int, ip_t *, struct in_addr, int)); -extern int do_socket __P((char *, int, struct tcpiphdr *, struct in_addr)); -extern int kmemcpy __P((char *, void *, int)); +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/contrib/ipfilter/ipsend/iptest.c b/contrib/ipfilter/ipsend/iptest.c index ffabaf37b5bc..df2efb96a32a 100644 --- a/contrib/ipfilter/ipsend/iptest.c +++ b/contrib/ipfilter/ipsend/iptest.c @@ -1,197 +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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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 __P((char *)); -int main __P((int, char **)); +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/contrib/ipfilter/ipsend/resend.c b/contrib/ipfilter/ipsend/resend.c index 3401673ff3a1..5d01bb78e5e5 100644 --- a/contrib/ipfilter/ipsend/resend.c +++ b/contrib/ipfilter/ipsend/resend.c @@ -1,141 +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 #include #include #include #include #include #include #include #include # include # include #include #include #include #include #include #include "ipsend.h" extern int opts; -void dumppacket __P((ip_t *)); +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/contrib/ipfilter/ipsend/sock.c b/contrib/ipfilter/ipsend/sock.c index 66e1a0aa897e..a39f9de16345 100644 --- a/contrib/ipfilter/ipsend/sock.c +++ b/contrib/ipfilter/ipsend/sock.c @@ -1,322 +1,322 @@ /* $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 #include #include #include #if defined(__NetBSD__) && defined(__vax__) /* * XXX need to declare boolean_t for _KERNEL * which ends up including for vax. See PR#32907 * for further details. */ typedef int boolean_t; #endif #include # include # ifdef __NetBSD__ # include # endif # ifdef __FreeBSD__ # define _WANT_FILE # else # define _KERNEL # define KERNEL # endif # include # ifdef __FreeBSD__ # undef _WANT_FILE # else # undef _KERNEL # undef KERNEL # endif #include #include #include #include #include # include #ifdef sun #include #include #endif #include #include #include #include #include #include #include #include #include # include #include #define _WANT_INPCB #include #include #define _WANT_TCPCB #include #include #include #include #include #include #include #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 #if BSD < 199103 -static struct proc *getproc __P((void)); +static struct proc *getproc(void); #else -static struct kinfo_proc *getproc __P((void)); +static struct kinfo_proc *getproc(void); #endif 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_version) 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/contrib/ipfilter/ipt.h b/contrib/ipfilter/ipt.h index 16d88dfc8809..9a4d75a85ccb 100644 --- a/contrib/ipfilter/ipt.h +++ b/contrib/ipfilter/ipt.h @@ -1,40 +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 struct ipread { - int (*r_open) __P((char *)); - int (*r_close) __P((void)); - int (*r_readip) __P((mb_t *, char **, int *)); + 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/contrib/ipfilter/kmem.h b/contrib/ipfilter/kmem.h index ea21c052599f..c4b65ed63ce9 100644 --- a/contrib/ipfilter/kmem.h +++ b/contrib/ipfilter/kmem.h @@ -1,30 +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 __P((char *, char *)); -extern int kmemcpy __P((char *, long, int)); -extern int kstrncpy __P((char *, long, int)); +extern int openkmem(char *, char *); +extern int kmemcpy(char *, long, int); +extern int kstrncpy(char *, long, int); #if defined(__NetBSD__) || defined(__OpenBSD) # include #endif #ifdef _PATH_KMEM # define KMEM _PATH_KMEM #else # define KMEM "/dev/kmem" #endif #endif /* __KMEM_H__ */ diff --git a/contrib/ipfilter/lib/facpri.h b/contrib/ipfilter/lib/facpri.h index d3634e99d795..5698c0ebe047 100644 --- a/contrib/ipfilter/lib/facpri.h +++ b/contrib/ipfilter/lib/facpri.h @@ -1,39 +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 __P((int)); -extern int fac_findname __P((char *)); +extern char *fac_toname(int); +extern int fac_findname(char *); -extern char *pri_toname __P((int)); -extern int pri_findname __P((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/contrib/ipfilter/lib/inet_addr.c b/contrib/ipfilter/lib/inet_addr.c index fcaefe0fc23e..2b192ae19eff 100644 --- a/contrib/ipfilter/lib/inet_addr.c +++ b/contrib/ipfilter/lib/inet_addr.c @@ -1,204 +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 #include #include #include #ifndef __P # define __P(x) x #endif -int inet_aton __P((const char *, struct in_addr *)); +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/contrib/ipfilter/lib/initparse.c b/contrib/ipfilter/lib/initparse.c index a16ac0f36bcd..c85d6d3ed69d 100644 --- a/contrib/ipfilter/lib/initparse.c +++ b/contrib/ipfilter/lib/initparse.c @@ -1,20 +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 __P((void)) +void initparse(void) { gethostname(thishost, sizeof(thishost)); thishost[sizeof(thishost) - 1] = '\0'; } diff --git a/contrib/ipfilter/lib/interror.c b/contrib/ipfilter/lib/interror.c index c13f5f8618a8..ffa12def4ac1 100644 --- a/contrib/ipfilter/lib/interror.c +++ b/contrib/ipfilter/lib/interror.c @@ -1,582 +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 #include typedef struct { int iee_number; char *iee_text; } ipf_error_entry_t; -static ipf_error_entry_t *find_error __P((int)); +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 addresss 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; sprintf(text, "unknown error %d", errnum); } else { sprintf(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; sprintf(text, "unknown error %d", errnum); return text; } diff --git a/contrib/ipfilter/lib/ipft_hx.c b/contrib/ipfilter/lib/ipft_hx.c index 15002eac788d..0165d0b092f7 100644 --- a/contrib/ipfilter/lib/ipft_hx.c +++ b/contrib/ipfilter/lib/ipft_hx.c @@ -1,185 +1,185 @@ /* $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 #include "ipf.h" #include "ipt.h" extern int opts; -static int hex_open __P((char *)); -static int hex_close __P((void)); -static int hex_readip __P((mb_t *, char **, int *)); -static char *readhex __P((char *, char *)); +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; int cnt; buf = (char *)mb->mb_buf; cnt = sizeof(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/contrib/ipfilter/lib/ipft_pc.c b/contrib/ipfilter/lib/ipft_pc.c index 3a264bd66ffe..bd0c752c392f 100644 --- a/contrib/ipfilter/lib/ipft_pc.c +++ b/contrib/ipfilter/lib/ipft_pc.c @@ -1,254 +1,254 @@ /* $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 __P((char *)); -static int ipcap_close __P((void)); -static int ipcap_readip __P((mb_t *, char **, int *)); -static int ipcap_read_rec __P((packetheader_t *)); -static void iswap_hdr __P((fileheader_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; char *s; s = (char *)rec; n = sizeof(*rec); while (n > 0) { i = read(pfd, (char *)rec, sizeof(*rec)); if (i <= 0) return -2; s += i; 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/contrib/ipfilter/lib/ipft_tx.c b/contrib/ipfilter/lib/ipft_tx.c index a996c5b9612a..49d7cb2b8c5c 100644 --- a/contrib/ipfilter/lib/ipft_tx.c +++ b/contrib/ipfilter/lib/ipft_tx.c @@ -1,510 +1,510 @@ /* $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 #include "ipf.h" #include "ipt.h" extern int opts; static char *tx_proto = ""; -static int text_open __P((char *)), text_close __P((void)); -static int text_readip __P((mb_t *, char **, int *)); -static int parseline __P((char *, ip_t *, char **, int *)); +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 __P((char *, int *)); -static u_short tx_portnum __P((char *)); +static u_32_t tx_hostnum(char *, int *); +static u_short tx_portnum(char *); #ifdef USE_INET6 -int parseipv6 __P((char **, ip6_t *, char **, int *)); +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; int cnt; buf = (char *)mb->mb_buf; cnt = sizeof(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/contrib/ipfilter/lib/kmem.h b/contrib/ipfilter/lib/kmem.h index 31cd9725cc62..bcf6a0be7e27 100644 --- a/contrib/ipfilter/lib/kmem.h +++ b/contrib/ipfilter/lib/kmem.h @@ -1,30 +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 __P((char *, char *)); -extern int kmemcpy __P((char *, long, int)); -extern int kstrncpy __P((char *, long, int)); +extern int openkmem(char *, char *); +extern int kmemcpy(char *, long, int); +extern int kstrncpy(char *, long, int); #if defined(__NetBSD__) # include #endif #ifdef _PATH_KMEM # define KMEM _PATH_KMEM #else # define KMEM "/dev/kmem" #endif #endif /* __KMEM_H__ */ diff --git a/contrib/ipfilter/lib/printipfexpr.c b/contrib/ipfilter/lib/printipfexpr.c index 06b987e07575..6fb74c1e2e26 100644 --- a/contrib/ipfilter/lib/printipfexpr.c +++ b/contrib/ipfilter/lib/printipfexpr.c @@ -1,199 +1,199 @@ #include "ipf.h" -static void printport __P((int *)); -static void printhosts __P((int *)); -static void printsingle __P((int *)); +static void printport(int *); +static void printhosts(int *); +static void printsingle(int *); #ifdef USE_INET6 -static void printhostsv6 __P((int *)); +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/contrib/ipfilter/lib/save_execute.c b/contrib/ipfilter/lib/save_execute.c index 65caca46bc76..68a3a3754900 100644 --- a/contrib/ipfilter/lib/save_execute.c +++ b/contrib/ipfilter/lib/save_execute.c @@ -1,80 +1,80 @@ #include "ipf.h" #include "ipmon.h" -static void *execute_parse __P((char **)); -static void execute_destroy __P((void *)); -static int execute_send __P((void *, ipmon_msg_t *)); -static void execute_print __P((void *)); +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/contrib/ipfilter/lib/save_file.c b/contrib/ipfilter/lib/save_file.c index b852bd601982..84ef157009f5 100644 --- a/contrib/ipfilter/lib/save_file.c +++ b/contrib/ipfilter/lib/save_file.c @@ -1,130 +1,130 @@ #include "ipf.h" #include "ipmon.h" -static void *file_parse __P((char **)); -static void file_destroy __P((void *)); -static int file_send __P((void *, ipmon_msg_t *)); -static void file_print __P((void *)); -static int file_match __P((void *, void *)); -static void *file_dup __P((void *)); +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/contrib/ipfilter/lib/save_nothing.c b/contrib/ipfilter/lib/save_nothing.c index d25ab519bce1..39158cf51ddb 100644 --- a/contrib/ipfilter/lib/save_nothing.c +++ b/contrib/ipfilter/lib/save_nothing.c @@ -1,62 +1,62 @@ #include "ipf.h" #include "ipmon.h" -static void *nothing_parse __P((char **)); -static void nothing_destroy __P((void *)); -static int nothing_send __P((void *, ipmon_msg_t *)); +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/contrib/ipfilter/lib/save_syslog.c b/contrib/ipfilter/lib/save_syslog.c index c1efdf41d984..37d428bdb4aa 100644 --- a/contrib/ipfilter/lib/save_syslog.c +++ b/contrib/ipfilter/lib/save_syslog.c @@ -1,137 +1,137 @@ #include "ipf.h" #include "ipmon.h" #include -static void *syslog_parse __P((char **)); -static void syslog_destroy __P((void *)); -static int syslog_send __P((void *, ipmon_msg_t *)); -static void syslog_print __P((void *)); +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/contrib/ipfilter/lib/save_v1trap.c b/contrib/ipfilter/lib/save_v1trap.c index 78671c79c768..cca61ac600e5 100644 --- a/contrib/ipfilter/lib/save_v1trap.c +++ b/contrib/ipfilter/lib/save_v1trap.c @@ -1,463 +1,463 @@ #include "ipf.h" #include "netinet/ipl.h" #include "ipmon.h" #include #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 __P((u_char *, int)); -static int writelength __P((u_char *, u_int)); -static int maketrap_v1 __P((char *, u_char *, int, u_char *, int, u_32_t, - time_t)); -static void snmpv1_destroy __P((void *)); -static void *snmpv1_dup __P((void *)); -static int snmpv1_match __P((void *, void *)); -static void *snmpv1_parse __P((char **)); -static void snmpv1_print __P((void *)); -static int snmpv1_send __P((void *, ipmon_msg_t *)); +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/contrib/ipfilter/lib/save_v2trap.c b/contrib/ipfilter/lib/save_v2trap.c index 0507be9f2696..480f4290851d 100644 --- a/contrib/ipfilter/lib/save_v2trap.c +++ b/contrib/ipfilter/lib/save_v2trap.c @@ -1,461 +1,461 @@ #include "ipf.h" #include "netinet/ipl.h" #include "ipmon.h" #include 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 __P((u_char *, int)); -static int writelength __P((u_char *, u_int)); -static int maketrap_v2 __P((char *, u_char *, int, u_char *, int)); -static void snmpv2_destroy __P((void *)); -static void *snmpv2_dup __P((void *)); -static int snmpv2_match __P((void *, void *)); -static void *snmpv2_parse __P((char **)); -static void snmpv2_print __P((void *)); -static int snmpv2_send __P((void *, ipmon_msg_t *)); - - -int sendtrap_v2_0 __P((int, char *, char *, int)); +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/contrib/ipfilter/lib/var.c b/contrib/ipfilter/lib/var.c index e61c8d1cfea9..22d5b2ac1b1a 100644 --- a/contrib/ipfilter/lib/var.c +++ b/contrib/ipfilter/lib/var.c @@ -1,179 +1,179 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ */ #include #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 __P((char *)); -static char *expand_string __P((char *, int)); +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/contrib/ipfilter/man/ipf.4 b/contrib/ipfilter/man/ipf.4 index aaa050d21340..9b82e01bfc76 100644 --- a/contrib/ipfilter/man/ipf.4 +++ b/contrib/ipfilter/man/ipf.4 @@ -1,254 +1,254 @@ .\" $FreeBSD$ .TH IPF 4 .SH NAME ipf \- packet filtering kernel interface .SH SYNOPSIS #include .br #include .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; #if BSD >= 199306 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) __P((int, ip_t *, fr_info_t *)); + int (*fr_func)(int, ip_t *, fr_info_t *)); char fr_icode; /* return ICMP code */ char fr_ifname[IFNAMSIZ]; #if BSD > 199306 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/contrib/ipfilter/md5.c b/contrib/ipfilter/md5.c index d2ed954576d3..899bb91700ba 100644 --- a/contrib/ipfilter/md5.c +++ b/contrib/ipfilter/md5.c @@ -1,310 +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 # else # include # 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 __P((UINT4 *, UINT4 *)); +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/contrib/ipfilter/md5.h b/contrib/ipfilter/md5.h index cc09b48e8547..6f59500daf19 100644 --- a/contrib/ipfilter/md5.h +++ b/contrib/ipfilter/md5.h @@ -1,64 +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 __P((MD5_CTX *)); -extern void MD5Update __P((MD5_CTX *, unsigned char *, unsigned int)); -extern void MD5Final __P((unsigned char *, 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/contrib/ipfilter/radix_ipf.c b/contrib/ipfilter/radix_ipf.c index 1c9fa7c446c8..4b3804478527 100644 --- a/contrib/ipfilter/radix_ipf.c +++ b/contrib/ipfilter/radix_ipf.c @@ -1,1528 +1,1528 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include #include #include #include #include #include #if !defined(_KERNEL) # include # include # include # include #endif #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #ifdef RDX_DEBUG # include # include # include #endif #include "netinet/radix_ipf.h" #define ADF_OFF offsetof(addrfamily_t, adf_addr) #define ADF_OFF_BITS (ADF_OFF << 3) -static ipf_rdx_node_t *ipf_rx_insert __P((ipf_rdx_head_t *, - ipf_rdx_node_t nodes[2], int *)); -static void ipf_rx_attach_mask __P((ipf_rdx_node_t *, ipf_rdx_mask_t *)); -static int count_mask_bits __P((addrfamily_t *, u_32_t **)); -static void buildnodes __P((addrfamily_t *, addrfamily_t *, - ipf_rdx_node_t n[2])); -static ipf_rdx_node_t *ipf_rx_find_addr __P((ipf_rdx_node_t *, u_32_t *)); -static ipf_rdx_node_t *ipf_rx_lookup __P((ipf_rdx_head_t *, addrfamily_t *, - addrfamily_t *)); -static ipf_rdx_node_t *ipf_rx_match __P((ipf_rdx_head_t *, addrfamily_t *)); +static ipf_rdx_node_t *ipf_rx_insert(ipf_rdx_head_t *, + ipf_rdx_node_t nodes[2], int *); +static void ipf_rx_attach_mask(ipf_rdx_node_t *, ipf_rdx_mask_t *); +static int count_mask_bits(addrfamily_t *, u_32_t **); +static void buildnodes(addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t n[2]); +static ipf_rdx_node_t *ipf_rx_find_addr(ipf_rdx_node_t *, u_32_t *); +static ipf_rdx_node_t *ipf_rx_lookup(ipf_rdx_head_t *, addrfamily_t *, + addrfamily_t *); +static ipf_rdx_node_t *ipf_rx_match(ipf_rdx_head_t *, addrfamily_t *); /* * Foreword. * --------- * The code in this file has been written to target using the addrfamily_t * data structure to house the address information and no other. Thus there * are certain aspects of thise code (such as offsets to the address itself) * that are hard coded here whilst they might be more variable elsewhere. * Similarly, this code enforces no maximum key length as that's implied by * all keys needing to be stored in addrfamily_t. */ /* ------------------------------------------------------------------------ */ /* Function: count_mask_bits */ /* Returns: number of consecutive bits starting at "mask". */ /* */ /* Count the number of bits set in the address section of addrfamily_t and */ /* return both that number and a pointer to the last word with a bit set if */ /* lastp is not NULL. The bit count is performed using network byte order */ /* as the guide for which bit is the most significant bit. */ /* ------------------------------------------------------------------------ */ static int count_mask_bits(mask, lastp) addrfamily_t *mask; u_32_t **lastp; { u_32_t *mp = (u_32_t *)&mask->adf_addr; u_32_t m; int count = 0; int mlen; mlen = mask->adf_len - offsetof(addrfamily_t, adf_addr); for (; mlen > 0; mlen -= 4, mp++) { if ((m = ntohl(*mp)) == 0) break; if (lastp != NULL) *lastp = mp; for (; m & 0x80000000; m <<= 1) count++; } return count; } /* ------------------------------------------------------------------------ */ /* Function: buildnodes */ /* Returns: Nil */ /* Parameters: addr(I) - network address for this radix node */ /* mask(I) - netmask associated with the above address */ /* nodes(O) - pair of ipf_rdx_node_t's to initialise with data */ /* associated with addr and mask. */ /* */ /* Initialise the fields in a pair of radix tree nodes according to the */ /* data supplied in the paramters "addr" and "mask". It is expected that */ /* "mask" will contain a consecutive string of bits set. Masks with gaps in */ /* the middle are not handled by this implementation. */ /* ------------------------------------------------------------------------ */ static void buildnodes(addr, mask, nodes) addrfamily_t *addr, *mask; ipf_rdx_node_t nodes[2]; { u_32_t maskbits; u_32_t lastbits; u_32_t lastmask; u_32_t *last; int masklen; last = NULL; maskbits = count_mask_bits(mask, &last); if (last == NULL) { masklen = 0; lastmask = 0; } else { masklen = last - (u_32_t *)mask; lastmask = *last; } lastbits = maskbits & 0x1f; bzero(&nodes[0], sizeof(ipf_rdx_node_t) * 2); nodes[0].maskbitcount = maskbits; nodes[0].index = -1 - (ADF_OFF_BITS + maskbits); nodes[0].addrkey = (u_32_t *)addr; nodes[0].maskkey = (u_32_t *)mask; nodes[0].addroff = nodes[0].addrkey + masklen; nodes[0].maskoff = nodes[0].maskkey + masklen; nodes[0].parent = &nodes[1]; nodes[0].offset = masklen; nodes[0].lastmask = lastmask; nodes[1].offset = masklen; nodes[1].left = &nodes[0]; nodes[1].maskbitcount = maskbits; #ifdef RDX_DEBUG (void) strcpy(nodes[0].name, "_BUILD.0"); (void) strcpy(nodes[1].name, "_BUILD.1"); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_find_addr */ /* Returns: ipf_rdx_node_t * - pointer to a node in the radix tree. */ /* Parameters: tree(I) - pointer to first right node in tree to search */ /* addr(I) - pointer to address to match */ /* */ /* Walk the radix tree given by "tree", looking for a leaf node that is a */ /* match for the address given by "addr". */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * ipf_rx_find_addr(tree, addr) ipf_rdx_node_t *tree; u_32_t *addr; { ipf_rdx_node_t *cur; for (cur = tree; cur->index >= 0;) { if (cur->bitmask & addr[cur->offset]) { cur = cur->right; } else { cur = cur->left; } } return (cur); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_match */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - pointer to address to find */ /* */ /* Search the radix tree for the best match to the address pointed to by */ /* "addr" and return a pointer to that node. This search will not match the */ /* address information stored in either of the root leaves as neither of */ /* them are considered to be part of the tree of data being stored. */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * ipf_rx_match(head, addr) ipf_rdx_head_t *head; addrfamily_t *addr; { ipf_rdx_mask_t *masknode; ipf_rdx_node_t *prev; ipf_rdx_node_t *node; ipf_rdx_node_t *cur; u_32_t *data; u_32_t *mask; u_32_t *key; u_32_t *end; int len; int i; len = addr->adf_len; end = (u_32_t *)((u_char *)addr + len); node = ipf_rx_find_addr(head->root, (u_32_t *)addr); /* * Search the dupkey list for a potential match. */ for (cur = node; (cur != NULL) && (cur->root == 0); cur = cur->dupkey) { i = cur[0].addroff - cur[0].addrkey; data = cur[0].addrkey + i; mask = cur[0].maskkey + i; key = (u_32_t *)addr + i; for (; key < end; data++, key++, mask++) if ((*key & *mask) != *data) break; if ((end == key) && (cur->root == 0)) return (cur); /* Equal keys */ } prev = node->parent; key = (u_32_t *)addr; for (node = prev; node->root == 0; node = node->parent) { /* * We know that the node hasn't matched so therefore only * the entries in the mask list are searched, not the top * node nor the dupkey list. */ masknode = node->masks; for (; masknode != NULL; masknode = masknode->next) { if (masknode->maskbitcount > node->maskbitcount) continue; cur = masknode->node; for (i = ADF_OFF >> 2; i <= node->offset; i++) { if ((key[i] & masknode->mask[i]) == cur->addrkey[i]) return (cur); } } } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_lookup */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - address part of the key to match */ /* mask(I) - netmask part of the key to match */ /* */ /* ipf_rx_lookup searches for an exact match on (addr,mask). The intention */ /* is to see if a given key is in the tree, not to see if a route exists. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * ipf_rx_lookup(head, addr, mask) ipf_rdx_head_t *head; addrfamily_t *addr, *mask; { ipf_rdx_node_t *found; ipf_rdx_node_t *node; u_32_t *akey; int count; found = ipf_rx_find_addr(head->root, (u_32_t *)addr); if (found->root == 1) return NULL; /* * It is possible to find a matching address in the tree but for the * netmask to not match. If the netmask does not match and there is * no list of alternatives present at dupkey, return a failure. */ count = count_mask_bits(mask, NULL); if (count != found->maskbitcount && found->dupkey == NULL) return (NULL); akey = (u_32_t *)addr; if ((found->addrkey[found->offset] & found->maskkey[found->offset]) != akey[found->offset]) return NULL; if (found->dupkey != NULL) { node = found; while (node != NULL && node->maskbitcount != count) node = node->dupkey; if (node == NULL) return (NULL); found = node; } return found; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_attach_mask */ /* Returns: Nil */ /* Parameters: node(I) - pointer to a radix tree node */ /* mask(I) - pointer to mask structure to add */ /* */ /* Add the netmask to the given node in an ordering where the most specific */ /* netmask is at the top of the list. */ /* ------------------------------------------------------------------------ */ static void ipf_rx_attach_mask(node, mask) ipf_rdx_node_t *node; ipf_rdx_mask_t *mask; { ipf_rdx_mask_t **pm; ipf_rdx_mask_t *m; for (pm = &node->masks; (m = *pm) != NULL; pm = &m->next) if (m->maskbitcount < mask->maskbitcount) break; mask->next = *pm; *pm = mask; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_insert */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to add nodes to */ /* nodes(I) - pointer to radix nodes to be added */ /* dup(O) - set to 1 if node is a duplicate, else 0. */ /* */ /* Add the new radix tree entry that owns nodes[] to the tree given by head.*/ /* If there is already a matching key in the table, "dup" will be set to 1 */ /* and the existing node pointer returned if there is a complete key match. */ /* A complete key match is a matching of all key data that is presented by */ /* by the netmask. */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * ipf_rx_insert(head, nodes, dup) ipf_rdx_head_t *head; ipf_rdx_node_t nodes[2]; int *dup; { ipf_rdx_mask_t **pmask; ipf_rdx_node_t *node; ipf_rdx_node_t *prev; ipf_rdx_mask_t *mask; ipf_rdx_node_t *cur; u_32_t nodemask; u_32_t *addr; u_32_t *data; int nodebits; u_32_t *key; u_32_t *end; u_32_t bits; int nodekey; int nodeoff; int nlen; int len; addr = nodes[0].addrkey; node = ipf_rx_find_addr(head->root, addr); len = ((addrfamily_t *)addr)->adf_len; key = (u_32_t *)&((addrfamily_t *)addr)->adf_addr; data= (u_32_t *)&((addrfamily_t *)node->addrkey)->adf_addr; end = (u_32_t *)((u_char *)addr + len); for (nlen = 0; key < end; data++, key++, nlen += 32) if (*key != *data) break; if (end == data) { *dup = 1; return (node); /* Equal keys */ } *dup = 0; bits = (ntohl(*data) ^ ntohl(*key)); for (; bits != 0; nlen++) { if ((bits & 0x80000000) != 0) break; bits <<= 1; } nlen += ADF_OFF_BITS; nodes[1].index = nlen; nodes[1].bitmask = htonl(0x80000000 >> (nlen & 0x1f)); nodes[0].offset = nlen / 32; nodes[1].offset = nlen / 32; /* * Walk through the tree and look for the correct place to attach * this node. ipf_rx_fin_addr is not used here because the place * to attach this node may be an internal node (same key, different * netmask.) Additionally, the depth of the search is forcibly limited * here to not exceed the netmask, so that a short netmask will be * added higher up the tree even if there are lower branches. */ cur = head->root; key = nodes[0].addrkey; do { prev = cur; if (key[cur->offset] & cur->bitmask) { cur = cur->right; } else { cur = cur->left; } } while (nlen > (unsigned)cur->index); if ((key[prev->offset] & prev->bitmask) == 0) { prev->left = &nodes[1]; } else { prev->right = &nodes[1]; } cur->parent = &nodes[1]; nodes[1].parent = prev; if ((key[nodes[1].offset] & nodes[1].bitmask) == 0) { nodes[1].right = cur; } else { nodes[1].right = &nodes[0]; nodes[1].left = cur; } nodeoff = nodes[0].offset; nodekey = nodes[0].addrkey[nodeoff]; nodemask = nodes[0].lastmask; nodebits = nodes[0].maskbitcount; prev = NULL; /* * Find the node up the tree with the largest pattern that still * matches the node being inserted to see if this mask can be * moved there. */ for (cur = nodes[1].parent; cur->root == 0; cur = cur->parent) { if (cur->maskbitcount <= nodebits) break; if (((cur - 1)->addrkey[nodeoff] & nodemask) != nodekey) break; prev = cur; } KMALLOC(mask, ipf_rdx_mask_t *); if (mask == NULL) return NULL; bzero(mask, sizeof(*mask)); mask->next = NULL; mask->node = &nodes[0]; mask->maskbitcount = nodebits; mask->mask = nodes[0].maskkey; nodes[0].mymask = mask; if (prev != NULL) { ipf_rdx_mask_t *m; for (pmask = &prev->masks; (m = *pmask) != NULL; pmask = &m->next) { if (m->maskbitcount < nodebits) break; } } else { /* * No higher up nodes qualify, so attach mask locally. */ pmask = &nodes[0].masks; } mask->next = *pmask; *pmask = mask; /* * Search the mask list on each child to see if there are any masks * there that can be moved up to this newly inserted node. */ cur = nodes[1].right; if (cur->root == 0) { for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { if (mask->maskbitcount < nodebits) { *pmask = mask->next; ipf_rx_attach_mask(&nodes[0], mask); } else { pmask = &mask->next; } } } cur = nodes[1].left; if (cur->root == 0 && cur != &nodes[0]) { for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { if (mask->maskbitcount < nodebits) { *pmask = mask->next; ipf_rx_attach_mask(&nodes[0], mask); } else { pmask = &mask->next; } } } return (&nodes[0]); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_addroute */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - address portion of "route" to add */ /* mask(I) - netmask portion of "route" to add */ /* nodes(I) - radix tree data nodes inside allocate structure */ /* */ /* Attempt to add a node to the radix tree. The key for the node is the */ /* (addr,mask). No memory allocation for the radix nodes themselves is */ /* performed here, the data structure that this radix node is being used to */ /* find is expected to house the node data itself however the call to */ /* ipf_rx_insert() will attempt to allocate memory in order for netmask to */ /* be promoted further up the tree. */ /* In this case, the ip_pool_node_t structure from ip_pool.h contains both */ /* the key material (addr,mask) and the radix tree nodes[]. */ /* */ /* The mechanics of inserting the node into the tree is handled by the */ /* function ipf_rx_insert() above. Here, the code deals with the case */ /* where the data to be inserted is a duplicate. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * ipf_rx_addroute(head, addr, mask, nodes) ipf_rdx_head_t *head; addrfamily_t *addr, *mask; ipf_rdx_node_t *nodes; { ipf_rdx_node_t *node; ipf_rdx_node_t *prev; ipf_rdx_node_t *x; int dup; buildnodes(addr, mask, nodes); x = ipf_rx_insert(head, nodes, &dup); if (x == NULL) return NULL; if (dup == 1) { node = &nodes[0]; prev = NULL; /* * The duplicate list is kept sorted with the longest * mask at the top, meaning that the most specific entry * in the listis found first. This list thus allows for * duplicates such as 128.128.0.0/32 and 128.128.0.0/16. */ while ((x != NULL) && (x->maskbitcount > node->maskbitcount)) { prev = x; x = x->dupkey; } /* * Is it a complete duplicate? If so, return NULL and * fail the insert. Otherwise, insert it into the list * of netmasks active for this key. */ if ((x != NULL) && (x->maskbitcount == node->maskbitcount)) return (NULL); if (prev != NULL) { nodes[0].dupkey = x; prev->dupkey = &nodes[0]; nodes[0].parent = prev; if (x != NULL) x->parent = &nodes[0]; } else { nodes[0].dupkey = x->dupkey; prev = x->parent; nodes[0].parent = prev; x->parent = &nodes[0]; if (prev->left == x) prev->left = &nodes[0]; else prev->right = &nodes[0]; } } return &nodes[0]; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_delete */ /* Returns: ipf_rdx_node_t * - NULL on error, else node removed from */ /* the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - pointer to the address part of the key */ /* mask(I) - pointer to the netmask part of the key */ /* */ /* Search for an entry in the radix tree that is an exact match for (addr, */ /* mask) and remove it if it exists. In the case where (addr,mask) is a not */ /* a unique key, the tree structure itself is not changed - only the list */ /* of duplicate keys. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * ipf_rx_delete(head, addr, mask) ipf_rdx_head_t *head; addrfamily_t *addr, *mask; { ipf_rdx_mask_t **pmask; ipf_rdx_node_t *parent; ipf_rdx_node_t *found; ipf_rdx_node_t *prev; ipf_rdx_node_t *node; ipf_rdx_node_t *cur; ipf_rdx_mask_t *m; int count; found = ipf_rx_find_addr(head->root, (u_32_t *)addr); if (found == NULL) return NULL; if (found->root == 1) return NULL; count = count_mask_bits(mask, NULL); parent = found->parent; if (found->dupkey != NULL) { node = found; while (node != NULL && node->maskbitcount != count) node = node->dupkey; if (node == NULL) return (NULL); if (node != found) { /* * Remove from the dupkey list. Here, "parent" is * the previous node on the list (rather than tree) * and "dupkey" is the next node on the list. */ parent = node->parent; parent->dupkey = node->dupkey; node->dupkey->parent = parent; } else { /* * * When removing the top node of the dupkey list, * the pointers at the top of the list that point * to other tree nodes need to be preserved and * any children must have their parent updated. */ node = node->dupkey; node->parent = found->parent; node->right = found->right; node->left = found->left; found->right->parent = node; found->left->parent = node; if (parent->left == found) parent->left = node; else parent->right= node; } } else { if (count != found->maskbitcount) return (NULL); /* * Remove the node from the tree and reconnect the subtree * below. */ /* * If there is a tree to the left, look for something to * attach in place of "found". */ prev = found + 1; cur = parent->parent; if (parent != found + 1) { if ((found + 1)->parent->right == found + 1) (found + 1)->parent->right = parent; else (found + 1)->parent->left = parent; if (cur->right == parent) { if (parent->left == found) { cur->right = parent->right; } else if (parent->left != parent - 1) { cur->right = parent->left; } else { cur->right = parent - 1; } cur->right->parent = cur; } else { if (parent->right == found) { cur->left = parent->left; } else if (parent->right != parent - 1) { cur->left = parent->right; } else { cur->left = parent - 1; } cur->left->parent = cur; } parent->left = (found + 1)->left; if ((found + 1)->right != parent) parent->right = (found + 1)->right; parent->left->parent = parent; parent->right->parent = parent; parent->parent = (found + 1)->parent; parent->bitmask = prev->bitmask; parent->offset = prev->offset; parent->index = prev->index; } else { /* * We found an edge node. */ cur = parent->parent; if (cur->left == parent) { if (parent->left == found) { cur->left = parent->right; parent->right->parent = cur; } else { cur->left = parent->left; parent->left->parent = cur; } } else { if (parent->right != found) { cur->right = parent->right; parent->right->parent = cur; } else { cur->right = parent->left; prev->left->parent = cur; } } } } /* * Remove mask associated with this node. */ for (cur = parent; cur->root == 0; cur = cur->parent) { ipf_rdx_mask_t **pm; if (cur->maskbitcount <= found->maskbitcount) break; if (((cur - 1)->addrkey[found->offset] & found->bitmask) != found->addrkey[found->offset]) break; for (pm = &cur->masks; (m = *pm) != NULL; ) if (m->node == cur) { *pm = m->next; break; } else { pm = &m->next; } } KFREE(found->mymask); /* * Masks that have been brought up to this node from below need to * be sent back down. */ for (pmask = &parent->masks; (m = *pmask) != NULL; ) { *pmask = m->next; cur = m->node; if (cur == found) continue; if (found->addrkey[cur->offset] & cur->lastmask) { ipf_rx_attach_mask(parent->right, m); } else if (parent->left != found) { ipf_rx_attach_mask(parent->left, m); } } return (found); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_walktree */ /* Returns: Nil */ /* Paramters: head(I) - pointer to tree head to search */ /* walker(I) - function to call for each node in the tree */ /* arg(I) - parameter to pass to walker, in addition to the */ /* node pointer */ /* */ /* A standard tree walking function except that it is iterative, rather */ /* than recursive and tracks the next node in case the "walker" function */ /* should happen to delete and free the current node. It thus goes without */ /* saying that the "walker" function is not permitted to cause any change */ /* in the validity of the data found at either the left or right child. */ /* ------------------------------------------------------------------------ */ void ipf_rx_walktree(head, walker, arg) ipf_rdx_head_t *head; radix_walk_func_t walker; void *arg; { ipf_rdx_node_t *next; ipf_rdx_node_t *node = head->root; ipf_rdx_node_t *base; while (node->index >= 0) node = node->left; for (;;) { base = node; while ((node->parent->right == node) && (node->root == 0)) node = node->parent; for (node = node->parent->right; node->index >= 0; ) node = node->left; next = node; for (node = base; node != NULL; node = base) { base = node->dupkey; if (node->root == 0) walker(node, arg); } node = next; if (node->root) return; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_inithead */ /* Returns: int - 0 = success, else failure */ /* Paramters: softr(I) - pointer to radix context */ /* headp(O) - location for where to store allocated tree head */ /* */ /* This function allocates and initialises a radix tree head structure. */ /* As a traditional radix tree, node 0 is used as the "0" sentinel and node */ /* "2" is used as the all ones sentinel, leaving node "1" as the root from */ /* which the tree is hung with node "0" on its left and node "2" to the */ /* right. The context, "softr", is used here to provide a common source of */ /* the zeroes and ones data rather than have one per head. */ /* ------------------------------------------------------------------------ */ int ipf_rx_inithead(softr, headp) radix_softc_t *softr; ipf_rdx_head_t **headp; { ipf_rdx_head_t *ptr; ipf_rdx_node_t *node; KMALLOC(ptr, ipf_rdx_head_t *); *headp = ptr; if (ptr == NULL) return -1; bzero(ptr, sizeof(*ptr)); node = ptr->nodes; ptr->root = node + 1; node[0].index = ADF_OFF_BITS; node[0].index = -1 - node[0].index; node[1].index = ADF_OFF_BITS; node[2].index = node[0].index; node[0].parent = node + 1; node[1].parent = node + 1; node[2].parent = node + 1; node[1].bitmask = htonl(0x80000000); node[0].root = 1; node[1].root = 1; node[2].root = 1; node[0].offset = ADF_OFF_BITS >> 5; node[1].offset = ADF_OFF_BITS >> 5; node[2].offset = ADF_OFF_BITS >> 5; node[1].left = &node[0]; node[1].right = &node[2]; node[0].addrkey = (u_32_t *)softr->zeros; node[2].addrkey = (u_32_t *)softr->ones; #ifdef RDX_DEBUG (void) strcpy(node[0].name, "0_ROOT"); (void) strcpy(node[1].name, "1_ROOT"); (void) strcpy(node[2].name, "2_ROOT"); #endif ptr->addaddr = ipf_rx_addroute; ptr->deladdr = ipf_rx_delete; ptr->lookup = ipf_rx_lookup; ptr->matchaddr = ipf_rx_match; ptr->walktree = ipf_rx_walktree; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_freehead */ /* Returns: Nil */ /* Paramters: head(I) - pointer to tree head to free */ /* */ /* This function simply free's up the radix tree head. Prior to calling */ /* this function, it is expected that the tree will have been emptied. */ /* ------------------------------------------------------------------------ */ void ipf_rx_freehead(head) ipf_rdx_head_t *head; { KFREE(head); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_create */ /* Parameters: Nil */ /* */ /* ------------------------------------------------------------------------ */ void * ipf_rx_create() { radix_softc_t *softr; KMALLOC(softr, radix_softc_t *); if (softr == NULL) return NULL; bzero((char *)softr, sizeof(*softr)); KMALLOCS(softr->zeros, u_char *, 3 * sizeof(addrfamily_t)); if (softr->zeros == NULL) { KFREE(softr); return (NULL); } softr->ones = softr->zeros + sizeof(addrfamily_t); return softr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_init */ /* Returns: int - 0 = success (always) */ /* */ /* ------------------------------------------------------------------------ */ int ipf_rx_init(ctx) void *ctx; { radix_softc_t *softr = ctx; memset(softr->zeros, 0, 3 * sizeof(addrfamily_t)); memset(softr->ones, 0xff, sizeof(addrfamily_t)); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_destroy */ /* Returns: Nil */ /* */ /* ------------------------------------------------------------------------ */ void ipf_rx_destroy(ctx) void *ctx; { radix_softc_t *softr = ctx; if (softr->zeros != NULL) KFREES(softr->zeros, 3 * sizeof(addrfamily_t)); KFREE(softr); } /* ====================================================================== */ #ifdef RDX_DEBUG /* * To compile this file as a standalone test unit, use -DRDX_DEBUG=1 */ #define NAME(x) ((x)->index < 0 ? (x)->name : (x)->name) #define GNAME(y) ((y) == NULL ? "NULL" : NAME(y)) typedef struct myst { struct ipf_rdx_node nodes[2]; addrfamily_t dst; addrfamily_t mask; struct myst *next; int printed; } myst_t; typedef struct tabe_s { char *host; char *mask; char *what; } tabe_t; tabe_t builtin[] = { #if 1 { "192:168:100::0", "48", "d" }, { "192:168:100::2", "128", "d" }, #else { "127.192.0.0", "255.255.255.0", "d" }, { "127.128.0.0", "255.255.255.0", "d" }, { "127.96.0.0", "255.255.255.0", "d" }, { "127.80.0.0", "255.255.255.0", "d" }, { "127.72.0.0", "255.255.255.0", "d" }, { "127.64.0.0", "255.255.255.0", "d" }, { "127.56.0.0", "255.255.255.0", "d" }, { "127.48.0.0", "255.255.255.0", "d" }, { "127.40.0.0", "255.255.255.0", "d" }, { "127.32.0.0", "255.255.255.0", "d" }, { "127.24.0.0", "255.255.255.0", "d" }, { "127.16.0.0", "255.255.255.0", "d" }, { "127.8.0.0", "255.255.255.0", "d" }, { "124.0.0.0", "255.0.0.0", "d" }, { "125.0.0.0", "255.0.0.0", "d" }, { "126.0.0.0", "255.0.0.0", "d" }, { "127.0.0.0", "255.0.0.0", "d" }, { "10.0.0.0", "255.0.0.0", "d" }, { "128.250.0.0", "255.255.0.0", "d" }, { "192.168.0.0", "255.255.0.0", "d" }, { "192.168.1.0", "255.255.255.0", "d" }, #endif { NULL, NULL, NULL } }; char *mtable[][1] = { #if 1 { "192:168:100::2" }, { "192:168:101::2" }, #else { "9.0.0.0" }, { "9.0.0.1" }, { "11.0.0.0" }, { "11.0.0.1" }, { "127.0.0.1" }, { "127.0.1.0" }, { "255.255.255.0" }, { "126.0.0.1" }, { "128.251.0.0" }, { "128.251.0.1" }, { "128.251.255.255" }, { "129.250.0.0" }, { "129.250.0.1" }, { "192.168.255.255" }, #endif { NULL } }; int forder[22] = { 14, 13, 12, 5, 10, 3, 19, 7, 4, 20, 8, 2, 17, 9, 16, 11, 15, 1, 6, 18, 0, 21 }; static int nodecount = 0; myst_t *myst_top = NULL; tabe_t *ttable = NULL; void add_addr(ipf_rdx_head_t *, int , int); void checktree(ipf_rdx_head_t *); void delete_addr(ipf_rdx_head_t *rnh, int item); void dumptree(ipf_rdx_head_t *rnh); void nodeprinter(ipf_rdx_node_t *, void *); void printroots(ipf_rdx_head_t *); void random_add(ipf_rdx_head_t *); void random_delete(ipf_rdx_head_t *); void test_addr(ipf_rdx_head_t *rnh, int pref, addrfamily_t *, int); static void ipf_rx_freenode(node, arg) ipf_rdx_node_t *node; void *arg; { ipf_rdx_head_t *head = arg; ipf_rdx_node_t *rv; myst_t *stp; stp = (myst_t *)node; rv = ipf_rx_delete(head, &stp->dst, &stp->mask); if (rv != NULL) { free(rv); } } const char * addrname(ap) addrfamily_t *ap; { static char name[80]; const char *txt; bzero((char *)name, sizeof(name)); txt = inet_ntop(ap->adf_family, &ap->adf_addr, name, sizeof(name)); return txt; } 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)); } } void setaddr(afp, str) addrfamily_t *afp; char *str; { bzero((char *)afp, sizeof(*afp)); if (strchr(str, ':') == NULL) { afp->adf_family = AF_INET; afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else { afp->adf_family = AF_INET6; afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; } inet_pton(afp->adf_family, str, &afp->adf_addr); } void setmask(afp, str) addrfamily_t *afp; char *str; { if (strchr(str, '.') != NULL) { afp->adf_addr.in4.s_addr = inet_addr(str); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else if (afp->adf_family == AF_INET) { afp->adf_addr.i6[0] = htonl(0xffffffff << (32 - atoi(str))); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else if (afp->adf_family == AF_INET6) { fill6bits(atoi(str), afp->adf_addr.i6); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; } } void nodeprinter(node, arg) ipf_rdx_node_t *node; void *arg; { myst_t *stp = (myst_t *)node; printf("Node %-9.9s L %-9.9s R %-9.9s P %9.9s/%-9.9s %s/%d\n", node[0].name, GNAME(node[1].left), GNAME(node[1].right), GNAME(node[0].parent), GNAME(node[1].parent), addrname(&stp->dst), node[0].maskbitcount); if (stp->printed == -1) printf("!!! %d\n", stp->printed); else stp->printed = 1; } void printnode(stp) myst_t *stp; { ipf_rdx_node_t *node = &stp->nodes[0]; if (stp->nodes[0].index > 0) stp = (myst_t *)&stp->nodes[-1]; printf("Node %-9.9s ", node[0].name); printf("L %-9.9s ", GNAME(node[1].left)); printf("R %-9.9s ", GNAME(node[1].right)); printf("P %9.9s", GNAME(node[0].parent)); printf("/%-9.9s ", GNAME(node[1].parent)); printf("%s P%d\n", addrname(&stp->dst), stp->printed); } void buildtab(void) { char line[80], *s; tabe_t *tab; int lines; FILE *fp; lines = 0; fp = fopen("hosts", "r"); while (fgets(line, sizeof(line), fp) != NULL) { s = strchr(line, '\n'); if (s != NULL) *s = '\0'; lines++; if (lines == 1) tab = malloc(sizeof(*tab) * 2); else tab = reallocarray(tab, lines + 1, sizeof(*tab)); tab[lines - 1].host = strdup(line); s = strchr(tab[lines - 1].host, '/'); *s++ = '\0'; tab[lines - 1].mask = s; tab[lines - 1].what = "d"; } fclose(fp); tab[lines].host = NULL; tab[lines].mask = NULL; tab[lines].what = NULL; ttable = tab; } void printroots(rnh) ipf_rdx_head_t *rnh; { printf("Root.0.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[0]), rnh->nodes[0].index, GNAME(rnh->nodes[0].parent), GNAME(rnh->nodes[0].left), GNAME(rnh->nodes[0].right)); printf("Root.1.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[1]), rnh->nodes[1].index, GNAME(rnh->nodes[1].parent), GNAME(rnh->nodes[1].left), GNAME(rnh->nodes[1].right)); printf("Root.2.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[2]), rnh->nodes[2].index, GNAME(rnh->nodes[2].parent), GNAME(rnh->nodes[2].left), GNAME(rnh->nodes[2].right)); } int main(int argc, char *argv[]) { addrfamily_t af; ipf_rdx_head_t *rnh; radix_softc_t *ctx; int j; int i; rnh = NULL; buildtab(); ctx = ipf_rx_create(); ipf_rx_init(ctx); ipf_rx_inithead(ctx, &rnh); printf("=== ADD-0 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { add_addr(rnh, i, i); checktree(rnh); } printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== DELETE-0 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { delete_addr(rnh, i); printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); } printf("=== ADD-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { setaddr(&af, ttable[i].host); add_addr(rnh, i, i); /*forder[i]); */ checktree(rnh); } dumptree(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== TEST-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { setaddr(&af, ttable[i].host); test_addr(rnh, i, &af, -1); } printf("=== TEST-2 ===\n"); for (i = 0; mtable[i][0] != NULL; i++) { setaddr(&af, mtable[i][0]); test_addr(rnh, i, &af, -1); } printf("=== DELETE-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { if (ttable[i].what[0] != 'd') continue; delete_addr(rnh, i); for (j = 0; ttable[j].host != NULL; j++) { setaddr(&af, ttable[j].host); test_addr(rnh, i, &af, 3); } printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); } dumptree(rnh); printf("=== ADD-2 ===\n"); random_add(rnh); checktree(rnh); dumptree(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== DELETE-2 ===\n"); random_delete(rnh); checktree(rnh); dumptree(rnh); ipf_rx_walktree(rnh, ipf_rx_freenode, rnh); return 0; } void dumptree(rnh) ipf_rdx_head_t *rnh; { myst_t *stp; printf("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n"); printroots(rnh); for (stp = myst_top; stp; stp = stp->next) printnode(stp); printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); } void test_addr(rnh, pref, addr, limit) ipf_rdx_head_t *rnh; int pref; addrfamily_t *addr; { static int extras[14] = { 0, -1, 1, 3, 5, 8, 9, 15, 16, 19, 255, 256, 65535, 65536 }; ipf_rdx_node_t *rn; addrfamily_t af; char name[80]; myst_t *stp; int i; memset(&af, 0, sizeof(af)); #if 0 if (limit < 0 || limit > 14) limit = 14; for (i = 0; i < limit; i++) { if (ttable[i].host == NULL) break; setaddr(&af, ttable[i].host); printf("%d.%d.LOOKUP(%s)", pref, i, addrname(&af)); rn = ipf_rx_match(rnh, &af); stp = (myst_t *)rn; printf(" = %s (%s/%d)\n", GNAME(rn), rn ? addrname(&stp->dst) : "NULL", rn ? rn->maskbitcount : 0); } #else printf("%d.%d.LOOKUP(%s)", pref, -1, addrname(addr)); rn = ipf_rx_match(rnh, addr); stp = (myst_t *)rn; printf(" = %s (%s/%d)\n", GNAME(rn), rn ? addrname(&stp->dst) : "NULL", rn ? rn->maskbitcount : 0); #endif } void delete_addr(rnh, item) ipf_rdx_head_t *rnh; int item; { ipf_rdx_node_t *rn; addrfamily_t mask; addrfamily_t af; myst_t **pstp; myst_t *stp; memset(&af, 0, sizeof(af)); memset(&mask, 0, sizeof(mask)); setaddr(&af, ttable[item].host); mask.adf_family = af.adf_family; setmask(&mask, ttable[item].mask); printf("DELETE(%s)\n", addrname(&af)); rn = ipf_rx_delete(rnh, &af, &mask); if (rn == NULL) { printf("FAIL LOOKUP DELETE\n"); checktree(rnh); for (stp = myst_top; stp != NULL; stp = stp->next) if (stp->printed != -1) stp->printed = -2; ipf_rx_walktree(rnh, nodeprinter, NULL); dumptree(rnh); abort(); } printf("%d.delete(%s) = %s\n", item, addrname(&af), GNAME(rn)); for (pstp = &myst_top; (stp = *pstp) != NULL; pstp = &stp->next) if (stp == (myst_t *)rn) break; stp->printed = -1; stp->nodes[0].parent = &stp->nodes[0]; stp->nodes[1].parent = &stp->nodes[1]; *pstp = stp->next; free(stp); nodecount--; checktree(rnh); } void add_addr(rnh, n, item) ipf_rdx_head_t *rnh; int n, item; { ipf_rdx_node_t *rn; myst_t *stp; stp = calloc(1, sizeof(*stp)); rn = (ipf_rdx_node_t *)stp; setaddr(&stp->dst, ttable[item].host); stp->mask.adf_family = stp->dst.adf_family; setmask(&stp->mask, ttable[item].mask); stp->next = myst_top; myst_top = stp; (void) sprintf(rn[0].name, "_BORN.0"); (void) sprintf(rn[1].name, "_BORN.1"); rn = ipf_rx_addroute(rnh, &stp->dst, &stp->mask, stp->nodes); (void) sprintf(rn[0].name, "%d_NODE.0", item); (void) sprintf(rn[1].name, "%d_NODE.1", item); printf("ADD %d/%d %s/%s\n", n, item, rn[0].name, rn[1].name); nodecount++; checktree(rnh); } void checktree(ipf_rdx_head_t *head) { myst_t *s1; ipf_rdx_node_t *rn; if (nodecount <= 1) return; for (s1 = myst_top; s1 != NULL; s1 = s1->next) { int fault = 0; if (s1->printed == -1) continue; rn = &s1->nodes[1]; if (rn->right->parent != rn) fault |= 1; if (rn->left->parent != rn) fault |= 2; if (rn->parent->left != rn && rn->parent->right != rn) fault |= 4; if (fault != 0) { printf("FAULT %#x %s\n", fault, rn->name); dumptree(head); ipf_rx_walktree(head, nodeprinter, NULL); fflush(stdout); fflush(stderr); printf("--\n"); abort(); } } } int * randomize(int *pnitems) { int *order; int nitems; int choice; int j; int i; nitems = sizeof(ttable) / sizeof(ttable[0]); *pnitems = nitems; order = calloc(nitems, sizeof(*order)); srandom(getpid() * time(NULL)); memset(order, 0xff, nitems * sizeof(*order)); order[21] = 21; for (i = 0; i < nitems - 1; i++) { do { choice = rand() % (nitems - 1); for (j = 0; j < nitems; j++) if (order[j] == choice) break; } while (j != nitems); order[i] = choice; } return order; } void random_add(rnh) ipf_rdx_head_t *rnh; { int *order; int nitems; int i; order = randomize(&nitems); for (i = 0; i < nitems - 1; i++) { add_addr(rnh, i, order[i]); checktree(rnh); } } void random_delete(rnh) ipf_rdx_head_t *rnh; { int *order; int nitems; int i; order = randomize(&nitems); for (i = 0; i < nitems - 1; i++) { delete_addr(rnh, i); checktree(rnh); } } #endif /* RDX_DEBUG */ diff --git a/contrib/ipfilter/radix_ipf.h b/contrib/ipfilter/radix_ipf.h index bbbf5592b990..50be3fceb6f6 100644 --- a/contrib/ipfilter/radix_ipf.h +++ b/contrib/ipfilter/radix_ipf.h @@ -1,97 +1,97 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #ifndef __RADIX_IPF_H__ #define __RADIX_IPF_H__ #ifndef U_32_T typedef unsigned int u_32_t; # define U_32_T 1 #endif typedef struct ipf_rdx_mask { struct ipf_rdx_mask *next; struct ipf_rdx_node *node; u_32_t *mask; int maskbitcount; } ipf_rdx_mask_t; typedef struct ipf_rdx_node { struct ipf_rdx_node *left; struct ipf_rdx_node *right; struct ipf_rdx_node *parent; struct ipf_rdx_node *dupkey; struct ipf_rdx_mask *masks; struct ipf_rdx_mask *mymask; u_32_t *addrkey; u_32_t *maskkey; u_32_t *addroff; u_32_t *maskoff; u_32_t lastmask; u_32_t bitmask; int offset; int index; int maskbitcount; int root; #ifdef RDX_DEBUG char name[40]; #endif } ipf_rdx_node_t; struct ipf_rdx_head; typedef void (* radix_walk_func_t)(ipf_rdx_node_t *, void *); typedef ipf_rdx_node_t *(* idx_hamn_func_t)(struct ipf_rdx_head *, addrfamily_t *, addrfamily_t *, ipf_rdx_node_t *); typedef ipf_rdx_node_t *(* idx_ham_func_t)(struct ipf_rdx_head *, addrfamily_t *, addrfamily_t *); typedef ipf_rdx_node_t *(* idx_ha_func_t)(struct ipf_rdx_head *, addrfamily_t *); typedef void (* idx_walk_func_t)(struct ipf_rdx_head *, radix_walk_func_t, void *); typedef struct ipf_rdx_head { ipf_rdx_node_t *root; ipf_rdx_node_t nodes[3]; ipfmutex_t lock; idx_hamn_func_t addaddr; /* add addr/mask to tree */ idx_ham_func_t deladdr; /* delete addr/mask from tree */ idx_ham_func_t lookup; /* look for specific addr/mask */ idx_ha_func_t matchaddr; /* search tree for address match */ idx_walk_func_t walktree; /* walk entire tree */ } ipf_rdx_head_t; typedef struct radix_softc { u_char *zeros; u_char *ones; } radix_softc_t; #undef RADIX_NODE_HEAD_LOCK #undef RADIX_NODE_HEAD_UNLOCK #ifdef _KERNEL # define RADIX_NODE_HEAD_LOCK(x) MUTEX_ENTER(&(x)->lock) # define RADIX_NODE_HEAD_UNLOCK(x) MUTEX_UNLOCK(&(x)->lock) #else # define RADIX_NODE_HEAD_LOCK(x) # define RADIX_NODE_HEAD_UNLOCK(x) #endif -extern void *ipf_rx_create __P((void)); -extern int ipf_rx_init __P((void *)); -extern void ipf_rx_destroy __P((void *)); -extern int ipf_rx_inithead __P((radix_softc_t *, ipf_rdx_head_t **)); -extern void ipf_rx_freehead __P((ipf_rdx_head_t *)); -extern ipf_rdx_node_t *ipf_rx_addroute __P((ipf_rdx_head_t *, - addrfamily_t *, addrfamily_t *, - ipf_rdx_node_t *)); -extern ipf_rdx_node_t *ipf_rx_delete __P((ipf_rdx_head_t *, addrfamily_t *, - addrfamily_t *)); -extern void ipf_rx_walktree __P((ipf_rdx_head_t *, radix_walk_func_t, - void *)); +extern void *ipf_rx_create(void); +extern int ipf_rx_init(void *); +extern void ipf_rx_destroy(void *); +extern int ipf_rx_inithead(radix_softc_t *, ipf_rdx_head_t **); +extern void ipf_rx_freehead(ipf_rdx_head_t *); +extern ipf_rdx_node_t *ipf_rx_addroute(ipf_rdx_head_t *, + addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t *); +extern ipf_rdx_node_t *ipf_rx_delete(ipf_rdx_head_t *, addrfamily_t *, + addrfamily_t *); +extern void ipf_rx_walktree(ipf_rdx_head_t *, radix_walk_func_t, + void *); #endif /* __RADIX_IPF_H__ */ diff --git a/contrib/ipfilter/tools/ipf.c b/contrib/ipfilter/tools/ipf.c index 0551108488e0..406737e25d8e 100644 --- a/contrib/ipfilter/tools/ipf.c +++ b/contrib/ipfilter/tools/ipf.c @@ -1,577 +1,577 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include "ipf.h" #include #include #include #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 __P((const char *, int)); +extern char *index(const char *, int); #endif extern char *optarg; extern int optind; extern frentry_t *frtop; -void ipf_frsync __P((void)); -void zerostats __P((void)); -int main __P((int, char *[])); +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 __P((char *)); -static void flushfilter __P((char *, int *)); -static void set_state __P((u_int)); -static void showstats __P((friostat_t *)); -static void packetlogon __P((char *)); -static void swapactive __P((void)); -static int opendevice __P((char *, int)); -static void closedevice __P((void)); +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 __P((void)); -static int showversion __P((void)); -static int get_flags __P((void)); -static int ipf_interceptadd __P((int, ioctlfunc_t, void *)); +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 ]"); 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/contrib/ipfilter/tools/ipf_y.y b/contrib/ipfilter/tools/ipf_y.y index 2240a2a1c21f..3851f34bb693 100644 --- a/contrib/ipfilter/tools/ipf_y.y +++ b/contrib/ipfilter/tools/ipf_y.y @@ -1,2754 +1,2754 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ %{ #include "ipf.h" #include #include #include #ifdef IPFILTER_BPF # include #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 __P((char *)); -extern int yyparse __P((void)); -extern int yylex __P((void)); +extern void yyerror(char *); +extern int yyparse(void); +extern int yylex(void); extern int yydebug; extern FILE *yyin; extern int yylineNum; -static int addname __P((frentry_t **, char *)); -static frentry_t *addrule __P((void)); -static frentry_t *allocfr __P((void)); -static void build_dstaddr_af __P((frentry_t *, void *)); -static void build_srcaddr_af __P((frentry_t *, void *)); -static void dobpf __P((int, char *)); -static void doipfexpr __P((char *)); -static void do_tuneint __P((char *, int)); -static void do_tunestr __P((char *, char *)); -static void fillgroup __P((frentry_t *)); -static int lookuphost __P((char *, i6addr_t *)); -static u_int makehash __P((struct alist_s *)); -static int makepool __P((struct alist_s *)); -static struct alist_s *newalist __P((struct alist_s *)); -static void newrule __P((void)); -static void resetaddr __P((void)); -static void setgroup __P((frentry_t **, char *)); -static void setgrhead __P((frentry_t **, char *)); -static void seticmphead __P((frentry_t **, char *)); -static void setifname __P((frentry_t **, int, char *)); -static void setipftype __P((void)); -static void setsyslog __P((void)); -static void unsetsyslog __P((void)); +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 portnum %type facility priority icmpcode seclevel secname icmptype %type opt compare range opttype flagset optlist ipv6hdrlist ipv6hdr %type portc porteq ipmask maskopts %type ipv4 ipv4_16 ipv4_24 %type hostname %type addr ipaddr %type servicename name interfacename groupname %type portrange portcomp %type addrlist poollist %type onname %token YY_NUMBER YY_HEX %token 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 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]; sprintf(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]; sprintf(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]; sprintf(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, "="); sprintf(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/contrib/ipfilter/tools/ipfcomp.c b/contrib/ipfilter/tools/ipfcomp.c index d41faa44e3b3..505fde55cfa7 100644 --- a/contrib/ipfilter/tools/ipfcomp.c +++ b/contrib/ipfilter/tools/ipfcomp.c @@ -1,1374 +1,1374 @@ /* $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 __P((const void *, const void *)); -static void indent __P((FILE *, int)); -static void printeq __P((FILE *, char *, int, int, int)); -static void printipeq __P((FILE *, char *, int, int, int)); -static void addrule __P((FILE *, frentry_t *)); -static void printhooks __P((FILE *, int, int, frgroup_t *)); -static void emitheader __P((frgroup_t *, u_int, u_int)); -static void emitGroup __P((int, int, void *, frentry_t *, char *, - u_int, u_int)); -static void emittail __P((void)); -static void printCgroup __P((int, frentry_t *, mc_t *, char *)); +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; { fripf_t *ipf; 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; ipf = fr->fr_ipf; 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 \n"); fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); fprintf(fp, "#if (__FreeBSD_version >= 40000)\n"); fprintf(fp, "# if defined(_KERNEL)\n"); fprintf(fp, "# include \n"); fprintf(fp, "# else\n"); fprintf(fp, "# include \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 \n"); fprintf(fp, "# endif\n"); fprintf(fp, "#endif\n"); fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); fprintf(fp, "#if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux)\n"); fprintf(fp, "# include \n"); fprintf(fp, "#endif\n"); fprintf(fp, "#if defined(__FreeBSD__) && (__FreeBSD_version > 220000)\n"); fprintf(fp, "# include \n"); fprintf(fp, "#else\n"); fprintf(fp, "# include \n"); fprintf(fp, "#endif /* FreeBSD */\n"); fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); fprintf(fp, "#include \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 \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 __P((void));\n"); - fprintf(fph, "extern int ipfrule_remove __P((void));\n"); + 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 __P((fr_info_t *, u_32_t *));\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 __P((void));\n\ -extern int ipfrule_remove_in_%s __P((void));\n", grp->fg_name, grp->fg_name); +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 __P((fr_info_t *, u_32_t *));\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 __P((void));\n\ -extern int ipfrule_remove_out_%s __P((void));\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/contrib/ipfilter/tools/ipfs.c b/contrib/ipfilter/tools/ipfs.c index 7a2fe0252498..e9a535977bd9 100644 --- a/contrib/ipfilter/tools/ipfs.c +++ b/contrib/ipfilter/tools/ipfs.c @@ -1,872 +1,872 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include #include #include #include #include #if !defined(__SVR4) && !defined(__GNUC__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 __P((const char *, int)); +extern char *index(const char *, int); #endif extern char *optarg; extern int optind; -int main __P((int, char *[])); -void usage __P((void)); -int changestateif __P((char *, char *)); -int changenatif __P((char *, char *)); -int readstate __P((int, char *)); -int readnat __P((int, char *)); -int writestate __P((int, char *)); -int opendevice __P((char *)); -void closedevice __P((int)); -int setlock __P((int, int)); -int writeall __P((char *)); -int readall __P((char *)); -int writenat __P((int, char *)); +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 ] -R\n", progname); fprintf(stderr, "usage: %s [-nv] [-d ] -W\n", progname); fprintf(stderr, "usage: %s [-nNSv] [-f ] -r\n", progname); fprintf(stderr, "usage: %s [-nNSv] [-f ] -w\n", progname); fprintf(stderr, "usage: %s [-nNSv] -f -i ,\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/contrib/ipfilter/tools/ipfstat.c b/contrib/ipfilter/tools/ipfstat.c index 23ffb3832844..2165a671a9e9 100644 --- a/contrib/ipfilter/tools/ipfstat.c +++ b/contrib/ipfilter/tools/ipfstat.c @@ -1,2383 +1,2383 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include #include #include # include #include #if defined(sun) && defined(__SVR4) # include #endif #include "ipf.h" #include "netinet/ipl.h" #if defined(STATETOP) # if defined(sun) && defined(__SVR4) # include # endif # include # include # include # include # include # if SOLARIS || defined(__NetBSD__) # ifdef ERR # undef ERR # endif # include # else /* SOLARIS */ # include # endif /* SOLARIS */ #endif /* STATETOP */ #include "kmem.h" #if defined(__NetBSD__) # include #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 __P((int, char *[])); - -static int fetchfrag __P((int, int, ipfr_t *)); -static void showstats __P((friostat_t *, u_32_t)); -static void showfrstates __P((ipfrstat_t *, u_long)); -static void showlist __P((friostat_t *)); -static void showstatestats __P((ips_stat_t *)); -static void showipstates __P((ips_stat_t *, int *)); -static void showauthstates __P((ipf_authstat_t *)); -static void showtqtable_live __P((int)); -static void showgroups __P((friostat_t *)); -static void usage __P((char *)); -static int state_matcharray __P((ipstate_t *, int *)); -static int printlivelist __P((friostat_t *, int, int, frentry_t *, - char *, char *)); -static void printdeadlist __P((friostat_t *, int, int, frentry_t *, - char *, char *)); -static void printside __P((char *, ipf_statistics_t *)); -static void parse_ipportstr __P((const char *, i6addr_t *, int *)); -static void ipfstate_live __P((char *, friostat_t **, ips_stat_t **, - ipfrstat_t **, ipf_authstat_t **, u_32_t *)); -static void ipfstate_dead __P((char *, friostat_t **, ips_stat_t **, - ipfrstat_t **, ipf_authstat_t **, u_32_t *)); -static ipstate_t *fetchstate __P((ipstate_t *, ipstate_t *)); +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 __P((i6addr_t, i6addr_t, int, int, int, - int, int, int, int *)); -static void sig_break __P((int)); -static void sig_resize __P((int)); -static char *getip __P((int, i6addr_t *)); -static char *ttl_to_string __P((long)); -static int sort_p __P((const void *, const void *)); -static int sort_pkts __P((const void *, const void *)); -static int sort_bytes __P((const void *, const void *)); -static int sort_ttl __P((const void *, const void *)); -static int sort_srcip __P((const void *, const void *)); -static int sort_srcpt __P((const void *, const void *)); -static int sort_dstip __P((const void *, const void *)); -static int sort_dstpt __P((const void *, const void *)); +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); sprintf(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) sprintf(str1, "%s,%d", getip(ver, &saddr), sport); else sprintf(str1, "%s", getip(ver, &saddr)); if (dport >= 0) sprintf(str2, "%s,%d", getip(ver, &daddr), dport); else sprintf(str2, "%s", getip(ver, &daddr)); if (protocol < 0) strcpy(str3, "any"); else if ((proto = getprotobynumber(protocol)) != NULL) sprintf(str3, "%s", proto->p_name); else sprintf(str3, "%d", protocol); switch (sorting) { case STSORT_PR: sprintf(str4, "proto"); break; case STSORT_PKTS: sprintf(str4, "# pkts"); break; case STSORT_BYTES: sprintf(str4, "# bytes"); break; case STSORT_TTL: sprintf(str4, "ttl"); break; case STSORT_SRCIP: sprintf(str4, "src ip"); break; case STSORT_SRCPT: sprintf(str4, "src port"); break; case STSORT_DSTIP: sprintf(str4, "dest ip"); break; case STSORT_DSTPT: sprintf(str4, "dest port"); break; default: sprintf(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)) { sprintf(str1, "%s,%hu", getip(tp->st_v, &tp->st_src), ntohs(tp->st_sport)); sprintf(str2, "%s,%hu", getip(tp->st_v, &tp->st_dst), ntohs(tp->st_dport)); } else { sprintf(str1, "%s", getip(tp->st_v, &tp->st_src)); sprintf(str2, "%s", getip(tp->st_v, &tp->st_dst)); } winy++; move(winy, 0); printw("%-*s %-*s", srclen + 6, str1, dstlen + 6, str2); /* print state */ sprintf(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 { sprintf(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) sprintf(ttlbuf, "%2d:%02d:%02d", hours, minutes, seconds); else sprintf(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/contrib/ipfilter/tools/ipfsyncd.c b/contrib/ipfilter/tools/ipfsyncd.c index d4671e409d63..0ccc15542f6b 100644 --- a/contrib/ipfilter/tools/ipfsyncd.c +++ b/contrib/ipfilter/tools/ipfsyncd.c @@ -1,671 +1,671 @@ /* * 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: ipfsyncd.c,v 1.1.2.2 2012/07/22 08:04:24 darren_r Exp $"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipf.h" #include "opts.h" #define R_IO_ERROR -1 #define R_OKAY 0 #define R_MORE 1 #define R_SKIP 2 #if defined(sun) && !defined(SOLARIS2) # define STRERROR(x) sys_errlist[x] extern char *sys_errlist[]; #else # define STRERROR(x) strerror(x) #endif -int main __P((int, char *[])); -void usage __P((char *)); -void printsynchdr __P((synchdr_t *)); -void printtable __P((int)); -void printsmcproto __P((char *)); -void printcommand __P((int)); -int do_kbuff __P((int, char *, int *)); -int do_packet __P((int, char *)); -int buildsocket __P((char *, struct sockaddr_in *)); -void do_io __P((void)); -void handleterm __P((int)); +int main(int, char *[]); +void usage(char *); +void printsynchdr(synchdr_t *); +void printtable(int); +void printsmcproto(char *); +void printcommand(int); +int do_kbuff(int, char *, int *); +int do_packet(int, char *); +int buildsocket(char *, struct sockaddr_in *); +void do_io(void); +void handleterm(int); int terminate = 0; int igmpfd = -1; int nfd = -1; int lfd = -1; int opts = 0; void usage(progname) char *progname; { fprintf(stderr, "Usage: %s [-d] [-p port] [-i address] -I \n", progname); } void handleterm(sig) int sig; { terminate = sig; } /* should be large enough to hold header + any datatype */ #define BUFFERLEN 1400 int main(argc, argv) int argc; char *argv[]; { struct sockaddr_in sin; char *interface; char *progname; int opt, tries; progname = strrchr(argv[0], '/'); if (progname) { progname++; } else { progname = argv[0]; } opts = 0; tries = 0; interface = NULL; bzero((char *)&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(0xaf6c); sin.sin_addr.s_addr = htonl(INADDR_UNSPEC_GROUP | 0x697066); while ((opt = getopt(argc, argv, "di:I:p:")) != -1) switch (opt) { case 'd' : debuglevel++; break; case 'I' : interface = optarg; break; case 'i' : sin.sin_addr.s_addr = inet_addr(optarg); break; case 'p' : sin.sin_port = htons(atoi(optarg)); break; } if (interface == NULL) { usage(progname); exit(1); } if (!debuglevel) { #if BSD >= 199306 daemon(0, 0); #else int fd = open("/dev/null", O_RDWR); switch (fork()) { case 0 : break; case -1 : fprintf(stderr, "%s: fork() failed: %s\n", argv[0], STRERROR(errno)); exit(1); /* NOTREACHED */ default : exit(0); /* NOTREACHED */ } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); close(fd); setsid(); #endif } signal(SIGHUP, handleterm); signal(SIGINT, handleterm); signal(SIGTERM, handleterm); openlog(progname, LOG_PID, LOG_SECURITY); while (!terminate) { if (lfd != -1) { close(lfd); lfd = -1; } if (nfd != -1) { close(nfd); nfd = -1; } if (igmpfd != -1) { close(igmpfd); igmpfd = -1; } if (buildsocket(interface, &sin) == -1) goto tryagain; lfd = open(IPSYNC_NAME, O_RDWR); if (lfd == -1) { syslog(LOG_ERR, "open(%s):%m", IPSYNC_NAME); debug(1, "open(%s): %s\n", IPSYNC_NAME, STRERROR(errno)); goto tryagain; } tries = -1; do_io(); tryagain: tries++; syslog(LOG_INFO, "retry in %d seconds", 1 << tries); debug(1, "wait %d seconds\n", 1 << tries); sleep(1 << tries); } /* terminate */ if (lfd != -1) close(lfd); if (nfd != -1) close(nfd); syslog(LOG_ERR, "signal %d received, exiting...", terminate); debug(1, "signal %d received, exiting...", terminate); exit(1); } void do_io() { char nbuff[BUFFERLEN]; char buff[BUFFERLEN]; fd_set mrd, rd; int maxfd; int inbuf; int n1; int left; FD_ZERO(&mrd); FD_SET(lfd, &mrd); FD_SET(nfd, &mrd); maxfd = nfd; if (lfd > maxfd) maxfd = lfd; debug(2, "nfd %d lfd %d maxfd %d\n", nfd, lfd, maxfd); inbuf = 0; /* * A threaded approach to this loop would have one thread * work on reading lfd (only) all the time and another thread * working on reading nfd all the time. */ while (!terminate) { int n; rd = mrd; n = select(maxfd + 1, &rd, NULL, NULL, NULL); if (n < 0) { switch (errno) { case EINTR : continue; default : syslog(LOG_ERR, "select error: %m"); debug(1, "select error: %s\n", STRERROR(errno)); return; } } if (FD_ISSET(lfd, &rd)) { n1 = read(lfd, buff+inbuf, BUFFERLEN-inbuf); debug(3, "read(K):%d\n", n1); if (n1 <= 0) { syslog(LOG_ERR, "read error (k-header): %m"); debug(1, "read error (k-header): %s\n", STRERROR(errno)); return; } left = 0; switch (do_kbuff(n1, buff, &left)) { case R_IO_ERROR : return; case R_MORE : inbuf += left; break; default : inbuf = 0; break; } } if (FD_ISSET(nfd, &rd)) { n1 = recv(nfd, nbuff, sizeof(nbuff), 0); debug(3, "read(N):%d\n", n1); if (n1 <= 0) { syslog(LOG_ERR, "read error (n-header): %m"); debug(1, "read error (n-header): %s\n", STRERROR(errno)); return; } switch (do_packet(n1, nbuff)) { case R_IO_ERROR : return; default : break; } } } } int buildsocket(nicname, sinp) char *nicname; struct sockaddr_in *sinp; { struct sockaddr_in *reqip; struct ifreq req; char opt; debug(2, "binding to %s:%s\n", nicname, inet_ntoa(sinp->sin_addr)); if (IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) { struct in_addr addr; struct ip_mreq mreq; igmpfd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); if (igmpfd == -1) { syslog(LOG_ERR, "socket:%m"); debug(1, "socket:%s\n", STRERROR(errno)); return -1; } bzero((char *)&req, sizeof(req)); strncpy(req.ifr_name, nicname, sizeof(req.ifr_name)); req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; if (ioctl(igmpfd, SIOCGIFADDR, &req) == -1) { syslog(LOG_ERR, "ioctl(SIOCGIFADDR):%m"); debug(1, "ioctl(SIOCGIFADDR):%s\n", STRERROR(errno)); close(igmpfd); igmpfd = -1; return -1; } reqip = (struct sockaddr_in *)&req.ifr_addr; addr = reqip->sin_addr; if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr, sizeof(addr)) == -1) { syslog(LOG_ERR, "setsockopt(IP_MULTICAST_IF(%s)):%m", inet_ntoa(addr)); debug(1, "setsockopt(IP_MULTICAST_IF(%s)):%s\n", inet_ntoa(addr), STRERROR(errno)); close(igmpfd); igmpfd = -1; return -1; } opt = 0; if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&opt, sizeof(opt)) == -1) { syslog(LOG_ERR, "setsockopt(IP_MULTICAST_LOOP=0):%m"); debug(1, "setsockopt(IP_MULTICAST_LOOP=0):%s\n", STRERROR(errno)); close(igmpfd); igmpfd = -1; return -1; } opt = 63; if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&opt, sizeof(opt)) == -1) { syslog(LOG_ERR, "setsockopt(IP_MULTICAST_TTL=%d):%m", opt); debug(1, "setsockopt(IP_MULTICAST_TTL=%d):%s\n", opt, STRERROR(errno)); close(igmpfd); igmpfd = -1; return -1; } mreq.imr_multiaddr.s_addr = sinp->sin_addr.s_addr; mreq.imr_interface.s_addr = reqip->sin_addr.s_addr; if (setsockopt(igmpfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) { char buffer[80]; sprintf(buffer, "%s,", inet_ntoa(sinp->sin_addr)); strcat(buffer, inet_ntoa(reqip->sin_addr)); syslog(LOG_ERR, "setsockpt(IP_ADD_MEMBERSHIP,%s):%m", buffer); debug(1, "setsockpt(IP_ADD_MEMBERSHIP,%s):%s\n", buffer, STRERROR(errno)); close(igmpfd); igmpfd = -1; return -1; } } nfd = socket(AF_INET, SOCK_DGRAM, 0); if (nfd == -1) { syslog(LOG_ERR, "socket:%m"); if (igmpfd != -1) { close(igmpfd); igmpfd = -1; } return -1; } bzero((char *)&req, sizeof(req)); strncpy(req.ifr_name, nicname, sizeof(req.ifr_name)); req.ifr_name[sizeof(req.ifr_name) - 1] = '\0'; if (ioctl(nfd, SIOCGIFADDR, &req) == -1) { syslog(LOG_ERR, "ioctl(SIOCGIFADDR):%m"); debug(1, "ioctl(SIOCGIFADDR):%s\n", STRERROR(errno)); close(igmpfd); igmpfd = -1; return -1; } if (bind(nfd, (struct sockaddr *)&req.ifr_addr, sizeof(req.ifr_addr)) == -1) { syslog(LOG_ERR, "bind:%m"); debug(1, "bind:%s\n", STRERROR(errno)); close(nfd); if (igmpfd != -1) { close(igmpfd); igmpfd = -1; } nfd = -1; return -1; } if (connect(nfd, (struct sockaddr *)sinp, sizeof(*sinp)) == -1) { syslog(LOG_ERR, "connect:%m"); debug(1, "connect:%s\n", STRERROR(errno)); close(nfd); if (igmpfd != -1) { close(igmpfd); igmpfd = -1; } nfd = -1; return -1; } syslog(LOG_INFO, "Sending data to %s", inet_ntoa(sinp->sin_addr)); debug(3, "Sending data to %s\n", inet_ntoa(sinp->sin_addr)); return nfd; } int do_packet(pklen, buff) int pklen; char *buff; { synchdr_t *sh; u_32_t magic; int len; int n2; int n3; while (pklen > 0) { if (pklen < sizeof(*sh)) { syslog(LOG_ERR, "packet length too short:%d", pklen); debug(2, "packet length too short:%d\n", pklen); return R_SKIP; } sh = (synchdr_t *)buff; len = ntohl(sh->sm_len); magic = ntohl(sh->sm_magic); if (magic != SYNHDRMAGIC) { syslog(LOG_ERR, "invalid header magic %x", magic); debug(2, "invalid header magic %x\n", magic); return R_SKIP; } if (pklen < len + sizeof(*sh)) { syslog(LOG_ERR, "packet length too short:%d", pklen); debug(2, "packet length too short:%d\n", pklen); return R_SKIP; } if (debuglevel > 3) { printsynchdr(sh); printcommand(sh->sm_cmd); printtable(sh->sm_table); printsmcproto(buff); } n2 = sizeof(*sh) + len; do { n3 = write(lfd, buff, n2); if (n3 <= 0) { syslog(LOG_ERR, "write error: %m"); debug(1, "write error: %s\n", STRERROR(errno)); return R_IO_ERROR; } n2 -= n3; buff += n3; pklen -= n3; } while (n3 != 0); } return R_OKAY; } int do_kbuff(inbuf, buf, left) int inbuf, *left; char *buf; { synchdr_t *sh; u_32_t magic; int complete; int sendlen; int error; int bytes; int len; int n2; int n3; sendlen = 0; bytes = inbuf; error = R_OKAY; sh = (synchdr_t *)buf; for (complete = 0; bytes > 0; complete++) { len = ntohl(sh->sm_len); magic = ntohl(sh->sm_magic); if (magic != SYNHDRMAGIC) { syslog(LOG_ERR, "read invalid header magic 0x%x, flushing", magic); debug(2, "read invalid header magic 0x%x, flushing\n", magic); n2 = SMC_RLOG; (void) ioctl(lfd, SIOCIPFFL, &n2); break; } if (debuglevel > 3) { printsynchdr(sh); printcommand(sh->sm_cmd); printtable(sh->sm_table); putchar('\n'); } if (bytes < sizeof(*sh) + len) { debug(3, "Not enough bytes %d < %d\n", bytes, sizeof(*sh) + len); error = R_MORE; break; } if (debuglevel > 3) { printsmcproto(buf); } sendlen += len + sizeof(*sh); sh = (synchdr_t *)(buf + sendlen); bytes -= sendlen; } if (complete) { n3 = send(nfd, buf, sendlen, 0); if (n3 <= 0) { syslog(LOG_ERR, "write error: %m"); debug(1, "write error: %s\n", STRERROR(errno)); return R_IO_ERROR; } debug(3, "send on %d len %d = %d\n", nfd, sendlen, n3); error = R_OKAY; } /* move buffer to the front,we might need to make * this more efficient, by using a rolling pointer * over the buffer and only copying it, when * we are reaching the end */ if (bytes > 0) { bcopy(buf + bytes, buf, bytes); error = R_MORE; } debug(4, "complete %d bytes %d error %d\n", complete, bytes, error); *left = bytes; return error; } void printcommand(cmd) int cmd; { switch (cmd) { case SMC_CREATE : printf(" cmd:CREATE"); break; case SMC_UPDATE : printf(" cmd:UPDATE"); break; default : printf(" cmd:Unknown(%d)", cmd); break; } } void printtable(table) int table; { switch (table) { case SMC_NAT : printf(" table:NAT"); break; case SMC_STATE : printf(" table:STATE"); break; default : printf(" table:Unknown(%d)", table); break; } } void printsmcproto(buff) char *buff; { syncupdent_t *su; synchdr_t *sh; sh = (synchdr_t *)buff; if (sh->sm_cmd == SMC_CREATE) { ; } else if (sh->sm_cmd == SMC_UPDATE) { su = (syncupdent_t *)buff; if (sh->sm_p == IPPROTO_TCP) { printf(" TCP Update: age %lu state %d/%d\n", su->sup_tcp.stu_age, su->sup_tcp.stu_state[0], su->sup_tcp.stu_state[1]); } } else { printf("Unknown command\n"); } } void printsynchdr(sh) synchdr_t *sh; { printf("v:%d p:%d num:%d len:%d magic:%x", sh->sm_v, sh->sm_p, ntohl(sh->sm_num), ntohl(sh->sm_len), ntohl(sh->sm_magic)); } diff --git a/contrib/ipfilter/tools/ipftest.c b/contrib/ipfilter/tools/ipftest.c index f9d45f71cbb2..d7f97c1fa66b 100644 --- a/contrib/ipfilter/tools/ipftest.c +++ b/contrib/ipfilter/tools/ipftest.c @@ -1,716 +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 #include #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 __P((char *, int)); -extern void init_ifp __P((void)); -extern ipnat_t *natparse __P((char *, int)); +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 __P((int, char *[])); -int loadrules __P((char *, int)); -int kmemcpy __P((char *, long, int)); -int kstrncpy __P((char *, long, int n)); +int main(int, char *[]); +int loadrules(char *, int); +int kmemcpy(char *, long, int); +int kstrncpy(char *, long, int n); int blockreason; -void dumpnat __P((void *)); -void dumpgroups __P((ipf_main_softc_t *)); -void dumprules __P((frentry_t *)); -void drain_log __P((char *)); -void fixv4sums __P((mb_t *, ip_t *)); - -int ipftestioctl __P((int, ioctlcmd_t, ...)); -int ipnattestioctl __P((int, ioctlcmd_t, ...)); -int ipstatetestioctl __P((int, ioctlcmd_t, ...)); -int ipauthtestioctl __P((int, ioctlcmd_t, ...)); -int ipscantestioctl __P((int, ioctlcmd_t, ...)); -int ipsynctestioctl __P((int, ioctlcmd_t, ...)); -int ipooltestioctl __P((int, ioctlcmd_t, ...)); +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/contrib/ipfilter/tools/ipmon.c b/contrib/ipfilter/tools/ipmon.c index d7ad1a228378..005cd316f91a 100644 --- a/contrib/ipfilter/tools/ipmon.c +++ b/contrib/ipfilter/tools/ipmon.c @@ -1,1872 +1,1872 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include "ipf.h" #include "ipmon.h" #include #include #include #include #include #include #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", }; #ifdef MENTAT 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 __P((char *)); -static void handlehup __P((int)); -static void flushlogs __P((char *, FILE *)); -static void print_log __P((config_t *, logsource_t *, char *, int)); -static void print_ipflog __P((config_t *, char *, int)); -static void print_natlog __P((config_t *, char *, int)); -static void print_statelog __P((config_t *, char *, int)); -static int read_log __P((int, int *, char *, int)); -static void write_pid __P((char *)); -static char *icmpname __P((u_int, u_int)); -static char *icmpname6 __P((u_int, u_int)); -static icmp_type_t *find_icmptype __P((int, icmp_type_t *, size_t)); -static icmp_subtype_t *find_icmpsubtype __P((int, icmp_subtype_t *, size_t)); -static struct tm *get_tm __P((time_t)); - -char *portlocalname __P((int, char *, u_int)); -int main __P((int, char *[])); - -static void logopts __P((int, char *)); -static void init_tabs __P((void)); -static char *getlocalproto __P((u_int)); -static void openlogs __P((config_t *conf)); -static int read_loginfo __P((config_t *conf)); -static void initconfig __P((config_t *conf)); +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) { sprintf(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; sprintf(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) sprintf(name, "icmptype(%d)/", type); else sprintf(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 sprintf(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) sprintf(name, "icmpv6type(%d)/", type); else sprintf(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 sprintf(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); sprintf(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 : sprintf(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 : sprintf(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) { sprintf(t, "%s,%s <- -> ", hostname(family, nl->nl_osrcip.i6), portlocalname(res, proto, (u_int)nl->nl_osrcport)); t += strlen(t); sprintf(t, "%s,%s ", hostname(family, nl->nl_nsrcip.i6), portlocalname(res, proto, (u_int)nl->nl_nsrcport)); t += strlen(t); sprintf(t, "[%s,%s] ", hostname(family, nl->nl_odstip.i6), portlocalname(res, proto, (u_int)nl->nl_odstport)); } else { sprintf(t, "%s,%s ", hostname(family, nl->nl_osrcip.i6), portlocalname(res, proto, (u_int)nl->nl_osrcport)); t += strlen(t); sprintf(t, "%s,%s <- -> ", hostname(family, nl->nl_odstip.i6), portlocalname(res, proto, (u_int)nl->nl_odstport)); t += strlen(t); sprintf(t, "%s,%s ", hostname(family, nl->nl_nsrcip.i6), portlocalname(res, proto, (u_int)nl->nl_nsrcport)); t += strlen(t); sprintf(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 sprintf(t, " Pkts %" PRId64 "/%" PRId64 " Bytes %" PRId64 "/%" PRId64, # else sprintf(t, " Pkts %qd/%qd Bytes %qd/%qd", # endif #else sprintf(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); sprintf(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 : sprintf(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) { sprintf(t, "%s,%s -> ", hostname(family, (u_32_t *)&sl->isl_src), portlocalname(res, proto, (u_int)sl->isl_sport)); t += strlen(t); sprintf(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) { sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); t += strlen(t); sprintf(t, "%s PR icmp %d", hostname(family, (u_32_t *)&sl->isl_dst), sl->isl_itype); } else if (sl->isl_p == IPPROTO_ICMPV6) { sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); t += strlen(t); sprintf(t, "%s PR icmpv6 %d", hostname(family, (u_32_t *)&sl->isl_dst), sl->isl_itype); } else { sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src)); t += strlen(t); sprintf(t, "%s PR %s", hostname(family, (u_32_t *)&sl->isl_dst), proto); } t += strlen(t); if (sl->isl_tag != FR_NOLOGTAG) { sprintf(t, " tag %u", sl->isl_tag); t += strlen(t); } if (sl->isl_type != ISL_NEW) { sprintf(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); sprintf(t, ".%-.6ld ", (long)ipl->ipl_usec); t += strlen(t); if (ipl->ipl_count > 1) { sprintf(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'; sprintf(t, "%s", ifname); t += strlen(t); # if defined(MENTAT) if (ISALPHA(*(t - 1))) { sprintf(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 sprintf(t, " @%s:", ipf->fl_group); t += strlen(t); if (ipf->fl_rule == 0xffffffff) strcat(t, "-1 "); else sprintf(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)) { sprintf(t, "%s,%s -> ", hostname(f, s), portlocalname(res, proto, (u_int)tp->th_sport)); t += strlen(t); sprintf(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) { sprintf(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 { sprintf(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); sprintf(t, "%s -> ", hostname(f, s)); t += strlen(t); sprintf(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); sprintf(t, "%s -> ", hostname(f, s)); t += strlen(t); sprintf(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); sprintf(t, " for %s,%s -", HOSTNAMEV4(ipc->ip_src), portlocalname(res, proto, (u_int)tp->th_sport)); t += strlen(t); sprintf(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); sprintf(t, " for %s -", HOSTNAMEV4(ipc->ip_src)); t += strlen(t); sprintf(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); sprintf(t, " for %s -", HOSTNAMEV4(ipc->ip_src)); t += strlen(t); sprintf(t, " %s PR %s len %hu (%hu)", HOSTNAMEV4(ipc->ip_dst), proto, IP_HL(ipc) << 2, i); t += strlen(t); if (ipoff & IP_OFFMASK) { sprintf(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 { sprintf(t, "%s -> ", hostname(f, s)); t += strlen(t); sprintf(t, "%s PR %s len %hu (%hu)", hostname(f, d), proto, hl, plen); t += strlen(t); if (off & IP_OFFMASK) sprintf(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) { sprintf(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 ] [ -C ]\n" "\t[ -f ] [ -L ] [ -N ]\n" "\t[ -o [NSI] ] [ -O [NSI] ] [ -P ] [ -S ]\n" "\t[ ]\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))) { #if BSD >= 199306 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/contrib/ipfilter/tools/ipmon_y.y b/contrib/ipfilter/tools/ipmon_y.y index f14180d6a9e9..0aeb20a32519 100644 --- a/contrib/ipfilter/tools/ipmon_y.y +++ b/contrib/ipfilter/tools/ipmon_y.y @@ -1,1052 +1,1052 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ %{ #include "ipf.h" #include #undef OPT_NAT #undef OPT_VERBOSE #include "ipmon_l.h" #include "ipmon.h" #include #define YYDEBUG 1 -extern void yyerror __P((char *)); -extern int yyparse __P((void)); -extern int yylex __P((void)); +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 __P((opt_t *, ipmon_doing_t *)); -static opt_t *new_opt __P((int)); -static void free_action __P((ipmon_action_t *)); -static void print_action __P((ipmon_action_t *)); -static int find_doing __P((char *)); -static ipmon_doing_t *build_doing __P((char *, char *)); -static void print_match __P((ipmon_action_t *)); -static int install_saver __P((char *, char *)); +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 YY_NUMBER YY_HEX %token YY_STR %token 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 ipv4 %type direction dstip dstport every group interface %type protocol result rule srcip srcport logtag matching %type matchopt nattag type %type typeopt %type 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 sprintf(a->ac_group, "%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/contrib/ipfilter/tools/ipnat.c b/contrib/ipfilter/tools/ipnat.c index 7d76893cfd2c..1ca6e776ffdf 100644 --- a/contrib/ipfilter/tools/ipnat.c +++ b/contrib/ipfilter/tools/ipnat.c @@ -1,842 +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 #include #include #include #include #if !defined(__SVR4) #include #else #include #endif #include #include #include #include #include #include #define _KERNEL #include #undef _KERNEL #include #include #if defined(sun) && defined(__SVR4) # include # include #endif #include #include #include #include #include #include #include #include #include #include # include #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 __P((int, natstat_t *, int, int, int *)); -void dotable __P((natstat_t *, int, int, int, char *)); -void flushtable __P((int, int, int *)); -void usage __P((char *)); -int main __P((int, char*[])); -void showhostmap __P((natstat_t *nsp)); -void natstat_dead __P((natstat_t *, char *)); -void dostats_live __P((int, natstat_t *, int, int *)); -void showhostmap_dead __P((natstat_t *)); -void showhostmap_live __P((int, natstat_t *)); -void dostats_dead __P((natstat_t *, int, int *)); -int nat_matcharray __P((nat_t *, int *)); +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/contrib/ipfilter/tools/ipnat_y.y b/contrib/ipfilter/tools/ipnat_y.y index e24641306634..30e888d8b490 100644 --- a/contrib/ipfilter/tools/ipnat_y.y +++ b/contrib/ipfilter/tools/ipnat_y.y @@ -1,1773 +1,1773 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ %{ #include #include #include #include #include #if !defined(__SVR4) && !defined(__GNUC__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipf.h" #include "netinet/ipl.h" #include "ipnat_l.h" #define YYDEBUG 1 -extern void yyerror __P((char *)); -extern int yyparse __P((void)); -extern int yylex __P((void)); +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 __P((void)); -static void setnatproto __P((int)); -static void setmapifnames __P((void)); -static void setrdrifnames __P((void)); -static void proxy_setconfig __P((int)); -static void proxy_unsetconfig __P((void)); -static namelist_t *proxy_dns_add_pass __P((char *, char *)); -static namelist_t *proxy_dns_add_block __P((char *, char *)); -static void proxy_addconfig __P((char *, int, char *, namelist_t *)); -static void proxy_loadconfig __P((int, ioctlfunc_t, char *, int, - char *, namelist_t *)); -static void proxy_loadrules __P((int, ioctlfunc_t, proxyrule_t *)); -static void setmapifnames __P((void)); -static void setrdrifnames __P((void)); -static void setifname __P((ipnat_t **, int, char *)); -static int addname __P((ipnat_t **, char *)); +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 YY_NUMBER YY_HEX %token 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 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 portspec %type hexnumber compare range proto %type saddr daddr sobject dobject mapfrom rdrfrom dip %type hostname ipv4 ipaddr %type addr rhsaddr rhdaddr erhdaddr %type portstuff portpair comaports srcports dstports %type 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]; sprintf(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]; sprintf(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]; sprintf(msg, "%d:ioctl(add/insert nat rule)", ipn->in_flineno); if (errno == EEXIST) { sprintf(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]; sprintf(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/contrib/ipfilter/tools/ippool.c b/contrib/ipfilter/tools/ippool.c index 34800d92defa..62d096cef88d 100644 --- a/contrib/ipfilter/tools/ippool.c +++ b/contrib/ipfilter/tools/ippool.c @@ -1,1157 +1,1157 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include #include #include #include # include #include #include #include #include #include #include #include #include #include #include #include # include #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 __P((void)); +extern int ippool_yyparse(void); extern int ippool_yydebug; extern FILE *ippool_yyin; extern char *optarg; extern int lineNum; -void usage __P((char *)); -int main __P((int, char **)); -int poolcommand __P((int, int, char *[])); -int poolnodecommand __P((int, int, char *[])); -int loadpoolfile __P((int, char *[], char *)); -int poollist __P((int, char *[])); -void poollist_dead __P((int, char *, int, char *, char *)); -void poollist_live __P((int, char *, int, int)); -int poolflush __P((int, char *[])); -int poolstats __P((int, char *[])); -int gettype __P((char *, u_int *)); -int getrole __P((char *)); -int setnodeaddr __P((int, int, void *ptr, char *arg)); -void showpools_live __P((int, int, ipf_pool_stat_t *, char *)); -void showhashs_live __P((int, int, iphtstat_t *, char *)); -void showdstls_live __P((int, int, ipf_dstl_stat_t *, char *)); +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 [-o ] [-t type] [-T ttl] -i [/netmask]\n"); fprintf(stderr, "\t-A [-dnv] [-m ] [-o ] [-S ] [-t ]\n"); fprintf(stderr, "\t-f [-dnuvR]\n"); fprintf(stderr, "\t-F [-dv] [-o ] [-t ]\n"); fprintf(stderr, "\t-l [-dv] [-m ] [-t ] [-o ] [-M ] [-N ]\n"); fprintf(stderr, "\t-r [-dnv] [-m ] [-o ] [-t type] -i [/netmask]\n"); fprintf(stderr, "\t-R [-dnv] [-m ] [-o ] [-t ]\n"); fprintf(stderr, "\t-s [-dtv] [-M ] [-N ]\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, live_kernel; ipf_pool_stat_t plstat; ipf_dstl_stat_t dlstat; char *kernel, *core; iphtstat_t htstat; iplookupop_t op; core = NULL; kernel = NULL; live_kernel = 1; 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 '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; 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/contrib/ipfilter/tools/ippool_y.y b/contrib/ipfilter/tools/ippool_y.y index 02ae5c22141a..741ae2db7466 100644 --- a/contrib/ipfilter/tools/ippool_y.y +++ b/contrib/ipfilter/tools/ippool_y.y @@ -1,832 +1,832 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ %{ #include #include #include #include # include #include #include #include #include #include #include #include #include #include #include #include #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 __P((void)); +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 __P((char *)); -static ip_pool_node_t *add_poolhosts __P((char *)); -static ip_pool_node_t *read_whoisfile __P((char *)); -static void setadflen __P((addrfamily_t *)); +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 YY_NUMBER YY_HEX %token YY_STR %token 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 role table inout unit dstopts weighting %type ipftree range addrlist %type addrmask %type ipfgroup ipfhash hashlist hashentry %type groupentry setgrouplist grouplist %type ipaddr mask %type ipv4 %type number setgroup name %type 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 { sprintf(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]; sprintf(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]; sprintf(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/contrib/ipfilter/tools/ipscan_y.y b/contrib/ipfilter/tools/ipscan_y.y index d323f054bd27..b36b66db2b97 100644 --- a/contrib/ipfilter/tools/ipscan_y.y +++ b/contrib/ipfilter/tools/ipscan_y.y @@ -1,572 +1,572 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ %{ #include #include #include "ipf.h" #include "opts.h" #include "kmem.h" #include "ipscan_l.h" #include "netinet/ip_scan.h" #include #define YYDEBUG 1 extern char *optarg; -extern void yyerror __P((char *)); -extern int yyparse __P((void)); -extern int yylex __P((void)); +extern void yyerror(char *); +extern int yyparse(void); +extern int yylex(void); extern int yydebug; extern FILE *yyin; extern int yylineNum; -extern void printbuf __P((char *, int, int)); - - -void printent __P((ipscan_t *)); -void showlist __P((void)); -int getportnum __P((char *)); -struct in_addr gethostip __P((char *)); -struct in_addr combine __P((int, int, int, int)); -char **makepair __P((char *, char *)); -void addtag __P((char *, char **, char **, struct action *)); -int cram __P((char *, char *)); -void usage __P((char *)); -int main __P((int, char **)); +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 tag %type action redirect result %type ipaddr %type portnum %type matchup onehalf twohalves %token YY_NUMBER YY_HEX %token 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 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 \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/contrib/ipfilter/tools/ipsyncm.c b/contrib/ipfilter/tools/ipsyncm.c index 41513fae3bcc..d57196379210 100644 --- a/contrib/ipfilter/tools/ipsyncm.c +++ b/contrib/ipfilter/tools/ipsyncm.c @@ -1,256 +1,256 @@ /* $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 #include #include #include #include #include #include #include #include #include #include #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_state.h" #include "netinet/ip_sync.h" -int main __P((int, char *[])); -void usage __P((const char *)); +int main(int, char *[]); +void usage(const char *); int terminate = 0; void usage(const char *progname) { fprintf(stderr, "Usage: %s \n", progname); } #if 0 static void handleterm(int sig) { terminate = sig; } #endif /* should be large enough to hold header + any datatype */ #define BUFFERLEN 1400 int main(argc, argv) int argc; char *argv[]; { struct sockaddr_in sin; char buff[BUFFERLEN]; synclogent_t *sl; syncupdent_t *su; int nfd = -1, lfd = -1, n1, n2, n3, len; int inbuf; u_32_t magic; synchdr_t *sh; char *progname; progname = strrchr(argv[0], '/'); if (progname) { progname++; } else { progname = argv[0]; } if (argc < 2) { usage(progname); exit(1); } #if 0 signal(SIGHUP, handleterm); signal(SIGINT, handleterm); signal(SIGTERM, handleterm); #endif openlog(progname, LOG_PID, LOG_SECURITY); bzero((char *)&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(argv[1]); if (argc > 2) sin.sin_port = htons(atoi(argv[2])); else sin.sin_port = htons(43434); while (1) { if (lfd != -1) close(lfd); if (nfd != -1) close(nfd); lfd = open(IPSYNC_NAME, O_RDONLY); if (lfd == -1) { syslog(LOG_ERR, "Opening %s :%m", IPSYNC_NAME); goto tryagain; } nfd = socket(AF_INET, SOCK_DGRAM, 0); if (nfd == -1) { syslog(LOG_ERR, "Socket :%m"); goto tryagain; } if (connect(nfd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { syslog(LOG_ERR, "Connect: %m"); goto tryagain; } syslog(LOG_INFO, "Sending data to %s", inet_ntoa(sin.sin_addr)); inbuf = 0; while (1) { n1 = read(lfd, buff+inbuf, BUFFERLEN-inbuf); printf("header : %d bytes read (header = %d bytes)\n", n1, (int) sizeof(*sh)); if (n1 < 0) { syslog(LOG_ERR, "Read error (header): %m"); goto tryagain; } if (n1 == 0) { /* XXX can this happen??? */ syslog(LOG_ERR, "Read error (header) : No data"); sleep(1); continue; } inbuf += n1; moreinbuf: if (inbuf < sizeof(*sh)) { continue; /* need more data */ } sh = (synchdr_t *)buff; len = ntohl(sh->sm_len); magic = ntohl(sh->sm_magic); if (magic != SYNHDRMAGIC) { syslog(LOG_ERR, "Invalid header magic %x", magic); goto tryagain; } #define IPSYNC_DEBUG #ifdef IPSYNC_DEBUG printf("v:%d p:%d len:%d magic:%x", sh->sm_v, sh->sm_p, len, magic); if (sh->sm_cmd == SMC_CREATE) printf(" cmd:CREATE"); else if (sh->sm_cmd == SMC_UPDATE) printf(" cmd:UPDATE"); else printf(" cmd:Unknown(%d)", sh->sm_cmd); if (sh->sm_table == SMC_NAT) printf(" table:NAT"); else if (sh->sm_table == SMC_STATE) printf(" table:STATE"); else printf(" table:Unknown(%d)", sh->sm_table); printf(" num:%d\n", (u_32_t)ntohl(sh->sm_num)); #endif if (inbuf < sizeof(*sh) + len) { continue; /* need more data */ goto tryagain; } #ifdef IPSYNC_DEBUG if (sh->sm_cmd == SMC_CREATE) { sl = (synclogent_t *)buff; } else if (sh->sm_cmd == SMC_UPDATE) { su = (syncupdent_t *)buff; if (sh->sm_p == IPPROTO_TCP) { printf(" TCP Update: age %lu state %d/%d\n", su->sup_tcp.stu_age, su->sup_tcp.stu_state[0], su->sup_tcp.stu_state[1]); } } else { printf("Unknown command\n"); } #endif n2 = sizeof(*sh) + len; n3 = write(nfd, buff, n2); if (n3 <= 0) { syslog(LOG_ERR, "Write error: %m"); goto tryagain; } if (n3 != n2) { syslog(LOG_ERR, "Incomplete write (%d/%d)", n3, n2); goto tryagain; } /* signal received? */ if (terminate) break; /* move buffer to the front,we might need to make * this more efficient, by using a rolling pointer * over the buffer and only copying it, when * we are reaching the end */ inbuf -= n2; if (inbuf) { bcopy(buff+n2, buff, inbuf); printf("More data in buffer\n"); goto moreinbuf; } } if (terminate) break; tryagain: sleep(1); } /* terminate */ if (lfd != -1) close(lfd); if (nfd != -1) close(nfd); syslog(LOG_ERR, "signal %d received, exiting...", terminate); exit(1); } diff --git a/contrib/ipfilter/tools/ipsyncs.c b/contrib/ipfilter/tools/ipsyncs.c index 43692cd6a0d1..a53cfb8c9508 100644 --- a/contrib/ipfilter/tools/ipsyncs.c +++ b/contrib/ipfilter/tools/ipsyncs.c @@ -1,274 +1,274 @@ /* $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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_state.h" #include "netinet/ip_nat.h" #include "netinet/ip_sync.h" -int main __P((int, char *[])); -void usage __P((const char *progname)); +int main(int, char *[]); +void usage(const char *progname); int terminate = 0; void usage(const char *progname) { fprintf(stderr, "Usage: %s [remote IP]\n", progname); } #if 0 static void handleterm(int sig) { terminate = sig; } #endif #define BUFFERLEN 1400 int main(argc, argv) int argc; char *argv[]; { int nfd = -1 , lfd = -1; int n1, n2, n3, magic, len, inbuf; struct sockaddr_in sin; struct sockaddr_in in; char buff[BUFFERLEN]; synclogent_t *sl; syncupdent_t *su; synchdr_t *sh; char *progname; progname = strrchr(argv[0], '/'); if (progname) { progname++; } else { progname = argv[0]; } if (argc < 2) { usage(progname); exit(1); } #if 0 signal(SIGHUP, handleterm); signal(SIGINT, handleterm); signal(SIGTERM, handleterm); #endif openlog(progname, LOG_PID, LOG_SECURITY); lfd = open(IPSYNC_NAME, O_WRONLY); if (lfd == -1) { syslog(LOG_ERR, "Opening %s :%m", IPSYNC_NAME); exit(1); } bzero((char *)&sin, sizeof(sin)); sin.sin_family = AF_INET; if (argc > 1) sin.sin_addr.s_addr = inet_addr(argv[1]); if (argc > 2) sin.sin_port = htons(atoi(argv[2])); else sin.sin_port = htons(43434); if (argc > 3) in.sin_addr.s_addr = inet_addr(argv[3]); else in.sin_addr.s_addr = 0; in.sin_port = 0; while(1) { if (lfd != -1) close(lfd); if (nfd != -1) close(nfd); lfd = open(IPSYNC_NAME, O_WRONLY); if (lfd == -1) { syslog(LOG_ERR, "Opening %s :%m", IPSYNC_NAME); goto tryagain; } nfd = socket(AF_INET, SOCK_DGRAM, 0); if (nfd == -1) { syslog(LOG_ERR, "Socket :%m"); goto tryagain; } n1 = 1; setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &n1, sizeof(n1)); if (bind(nfd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { syslog(LOG_ERR, "Bind: %m"); goto tryagain; } syslog(LOG_INFO, "Listening to %s", inet_ntoa(sin.sin_addr)); inbuf = 0; while (1) { /* * XXX currently we do not check the source address * of a datagram, this can be a security risk */ n1 = read(nfd, buff+inbuf, BUFFERLEN-inbuf); printf("header : %d bytes read (header = %d bytes)\n", n1, (int) sizeof(*sh)); if (n1 < 0) { syslog(LOG_ERR, "Read error (header): %m"); goto tryagain; } if (n1 == 0) { /* XXX can this happen??? */ syslog(LOG_ERR, "Read error (header) : No data"); sleep(1); continue; } inbuf += n1; moreinbuf: if (inbuf < sizeof(*sh)) { continue; /* need more data */ } sh = (synchdr_t *)buff; len = ntohl(sh->sm_len); magic = ntohl(sh->sm_magic); if (magic != SYNHDRMAGIC) { syslog(LOG_ERR, "Invalid header magic %x", magic); goto tryagain; } #define IPSYNC_DEBUG #ifdef IPSYNC_DEBUG printf("v:%d p:%d len:%d magic:%x", sh->sm_v, sh->sm_p, len, magic); if (sh->sm_cmd == SMC_CREATE) printf(" cmd:CREATE"); else if (sh->sm_cmd == SMC_UPDATE) printf(" cmd:UPDATE"); else printf(" cmd:Unknown(%d)", sh->sm_cmd); if (sh->sm_table == SMC_NAT) printf(" table:NAT"); else if (sh->sm_table == SMC_STATE) printf(" table:STATE"); else printf(" table:Unknown(%d)", sh->sm_table); printf(" num:%d\n", (u_32_t)ntohl(sh->sm_num)); #endif if (inbuf < sizeof(*sh) + len) { continue; /* need more data */ goto tryagain; } #ifdef IPSYNC_DEBUG if (sh->sm_cmd == SMC_CREATE) { sl = (synclogent_t *)buff; } else if (sh->sm_cmd == SMC_UPDATE) { su = (syncupdent_t *)buff; if (sh->sm_p == IPPROTO_TCP) { printf(" TCP Update: age %lu state %d/%d\n", su->sup_tcp.stu_age, su->sup_tcp.stu_state[0], su->sup_tcp.stu_state[1]); } } else { printf("Unknown command\n"); } #endif n2 = sizeof(*sh) + len; n3 = write(lfd, buff, n2); if (n3 <= 0) { syslog(LOG_ERR, "%s: Write error: %m", IPSYNC_NAME); goto tryagain; } if (n3 != n2) { syslog(LOG_ERR, "%s: Incomplete write (%d/%d)", IPSYNC_NAME, n3, n2); goto tryagain; } /* signal received? */ if (terminate) break; /* move buffer to the front,we might need to make * this more efficient, by using a rolling pointer * over the buffer and only copying it, when * we are reaching the end */ inbuf -= n2; if (inbuf) { bcopy(buff+n2, buff, inbuf); printf("More data in buffer\n"); goto moreinbuf; } } if (terminate) break; tryagain: sleep(1); } /* terminate */ if (lfd != -1) close(lfd); if (nfd != -1) close(nfd); syslog(LOG_ERR, "signal %d received, exiting...", terminate); exit(1); } diff --git a/contrib/ipfilter/tools/lexer.c b/contrib/ipfilter/tools/lexer.c index 41b7896090ad..926ee201685f 100644 --- a/contrib/ipfilter/tools/lexer.c +++ b/contrib/ipfilter/tools/lexer.c @@ -1,735 +1,735 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include #include "ipf.h" #ifdef IPFILTER_SCAN # include "netinet/ip_scan.h" #endif #include #include #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 __P((char *)); -static int yygetc __P((int)); -static void yyunputc __P((int)); -static int yyswallow __P((int)); -static char *yytexttostr __P((int, int)); -static void yystrtotext __P((char *)); -static char *yytexttochar __P((void)); +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 == ':') { sprintf(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 ""; for (w = yywordtab; w->w_word; w++) if (w->w_value == num) return w->w_word; return ""; } 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/contrib/ipfilter/tools/lexer.h b/contrib/ipfilter/tools/lexer.h index cff24b4eb402..cc200f1cad41 100644 --- a/contrib/ipfilter/tools/lexer.h +++ b/contrib/ipfilter/tools/lexer.h @@ -1,38 +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 __P((wordtab_t *)); -extern void yysetdict __P((wordtab_t *)); -extern void yysetfixeddict __P((wordtab_t *)); -extern int yylex __P((void)); -extern void yyerror __P((char *)); -extern char *yykeytostr __P((int)); -extern void yyresetdict __P((void)); +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/sys/contrib/ipfilter/netinet/fil.c b/sys/contrib/ipfilter/netinet/fil.c index 03d50e0963a1..7402437d98c7 100644 --- a/sys/contrib/ipfilter/netinet/fil.c +++ b/sys/contrib/ipfilter/netinet/fil.c @@ -1,10279 +1,10277 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Copyright 2008 Sun Microsystems. * * $Id$ * */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if defined(_KERNEL) && defined(__FreeBSD_version) # if !defined(IPFILTER_LKM) # include "opt_inet6.h" # endif # include #else # include #endif #if defined(__SVR4) || defined(sun) /* SOLARIS */ # include #endif # include #if defined(_KERNEL) # include # include #else # include # include # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if !defined(__SVR4) # include #else # include # if (SOLARIS2 < 5) && defined(sun) # include # endif #endif # include #include #include #ifdef sun # include #endif #include #include #include #include # include # include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include # if !SOLARIS && defined(_KERNEL) # include # endif #endif #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" #ifdef IPFILTER_SCAN # include "netinet/ip_scan.h" #endif #include "netinet/ip_sync.h" #include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #ifdef IPFILTER_COMPILED # include "netinet/ip_rules.h" #endif #if defined(IPFILTER_BPF) && defined(_KERNEL) # include #endif #if defined(__FreeBSD_version) # include #endif #include "netinet/ipl.h" #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) # include extern struct callout ipf_slowtimer_ch; #endif /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: fil.c,v 2.243.2.125 2007/10/10 09:27:20 darrenr Exp $"; */ #endif #ifndef _KERNEL # include "ipf.h" # include "ipt.h" extern int opts; extern int blockreason; #endif /* _KERNEL */ #define FASTROUTE_RECURSION #define LBUMP(x) softc->x++ #define LBUMPD(x, y) do { softc->x.y++; DT(y); } while (0) -static INLINE int ipf_check_ipf __P((fr_info_t *, frentry_t *, int)); -static u_32_t ipf_checkcipso __P((fr_info_t *, u_char *, int)); -static u_32_t ipf_checkripso __P((u_char *)); -static u_32_t ipf_decaps __P((fr_info_t *, u_32_t, int)); +static INLINE int ipf_check_ipf(fr_info_t *, frentry_t *, int); +static u_32_t ipf_checkcipso(fr_info_t *, u_char *, int); +static u_32_t ipf_checkripso(u_char *); +static u_32_t ipf_decaps(fr_info_t *, u_32_t, int); #ifdef IPFILTER_LOG -static frentry_t *ipf_dolog __P((fr_info_t *, u_32_t *)); +static frentry_t *ipf_dolog(fr_info_t *, u_32_t *); #endif -static int ipf_flushlist __P((ipf_main_softc_t *, int *, - frentry_t **)); -static int ipf_flush_groups __P((ipf_main_softc_t *, frgroup_t **, - int)); -static ipfunc_t ipf_findfunc __P((ipfunc_t)); -static void *ipf_findlookup __P((ipf_main_softc_t *, int, - frentry_t *, - i6addr_t *, i6addr_t *)); -static frentry_t *ipf_firewall __P((fr_info_t *, u_32_t *)); -static int ipf_fr_matcharray __P((fr_info_t *, int *)); -static int ipf_frruleiter __P((ipf_main_softc_t *, void *, int, - void *)); -static void ipf_funcfini __P((ipf_main_softc_t *, frentry_t *)); -static int ipf_funcinit __P((ipf_main_softc_t *, frentry_t *)); -static int ipf_geniter __P((ipf_main_softc_t *, ipftoken_t *, - ipfgeniter_t *)); -static void ipf_getstat __P((ipf_main_softc_t *, - struct friostat *, int)); -static int ipf_group_flush __P((ipf_main_softc_t *, frgroup_t *)); -static void ipf_group_free __P((frgroup_t *)); -static int ipf_grpmapfini __P((struct ipf_main_softc_s *, - frentry_t *)); -static int ipf_grpmapinit __P((struct ipf_main_softc_s *, - frentry_t *)); -static frentry_t *ipf_nextrule __P((ipf_main_softc_t *, int, int, - frentry_t *, int)); -static int ipf_portcheck __P((frpcmp_t *, u_32_t)); -static INLINE int ipf_pr_ah __P((fr_info_t *)); -static INLINE void ipf_pr_esp __P((fr_info_t *)); -static INLINE void ipf_pr_gre __P((fr_info_t *)); -static INLINE void ipf_pr_udp __P((fr_info_t *)); -static INLINE void ipf_pr_tcp __P((fr_info_t *)); -static INLINE void ipf_pr_icmp __P((fr_info_t *)); -static INLINE void ipf_pr_ipv4hdr __P((fr_info_t *)); -static INLINE void ipf_pr_short __P((fr_info_t *, int)); -static INLINE int ipf_pr_tcpcommon __P((fr_info_t *)); -static INLINE int ipf_pr_udpcommon __P((fr_info_t *)); -static void ipf_rule_delete __P((ipf_main_softc_t *, frentry_t *f, - int, int)); -static void ipf_rule_expire_insert __P((ipf_main_softc_t *, - frentry_t *, int)); -static int ipf_synclist __P((ipf_main_softc_t *, frentry_t *, - void *)); -static void ipf_token_flush __P((ipf_main_softc_t *)); -static void ipf_token_unlink __P((ipf_main_softc_t *, - ipftoken_t *)); -static ipftuneable_t *ipf_tune_findbyname __P((ipftuneable_t *, - const char *)); -static ipftuneable_t *ipf_tune_findbycookie __P((ipftuneable_t **, void *, - void **)); -static int ipf_updateipid __P((fr_info_t *)); -static int ipf_settimeout __P((struct ipf_main_softc_s *, +static int ipf_flushlist(ipf_main_softc_t *, int *, frentry_t **); +static int ipf_flush_groups(ipf_main_softc_t *, frgroup_t **, + int); +static ipfunc_t ipf_findfunc(ipfunc_t); +static void *ipf_findlookup(ipf_main_softc_t *, int, frentry_t *, + i6addr_t *, i6addr_t *); +static frentry_t *ipf_firewall(fr_info_t *, u_32_t *); +static int ipf_fr_matcharray(fr_info_t *, int *); +static int ipf_frruleiter(ipf_main_softc_t *, void *, int, + void *); +static void ipf_funcfini(ipf_main_softc_t *, frentry_t *); +static int ipf_funcinit(ipf_main_softc_t *, frentry_t *); +static int ipf_geniter(ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *); +static void ipf_getstat(ipf_main_softc_t *, + struct friostat *, int); +static int ipf_group_flush(ipf_main_softc_t *, frgroup_t *); +static void ipf_group_free(frgroup_t *); +static int ipf_grpmapfini(struct ipf_main_softc_s *, + frentry_t *); +static int ipf_grpmapinit(struct ipf_main_softc_s *, + frentry_t *); +static frentry_t *ipf_nextrule(ipf_main_softc_t *, int, int, + frentry_t *, int); +static int ipf_portcheck(frpcmp_t *, u_32_t); +static INLINE int ipf_pr_ah(fr_info_t *); +static INLINE void ipf_pr_esp(fr_info_t *); +static INLINE void ipf_pr_gre(fr_info_t *); +static INLINE void ipf_pr_udp(fr_info_t *); +static INLINE void ipf_pr_tcp(fr_info_t *); +static INLINE void ipf_pr_icmp(fr_info_t *); +static INLINE void ipf_pr_ipv4hdr(fr_info_t *); +static INLINE void ipf_pr_short(fr_info_t *, int); +static INLINE int ipf_pr_tcpcommon(fr_info_t *); +static INLINE int ipf_pr_udpcommon(fr_info_t *); +static void ipf_rule_delete(ipf_main_softc_t *, frentry_t *f, + int, int); +static void ipf_rule_expire_insert(ipf_main_softc_t *, + frentry_t *, int); +static int ipf_synclist(ipf_main_softc_t *, frentry_t *, + void *); +static void ipf_token_flush(ipf_main_softc_t *); +static void ipf_token_unlink(ipf_main_softc_t *, + ipftoken_t *); +static ipftuneable_t *ipf_tune_findbyname(ipftuneable_t *, + const char *); +static ipftuneable_t *ipf_tune_findbycookie(ipftuneable_t **, void *, + void **); +static int ipf_updateipid(fr_info_t *); +static int ipf_settimeout(struct ipf_main_softc_s *, struct ipftuneable *, - ipftuneval_t *)); + ipftuneval_t *); #if !defined(_KERNEL) || SOLARIS static int ppsratecheck(struct timeval *, int *, int); #endif /* * bit values for identifying presence of individual IP options * All of these tables should be ordered by increasing key value on the left * hand side to allow for binary searching of the array and include a trailer * with a 0 for the bitmask for linear searches to easily find the end with. */ static const struct optlist ipopts[] = { { IPOPT_NOP, 0x000001 }, { IPOPT_RR, 0x000002 }, { IPOPT_ZSU, 0x000004 }, { IPOPT_MTUP, 0x000008 }, { IPOPT_MTUR, 0x000010 }, { IPOPT_ENCODE, 0x000020 }, { IPOPT_TS, 0x000040 }, { IPOPT_TR, 0x000080 }, { IPOPT_SECURITY, 0x000100 }, { IPOPT_LSRR, 0x000200 }, { IPOPT_E_SEC, 0x000400 }, { IPOPT_CIPSO, 0x000800 }, { IPOPT_SATID, 0x001000 }, { IPOPT_SSRR, 0x002000 }, { IPOPT_ADDEXT, 0x004000 }, { IPOPT_VISA, 0x008000 }, { IPOPT_IMITD, 0x010000 }, { IPOPT_EIP, 0x020000 }, { IPOPT_FINN, 0x040000 }, { 0, 0x000000 } }; #ifdef USE_INET6 static const struct optlist ip6exthdr[] = { { IPPROTO_HOPOPTS, 0x000001 }, { IPPROTO_IPV6, 0x000002 }, { IPPROTO_ROUTING, 0x000004 }, { IPPROTO_FRAGMENT, 0x000008 }, { IPPROTO_ESP, 0x000010 }, { IPPROTO_AH, 0x000020 }, { IPPROTO_NONE, 0x000040 }, { IPPROTO_DSTOPTS, 0x000080 }, { IPPROTO_MOBILITY, 0x000100 }, { 0, 0 } }; #endif /* * bit values for identifying presence of individual IP security options */ static const struct optlist secopt[] = { { IPSO_CLASS_RES4, 0x01 }, { IPSO_CLASS_TOPS, 0x02 }, { IPSO_CLASS_SECR, 0x04 }, { IPSO_CLASS_RES3, 0x08 }, { IPSO_CLASS_CONF, 0x10 }, { IPSO_CLASS_UNCL, 0x20 }, { IPSO_CLASS_RES2, 0x40 }, { IPSO_CLASS_RES1, 0x80 } }; char ipfilter_version[] = IPL_VERSION; int ipf_features = 0 #ifdef IPFILTER_LKM | IPF_FEAT_LKM #endif #ifdef IPFILTER_LOG | IPF_FEAT_LOG #endif | IPF_FEAT_LOOKUP #ifdef IPFILTER_BPF | IPF_FEAT_BPF #endif #ifdef IPFILTER_COMPILED | IPF_FEAT_COMPILED #endif #ifdef IPFILTER_CKSUM | IPF_FEAT_CKSUM #endif | IPF_FEAT_SYNC #ifdef IPFILTER_SCAN | IPF_FEAT_SCAN #endif #ifdef USE_INET6 | IPF_FEAT_IPV6 #endif ; /* * Table of functions available for use with call rules. */ static ipfunc_resolve_t ipf_availfuncs[] = { { "srcgrpmap", ipf_srcgrpmap, ipf_grpmapinit, ipf_grpmapfini }, { "dstgrpmap", ipf_dstgrpmap, ipf_grpmapinit, ipf_grpmapfini }, { "", NULL, NULL, NULL } }; static ipftuneable_t ipf_main_tuneables[] = { { { (void *)offsetof(struct ipf_main_softc_s, ipf_flags) }, "ipf_flags", 0, 0xffffffff, stsizeof(ipf_main_softc_t, ipf_flags), 0, NULL, NULL }, { { (void *)offsetof(struct ipf_main_softc_s, ipf_active) }, "active", 0, 0, stsizeof(ipf_main_softc_t, ipf_active), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_control_forwarding) }, "control_forwarding", 0, 1, stsizeof(ipf_main_softc_t, ipf_control_forwarding), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_update_ipid) }, "update_ipid", 0, 1, stsizeof(ipf_main_softc_t, ipf_update_ipid), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_chksrc) }, "chksrc", 0, 1, stsizeof(ipf_main_softc_t, ipf_chksrc), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_minttl) }, "min_ttl", 0, 1, stsizeof(ipf_main_softc_t, ipf_minttl), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmpminfragmtu) }, "icmp_minfragmtu", 0, 1, stsizeof(ipf_main_softc_t, ipf_icmpminfragmtu), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_pass) }, "default_pass", 0, 0xffffffff, stsizeof(ipf_main_softc_t, ipf_pass), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpidletimeout) }, "tcp_idle_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpidletimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosewait) }, "tcp_close_wait", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpclosewait), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcplastack) }, "tcp_last_ack", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcplastack), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimeout) }, "tcp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynsent) }, "tcp_syn_sent", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpsynsent), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynrecv) }, "tcp_syn_received", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpsynrecv), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosed) }, "tcp_closed", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpclosed), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcphalfclosed) }, "tcp_half_closed", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcphalfclosed), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimewait) }, "tcp_time_wait", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcptimewait), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_udptimeout) }, "udp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_udptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_udpacktimeout) }, "udp_ack_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_udpacktimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmptimeout) }, "icmp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_icmptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmpacktimeout) }, "icmp_ack_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_icmpacktimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_iptimeout) }, "ip_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_iptimeout), 0, NULL, ipf_settimeout }, #if defined(INSTANCES) && defined(_KERNEL) { { (void *)offsetof(ipf_main_softc_t, ipf_get_loopback) }, "intercept_loopback", 0, 1, stsizeof(ipf_main_softc_t, ipf_get_loopback), 0, NULL, ipf_set_loopback }, #endif { { 0 }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* * The next section of code is a collection of small routines that set * fields in the fr_info_t structure passed based on properties of the * current packet. There are different routines for the same protocol * for each of IPv4 and IPv6. Adding a new protocol, for which there * will "special" inspection for setup, is now more easily done by adding * a new routine and expanding the ipf_pr_ipinit*() function rather than by * adding more code to a growing switch statement. */ #ifdef USE_INET6 -static INLINE int ipf_pr_ah6 __P((fr_info_t *)); -static INLINE void ipf_pr_esp6 __P((fr_info_t *)); -static INLINE void ipf_pr_gre6 __P((fr_info_t *)); -static INLINE void ipf_pr_udp6 __P((fr_info_t *)); -static INLINE void ipf_pr_tcp6 __P((fr_info_t *)); -static INLINE void ipf_pr_icmp6 __P((fr_info_t *)); -static INLINE void ipf_pr_ipv6hdr __P((fr_info_t *)); -static INLINE void ipf_pr_short6 __P((fr_info_t *, int)); -static INLINE int ipf_pr_hopopts6 __P((fr_info_t *)); -static INLINE int ipf_pr_mobility6 __P((fr_info_t *)); -static INLINE int ipf_pr_routing6 __P((fr_info_t *)); -static INLINE int ipf_pr_dstopts6 __P((fr_info_t *)); -static INLINE int ipf_pr_fragment6 __P((fr_info_t *)); -static INLINE struct ip6_ext *ipf_pr_ipv6exthdr __P((fr_info_t *, int, int)); +static INLINE int ipf_pr_ah6(fr_info_t *); +static INLINE void ipf_pr_esp6(fr_info_t *); +static INLINE void ipf_pr_gre6(fr_info_t *); +static INLINE void ipf_pr_udp6(fr_info_t *); +static INLINE void ipf_pr_tcp6(fr_info_t *); +static INLINE void ipf_pr_icmp6(fr_info_t *); +static INLINE void ipf_pr_ipv6hdr(fr_info_t *); +static INLINE void ipf_pr_short6(fr_info_t *, int); +static INLINE int ipf_pr_hopopts6(fr_info_t *); +static INLINE int ipf_pr_mobility6(fr_info_t *); +static INLINE int ipf_pr_routing6(fr_info_t *); +static INLINE int ipf_pr_dstopts6(fr_info_t *); +static INLINE int ipf_pr_fragment6(fr_info_t *); +static INLINE struct ip6_ext *ipf_pr_ipv6exthdr(fr_info_t *, int, int); /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_short6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ /* */ /* IPv6 Only */ /* This is function enforces the 'is a packet too short to be legit' rule */ /* for IPv6 and marks the packet with FI_SHORT if so. See function comment */ /* for ipf_pr_short() for more details. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_short6(fin, xmin) fr_info_t *fin; int xmin; { if (fin->fin_dlen < xmin) fin->fin_flx |= FI_SHORT; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv6hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Copy values from the IPv6 header into the fr_info_t struct and call the */ /* per-protocol analyzer if it exists. In validating the packet, a protocol*/ /* analyzer may pullup or free the packet itself so we need to be vigiliant */ /* of that possibility arising. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_ipv6hdr(fin) fr_info_t *fin; { ip6_t *ip6 = (ip6_t *)fin->fin_ip; int p, go = 1, i, hdrcount; fr_ip_t *fi = &fin->fin_fi; fin->fin_off = 0; fi->fi_tos = 0; fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; p = ip6->ip6_nxt; fin->fin_crc = p; fi->fi_ttl = ip6->ip6_hlim; fi->fi_src.in6 = ip6->ip6_src; fin->fin_crc += fi->fi_src.i6[0]; fin->fin_crc += fi->fi_src.i6[1]; fin->fin_crc += fi->fi_src.i6[2]; fin->fin_crc += fi->fi_src.i6[3]; fi->fi_dst.in6 = ip6->ip6_dst; fin->fin_crc += fi->fi_dst.i6[0]; fin->fin_crc += fi->fi_dst.i6[1]; fin->fin_crc += fi->fi_dst.i6[2]; fin->fin_crc += fi->fi_dst.i6[3]; fin->fin_id = 0; if (IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) fin->fin_flx |= FI_MULTICAST|FI_MBCAST; hdrcount = 0; while (go && !(fin->fin_flx & FI_SHORT)) { switch (p) { case IPPROTO_UDP : ipf_pr_udp6(fin); go = 0; break; case IPPROTO_TCP : ipf_pr_tcp6(fin); go = 0; break; case IPPROTO_ICMPV6 : ipf_pr_icmp6(fin); go = 0; break; case IPPROTO_GRE : ipf_pr_gre6(fin); go = 0; break; case IPPROTO_HOPOPTS : p = ipf_pr_hopopts6(fin); break; case IPPROTO_MOBILITY : p = ipf_pr_mobility6(fin); break; case IPPROTO_DSTOPTS : p = ipf_pr_dstopts6(fin); break; case IPPROTO_ROUTING : p = ipf_pr_routing6(fin); break; case IPPROTO_AH : p = ipf_pr_ah6(fin); break; case IPPROTO_ESP : ipf_pr_esp6(fin); go = 0; break; case IPPROTO_IPV6 : for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == p) { fin->fin_flx |= ip6exthdr[i].ol_bit; break; } go = 0; break; case IPPROTO_NONE : go = 0; break; case IPPROTO_FRAGMENT : p = ipf_pr_fragment6(fin); /* * Given that the only fragments we want to let through * (where fin_off != 0) are those where the non-first * fragments only have data, we can safely stop looking * at headers if this is a non-leading fragment. */ if (fin->fin_off != 0) go = 0; break; default : go = 0; break; } hdrcount++; /* * It is important to note that at this point, for the * extension headers (go != 0), the entire header may not have * been pulled up when the code gets to this point. This is * only done for "go != 0" because the other header handlers * will all pullup their complete header. The other indicator * of an incomplete packet is that this was just an extension * header. */ if ((go != 0) && (p != IPPROTO_NONE) && (ipf_pr_pullup(fin, 0) == -1)) { p = IPPROTO_NONE; break; } } /* * Some of the above functions, like ipf_pr_esp6(), can call ipf_pullup * and destroy whatever packet was here. The caller of this function * expects us to return if there is a problem with ipf_pullup. */ if (fin->fin_m == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_bad); return; } fi->fi_p = p; /* * IPv6 fragment case 1 - see comment for ipf_pr_fragment6(). * "go != 0" imples the above loop hasn't arrived at a layer 4 header. */ if ((go != 0) && (fin->fin_flx & FI_FRAG) && (fin->fin_off == 0)) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipv6_frag_1, fr_info_t *, fin, int, go); LBUMPD(ipf_stats[fin->fin_out], fr_v6_badfrag); LBUMP(ipf_stats[fin->fin_out].fr_v6_bad); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv6exthdr */ /* Returns: struct ip6_ext * - pointer to the start of the next header */ /* or NULL if there is a prolblem. */ /* Parameters: fin(I) - pointer to packet information */ /* multiple(I) - flag indicating yes/no if multiple occurances */ /* of this extension header are allowed. */ /* proto(I) - protocol number for this extension header */ /* */ /* IPv6 Only */ /* This function embodies a number of common checks that all IPv6 extension */ /* headers must be subjected to. For example, making sure the packet is */ /* big enough for it to be in, checking if it is repeated and setting a */ /* flag to indicate its presence. */ /* ------------------------------------------------------------------------ */ static INLINE struct ip6_ext * ipf_pr_ipv6exthdr(fin, multiple, proto) fr_info_t *fin; int multiple, proto; { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; u_short shift; int i; fin->fin_flx |= FI_V6EXTHDR; /* 8 is default length of extension hdr */ if ((fin->fin_dlen - 8) < 0) { fin->fin_flx |= FI_SHORT; LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_short); return NULL; } if (ipf_pr_pullup(fin, 8) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_pullup); return NULL; } hdr = fin->fin_dp; switch (proto) { case IPPROTO_FRAGMENT : shift = 8; break; default : shift = 8 + (hdr->ip6e_len << 3); break; } if (shift > fin->fin_dlen) { /* Nasty extension header length? */ fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_pr_ipv6exthdr_len, fr_info_t *, fin, u_short, shift, u_short, fin->fin_dlen); LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_hlen); return NULL; } fin->fin_dp = (char *)fin->fin_dp + shift; fin->fin_dlen -= shift; /* * If we have seen a fragment header, do not set any flags to indicate * the presence of this extension header as it has no impact on the * end result until after it has been defragmented. */ if (fin->fin_flx & FI_FRAG) return hdr; for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == proto) { /* * Most IPv6 extension headers are only allowed once. */ if ((multiple == 0) && ((fin->fin_optmsk & ip6exthdr[i].ol_bit) != 0)) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipv6exthdr_once, fr_info_t *, fin, u_int, (fin->fin_optmsk & ip6exthdr[i].ol_bit)); } else fin->fin_optmsk |= ip6exthdr[i].ol_bit; break; } return hdr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_hopopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending hop by hop options extension header */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_hopopts6(fin) fr_info_t *fin; { struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); if (hdr == NULL) return IPPROTO_NONE; return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_mobility6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks the IPv6 mobility extension header */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_mobility6(fin) fr_info_t *fin; { struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); if (hdr == NULL) return IPPROTO_NONE; return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_routing6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending routing extension header */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_routing6(fin) fr_info_t *fin; { struct ip6_routing *hdr; hdr = (struct ip6_routing *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_ROUTING); if (hdr == NULL) return IPPROTO_NONE; switch (hdr->ip6r_type) { case 0 : /* * Nasty extension header length? */ if (((hdr->ip6r_len >> 1) < hdr->ip6r_segleft) || (hdr->ip6r_segleft && (hdr->ip6r_len & 1))) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_routing6, fr_info_t *, fin); LBUMPD(ipf_stats[fin->fin_out], fr_v6_rh_bad); return IPPROTO_NONE; } break; default : break; } return hdr->ip6r_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_fragment6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Examine the IPv6 fragment header and extract fragment offset information.*/ /* */ /* Fragments in IPv6 are extraordinarily difficult to deal with - much more */ /* so than in IPv4. There are 5 cases of fragments with IPv6 that all */ /* packets with a fragment header can fit into. They are as follows: */ /* */ /* 1. [IPv6][0-n EH][FH][0-n EH] (no L4HDR present) */ /* 2. [IPV6][0-n EH][FH][0-n EH][L4HDR part] (short) */ /* 3. [IPV6][0-n EH][FH][L4HDR part][0-n data] (short) */ /* 4. [IPV6][0-n EH][FH][0-n EH][L4HDR][0-n data] */ /* 5. [IPV6][0-n EH][FH][data] */ /* */ /* IPV6 = IPv6 header, FH = Fragment Header, */ /* 0-n EH = 0 or more extension headers, 0-n data = 0 or more bytes of data */ /* */ /* Packets that match 1, 2, 3 will be dropped as the only reasonable */ /* scenario in which they happen is in extreme circumstances that are most */ /* likely to be an indication of an attack rather than normal traffic. */ /* A type 3 packet may be sent by an attacked after a type 4 packet. There */ /* are two rules that can be used to guard against type 3 packets: L4 */ /* headers must always be in a packet that has the offset field set to 0 */ /* and no packet is allowed to overlay that where offset = 0. */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_fragment6(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_frag *frag; fin->fin_flx |= FI_FRAG; frag = (struct ip6_frag *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT); if (frag == NULL) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_frag_bad); return IPPROTO_NONE; } if ((frag->ip6f_offlg & IP6F_MORE_FRAG) != 0) { /* * Any fragment that isn't the last fragment must have its * length as a multiple of 8. */ if ((fin->fin_plen & 7) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_frag_not_8, fr_info_t *, fin, u_int, (fin->fin_plen & 7)); } } fin->fin_fraghdr = frag; fin->fin_id = frag->ip6f_ident; fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK); if (fin->fin_off != 0) fin->fin_flx |= FI_FRAGBODY; /* * Jumbograms aren't handled, so the max. length is 64k */ if ((fin->fin_off << 3) + fin->fin_dlen > 65535) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_jumbogram, fr_info_t *, fin, u_int, ((fin->fin_off << 3) + fin->fin_dlen)); } /* * We don't know where the transport layer header (or whatever is next * is), as it could be behind destination options (amongst others) so * return the fragment header as the type of packet this is. Note that * this effectively disables the fragment cache for > 1 protocol at a * time. */ return frag->ip6f_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_dstopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending destination options extension header */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_dstopts6(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_DSTOPTS); if (hdr == NULL) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_dst_bad); return IPPROTO_NONE; } return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_icmp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This routine is mainly concerned with determining the minimum valid size */ /* for an ICMPv6 packet. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_icmp6(fin) fr_info_t *fin; { int minicmpsz = sizeof(struct icmp6_hdr); struct icmp6_hdr *icmp6; if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_icmp6_pullup); return; } if (fin->fin_dlen > 1) { ip6_t *ip6; icmp6 = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp6; if ((icmp6->icmp6_type & ICMP6_INFOMSG_MASK) != 0) fin->fin_flx |= FI_ICMPQUERY; switch (icmp6->icmp6_type) { case ICMP6_ECHO_REPLY : case ICMP6_ECHO_REQUEST : if (fin->fin_dlen >= 6) fin->fin_data[1] = icmp6->icmp6_id; minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t); break; case ICMP6_DST_UNREACH : case ICMP6_PACKET_TOO_BIG : case ICMP6_TIME_EXCEEDED : case ICMP6_PARAM_PROB : fin->fin_flx |= FI_ICMPERR; minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t); if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) break; if (M_LEN(fin->fin_m) < fin->fin_plen) { if (ipf_coalesce(fin) != 1) return; } if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN) == -1) return; /* * If the destination of this packet doesn't match the * source of the original packet then this packet is * not correct. */ icmp6 = fin->fin_dp; ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN); if (IP6_NEQ(&fin->fin_fi.fi_dst, (i6addr_t *)&ip6->ip6_src)) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_icmp6, fr_info_t *, fin); } break; default : break; } } ipf_pr_short6(fin, minicmpsz); if ((fin->fin_flx & (FI_SHORT|FI_BAD)) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_ICMPV6; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for IPv6/UDP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_udp6(fin) fr_info_t *fin; { if (ipf_pr_udpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_UDP; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for IPv6/TCP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_tcp6(fin) fr_info_t *fin; { if (ipf_pr_tcpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_TCP; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_esp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for ESP properties. */ /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ /* even though the newer ESP packets must also have a sequence number that */ /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_esp6(fin) fr_info_t *fin; { if ((fin->fin_off == 0) && (ipf_pr_pullup(fin, 8) == -1)) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_esp_pullup); return; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ah6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_ah6(fin) fr_info_t *fin; { authhdr_t *ah; fin->fin_flx |= FI_AH; ah = (authhdr_t *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); if (ah == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_ah_bad); return IPPROTO_NONE; } ipf_pr_short6(fin, sizeof(*ah)); /* * No need for another pullup, ipf_pr_ipv6exthdr() will pullup * enough data to satisfy ah_next (the very first one.) */ return ah->ah_next; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_gre6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_gre6(fin) fr_info_t *fin; { grehdr_t *gre; if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_gre_pullup); return; } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) fin->fin_data[0] = gre->gr_call; } #endif /* USE_INET6 */ /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_pullup */ /* Returns: int - 0 == pullup succeeded, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* plen(I) - length (excluding L3 header) to pullup */ /* */ /* Short inline function to cut down on code duplication to perform a call */ /* to ipf_pullup to ensure there is the required amount of data, */ /* consecutively in the packet buffer. */ /* */ /* This function pulls up 'extra' data at the location of fin_dp. fin_dp */ /* points to the first byte after the complete layer 3 header, which will */ /* include all of the known extension headers for IPv6 or options for IPv4. */ /* */ /* Since fr_pullup() expects the total length of bytes to be pulled up, it */ /* is necessary to add those we can already assume to be pulled up (fin_dp */ /* - fin_ip) to what is passed through. */ /* ------------------------------------------------------------------------ */ int ipf_pr_pullup(fin, plen) fr_info_t *fin; int plen; { ipf_main_softc_t *softc = fin->fin_main_soft; if (fin->fin_m != NULL) { if (fin->fin_dp != NULL) plen += (char *)fin->fin_dp - ((char *)fin->fin_ip + fin->fin_hlen); plen += fin->fin_hlen; if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) { #if defined(_KERNEL) if (ipf_pullup(fin->fin_m, fin, plen) == NULL) { DT(ipf_pullup_fail); LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); return -1; } LBUMP(ipf_stats[fin->fin_out].fr_pull[0]); #else LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); /* * Fake ipf_pullup failing */ fin->fin_reason = FRB_PULLUP; *fin->fin_mp = NULL; fin->fin_m = NULL; fin->fin_ip = NULL; return -1; #endif } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_short */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ /* */ /* Check if a packet is "short" as defined by xmin. The rule we are */ /* applying here is that the packet must not be fragmented within the layer */ /* 4 header. That is, it must not be a fragment that has its offset set to */ /* start within the layer 4 header (hdrmin) or if it is at offset 0, the */ /* entire layer 4 header must be present (min). */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_short(fin, xmin) fr_info_t *fin; int xmin; { if (fin->fin_off == 0) { if (fin->fin_dlen < xmin) fin->fin_flx |= FI_SHORT; } else if (fin->fin_off < xmin) { fin->fin_flx |= FI_SHORT; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_icmp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Do a sanity check on the packet for ICMP (v4). In nearly all cases, */ /* except extrememly bad packets, both type and code will be present. */ /* The expected minimum size of an ICMP packet is very much dependent on */ /* the type of it. */ /* */ /* XXX - other ICMP sanity checks? */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_icmp(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; int minicmpsz = sizeof(struct icmp); icmphdr_t *icmp; ip_t *oip; ipf_pr_short(fin, ICMPERR_ICMPHLEN); if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_frag); return; } if (ipf_pr_pullup(fin, ICMPERR_ICMPHLEN) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_pullup); return; } icmp = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp; fin->fin_data[1] = icmp->icmp_id; switch (icmp->icmp_type) { case ICMP_ECHOREPLY : case ICMP_ECHO : /* Router discovery messaes - RFC 1256 */ case ICMP_ROUTERADVERT : case ICMP_ROUTERSOLICIT : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = ICMP_MINLEN; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + * 3 * timestamp(3 * 4) */ case ICMP_TSTAMP : case ICMP_TSTAMPREPLY : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = 20; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + * mask(4) */ case ICMP_IREQ : case ICMP_IREQREPLY : case ICMP_MASKREQ : case ICMP_MASKREPLY : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = 12; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) */ case ICMP_UNREACH : #ifdef icmp_nextmtu if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { if (icmp->icmp_nextmtu < softc->ipf_icmpminfragmtu) { fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_icmp_nextmtu, fr_info_t *, fin, u_int, icmp->icmp_nextmtu, u_int, softc->ipf_icmpminfragmtu); } } #endif /* FALLTHROUGH */ case ICMP_SOURCEQUENCH : case ICMP_REDIRECT : case ICMP_TIMXCEED : case ICMP_PARAMPROB : fin->fin_flx |= FI_ICMPERR; if (ipf_coalesce(fin) != 1) { LBUMPD(ipf_stats[fin->fin_out], fr_icmp_coalesce); return; } /* * ICMP error packets should not be generated for IP * packets that are a fragment that isn't the first * fragment. */ oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_icmp_err, fr_info_t, fin, u_int, (ntohs(oip->ip_off) & IP_OFFMASK)); } /* * If the destination of this packet doesn't match the * source of the original packet then this packet is * not correct. */ if (oip->ip_src.s_addr != fin->fin_daddr) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_src_ne_dst, fr_info_t *, fin); } break; default : break; } ipf_pr_short(fin, minicmpsz); ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* TCP header sanity checking. Look for bad combinations of TCP flags, */ /* and make some checks with how they interact with other fields. */ /* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is */ /* valid and mark the packet as bad if not. */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_tcpcommon(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; int flags, tlen; tcphdr_t *tcp; fin->fin_flx |= FI_TCPUDP; if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_frag); return 0; } if (ipf_pr_pullup(fin, sizeof(*tcp)) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return -1; } tcp = fin->fin_dp; if (fin->fin_dlen > 3) { fin->fin_sport = ntohs(tcp->th_sport); fin->fin_dport = ntohs(tcp->th_dport); } if ((fin->fin_flx & FI_SHORT) != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_short); return 1; } /* * Use of the TCP data offset *must* result in a value that is at * least the same size as the TCP header. */ tlen = TCP_OFF(tcp) << 2; if (tlen < sizeof(tcphdr_t)) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_small); fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_tlen, fr_info_t, fin, u_int, tlen, u_int, sizeof(tcphdr_t)); return 1; } flags = tcp->th_flags; fin->fin_tcpf = tcp->th_flags; /* * If the urgent flag is set, then the urgent pointer must * also be set and vice versa. Good TCP packets do not have * just one of these set. */ if ((flags & TH_URG) != 0 && (tcp->th_urp == 0)) { fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_th_urg, fr_info_t*, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); #if 0 } else if ((flags & TH_URG) == 0 && (tcp->th_urp != 0)) { /* * Ignore this case (#if 0) as it shows up in "real" * traffic with bogus values in the urgent pointer field. */ fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_th_urg0, fr_info_t *, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); #endif } else if (((flags & (TH_SYN|TH_FIN)) != 0) && ((flags & (TH_RST|TH_ACK)) == TH_RST)) { /* TH_FIN|TH_RST|TH_ACK seems to appear "naturally" */ fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_fin_rst_ack, fr_info_t, fin); #if 1 } else if (((flags & TH_SYN) != 0) && ((flags & (TH_URG|TH_PUSH)) != 0)) { /* * SYN with URG and PUSH set is not for normal TCP but it is * possible(?) with T/TCP...but who uses T/TCP? */ fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_syn_urg_psh, fr_info_t *, fin); #endif } else if (!(flags & TH_ACK)) { /* * If the ack bit isn't set, then either the SYN or * RST bit must be set. If the SYN bit is set, then * we expect the ACK field to be 0. If the ACK is * not set and if URG, PSH or FIN are set, consdier * that to indicate a bad TCP packet. */ if ((flags == TH_SYN) && (tcp->th_ack != 0)) { /* * Cisco PIX sets the ACK field to a random value. * In light of this, do not set FI_BAD until a patch * is available from Cisco to ensure that * interoperability between existing systems is * achieved. */ /*fin->fin_flx |= FI_BAD*/; /*DT1(ipf_fi_bad_th_syn_ack, fr_info_t *, fin);*/ } else if (!(flags & (TH_RST|TH_SYN))) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_rst_syn, fr_info_t *, fin); } else if ((flags & (TH_URG|TH_PUSH|TH_FIN)) != 0) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_urg_push_fin, fr_info_t *, fin); } } if (fin->fin_flx & FI_BAD) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_bad_flags); return 1; } /* * At this point, it's not exactly clear what is to be gained by * marking up which TCP options are and are not present. The one we * are most interested in is the TCP window scale. This is only in * a SYN packet [RFC1323] so we don't need this here...? * Now if we were to analyse the header for passive fingerprinting, * then that might add some weight to adding this... */ if (tlen == sizeof(tcphdr_t)) { return 0; } if (ipf_pr_pullup(fin, tlen) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return -1; } #if 0 tcp = fin->fin_dp; ip = fin->fin_ip; s = (u_char *)(tcp + 1); off = IP_HL(ip) << 2; # ifdef _KERNEL if (fin->fin_mp != NULL) { mb_t *m = *fin->fin_mp; if (off + tlen > M_LEN(m)) return; } # endif for (tlen -= (int)sizeof(*tcp); tlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == TCPOPT_NOP) ol = 1; else { if (tlen < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > tlen) break; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; if (opt == (u_char)op->ol_val) { optmsk |= op->ol_bit; break; } } tlen -= ol; s += ol; } #endif /* 0 */ return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Extract the UDP source and destination ports, if present. If compiled */ /* with IPFILTER_CKSUM, check to see if the UDP checksum is valid. */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_udpcommon(fin) fr_info_t *fin; { udphdr_t *udp; fin->fin_flx |= FI_TCPUDP; if (!fin->fin_off && (fin->fin_dlen > 3)) { if (ipf_pr_pullup(fin, sizeof(*udp)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_SHORT; LBUMPD(ipf_stats[fin->fin_out], fr_udp_pullup); return 1; } udp = fin->fin_dp; fin->fin_sport = ntohs(udp->uh_sport); fin->fin_dport = ntohs(udp->uh_dport); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/TCP properties. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_tcp(fin) fr_info_t *fin; { ipf_pr_short(fin, sizeof(tcphdr_t)); if (ipf_pr_tcpcommon(fin) == 0) ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/UDP properties. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_udp(fin) fr_info_t *fin; { ipf_pr_short(fin, sizeof(udphdr_t)); if (ipf_pr_udpcommon(fin) == 0) ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_esp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for ESP properties. */ /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ /* even though the newer ESP packets must also have a sequence number that */ /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_esp(fin) fr_info_t *fin; { if (fin->fin_off == 0) { ipf_pr_short(fin, 8); if (ipf_pr_pullup(fin, 8) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v4_esp_pullup); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ah */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_pr_ah(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; authhdr_t *ah; int len; fin->fin_flx |= FI_AH; ipf_pr_short(fin, sizeof(*ah)); if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_ah_bad); return IPPROTO_NONE; } if (ipf_pr_pullup(fin, sizeof(*ah)) == -1) { DT(fr_v4_ah_pullup_1); LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); return IPPROTO_NONE; } ah = (authhdr_t *)fin->fin_dp; len = (ah->ah_plen + 2) << 2; ipf_pr_short(fin, len); if (ipf_pr_pullup(fin, len) == -1) { DT(fr_v4_ah_pullup_2); LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); return IPPROTO_NONE; } /* * Adjust fin_dp and fin_dlen for skipping over the authentication * header. */ fin->fin_dp = (char *)fin->fin_dp + len; fin->fin_dlen -= len; return ah->ah_next; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_gre */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_gre(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; grehdr_t *gre; ipf_pr_short(fin, sizeof(grehdr_t)); if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_frag); return; } if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_pullup); return; } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) fin->fin_data[0] = gre->gr_call; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv4hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyze the IPv4 header and set fields in the fr_info_t structure. */ /* Check all options present and flag their presence if any exist. */ /* ------------------------------------------------------------------------ */ static INLINE void ipf_pr_ipv4hdr(fin) fr_info_t *fin; { u_short optmsk = 0, secmsk = 0, auth = 0; int hlen, ol, mv, p, i; const struct optlist *op; u_char *s, opt; u_short off; fr_ip_t *fi; ip_t *ip; fi = &fin->fin_fi; hlen = fin->fin_hlen; ip = fin->fin_ip; p = ip->ip_p; fi->fi_p = p; fin->fin_crc = p; fi->fi_tos = ip->ip_tos; fin->fin_id = ntohs(ip->ip_id); off = ntohs(ip->ip_off); /* Get both TTL and protocol */ fi->fi_p = ip->ip_p; fi->fi_ttl = ip->ip_ttl; /* Zero out bits not used in IPv6 address */ fi->fi_src.i6[1] = 0; fi->fi_src.i6[2] = 0; fi->fi_src.i6[3] = 0; fi->fi_dst.i6[1] = 0; fi->fi_dst.i6[2] = 0; fi->fi_dst.i6[3] = 0; fi->fi_saddr = ip->ip_src.s_addr; fin->fin_crc += fi->fi_saddr; fi->fi_daddr = ip->ip_dst.s_addr; fin->fin_crc += fi->fi_daddr; if (IN_CLASSD(ntohl(fi->fi_daddr))) fin->fin_flx |= FI_MULTICAST|FI_MBCAST; /* * set packet attribute flags based on the offset and * calculate the byte offset that it represents. */ off &= IP_MF|IP_OFFMASK; if (off != 0) { int morefrag = off & IP_MF; fi->fi_flx |= FI_FRAG; off &= IP_OFFMASK; if (off == 1 && p == IPPROTO_TCP) { fin->fin_flx |= FI_SHORT; /* RFC 3128 */ DT1(ipf_fi_tcp_frag_off_1, fr_info_t *, fin); } if (off != 0) { fin->fin_flx |= FI_FRAGBODY; off <<= 3; if ((off + fin->fin_dlen > 65535) || (fin->fin_dlen == 0) || ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) { /* * The length of the packet, starting at its * offset cannot exceed 65535 (0xffff) as the * length of an IP packet is only 16 bits. * * Any fragment that isn't the last fragment * must have a length greater than 0 and it * must be an even multiple of 8. */ fi->fi_flx |= FI_BAD; DT1(ipf_fi_bad_fragbody_gt_65535, fr_info_t *, fin); } } } fin->fin_off = off; /* * Call per-protocol setup and checking */ if (p == IPPROTO_AH) { /* * Treat AH differently because we expect there to be another * layer 4 header after it. */ p = ipf_pr_ah(fin); } switch (p) { case IPPROTO_UDP : ipf_pr_udp(fin); break; case IPPROTO_TCP : ipf_pr_tcp(fin); break; case IPPROTO_ICMP : ipf_pr_icmp(fin); break; case IPPROTO_ESP : ipf_pr_esp(fin); break; case IPPROTO_GRE : ipf_pr_gre(fin); break; } ip = fin->fin_ip; if (ip == NULL) return; /* * If it is a standard IP header (no options), set the flag fields * which relate to options to 0. */ if (hlen == sizeof(*ip)) { fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; return; } /* * So the IP header has some IP options attached. Walk the entire * list of options present with this packet and set flags to indicate * which ones are here and which ones are not. For the somewhat out * of date and obscure security classification options, set a flag to * represent which classification is present. */ fi->fi_flx |= FI_OPTIONS; for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == IPOPT_NOP) ol = 1; else { if (hlen < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > hlen) break; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; if ((opt == (u_char)op->ol_val) && (ol > 4)) { u_32_t doi; switch (opt) { case IPOPT_SECURITY : if (optmsk & op->ol_bit) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipopt_security, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); } else { doi = ipf_checkripso(s); secmsk = doi >> 16; auth = doi & 0xffff; } break; case IPOPT_CIPSO : if (optmsk & op->ol_bit) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipopt_cipso, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); } else { doi = ipf_checkcipso(fin, s, ol); secmsk = doi >> 16; auth = doi & 0xffff; } break; } optmsk |= op->ol_bit; } if (opt < op->ol_val) i -= mv; else i += mv; mv--; } hlen -= ol; s += ol; } /* * */ if (auth && !(auth & 0x0100)) auth &= 0xff00; fi->fi_optmsk = optmsk; fi->fi_secmsk = secmsk; fi->fi_auth = auth; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkripso */ /* Returns: void */ /* Parameters: s(I) - pointer to start of RIPSO option */ /* */ /* ------------------------------------------------------------------------ */ static u_32_t ipf_checkripso(s) u_char *s; { const struct optlist *sp; u_short secmsk = 0, auth = 0; u_char sec; int j, m; sec = *(s + 2); /* classification */ for (j = 3, m = 2; m >= 0; ) { sp = secopt + j; if (sec == sp->ol_val) { secmsk |= sp->ol_bit; auth = *(s + 3); auth *= 256; auth += *(s + 4); break; } if (sec < sp->ol_val) j -= m; else j += m; m--; } return (secmsk << 16) | auth; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkcipso */ /* Returns: u_32_t - 0 = failure, else the doi from the header */ /* Parameters: fin(IO) - pointer to packet information */ /* s(I) - pointer to start of CIPSO option */ /* ol(I) - length of CIPSO option field */ /* */ /* This function returns the domain of integrity (DOI) field from the CIPSO */ /* header and returns that whilst also storing the highest sensitivity */ /* value found in the fr_info_t structure. */ /* */ /* No attempt is made to extract the category bitmaps as these are defined */ /* by the user (rather than the protocol) and can be rather numerous on the */ /* end nodes. */ /* ------------------------------------------------------------------------ */ static u_32_t ipf_checkcipso(fin, s, ol) fr_info_t *fin; u_char *s; int ol; { ipf_main_softc_t *softc = fin->fin_main_soft; fr_ip_t *fi; u_32_t doi; u_char *t, tag, tlen, sensitivity; int len; if (ol < 6 || ol > 40) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_bad); fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_ol, fr_info_t *, fin, u_int, ol); return 0; } fi = &fin->fin_fi; fi->fi_sensitivity = 0; /* * The DOI field MUST be there. */ bcopy(s + 2, &doi, sizeof(doi)); t = (u_char *)s + 6; for (len = ol - 6; len >= 2; len -= tlen, t+= tlen) { tag = *t; tlen = *(t + 1); if (tlen > len || tlen < 4 || tlen > 34) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_tlen); fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tlen, fr_info_t *, fin, u_int, tlen); return 0; } sensitivity = 0; /* * Tag numbers 0, 1, 2, 5 are laid out in the CIPSO Internet * draft (16 July 1992) that has expired. */ if (tag == 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag, fr_info_t *, fin, u_int, tag); continue; } else if (tag == 1) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag1_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Category bitmap for categories 0-239 */ } else if (tag == 4) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag4_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Enumerated categories, 16bits each, upto 15 */ } else if (tag == 5) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag5_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Range of categories (2*16bits), up to 7 pairs */ } else if (tag > 127) { /* Custom defined DOI */ ; } else { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag127, fr_info_t *, fin, u_int, tag); continue; } if (sensitivity > fi->fi_sensitivity) fi->fi_sensitivity = sensitivity; } return doi; } /* ------------------------------------------------------------------------ */ /* Function: ipf_makefrip */ /* Returns: int - 0 == packet ok, -1 == packet freed */ /* Parameters: hlen(I) - length of IP packet header */ /* ip(I) - pointer to the IP header */ /* fin(IO) - pointer to packet information */ /* */ /* Compact the IP header into a structure which contains just the info. */ /* which is useful for comparing IP headers with and store this information */ /* in the fr_info_t structure pointer to by fin. At present, it is assumed */ /* this function will be called with either an IPv4 or IPv6 packet. */ /* ------------------------------------------------------------------------ */ int ipf_makefrip(hlen, ip, fin) int hlen; ip_t *ip; fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; int v; fin->fin_depth = 0; fin->fin_hlen = (u_short)hlen; fin->fin_ip = ip; fin->fin_rule = 0xffffffff; fin->fin_group[0] = -1; fin->fin_group[1] = '\0'; fin->fin_dp = (char *)ip + hlen; v = fin->fin_v; if (v == 4) { fin->fin_plen = ntohs(ip->ip_len); fin->fin_dlen = fin->fin_plen - hlen; ipf_pr_ipv4hdr(fin); #ifdef USE_INET6 } else if (v == 6) { fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen); fin->fin_dlen = fin->fin_plen; fin->fin_plen += hlen; ipf_pr_ipv6hdr(fin); #endif } if (fin->fin_ip == NULL) { LBUMP(ipf_stats[fin->fin_out].fr_ip_freed); return -1; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_portcheck */ /* Returns: int - 1 == port matched, 0 == port match failed */ /* Parameters: frp(I) - pointer to port check `expression' */ /* pop(I) - port number to evaluate */ /* */ /* Perform a comparison of a port number against some other(s), using a */ /* structure with compare information stored in it. */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_portcheck(frp, pop) frpcmp_t *frp; u_32_t pop; { int err = 1; u_32_t po; po = frp->frp_port; /* * Do opposite test to that required and continue if that succeeds. */ switch (frp->frp_cmp) { case FR_EQUAL : if (pop != po) /* EQUAL */ err = 0; break; case FR_NEQUAL : if (pop == po) /* NOTEQUAL */ err = 0; break; case FR_LESST : if (pop >= po) /* LESSTHAN */ err = 0; break; case FR_GREATERT : if (pop <= po) /* GREATERTHAN */ err = 0; break; case FR_LESSTE : if (pop > po) /* LT or EQ */ err = 0; break; case FR_GREATERTE : if (pop < po) /* GT or EQ */ err = 0; break; case FR_OUTRANGE : if (pop >= po && pop <= frp->frp_top) /* Out of range */ err = 0; break; case FR_INRANGE : if (pop <= po || pop >= frp->frp_top) /* In range */ err = 0; break; case FR_INCRANGE : if (pop < po || pop > frp->frp_top) /* Inclusive range */ err = 0; break; default : break; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tcpudpchk */ /* Returns: int - 1 == protocol matched, 0 == check failed */ /* Parameters: fda(I) - pointer to packet information */ /* ft(I) - pointer to structure with comparison data */ /* */ /* Compares the current pcket (assuming it is TCP/UDP) information with a */ /* structure containing information that we want to match against. */ /* ------------------------------------------------------------------------ */ int ipf_tcpudpchk(fi, ft) fr_ip_t *fi; frtuc_t *ft; { int err = 1; /* * Both ports should *always* be in the first fragment. * So far, I cannot find any cases where they can not be. * * compare destination ports */ if (ft->ftu_dcmp) err = ipf_portcheck(&ft->ftu_dst, fi->fi_ports[1]); /* * compare source ports */ if (err && ft->ftu_scmp) err = ipf_portcheck(&ft->ftu_src, fi->fi_ports[0]); /* * If we don't have all the TCP/UDP header, then how can we * expect to do any sort of match on it ? If we were looking for * TCP flags, then NO match. If not, then match (which should * satisfy the "short" class too). */ if (err && (fi->fi_p == IPPROTO_TCP)) { if (fi->fi_flx & FI_SHORT) return !(ft->ftu_tcpf | ft->ftu_tcpfm); /* * Match the flags ? If not, abort this match. */ if (ft->ftu_tcpfm && ft->ftu_tcpf != (fi->fi_tcpf & ft->ftu_tcpfm)) { FR_DEBUG(("f. %#x & %#x != %#x\n", fi->fi_tcpf, ft->ftu_tcpfm, ft->ftu_tcpf)); err = 0; } } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_check_ipf */ /* Returns: int - 0 == match, else no match */ /* Parameters: fin(I) - pointer to packet information */ /* fr(I) - pointer to filter rule */ /* portcmp(I) - flag indicating whether to attempt matching on */ /* TCP/UDP port data. */ /* */ /* Check to see if a packet matches an IPFilter rule. Checks of addresses, */ /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */ /* this function. */ /* ------------------------------------------------------------------------ */ static INLINE int ipf_check_ipf(fin, fr, portcmp) fr_info_t *fin; frentry_t *fr; int portcmp; { u_32_t *ld, *lm, *lip; fripf_t *fri; fr_ip_t *fi; int i; fi = &fin->fin_fi; fri = fr->fr_ipf; lip = (u_32_t *)fi; lm = (u_32_t *)&fri->fri_mip; ld = (u_32_t *)&fri->fri_ip; /* * first 32 bits to check coversion: * IP version, TOS, TTL, protocol */ i = ((*lip & *lm) != *ld); FR_DEBUG(("0. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i) return 1; /* * Next 32 bits is a constructed bitmask indicating which IP options * are present (if any) in this packet. */ lip++, lm++, ld++; i = ((*lip & *lm) != *ld); FR_DEBUG(("1. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i != 0) return 1; lip++, lm++, ld++; /* * Unrolled loops (4 each, for 32 bits) for address checks. */ /* * Check the source address. */ if (fr->fr_satype == FRI_LOOKUP) { i = (*fr->fr_srcfunc)(fin->fin_main_soft, fr->fr_srcptr, fi->fi_v, lip, fin->fin_plen); if (i == -1) return 1; lip += 3; lm += 3; ld += 3; } else { i = ((*lip & *lm) != *ld); FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; ld += 3; } } i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6; if (i != 0) return 1; /* * Check the destination address. */ lip++, lm++, ld++; if (fr->fr_datype == FRI_LOOKUP) { i = (*fr->fr_dstfunc)(fin->fin_main_soft, fr->fr_dstptr, fi->fi_v, lip, fin->fin_plen); if (i == -1) return 1; lip += 3; lm += 3; ld += 3; } else { i = ((*lip & *lm) != *ld); FR_DEBUG(("3a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3b. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3c. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3d. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; ld += 3; } } i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7; if (i != 0) return 1; /* * IP addresses matched. The next 32bits contains: * mast of old IP header security & authentication bits. */ lip++, lm++, ld++; i = (*ld - (*lip & *lm)); FR_DEBUG(("4. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); /* * Next we have 32 bits of packet flags. */ lip++, lm++, ld++; i |= (*ld - (*lip & *lm)); FR_DEBUG(("5. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i == 0) { /* * If a fragment, then only the first has what we're * looking for here... */ if (portcmp) { if (!ipf_tcpudpchk(&fin->fin_fi, &fr->fr_tuc)) i = 1; } else { if (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf || fr->fr_tcpfm) i = 1; if (fr->fr_icmpm || fr->fr_icmp) { if (((fi->fi_p != IPPROTO_ICMP) && (fi->fi_p != IPPROTO_ICMPV6)) || fin->fin_off || (fin->fin_dlen < 2)) i = 1; else if ((fin->fin_data[0] & fr->fr_icmpm) != fr->fr_icmp) { FR_DEBUG(("i. %#x & %#x != %#x\n", fin->fin_data[0], fr->fr_icmpm, fr->fr_icmp)); i = 1; } } } } return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_scanlist */ /* Returns: int - result flags of scanning filter list */ /* Parameters: fin(I) - pointer to packet information */ /* pass(I) - default result to return for filtering */ /* */ /* Check the input/output list of rules for a match to the current packet. */ /* If a match is found, the value of fr_flags from the rule becomes the */ /* return value and fin->fin_fr points to the matched rule. */ /* */ /* This function may be called recusively upto 16 times (limit inbuilt.) */ /* When unwinding, it should finish up with fin_depth as 0. */ /* */ /* Could be per interface, but this gets real nasty when you don't have, */ /* or can't easily change, the kernel source code to . */ /* ------------------------------------------------------------------------ */ int ipf_scanlist(fin, pass) fr_info_t *fin; u_32_t pass; { ipf_main_softc_t *softc = fin->fin_main_soft; int rulen, portcmp, off, skip; struct frentry *fr, *fnext; u_32_t passt, passo; /* * Do not allow nesting deeper than 16 levels. */ if (fin->fin_depth >= 16) return pass; fr = fin->fin_fr; /* * If there are no rules in this list, return now. */ if (fr == NULL) return pass; skip = 0; portcmp = 0; fin->fin_depth++; fin->fin_fr = NULL; off = fin->fin_off; if ((fin->fin_flx & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) portcmp = 1; for (rulen = 0; fr; fr = fnext, rulen++) { fnext = fr->fr_next; if (skip != 0) { FR_VERBOSE(("SKIP %d (%#x)\n", skip, fr->fr_flags)); skip--; continue; } /* * In all checks below, a null (zero) value in the * filter struture is taken to mean a wildcard. * * check that we are working for the right interface */ #ifdef _KERNEL if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; #else if (opts & (OPT_VERBOSE|OPT_DEBUG)) printf("\n"); FR_VERBOSE(("%c", FR_ISSKIP(pass) ? 's' : FR_ISPASS(pass) ? 'p' : FR_ISACCOUNT(pass) ? 'A' : FR_ISAUTH(pass) ? 'a' : (pass & FR_NOMATCH) ? 'n' :'b')); if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; FR_VERBOSE((":i")); #endif switch (fr->fr_type) { case FR_T_IPF : case FR_T_IPF_BUILTIN : if (ipf_check_ipf(fin, fr, portcmp)) continue; break; #if defined(IPFILTER_BPF) case FR_T_BPFOPC : case FR_T_BPFOPC_BUILTIN : { u_char *mc; int wlen; if (*fin->fin_mp == NULL) continue; if (fin->fin_family != fr->fr_family) continue; mc = (u_char *)fin->fin_m; wlen = fin->fin_dlen + fin->fin_hlen; if (!bpf_filter(fr->fr_data, mc, wlen, 0)) continue; break; } #endif case FR_T_CALLFUNC_BUILTIN : { frentry_t *f; f = (*fr->fr_func)(fin, &pass); if (f != NULL) fr = f; else continue; break; } case FR_T_IPFEXPR : case FR_T_IPFEXPR_BUILTIN : if (fin->fin_family != fr->fr_family) continue; if (ipf_fr_matcharray(fin, fr->fr_data) == 0) continue; break; default : break; } if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { if (fin->fin_nattag == NULL) continue; if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) continue; } FR_VERBOSE(("=%d/%d.%d *", fr->fr_grhead, fr->fr_group, rulen)); passt = fr->fr_flags; /* * If the rule is a "call now" rule, then call the function * in the rule, if it exists and use the results from that. * If the function pointer is bad, just make like we ignore * it, except for increasing the hit counter. */ if ((passt & FR_CALLNOW) != 0) { frentry_t *frs; ATOMIC_INC64(fr->fr_hits); if ((fr->fr_func == NULL) || (fr->fr_func == (ipfunc_t)-1)) continue; frs = fin->fin_fr; fin->fin_fr = fr; fr = (*fr->fr_func)(fin, &passt); if (fr == NULL) { fin->fin_fr = frs; continue; } passt = fr->fr_flags; } fin->fin_fr = fr; #ifdef IPFILTER_LOG /* * Just log this packet... */ if ((passt & FR_LOGMASK) == FR_LOG) { if (ipf_log_pkt(fin, passt) == -1) { if (passt & FR_LOGORBLOCK) { DT(frb_logfail); passt &= ~FR_CMDMASK; passt |= FR_BLOCK|FR_QUICK; fin->fin_reason = FRB_LOGFAIL; } } } #endif /* IPFILTER_LOG */ MUTEX_ENTER(&fr->fr_lock); fr->fr_bytes += (U_QUAD_T)fin->fin_plen; fr->fr_hits++; MUTEX_EXIT(&fr->fr_lock); fin->fin_rule = rulen; passo = pass; if (FR_ISSKIP(passt)) { skip = fr->fr_arg; continue; } else if (((passt & FR_LOGMASK) != FR_LOG) && ((passt & FR_LOGMASK) != FR_DECAPSULATE)) { pass = passt; } if (passt & (FR_RETICMP|FR_FAKEICMP)) fin->fin_icode = fr->fr_icode; if (fr->fr_group != -1) { (void) strncpy(fin->fin_group, FR_NAME(fr, fr_group), strlen(FR_NAME(fr, fr_group))); } else { fin->fin_group[0] = '\0'; } FR_DEBUG(("pass %#x/%#x/%x\n", passo, pass, passt)); if (fr->fr_grphead != NULL) { fin->fin_fr = fr->fr_grphead->fg_start; FR_VERBOSE(("group %s\n", FR_NAME(fr, fr_grhead))); if (FR_ISDECAPS(passt)) passt = ipf_decaps(fin, pass, fr->fr_icode); else passt = ipf_scanlist(fin, pass); if (fin->fin_fr == NULL) { fin->fin_rule = rulen; if (fr->fr_group != -1) (void) strncpy(fin->fin_group, fr->fr_names + fr->fr_group, strlen(fr->fr_names + fr->fr_group)); fin->fin_fr = fr; passt = pass; } pass = passt; } if (pass & FR_QUICK) { /* * Finally, if we've asked to track state for this * packet, set it up. Add state for "quick" rules * here so that if the action fails we can consider * the rule to "not match" and keep on processing * filter rules. */ if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) && !(fin->fin_flx & FI_STATE)) { int out = fin->fin_out; fin->fin_fr = fr; if (ipf_state_add(softc, fin, NULL, 0) == 0) { LBUMPD(ipf_stats[out], fr_ads); } else { LBUMPD(ipf_stats[out], fr_bads); pass = passo; continue; } } break; } } fin->fin_depth--; return pass; } /* ------------------------------------------------------------------------ */ /* Function: ipf_acctpkt */ /* Returns: frentry_t* - always returns NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Checks a packet against accounting rules, if there are any for the given */ /* IP protocol version. */ /* */ /* N.B.: this function returns NULL to match the prototype used by other */ /* functions called from the IPFilter "mainline" in ipf_check(). */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_acctpkt(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc = fin->fin_main_soft; char group[FR_GROUPLEN]; frentry_t *fr, *frsave; u_32_t pass, rulen; passp = passp; fr = softc->ipf_acct[fin->fin_out][softc->ipf_active]; if (fr != NULL) { frsave = fin->fin_fr; bcopy(fin->fin_group, group, FR_GROUPLEN); rulen = fin->fin_rule; fin->fin_fr = fr; pass = ipf_scanlist(fin, FR_NOMATCH); if (FR_ISACCOUNT(pass)) { LBUMPD(ipf_stats[0], fr_acct); } fin->fin_fr = frsave; bcopy(group, fin->fin_group, FR_GROUPLEN); fin->fin_rule = rulen; } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_firewall */ /* Returns: frentry_t* - returns pointer to matched rule, if no matches */ /* were found, returns NULL. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Applies an appropriate set of firewall rules to the packet, to see if */ /* there are any matches. The first check is to see if a match can be seen */ /* in the cache. If not, then search an appropriate list of rules. Once a */ /* matching rule is found, take any appropriate actions as defined by the */ /* rule - except logging. */ /* ------------------------------------------------------------------------ */ static frentry_t * ipf_firewall(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc = fin->fin_main_soft; frentry_t *fr; u_32_t pass; int out; out = fin->fin_out; pass = *passp; /* * This rule cache will only affect packets that are not being * statefully filtered. */ fin->fin_fr = softc->ipf_rules[out][softc->ipf_active]; if (fin->fin_fr != NULL) pass = ipf_scanlist(fin, softc->ipf_pass); if ((pass & FR_NOMATCH)) { LBUMPD(ipf_stats[out], fr_nom); } fr = fin->fin_fr; /* * Apply packets per second rate-limiting to a rule as required. */ if ((fr != NULL) && (fr->fr_pps != 0) && !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) { DT2(frb_ppsrate, fr_info_t *, fin, frentry_t *, fr); pass &= ~(FR_CMDMASK|FR_RETICMP|FR_RETRST); pass |= FR_BLOCK; LBUMPD(ipf_stats[out], fr_ppshit); fin->fin_reason = FRB_PPSRATE; } /* * If we fail to add a packet to the authorization queue, then we * drop the packet later. However, if it was added then pretend * we've dropped it already. */ if (FR_ISAUTH(pass)) { if (ipf_auth_new(fin->fin_m, fin) != 0) { DT1(frb_authnew, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; fin->fin_reason = FRB_AUTHNEW; fin->fin_error = 0; } else { IPFERROR(1); fin->fin_error = ENOSPC; } } if ((fr != NULL) && (fr->fr_func != NULL) && (fr->fr_func != (ipfunc_t)-1) && !(pass & FR_CALLNOW)) (void) (*fr->fr_func)(fin, &pass); /* * If a rule is a pre-auth rule, check again in the list of rules * loaded for authenticated use. It does not particulary matter * if this search fails because a "preauth" result, from a rule, * is treated as "not a pass", hence the packet is blocked. */ if (FR_ISPREAUTH(pass)) { pass = ipf_auth_pre_scanlist(softc, fin, pass); } /* * If the rule has "keep frag" and the packet is actually a fragment, * then create a fragment state entry. */ if (pass & FR_KEEPFRAG) { if (fin->fin_flx & FI_FRAG) { if (ipf_frag_new(softc, fin, pass) == -1) { LBUMP(ipf_stats[out].fr_bnfr); } else { LBUMP(ipf_stats[out].fr_nfr); } } else { LBUMP(ipf_stats[out].fr_cfr); } } fr = fin->fin_fr; *passp = pass; return fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_check */ /* Returns: int - 0 == packet allowed through, */ /* User space: */ /* -1 == packet blocked */ /* 1 == packet not matched */ /* -2 == requires authentication */ /* Kernel: */ /* > 0 == filter error # for packet */ /* Parameters: ctx(I) - pointer to the instance context */ /* ip(I) - pointer to start of IPv4/6 packet */ /* hlen(I) - length of header */ /* ifp(I) - pointer to interface this packet is on */ /* out(I) - 0 == packet going in, 1 == packet going out */ /* mp(IO) - pointer to caller's buffer pointer that holds this */ /* IP packet. */ /* Solaris: */ /* qpi(I) - pointer to STREAMS queue information for this */ /* interface & direction. */ /* */ /* ipf_check() is the master function for all IPFilter packet processing. */ /* It orchestrates: Network Address Translation (NAT), checking for packet */ /* authorisation (or pre-authorisation), presence of related state info., */ /* generating log entries, IP packet accounting, routing of packets as */ /* directed by firewall rules and of course whether or not to allow the */ /* packet to be further processed by the kernel. */ /* */ /* For packets blocked, the contents of "mp" will be NULL'd and the buffer */ /* freed. Packets passed may be returned with the pointer pointed to by */ /* by "mp" changed to a new buffer. */ /* ------------------------------------------------------------------------ */ int ipf_check(ctx, ip, hlen, ifp, out #if defined(_KERNEL) && defined(MENTAT) , qif, mp) void *qif; #else , mp) #endif mb_t **mp; ip_t *ip; int hlen; struct ifnet *ifp; int out; void *ctx; { /* * The above really sucks, but short of writing a diff */ ipf_main_softc_t *softc = ctx; fr_info_t frinfo; fr_info_t *fin = &frinfo; u_32_t pass = softc->ipf_pass; frentry_t *fr = NULL; int v = IP_V(ip); mb_t *mc = NULL; mb_t *m; /* * The first part of ipf_check() deals with making sure that what goes * into the filtering engine makes some sense. Information about the * the packet is distilled, collected into a fr_info_t structure and * the an attempt to ensure the buffer the packet is in is big enough * to hold all the required packet headers. */ #ifdef _KERNEL # ifdef MENTAT qpktinfo_t *qpi = qif; # ifdef __sparc if ((u_int)ip & 0x3) return 2; # endif # else SPL_INT(s); # endif if (softc->ipf_running <= 0) { return 0; } bzero((char *)fin, sizeof(*fin)); # ifdef MENTAT if (qpi->qpi_flags & QF_BROADCAST) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; if (qpi->qpi_flags & QF_MULTICAST) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; m = qpi->qpi_m; fin->fin_qfm = m; fin->fin_qpi = qpi; # else /* MENTAT */ m = *mp; # if defined(M_MCAST) if ((m->m_flags & M_MCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_MLOOP) if ((m->m_flags & M_MLOOP) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_BCAST) if ((m->m_flags & M_BCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; # endif # ifdef M_CANFASTFWD /* * XXX For now, IP Filter and fast-forwarding of cached flows * XXX are mutually exclusive. Eventually, IP Filter should * XXX get a "can-fast-forward" filter rule. */ m->m_flags &= ~M_CANFASTFWD; # endif /* M_CANFASTFWD */ # if defined(CSUM_DELAY_DATA) && !defined(__FreeBSD_version) /* * disable delayed checksums. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } # endif /* CSUM_DELAY_DATA */ # endif /* MENTAT */ #else bzero((char *)fin, sizeof(*fin)); m = *mp; # if defined(M_MCAST) if ((m->m_flags & M_MCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_MLOOP) if ((m->m_flags & M_MLOOP) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_BCAST) if ((m->m_flags & M_BCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; # endif #endif /* _KERNEL */ fin->fin_v = v; fin->fin_m = m; fin->fin_ip = ip; fin->fin_mp = mp; fin->fin_out = out; fin->fin_ifp = ifp; fin->fin_error = ENETUNREACH; fin->fin_hlen = (u_short)hlen; fin->fin_dp = (char *)ip + hlen; fin->fin_main_soft = softc; fin->fin_ipoff = (char *)ip - MTOD(m, char *); SPL_NET(s); #ifdef USE_INET6 if (v == 6) { LBUMP(ipf_stats[out].fr_ipv6); /* * Jumbo grams are quite likely too big for internal buffer * structures to handle comfortably, for now, so just drop * them. */ if (((ip6_t *)ip)->ip6_plen == 0) { DT1(frb_jumbo, ip6_t *, (ip6_t *)ip); pass = FR_BLOCK|FR_NOMATCH; fin->fin_reason = FRB_JUMBO; goto finished; } fin->fin_family = AF_INET6; } else #endif { fin->fin_family = AF_INET; } if (ipf_makefrip(hlen, ip, fin) == -1) { DT1(frb_makefrip, fr_info_t *, fin); pass = FR_BLOCK|FR_NOMATCH; fin->fin_reason = FRB_MAKEFRIP; goto finished; } /* * For at least IPv6 packets, if a m_pullup() fails then this pointer * becomes NULL and so we have no packet to free. */ if (*fin->fin_mp == NULL) goto finished; if (!out) { if (v == 4) { if (softc->ipf_chksrc && !ipf_verifysrc(fin)) { LBUMPD(ipf_stats[0], fr_v4_badsrc); fin->fin_flx |= FI_BADSRC; } if (fin->fin_ip->ip_ttl < softc->ipf_minttl) { LBUMPD(ipf_stats[0], fr_v4_badttl); fin->fin_flx |= FI_LOWTTL; } } #ifdef USE_INET6 else if (v == 6) { if (((ip6_t *)ip)->ip6_hlim < softc->ipf_minttl) { LBUMPD(ipf_stats[0], fr_v6_badttl); fin->fin_flx |= FI_LOWTTL; } } #endif } if (fin->fin_flx & FI_SHORT) { LBUMPD(ipf_stats[out], fr_short); } READ_ENTER(&softc->ipf_mutex); if (!out) { switch (fin->fin_v) { case 4 : if (ipf_nat_checkin(fin, &pass) == -1) { goto filterdone; } break; #ifdef USE_INET6 case 6 : if (ipf_nat6_checkin(fin, &pass) == -1) { goto filterdone; } break; #endif default : break; } } /* * Check auth now. * If a packet is found in the auth table, then skip checking * the access lists for permission but we do need to consider * the result as if it were from the ACL's. In addition, being * found in the auth table means it has been seen before, so do * not pass it through accounting (again), lest it be counted twice. */ fr = ipf_auth_check(fin, &pass); if (!out && (fr == NULL)) (void) ipf_acctpkt(fin, NULL); if (fr == NULL) { if ((fin->fin_flx & FI_FRAG) != 0) fr = ipf_frag_known(fin, &pass); if (fr == NULL) fr = ipf_state_check(fin, &pass); } if ((pass & FR_NOMATCH) || (fr == NULL)) fr = ipf_firewall(fin, &pass); /* * If we've asked to track state for this packet, set it up. * Here rather than ipf_firewall because ipf_checkauth may decide * to return a packet for "keep state" */ if ((pass & FR_KEEPSTATE) && (fin->fin_m != NULL) && !(fin->fin_flx & FI_STATE)) { if (ipf_state_add(softc, fin, NULL, 0) == 0) { LBUMP(ipf_stats[out].fr_ads); } else { LBUMP(ipf_stats[out].fr_bads); if (FR_ISPASS(pass)) { DT(frb_stateadd); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_STATEADD; } } } fin->fin_fr = fr; if ((fr != NULL) && !(fin->fin_flx & FI_STATE)) { fin->fin_dif = &fr->fr_dif; fin->fin_tif = &fr->fr_tifs[fin->fin_rev]; } /* * Only count/translate packets which will be passed on, out the * interface. */ if (out && FR_ISPASS(pass)) { (void) ipf_acctpkt(fin, NULL); switch (fin->fin_v) { case 4 : if (ipf_nat_checkout(fin, &pass) == -1) { ; } else if ((softc->ipf_update_ipid != 0) && (v == 4)) { if (ipf_updateipid(fin) == -1) { DT(frb_updateipid); LBUMP(ipf_stats[1].fr_ipud); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_UPDATEIPID; } else { LBUMP(ipf_stats[0].fr_ipud); } } break; #ifdef USE_INET6 case 6 : (void) ipf_nat6_checkout(fin, &pass); break; #endif default : break; } } filterdone: #ifdef IPFILTER_LOG if ((softc->ipf_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { (void) ipf_dolog(fin, &pass); } #endif /* * The FI_STATE flag is cleared here so that calling ipf_state_check * will work when called from inside of fr_fastroute. Although * there is a similar flag, FI_NATED, for NAT, it does have the same * impact on code execution. */ fin->fin_flx &= ~FI_STATE; #if defined(FASTROUTE_RECURSION) /* * Up the reference on fr_lock and exit ipf_mutex. The generation of * a packet below can sometimes cause a recursive call into IPFilter. * On those platforms where that does happen, we need to hang onto * the filter rule just in case someone decides to remove or flush it * in the meantime. */ if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } RWLOCK_EXIT(&softc->ipf_mutex); #endif if ((pass & FR_RETMASK) != 0) { /* * Should we return an ICMP packet to indicate error * status passing through the packet filter ? * WARNING: ICMP error packets AND TCP RST packets should * ONLY be sent in repsonse to incoming packets. Sending * them in response to outbound packets can result in a * panic on some operating systems. */ if (!out) { if (pass & FR_RETICMP) { int dst; if ((pass & FR_RETMASK) == FR_FAKEICMP) dst = 1; else dst = 0; (void) ipf_send_icmp_err(ICMP_UNREACH, fin, dst); LBUMP(ipf_stats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_flx & FI_SHORT)) { if (((fin->fin_flx & FI_OOW) != 0) || (ipf_send_reset(fin) == 0)) { LBUMP(ipf_stats[1].fr_ret); } } /* * When using return-* with auth rules, the auth code * takes over disposing of this packet. */ if (FR_ISAUTH(pass) && (fin->fin_m != NULL)) { DT1(frb_authcapture, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; fin->fin_reason = FRB_AUTHCAPTURE; m = NULL; } } else { if (pass & FR_RETRST) { fin->fin_error = ECONNRESET; } } } /* * After the above so that ICMP unreachables and TCP RSTs get * created properly. */ if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) ipf_nat_uncreate(fin); /* * If we didn't drop off the bottom of the list of rules (and thus * the 'current' rule fr is not NULL), then we may have some extra * instructions about what to do with a packet. * Once we're finished return to our caller, freeing the packet if * we are dropping it. */ if (fr != NULL) { frdest_t *fdp; /* * Generate a duplicated packet first because ipf_fastroute * can lead to fin_m being free'd... not good. */ fdp = fin->fin_dif; if ((fdp != NULL) && (fdp->fd_ptr != NULL) && (fdp->fd_ptr != (void *)-1)) { mc = M_COPY(fin->fin_m); if (mc != NULL) ipf_fastroute(mc, &mc, fin, fdp); } fdp = fin->fin_tif; if (!out && (pass & FR_FASTROUTE)) { /* * For fastroute rule, no destination interface defined * so pass NULL as the frdest_t parameter */ (void) ipf_fastroute(fin->fin_m, mp, fin, NULL); m = *mp = NULL; } else if ((fdp != NULL) && (fdp->fd_ptr != NULL) && (fdp->fd_ptr != (struct ifnet *)-1)) { /* this is for to rules: */ ipf_fastroute(fin->fin_m, mp, fin, fdp); m = *mp = NULL; } #if defined(FASTROUTE_RECURSION) (void) ipf_derefrule(softc, &fr); #endif } #if !defined(FASTROUTE_RECURSION) RWLOCK_EXIT(&softc->ipf_mutex); #endif finished: if (!FR_ISPASS(pass)) { LBUMP(ipf_stats[out].fr_block); if (*mp != NULL) { #ifdef _KERNEL FREE_MB_T(*mp); #endif m = *mp = NULL; } } else { LBUMP(ipf_stats[out].fr_pass); } SPL_X(s); #ifdef _KERNEL if (FR_ISPASS(pass)) return 0; LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); return fin->fin_error; #else /* _KERNEL */ if (*mp != NULL) (*mp)->mb_ifp = fin->fin_ifp; blockreason = fin->fin_reason; FR_VERBOSE(("fin_flx %#x pass %#x ", fin->fin_flx, pass)); /*if ((pass & FR_CMDMASK) == (softc->ipf_pass & FR_CMDMASK))*/ if ((pass & FR_NOMATCH) != 0) return 1; if ((pass & FR_RETMASK) != 0) switch (pass & FR_RETMASK) { case FR_RETRST : return 3; case FR_RETICMP : return 4; case FR_FAKEICMP : return 5; } switch (pass & FR_CMDMASK) { case FR_PASS : return 0; case FR_BLOCK : return -1; case FR_AUTH : return -2; case FR_ACCOUNT : return -3; case FR_PREAUTH : return -4; } return 2; #endif /* _KERNEL */ } #ifdef IPFILTER_LOG /* ------------------------------------------------------------------------ */ /* Function: ipf_dolog */ /* Returns: frentry_t* - returns contents of fin_fr (no change made) */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Checks flags set to see how a packet should be logged, if it is to be */ /* logged. Adjust statistics based on its success or not. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_dolog(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc = fin->fin_main_soft; u_32_t pass; int out; out = fin->fin_out; pass = *passp; if ((softc->ipf_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { pass |= FF_LOGNOMATCH; LBUMPD(ipf_stats[out], fr_npkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGP) || (FR_ISPASS(pass) && (softc->ipf_flags & FF_LOGPASS))) { if ((pass & FR_LOGMASK) != FR_LOGP) pass |= FF_LOGPASS; LBUMPD(ipf_stats[out], fr_ppkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGB) || (FR_ISBLOCK(pass) && (softc->ipf_flags & FF_LOGBLOCK))) { if ((pass & FR_LOGMASK) != FR_LOGB) pass |= FF_LOGBLOCK; LBUMPD(ipf_stats[out], fr_bpkl); logit: if (ipf_log_pkt(fin, pass) == -1) { /* * If the "or-block" option has been used then * block the packet if we failed to log it. */ if ((pass & FR_LOGORBLOCK) && FR_ISPASS(pass)) { DT1(frb_logfail2, u_int, pass); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_LOGFAIL2; } } *passp = pass; } return fin->fin_fr; } #endif /* IPFILTER_LOG */ /* ------------------------------------------------------------------------ */ /* Function: ipf_cksum */ /* Returns: u_short - IP header checksum */ /* Parameters: addr(I) - pointer to start of buffer to checksum */ /* len(I) - length of buffer in bytes */ /* */ /* Calculate the two's complement 16 bit checksum of the buffer passed. */ /* */ /* N.B.: addr should be 16bit aligned. */ /* ------------------------------------------------------------------------ */ u_short ipf_cksum(addr, len) u_short *addr; int len; { u_32_t sum = 0; for (sum = 0; len > 1; len -= 2) sum += *addr++; /* mop up an odd byte, if necessary */ if (len == 1) sum += *(u_char *)addr; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ return (u_short)(~sum); } /* ------------------------------------------------------------------------ */ /* Function: fr_cksum */ /* Returns: u_short - layer 4 checksum */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to IP header */ /* l4proto(I) - protocol to caclulate checksum for */ /* l4hdr(I) - pointer to layer 4 header */ /* */ /* Calculates the TCP checksum for the packet held in "m", using the data */ /* in the IP header "ip" to seed it. */ /* */ /* NB: This function assumes we've pullup'd enough for all of the IP header */ /* and the TCP header. We also assume that data blocks aren't allocated in */ /* odd sizes. */ /* */ /* Expects ip_len and ip_off to be in network byte order when called. */ /* ------------------------------------------------------------------------ */ u_short fr_cksum(fin, ip, l4proto, l4hdr) fr_info_t *fin; ip_t *ip; int l4proto; void *l4hdr; { u_short *sp, slen, sumsave, *csump; u_int sum, sum2; int hlen; int off; #ifdef USE_INET6 ip6_t *ip6; #endif csump = NULL; sumsave = 0; sp = NULL; slen = 0; hlen = 0; sum = 0; sum = htons((u_short)l4proto); /* * Add up IP Header portion */ #ifdef USE_INET6 if (IP_V(ip) == 4) { #endif hlen = IP_HL(ip) << 2; off = hlen; sp = (u_short *)&ip->ip_src; sum += *sp++; /* ip_src */ sum += *sp++; sum += *sp++; /* ip_dst */ sum += *sp++; slen = fin->fin_plen - off; sum += htons(slen); #ifdef USE_INET6 } else if (IP_V(ip) == 6) { mb_t *m; m = fin->fin_m; ip6 = (ip6_t *)ip; off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr); int len = ntohs(ip6->ip6_plen) - (off - sizeof(*ip6)); return(ipf_pcksum6(m, ip6, off, len)); } else { return 0xffff; } #endif switch (l4proto) { case IPPROTO_UDP : csump = &((udphdr_t *)l4hdr)->uh_sum; break; case IPPROTO_TCP : csump = &((tcphdr_t *)l4hdr)->th_sum; break; case IPPROTO_ICMP : csump = &((icmphdr_t *)l4hdr)->icmp_cksum; sum = 0; /* Pseudo-checksum is not included */ break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : csump = &((struct icmp6_hdr *)l4hdr)->icmp6_cksum; break; #endif default : break; } if (csump != NULL) { sumsave = *csump; *csump = 0; } sum2 = ipf_pcksum(fin, off, sum); if (csump != NULL) *csump = sumsave; return sum2; } /* ------------------------------------------------------------------------ */ /* Function: ipf_findgroup */ /* Returns: frgroup_t * - NULL = group not found, else pointer to group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* group(I) - group name to search for */ /* unit(I) - device to which this group belongs */ /* set(I) - which set of rules (inactive/inactive) this is */ /* fgpp(O) - pointer to place to store pointer to the pointer */ /* to where to add the next (last) group or where */ /* to delete group from. */ /* */ /* Search amongst the defined groups for a particular group number. */ /* ------------------------------------------------------------------------ */ frgroup_t * ipf_findgroup(softc, group, unit, set, fgpp) ipf_main_softc_t *softc; char *group; minor_t unit; int set; frgroup_t ***fgpp; { frgroup_t *fg, **fgp; /* * Which list of groups to search in is dependent on which list of * rules are being operated on. */ fgp = &softc->ipf_groups[unit][set]; while ((fg = *fgp) != NULL) { if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0) break; else fgp = &fg->fg_next; } if (fgpp != NULL) *fgpp = fgp; return fg; } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_add */ /* Returns: frgroup_t * - NULL == did not create group, */ /* != NULL == pointer to the group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* num(I) - group number to add */ /* head(I) - rule pointer that is using this as the head */ /* flags(I) - rule flags which describe the type of rule it is */ /* unit(I) - device to which this group will belong to */ /* set(I) - which set of rules (inactive/inactive) this is */ /* Write Locks: ipf_mutex */ /* */ /* Add a new group head, or if it already exists, increase the reference */ /* count to it. */ /* ------------------------------------------------------------------------ */ frgroup_t * ipf_group_add(softc, group, head, flags, unit, set) ipf_main_softc_t *softc; char *group; void *head; u_32_t flags; minor_t unit; int set; { frgroup_t *fg, **fgp; u_32_t gflags; if (group == NULL) return NULL; if (unit == IPL_LOGIPF && *group == '\0') return NULL; fgp = NULL; gflags = flags & FR_INOUT; fg = ipf_findgroup(softc, group, unit, set, &fgp); if (fg != NULL) { if (fg->fg_head == NULL && head != NULL) fg->fg_head = head; if (fg->fg_flags == 0) fg->fg_flags = gflags; else if (gflags != fg->fg_flags) return NULL; fg->fg_ref++; return fg; } KMALLOC(fg, frgroup_t *); if (fg != NULL) { fg->fg_head = head; fg->fg_start = NULL; fg->fg_next = *fgp; bcopy(group, fg->fg_name, strlen(group) + 1); fg->fg_flags = gflags; fg->fg_ref = 1; fg->fg_set = &softc->ipf_groups[unit][set]; *fgp = fg; } return fg; } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_del */ /* Returns: int - number of rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* group(I) - group name to delete */ /* fr(I) - filter rule from which group is referenced */ /* Write Locks: ipf_mutex */ /* */ /* This function is called whenever a reference to a group is to be dropped */ /* and thus its reference count needs to be lowered and the group free'd if */ /* the reference count reaches zero. Passing in fr is really for the sole */ /* purpose of knowing when the head rule is being deleted. */ /* ------------------------------------------------------------------------ */ void ipf_group_del(softc, group, fr) ipf_main_softc_t *softc; frgroup_t *group; frentry_t *fr; { if (group->fg_head == fr) group->fg_head = NULL; group->fg_ref--; if ((group->fg_ref == 0) && (group->fg_start == NULL)) ipf_group_free(group); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_free */ /* Returns: Nil */ /* Parameters: group(I) - pointer to filter rule group */ /* */ /* Remove the group from the list of groups and free it. */ /* ------------------------------------------------------------------------ */ static void ipf_group_free(group) frgroup_t *group; { frgroup_t **gp; for (gp = group->fg_set; *gp != NULL; gp = &(*gp)->fg_next) { if (*gp == group) { *gp = group->fg_next; break; } } KFREE(group); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_flush */ /* Returns: int - number of rules flush from group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: group(I) - pointer to filter rule group */ /* */ /* Remove all of the rules that currently are listed under the given group. */ /* ------------------------------------------------------------------------ */ static int ipf_group_flush(softc, group) ipf_main_softc_t *softc; frgroup_t *group; { int gone = 0; (void) ipf_flushlist(softc, &gone, &group->fg_start); return gone; } /* ------------------------------------------------------------------------ */ /* Function: ipf_getrulen */ /* Returns: frentry_t * - NULL == not found, else pointer to rule n */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: unit(I) - device for which to count the rule's number */ /* flags(I) - which set of rules to find the rule in */ /* group(I) - group name */ /* n(I) - rule number to find */ /* */ /* Find rule # n in group # g and return a pointer to it. Return NULl if */ /* group # g doesn't exist or there are less than n rules in the group. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_getrulen(softc, unit, group, n) ipf_main_softc_t *softc; int unit; char *group; u_32_t n; { frentry_t *fr; frgroup_t *fg; fg = ipf_findgroup(softc, group, unit, softc->ipf_active, NULL); if (fg == NULL) return NULL; for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--) ; if (n != 0) return NULL; return fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_flushlist */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nfreedp(O) - pointer to int where flush count is stored */ /* listp(I) - pointer to list to flush pointer */ /* Write Locks: ipf_mutex */ /* */ /* Recursively flush rules from the list, descending groups as they are */ /* encountered. if a rule is the head of a group and it has lost all its */ /* group members, then also delete the group reference. nfreedp is needed */ /* to store the accumulating count of rules removed, whereas the returned */ /* value is just the number removed from the current list. The latter is */ /* needed to correctly adjust reference counts on rules that define groups. */ /* */ /* NOTE: Rules not loaded from user space cannot be flushed. */ /* ------------------------------------------------------------------------ */ static int ipf_flushlist(softc, nfreedp, listp) ipf_main_softc_t *softc; int *nfreedp; frentry_t **listp; { int freed = 0; frentry_t *fp; while ((fp = *listp) != NULL) { if ((fp->fr_type & FR_T_BUILTIN) || !(fp->fr_flags & FR_COPIED)) { listp = &fp->fr_next; continue; } *listp = fp->fr_next; if (fp->fr_next != NULL) fp->fr_next->fr_pnext = fp->fr_pnext; fp->fr_pnext = NULL; if (fp->fr_grphead != NULL) { freed += ipf_group_flush(softc, fp->fr_grphead); fp->fr_names[fp->fr_grhead] = '\0'; } if (fp->fr_icmpgrp != NULL) { freed += ipf_group_flush(softc, fp->fr_icmpgrp); fp->fr_names[fp->fr_icmphead] = '\0'; } if (fp->fr_srctrack.ht_max_nodes) ipf_rb_ht_flush(&fp->fr_srctrack); fp->fr_next = NULL; ASSERT(fp->fr_ref > 0); if (ipf_derefrule(softc, &fp) == 0) freed++; } *nfreedp += freed; return freed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_flush */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which to flush rules */ /* flags(I) - which set of rules to flush */ /* */ /* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */ /* and IPv6) as defined by the value of flags. */ /* ------------------------------------------------------------------------ */ int ipf_flush(softc, unit, flags) ipf_main_softc_t *softc; minor_t unit; int flags; { int flushed = 0, set; WRITE_ENTER(&softc->ipf_mutex); set = softc->ipf_active; if ((flags & FR_INACTIVE) == FR_INACTIVE) set = 1 - set; if (flags & FR_OUTQUE) { ipf_flushlist(softc, &flushed, &softc->ipf_rules[1][set]); ipf_flushlist(softc, &flushed, &softc->ipf_acct[1][set]); } if (flags & FR_INQUE) { ipf_flushlist(softc, &flushed, &softc->ipf_rules[0][set]); ipf_flushlist(softc, &flushed, &softc->ipf_acct[0][set]); } flushed += ipf_flush_groups(softc, &softc->ipf_groups[unit][set], flags & (FR_INQUE|FR_OUTQUE)); RWLOCK_EXIT(&softc->ipf_mutex); if (unit == IPL_LOGIPF) { int tmp; tmp = ipf_flush(softc, IPL_LOGCOUNT, flags); if (tmp >= 0) flushed += tmp; } return flushed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_flush_groups */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - soft context pointerto work with */ /* grhead(I) - pointer to the start of the group list to flush */ /* flags(I) - which set of rules to flush */ /* */ /* Walk through all of the groups under the given group head and remove all */ /* of those that match the flags passed in. The for loop here is bit more */ /* complicated than usual because the removal of a rule with ipf_derefrule */ /* may end up removing not only the structure pointed to by "fg" but also */ /* what is fg_next and fg_next after that. So if a filter rule is actually */ /* removed from the group then it is necessary to start again. */ /* ------------------------------------------------------------------------ */ static int ipf_flush_groups(softc, grhead, flags) ipf_main_softc_t *softc; frgroup_t **grhead; int flags; { frentry_t *fr, **frp; frgroup_t *fg, **fgp; int flushed = 0; int removed = 0; for (fgp = grhead; (fg = *fgp) != NULL; ) { while ((fg != NULL) && ((fg->fg_flags & flags) == 0)) fg = fg->fg_next; if (fg == NULL) break; removed = 0; frp = &fg->fg_start; while ((removed == 0) && ((fr = *frp) != NULL)) { if ((fr->fr_flags & flags) == 0) { frp = &fr->fr_next; } else { if (fr->fr_next != NULL) fr->fr_next->fr_pnext = fr->fr_pnext; *frp = fr->fr_next; fr->fr_pnext = NULL; fr->fr_next = NULL; (void) ipf_derefrule(softc, &fr); flushed++; removed++; } } if (removed == 0) fgp = &fg->fg_next; } return flushed; } /* ------------------------------------------------------------------------ */ /* Function: memstr */ /* Returns: char * - NULL if failed, != NULL pointer to matching bytes */ /* Parameters: src(I) - pointer to byte sequence to match */ /* dst(I) - pointer to byte sequence to search */ /* slen(I) - match length */ /* dlen(I) - length available to search in */ /* */ /* Search dst for a sequence of bytes matching those at src and extend for */ /* slen bytes. */ /* ------------------------------------------------------------------------ */ char * memstr(src, dst, slen, dlen) const char *src; char *dst; size_t slen, dlen; { char *s = NULL; while (dlen >= slen) { if (bcmp(src, dst, slen) == 0) { s = dst; break; } dst++; dlen--; } return s; } /* ------------------------------------------------------------------------ */ /* Function: ipf_fixskip */ /* Returns: Nil */ /* Parameters: listp(IO) - pointer to start of list with skip rule */ /* rp(I) - rule added/removed with skip in it. */ /* addremove(I) - adjustment (-1/+1) to make to skip count, */ /* depending on whether a rule was just added */ /* or removed. */ /* */ /* Adjust all the rules in a list which would have skip'd past the position */ /* where we are inserting to skip to the right place given the change. */ /* ------------------------------------------------------------------------ */ void ipf_fixskip(listp, rp, addremove) frentry_t **listp, *rp; int addremove; { int rules, rn; frentry_t *fp; rules = 0; for (fp = *listp; (fp != NULL) && (fp != rp); fp = fp->fr_next) rules++; if (fp == NULL) return; for (rn = 0, fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) if (FR_ISSKIP(fp->fr_flags) && (rn + fp->fr_arg >= rules)) fp->fr_arg += addremove; } #ifdef _KERNEL /* ------------------------------------------------------------------------ */ /* Function: count4bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ /* Parameters: ip(I) - 32bit IP address */ /* */ /* IPv4 ONLY */ /* 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_32_t ip; { u_32_t ipn; int cnt = 0, i, j; ip = ipn = ntohl(ip); for (i = 32; i; i--, ipn *= 2) if (ipn & 0x80000000) cnt++; else break; ipn = 0; for (i = 32, j = cnt; i; i--, j--) { ipn *= 2; if (j > 0) ipn++; } if (ipn == ip) return cnt; return -1; } /* ------------------------------------------------------------------------ */ /* Function: count6bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ /* Parameters: msk(I) - pointer to start of IPv6 bitmask */ /* */ /* IPv6 ONLY */ /* count consecutive 1's in bit mask. */ /* ------------------------------------------------------------------------ */ # ifdef USE_INET6 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; } # endif #endif /* _KERNEL */ /* ------------------------------------------------------------------------ */ /* Function: ipf_synclist */ /* Returns: int - 0 = no failures, else indication of first failure */ /* Parameters: fr(I) - start of filter list to sync interface names for */ /* ifp(I) - interface pointer for limiting sync lookups */ /* Write Locks: ipf_mutex */ /* */ /* Walk through a list of filter rules and resolve any interface names into */ /* pointers. Where dynamic addresses are used, also update the IP address */ /* used in the rule. The interface pointer is used to limit the lookups to */ /* a specific set of matching names if it is non-NULL. */ /* Errors can occur when resolving the destination name of to/dup-to fields */ /* when the name points to a pool and that pool doest not exist. If this */ /* does happen then it is necessary to check if there are any lookup refs */ /* that need to be dropped before returning with an error. */ /* ------------------------------------------------------------------------ */ static int ipf_synclist(softc, fr, ifp) ipf_main_softc_t *softc; frentry_t *fr; void *ifp; { frentry_t *frt, *start = fr; frdest_t *fdp; char *name; int error; void *ifa; int v, i; error = 0; for (; fr; fr = fr->fr_next) { if (fr->fr_family == AF_INET) v = 4; else if (fr->fr_family == AF_INET6) v = 6; else v = 0; /* * Lookup all the interface names that are part of the rule. */ for (i = 0; i < FR_NUM(fr->fr_ifas); i++) { if ((ifp != NULL) && (fr->fr_ifas[i] != ifp)) continue; if (fr->fr_ifnames[i] == -1) continue; name = FR_NAME(fr, fr_ifnames[i]); fr->fr_ifas[i] = ipf_resolvenic(softc, name, v); } if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if (fr->fr_satype != FRI_NORMAL && fr->fr_satype != FRI_LOOKUP) { ifa = ipf_resolvenic(softc, fr->fr_names + fr->fr_sifpidx, v); ipf_ifpaddr(softc, v, fr->fr_satype, ifa, &fr->fr_src6, &fr->fr_smsk6); } if (fr->fr_datype != FRI_NORMAL && fr->fr_datype != FRI_LOOKUP) { ifa = ipf_resolvenic(softc, fr->fr_names + fr->fr_sifpidx, v); ipf_ifpaddr(softc, v, fr->fr_datype, ifa, &fr->fr_dst6, &fr->fr_dmsk6); } } fdp = &fr->fr_tifs[0]; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } fdp = &fr->fr_tifs[1]; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } fdp = &fr->fr_dif; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (fr->fr_satype == FRI_LOOKUP) && (fr->fr_srcptr == NULL)) { fr->fr_srcptr = ipf_lookup_res_num(softc, fr->fr_srctype, IPL_LOGIPF, fr->fr_srcnum, &fr->fr_srcfunc); } if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (fr->fr_datype == FRI_LOOKUP) && (fr->fr_dstptr == NULL)) { fr->fr_dstptr = ipf_lookup_res_num(softc, fr->fr_dsttype, IPL_LOGIPF, fr->fr_dstnum, &fr->fr_dstfunc); } } return 0; unwind: for (frt = start; frt != fr; fr = fr->fr_next) { if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (frt->fr_satype == FRI_LOOKUP) && (frt->fr_srcptr != NULL)) ipf_lookup_deref(softc, frt->fr_srctype, frt->fr_srcptr); if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (frt->fr_datype == FRI_LOOKUP) && (frt->fr_dstptr != NULL)) ipf_lookup_deref(softc, frt->fr_dsttype, frt->fr_dstptr); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync */ /* Returns: void */ /* Parameters: Nil */ /* */ /* ipf_sync() is called when we suspect that the interface list or */ /* information about interfaces (like IP#) has changed. Go through all */ /* filter rules, NAT entries and the state table and check if anything */ /* needs to be changed/updated. */ /* ------------------------------------------------------------------------ */ int ipf_sync(softc, ifp) ipf_main_softc_t *softc; void *ifp; { int i; # if !SOLARIS ipf_nat_sync(softc, ifp); ipf_state_sync(softc, ifp); ipf_lookup_sync(softc, ifp); # endif WRITE_ENTER(&softc->ipf_mutex); (void) ipf_synclist(softc, softc->ipf_acct[0][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_acct[1][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_rules[0][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_rules[1][softc->ipf_active], ifp); for (i = 0; i < IPL_LOGSIZE; i++) { frgroup_t *g; for (g = softc->ipf_groups[i][0]; g != NULL; g = g->fg_next) (void) ipf_synclist(softc, g->fg_start, ifp); for (g = softc->ipf_groups[i][1]; g != NULL; g = g->fg_next) (void) ipf_synclist(softc, g->fg_start, ifp); } RWLOCK_EXIT(&softc->ipf_mutex); return 0; } /* * In the functions below, bcopy() is called because the pointer being * copied _from_ in this instance is a pointer to a char buf (which could * end up being unaligned) and on the kernel's local stack. */ /* ------------------------------------------------------------------------ */ /* Function: copyinptr */ /* Returns: int - 0 = success, else failure */ /* Parameters: src(I) - pointer to the source address */ /* dst(I) - destination address */ /* size(I) - number of bytes to copy */ /* */ /* Copy a block of data in from user space, given a pointer to the pointer */ /* to start copying from (src) and a pointer to where to store it (dst). */ /* NB: src - pointer to user space pointer, dst - kernel space pointer */ /* ------------------------------------------------------------------------ */ int copyinptr(softc, src, dst, size) ipf_main_softc_t *softc; void *src, *dst; size_t size; { caddr_t ca; int error; # if SOLARIS error = COPYIN(src, &ca, sizeof(ca)); if (error != 0) return error; # else bcopy(src, (caddr_t)&ca, sizeof(ca)); # endif error = COPYIN(ca, dst, size); if (error != 0) { IPFERROR(3); error = EFAULT; } return error; } /* ------------------------------------------------------------------------ */ /* Function: copyoutptr */ /* Returns: int - 0 = success, else failure */ /* Parameters: src(I) - pointer to the source address */ /* dst(I) - destination address */ /* size(I) - number of bytes to copy */ /* */ /* Copy a block of data out to user space, given a pointer to the pointer */ /* to start copying from (src) and a pointer to where to store it (dst). */ /* NB: src - kernel space pointer, dst - pointer to user space pointer. */ /* ------------------------------------------------------------------------ */ int copyoutptr(softc, src, dst, size) ipf_main_softc_t *softc; void *src, *dst; size_t size; { caddr_t ca; int error; bcopy(dst, (caddr_t)&ca, sizeof(ca)); error = COPYOUT(src, ca, size); if (error != 0) { IPFERROR(4); error = EFAULT; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lock */ /* Returns: int - 0 = success, else error */ /* Parameters: data(I) - pointer to lock value to set */ /* lockp(O) - pointer to location to store old lock value */ /* */ /* Get the new value for the lock integer, set it and return the old value */ /* in *lockp. */ /* ------------------------------------------------------------------------ */ int ipf_lock(data, lockp) caddr_t data; int *lockp; { int arg, err; err = BCOPYIN(data, &arg, sizeof(arg)); if (err != 0) return EFAULT; err = BCOPYOUT(lockp, data, sizeof(*lockp)); if (err != 0) return EFAULT; *lockp = arg; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_getstat */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fiop(I) - pointer to ipfilter stats structure */ /* rev(I) - version claim by program doing ioctl */ /* */ /* Stores a copy of current pointers, counters, etc, in the friostat */ /* structure. */ /* If IPFILTER_COMPAT is compiled, we pretend to be whatever version the */ /* program is looking for. This ensure that validation of the version it */ /* expects will always succeed. Thus kernels with IPFILTER_COMPAT will */ /* allow older binaries to work but kernels without it will not. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static void ipf_getstat(softc, fiop, rev) ipf_main_softc_t *softc; friostat_t *fiop; int rev; { int i; bcopy((char *)softc->ipf_stats, (char *)fiop->f_st, sizeof(ipf_statistics_t) * 2); fiop->f_locks[IPL_LOGSTATE] = -1; fiop->f_locks[IPL_LOGNAT] = -1; fiop->f_locks[IPL_LOGIPF] = -1; fiop->f_locks[IPL_LOGAUTH] = -1; fiop->f_ipf[0][0] = softc->ipf_rules[0][0]; fiop->f_acct[0][0] = softc->ipf_acct[0][0]; fiop->f_ipf[0][1] = softc->ipf_rules[0][1]; fiop->f_acct[0][1] = softc->ipf_acct[0][1]; fiop->f_ipf[1][0] = softc->ipf_rules[1][0]; fiop->f_acct[1][0] = softc->ipf_acct[1][0]; fiop->f_ipf[1][1] = softc->ipf_rules[1][1]; fiop->f_acct[1][1] = softc->ipf_acct[1][1]; fiop->f_ticks = softc->ipf_ticks; fiop->f_active = softc->ipf_active; fiop->f_froute[0] = softc->ipf_frouteok[0]; fiop->f_froute[1] = softc->ipf_frouteok[1]; fiop->f_rb_no_mem = softc->ipf_rb_no_mem; fiop->f_rb_node_max = softc->ipf_rb_node_max; fiop->f_running = softc->ipf_running; for (i = 0; i < IPL_LOGSIZE; i++) { fiop->f_groups[i][0] = softc->ipf_groups[i][0]; fiop->f_groups[i][1] = softc->ipf_groups[i][1]; } #ifdef IPFILTER_LOG fiop->f_log_ok = ipf_log_logok(softc, IPL_LOGIPF); fiop->f_log_fail = ipf_log_failures(softc, IPL_LOGIPF); fiop->f_logging = 1; #else fiop->f_log_ok = 0; fiop->f_log_fail = 0; fiop->f_logging = 0; #endif fiop->f_defpass = softc->ipf_pass; fiop->f_features = ipf_features; #ifdef IPFILTER_COMPAT sprintf(fiop->f_version, "IP Filter: v%d.%d.%d", (rev / 1000000) % 100, (rev / 10000) % 100, (rev / 100) % 100); #else rev = rev; (void) strncpy(fiop->f_version, ipfilter_version, sizeof(fiop->f_version)); #endif } #ifdef USE_INET6 int icmptoicmp6types[ICMP_MAXTYPE+1] = { ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */ -1, /* 1: UNUSED */ -1, /* 2: UNUSED */ ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */ -1, /* 4: ICMP_SOURCEQUENCH */ ND_REDIRECT, /* 5: ICMP_REDIRECT */ -1, /* 6: UNUSED */ -1, /* 7: UNUSED */ ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */ -1, /* 9: UNUSED */ -1, /* 10: UNUSED */ ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */ ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */ -1, /* 13: ICMP_TSTAMP */ -1, /* 14: ICMP_TSTAMPREPLY */ -1, /* 15: ICMP_IREQ */ -1, /* 16: ICMP_IREQREPLY */ -1, /* 17: ICMP_MASKREQ */ -1, /* 18: ICMP_MASKREPLY */ }; int icmptoicmp6unreach[ICMP_MAX_UNREACH] = { ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */ ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */ -1, /* 2: ICMP_UNREACH_PROTOCOL */ ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */ -1, /* 4: ICMP_UNREACH_NEEDFRAG */ ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */ ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */ ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */ -1, /* 8: ICMP_UNREACH_ISOLATED */ ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */ ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */ -1, /* 11: ICMP_UNREACH_TOSNET */ -1, /* 12: ICMP_UNREACH_TOSHOST */ ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */ }; int icmpreplytype6[ICMP6_MAXTYPE + 1]; #endif int icmpreplytype4[ICMP_MAXTYPE + 1]; /* ------------------------------------------------------------------------ */ /* Function: ipf_matchicmpqueryreply */ /* Returns: int - 1 if "icmp" is a valid reply to "ic" else 0. */ /* Parameters: v(I) - IP protocol version (4 or 6) */ /* ic(I) - ICMP information */ /* icmp(I) - ICMP packet header */ /* rev(I) - direction (0 = forward/1 = reverse) of packet */ /* */ /* Check if the ICMP packet defined by the header pointed to by icmp is a */ /* reply to one as described by what's in ic. If it is a match, return 1, */ /* else return 0 for no match. */ /* ------------------------------------------------------------------------ */ int ipf_matchicmpqueryreply(v, ic, icmp, rev) int v; icmpinfo_t *ic; icmphdr_t *icmp; int rev; { int ictype; ictype = ic->ici_type; if (v == 4) { /* * If we matched its type on the way in, then when going out * it will still be the same type. */ if ((!rev && (icmp->icmp_type == ictype)) || (rev && (icmpreplytype4[ictype] == icmp->icmp_type))) { if (icmp->icmp_type != ICMP_ECHOREPLY) return 1; if (icmp->icmp_id == ic->ici_id) return 1; } } #ifdef USE_INET6 else if (v == 6) { if ((!rev && (icmp->icmp_type == ictype)) || (rev && (icmpreplytype6[ictype] == icmp->icmp_type))) { if (icmp->icmp_type != ICMP6_ECHO_REPLY) return 1; if (icmp->icmp_id == ic->ici_id) return 1; } } #endif return 0; } /* * IFNAMES are located in the variable length field starting at * frentry.fr_names. As pointers within the struct cannot be passed * to the kernel from ipf(8), an offset is used. An offset of -1 means it * is unused (invalid). If it is used (valid) it is an offset to the * character string of an interface name or a comment. The following * macros will assist those who follow to understand the code. */ #define IPF_IFNAME_VALID(_a) (_a != -1) #define IPF_IFNAME_INVALID(_a) (_a == -1) #define IPF_IFNAMES_DIFFERENT(_a) \ !((IPF_IFNAME_INVALID(fr1->_a) && \ IPF_IFNAME_INVALID(fr2->_a)) || \ (IPF_IFNAME_VALID(fr1->_a) && \ IPF_IFNAME_VALID(fr2->_a) && \ !strcmp(FR_NAME(fr1, _a), FR_NAME(fr2, _a)))) #define IPF_FRDEST_DIFFERENT(_a) \ (memcmp(&fr1->_a.fd_addr, &fr2->_a.fd_addr, \ offsetof(frdest_t, fd_name) - offsetof(frdest_t, fd_addr)) || \ IPF_IFNAMES_DIFFERENT(_a.fd_name)) /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_compare */ /* Parameters: fr1(I) - first rule structure to compare */ /* fr2(I) - second rule structure to compare */ /* Returns: int - 0 == rules are the same, else mismatch */ /* */ /* Compare two rules and return 0 if they match or a number indicating */ /* which of the individual checks failed. */ /* ------------------------------------------------------------------------ */ static int ipf_rule_compare(frentry_t *fr1, frentry_t *fr2) { int i; if (fr1->fr_cksum != fr2->fr_cksum) return (1); if (fr1->fr_size != fr2->fr_size) return (2); if (fr1->fr_dsize != fr2->fr_dsize) return (3); if (bcmp((char *)&fr1->fr_func, (char *)&fr2->fr_func, FR_CMPSIZ) != 0) return (4); /* * XXX: There is still a bug here as different rules with the * the same interfaces but in a different order will compare * differently. But since multiple interfaces in a rule doesn't * work anyway a simple straightforward compare is performed * here. Ultimately frentry_t creation will need to be * revisited in ipf_y.y. While the other issue, recognition * of only the first interface in a list of interfaces will * need to be separately addressed along with why only four. */ for (i = 0; i < FR_NUM(fr1->fr_ifnames); i++) { /* * XXX: It's either the same index or uninitialized. * We assume this because multiple interfaces * referenced by the same rule doesn't work anyway. */ if (IPF_IFNAMES_DIFFERENT(fr_ifnames[i])) return(5); } if (IPF_FRDEST_DIFFERENT(fr_tif)) return (6); if (IPF_FRDEST_DIFFERENT(fr_rif)) return (7); if (IPF_FRDEST_DIFFERENT(fr_dif)) return (8); if (!fr1->fr_data && !fr2->fr_data) return (0); /* move along, nothing to see here */ if (fr1->fr_data && fr2->fr_data) { if (bcmp(fr1->fr_caddr, fr2->fr_caddr, fr1->fr_dsize) == 0) return (0); /* same */ } return (9); } /* ------------------------------------------------------------------------ */ /* Function: frrequest */ /* Returns: int - 0 == success, > 0 == errno value */ /* Parameters: unit(I) - device for which this is for */ /* req(I) - ioctl command (SIOC*) */ /* data(I) - pointr to ioctl data */ /* set(I) - 1 or 0 (filter set) */ /* makecopy(I) - flag indicating whether data points to a rule */ /* in kernel space & hence doesn't need copying. */ /* */ /* This function handles all the requests which operate on the list of */ /* filter rules. This includes adding, deleting, insertion. It is also */ /* responsible for creating groups when a "head" rule is loaded. Interface */ /* names are resolved here and other sanity checks are made on the content */ /* of the rule structure being loaded. If a rule has user defined timeouts */ /* then make sure they are created and initialised before exiting. */ /* ------------------------------------------------------------------------ */ int frrequest(softc, unit, req, data, set, makecopy) ipf_main_softc_t *softc; int unit; ioctlcmd_t req; int set, makecopy; caddr_t data; { int error = 0, in, family, need_free = 0; enum { OP_ADD, /* add rule */ OP_REM, /* remove rule */ OP_ZERO /* zero statistics and counters */ } addrem = OP_ADD; frentry_t frd, *fp, *f, **fprev, **ftail; void *ptr, *uptr, *cptr; u_int *p, *pp; frgroup_t *fg; char *group; ptr = NULL; cptr = NULL; fg = NULL; fp = &frd; if (makecopy != 0) { bzero(fp, sizeof(frd)); error = ipf_inobj(softc, data, NULL, fp, IPFOBJ_FRENTRY); if (error) { return error; } if ((fp->fr_type & FR_T_BUILTIN) != 0) { IPFERROR(6); return EINVAL; } KMALLOCS(f, frentry_t *, fp->fr_size); if (f == NULL) { IPFERROR(131); return ENOMEM; } bzero(f, fp->fr_size); error = ipf_inobjsz(softc, data, f, IPFOBJ_FRENTRY, fp->fr_size); if (error) { KFREES(f, fp->fr_size); return error; } fp = f; f = NULL; fp->fr_next = NULL; fp->fr_dnext = NULL; fp->fr_pnext = NULL; fp->fr_pdnext = NULL; fp->fr_grp = NULL; fp->fr_grphead = NULL; fp->fr_icmpgrp = NULL; fp->fr_isc = (void *)-1; fp->fr_ptr = NULL; fp->fr_ref = 0; fp->fr_flags |= FR_COPIED; } else { fp = (frentry_t *)data; if ((fp->fr_type & FR_T_BUILTIN) == 0) { IPFERROR(7); return EINVAL; } fp->fr_flags &= ~FR_COPIED; } if (((fp->fr_dsize == 0) && (fp->fr_data != NULL)) || ((fp->fr_dsize != 0) && (fp->fr_data == NULL))) { IPFERROR(8); error = EINVAL; goto donenolock; } family = fp->fr_family; uptr = fp->fr_data; if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR || req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) addrem = OP_ADD; /* Add rule */ else if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) addrem = OP_REM; /* Remove rule */ else if (req == (ioctlcmd_t)SIOCZRLST) addrem = OP_ZERO; /* Zero statistics and counters */ else { IPFERROR(9); error = EINVAL; goto donenolock; } /* * Only filter rules for IPv4 or IPv6 are accepted. */ if (family == AF_INET) { /*EMPTY*/; #ifdef USE_INET6 } else if (family == AF_INET6) { /*EMPTY*/; #endif } else if (family != 0) { IPFERROR(10); error = EINVAL; goto donenolock; } /* * If the rule is being loaded from user space, i.e. we had to copy it * into kernel space, then do not trust the function pointer in the * rule. */ if ((makecopy == 1) && (fp->fr_func != NULL)) { if (ipf_findfunc(fp->fr_func) == NULL) { IPFERROR(11); error = ESRCH; goto donenolock; } if (addrem == OP_ADD) { error = ipf_funcinit(softc, fp); if (error != 0) goto donenolock; } } if ((fp->fr_flags & FR_CALLNOW) && ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { IPFERROR(142); error = ESRCH; goto donenolock; } if (((fp->fr_flags & FR_CMDMASK) == FR_CALL) && ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { IPFERROR(143); error = ESRCH; goto donenolock; } ptr = NULL; cptr = NULL; if (FR_ISACCOUNT(fp->fr_flags)) unit = IPL_LOGCOUNT; /* * Check that each group name in the rule has a start index that * is valid. */ if (fp->fr_icmphead != -1) { if ((fp->fr_icmphead < 0) || (fp->fr_icmphead >= fp->fr_namelen)) { IPFERROR(136); error = EINVAL; goto donenolock; } if (!strcmp(FR_NAME(fp, fr_icmphead), "0")) fp->fr_names[fp->fr_icmphead] = '\0'; } if (fp->fr_grhead != -1) { if ((fp->fr_grhead < 0) || (fp->fr_grhead >= fp->fr_namelen)) { IPFERROR(137); error = EINVAL; goto donenolock; } if (!strcmp(FR_NAME(fp, fr_grhead), "0")) fp->fr_names[fp->fr_grhead] = '\0'; } if (fp->fr_group != -1) { if ((fp->fr_group < 0) || (fp->fr_group >= fp->fr_namelen)) { IPFERROR(138); error = EINVAL; goto donenolock; } if ((req != (int)SIOCZRLST) && (fp->fr_group != -1)) { /* * Allow loading rules that are in groups to cause * them to be created if they don't already exit. */ group = FR_NAME(fp, fr_group); if (addrem == OP_ADD) { fg = ipf_group_add(softc, group, NULL, fp->fr_flags, unit, set); fp->fr_grp = fg; } else { fg = ipf_findgroup(softc, group, unit, set, NULL); if (fg == NULL) { IPFERROR(12); error = ESRCH; goto donenolock; } } if (fg->fg_flags == 0) { fg->fg_flags = fp->fr_flags & FR_INOUT; } else if (fg->fg_flags != (fp->fr_flags & FR_INOUT)) { IPFERROR(13); error = ESRCH; goto donenolock; } } } else { /* * If a rule is going to be part of a group then it does * not matter whether it is an in or out rule, but if it * isn't in a group, then it does... */ if ((fp->fr_flags & (FR_INQUE|FR_OUTQUE)) == 0) { IPFERROR(14); error = EINVAL; goto donenolock; } } in = (fp->fr_flags & FR_INQUE) ? 0 : 1; /* * Work out which rule list this change is being applied to. */ ftail = NULL; fprev = NULL; if (unit == IPL_LOGAUTH) { if ((fp->fr_tifs[0].fd_ptr != NULL) || (fp->fr_tifs[1].fd_ptr != NULL) || (fp->fr_dif.fd_ptr != NULL) || (fp->fr_flags & FR_FASTROUTE)) { softc->ipf_interror = 145; error = EINVAL; goto donenolock; } fprev = ipf_auth_rulehead(softc); } else { if (FR_ISACCOUNT(fp->fr_flags)) fprev = &softc->ipf_acct[in][set]; else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) fprev = &softc->ipf_rules[in][set]; } if (fprev == NULL) { IPFERROR(15); error = ESRCH; goto donenolock; } if (fg != NULL) fprev = &fg->fg_start; /* * Copy in extra data for the rule. */ if (fp->fr_dsize != 0) { if (makecopy != 0) { KMALLOCS(ptr, void *, fp->fr_dsize); if (ptr == NULL) { IPFERROR(16); error = ENOMEM; goto donenolock; } /* * The bcopy case is for when the data is appended * to the rule by ipf_in_compat(). */ if (uptr >= (void *)fp && uptr < (void *)((char *)fp + fp->fr_size)) { bcopy(uptr, ptr, fp->fr_dsize); error = 0; } else { error = COPYIN(uptr, ptr, fp->fr_dsize); if (error != 0) { IPFERROR(17); error = EFAULT; goto donenolock; } } } else { ptr = uptr; } fp->fr_data = ptr; } else { fp->fr_data = NULL; } /* * Perform per-rule type sanity checks of their members. * All code after this needs to be aware that allocated memory * may need to be free'd before exiting. */ switch (fp->fr_type & ~FR_T_BUILTIN) { #if defined(IPFILTER_BPF) case FR_T_BPFOPC : if (fp->fr_dsize == 0) { IPFERROR(19); error = EINVAL; break; } if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) { IPFERROR(20); error = EINVAL; break; } break; #endif case FR_T_IPF : /* * Preparation for error case at the bottom of this function. */ if (fp->fr_datype == FRI_LOOKUP) fp->fr_dstptr = NULL; if (fp->fr_satype == FRI_LOOKUP) fp->fr_srcptr = NULL; if (fp->fr_dsize != sizeof(fripf_t)) { IPFERROR(21); error = EINVAL; break; } /* * Allowing a rule with both "keep state" and "with oow" is * pointless because adding a state entry to the table will * fail with the out of window (oow) flag set. */ if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW)) { IPFERROR(22); error = EINVAL; break; } switch (fp->fr_satype) { case FRI_BROADCAST : case FRI_DYNAMIC : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (fp->fr_sifpidx < 0) { IPFERROR(23); error = EINVAL; } break; case FRI_LOOKUP : fp->fr_srcptr = ipf_findlookup(softc, unit, fp, &fp->fr_src6, &fp->fr_smsk6); if (fp->fr_srcfunc == NULL) { IPFERROR(132); error = ESRCH; break; } break; case FRI_NORMAL : break; default : IPFERROR(133); error = EINVAL; break; } if (error != 0) break; switch (fp->fr_datype) { case FRI_BROADCAST : case FRI_DYNAMIC : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (fp->fr_difpidx < 0) { IPFERROR(24); error = EINVAL; } break; case FRI_LOOKUP : fp->fr_dstptr = ipf_findlookup(softc, unit, fp, &fp->fr_dst6, &fp->fr_dmsk6); if (fp->fr_dstfunc == NULL) { IPFERROR(134); error = ESRCH; } break; case FRI_NORMAL : break; default : IPFERROR(135); error = EINVAL; } break; case FR_T_NONE : case FR_T_CALLFUNC : case FR_T_COMPIPF : break; case FR_T_IPFEXPR : if (ipf_matcharray_verify(fp->fr_data, fp->fr_dsize) == -1) { IPFERROR(25); error = EINVAL; } break; default : IPFERROR(26); error = EINVAL; break; } if (error != 0) goto donenolock; if (fp->fr_tif.fd_name != -1) { if ((fp->fr_tif.fd_name < 0) || (fp->fr_tif.fd_name >= fp->fr_namelen)) { IPFERROR(139); error = EINVAL; goto donenolock; } } if (fp->fr_dif.fd_name != -1) { if ((fp->fr_dif.fd_name < 0) || (fp->fr_dif.fd_name >= fp->fr_namelen)) { IPFERROR(140); error = EINVAL; goto donenolock; } } if (fp->fr_rif.fd_name != -1) { if ((fp->fr_rif.fd_name < 0) || (fp->fr_rif.fd_name >= fp->fr_namelen)) { IPFERROR(141); error = EINVAL; goto donenolock; } } /* * Lookup all the interface names that are part of the rule. */ error = ipf_synclist(softc, fp, NULL); if (error != 0) goto donenolock; fp->fr_statecnt = 0; if (fp->fr_srctrack.ht_max_nodes != 0) ipf_rb_ht_init(&fp->fr_srctrack); /* * Look for an existing matching filter rule, but don't include the * next or interface pointer in the comparison (fr_next, fr_ifa). * This elminates rules which are indentical being loaded. Checksum * the constant part of the filter rule to make comparisons quicker * (this meaning no pointers are included). */ pp = (u_int *)(fp->fr_caddr + fp->fr_dsize); for (fp->fr_cksum = 0, p = (u_int *)fp->fr_data; p < pp; p++) fp->fr_cksum += *p; WRITE_ENTER(&softc->ipf_mutex); /* * Now that the filter rule lists are locked, we can walk the * chain of them without fear. */ ftail = fprev; for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) { if (fp->fr_collect <= f->fr_collect) { ftail = fprev; f = NULL; break; } fprev = ftail; } for (; (f = *ftail) != NULL; ftail = &f->fr_next) { if (ipf_rule_compare(fp, f) == 0) break; } /* * If zero'ing statistics, copy current to caller and zero. */ if (addrem == OP_ZERO) { if (f == NULL) { IPFERROR(27); error = ESRCH; } else { /* * Copy and reduce lock because of impending copyout. * Well we should, but if we do then the atomicity of * this call and the correctness of fr_hits and * fr_bytes cannot be guaranteed. As it is, this code * only resets them to 0 if they are successfully * copied out into user space. */ bcopy((char *)f, (char *)fp, f->fr_size); /* MUTEX_DOWNGRADE(&softc->ipf_mutex); */ /* * When we copy this rule back out, set the data * pointer to be what it was in user space. */ fp->fr_data = uptr; error = ipf_outobj(softc, data, fp, IPFOBJ_FRENTRY); if (error == 0) { if ((f->fr_dsize != 0) && (uptr != NULL)) { error = COPYOUT(f->fr_data, uptr, f->fr_dsize); if (error == 0) { f->fr_hits = 0; f->fr_bytes = 0; } else { IPFERROR(28); error = EFAULT; } } } } if (makecopy != 0) { if (ptr != NULL) { KFREES(ptr, fp->fr_dsize); } KFREES(fp, fp->fr_size); } RWLOCK_EXIT(&softc->ipf_mutex); return error; } if (f == NULL) { /* * At the end of this, ftail must point to the place where the * new rule is to be saved/inserted/added. * For SIOCAD*FR, this should be the last rule in the group of * rules that have equal fr_collect fields. * For SIOCIN*FR, ... */ if (req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) { for (ftail = fprev; (f = *ftail) != NULL; ) { if (f->fr_collect > fp->fr_collect) break; ftail = &f->fr_next; fprev = ftail; } ftail = fprev; f = NULL; ptr = NULL; } else if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR) { while ((f = *fprev) != NULL) { if (f->fr_collect >= fp->fr_collect) break; fprev = &f->fr_next; } ftail = fprev; if (fp->fr_hits != 0) { while (fp->fr_hits && (f = *ftail)) { if (f->fr_collect != fp->fr_collect) break; fprev = ftail; ftail = &f->fr_next; fp->fr_hits--; } } f = NULL; ptr = NULL; } } /* * Request to remove a rule. */ if (addrem == OP_REM) { if (f == NULL) { IPFERROR(29); error = ESRCH; } else { /* * Do not allow activity from user space to interfere * with rules not loaded that way. */ if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) { IPFERROR(30); error = EPERM; goto done; } /* * Return EBUSY if the rule is being reference by * something else (eg state information.) */ if (f->fr_ref > 1) { IPFERROR(31); error = EBUSY; goto done; } #ifdef IPFILTER_SCAN if (f->fr_isctag != -1 && (f->fr_isc != (struct ipscan *)-1)) ipf_scan_detachfr(f); #endif if (unit == IPL_LOGAUTH) { error = ipf_auth_precmd(softc, req, f, ftail); goto done; } ipf_rule_delete(softc, f, unit, set); need_free = makecopy; } } else { /* * Not removing, so we must be adding/inserting a rule. */ if (f != NULL) { IPFERROR(32); error = EEXIST; goto done; } if (unit == IPL_LOGAUTH) { error = ipf_auth_precmd(softc, req, fp, ftail); goto done; } MUTEX_NUKE(&fp->fr_lock); MUTEX_INIT(&fp->fr_lock, "filter rule lock"); if (fp->fr_die != 0) ipf_rule_expire_insert(softc, fp, set); fp->fr_hits = 0; if (makecopy != 0) fp->fr_ref = 1; fp->fr_pnext = ftail; fp->fr_next = *ftail; if (fp->fr_next != NULL) fp->fr_next->fr_pnext = &fp->fr_next; *ftail = fp; ipf_fixskip(ftail, fp, 1); fp->fr_icmpgrp = NULL; if (fp->fr_icmphead != -1) { group = FR_NAME(fp, fr_icmphead); fg = ipf_group_add(softc, group, fp, 0, unit, set); fp->fr_icmpgrp = fg; } fp->fr_grphead = NULL; if (fp->fr_grhead != -1) { group = FR_NAME(fp, fr_grhead); fg = ipf_group_add(softc, group, fp, fp->fr_flags, unit, set); fp->fr_grphead = fg; } } done: RWLOCK_EXIT(&softc->ipf_mutex); donenolock: if (need_free || (error != 0)) { if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if ((fp->fr_satype == FRI_LOOKUP) && (fp->fr_srcptr != NULL)) ipf_lookup_deref(softc, fp->fr_srctype, fp->fr_srcptr); if ((fp->fr_datype == FRI_LOOKUP) && (fp->fr_dstptr != NULL)) ipf_lookup_deref(softc, fp->fr_dsttype, fp->fr_dstptr); } if (fp->fr_grp != NULL) { WRITE_ENTER(&softc->ipf_mutex); ipf_group_del(softc, fp->fr_grp, fp); RWLOCK_EXIT(&softc->ipf_mutex); } if ((ptr != NULL) && (makecopy != 0)) { KFREES(ptr, fp->fr_dsize); } KFREES(fp, fp->fr_size); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* f(I) - pointer to the rule being deleted */ /* ftail(I) - pointer to the pointer to f */ /* unit(I) - device for which this is for */ /* set(I) - 1 or 0 (filter set) */ /* */ /* This function attempts to do what it can to delete a filter rule: remove */ /* it from any linked lists and remove any groups it is responsible for. */ /* But in the end, removing a rule can only drop the reference count - we */ /* must use that as the guide for whether or not it can be freed. */ /* ------------------------------------------------------------------------ */ static void ipf_rule_delete(softc, f, unit, set) ipf_main_softc_t *softc; frentry_t *f; int unit, set; { /* * If fr_pdnext is set, then the rule is on the expire list, so * remove it from there. */ if (f->fr_pdnext != NULL) { *f->fr_pdnext = f->fr_dnext; if (f->fr_dnext != NULL) f->fr_dnext->fr_pdnext = f->fr_pdnext; f->fr_pdnext = NULL; f->fr_dnext = NULL; } ipf_fixskip(f->fr_pnext, f, -1); if (f->fr_pnext != NULL) *f->fr_pnext = f->fr_next; if (f->fr_next != NULL) f->fr_next->fr_pnext = f->fr_pnext; f->fr_pnext = NULL; f->fr_next = NULL; (void) ipf_derefrule(softc, &f); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_expire_insert */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* f(I) - pointer to rule to be added to expire list */ /* set(I) - 1 or 0 (filter set) */ /* */ /* If the new rule has a given expiration time, insert it into the list of */ /* expiring rules with the ones to be removed first added to the front of */ /* the list. The insertion is O(n) but it is kept sorted for quick scans at */ /* expiration interval checks. */ /* ------------------------------------------------------------------------ */ static void ipf_rule_expire_insert(softc, f, set) ipf_main_softc_t *softc; frentry_t *f; int set; { frentry_t *fr; /* */ f->fr_die = softc->ipf_ticks + IPF_TTLVAL(f->fr_die); for (fr = softc->ipf_rule_explist[set]; fr != NULL; fr = fr->fr_dnext) { if (f->fr_die < fr->fr_die) break; if (fr->fr_dnext == NULL) { /* * We've got to the last rule and everything * wanted to be expired before this new node, * so we have to tack it on the end... */ fr->fr_dnext = f; f->fr_pdnext = &fr->fr_dnext; fr = NULL; break; } } if (softc->ipf_rule_explist[set] == NULL) { softc->ipf_rule_explist[set] = f; f->fr_pdnext = &softc->ipf_rule_explist[set]; } else if (fr != NULL) { f->fr_dnext = fr; f->fr_pdnext = fr->fr_pdnext; fr->fr_pdnext = &f->fr_dnext; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_findlookup */ /* Returns: NULL = failure, else success */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - ipf device we want to find match for */ /* fp(I) - rule for which lookup is for */ /* addrp(I) - pointer to lookup information in address struct */ /* maskp(O) - pointer to lookup information for storage */ /* */ /* When using pools and hash tables to store addresses for matching in */ /* rules, it is necessary to resolve both the object referred to by the */ /* name or address (and return that pointer) and also provide the means by */ /* which to determine if an address belongs to that object to make the */ /* packet matching quicker. */ /* ------------------------------------------------------------------------ */ static void * ipf_findlookup(softc, unit, fr, addrp, maskp) ipf_main_softc_t *softc; int unit; frentry_t *fr; i6addr_t *addrp, *maskp; { void *ptr = NULL; switch (addrp->iplookupsubtype) { case 0 : ptr = ipf_lookup_res_num(softc, unit, addrp->iplookuptype, addrp->iplookupnum, &maskp->iplookupfunc); break; case 1 : if (addrp->iplookupname < 0) break; if (addrp->iplookupname >= fr->fr_namelen) break; ptr = ipf_lookup_res_name(softc, unit, addrp->iplookuptype, fr->fr_names + addrp->iplookupname, &maskp->iplookupfunc); break; default : break; } return ptr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_funcinit */ /* Returns: int - 0 == success, else ESRCH: cannot resolve rule details */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* */ /* If a rule is a call rule, then check if the function it points to needs */ /* an init function to be called now the rule has been loaded. */ /* ------------------------------------------------------------------------ */ static int ipf_funcinit(softc, fr) ipf_main_softc_t *softc; frentry_t *fr; { ipfunc_resolve_t *ft; int err; IPFERROR(34); err = ESRCH; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == fr->fr_func) { err = 0; if (ft->ipfu_init != NULL) err = (*ft->ipfu_init)(softc, fr); break; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_funcfini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* */ /* For a given filter rule, call the matching "fini" function if the rule */ /* is using a known function that would have resulted in the "init" being */ /* called for ealier. */ /* ------------------------------------------------------------------------ */ static void ipf_funcfini(softc, fr) ipf_main_softc_t *softc; frentry_t *fr; { ipfunc_resolve_t *ft; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == fr->fr_func) { if (ft->ipfu_fini != NULL) (void) (*ft->ipfu_fini)(softc, fr); break; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_findfunc */ /* Returns: ipfunc_t - pointer to function if found, else NULL */ /* Parameters: funcptr(I) - function pointer to lookup */ /* */ /* Look for a function in the table of known functions. */ /* ------------------------------------------------------------------------ */ static ipfunc_t ipf_findfunc(funcptr) ipfunc_t funcptr; { ipfunc_resolve_t *ft; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == funcptr) return funcptr; return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvefunc */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - ioctl data pointer to ipfunc_resolve_t struct */ /* */ /* Copy in a ipfunc_resolve_t structure and then fill in the missing field. */ /* This will either be the function name (if the pointer is set) or the */ /* function pointer if the name is set. When found, fill in the other one */ /* so that the entire, complete, structure can be copied back to user space.*/ /* ------------------------------------------------------------------------ */ int ipf_resolvefunc(softc, data) ipf_main_softc_t *softc; void *data; { ipfunc_resolve_t res, *ft; int error; error = BCOPYIN(data, &res, sizeof(res)); if (error != 0) { IPFERROR(123); return EFAULT; } if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') { for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (strncmp(res.ipfu_name, ft->ipfu_name, sizeof(res.ipfu_name)) == 0) { res.ipfu_addr = ft->ipfu_addr; res.ipfu_init = ft->ipfu_init; if (COPYOUT(&res, data, sizeof(res)) != 0) { IPFERROR(35); return EFAULT; } return 0; } } if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') { for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == res.ipfu_addr) { (void) strncpy(res.ipfu_name, ft->ipfu_name, sizeof(res.ipfu_name)); res.ipfu_init = ft->ipfu_init; if (COPYOUT(&res, data, sizeof(res)) != 0) { IPFERROR(36); return EFAULT; } return 0; } } IPFERROR(37); return ESRCH; } #if !defined(_KERNEL) || SOLARIS /* * From: NetBSD * ppsratecheck(): packets (or events) per second limitation. */ int ppsratecheck(lasttime, curpps, maxpps) struct timeval *lasttime; int *curpps; int maxpps; /* maximum pps allowed */ { struct timeval tv, delta; int rv; GETKTIME(&tv); delta.tv_sec = tv.tv_sec - lasttime->tv_sec; delta.tv_usec = tv.tv_usec - lasttime->tv_usec; if (delta.tv_usec < 0) { delta.tv_sec--; delta.tv_usec += 1000000; } /* * check for 0,0 is so that the message will be seen at least once. * if more than one second have passed since the last update of * lasttime, reset the counter. * * we do increment *curpps even in *curpps < maxpps case, as some may * try to use *curpps for stat purposes as well. */ if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || delta.tv_sec >= 1) { *lasttime = tv; *curpps = 0; rv = 1; } else if (maxpps < 0) rv = 1; else if (*curpps < maxpps) rv = 1; else rv = 0; *curpps = *curpps + 1; return (rv); } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_derefrule */ /* Returns: int - 0 == rule freed up, else rule not freed */ /* Parameters: fr(I) - pointer to filter rule */ /* */ /* Decrement the reference counter to a rule by one. If it reaches zero, */ /* free it and any associated storage space being used by it. */ /* ------------------------------------------------------------------------ */ int ipf_derefrule(softc, frp) ipf_main_softc_t *softc; frentry_t **frp; { frentry_t *fr; frdest_t *fdp; fr = *frp; *frp = NULL; MUTEX_ENTER(&fr->fr_lock); fr->fr_ref--; if (fr->fr_ref == 0) { MUTEX_EXIT(&fr->fr_lock); MUTEX_DESTROY(&fr->fr_lock); ipf_funcfini(softc, fr); fdp = &fr->fr_tif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); fdp = &fr->fr_rif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); fdp = &fr->fr_dif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && fr->fr_satype == FRI_LOOKUP) ipf_lookup_deref(softc, fr->fr_srctype, fr->fr_srcptr); if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && fr->fr_datype == FRI_LOOKUP) ipf_lookup_deref(softc, fr->fr_dsttype, fr->fr_dstptr); if (fr->fr_grp != NULL) ipf_group_del(softc, fr->fr_grp, fr); if (fr->fr_grphead != NULL) ipf_group_del(softc, fr->fr_grphead, fr); if (fr->fr_icmpgrp != NULL) ipf_group_del(softc, fr->fr_icmpgrp, fr); if ((fr->fr_flags & FR_COPIED) != 0) { if (fr->fr_dsize) { KFREES(fr->fr_data, fr->fr_dsize); } KFREES(fr, fr->fr_size); return 0; } return 1; } else { MUTEX_EXIT(&fr->fr_lock); } return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_grpmapinit */ /* Returns: int - 0 == success, else ESRCH because table entry not found*/ /* Parameters: fr(I) - pointer to rule to find hash table for */ /* */ /* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr. */ /* fr_ptr is later used by ipf_srcgrpmap and ipf_dstgrpmap. */ /* ------------------------------------------------------------------------ */ static int ipf_grpmapinit(softc, fr) ipf_main_softc_t *softc; frentry_t *fr; { char name[FR_GROUPLEN]; iphtable_t *iph; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(name, sizeof(name), "%d", fr->fr_arg); #else (void) sprintf(name, "%d", fr->fr_arg); #endif iph = ipf_lookup_find_htable(softc, IPL_LOGIPF, name); if (iph == NULL) { IPFERROR(38); return ESRCH; } if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT)) { IPFERROR(39); return ESRCH; } iph->iph_ref++; fr->fr_ptr = iph; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_grpmapfini */ /* Returns: int - 0 == success, else ESRCH because table entry not found*/ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to rule to release hash table for */ /* */ /* For rules that have had ipf_grpmapinit called, ipf_lookup_deref needs to */ /* be called to undo what ipf_grpmapinit caused to be done. */ /* ------------------------------------------------------------------------ */ static int ipf_grpmapfini(softc, fr) ipf_main_softc_t *softc; frentry_t *fr; { iphtable_t *iph; iph = fr->fr_ptr; if (iph != NULL) ipf_lookup_deref(softc, IPLT_HASH, iph); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_srcgrpmap */ /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Look for a rule group head in a hash table, using the source address as */ /* the key, and descend into that group and continue matching rules against */ /* the packet. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_srcgrpmap(fin, passp) fr_info_t *fin; u_32_t *passp; { frgroup_t *fg; void *rval; rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, &fin->fin_src); if (rval == NULL) return NULL; fg = rval; fin->fin_fr = fg->fg_start; (void) ipf_scanlist(fin, *passp); return fin->fin_fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstgrpmap */ /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Look for a rule group head in a hash table, using the destination */ /* address as the key, and descend into that group and continue matching */ /* rules against the packet. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_dstgrpmap(fin, passp) fr_info_t *fin; u_32_t *passp; { frgroup_t *fg; void *rval; rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, &fin->fin_dst); if (rval == NULL) return NULL; fg = rval; fin->fin_fr = fg->fg_start; (void) ipf_scanlist(fin, *passp); return fin->fin_fr; } /* * Queue functions * =============== * These functions manage objects on queues for efficient timeouts. There * are a number of system defined queues as well as user defined timeouts. * It is expected that a lock is held in the domain in which the queue * belongs (i.e. either state or NAT) when calling any of these functions * that prevents ipf_freetimeoutqueue() from being called at the same time * as any other. */ /* ------------------------------------------------------------------------ */ /* Function: ipf_addtimeoutqueue */ /* Returns: struct ifqtq * - NULL if malloc fails, else pointer to */ /* timeout queue with given interval. */ /* Parameters: parent(I) - pointer to pointer to parent node of this list */ /* of interface queues. */ /* seconds(I) - timeout value in seconds for this queue. */ /* */ /* This routine first looks for a timeout queue that matches the interval */ /* being requested. If it finds one, increments the reference counter and */ /* returns a pointer to it. If none are found, it allocates a new one and */ /* inserts it at the top of the list. */ /* */ /* Locking. */ /* It is assumed that the caller of this function has an appropriate lock */ /* held (exclusively) in the domain that encompases 'parent'. */ /* ------------------------------------------------------------------------ */ ipftq_t * ipf_addtimeoutqueue(softc, parent, seconds) ipf_main_softc_t *softc; ipftq_t **parent; u_int seconds; { ipftq_t *ifq; u_int period; period = seconds * IPF_HZ_DIVIDE; MUTEX_ENTER(&softc->ipf_timeoutlock); for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) { if (ifq->ifq_ttl == period) { /* * Reset the delete flag, if set, so the structure * gets reused rather than freed and reallocated. */ MUTEX_ENTER(&ifq->ifq_lock); ifq->ifq_flags &= ~IFQF_DELETE; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); MUTEX_EXIT(&softc->ipf_timeoutlock); return ifq; } } KMALLOC(ifq, ipftq_t *); if (ifq != NULL) { MUTEX_NUKE(&ifq->ifq_lock); IPFTQ_INIT(ifq, period, "ipftq mutex"); ifq->ifq_next = *parent; ifq->ifq_pnext = parent; ifq->ifq_flags = IFQF_USER; ifq->ifq_ref++; *parent = ifq; softc->ipf_userifqs++; } MUTEX_EXIT(&softc->ipf_timeoutlock); return ifq; } /* ------------------------------------------------------------------------ */ /* Function: ipf_deletetimeoutqueue */ /* Returns: int - new reference count value of the timeout queue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Locks: ifq->ifq_lock */ /* */ /* This routine must be called when we're discarding a pointer to a timeout */ /* queue object, taking care of the reference counter. */ /* */ /* Now that this just sets a DELETE flag, it requires the expire code to */ /* check the list of user defined timeout queues and call the free function */ /* below (currently commented out) to stop memory leaking. It is done this */ /* way because the locking may not be sufficient to safely do a free when */ /* this function is called. */ /* ------------------------------------------------------------------------ */ int ipf_deletetimeoutqueue(ifq) ipftq_t *ifq; { ifq->ifq_ref--; if ((ifq->ifq_ref == 0) && ((ifq->ifq_flags & IFQF_USER) != 0)) { ifq->ifq_flags |= IFQF_DELETE; } return ifq->ifq_ref; } /* ------------------------------------------------------------------------ */ /* Function: ipf_freetimeoutqueue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Returns: Nil */ /* */ /* Locking: */ /* It is assumed that the caller of this function has an appropriate lock */ /* held (exclusively) in the domain that encompases the callers "domain". */ /* The ifq_lock for this structure should not be held. */ /* */ /* Remove a user defined timeout queue from the list of queues it is in and */ /* tidy up after this is done. */ /* ------------------------------------------------------------------------ */ void ipf_freetimeoutqueue(softc, ifq) ipf_main_softc_t *softc; ipftq_t *ifq; { if (((ifq->ifq_flags & IFQF_DELETE) == 0) || (ifq->ifq_ref != 0) || ((ifq->ifq_flags & IFQF_USER) == 0)) { printf("ipf_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n", (u_long)ifq, ifq->ifq_flags, ifq->ifq_ttl, ifq->ifq_ref); return; } /* * Remove from its position in the list. */ *ifq->ifq_pnext = ifq->ifq_next; if (ifq->ifq_next != NULL) ifq->ifq_next->ifq_pnext = ifq->ifq_pnext; ifq->ifq_next = NULL; ifq->ifq_pnext = NULL; MUTEX_DESTROY(&ifq->ifq_lock); ATOMIC_DEC(softc->ipf_userifqs); KFREE(ifq); } /* ------------------------------------------------------------------------ */ /* Function: ipf_deletequeueentry */ /* Returns: Nil */ /* Parameters: tqe(I) - timeout queue entry to delete */ /* */ /* Remove a tail queue entry from its queue and make it an orphan. */ /* ipf_deletetimeoutqueue is called to make sure the reference count on the */ /* queue is correct. We can't, however, call ipf_freetimeoutqueue because */ /* the correct lock(s) may not be held that would make it safe to do so. */ /* ------------------------------------------------------------------------ */ void ipf_deletequeueentry(tqe) ipftqent_t *tqe; { ipftq_t *ifq; ifq = tqe->tqe_ifq; MUTEX_ENTER(&ifq->ifq_lock); if (tqe->tqe_pnext != NULL) { *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next != NULL) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else /* we must be the tail anyway */ ifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_pnext = NULL; tqe->tqe_ifq = NULL; } (void) ipf_deletetimeoutqueue(ifq); ASSERT(ifq->ifq_ref > 0); MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queuefront */ /* Returns: Nil */ /* Parameters: tqe(I) - pointer to timeout queue entry */ /* */ /* Move a queue entry to the front of the queue, if it isn't already there. */ /* ------------------------------------------------------------------------ */ void ipf_queuefront(tqe) ipftqent_t *tqe; { ipftq_t *ifq; ifq = tqe->tqe_ifq; if (ifq == NULL) return; MUTEX_ENTER(&ifq->ifq_lock); if (ifq->ifq_head != tqe) { *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else ifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_next = ifq->ifq_head; ifq->ifq_head->tqe_pnext = &tqe->tqe_next; ifq->ifq_head = tqe; tqe->tqe_pnext = &ifq->ifq_head; } MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueback */ /* Returns: Nil */ /* Parameters: ticks(I) - ipf tick time to use with this call */ /* tqe(I) - pointer to timeout queue entry */ /* */ /* Move a queue entry to the back of the queue, if it isn't already there. */ /* We use use ticks to calculate the expiration and mark for when we last */ /* touched the structure. */ /* ------------------------------------------------------------------------ */ void ipf_queueback(ticks, tqe) u_long ticks; ipftqent_t *tqe; { ipftq_t *ifq; ifq = tqe->tqe_ifq; if (ifq == NULL) return; tqe->tqe_die = ticks + ifq->ifq_ttl; tqe->tqe_touched = ticks; MUTEX_ENTER(&ifq->ifq_lock); if (tqe->tqe_next != NULL) { /* at the end already ? */ /* * Remove from list */ *tqe->tqe_pnext = tqe->tqe_next; tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; /* * Make it the last entry. */ tqe->tqe_next = NULL; tqe->tqe_pnext = ifq->ifq_tail; *ifq->ifq_tail = tqe; ifq->ifq_tail = &tqe->tqe_next; } MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueappend */ /* Returns: Nil */ /* Parameters: ticks(I) - ipf tick time to use with this call */ /* tqe(I) - pointer to timeout queue entry */ /* ifq(I) - pointer to timeout queue */ /* parent(I) - owing object pointer */ /* */ /* Add a new item to this queue and put it on the very end. */ /* We use use ticks to calculate the expiration and mark for when we last */ /* touched the structure. */ /* ------------------------------------------------------------------------ */ void ipf_queueappend(ticks, tqe, ifq, parent) u_long ticks; ipftqent_t *tqe; ipftq_t *ifq; void *parent; { MUTEX_ENTER(&ifq->ifq_lock); tqe->tqe_parent = parent; tqe->tqe_pnext = ifq->ifq_tail; *ifq->ifq_tail = tqe; ifq->ifq_tail = &tqe->tqe_next; tqe->tqe_next = NULL; tqe->tqe_ifq = ifq; tqe->tqe_die = ticks + ifq->ifq_ttl; tqe->tqe_touched = ticks; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_movequeue */ /* Returns: Nil */ /* Parameters: tq(I) - pointer to timeout queue information */ /* oifp(I) - old timeout queue entry was on */ /* nifp(I) - new timeout queue to put entry on */ /* */ /* Move a queue entry from one timeout queue to another timeout queue. */ /* If it notices that the current entry is already last and does not need */ /* to move queue, the return. */ /* ------------------------------------------------------------------------ */ void ipf_movequeue(ticks, tqe, oifq, nifq) u_long ticks; ipftqent_t *tqe; ipftq_t *oifq, *nifq; { /* * If the queue hasn't changed and we last touched this entry at the * same ipf time, then we're not going to achieve anything by either * changing the ttl or moving it on the queue. */ if (oifq == nifq && tqe->tqe_touched == ticks) return; /* * For any of this to be outside the lock, there is a risk that two * packets entering simultaneously, with one changing to a different * queue and one not, could end up with things in a bizarre state. */ MUTEX_ENTER(&oifq->ifq_lock); tqe->tqe_touched = ticks; tqe->tqe_die = ticks + nifq->ifq_ttl; /* * Is the operation here going to be a no-op ? */ if (oifq == nifq) { if ((tqe->tqe_next == NULL) || (tqe->tqe_next->tqe_die == tqe->tqe_die)) { MUTEX_EXIT(&oifq->ifq_lock); return; } } /* * Remove from the old queue */ *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else oifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_next = NULL; /* * If we're moving from one queue to another, release the * lock on the old queue and get a lock on the new queue. * For user defined queues, if we're moving off it, call * delete in case it can now be freed. */ if (oifq != nifq) { tqe->tqe_ifq = NULL; (void) ipf_deletetimeoutqueue(oifq); MUTEX_EXIT(&oifq->ifq_lock); MUTEX_ENTER(&nifq->ifq_lock); tqe->tqe_ifq = nifq; nifq->ifq_ref++; } /* * Add to the bottom of the new queue */ tqe->tqe_pnext = nifq->ifq_tail; *nifq->ifq_tail = tqe; nifq->ifq_tail = &tqe->tqe_next; MUTEX_EXIT(&nifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_updateipid */ /* Returns: int - 0 == success, -1 == error (packet should be droppped) */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* When we are doing NAT, change the IP of every packet to represent a */ /* single sequence of packets coming from the host, hiding any host */ /* specific sequencing that might otherwise be revealed. If the packet is */ /* a fragment, then store the 'new' IPid in the fragment cache and look up */ /* the fragment cache for non-leading fragments. If a non-leading fragment */ /* has no match in the cache, return an error. */ /* ------------------------------------------------------------------------ */ static int ipf_updateipid(fin) fr_info_t *fin; { u_short id, ido, sums; u_32_t sumd, sum; ip_t *ip; ip = fin->fin_ip; ido = ntohs(ip->ip_id); if (fin->fin_off != 0) { sum = ipf_frag_ipidknown(fin); if (sum == 0xffffffff) return -1; sum &= 0xffff; id = (u_short)sum; ip->ip_id = htons(id); } else { ip_fillid(ip); id = ntohs(ip->ip_id); if ((fin->fin_flx & FI_FRAG) != 0) (void) ipf_frag_ipidnew(fin, (u_32_t)id); } if (id == ido) return 0; CALC_SUMD(ido, id, sumd); /* DESTRUCTIVE MACRO! id,ido change */ sum = (~ntohs(ip->ip_sum)) & 0xffff; sum += sumd; sum = (sum >> 16) + (sum & 0xffff); sum = (sum >> 16) + (sum & 0xffff); sums = ~(u_short)sum; ip->ip_sum = htons(sums); return 0; } #ifdef NEED_FRGETIFNAME /* ------------------------------------------------------------------------ */ /* Function: ipf_getifname */ /* Returns: char * - pointer to interface name */ /* Parameters: ifp(I) - pointer to network interface */ /* buffer(O) - pointer to where to store interface name */ /* */ /* Constructs an interface name in the buffer passed. The buffer passed is */ /* expected to be at least LIFNAMSIZ in bytes big. If buffer is passed in */ /* as a NULL pointer then return a pointer to a static array. */ /* ------------------------------------------------------------------------ */ char * ipf_getifname(ifp, buffer) struct ifnet *ifp; char *buffer; { static char namebuf[LIFNAMSIZ]; # if defined(MENTAT) || defined(__FreeBSD__) int unit, space; char temp[20]; char *s; # endif if (buffer == NULL) buffer = namebuf; (void) strncpy(buffer, ifp->if_name, LIFNAMSIZ); buffer[LIFNAMSIZ - 1] = '\0'; # if defined(MENTAT) || defined(__FreeBSD__) for (s = buffer; *s; s++) ; unit = ifp->if_unit; space = LIFNAMSIZ - (s - buffer); if ((space > 0) && (unit >= 0)) { # if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(temp, sizeof(temp), "%d", unit); # else (void) sprintf(temp, "%d", unit); # endif (void) strncpy(s, temp, space); } # endif return buffer; } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_ioctlswitch */ /* Returns: int - -1 continue processing, else ioctl return value */ /* Parameters: unit(I) - device unit opened */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode value */ /* uid(I) - uid making the ioctl call */ /* ctx(I) - pointer to context data */ /* */ /* Based on the value of unit, call the appropriate ioctl handler or return */ /* EIO if ipfilter is not running. Also checks if write perms are req'd */ /* for the device in order to execute the ioctl. A special case is made */ /* SIOCIPFINTERROR so that the same code isn't required in every handler. */ /* The context data pointer is passed through as this is used as the key */ /* for locating a matching token for continued access for walking lists, */ /* etc. */ /* ------------------------------------------------------------------------ */ int ipf_ioctlswitch(softc, unit, data, cmd, mode, uid, ctx) ipf_main_softc_t *softc; int unit, mode, uid; ioctlcmd_t cmd; void *data, *ctx; { int error = 0; switch (cmd) { case SIOCIPFINTERROR : error = BCOPYOUT(&softc->ipf_interror, data, sizeof(softc->ipf_interror)); if (error != 0) { IPFERROR(40); error = EFAULT; } return error; default : break; } switch (unit) { case IPL_LOGIPF : error = ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx); break; case IPL_LOGNAT : if (softc->ipf_running > 0) { error = ipf_nat_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(42); error = EIO; } break; case IPL_LOGSTATE : if (softc->ipf_running > 0) { error = ipf_state_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(43); error = EIO; } break; case IPL_LOGAUTH : if (softc->ipf_running > 0) { error = ipf_auth_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(44); error = EIO; } break; case IPL_LOGSYNC : if (softc->ipf_running > 0) { error = ipf_sync_ioctl(softc, data, cmd, mode, uid, ctx); } else { error = EIO; IPFERROR(45); } break; case IPL_LOGSCAN : #ifdef IPFILTER_SCAN if (softc->ipf_running > 0) error = ipf_scan_ioctl(softc, data, cmd, mode, uid, ctx); else #endif { error = EIO; IPFERROR(46); } break; case IPL_LOGLOOKUP : if (softc->ipf_running > 0) { error = ipf_lookup_ioctl(softc, data, cmd, mode, uid, ctx); } else { error = EIO; IPFERROR(47); } break; default : IPFERROR(48); error = EIO; break; } return error; } /* * This array defines the expected size of objects coming into the kernel * for the various recognised object types. The first column is flags (see * below), 2nd column is current size, 3rd column is the version number of * when the current size became current. * Flags: * 1 = minimum size, not absolute size */ static const int ipf_objbytes[IPFOBJ_COUNT][3] = { { 1, sizeof(struct frentry), 5010000 }, /* 0 */ { 1, sizeof(struct friostat), 5010000 }, { 0, sizeof(struct fr_info), 5010000 }, { 0, sizeof(struct ipf_authstat), 4010100 }, { 0, sizeof(struct ipfrstat), 5010000 }, { 1, sizeof(struct ipnat), 5010000 }, /* 5 */ { 0, sizeof(struct natstat), 5010000 }, { 0, sizeof(struct ipstate_save), 5010000 }, { 1, sizeof(struct nat_save), 5010000 }, { 0, sizeof(struct natlookup), 5010000 }, { 1, sizeof(struct ipstate), 5010000 }, /* 10 */ { 0, sizeof(struct ips_stat), 5010000 }, { 0, sizeof(struct frauth), 5010000 }, { 0, sizeof(struct ipftune), 4010100 }, { 0, sizeof(struct nat), 5010000 }, { 0, sizeof(struct ipfruleiter), 4011400 }, /* 15 */ { 0, sizeof(struct ipfgeniter), 4011400 }, { 0, sizeof(struct ipftable), 4011400 }, { 0, sizeof(struct ipflookupiter), 4011400 }, { 0, sizeof(struct ipftq) * IPF_TCP_NSTATES }, { 1, 0, 0 }, /* IPFEXPR */ { 0, 0, 0 }, /* PROXYCTL */ { 0, sizeof (struct fripf), 5010000 } }; /* ------------------------------------------------------------------------ */ /* Function: ipf_inobj */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* data(I) - pointer to ioctl data */ /* objp(O) - where to store ipfobj structure */ /* ptr(I) - pointer to data to copy out */ /* type(I) - type of structure being moved */ /* */ /* Copy in the contents of what the ipfobj_t points to. In future, we */ /* add things to check for version numbers, sizes, etc, to make it backward */ /* compatible at the ABI for user land. */ /* If objp is not NULL then we assume that the caller wants to see what is */ /* in the ipfobj_t structure being copied in. As an example, this can tell */ /* the caller what version of ipfilter the ioctl program was written to. */ /* ------------------------------------------------------------------------ */ int ipf_inobj(softc, data, objp, ptr, type) ipf_main_softc_t *softc; void *data; ipfobj_t *objp; void *ptr; int type; { ipfobj_t obj; int error; int size; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(49); return EINVAL; } if (objp == NULL) objp = &obj; error = BCOPYIN(data, objp, sizeof(*objp)); if (error != 0) { IPFERROR(124); return EFAULT; } if (objp->ipfo_type != type) { IPFERROR(50); return EINVAL; } if (objp->ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (objp->ipfo_size < ipf_objbytes[type][1]) { IPFERROR(51); return EINVAL; } size = ipf_objbytes[type][1]; } else if (objp->ipfo_size == ipf_objbytes[type][1]) { size = objp->ipfo_size; } else { IPFERROR(52); return EINVAL; } error = COPYIN(objp->ipfo_ptr, ptr, size); if (error != 0) { IPFERROR(55); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_in_compat(softc, objp, ptr, 0); #else IPFERROR(54); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inobjsz */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* sz(I) - size of data to copy */ /* */ /* As per ipf_inobj, except the size of the object to copy in is passed in */ /* but it must not be smaller than the size defined for the type and the */ /* type must allow for varied sized objects. The extra requirement here is */ /* that sz must match the size of the object being passed in - this is not */ /* not possible nor required in ipf_inobj(). */ /* ------------------------------------------------------------------------ */ int ipf_inobjsz(softc, data, ptr, type, sz) ipf_main_softc_t *softc; void *data; void *ptr; int type, sz; { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(56); return EINVAL; } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(125); return EFAULT; } if (obj.ipfo_type != type) { IPFERROR(58); return EINVAL; } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if (((ipf_objbytes[type][0] & 1) == 0) || (sz < ipf_objbytes[type][1])) { IPFERROR(57); return EINVAL; } error = COPYIN(obj.ipfo_ptr, ptr, sz); if (error != 0) { IPFERROR(61); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_in_compat(softc, &obj, ptr, sz); #else IPFERROR(60); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobjsz */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* sz(I) - size of data to copy */ /* */ /* As per ipf_outobj, except the size of the object to copy out is passed in*/ /* but it must not be smaller than the size defined for the type and the */ /* type must allow for varied sized objects. The extra requirement here is */ /* that sz must match the size of the object being passed in - this is not */ /* not possible nor required in ipf_outobj(). */ /* ------------------------------------------------------------------------ */ int ipf_outobjsz(softc, data, ptr, type, sz) ipf_main_softc_t *softc; void *data; void *ptr; int type, sz; { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(62); return EINVAL; } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(127); return EFAULT; } if (obj.ipfo_type != type) { IPFERROR(63); return EINVAL; } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if (((ipf_objbytes[type][0] & 1) == 0) || (sz < ipf_objbytes[type][1])) { IPFERROR(146); return EINVAL; } error = COPYOUT(ptr, obj.ipfo_ptr, sz); if (error != 0) { IPFERROR(66); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, &obj, ptr); #else IPFERROR(65); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobj */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* */ /* Copy out the contents of what ptr is to where ipfobj points to. In */ /* future, we add things to check for version numbers, sizes, etc, to make */ /* it backward compatible at the ABI for user land. */ /* ------------------------------------------------------------------------ */ int ipf_outobj(softc, data, ptr, type) ipf_main_softc_t *softc; void *data; void *ptr; int type; { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(67); return EINVAL; } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(126); return EFAULT; } if (obj.ipfo_type != type) { IPFERROR(68); return EINVAL; } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (obj.ipfo_size < ipf_objbytes[type][1]) { IPFERROR(69); return EINVAL; } } else if (obj.ipfo_size != ipf_objbytes[type][1]) { IPFERROR(70); return EINVAL; } error = COPYOUT(ptr, obj.ipfo_ptr, obj.ipfo_size); if (error != 0) { IPFERROR(73); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, &obj, ptr); #else IPFERROR(72); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobjk */ /* Returns: int - 0 = success, else failure */ /* Parameters: obj(I) - pointer to data description structure */ /* ptr(I) - pointer to kernel data to copy out */ /* */ /* In the above functions, the ipfobj_t structure is copied into the kernel,*/ /* telling ipfilter how to copy out data. In this instance, the ipfobj_t is */ /* already populated with information and now we just need to use it. */ /* There is no need for this function to have a "type" parameter as there */ /* is no point in validating information that comes from the kernel with */ /* itself. */ /* ------------------------------------------------------------------------ */ int ipf_outobjk(softc, obj, ptr) ipf_main_softc_t *softc; ipfobj_t *obj; void *ptr; { int type = obj->ipfo_type; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(147); return EINVAL; } if (obj->ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (obj->ipfo_size < ipf_objbytes[type][1]) { IPFERROR(148); return EINVAL; } } else if (obj->ipfo_size != ipf_objbytes[type][1]) { IPFERROR(149); return EINVAL; } error = COPYOUT(ptr, obj->ipfo_ptr, obj->ipfo_size); if (error != 0) { IPFERROR(150); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, obj, ptr); #else IPFERROR(151); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkl4sum */ /* Returns: int - 0 = good, -1 = bad, 1 = cannot check */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* If possible, calculate the layer 4 checksum for the packet. If this is */ /* not possible, return without indicating a failure or success but in a */ /* way that is ditinguishable. This function should only be called by the */ /* ipf_checkv6sum() for each platform. */ /* ------------------------------------------------------------------------ */ INLINE int ipf_checkl4sum(fin) fr_info_t *fin; { u_short sum, hdrsum, *csump; udphdr_t *udp; int dosum; /* * If the TCP packet isn't a fragment, isn't too short and otherwise * isn't already considered "bad", then validate the checksum. If * this check fails then considered the packet to be "bad". */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) != 0) return 1; DT2(l4sumo, int, fin->fin_out, int, (int)fin->fin_p); if (fin->fin_out == 1) { fin->fin_cksum = FI_CK_SUMOK; return 0; } csump = NULL; hdrsum = 0; dosum = 0; sum = 0; switch (fin->fin_p) { case IPPROTO_TCP : csump = &((tcphdr_t *)fin->fin_dp)->th_sum; dosum = 1; break; case IPPROTO_UDP : udp = fin->fin_dp; if (udp->uh_sum != 0) { csump = &udp->uh_sum; dosum = 1; } break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : csump = &((struct icmp6_hdr *)fin->fin_dp)->icmp6_cksum; dosum = 1; break; #endif case IPPROTO_ICMP : csump = &((struct icmp *)fin->fin_dp)->icmp_cksum; dosum = 1; break; default : return 1; /*NOTREACHED*/ } if (csump != NULL) { hdrsum = *csump; if (fin->fin_p == IPPROTO_UDP && hdrsum == 0xffff) hdrsum = 0x0000; } if (dosum) { sum = fr_cksum(fin, fin->fin_ip, fin->fin_p, fin->fin_dp); } #if !defined(_KERNEL) if (sum == hdrsum) { FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum)); } else { FR_DEBUG(("checkl4sum: %hx != %hx\n", sum, hdrsum)); } #endif DT3(l4sums, u_short, hdrsum, u_short, sum, fr_info_t *, fin); #ifdef USE_INET6 if (hdrsum == sum || (sum == 0 && IP_V(fin->fin_ip) == 6)) { #else if (hdrsum == sum) { #endif fin->fin_cksum = FI_CK_SUMOK; return 0; } fin->fin_cksum = FI_CK_BAD; return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ifpfillv4addr */ /* Returns: int - 0 = address update, -1 = address not updated */ /* Parameters: atype(I) - type of network address update to perform */ /* sin(I) - pointer to source of address information */ /* mask(I) - pointer to source of netmask information */ /* inp(I) - pointer to destination address store */ /* inpmask(I) - pointer to destination netmask store */ /* */ /* Given a type of network address update (atype) to perform, copy */ /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ /* which case the operation fails. For all values of atype other than */ /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ int ipf_ifpfillv4addr(atype, sin, mask, inp, inpmask) int atype; struct sockaddr_in *sin, *mask; struct in_addr *inp, *inpmask; { if (inpmask != NULL && atype != FRI_NETMASKED) inpmask->s_addr = 0xffffffff; if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { if (atype == FRI_NETMASKED) { if (inpmask == NULL) return -1; inpmask->s_addr = mask->sin_addr.s_addr; } inp->s_addr = sin->sin_addr.s_addr & mask->sin_addr.s_addr; } else { inp->s_addr = sin->sin_addr.s_addr; } return 0; } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_ifpfillv6addr */ /* Returns: int - 0 = address update, -1 = address not updated */ /* Parameters: atype(I) - type of network address update to perform */ /* sin(I) - pointer to source of address information */ /* mask(I) - pointer to source of netmask information */ /* inp(I) - pointer to destination address store */ /* inpmask(I) - pointer to destination netmask store */ /* */ /* Given a type of network address update (atype) to perform, copy */ /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ /* which case the operation fails. For all values of atype other than */ /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ int ipf_ifpfillv6addr(atype, sin, mask, inp, inpmask) int atype; struct sockaddr_in6 *sin, *mask; i6addr_t *inp, *inpmask; { i6addr_t *src, *and; src = (i6addr_t *)&sin->sin6_addr; and = (i6addr_t *)&mask->sin6_addr; if (inpmask != NULL && atype != FRI_NETMASKED) { inpmask->i6[0] = 0xffffffff; inpmask->i6[1] = 0xffffffff; inpmask->i6[2] = 0xffffffff; inpmask->i6[3] = 0xffffffff; } if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { if (atype == FRI_NETMASKED) { if (inpmask == NULL) return -1; inpmask->i6[0] = and->i6[0]; inpmask->i6[1] = and->i6[1]; inpmask->i6[2] = and->i6[2]; inpmask->i6[3] = and->i6[3]; } inp->i6[0] = src->i6[0] & and->i6[0]; inp->i6[1] = src->i6[1] & and->i6[1]; inp->i6[2] = src->i6[2] & and->i6[2]; inp->i6[3] = src->i6[3] & and->i6[3]; } else { inp->i6[0] = src->i6[0]; inp->i6[1] = src->i6[1]; inp->i6[2] = src->i6[2]; inp->i6[3] = src->i6[3]; } return 0; } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_matchtag */ /* Returns: 0 == mismatch, 1 == match. */ /* Parameters: tag1(I) - pointer to first tag to compare */ /* tag2(I) - pointer to second tag to compare */ /* */ /* Returns true (non-zero) or false(0) if the two tag structures can be */ /* considered to be a match or not match, respectively. The tag is 16 */ /* bytes long (16 characters) but that is overlayed with 4 32bit ints so */ /* compare the ints instead, for speed. tag1 is the master of the */ /* comparison. This function should only be called with both tag1 and tag2 */ /* as non-NULL pointers. */ /* ------------------------------------------------------------------------ */ int ipf_matchtag(tag1, tag2) ipftag_t *tag1, *tag2; { if (tag1 == tag2) return 1; if ((tag1->ipt_num[0] == 0) && (tag2->ipt_num[0] == 0)) return 1; if ((tag1->ipt_num[0] == tag2->ipt_num[0]) && (tag1->ipt_num[1] == tag2->ipt_num[1]) && (tag1->ipt_num[2] == tag2->ipt_num[2]) && (tag1->ipt_num[3] == tag2->ipt_num[3])) return 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_coalesce */ /* Returns: 1 == success, -1 == failure, 0 == no change */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Attempt to get all of the packet data into a single, contiguous buffer. */ /* If this call returns a failure then the buffers have also been freed. */ /* ------------------------------------------------------------------------ */ int ipf_coalesce(fin) fr_info_t *fin; { if ((fin->fin_flx & FI_COALESCE) != 0) return 1; /* * If the mbuf pointers indicate that there is no mbuf to work with, * return but do not indicate success or failure. */ if (fin->fin_m == NULL || fin->fin_mp == NULL) return 0; #if defined(_KERNEL) if (ipf_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; DT1(frb_coalesce, fr_info_t *, fin); LBUMP(ipf_stats[fin->fin_out].fr_badcoalesces); # ifdef MENTAT FREE_MB_T(*fin->fin_mp); # endif fin->fin_reason = FRB_COALESCE; *fin->fin_mp = NULL; fin->fin_m = NULL; return -1; } #else fin = fin; /* LINT */ #endif return 1; } /* * The following table lists all of the tunable variables that can be * accessed via SIOCIPFGET/SIOCIPFSET/SIOCIPFGETNEXt. The format of each row * in the table below is as follows: * * pointer to value, name of value, minimum, maximum, size of the value's * container, value attribute flags * * For convienience, IPFT_RDONLY means the value is read-only, IPFT_WRDISABLED * means the value can only be written to when IPFilter is loaded but disabled. * The obvious implication is if neither of these are set then the value can be * changed at any time without harm. */ /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_findbycookie */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: cookie(I) - cookie value to search for amongst tuneables */ /* next(O) - pointer to place to store the cookie for the */ /* "next" tuneable, if it is desired. */ /* */ /* This function is used to walk through all of the existing tunables with */ /* successive calls. It searches the known tunables for the one which has */ /* a matching value for "cookie" - ie its address. When returning a match, */ /* the next one to be found may be returned inside next. */ /* ------------------------------------------------------------------------ */ static ipftuneable_t * ipf_tune_findbycookie(ptop, cookie, next) ipftuneable_t **ptop; void *cookie, **next; { ipftuneable_t *ta, **tap; for (ta = *ptop; ta->ipft_name != NULL; ta++) if (ta == cookie) { if (next != NULL) { /* * If the next entry in the array has a name * present, then return a pointer to it for * where to go next, else return a pointer to * the dynaminc list as a key to search there * next. This facilitates a weak linking of * the two "lists" together. */ if ((ta + 1)->ipft_name != NULL) *next = ta + 1; else *next = ptop; } return ta; } for (tap = ptop; (ta = *tap) != NULL; tap = &ta->ipft_next) if (tap == cookie) { if (next != NULL) *next = &ta->ipft_next; return ta; } if (next != NULL) *next = NULL; return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_findbyname */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: name(I) - name of the tuneable entry to find. */ /* */ /* Search the static array of tuneables and the list of dynamic tuneables */ /* for an entry with a matching name. If we can find one, return a pointer */ /* to the matching structure. */ /* ------------------------------------------------------------------------ */ static ipftuneable_t * ipf_tune_findbyname(top, name) ipftuneable_t *top; const char *name; { ipftuneable_t *ta; for (ta = top; ta != NULL; ta = ta->ipft_next) if (!strcmp(ta->ipft_name, name)) { return ta; } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_add_array */ /* Returns: int - 0 == success, else failure */ /* Parameters: newtune - pointer to new tune array to add to tuneables */ /* */ /* Appends tune structures from the array passed in (newtune) to the end of */ /* the current list of "dynamic" tuneable parameters. */ /* If any entry to be added is already present (by name) then the operation */ /* is aborted - entries that have been added are removed before returning. */ /* An entry with no name (NULL) is used as the indication that the end of */ /* the array has been reached. */ /* ------------------------------------------------------------------------ */ int ipf_tune_add_array(softc, newtune) ipf_main_softc_t *softc; ipftuneable_t *newtune; { ipftuneable_t *nt, *dt; int error = 0; for (nt = newtune; nt->ipft_name != NULL; nt++) { error = ipf_tune_add(softc, nt); if (error != 0) { for (dt = newtune; dt != nt; dt++) { (void) ipf_tune_del(softc, dt); } } } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_link */ /* Returns: 0 == success, -1 == failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* array(I) - pointer to an array of tuneables */ /* */ /* Given an array of tunables (array), append them to the current list of */ /* tuneables for this context (softc->ipf_tuners.) To properly prepare the */ /* the array for being appended to the list, initialise all of the next */ /* pointers so we don't need to walk parts of it with ++ and others with */ /* next. The array is expected to have an entry with a NULL name as the */ /* terminator. Trying to add an array with no non-NULL names will return as */ /* a failure. */ /* ------------------------------------------------------------------------ */ int ipf_tune_array_link(softc, array) ipf_main_softc_t *softc; ipftuneable_t *array; { ipftuneable_t *t, **p; t = array; if (t->ipft_name == NULL) return -1; for (; t[1].ipft_name != NULL; t++) t[0].ipft_next = &t[1]; t->ipft_next = NULL; /* * Since a pointer to the last entry isn't kept, we need to find it * each time we want to add new variables to the list. */ for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) if (t->ipft_name == NULL) break; *p = array; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_unlink */ /* Returns: 0 == success, -1 == failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* array(I) - pointer to an array of tuneables */ /* */ /* ------------------------------------------------------------------------ */ int ipf_tune_array_unlink(softc, array) ipf_main_softc_t *softc; ipftuneable_t *array; { ipftuneable_t *t, **p; for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) if (t == array) break; if (t == NULL) return -1; for (; t[1].ipft_name != NULL; t++) ; *p = t->ipft_next; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_copy */ /* Returns: NULL = failure, else pointer to new array */ /* Parameters: base(I) - pointer to structure base */ /* size(I) - size of the array at template */ /* template(I) - original array to copy */ /* */ /* Allocate memory for a new set of tuneable values and copy everything */ /* from template into the new region of memory. The new region is full of */ /* uninitialised pointers (ipft_next) so set them up. Now, ipftp_offset... */ /* */ /* NOTE: the following assumes that sizeof(long) == sizeof(void *) */ /* In the array template, ipftp_offset is the offset (in bytes) of the */ /* location of the tuneable value inside the structure pointed to by base. */ /* As ipftp_offset is a union over the pointers to the tuneable values, if */ /* we add base to the copy's ipftp_offset, copy ends up with a pointer in */ /* ipftp_void that points to the stored value. */ /* ------------------------------------------------------------------------ */ ipftuneable_t * ipf_tune_array_copy(base, size, template) void *base; size_t size; ipftuneable_t *template; { ipftuneable_t *copy; int i; KMALLOCS(copy, ipftuneable_t *, size); if (copy == NULL) { return NULL; } bcopy(template, copy, size); for (i = 0; copy[i].ipft_name; i++) { copy[i].ipft_una.ipftp_offset += (u_long)base; copy[i].ipft_next = copy + i + 1; } return copy; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_add */ /* Returns: int - 0 == success, else failure */ /* Parameters: newtune - pointer to new tune entry to add to tuneables */ /* */ /* Appends tune structures from the array passed in (newtune) to the end of */ /* the current list of "dynamic" tuneable parameters. Once added, the */ /* owner of the object is not expected to ever change "ipft_next". */ /* ------------------------------------------------------------------------ */ int ipf_tune_add(softc, newtune) ipf_main_softc_t *softc; ipftuneable_t *newtune; { ipftuneable_t *ta, **tap; ta = ipf_tune_findbyname(softc->ipf_tuners, newtune->ipft_name); if (ta != NULL) { IPFERROR(74); return EEXIST; } for (tap = &softc->ipf_tuners; *tap != NULL; tap = &(*tap)->ipft_next) ; newtune->ipft_next = NULL; *tap = newtune; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_del */ /* Returns: int - 0 == success, else failure */ /* Parameters: oldtune - pointer to tune entry to remove from the list of */ /* current dynamic tuneables */ /* */ /* Search for the tune structure, by pointer, in the list of those that are */ /* dynamically added at run time. If found, adjust the list so that this */ /* structure is no longer part of it. */ /* ------------------------------------------------------------------------ */ int ipf_tune_del(softc, oldtune) ipf_main_softc_t *softc; ipftuneable_t *oldtune; { ipftuneable_t *ta, **tap; int error = 0; for (tap = &softc->ipf_tuners; (ta = *tap) != NULL; tap = &ta->ipft_next) { if (ta == oldtune) { *tap = oldtune->ipft_next; oldtune->ipft_next = NULL; break; } } if (ta == NULL) { error = ESRCH; IPFERROR(75); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_del_array */ /* Returns: int - 0 == success, else failure */ /* Parameters: oldtune - pointer to tuneables array */ /* */ /* Remove each tuneable entry in the array from the list of "dynamic" */ /* tunables. If one entry should fail to be found, an error will be */ /* returned and no further ones removed. */ /* An entry with a NULL name is used as the indicator of the last entry in */ /* the array. */ /* ------------------------------------------------------------------------ */ int ipf_tune_del_array(softc, oldtune) ipf_main_softc_t *softc; ipftuneable_t *oldtune; { ipftuneable_t *ot; int error = 0; for (ot = oldtune; ot->ipft_name != NULL; ot++) { error = ipf_tune_del(softc, ot); if (error != 0) break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune */ /* Returns: int - 0 == success, else failure */ /* Parameters: cmd(I) - ioctl command number */ /* data(I) - pointer to ioctl data structure */ /* */ /* Implement handling of SIOCIPFGETNEXT, SIOCIPFGET and SIOCIPFSET. These */ /* three ioctls provide the means to access and control global variables */ /* within IPFilter, allowing (for example) timeouts and table sizes to be */ /* changed without rebooting, reloading or recompiling. The initialisation */ /* and 'destruction' routines of the various components of ipfilter are all */ /* each responsible for handling their own values being too big. */ /* ------------------------------------------------------------------------ */ int ipf_ipftune(softc, cmd, data) ipf_main_softc_t *softc; ioctlcmd_t cmd; void *data; { ipftuneable_t *ta; ipftune_t tu; void *cookie; int error; error = ipf_inobj(softc, data, NULL, &tu, IPFOBJ_TUNEABLE); if (error != 0) return error; tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0'; cookie = tu.ipft_cookie; ta = NULL; switch (cmd) { case SIOCIPFGETNEXT : /* * If cookie is non-NULL, assume it to be a pointer to the last * entry we looked at, so find it (if possible) and return a * pointer to the next one after it. The last entry in the * the table is a NULL entry, so when we get to it, set cookie * to NULL and return that, indicating end of list, erstwhile * if we come in with cookie set to NULL, we are starting anew * at the front of the list. */ if (cookie != NULL) { ta = ipf_tune_findbycookie(&softc->ipf_tuners, cookie, &tu.ipft_cookie); } else { ta = softc->ipf_tuners; tu.ipft_cookie = ta + 1; } if (ta != NULL) { /* * Entry found, but does the data pointed to by that * row fit in what we can return? */ if (ta->ipft_sz > sizeof(tu.ipft_un)) { IPFERROR(76); return EINVAL; } tu.ipft_vlong = 0; if (ta->ipft_sz == sizeof(u_long)) tu.ipft_vlong = *ta->ipft_plong; else if (ta->ipft_sz == sizeof(u_int)) tu.ipft_vint = *ta->ipft_pint; else if (ta->ipft_sz == sizeof(u_short)) tu.ipft_vshort = *ta->ipft_pshort; else if (ta->ipft_sz == sizeof(u_char)) tu.ipft_vchar = *ta->ipft_pchar; tu.ipft_sz = ta->ipft_sz; tu.ipft_min = ta->ipft_min; tu.ipft_max = ta->ipft_max; tu.ipft_flags = ta->ipft_flags; bcopy(ta->ipft_name, tu.ipft_name, MIN(sizeof(tu.ipft_name), strlen(ta->ipft_name) + 1)); } error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); break; case SIOCIPFGET : case SIOCIPFSET : /* * Search by name or by cookie value for a particular entry * in the tuning paramter table. */ IPFERROR(77); error = ESRCH; if (cookie != NULL) { ta = ipf_tune_findbycookie(&softc->ipf_tuners, cookie, NULL); if (ta != NULL) error = 0; } else if (tu.ipft_name[0] != '\0') { ta = ipf_tune_findbyname(softc->ipf_tuners, tu.ipft_name); if (ta != NULL) error = 0; } if (error != 0) break; if (cmd == (ioctlcmd_t)SIOCIPFGET) { /* * Fetch the tuning parameters for a particular value */ tu.ipft_vlong = 0; if (ta->ipft_sz == sizeof(u_long)) tu.ipft_vlong = *ta->ipft_plong; else if (ta->ipft_sz == sizeof(u_int)) tu.ipft_vint = *ta->ipft_pint; else if (ta->ipft_sz == sizeof(u_short)) tu.ipft_vshort = *ta->ipft_pshort; else if (ta->ipft_sz == sizeof(u_char)) tu.ipft_vchar = *ta->ipft_pchar; tu.ipft_cookie = ta; tu.ipft_sz = ta->ipft_sz; tu.ipft_min = ta->ipft_min; tu.ipft_max = ta->ipft_max; tu.ipft_flags = ta->ipft_flags; error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } else if (cmd == (ioctlcmd_t)SIOCIPFSET) { /* * Set an internal parameter. The hard part here is * getting the new value safely and correctly out of * the kernel (given we only know its size, not type.) */ u_long in; if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) && (softc->ipf_running > 0)) { IPFERROR(78); error = EBUSY; break; } in = tu.ipft_vlong; if (in < ta->ipft_min || in > ta->ipft_max) { IPFERROR(79); error = EINVAL; break; } if (ta->ipft_func != NULL) { SPL_INT(s); SPL_NET(s); error = (*ta->ipft_func)(softc, ta, &tu.ipft_un); SPL_X(s); } else if (ta->ipft_sz == sizeof(u_long)) { tu.ipft_vlong = *ta->ipft_plong; *ta->ipft_plong = in; } else if (ta->ipft_sz == sizeof(u_int)) { tu.ipft_vint = *ta->ipft_pint; *ta->ipft_pint = (u_int)(in & 0xffffffff); } else if (ta->ipft_sz == sizeof(u_short)) { tu.ipft_vshort = *ta->ipft_pshort; *ta->ipft_pshort = (u_short)(in & 0xffff); } else if (ta->ipft_sz == sizeof(u_char)) { tu.ipft_vchar = *ta->ipft_pchar; *ta->ipft_pchar = (u_char)(in & 0xff); } error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } break; default : IPFERROR(80); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_zerostats */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(O) - pointer to pointer for copying data back to */ /* */ /* Copies the current statistics out to userspace and then zero's the */ /* current ones in the kernel. The lock is only held across the bzero() as */ /* the copyout may result in paging (ie network activity.) */ /* ------------------------------------------------------------------------ */ int ipf_zerostats(softc, data) ipf_main_softc_t *softc; caddr_t data; { friostat_t fio; ipfobj_t obj; int error; error = ipf_inobj(softc, data, &obj, &fio, IPFOBJ_IPFSTAT); if (error != 0) return error; ipf_getstat(softc, &fio, obj.ipfo_rev); error = ipf_outobj(softc, data, &fio, IPFOBJ_IPFSTAT); if (error != 0) return error; WRITE_ENTER(&softc->ipf_mutex); bzero(&softc->ipf_stats, sizeof(softc->ipf_stats)); RWLOCK_EXIT(&softc->ipf_mutex); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvedest */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* base(I) - where strings are stored */ /* fdp(IO) - pointer to destination information to resolve */ /* v(I) - IP protocol version to match */ /* */ /* Looks up an interface name in the frdest structure pointed to by fdp and */ /* if a matching name can be found for the particular IP protocol version */ /* then store the interface pointer in the frdest struct. If no match is */ /* found, then set the interface pointer to be -1 as NULL is considered to */ /* indicate there is no information at all in the structure. */ /* ------------------------------------------------------------------------ */ int ipf_resolvedest(softc, base, fdp, v) ipf_main_softc_t *softc; char *base; frdest_t *fdp; int v; { int errval = 0; void *ifp; ifp = NULL; if (fdp->fd_name != -1) { if (fdp->fd_type == FRD_DSTLIST) { ifp = ipf_lookup_res_name(softc, IPL_LOGIPF, IPLT_DSTLIST, base + fdp->fd_name, NULL); if (ifp == NULL) { IPFERROR(144); errval = ESRCH; } } else { ifp = GETIFP(base + fdp->fd_name, v); if (ifp == NULL) ifp = (void *)-1; } } fdp->fd_ptr = ifp; return errval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvenic */ /* Returns: void* - NULL = wildcard name, -1 = failed to find NIC, else */ /* pointer to interface structure for NIC */ /* Parameters: softc(I)- pointer to soft context main structure */ /* name(I) - complete interface name */ /* v(I) - IP protocol version */ /* */ /* Look for a network interface structure that firstly has a matching name */ /* to that passed in and that is also being used for that IP protocol */ /* version (necessary on some platforms where there are separate listings */ /* for both IPv4 and IPv6 on the same physical NIC. */ /* ------------------------------------------------------------------------ */ void * ipf_resolvenic(softc, name, v) ipf_main_softc_t *softc; char *name; int v; { void *nic; softc = softc; /* gcc -Wextra */ if (name[0] == '\0') return NULL; if ((name[1] == '\0') && ((name[0] == '-') || (name[0] == '*'))) { return NULL; } nic = GETIFP(name, v); if (nic == NULL) nic = (void *)-1; return nic; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_expire */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* This function is run every ipf tick to see if there are any tokens that */ /* have been held for too long and need to be freed up. */ /* ------------------------------------------------------------------------ */ void ipf_token_expire(softc) ipf_main_softc_t *softc; { ipftoken_t *it; WRITE_ENTER(&softc->ipf_tokens); while ((it = softc->ipf_token_head) != NULL) { if (it->ipt_die > softc->ipf_ticks) break; ipf_token_deref(softc, it); } RWLOCK_EXIT(&softc->ipf_tokens); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_flush */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Loop through all of the existing tokens and call deref to see if they */ /* can be freed. Normally a function like this might just loop on */ /* ipf_token_head but there is a chance that a token might have a ref count */ /* of greater than one and in that case the the reference would drop twice */ /* by code that is only entitled to drop it once. */ /* ------------------------------------------------------------------------ */ static void ipf_token_flush(softc) ipf_main_softc_t *softc; { ipftoken_t *it, *next; WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = next) { next = it->ipt_next; (void) ipf_token_deref(softc, it); } RWLOCK_EXIT(&softc->ipf_tokens); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* type(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function looks for a a token in the current list that matches up */ /* the fields (type, uid, ptr). If none is found, ESRCH is returned, else */ /* call ipf_token_dewref() to remove it from the list. In the event that */ /* the token has a reference held elsewhere, setting ipt_complete to 2 */ /* enables debugging to distinguish between the two paths that ultimately */ /* lead to a token to be deleted. */ /* ------------------------------------------------------------------------ */ int ipf_token_del(softc, type, uid, ptr) ipf_main_softc_t *softc; int type, uid; void *ptr; { ipftoken_t *it; int error; IPFERROR(82); error = ESRCH; WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { if (ptr == it->ipt_ctx && type == it->ipt_type && uid == it->ipt_uid) { it->ipt_complete = 2; ipf_token_deref(softc, it); error = 0; break; } } RWLOCK_EXIT(&softc->ipf_tokens); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_mark_complete */ /* Returns: None. */ /* Parameters: token(I) - pointer to token structure */ /* */ /* Mark a token as being ineligable for being found with ipf_token_find. */ /* ------------------------------------------------------------------------ */ void ipf_token_mark_complete(token) ipftoken_t *token; { if (token->ipt_complete == 0) token->ipt_complete = 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_find */ /* Returns: ipftoken_t * - NULL if no memory, else pointer to token */ /* Parameters: softc(I)- pointer to soft context main structure */ /* type(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function looks for a live token in the list of current tokens that */ /* matches the tuple (type, uid, ptr). If one cannot be found then one is */ /* allocated. If one is found then it is moved to the top of the list of */ /* currently active tokens. */ /* ------------------------------------------------------------------------ */ ipftoken_t * ipf_token_find(softc, type, uid, ptr) ipf_main_softc_t *softc; int type, uid; void *ptr; { ipftoken_t *it, *new; KMALLOC(new, ipftoken_t *); if (new != NULL) bzero((char *)new, sizeof(*new)); WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { if ((ptr == it->ipt_ctx) && (type == it->ipt_type) && (uid == it->ipt_uid) && (it->ipt_complete < 2)) break; } if (it == NULL) { it = new; new = NULL; if (it == NULL) { RWLOCK_EXIT(&softc->ipf_tokens); return NULL; } it->ipt_ctx = ptr; it->ipt_uid = uid; it->ipt_type = type; it->ipt_ref = 1; } else { if (new != NULL) { KFREE(new); new = NULL; } if (it->ipt_complete > 0) it = NULL; else ipf_token_unlink(softc, it); } if (it != NULL) { it->ipt_pnext = softc->ipf_token_tail; *softc->ipf_token_tail = it; softc->ipf_token_tail = &it->ipt_next; it->ipt_next = NULL; it->ipt_ref++; it->ipt_die = softc->ipf_ticks + 20; } RWLOCK_EXIT(&softc->ipf_tokens); return it; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_unlink */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token structure */ /* Write Locks: ipf_tokens */ /* */ /* This function unlinks a token structure from the linked list of tokens */ /* that "own" it. The head pointer never needs to be explicitly adjusted */ /* but the tail does due to the linked list implementation. */ /* ------------------------------------------------------------------------ */ static void ipf_token_unlink(softc, token) ipf_main_softc_t *softc; ipftoken_t *token; { if (softc->ipf_token_tail == &token->ipt_next) softc->ipf_token_tail = token->ipt_pnext; *token->ipt_pnext = token->ipt_next; if (token->ipt_next != NULL) token->ipt_next->ipt_pnext = token->ipt_pnext; token->ipt_next = NULL; token->ipt_pnext = NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_deref */ /* Returns: int - 0 == token freed, else reference count */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token structure */ /* Write Locks: ipf_tokens */ /* */ /* Drop the reference count on the token structure and if it drops to zero, */ /* call the dereference function for the token type because it is then */ /* possible to free the token data structure. */ /* ------------------------------------------------------------------------ */ int ipf_token_deref(softc, token) ipf_main_softc_t *softc; ipftoken_t *token; { void *data, **datap; ASSERT(token->ipt_ref > 0); token->ipt_ref--; if (token->ipt_ref > 0) return token->ipt_ref; data = token->ipt_data; datap = &data; if ((data != NULL) && (data != (void *)-1)) { switch (token->ipt_type) { case IPFGENITER_IPF : (void) ipf_derefrule(softc, (frentry_t **)datap); break; case IPFGENITER_IPNAT : WRITE_ENTER(&softc->ipf_nat); ipf_nat_rule_deref(softc, (ipnat_t **)datap); RWLOCK_EXIT(&softc->ipf_nat); break; case IPFGENITER_NAT : ipf_nat_deref(softc, (nat_t **)datap); break; case IPFGENITER_STATE : ipf_state_deref(softc, (ipstate_t **)datap); break; case IPFGENITER_FRAG : ipf_frag_pkt_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_NATFRAG : ipf_frag_nat_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_HOSTMAP : WRITE_ENTER(&softc->ipf_nat); ipf_nat_hostmapdel(softc, (hostmap_t **)datap); RWLOCK_EXIT(&softc->ipf_nat); break; default : ipf_lookup_iterderef(softc, token->ipt_type, data); break; } } ipf_token_unlink(softc, token); KFREE(token); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nextrule */ /* Returns: frentry_t * - NULL == no more rules, else pointer to next */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* out(I) - 1 == out rules, 0 == input rules */ /* */ /* Starting with "fr", find the next rule to visit. This includes visiting */ /* the list of rule groups if either fr is NULL (empty list) or it is the */ /* last rule in the list. When walking rule lists, it is either input or */ /* output rules that are returned, never both. */ /* ------------------------------------------------------------------------ */ static frentry_t * ipf_nextrule(softc, active, unit, fr, out) ipf_main_softc_t *softc; int active, unit; frentry_t *fr; int out; { frentry_t *next; frgroup_t *fg; if (fr != NULL && fr->fr_group != -1) { fg = ipf_findgroup(softc, fr->fr_names + fr->fr_group, unit, active, NULL); if (fg != NULL) fg = fg->fg_next; } else { fg = softc->ipf_groups[unit][active]; } while (fg != NULL) { next = fg->fg_start; while (next != NULL) { if (out) { if (next->fr_flags & FR_OUTQUE) return next; } else if (next->fr_flags & FR_INQUE) { return next; } next = next->fr_next; } if (next == NULL) fg = fg->fg_next; } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_getnextrule */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* t(I) - pointer to destination information to resolve */ /* ptr(I) - pointer to ipfobj_t to copyin from user space */ /* */ /* This function's first job is to bring in the ipfruleiter_t structure via */ /* the ipfobj_t structure to determine what should be the next rule to */ /* return. Once the ipfruleiter_t has been brought in, it then tries to */ /* find the 'next rule'. This may include searching rule group lists or */ /* just be as simple as looking at the 'next' field in the rule structure. */ /* When we have found the rule to return, increase its reference count and */ /* if we used an existing rule to get here, decrease its reference count. */ /* ------------------------------------------------------------------------ */ int ipf_getnextrule(softc, t, ptr) ipf_main_softc_t *softc; ipftoken_t *t; void *ptr; { frentry_t *fr, *next, zero; ipfruleiter_t it; int error, out; frgroup_t *fg; ipfobj_t obj; int predict; char *dst; int unit; if (t == NULL || ptr == NULL) { IPFERROR(84); return EFAULT; } error = ipf_inobj(softc, ptr, &obj, &it, IPFOBJ_IPFITER); if (error != 0) return error; if ((it.iri_inout < 0) || (it.iri_inout > 3)) { IPFERROR(85); return EINVAL; } if ((it.iri_active != 0) && (it.iri_active != 1)) { IPFERROR(86); return EINVAL; } if (it.iri_nrules == 0) { IPFERROR(87); return ENOSPC; } if (it.iri_rule == NULL) { IPFERROR(88); return EFAULT; } fg = NULL; fr = t->ipt_data; if ((it.iri_inout & F_OUT) != 0) out = 1; else out = 0; if ((it.iri_inout & F_ACIN) != 0) unit = IPL_LOGCOUNT; else unit = IPL_LOGIPF; READ_ENTER(&softc->ipf_mutex); if (fr == NULL) { if (*it.iri_group == '\0') { if (unit == IPL_LOGCOUNT) { next = softc->ipf_acct[out][it.iri_active]; } else { next = softc->ipf_rules[out][it.iri_active]; } if (next == NULL) next = ipf_nextrule(softc, it.iri_active, unit, NULL, out); } else { fg = ipf_findgroup(softc, it.iri_group, unit, it.iri_active, NULL); if (fg != NULL) next = fg->fg_start; else next = NULL; } } else { next = fr->fr_next; if (next == NULL) next = ipf_nextrule(softc, it.iri_active, unit, fr, out); } if (next != NULL && next->fr_next != NULL) predict = 1; else if (ipf_nextrule(softc, it.iri_active, unit, next, out) != NULL) predict = 1; else predict = 0; if (fr != NULL) (void) ipf_derefrule(softc, &fr); obj.ipfo_type = IPFOBJ_FRENTRY; dst = (char *)it.iri_rule; if (next != NULL) { obj.ipfo_size = next->fr_size; MUTEX_ENTER(&next->fr_lock); next->fr_ref++; MUTEX_EXIT(&next->fr_lock); t->ipt_data = next; } else { obj.ipfo_size = sizeof(frentry_t); bzero(&zero, sizeof(zero)); next = &zero; t->ipt_data = NULL; } it.iri_rule = predict ? next : NULL; if (predict == 0) ipf_token_mark_complete(t); RWLOCK_EXIT(&softc->ipf_mutex); obj.ipfo_ptr = dst; error = ipf_outobjk(softc, &obj, next); if (error == 0 && t->ipt_data != NULL) { dst += obj.ipfo_size; if (next->fr_data != NULL) { ipfobj_t dobj; if (next->fr_type == FR_T_IPFEXPR) dobj.ipfo_type = IPFOBJ_IPFEXPR; else dobj.ipfo_type = IPFOBJ_FRIPF; dobj.ipfo_size = next->fr_dsize; dobj.ipfo_rev = obj.ipfo_rev; dobj.ipfo_ptr = dst; error = ipf_outobjk(softc, &dobj, next->fr_data); } } if ((fr != NULL) && (next == &zero)) (void) ipf_derefrule(softc, &fr); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frruleiter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function serves as a stepping stone between ipf_ipf_ioctl and */ /* ipf_getnextrule. It's role is to find the right token in the kernel for */ /* the process doing the ioctl and use that to ask for the next rule. */ /* ------------------------------------------------------------------------ */ static int ipf_frruleiter(softc, data, uid, ctx) ipf_main_softc_t *softc; void *data, *ctx; int uid; { ipftoken_t *token; ipfruleiter_t it; ipfobj_t obj; int error; token = ipf_token_find(softc, IPFGENITER_IPF, uid, ctx); if (token != NULL) { error = ipf_getnextrule(softc, token, data); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } else { error = ipf_inobj(softc, data, &obj, &it, IPFOBJ_IPFITER); if (error != 0) return error; it.iri_rule = NULL; error = ipf_outobj(softc, data, &it, IPFOBJ_IPFITER); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_geniter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to ipftoken_t structure */ /* itp(I) - pointer to iterator data */ /* */ /* Decide which iterator function to call using information passed through */ /* the ipfgeniter_t structure at itp. */ /* ------------------------------------------------------------------------ */ static int ipf_geniter(softc, token, itp) ipf_main_softc_t *softc; ipftoken_t *token; ipfgeniter_t *itp; { int error; switch (itp->igi_type) { case IPFGENITER_FRAG : error = ipf_frag_pkt_next(softc, token, itp); break; default : IPFERROR(92); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_genericiter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* Handle the SIOCGENITER ioctl for the ipfilter device. The primary role */ /* ------------------------------------------------------------------------ */ int ipf_genericiter(softc, data, uid, ctx) ipf_main_softc_t *softc; void *data, *ctx; int uid; { ipftoken_t *token; ipfgeniter_t iter; int error; error = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_GENITER); if (error != 0) return error; token = ipf_token_find(softc, iter.igi_type, uid, ctx); if (token != NULL) { token->ipt_subtype = iter.igi_type; error = ipf_geniter(softc, token, &iter); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } else { IPFERROR(93); error = 0; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ipf_ioctl */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* cmd(I) - the ioctl command number */ /* mode(I) - mode flags for the ioctl */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function handles all of the ioctl command that are actually isssued */ /* to the /dev/ipl device. */ /* ------------------------------------------------------------------------ */ int ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx) ipf_main_softc_t *softc; caddr_t data; ioctlcmd_t cmd; int mode, uid; void *ctx; { friostat_t fio; int error, tmp; ipfobj_t obj; SPL_INT(s); switch (cmd) { case SIOCFRENB : if (!(mode & FWRITE)) { IPFERROR(94); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error != 0) { IPFERROR(95); error = EFAULT; break; } WRITE_ENTER(&softc->ipf_global); if (tmp) { if (softc->ipf_running > 0) error = 0; else error = ipfattach(softc); if (error == 0) softc->ipf_running = 1; else (void) ipfdetach(softc); } else { if (softc->ipf_running == 1) error = ipfdetach(softc); else error = 0; if (error == 0) softc->ipf_running = -1; } RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCIPFSET : if (!(mode & FWRITE)) { IPFERROR(96); error = EPERM; break; } /* FALLTHRU */ case SIOCIPFGETNEXT : case SIOCIPFGET : error = ipf_ipftune(softc, cmd, (void *)data); break; case SIOCSETFF : if (!(mode & FWRITE)) { IPFERROR(97); error = EPERM; } else { error = BCOPYIN(data, &softc->ipf_flags, sizeof(softc->ipf_flags)); if (error != 0) { IPFERROR(98); error = EFAULT; } } break; case SIOCGETFF : error = BCOPYOUT(&softc->ipf_flags, data, sizeof(softc->ipf_flags)); if (error != 0) { IPFERROR(99); error = EFAULT; } break; case SIOCFUNCL : error = ipf_resolvefunc(softc, (void *)data); break; case SIOCINAFR : case SIOCRMAFR : case SIOCADAFR : case SIOCZRLST : if (!(mode & FWRITE)) { IPFERROR(100); error = EPERM; } else { error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, softc->ipf_active, 1); } break; case SIOCINIFR : case SIOCRMIFR : case SIOCADIFR : if (!(mode & FWRITE)) { IPFERROR(101); error = EPERM; } else { error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, 1 - softc->ipf_active, 1); } break; case SIOCSWAPA : if (!(mode & FWRITE)) { IPFERROR(102); error = EPERM; } else { WRITE_ENTER(&softc->ipf_mutex); error = BCOPYOUT(&softc->ipf_active, data, sizeof(softc->ipf_active)); if (error != 0) { IPFERROR(103); error = EFAULT; } else { softc->ipf_active = 1 - softc->ipf_active; } RWLOCK_EXIT(&softc->ipf_mutex); } break; case SIOCGETFS : error = ipf_inobj(softc, (void *)data, &obj, &fio, IPFOBJ_IPFSTAT); if (error != 0) break; ipf_getstat(softc, &fio, obj.ipfo_rev); error = ipf_outobj(softc, (void *)data, &fio, IPFOBJ_IPFSTAT); break; case SIOCFRZST : if (!(mode & FWRITE)) { IPFERROR(104); error = EPERM; } else error = ipf_zerostats(softc, (caddr_t)data); break; case SIOCIPFFL : if (!(mode & FWRITE)) { IPFERROR(105); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(106); error = EFAULT; } } else { IPFERROR(107); error = EFAULT; } } break; #ifdef USE_INET6 case SIOCIPFL6 : if (!(mode & FWRITE)) { IPFERROR(108); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(109); error = EFAULT; } } else { IPFERROR(110); error = EFAULT; } } break; #endif case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(122); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error == 0) { ipf_state_setlock(softc->ipf_state_soft, tmp); ipf_nat_setlock(softc->ipf_nat_soft, tmp); ipf_frag_setlock(softc->ipf_frag_soft, tmp); ipf_auth_setlock(softc->ipf_auth_soft, tmp); } else { IPFERROR(111); error = EFAULT; } } break; #ifdef IPFILTER_LOG case SIOCIPFFB : if (!(mode & FWRITE)) { IPFERROR(112); error = EPERM; } else { tmp = ipf_log_clear(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error) { IPFERROR(113); error = EFAULT; } } break; #endif /* IPFILTER_LOG */ case SIOCFRSYN : if (!(mode & FWRITE)) { IPFERROR(114); error = EPERM; } else { WRITE_ENTER(&softc->ipf_global); #if (defined(MENTAT) && defined(_KERNEL)) && !defined(INSTANCES) error = ipfsync(); #else ipf_sync(softc, NULL); error = 0; #endif RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCGFRST : error = ipf_outobj(softc, (void *)data, ipf_frag_stats(softc->ipf_frag_soft), IPFOBJ_FRAGSTAT); break; #ifdef IPFILTER_LOG case FIONREAD : tmp = ipf_log_bytesused(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); break; #endif case SIOCIPFITER : SPL_SCHED(s); error = ipf_frruleiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCGENITER : SPL_SCHED(s); error = ipf_genericiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCIPFDELTOK : error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error == 0) { SPL_SCHED(s); error = ipf_token_del(softc, tmp, uid, ctx); SPL_X(s); } break; default : IPFERROR(115); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_decaps */ /* Returns: int - -1 == decapsulation failed, else bit mask of */ /* flags indicating packet filtering decision. */ /* Parameters: fin(I) - pointer to packet information */ /* pass(I) - IP protocol version to match */ /* l5proto(I) - layer 5 protocol to decode UDP data as. */ /* */ /* This function is called for packets that are wrapt up in other packets, */ /* for example, an IP packet that is the entire data segment for another IP */ /* packet. If the basic constraints for this are satisfied, change the */ /* buffer to point to the start of the inner packet and start processing */ /* rules belonging to the head group this rule specifies. */ /* ------------------------------------------------------------------------ */ u_32_t ipf_decaps(fin, pass, l5proto) fr_info_t *fin; u_32_t pass; int l5proto; { fr_info_t fin2, *fino = NULL; int elen, hlen, nh; grehdr_t gre; ip_t *ip; mb_t *m; if ((fin->fin_flx & FI_COALESCE) == 0) if (ipf_coalesce(fin) == -1) goto cantdecaps; m = fin->fin_m; hlen = fin->fin_hlen; switch (fin->fin_p) { case IPPROTO_UDP : /* * In this case, the specific protocol being decapsulated * inside UDP frames comes from the rule. */ nh = fin->fin_fr->fr_icode; break; case IPPROTO_GRE : /* 47 */ bcopy(fin->fin_dp, (char *)&gre, sizeof(gre)); hlen += sizeof(grehdr_t); if (gre.gr_R|gre.gr_s) goto cantdecaps; if (gre.gr_C) hlen += 4; if (gre.gr_K) hlen += 4; if (gre.gr_S) hlen += 4; nh = IPPROTO_IP; /* * If the routing options flag is set, validate that it is * there and bounce over it. */ #if 0 /* This is really heavy weight and lots of room for error, */ /* so for now, put it off and get the simple stuff right. */ if (gre.gr_R) { u_char off, len, *s; u_short af; int end; end = 0; s = fin->fin_dp; s += hlen; aplen = fin->fin_plen - hlen; while (aplen > 3) { af = (s[0] << 8) | s[1]; off = s[2]; len = s[3]; aplen -= 4; s += 4; if (af == 0 && len == 0) { end = 1; break; } if (aplen < len) break; s += len; aplen -= len; } if (end != 1) goto cantdecaps; hlen = s - (u_char *)fin->fin_dp; } #endif break; #ifdef IPPROTO_IPIP case IPPROTO_IPIP : /* 4 */ #endif nh = IPPROTO_IP; break; default : /* Includes ESP, AH is special for IPv4 */ goto cantdecaps; } switch (nh) { case IPPROTO_IP : case IPPROTO_IPV6 : break; default : goto cantdecaps; } bcopy((char *)fin, (char *)&fin2, sizeof(fin2)); fino = fin; fin = &fin2; elen = hlen; #if defined(MENTAT) && defined(_KERNEL) m->b_rptr += elen; #else m->m_data += elen; m->m_len -= elen; #endif fin->fin_plen -= elen; ip = (ip_t *)((char *)fin->fin_ip + elen); /* * Make sure we have at least enough data for the network layer * header. */ if (IP_V(ip) == 4) hlen = IP_HL(ip) << 2; #ifdef USE_INET6 else if (IP_V(ip) == 6) hlen = sizeof(ip6_t); #endif else goto cantdecaps2; if (fin->fin_plen < hlen) goto cantdecaps2; fin->fin_dp = (char *)ip + hlen; if (IP_V(ip) == 4) { /* * Perform IPv4 header checksum validation. */ if (ipf_cksum((u_short *)ip, hlen)) goto cantdecaps2; } if (ipf_makefrip(hlen, ip, fin) == -1) { cantdecaps2: if (m != NULL) { #if defined(MENTAT) && defined(_KERNEL) m->b_rptr -= elen; #else m->m_data -= elen; m->m_len += elen; #endif } cantdecaps: DT1(frb_decapfrip, fr_info_t *, fin); pass &= ~FR_CMDMASK; pass |= FR_BLOCK|FR_QUICK; fin->fin_reason = FRB_DECAPFRIP; return -1; } pass = ipf_scanlist(fin, pass); /* * Copy the packet filter "result" fields out of the fr_info_t struct * that is local to the decapsulation processing and back into the * one we were called with. */ fino->fin_flx = fin->fin_flx; fino->fin_rev = fin->fin_rev; fino->fin_icode = fin->fin_icode; fino->fin_rule = fin->fin_rule; (void) strncpy(fino->fin_group, fin->fin_group, FR_GROUPLEN); fino->fin_fr = fin->fin_fr; fino->fin_error = fin->fin_error; fino->fin_mp = fin->fin_mp; fino->fin_m = fin->fin_m; m = fin->fin_m; if (m != NULL) { #if defined(MENTAT) && defined(_KERNEL) m->b_rptr -= elen; #else m->m_data -= elen; m->m_len += elen; #endif } return pass; } /* ------------------------------------------------------------------------ */ /* Function: ipf_matcharray_load */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* objp(I) - ipfobj_t structure to load data into */ /* arrayptr(I) - pointer to location to store array pointer */ /* */ /* This function loads in a mathing array through the ipfobj_t struct that */ /* describes it. Sanity checking and array size limitations are enforced */ /* in this function to prevent userspace from trying to load in something */ /* that is insanely big. Once the size of the array is known, the memory */ /* required is malloc'd and returned through changing *arrayptr. The */ /* contents of the array are verified before returning. Only in the event */ /* of a successful call is the caller required to free up the malloc area. */ /* ------------------------------------------------------------------------ */ int ipf_matcharray_load(softc, data, objp, arrayptr) ipf_main_softc_t *softc; caddr_t data; ipfobj_t *objp; int **arrayptr; { int arraysize, *array, error; *arrayptr = NULL; error = BCOPYIN(data, objp, sizeof(*objp)); if (error != 0) { IPFERROR(116); return EFAULT; } if (objp->ipfo_type != IPFOBJ_IPFEXPR) { IPFERROR(117); return EINVAL; } if (((objp->ipfo_size & 3) != 0) || (objp->ipfo_size == 0) || (objp->ipfo_size > 1024)) { IPFERROR(118); return EINVAL; } arraysize = objp->ipfo_size * sizeof(*array); KMALLOCS(array, int *, arraysize); if (array == NULL) { IPFERROR(119); return ENOMEM; } error = COPYIN(objp->ipfo_ptr, array, arraysize); if (error != 0) { KFREES(array, arraysize); IPFERROR(120); return EFAULT; } if (ipf_matcharray_verify(array, arraysize) != 0) { KFREES(array, arraysize); IPFERROR(121); return EINVAL; } *arrayptr = array; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_matcharray_verify */ /* Returns: Nil */ /* Parameters: array(I) - pointer to matching array */ /* arraysize(I) - number of elements in the array */ /* */ /* Verify the contents of a matching array by stepping through each element */ /* in it. The actual commands in the array are not verified for */ /* correctness, only that all of the sizes are correctly within limits. */ /* ------------------------------------------------------------------------ */ int ipf_matcharray_verify(array, arraysize) int *array, arraysize; { int i, nelem, maxidx; ipfexp_t *e; nelem = arraysize / sizeof(*array); /* * Currently, it makes no sense to have an array less than 6 * elements long - the initial size at the from, a single operation * (minimum 4 in length) and a trailer, for a total of 6. */ if ((array[0] < 6) || (arraysize < 24) || (arraysize > 4096)) { return -1; } /* * Verify the size of data pointed to by array with how long * the array claims to be itself. */ if (array[0] * sizeof(*array) != arraysize) { return -1; } maxidx = nelem - 1; /* * The last opcode in this array should be an IPF_EXP_END. */ if (array[maxidx] != IPF_EXP_END) { return -1; } for (i = 1; i < maxidx; ) { e = (ipfexp_t *)(array + i); /* * The length of the bits to check must be at least 1 * (or else there is nothing to comapre with!) and it * cannot exceed the length of the data present. */ if ((e->ipfe_size < 1 ) || (e->ipfe_size + i > maxidx)) { return -1; } i += e->ipfe_size; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_fr_matcharray */ /* Returns: int - 0 = match failed, else positive match */ /* Parameters: fin(I) - pointer to packet information */ /* array(I) - pointer to matching array */ /* */ /* This function is used to apply a matching array against a packet and */ /* return an indication of whether or not the packet successfully matches */ /* all of the commands in it. */ /* ------------------------------------------------------------------------ */ static int ipf_fr_matcharray(fin, array) fr_info_t *fin; 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; /* * 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 != fin->fin_p)) break; switch (e->ipfe_cmd) { case IPF_EXP_IP_PR : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_p == e->ipfe_arg0[i]); } break; case IPF_EXP_IP_SRCADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_DSTADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_ADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((fin->fin_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_src6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_DSTADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_dst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_ADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_src6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&fin->fin_dst6, &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 |= (fin->fin_sport == e->ipfe_arg0[i]) || (fin->fin_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 |= (fin->fin_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 |= (fin->fin_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_TCP_FLAGS : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_tcpf & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; } rv ^= e->ipfe_not; if (rv == 0) break; } return rv; } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueflush */ /* Returns: int - number of entries flushed (0 = none) */ /* Parameters: softc(I) - pointer to soft context main structure */ /* deletefn(I) - function to call to delete entry */ /* ipfqs(I) - top of the list of ipf internal queues */ /* userqs(I) - top of the list of user defined timeouts */ /* */ /* This fucntion gets called when the state/NAT hash tables fill up and we */ /* need to try a bit harder to free up some space. The algorithm used here */ /* split into two parts but both halves have the same goal: to reduce the */ /* number of connections considered to be "active" to the low watermark. */ /* There are two steps in doing this: */ /* 1) Remove any TCP connections that are already considered to be "closed" */ /* but have not yet been removed from the state table. The two states */ /* TCPS_TIME_WAIT and TCPS_CLOSED are considered to be the perfect */ /* candidates for this style of removal. If freeing up entries in */ /* CLOSED or both CLOSED and TIME_WAIT brings us to the low watermark, */ /* we do not go on to step 2. */ /* */ /* 2) Look for the oldest entries on each timeout queue and free them if */ /* they are within the given window we are considering. Where the */ /* window starts and the steps taken to increase its size depend upon */ /* how long ipf has been running (ipf_ticks.) Anything modified in the */ /* last 30 seconds is not touched. */ /* touched */ /* die ipf_ticks 30*1.5 1800*1.5 | 43200*1.5 */ /* | | | | | | */ /* future <--+----------+--------+-----------+-----+-----+-----------> past */ /* now \_int=30s_/ \_int=1hr_/ \_int=12hr */ /* */ /* Points to note: */ /* - tqe_die is the time, in the future, when entries die. */ /* - tqe_die - ipf_ticks is how long left the connection has to live in ipf */ /* ticks. */ /* - tqe_touched is when the entry was last used by NAT/state */ /* - the closer tqe_touched is to ipf_ticks, the further tqe_die will be */ /* ipf_ticks any given timeout queue and vice versa. */ /* - both tqe_die and tqe_touched increase over time */ /* - timeout queues are sorted with the highest value of tqe_die at the */ /* bottom and therefore the smallest values of each are at the top */ /* - the pointer passed in as ipfqs should point to an array of timeout */ /* queues representing each of the TCP states */ /* */ /* We start by setting up a maximum range to scan for things to move of */ /* iend (newest) to istart (oldest) in chunks of "interval". If nothing is */ /* found in that range, "interval" is adjusted (so long as it isn't 30) and */ /* we start again with a new value for "iend" and "istart". This is */ /* continued until we either finish the scan of 30 second intervals or the */ /* low water mark is reached. */ /* ------------------------------------------------------------------------ */ int ipf_queueflush(softc, deletefn, ipfqs, userqs, activep, size, low) ipf_main_softc_t *softc; ipftq_delete_fn_t deletefn; ipftq_t *ipfqs, *userqs; u_int *activep; int size, low; { u_long interval, istart, iend; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; int removed = 0; for (tqn = ipfqs[IPF_TCPS_CLOSED].ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } if ((*activep * 100 / size) > low) { for (tqn = ipfqs[IPF_TCPS_TIME_WAIT].ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } if ((*activep * 100 / size) <= low) { return removed; } /* * NOTE: Use of "* 15 / 10" is required here because if "* 1.5" is * used then the operations are upgraded to floating point * and kernels don't like floating point... */ if (softc->ipf_ticks > IPF_TTLVAL(43200 * 15 / 10)) { istart = IPF_TTLVAL(86400 * 4); interval = IPF_TTLVAL(43200); } else if (softc->ipf_ticks > IPF_TTLVAL(1800 * 15 / 10)) { istart = IPF_TTLVAL(43200); interval = IPF_TTLVAL(1800); } else if (softc->ipf_ticks > IPF_TTLVAL(30 * 15 / 10)) { istart = IPF_TTLVAL(1800); interval = IPF_TTLVAL(30); } else { return 0; } if (istart > softc->ipf_ticks) { if (softc->ipf_ticks - interval < interval) istart = interval; else istart = (softc->ipf_ticks / interval) * interval; } iend = softc->ipf_ticks - interval; while ((*activep * 100 / size) > low) { u_long try; try = softc->ipf_ticks - istart; for (ifq = ipfqs; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } for (ifq = userqs; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } if (try >= iend) { if (interval == IPF_TTLVAL(43200)) { interval = IPF_TTLVAL(1800); } else if (interval == IPF_TTLVAL(1800)) { interval = IPF_TTLVAL(30); } else { break; } if (interval >= softc->ipf_ticks) break; iend = softc->ipf_ticks - interval; } istart -= interval; } return removed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_deliverlocal */ /* Returns: int - 1 = local address, 0 = non-local address */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ipversion(I) - IP protocol version (4 or 6) */ /* ifp(I) - network interface pointer */ /* ipaddr(I) - IPv4/6 destination address */ /* */ /* This fucntion is used to determine in the address "ipaddr" belongs to */ /* the network interface represented by ifp. */ /* ------------------------------------------------------------------------ */ int ipf_deliverlocal(softc, ipversion, ifp, ipaddr) ipf_main_softc_t *softc; int ipversion; void *ifp; i6addr_t *ipaddr; { i6addr_t addr; int islocal = 0; if (ipversion == 4) { if (ipf_ifpaddr(softc, 4, FRI_NORMAL, ifp, &addr, NULL) == 0) { if (addr.in4.s_addr == ipaddr->in4.s_addr) islocal = 1; } #ifdef USE_INET6 } else if (ipversion == 6) { if (ipf_ifpaddr(softc, 6, FRI_NORMAL, ifp, &addr, NULL) == 0) { if (IP6_EQ(&addr, ipaddr)) islocal = 1; } #endif } return islocal; } /* ------------------------------------------------------------------------ */ /* Function: ipf_settimeout */ /* Returns: int - 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tuneable array entry */ /* p(I) - pointer to values passed in to apply */ /* */ /* This function is called to set the timeout values for each distinct */ /* queue timeout that is available. When called, it calls into both the */ /* state and NAT code, telling them to update their timeout queues. */ /* ------------------------------------------------------------------------ */ static int ipf_settimeout(softc, t, p) struct ipf_main_softc_s *softc; ipftuneable_t *t; ipftuneval_t *p; { /* * ipf_interror should be set by the functions called here, not * by this function - it's just a middle man. */ if (ipf_state_settimeout(softc, t, p) == -1) return -1; if (ipf_nat_settimeout(softc, t, p) == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_apply_timeout */ /* Returns: int - 0 = success, -1 = failure */ /* Parameters: head(I) - pointer to tuneable array entry */ /* seconds(I) - pointer to values passed in to apply */ /* */ /* This function applies a timeout of "seconds" to the timeout queue that */ /* is pointed to by "head". All entries on this list have an expiration */ /* set to be the current tick value of ipf plus the ttl. Given that this */ /* function should only be called when the delta is non-zero, the task is */ /* to walk the entire list and apply the change. The sort order will not */ /* change. The only catch is that this is O(n) across the list, so if the */ /* queue has lots of entries (10s of thousands or 100s of thousands), it */ /* could take a relatively long time to work through them all. */ /* ------------------------------------------------------------------------ */ void ipf_apply_timeout(head, seconds) ipftq_t *head; u_int seconds; { u_int oldtimeout, newtimeout; ipftqent_t *tqe; int delta; MUTEX_ENTER(&head->ifq_lock); oldtimeout = head->ifq_ttl; newtimeout = IPF_TTLVAL(seconds); delta = oldtimeout - newtimeout; head->ifq_ttl = newtimeout; for (tqe = head->ifq_head; tqe != NULL; tqe = tqe->tqe_next) { tqe->tqe_die += delta; } MUTEX_EXIT(&head->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_settimeout_tcp */ /* Returns: int - 0 = successfully applied, -1 = failed */ /* Parameters: t(I) - pointer to tuneable to change */ /* p(I) - pointer to new timeout information */ /* tab(I) - pointer to table of TCP queues */ /* */ /* This function applies the new timeout (p) to the TCP tunable (t) and */ /* updates all of the entries on the relevant timeout queue by calling */ /* ipf_apply_timeout(). */ /* ------------------------------------------------------------------------ */ int ipf_settimeout_tcp(t, p, tab) ipftuneable_t *t; ipftuneval_t *p; ipftq_t *tab; { if (!strcmp(t->ipft_name, "tcp_idle_timeout") || !strcmp(t->ipft_name, "tcp_established")) { ipf_apply_timeout(&tab[IPF_TCPS_ESTABLISHED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_close_wait")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSE_WAIT], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_last_ack")) { ipf_apply_timeout(&tab[IPF_TCPS_LAST_ACK], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_timeout")) { ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_listen")) { ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_half_established")) { ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_closing")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_syn_received")) { ipf_apply_timeout(&tab[IPF_TCPS_SYN_RECEIVED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_syn_sent")) { ipf_apply_timeout(&tab[IPF_TCPS_SYN_SENT], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_closed")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_half_closed")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_time_wait")) { ipf_apply_timeout(&tab[IPF_TCPS_TIME_WAIT], p->ipftu_int); } else { /* * ipf_interror isn't set here because it should be set * by whatever called this function. */ return -1; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_create */ /* Returns: NULL = failure, else success */ /* Parameters: arg(I) - pointer to soft context structure if already allocd */ /* */ /* Create the foundation soft context structure. In circumstances where it */ /* is not required to dynamically allocate the context, a pointer can be */ /* passed in (rather than NULL) to a structure to be initialised. */ /* The main thing of interest is that a number of locks are initialised */ /* here instead of in the where might be expected - in the relevant create */ /* function elsewhere. This is done because the current locking design has */ /* some areas where these locks are used outside of their module. */ /* Possibly the most important exercise that is done here is setting of all */ /* the timeout values, allowing them to be changed before init(). */ /* ------------------------------------------------------------------------ */ void * ipf_main_soft_create(arg) void *arg; { ipf_main_softc_t *softc; if (arg == NULL) { KMALLOC(softc, ipf_main_softc_t *); if (softc == NULL) return NULL; } else { softc = arg; } bzero((char *)softc, sizeof(*softc)); /* * This serves as a flag as to whether or not the softc should be * free'd when _destroy is called. */ softc->ipf_dynamic_softc = (arg == NULL) ? 1 : 0; softc->ipf_tuners = ipf_tune_array_copy(softc, sizeof(ipf_main_tuneables), ipf_main_tuneables); if (softc->ipf_tuners == NULL) { ipf_main_soft_destroy(softc); return NULL; } MUTEX_INIT(&softc->ipf_rw, "ipf rw mutex"); MUTEX_INIT(&softc->ipf_timeoutlock, "ipf timeout lock"); RWLOCK_INIT(&softc->ipf_global, "ipf filter load/unload mutex"); RWLOCK_INIT(&softc->ipf_mutex, "ipf filter rwlock"); RWLOCK_INIT(&softc->ipf_tokens, "ipf token rwlock"); RWLOCK_INIT(&softc->ipf_state, "ipf state rwlock"); RWLOCK_INIT(&softc->ipf_nat, "ipf IP NAT rwlock"); RWLOCK_INIT(&softc->ipf_poolrw, "ipf pool rwlock"); RWLOCK_INIT(&softc->ipf_frag, "ipf frag rwlock"); softc->ipf_token_head = NULL; softc->ipf_token_tail = &softc->ipf_token_head; softc->ipf_tcpidletimeout = FIVE_DAYS; softc->ipf_tcpclosewait = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcplastack = IPF_TTLVAL(30); softc->ipf_tcptimewait = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcptimeout = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpsynsent = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpsynrecv = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpclosed = IPF_TTLVAL(30); softc->ipf_tcphalfclosed = IPF_TTLVAL(2 * 3600); softc->ipf_udptimeout = IPF_TTLVAL(120); softc->ipf_udpacktimeout = IPF_TTLVAL(12); softc->ipf_icmptimeout = IPF_TTLVAL(60); softc->ipf_icmpacktimeout = IPF_TTLVAL(6); softc->ipf_iptimeout = IPF_TTLVAL(60); #if defined(IPFILTER_DEFAULT_BLOCK) softc->ipf_pass = FR_BLOCK|FR_NOMATCH; #else softc->ipf_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH; #endif softc->ipf_minttl = 4; softc->ipf_icmpminfragmtu = 68; softc->ipf_flags = IPF_LOGGING; return softc; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_init */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ int ipf_main_soft_init(softc) ipf_main_softc_t *softc; { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_destroy */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Undo everything that we did in ipf_main_soft_create. */ /* */ /* The most important check that needs to be made here is whether or not */ /* the structure was allocated by ipf_main_soft_create() by checking what */ /* value is stored in ipf_dynamic_main. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ void ipf_main_soft_destroy(softc) ipf_main_softc_t *softc; { RW_DESTROY(&softc->ipf_frag); RW_DESTROY(&softc->ipf_poolrw); RW_DESTROY(&softc->ipf_nat); RW_DESTROY(&softc->ipf_state); RW_DESTROY(&softc->ipf_tokens); RW_DESTROY(&softc->ipf_mutex); RW_DESTROY(&softc->ipf_global); MUTEX_DESTROY(&softc->ipf_timeoutlock); MUTEX_DESTROY(&softc->ipf_rw); if (softc->ipf_tuners != NULL) { KFREES(softc->ipf_tuners, sizeof(ipf_main_tuneables)); } if (softc->ipf_dynamic_softc == 1) { KFREE(softc); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_fini */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Clean out the rules which have been added since _init was last called, */ /* the only dynamic part of the mainline. */ /* ------------------------------------------------------------------------ */ int ipf_main_soft_fini(softc) ipf_main_softc_t *softc; { (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE|FR_INACTIVE); (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE); (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE|FR_INACTIVE); (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_load */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Handle global initialisation that needs to be done for the base part of */ /* IPFilter. At present this just amounts to initialising some ICMP lookup */ /* arrays that get used by the state/NAT code. */ /* ------------------------------------------------------------------------ */ int ipf_main_load() { int i; /* fill icmp reply type table */ for (i = 0; i <= ICMP_MAXTYPE; i++) icmpreplytype4[i] = -1; icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; #ifdef USE_INET6 /* fill icmp reply type table */ for (i = 0; i <= ICMP6_MAXTYPE; i++) icmpreplytype6[i] = -1; icmpreplytype6[ICMP6_ECHO_REQUEST] = ICMP6_ECHO_REPLY; icmpreplytype6[ICMP6_MEMBERSHIP_QUERY] = ICMP6_MEMBERSHIP_REPORT; icmpreplytype6[ICMP6_NI_QUERY] = ICMP6_NI_REPLY; icmpreplytype6[ND_ROUTER_SOLICIT] = ND_ROUTER_ADVERT; icmpreplytype6[ND_NEIGHBOR_SOLICIT] = ND_NEIGHBOR_ADVERT; #endif return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_unload */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_main_unload() { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_load_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Work through all of the subsystems inside IPFilter and call the load */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int ipf_load_all() { if (ipf_main_load() == -1) return -1; if (ipf_state_main_load() == -1) return -1; if (ipf_nat_main_load() == -1) return -1; if (ipf_frag_main_load() == -1) return -1; if (ipf_auth_main_load() == -1) return -1; if (ipf_proxy_main_load() == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_unload_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Work through all of the subsystems inside IPFilter and call the unload */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int ipf_unload_all() { if (ipf_proxy_main_unload() == -1) return -1; if (ipf_auth_main_unload() == -1) return -1; if (ipf_frag_main_unload() == -1) return -1; if (ipf_nat_main_unload() == -1) return -1; if (ipf_state_main_unload() == -1) return -1; if (ipf_main_unload() == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_create_all */ /* Returns: NULL = failure, else success */ /* Parameters: arg(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the create */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ ipf_main_softc_t * ipf_create_all(arg) void *arg; { ipf_main_softc_t *softc; softc = ipf_main_soft_create(arg); if (softc == NULL) return NULL; #ifdef IPFILTER_LOG softc->ipf_log_soft = ipf_log_soft_create(softc); if (softc->ipf_log_soft == NULL) { ipf_destroy_all(softc); return NULL; } #endif softc->ipf_lookup_soft = ipf_lookup_soft_create(softc); if (softc->ipf_lookup_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_sync_soft = ipf_sync_soft_create(softc); if (softc->ipf_sync_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_state_soft = ipf_state_soft_create(softc); if (softc->ipf_state_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_nat_soft = ipf_nat_soft_create(softc); if (softc->ipf_nat_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_frag_soft = ipf_frag_soft_create(softc); if (softc->ipf_frag_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_auth_soft = ipf_auth_soft_create(softc); if (softc->ipf_auth_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_proxy_soft = ipf_proxy_soft_create(softc); if (softc->ipf_proxy_soft == NULL) { ipf_destroy_all(softc); return NULL; } return softc; } /* ------------------------------------------------------------------------ */ /* Function: ipf_destroy_all */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the destroy */ /* function for each in an order that won't lead to a crash :) */ /* */ /* Every one of these functions is expected to succeed, so there is no */ /* checking of return values. */ /* ------------------------------------------------------------------------ */ void ipf_destroy_all(softc) ipf_main_softc_t *softc; { if (softc->ipf_state_soft != NULL) { ipf_state_soft_destroy(softc, softc->ipf_state_soft); softc->ipf_state_soft = NULL; } if (softc->ipf_nat_soft != NULL) { ipf_nat_soft_destroy(softc, softc->ipf_nat_soft); softc->ipf_nat_soft = NULL; } if (softc->ipf_frag_soft != NULL) { ipf_frag_soft_destroy(softc, softc->ipf_frag_soft); softc->ipf_frag_soft = NULL; } if (softc->ipf_auth_soft != NULL) { ipf_auth_soft_destroy(softc, softc->ipf_auth_soft); softc->ipf_auth_soft = NULL; } if (softc->ipf_proxy_soft != NULL) { ipf_proxy_soft_destroy(softc, softc->ipf_proxy_soft); softc->ipf_proxy_soft = NULL; } if (softc->ipf_sync_soft != NULL) { ipf_sync_soft_destroy(softc, softc->ipf_sync_soft); softc->ipf_sync_soft = NULL; } if (softc->ipf_lookup_soft != NULL) { ipf_lookup_soft_destroy(softc, softc->ipf_lookup_soft); softc->ipf_lookup_soft = NULL; } #ifdef IPFILTER_LOG if (softc->ipf_log_soft != NULL) { ipf_log_soft_destroy(softc, softc->ipf_log_soft); softc->ipf_log_soft = NULL; } #endif ipf_main_soft_destroy(softc); } /* ------------------------------------------------------------------------ */ /* Function: ipf_init_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the init */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int ipf_init_all(softc) ipf_main_softc_t *softc; { if (ipf_main_soft_init(softc) == -1) return -1; #ifdef IPFILTER_LOG if (ipf_log_soft_init(softc, softc->ipf_log_soft) == -1) return -1; #endif if (ipf_lookup_soft_init(softc, softc->ipf_lookup_soft) == -1) return -1; if (ipf_sync_soft_init(softc, softc->ipf_sync_soft) == -1) return -1; if (ipf_state_soft_init(softc, softc->ipf_state_soft) == -1) return -1; if (ipf_nat_soft_init(softc, softc->ipf_nat_soft) == -1) return -1; if (ipf_frag_soft_init(softc, softc->ipf_frag_soft) == -1) return -1; if (ipf_auth_soft_init(softc, softc->ipf_auth_soft) == -1) return -1; if (ipf_proxy_soft_init(softc, softc->ipf_proxy_soft) == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_fini_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the fini */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int ipf_fini_all(softc) ipf_main_softc_t *softc; { ipf_token_flush(softc); if (ipf_proxy_soft_fini(softc, softc->ipf_proxy_soft) == -1) return -1; if (ipf_auth_soft_fini(softc, softc->ipf_auth_soft) == -1) return -1; if (ipf_frag_soft_fini(softc, softc->ipf_frag_soft) == -1) return -1; if (ipf_nat_soft_fini(softc, softc->ipf_nat_soft) == -1) return -1; if (ipf_state_soft_fini(softc, softc->ipf_state_soft) == -1) return -1; if (ipf_sync_soft_fini(softc, softc->ipf_sync_soft) == -1) return -1; if (ipf_lookup_soft_fini(softc, softc->ipf_lookup_soft) == -1) return -1; #ifdef IPFILTER_LOG if (ipf_log_soft_fini(softc, softc->ipf_log_soft) == -1) return -1; #endif if (ipf_main_soft_fini(softc) == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* At present this function exists just to support temporary addition of */ /* firewall rules. Both inactive and active lists are scanned for items to */ /* purge, as by rights, the expiration is computed as soon as the rule is */ /* loaded in. */ /* ------------------------------------------------------------------------ */ void ipf_rule_expire(softc) ipf_main_softc_t *softc; { frentry_t *fr; if ((softc->ipf_rule_explist[0] == NULL) && (softc->ipf_rule_explist[1] == NULL)) return; WRITE_ENTER(&softc->ipf_mutex); while ((fr = softc->ipf_rule_explist[0]) != NULL) { /* * Because the list is kept sorted on insertion, the fist * one that dies in the future means no more work to do. */ if (fr->fr_die > softc->ipf_ticks) break; ipf_rule_delete(softc, fr, IPL_LOGIPF, 0); } while ((fr = softc->ipf_rule_explist[1]) != NULL) { /* * Because the list is kept sorted on insertion, the fist * one that dies in the future means no more work to do. */ if (fr->fr_die > softc->ipf_ticks) break; ipf_rule_delete(softc, fr, IPL_LOGIPF, 1); } RWLOCK_EXIT(&softc->ipf_mutex); } -static int ipf_ht_node_cmp __P((struct host_node_s *, struct host_node_s *)); -static void ipf_ht_node_make_key __P((host_track_t *, host_node_t *, int, - i6addr_t *)); +static int ipf_ht_node_cmp(struct host_node_s *, struct host_node_s *); +static void ipf_ht_node_make_key(host_track_t *, host_node_t *, int, + i6addr_t *); host_node_t RBI_ZERO(ipf_rb); RBI_CODE(ipf_rb, host_node_t, hn_entry, ipf_ht_node_cmp) /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_cmp */ /* Returns: int - 0 == nodes are the same, .. */ /* Parameters: k1(I) - pointer to first key to compare */ /* k2(I) - pointer to second key to compare */ /* */ /* The "key" for the node is a combination of two fields: the address */ /* family and the address itself. */ /* */ /* Because we're not actually interpreting the address data, it isn't */ /* necessary to convert them to/from network/host byte order. The mask is */ /* just used to remove bits that aren't significant - it doesn't matter */ /* where they are, as long as they're always in the same place. */ /* */ /* As with IP6_EQ, comparing IPv6 addresses starts at the bottom because */ /* this is where individual ones will differ the most - but not true for */ /* for /48's, etc. */ /* ------------------------------------------------------------------------ */ static int ipf_ht_node_cmp(k1, k2) struct host_node_s *k1, *k2; { int i; i = (k2->hn_addr.adf_family - k1->hn_addr.adf_family); if (i != 0) return i; if (k1->hn_addr.adf_family == AF_INET) return (k2->hn_addr.adf_addr.in4.s_addr - k1->hn_addr.adf_addr.in4.s_addr); i = k2->hn_addr.adf_addr.i6[3] - k1->hn_addr.adf_addr.i6[3]; if (i != 0) return i; i = k2->hn_addr.adf_addr.i6[2] - k1->hn_addr.adf_addr.i6[2]; if (i != 0) return i; i = k2->hn_addr.adf_addr.i6[1] - k1->hn_addr.adf_addr.i6[1]; if (i != 0) return i; i = k2->hn_addr.adf_addr.i6[0] - k1->hn_addr.adf_addr.i6[0]; return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_make_key */ /* Returns: Nil */ /* parameters: htp(I) - pointer to address tracking structure */ /* key(I) - where to store masked address for lookup */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* Using the "netmask" (number of bits) stored parent host tracking struct, */ /* copy the address passed in into the key structure whilst masking out the */ /* bits that we don't want. */ /* */ /* Because the parser will set ht_netmask to 128 if there is no protocol */ /* specified (the parser doesn't know if it should be a v4 or v6 rule), we */ /* have to be wary of that and not allow 32-128 to happen. */ /* ------------------------------------------------------------------------ */ static void ipf_ht_node_make_key(htp, key, family, addr) host_track_t *htp; host_node_t *key; int family; i6addr_t *addr; { key->hn_addr.adf_family = family; if (family == AF_INET) { u_32_t mask; int bits; key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in4); bits = htp->ht_netmask; if (bits >= 32) { mask = 0xffffffff; } else { mask = htonl(0xffffffff << (32 - bits)); } key->hn_addr.adf_addr.in4.s_addr = addr->in4.s_addr & mask; #ifdef USE_INET6 } else { int bits = htp->ht_netmask; key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in6); if (bits > 96) { key->hn_addr.adf_addr.i6[3] = addr->i6[3] & htonl(0xffffffff << (128 - bits)); key->hn_addr.adf_addr.i6[2] = addr->i6[2]; key->hn_addr.adf_addr.i6[1] = addr->i6[2]; key->hn_addr.adf_addr.i6[0] = addr->i6[2]; } else if (bits > 64) { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = addr->i6[2] & htonl(0xffffffff << (96 - bits)); key->hn_addr.adf_addr.i6[1] = addr->i6[1]; key->hn_addr.adf_addr.i6[0] = addr->i6[0]; } else if (bits > 32) { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = 0; key->hn_addr.adf_addr.i6[1] = addr->i6[1] & htonl(0xffffffff << (64 - bits)); key->hn_addr.adf_addr.i6[0] = addr->i6[0]; } else { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = 0; key->hn_addr.adf_addr.i6[1] = 0; key->hn_addr.adf_addr.i6[0] = addr->i6[0] & htonl(0xffffffff << (32 - bits)); } #endif } } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_add */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* htp(I) - pointer to address tracking structure */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ /* ipf_ht_node_del FROM RUNNING CONCURRENTLY ON THE SAME htp. */ /* */ /* After preparing the key with the address information to find, look in */ /* the red-black tree to see if the address is known. A successful call to */ /* this function can mean one of two things: a new node was added to the */ /* tree or a matching node exists and we're able to bump up its activity. */ /* ------------------------------------------------------------------------ */ int ipf_ht_node_add(softc, htp, family, addr) ipf_main_softc_t *softc; host_track_t *htp; int family; i6addr_t *addr; { host_node_t *h; host_node_t k; ipf_ht_node_make_key(htp, &k, family, addr); h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); if (h == NULL) { if (htp->ht_cur_nodes >= htp->ht_max_nodes) return -1; KMALLOC(h, host_node_t *); if (h == NULL) { DT(ipf_rb_no_mem); LBUMP(ipf_rb_no_mem); return -1; } /* * If there was a macro to initialise the RB node then that * would get used here, but there isn't... */ bzero((char *)h, sizeof(*h)); h->hn_addr = k.hn_addr; h->hn_addr.adf_family = k.hn_addr.adf_family; RBI_INSERT(ipf_rb, &htp->ht_root, h); htp->ht_cur_nodes++; } else { if ((htp->ht_max_per_node != 0) && (h->hn_active >= htp->ht_max_per_node)) { DT(ipf_rb_node_max); LBUMP(ipf_rb_node_max); return -1; } } h->hn_active++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_del */ /* Returns: int - 0 == success, -1 == failure */ /* parameters: htp(I) - pointer to address tracking structure */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ /* ipf_ht_node_add FROM RUNNING CONCURRENTLY ON THE SAME htp. */ /* */ /* Try and find the address passed in amongst the leavese on this tree to */ /* be friend. If found then drop the active account for that node drops by */ /* one. If that count reaches 0, it is time to free it all up. */ /* ------------------------------------------------------------------------ */ int ipf_ht_node_del(htp, family, addr) host_track_t *htp; int family; i6addr_t *addr; { host_node_t *h; host_node_t k; ipf_ht_node_make_key(htp, &k, family, addr); h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); if (h == NULL) { return -1; } else { h->hn_active--; if (h->hn_active == 0) { (void) RBI_DELETE(ipf_rb, &htp->ht_root, h); htp->ht_cur_nodes--; KFREE(h); } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_init */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* */ /* Initialise the host tracking structure to be ready for use above. */ /* ------------------------------------------------------------------------ */ void ipf_rb_ht_init(head) host_track_t *head; { RBI_INIT(ipf_rb, &head->ht_root); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_freenode */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* arg(I) - additional argument from walk caller */ /* */ /* Free an actual host_node_t structure. */ /* ------------------------------------------------------------------------ */ void ipf_rb_ht_freenode(node, arg) host_node_t *node; void *arg; { KFREE(node); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_flush */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* */ /* Remove all of the nodes in the tree tracking hosts by calling a walker */ /* and free'ing each one. */ /* ------------------------------------------------------------------------ */ void ipf_rb_ht_flush(head) host_track_t *head; { RBI_WALK(ipf_rb, &head->ht_root, ipf_rb_ht_freenode, NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_slowtimer */ /* Returns: Nil */ /* Parameters: ptr(I) - pointer to main ipf soft context structure */ /* */ /* Slowly expire held state for fragments. Timeouts are set * in */ /* expectation of this being called twice per second. */ /* ------------------------------------------------------------------------ */ void ipf_slowtimer(softc) ipf_main_softc_t *softc; { ipf_token_expire(softc); ipf_frag_expire(softc); ipf_state_expire(softc); ipf_nat_expire(softc); ipf_auth_expire(softc); ipf_lookup_expire(softc); ipf_rule_expire(softc); ipf_sync_expire(softc); softc->ipf_ticks++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet_mask_add */ /* Returns: Nil */ /* Parameters: bits(I) - pointer to nat context information */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* When called, bits represents the mask of a new NAT rule that has just */ /* been added. This function inserts a bitmask into the array of masks to */ /* search when searching for a matching NAT rule for a packet. */ /* Prevention of duplicate masks is achieved by checking the use count for */ /* a given netmask. */ /* ------------------------------------------------------------------------ */ void ipf_inet_mask_add(bits, mtab) int bits; ipf_v4_masktab_t *mtab; { u_32_t mask; int i, j; mtab->imt4_masks[bits]++; if (mtab->imt4_masks[bits] > 1) return; if (bits == 0) mask = 0; else mask = 0xffffffff << (32 - bits); for (i = 0; i < 33; i++) { if (ntohl(mtab->imt4_active[i]) < mask) { for (j = 32; j > i; j--) mtab->imt4_active[j] = mtab->imt4_active[j - 1]; mtab->imt4_active[i] = htonl(mask); break; } } mtab->imt4_max++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet_mask_del */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in the netmask */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* Remove the 32bit bitmask represented by "bits" from the collection of */ /* netmasks stored inside of mtab. */ /* ------------------------------------------------------------------------ */ void ipf_inet_mask_del(bits, mtab) int bits; ipf_v4_masktab_t *mtab; { u_32_t mask; int i, j; mtab->imt4_masks[bits]--; if (mtab->imt4_masks[bits] > 0) return; mask = htonl(0xffffffff << (32 - bits)); for (i = 0; i < 33; i++) { if (mtab->imt4_active[i] == mask) { for (j = i + 1; j < 33; j++) mtab->imt4_active[j - 1] = mtab->imt4_active[j]; break; } } mtab->imt4_max--; ASSERT(mtab->imt4_max >= 0); } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_inet6_mask_add */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in mask */ /* mask(I) - pointer to mask to add */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* When called, bitcount represents the mask of a IPv6 NAT map rule that */ /* has just been added. This function inserts a bitmask into the array of */ /* masks to search when searching for a matching NAT rule for a packet. */ /* Prevention of duplicate masks is achieved by checking the use count for */ /* a given netmask. */ /* ------------------------------------------------------------------------ */ void ipf_inet6_mask_add(bits, mask, mtab) int bits; i6addr_t *mask; ipf_v6_masktab_t *mtab; { i6addr_t zero; int i, j; mtab->imt6_masks[bits]++; if (mtab->imt6_masks[bits] > 1) return; if (bits == 0) { mask = &zero; zero.i6[0] = 0; zero.i6[1] = 0; zero.i6[2] = 0; zero.i6[3] = 0; } for (i = 0; i < 129; i++) { if (IP6_LT(&mtab->imt6_active[i], mask)) { for (j = 128; j > i; j--) mtab->imt6_active[j] = mtab->imt6_active[j - 1]; mtab->imt6_active[i] = *mask; break; } } mtab->imt6_max++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet6_mask_del */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in mask */ /* mask(I) - pointer to mask to remove */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* Remove the 128bit bitmask represented by "bits" from the collection of */ /* netmasks stored inside of mtab. */ /* ------------------------------------------------------------------------ */ void ipf_inet6_mask_del(bits, mask, mtab) int bits; i6addr_t *mask; ipf_v6_masktab_t *mtab; { i6addr_t zero; int i, j; mtab->imt6_masks[bits]--; if (mtab->imt6_masks[bits] > 0) return; if (bits == 0) mask = &zero; zero.i6[0] = 0; zero.i6[1] = 0; zero.i6[2] = 0; zero.i6[3] = 0; for (i = 0; i < 129; i++) { if (IP6_EQ(&mtab->imt6_active[i], mask)) { for (j = i + 1; j < 129; j++) { mtab->imt6_active[j - 1] = mtab->imt6_active[j]; if (IP6_EQ(&mtab->imt6_active[j - 1], &zero)) break; } break; } } mtab->imt6_max--; ASSERT(mtab->imt6_max >= 0); } #endif diff --git a/sys/contrib/ipfilter/netinet/ip_auth.c b/sys/contrib/ipfilter/netinet/ip_auth.c index f08d0b2fd1d9..18b8f3a65641 100644 --- a/sys/contrib/ipfilter/netinet/ip_auth.c +++ b/sys/contrib/ipfilter/netinet/ip_auth.c @@ -1,1246 +1,1246 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # ifdef _STDC_C99 # include # endif # include # define _KERNEL # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD_version) # include # include #else # include #endif # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD_version) # include #endif #if defined(__NetBSD__) # include #endif #if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include #endif #if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 400000) && \ !defined(_KERNEL) # include #endif #include #ifdef sun # include #endif #include #include #include # include #if !defined(_KERNEL) # define KERNEL # define _KERNEL # define NOT_KERNEL #endif #ifdef NOT_KERNEL # undef _KERNEL # undef KERNEL #endif #include # if defined(__FreeBSD_version) # include # define IF_QFULL _IF_QFULL # define IF_DROP _IF_DROP # endif # include # include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_auth.h" #if !defined(MENTAT) # include # ifdef __FreeBSD__ # include # endif #endif #if defined(__FreeBSD_version) # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include # include # endif #endif /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.73.2.24 2007/09/09 11:32:04 darrenr Exp $"; */ #endif -static void ipf_auth_deref __P((frauthent_t **)); -static void ipf_auth_deref_unlocked __P((ipf_auth_softc_t *, frauthent_t **)); -static int ipf_auth_geniter __P((ipf_main_softc_t *, ipftoken_t *, - ipfgeniter_t *, ipfobj_t *)); -static int ipf_auth_reply __P((ipf_main_softc_t *, ipf_auth_softc_t *, char *)); -static int ipf_auth_wait __P((ipf_main_softc_t *, ipf_auth_softc_t *, char *)); -static int ipf_auth_flush __P((void *)); +static void ipf_auth_deref(frauthent_t **); +static void ipf_auth_deref_unlocked(ipf_auth_softc_t *, frauthent_t **); +static int ipf_auth_geniter(ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *, ipfobj_t *); +static int ipf_auth_reply(ipf_main_softc_t *, ipf_auth_softc_t *, char *); +static int ipf_auth_wait(ipf_main_softc_t *, ipf_auth_softc_t *, char *); +static int ipf_auth_flush(void *); /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_main_load */ /* Returns: int - 0 == success, else error */ /* Parameters: None */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_auth_main_load() { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_main_unload */ /* Returns: int - 0 == success, else error */ /* Parameters: None */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_auth_main_unload() { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_create */ /* Returns: int - NULL = failure, else success */ /* Parameters: softc(I) - pointer to soft context data */ /* */ /* Create a structre to store all of the run-time data for packet auth in */ /* and initialise some fields to their defaults. */ /* ------------------------------------------------------------------------ */ void * ipf_auth_soft_create(softc) ipf_main_softc_t *softc; { ipf_auth_softc_t *softa; KMALLOC(softa, ipf_auth_softc_t *); if (softa == NULL) return NULL; bzero((char *)softa, sizeof(*softa)); softa->ipf_auth_size = FR_NUMAUTH; softa->ipf_auth_defaultage = 600; RWLOCK_INIT(&softa->ipf_authlk, "ipf IP User-Auth rwlock"); MUTEX_INIT(&softa->ipf_auth_mx, "ipf auth log mutex"); #if SOLARIS && defined(_KERNEL) cv_init(&softa->ipf_auth_wait, "ipf auth condvar", CV_DRIVER, NULL); #endif return softa; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_init */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Allocate memory and initialise data structures used in handling auth */ /* rules. */ /* ------------------------------------------------------------------------ */ int ipf_auth_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_auth_softc_t *softa = arg; KMALLOCS(softa->ipf_auth, frauth_t *, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); if (softa->ipf_auth == NULL) return -1; bzero((char *)softa->ipf_auth, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); KMALLOCS(softa->ipf_auth_pkts, mb_t **, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); if (softa->ipf_auth_pkts == NULL) return -2; bzero((char *)softa->ipf_auth_pkts, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_fini */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Free all network buffer memory used to keep saved packets that have been */ /* connectedd to the soft soft context structure *but* do not free that: it */ /* is free'd by _destroy(). */ /* ------------------------------------------------------------------------ */ int ipf_auth_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_auth_softc_t *softa = arg; frauthent_t *fae, **faep; frentry_t *fr, **frp; mb_t *m; int i; if (softa->ipf_auth != NULL) { KFREES(softa->ipf_auth, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); softa->ipf_auth = NULL; } if (softa->ipf_auth_pkts != NULL) { for (i = 0; i < softa->ipf_auth_size; i++) { m = softa->ipf_auth_pkts[i]; if (m != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } } KFREES(softa->ipf_auth_pkts, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); softa->ipf_auth_pkts = NULL; } faep = &softa->ipf_auth_entries; while ((fae = *faep) != NULL) { *faep = fae->fae_next; KFREE(fae); } softa->ipf_auth_ip = NULL; if (softa->ipf_auth_rules != NULL) { for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) { if (fr->fr_ref == 1) { *frp = fr->fr_next; MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); } else frp = &fr->fr_next; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_destroy */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Undo what was done in _create() - i.e. free the soft context data. */ /* ------------------------------------------------------------------------ */ void ipf_auth_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_auth_softc_t *softa = arg; # if SOLARIS && defined(_KERNEL) cv_destroy(&softa->ipf_auth_wait); # endif MUTEX_DESTROY(&softa->ipf_auth_mx); RW_DESTROY(&softa->ipf_authlk); KFREE(softa); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_setlock */ /* Returns: void */ /* Paramters: arg(I) - pointer to soft context data */ /* tmp(I) - value to assign to auth lock */ /* */ /* ------------------------------------------------------------------------ */ void ipf_auth_setlock(arg, tmp) void *arg; int tmp; { ipf_auth_softc_t *softa = arg; softa->ipf_auth_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_check */ /* Returns: frentry_t* - pointer to ipf rule if match found, else NULL */ /* Parameters: fin(I) - pointer to ipftoken structure */ /* passp(I) - pointer to ipfgeniter structure */ /* */ /* Check if a packet has authorization. If the packet is found to match an */ /* authorization result and that would result in a feedback loop (i.e. it */ /* will end up returning FR_AUTH) then return FR_BLOCK instead. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_auth_check(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_auth_softc_t *softa = softc->ipf_auth_soft; frentry_t *fr; frauth_t *fra; u_32_t pass; u_short id; ip_t *ip; int i; if (softa->ipf_auth_lock || !softa->ipf_auth_used) return NULL; ip = fin->fin_ip; id = ip->ip_id; READ_ENTER(&softa->ipf_authlk); for (i = softa->ipf_auth_start; i != softa->ipf_auth_end; ) { /* * index becomes -2 only after an SIOCAUTHW. Check this in * case the same packet gets sent again and it hasn't yet been * auth'd. */ fra = softa->ipf_auth + i; if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) && !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) { /* * Avoid feedback loop. */ if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass))) { pass = FR_BLOCK; fin->fin_reason = FRB_AUTHFEEDBACK; } /* * Create a dummy rule for the stateful checking to * use and return. Zero out any values we don't * trust from userland! */ if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) && (fin->fin_flx & FI_FRAG))) { KMALLOC(fr, frentry_t *); if (fr) { bcopy((char *)fra->fra_info.fin_fr, (char *)fr, sizeof(*fr)); fr->fr_grp = NULL; fr->fr_ifa = fin->fin_ifp; fr->fr_func = NULL; fr->fr_ref = 1; fr->fr_flags = pass; fr->fr_ifas[1] = NULL; fr->fr_ifas[2] = NULL; fr->fr_ifas[3] = NULL; MUTEX_INIT(&fr->fr_lock, "ipf auth rule"); } } else fr = fra->fra_info.fin_fr; fin->fin_fr = fr; fin->fin_flx |= fra->fra_flx; RWLOCK_EXIT(&softa->ipf_authlk); WRITE_ENTER(&softa->ipf_authlk); /* * ipf_auth_rules is populated with the rules malloc'd * above and only those. */ if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) { fr->fr_next = softa->ipf_auth_rules; softa->ipf_auth_rules = fr; } softa->ipf_auth_stats.fas_hits++; fra->fra_index = -1; softa->ipf_auth_used--; softa->ipf_auth_replies--; if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; fra++; if (i == softa->ipf_auth_size) { i = 0; fra = softa->ipf_auth; } softa->ipf_auth_start = i; if (i == softa->ipf_auth_end) break; } if (softa->ipf_auth_start == softa->ipf_auth_end) { softa->ipf_auth_next = 0; softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; } } RWLOCK_EXIT(&softa->ipf_authlk); if (passp != NULL) *passp = pass; softa->ipf_auth_stats.fas_hits++; return fr; } i++; if (i == softa->ipf_auth_size) i = 0; } RWLOCK_EXIT(&softa->ipf_authlk); softa->ipf_auth_stats.fas_miss++; return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_new */ /* Returns: int - 1 == success, 0 = did not put packet on auth queue */ /* Parameters: m(I) - pointer to mb_t with packet in it */ /* fin(I) - pointer to packet information */ /* */ /* Check if we have room in the auth array to hold details for another */ /* packet. If we do, store it and wake up any user programs which are */ /* waiting to hear about these events. */ /* ------------------------------------------------------------------------ */ int ipf_auth_new(m, fin) mb_t *m; fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_auth_softc_t *softa = softc->ipf_auth_soft; #if defined(_KERNEL) && defined(MENTAT) qpktinfo_t *qpi = fin->fin_qpi; #endif frauth_t *fra; #if !defined(sparc) && !defined(m68k) ip_t *ip; #endif int i; if (softa->ipf_auth_lock) return 0; WRITE_ENTER(&softa->ipf_authlk); if (((softa->ipf_auth_end + 1) % softa->ipf_auth_size) == softa->ipf_auth_start) { softa->ipf_auth_stats.fas_nospace++; RWLOCK_EXIT(&softa->ipf_authlk); return 0; } softa->ipf_auth_stats.fas_added++; softa->ipf_auth_used++; i = softa->ipf_auth_end++; if (softa->ipf_auth_end == softa->ipf_auth_size) softa->ipf_auth_end = 0; fra = softa->ipf_auth + i; fra->fra_index = i; if (fin->fin_fr != NULL) fra->fra_pass = fin->fin_fr->fr_flags; else fra->fra_pass = 0; fra->fra_age = softa->ipf_auth_defaultage; bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin)); fra->fra_flx = fra->fra_info.fin_flx & (FI_STATE|FI_NATED); fra->fra_info.fin_flx &= ~(FI_STATE|FI_NATED); #if !defined(sparc) && !defined(m68k) /* * No need to copyback here as we want to undo the changes, not keep * them. */ ip = fin->fin_ip; # if defined(MENTAT) && defined(_KERNEL) if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4)) # endif { register u_short bo; bo = ip->ip_len; ip->ip_len = htons(bo); bo = ip->ip_off; ip->ip_off = htons(bo); } #endif #if SOLARIS && defined(_KERNEL) COPYIFNAME(fin->fin_v, fin->fin_ifp, fra->fra_info.fin_ifname); m->b_rptr -= qpi->qpi_off; fra->fra_q = qpi->qpi_q; /* The queue can disappear! */ fra->fra_m = *fin->fin_mp; fra->fra_info.fin_mp = &fra->fra_m; softa->ipf_auth_pkts[i] = *(mblk_t **)fin->fin_mp; RWLOCK_EXIT(&softa->ipf_authlk); cv_signal(&softa->ipf_auth_wait); pollwakeup(&softc->ipf_poll_head[IPL_LOGAUTH], POLLIN|POLLRDNORM); #else softa->ipf_auth_pkts[i] = m; RWLOCK_EXIT(&softa->ipf_authlk); WAKEUP(&softa->ipf_auth_next, 0); #endif return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_ioctl */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode flags associated with open descriptor */ /* uid(I) - uid associatd with application making the call */ /* ctx(I) - pointer for context */ /* */ /* This function handles all of the ioctls recognised by the auth component */ /* in IPFilter - ie ioctls called on an open fd for /dev/ipf_auth */ /* ------------------------------------------------------------------------ */ int ipf_auth_ioctl(softc, data, cmd, mode, uid, ctx) ipf_main_softc_t *softc; caddr_t data; ioctlcmd_t cmd; int mode, uid; void *ctx; { ipf_auth_softc_t *softa = softc->ipf_auth_soft; int error = 0, i; SPL_INT(s); switch (cmd) { case SIOCGENITER : { ipftoken_t *token; ipfgeniter_t iter; ipfobj_t obj; error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); token = ipf_token_find(softc, IPFGENITER_AUTH, uid, ctx); if (token != NULL) error = ipf_auth_geniter(softc, token, &iter, &obj); else { WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); IPFERROR(10001); error = ESRCH; } SPL_X(s); break; } case SIOCADAFR : case SIOCRMAFR : if (!(mode & FWRITE)) { IPFERROR(10002); error = EPERM; } else error = frrequest(softc, IPL_LOGAUTH, cmd, data, softc->ipf_active, 1); break; case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(10003); error = EPERM; } else { error = ipf_lock(data, &softa->ipf_auth_lock); } break; case SIOCATHST: softa->ipf_auth_stats.fas_faelist = softa->ipf_auth_entries; error = ipf_outobj(softc, data, &softa->ipf_auth_stats, IPFOBJ_AUTHSTAT); break; case SIOCIPFFL: SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); i = ipf_auth_flush(softa); RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); error = BCOPYOUT(&i, data, sizeof(i)); if (error != 0) { IPFERROR(10004); error = EFAULT; } break; case SIOCAUTHW: error = ipf_auth_wait(softc, softa, data); break; case SIOCAUTHR: error = ipf_auth_reply(softc, softa, data); break; default : IPFERROR(10005); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_expire */ /* Returns: None */ /* Parameters: None */ /* */ /* Slowly expire held auth records. Timeouts are set in expectation of */ /* this being called twice per second. */ /* ------------------------------------------------------------------------ */ void ipf_auth_expire(softc) ipf_main_softc_t *softc; { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, **faep; frentry_t *fr, **frp; frauth_t *fra; mb_t *m; int i; SPL_INT(s); if (softa->ipf_auth_lock) return; SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); for (i = 0, fra = softa->ipf_auth; i < softa->ipf_auth_size; i++, fra++) { fra->fra_age--; if ((fra->fra_age == 0) && (softa->ipf_auth[i].fra_index != -1)) { if ((m = softa->ipf_auth_pkts[i]) != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } else if (softa->ipf_auth[i].fra_index == -2) { softa->ipf_auth_replies--; } softa->ipf_auth[i].fra_index = -1; softa->ipf_auth_stats.fas_expire++; softa->ipf_auth_used--; } } /* * Expire pre-auth rules */ for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { fae->fae_age--; if (fae->fae_age == 0) { ipf_auth_deref(&fae); softa->ipf_auth_stats.fas_expire++; } else faep = &fae->fae_next; } if (softa->ipf_auth_entries != NULL) softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; else softa->ipf_auth_ip = NULL; for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) { if (fr->fr_ref == 1) { *frp = fr->fr_next; MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); } else frp = &fr->fr_next; } RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_precmd */ /* Returns: int - 0 == success, else error */ /* Parameters: cmd(I) - ioctl command for rule */ /* fr(I) - pointer to ipf rule */ /* fptr(I) - pointer to caller's 'fr' */ /* */ /* ------------------------------------------------------------------------ */ int ipf_auth_precmd(softc, cmd, fr, frptr) ipf_main_softc_t *softc; ioctlcmd_t cmd; frentry_t *fr, **frptr; { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, **faep; int error = 0; SPL_INT(s); if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) { IPFERROR(10006); return EIO; } for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { if (&fae->fae_fr == fr) break; else faep = &fae->fae_next; } if (cmd == (ioctlcmd_t)SIOCRMAFR) { if (fr == NULL || frptr == NULL) { IPFERROR(10007); error = EINVAL; } else if (fae == NULL) { IPFERROR(10008); error = ESRCH; } else { SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); *faep = fae->fae_next; if (softa->ipf_auth_ip == &fae->fae_fr) softa->ipf_auth_ip = softa->ipf_auth_entries ? &softa->ipf_auth_entries->fae_fr : NULL; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); KFREE(fae); } } else if (fr != NULL && frptr != NULL) { KMALLOC(fae, frauthent_t *); if (fae != NULL) { bcopy((char *)fr, (char *)&fae->fae_fr, sizeof(*fr)); SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); fae->fae_age = softa->ipf_auth_defaultage; fae->fae_fr.fr_hits = 0; fae->fae_fr.fr_next = *frptr; fae->fae_ref = 1; *frptr = &fae->fae_fr; fae->fae_next = *faep; *faep = fae; softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); } else { IPFERROR(10009); error = ENOMEM; } } else { IPFERROR(10010); error = EINVAL; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_flush */ /* Returns: int - number of auth entries flushed */ /* Parameters: None */ /* Locks: WRITE(ipf_authlk) */ /* */ /* This function flushs the ipf_auth_pkts array of any packet data with */ /* references still there. */ /* It is expected that the caller has already acquired the correct locks or */ /* set the priority level correctly for this to block out other code paths */ /* into these data structures. */ /* ------------------------------------------------------------------------ */ static int ipf_auth_flush(arg) void *arg; { ipf_auth_softc_t *softa = arg; int i, num_flushed; mb_t *m; if (softa->ipf_auth_lock) return -1; num_flushed = 0; for (i = 0 ; i < softa->ipf_auth_size; i++) { if (softa->ipf_auth[i].fra_index != -1) { m = softa->ipf_auth_pkts[i]; if (m != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } softa->ipf_auth[i].fra_index = -1; /* perhaps add & use a flush counter inst.*/ softa->ipf_auth_stats.fas_expire++; num_flushed++; } } softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; softa->ipf_auth_next = 0; softa->ipf_auth_used = 0; softa->ipf_auth_replies = 0; return num_flushed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_waiting */ /* Returns: int - number of packets in the auth queue */ /* Parameters: None */ /* */ /* Simple truth check to see if there are any packets waiting in the auth */ /* queue. */ /* ------------------------------------------------------------------------ */ int ipf_auth_waiting(softc) ipf_main_softc_t *softc; { ipf_auth_softc_t *softa = softc->ipf_auth_soft; return (softa->ipf_auth_used != 0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_geniter */ /* Returns: int - 0 == success, else error */ /* Parameters: token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter structure */ /* objp(I) - pointer to ipf object destription */ /* */ /* Iterate through the list of entries in the auth queue list. */ /* objp is used here to get the location of where to do the copy out to. */ /* Stomping over various fields with new information will not harm anything */ /* ------------------------------------------------------------------------ */ static int ipf_auth_geniter(softc, token, itp, objp) ipf_main_softc_t *softc; ipftoken_t *token; ipfgeniter_t *itp; ipfobj_t *objp; { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, *next, zero; int error; if (itp->igi_data == NULL) { IPFERROR(10011); return EFAULT; } if (itp->igi_type != IPFGENITER_AUTH) { IPFERROR(10012); return EINVAL; } objp->ipfo_type = IPFOBJ_FRAUTH; objp->ipfo_ptr = itp->igi_data; objp->ipfo_size = sizeof(frauth_t); READ_ENTER(&softa->ipf_authlk); fae = token->ipt_data; if (fae == NULL) { next = softa->ipf_auth_entries; } else { next = fae->fae_next; } /* * If we found an auth entry to use, bump its reference count * so that it can be used for is_next when we come back. */ if (next != NULL) { ATOMIC_INC(next->fae_ref); token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; } RWLOCK_EXIT(&softa->ipf_authlk); error = ipf_outobjk(softc, objp, next); if (fae != NULL) ipf_auth_deref_unlocked(softa, &fae); if (next->fae_next == NULL) ipf_token_mark_complete(token); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_deref_unlocked */ /* Returns: None */ /* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ /* */ /* Wrapper for ipf_auth_deref for when a write lock on ipf_authlk is not */ /* held. */ /* ------------------------------------------------------------------------ */ static void ipf_auth_deref_unlocked(softa, faep) ipf_auth_softc_t *softa; frauthent_t **faep; { WRITE_ENTER(&softa->ipf_authlk); ipf_auth_deref(faep); RWLOCK_EXIT(&softa->ipf_authlk); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_deref */ /* Returns: None */ /* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ /* Locks: WRITE(ipf_authlk) */ /* */ /* This function unconditionally sets the pointer in the caller to NULL, */ /* to make it clear that it should no longer use that pointer, and drops */ /* the reference count on the structure by 1. If it reaches 0, free it up. */ /* ------------------------------------------------------------------------ */ static void ipf_auth_deref(faep) frauthent_t **faep; { frauthent_t *fae; fae = *faep; *faep = NULL; fae->fae_ref--; if (fae->fae_ref == 0) { KFREE(fae); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_wait_pkt */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ /* This function is called when an application is waiting for a packet to */ /* match an "auth" rule by issuing an SIOCAUTHW ioctl. If there is already */ /* a packet waiting on the queue then we will return that _one_ immediately.*/ /* If there are no packets present in the queue (ipf_auth_pkts) then we go */ /* to sleep. */ /* ------------------------------------------------------------------------ */ static int ipf_auth_wait(softc, softa, data) ipf_main_softc_t *softc; ipf_auth_softc_t *softa; char *data; { frauth_t auth, *au = &auth; int error, len, i; mb_t *m; char *t; SPL_INT(s); ipf_auth_ioctlloop: error = ipf_inobj(softc, data, NULL, au, IPFOBJ_FRAUTH); if (error != 0) return error; /* * XXX Locks are held below over calls to copyout...a better * solution needs to be found so this isn't necessary. The situation * we are trying to guard against here is an error in the copyout * steps should not cause the packet to "disappear" from the queue. */ SPL_NET(s); READ_ENTER(&softa->ipf_authlk); /* * If ipf_auth_next is not equal to ipf_auth_end it will be because * there is a packet waiting to be delt with in the ipf_auth_pkts * array. We copy as much of that out to user space as requested. */ if (softa->ipf_auth_used > 0) { while (softa->ipf_auth_pkts[softa->ipf_auth_next] == NULL) { softa->ipf_auth_next++; if (softa->ipf_auth_next == softa->ipf_auth_size) softa->ipf_auth_next = 0; } error = ipf_outobj(softc, data, &softa->ipf_auth[softa->ipf_auth_next], IPFOBJ_FRAUTH); if (error != 0) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return error; } if (auth.fra_len != 0 && auth.fra_buf != NULL) { /* * Copy packet contents out to user space if * requested. Bail on an error. */ m = softa->ipf_auth_pkts[softa->ipf_auth_next]; len = MSGDSIZE(m); if (len > auth.fra_len) len = auth.fra_len; auth.fra_len = len; for (t = auth.fra_buf; m && (len > 0); ) { i = MIN(M_LEN(m), len); error = copyoutptr(softc, MTOD(m, char *), &t, i); len -= i; t += i; if (error != 0) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return error; } m = m->m_next; } } RWLOCK_EXIT(&softa->ipf_authlk); SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); softa->ipf_auth_next++; if (softa->ipf_auth_next == softa->ipf_auth_size) softa->ipf_auth_next = 0; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return 0; } RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); MUTEX_ENTER(&softa->ipf_auth_mx); #ifdef _KERNEL # if SOLARIS error = 0; if (!cv_wait_sig(&softa->ipf_auth_wait, &softa->ipf_auth_mx.ipf_lk)) { IPFERROR(10014); error = EINTR; } # else /* SOLARIS */ error = SLEEP(&softa->ipf_auth_next, "ipf_auth_next"); # endif /* SOLARIS */ #endif MUTEX_EXIT(&softa->ipf_auth_mx); if (error == 0) goto ipf_auth_ioctlloop; return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_reply */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ /* This function is called by an application when it wants to return a */ /* decision on a packet using the SIOCAUTHR ioctl. This is after it has */ /* received information using an SIOCAUTHW. The decision returned in the */ /* form of flags, the same as those used in each rule. */ /* ------------------------------------------------------------------------ */ static int ipf_auth_reply(softc, softa, data) ipf_main_softc_t *softc; ipf_auth_softc_t *softa; char *data; { frauth_t auth, *au = &auth, *fra; fr_info_t fin; int error, i; mb_t *m; SPL_INT(s); error = ipf_inobj(softc, data, NULL, &auth, IPFOBJ_FRAUTH); if (error != 0) return error; SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); i = au->fra_index; fra = softa->ipf_auth + i; error = 0; /* * Check the validity of the information being returned with two simple * checks. First, the auth index value should be within the size of * the array and second the packet id being returned should also match. */ if ((i < 0) || (i >= softa->ipf_auth_size)) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); IPFERROR(10015); return ESRCH; } if (fra->fra_info.fin_id != au->fra_info.fin_id) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); IPFERROR(10019); return ESRCH; } m = softa->ipf_auth_pkts[i]; fra->fra_index = -2; fra->fra_pass = au->fra_pass; softa->ipf_auth_pkts[i] = NULL; softa->ipf_auth_replies++; bcopy(&fra->fra_info, &fin, sizeof(fin)); RWLOCK_EXIT(&softa->ipf_authlk); /* * Re-insert the packet back into the packet stream flowing through * the kernel in a manner that will mean IPFilter sees the packet * again. This is not the same as is done with fastroute, * deliberately, as we want to resume the normal packet processing * path for it. */ #ifdef _KERNEL if ((m != NULL) && (au->fra_info.fin_out != 0)) { error = ipf_inject(&fin, m); if (error != 0) { IPFERROR(10016); error = ENOBUFS; softa->ipf_auth_stats.fas_sendfail++; } else { softa->ipf_auth_stats.fas_sendok++; } } else if (m) { error = ipf_inject(&fin, m); if (error != 0) { IPFERROR(10017); error = ENOBUFS; softa->ipf_auth_stats.fas_quefail++; } else { softa->ipf_auth_stats.fas_queok++; } } else { IPFERROR(10018); error = EINVAL; } /* * If we experience an error which will result in the packet * not being processed, make sure we advance to the next one. */ if (error == ENOBUFS) { WRITE_ENTER(&softa->ipf_authlk); softa->ipf_auth_used--; fra->fra_index = -1; fra->fra_pass = 0; if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; if (i == softa->ipf_auth_size) i = 0; softa->ipf_auth_start = i; if (i == softa->ipf_auth_end) break; } if (softa->ipf_auth_start == softa->ipf_auth_end) { softa->ipf_auth_next = 0; softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; } } RWLOCK_EXIT(&softa->ipf_authlk); } #endif /* _KERNEL */ SPL_X(s); return 0; } u_32_t ipf_auth_pre_scanlist(softc, fin, pass) ipf_main_softc_t *softc; fr_info_t *fin; u_32_t pass; { ipf_auth_softc_t *softa = softc->ipf_auth_soft; if (softa->ipf_auth_ip != NULL) return ipf_scanlist(fin, softc->ipf_pass); return pass; } frentry_t ** ipf_auth_rulehead(softc) ipf_main_softc_t *softc; { ipf_auth_softc_t *softa = softc->ipf_auth_soft; return &softa->ipf_auth_ip; } diff --git a/sys/contrib/ipfilter/netinet/ip_auth.h b/sys/contrib/ipfilter/netinet/ip_auth.h index 81e166a6bee5..e914dacacdf0 100644 --- a/sys/contrib/ipfilter/netinet/ip_auth.h +++ b/sys/contrib/ipfilter/netinet/ip_auth.h @@ -1,92 +1,92 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $FreeBSD$ * Id: ip_auth.h,v 2.16.2.2 2006/03/16 06:45:49 darrenr Exp $ * */ #ifndef __IP_AUTH_H__ #define __IP_AUTH_H__ #define FR_NUMAUTH 32 typedef struct frauth { int fra_age; int fra_len; int fra_index; u_32_t fra_pass; fr_info_t fra_info; char *fra_buf; u_32_t fra_flx; #ifdef MENTAT queue_t *fra_q; mb_t *fra_m; #endif } frauth_t; typedef struct frauthent { struct frentry fae_fr; struct frauthent *fae_next; struct frauthent **fae_pnext; u_long fae_age; int fae_ref; } frauthent_t; typedef struct ipf_authstat { U_QUAD_T fas_hits; U_QUAD_T fas_miss; u_long fas_nospace; u_long fas_added; u_long fas_sendfail; u_long fas_sendok; u_long fas_queok; u_long fas_quefail; u_long fas_expire; frauthent_t *fas_faelist; } ipf_authstat_t; typedef struct ipf_auth_softc_s { ipfrwlock_t ipf_authlk; ipfmutex_t ipf_auth_mx; int ipf_auth_size; int ipf_auth_used; int ipf_auth_replies; int ipf_auth_defaultage; int ipf_auth_lock; ipf_authstat_t ipf_auth_stats; frauth_t *ipf_auth; mb_t **ipf_auth_pkts; int ipf_auth_start; int ipf_auth_end; int ipf_auth_next; frauthent_t *ipf_auth_entries; frentry_t *ipf_auth_ip; frentry_t *ipf_auth_rules; } ipf_auth_softc_t; -extern frentry_t *ipf_auth_check __P((fr_info_t *, u_32_t *)); -extern void ipf_auth_expire __P((ipf_main_softc_t *)); -extern int ipf_auth_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, - int, int, void *)); -extern int ipf_auth_init __P((void)); -extern int ipf_auth_main_load __P((void)); -extern int ipf_auth_main_unload __P((void)); -extern void ipf_auth_soft_destroy __P((ipf_main_softc_t *, void *)); -extern void *ipf_auth_soft_create __P((ipf_main_softc_t *)); -extern int ipf_auth_new __P((mb_t *, fr_info_t *)); -extern int ipf_auth_precmd __P((ipf_main_softc_t *, ioctlcmd_t, - frentry_t *, frentry_t **)); -extern void ipf_auth_unload __P((ipf_main_softc_t *)); -extern int ipf_auth_waiting __P((ipf_main_softc_t *)); -extern void ipf_auth_setlock __P((void *, int)); -extern int ipf_auth_soft_init __P((ipf_main_softc_t *, void *)); -extern int ipf_auth_soft_fini __P((ipf_main_softc_t *, void *)); -extern u_32_t ipf_auth_pre_scanlist __P((ipf_main_softc_t *, fr_info_t *, - u_32_t)); -extern frentry_t **ipf_auth_rulehead __P((ipf_main_softc_t *)); +extern frentry_t *ipf_auth_check(fr_info_t *, u_32_t *); +extern void ipf_auth_expire(ipf_main_softc_t *); +extern int ipf_auth_ioctl(ipf_main_softc_t *, caddr_t, ioctlcmd_t, + int, int, void *); +extern int ipf_auth_init(void); +extern int ipf_auth_main_load(void); +extern int ipf_auth_main_unload(void); +extern void ipf_auth_soft_destroy(ipf_main_softc_t *, void *); +extern void *ipf_auth_soft_create(ipf_main_softc_t *); +extern int ipf_auth_new(mb_t *, fr_info_t *); +extern int ipf_auth_precmd(ipf_main_softc_t *, ioctlcmd_t, + frentry_t *, frentry_t **); +extern void ipf_auth_unload(ipf_main_softc_t *); +extern int ipf_auth_waiting(ipf_main_softc_t *); +extern void ipf_auth_setlock(void *, int); +extern int ipf_auth_soft_init(ipf_main_softc_t *, void *); +extern int ipf_auth_soft_fini(ipf_main_softc_t *, void *); +extern u_32_t ipf_auth_pre_scanlist(ipf_main_softc_t *, fr_info_t *, + u_32_t); +extern frentry_t **ipf_auth_rulehead(ipf_main_softc_t *); #endif /* __IP_AUTH_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_compat.h b/sys/contrib/ipfilter/netinet/ip_compat.h index ac20b527ab34..4394b732e10c 100644 --- a/sys/contrib/ipfilter/netinet/ip_compat.h +++ b/sys/contrib/ipfilter/netinet/ip_compat.h @@ -1,1260 +1,1256 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_compat.h 1.8 1/14/96 * $FreeBSD$ * Id: ip_compat.h,v 2.142.2.57 2007/10/10 09:51:42 darrenr Exp $ */ #ifndef __IP_COMPAT_H__ #define __IP_COMPAT_H__ -#ifndef __P -# define __P(x) x -#endif - #if defined(_KERNEL) || defined(KERNEL) || defined(__KERNEL__) # undef KERNEL # undef _KERNEL # undef __KERNEL__ # define KERNEL # define _KERNEL # define __KERNEL__ #endif #ifndef SOLARIS # if defined(sun) && defined(__SVR4) # define SOLARIS 1 # else # define SOLARIS 0 # endif #endif #if defined(__SVR4) # define index strchr # if !defined(_KERNEL) # define bzero(a,b) memset(a,0,b) # define bcmp memcmp # define bcopy(a,b,c) memmove(b,a,c) # endif #endif #ifndef LIFNAMSIZ # ifdef IF_NAMESIZE # define LIFNAMSIZ IF_NAMESIZE # else # ifdef IFNAMSIZ # define LIFNAMSIZ IFNAMSIZ # else # define LIFNAMSIZ 16 # endif # endif #endif # define IPL_EXTERN(ep) ipl##ep /* * This is a workaround for troubles on FreeBSD and OpenBSD. */ # ifndef _KERNEL # define ADD_KERNEL # define _KERNEL # define KERNEL # endif # include # ifdef ADD_KERNEL # undef _KERNEL # undef KERNEL # endif #define NETBSD_GE_REV(x) (defined(__NetBSD_Version__) && \ (__NetBSD_Version__ >= (x))) #define NETBSD_GT_REV(x) (defined(__NetBSD_Version__) && \ (__NetBSD_Version__ > (x))) #define NETBSD_LT_REV(x) (defined(__NetBSD_Version__) && \ (__NetBSD_Version__ < (x))) /* ----------------------------------------------------------------------- */ /* F R E E B S D */ /* ----------------------------------------------------------------------- */ # define HAS_SYS_MD5_H 1 # if defined(_KERNEL) # include "opt_bpf.h" # include "opt_inet6.h" # if defined(INET6) && !defined(USE_INET6) # define USE_INET6 # endif # else # if !defined(USE_INET6) && !defined(NOINET6) # define USE_INET6 # endif # endif # if defined(_KERNEL) # include # define p_cred td_ucred # define p_uid td_ucred->cr_ruid /* * When #define'd, the 5.2.1 kernel panics when used with the ftp proxy. * There may be other, safe, kernels but this is not extensively tested yet. */ # define HAVE_M_PULLDOWN # if !defined(IPFILTER_LKM) && defined(__FreeBSD_version) # include "opt_ipfilter.h" # endif # define COPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) # define COPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) # else # include # endif /* _KERNEL */ # include # include # include # include # define KRWLOCK_FILL_SZ 56 # define KMUTEX_FILL_SZ 56 # include # define KMUTEX_T struct mtx # define KRWLOCK_T struct rwlock #ifdef _KERNEL # define READ_ENTER(x) rw_rlock(&(x)->ipf_lk) # define WRITE_ENTER(x) rw_wlock(&(x)->ipf_lk) # define MUTEX_DOWNGRADE(x) rw_downgrade(&(x)->ipf_lk) # define MUTEX_TRY_UPGRADE(x) rw_try_upgrade(&(x)->ipf_lk) # define RWLOCK_INIT(x,y) rw_init(&(x)->ipf_lk, (y)) # define RW_DESTROY(x) rw_destroy(&(x)->ipf_lk) # define RWLOCK_EXIT(x) do { \ if (rw_wowned(&(x)->ipf_lk)) \ rw_wunlock(&(x)->ipf_lk); \ else \ rw_runlock(&(x)->ipf_lk); \ } while (0) # include # define GETKTIME(x) microtime((struct timeval *)x) # define if_addrlist if_addrhead # include # include # include # define USE_MUTEXES # define MUTEX_ENTER(x) mtx_lock(&(x)->ipf_lk) # define MUTEX_EXIT(x) mtx_unlock(&(x)->ipf_lk) # define MUTEX_INIT(x,y) mtx_init(&(x)->ipf_lk, (y), NULL,\ MTX_DEF) # define MUTEX_DESTROY(x) mtx_destroy(&(x)->ipf_lk) # define MUTEX_NUKE(x) bzero((x), sizeof(*(x))) /* * Whilst the sx(9) locks on FreeBSD have the right semantics and interface * for what we want to use them for, despite testing showing they work - * with a WITNESS kernel, it generates LOR messages. */ # include # define ATOMIC_INC(x) { mtx_lock(&softc->ipf_rw.ipf_lk); (x)++; \ mtx_unlock(&softc->ipf_rw.ipf_lk); } # define ATOMIC_DEC(x) { mtx_lock(&softc->ipf_rw.ipf_lk); (x)--; \ mtx_unlock(&softc->ipf_rw.ipf_lk); } # define ATOMIC_INCL(x) atomic_add_long(&(x), 1) # define ATOMIC_INC64(x) ATOMIC_INC(x) # define ATOMIC_INC32(x) atomic_add_32((u_int *)&(x), 1) # define ATOMIC_DECL(x) atomic_add_long(&(x), -1) # define ATOMIC_DEC64(x) ATOMIC_DEC(x) # define ATOMIC_DEC32(x) atomic_add_32((u_int *)&(x), -1) # define SPL_X(x) ; # define SPL_NET(x) ; # define SPL_IMP(x) ; # define SPL_SCHED(x) ; # define GET_MINOR dev2unit # define MSGDSIZE(m) mbufchainlen(m) # define M_LEN(m) (m)->m_len # define M_ADJ(m,x) m_adj(m, x) # define M_COPY(x) m_copym((x), 0, M_COPYALL, M_NOWAIT) # define M_DUP(m) m_dup(m, M_NOWAIT) # define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } typedef struct mbuf mb_t; #else /* !_KERNEL */ #ifndef _NET_IF_VAR_H_ /* * Userland emulation of struct ifnet. */ struct route; struct mbuf; struct ifnet { char if_xname[IFNAMSIZ]; STAILQ_HEAD(, ifaddr) if_addrlist; int (*if_output)(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); }; #endif /* _NET_IF_VAR_H_ */ #endif /* _KERNEL */ # define IFNAME(x) ((struct ifnet *)x)->if_xname # define COPYIFNAME(v, x, b) \ (void) strncpy(b, \ ((struct ifnet *)x)->if_xname, \ LIFNAMSIZ) typedef u_long ioctlcmd_t; typedef struct uio uio_t; typedef int minor_t; typedef u_int32_t u_32_t; # define U_32_T 1 /* ----------------------------------------------------------------------- */ /* G E N E R I C */ /* ----------------------------------------------------------------------- */ /* * For BSD kernels, if bpf is in the kernel, enable ipfilter to use bpf in * filter rules. */ #if !defined(IPFILTER_BPF) # if (defined(NBPF) && (NBPF > 0)) || (defined(DEV_BPF) && (DEV_BPF > 0)) || \ (defined(NBPFILTER) && (NBPFILTER > 0)) # define IPFILTER_BPF # endif #endif /* * Userland locking primitives */ #ifndef _KERNEL #if !defined(KMUTEX_FILL_SZ) # define KMUTEX_FILL_SZ 1 #endif #if !defined(KRWLOCK_FILL_SZ) # define KRWLOCK_FILL_SZ 1 #endif #endif typedef struct { char *eMm_owner; char *eMm_heldin; u_int eMm_magic; int eMm_held; int eMm_heldat; } eMmutex_t; typedef struct { char *eMrw_owner; char *eMrw_heldin; u_int eMrw_magic; short eMrw_read; short eMrw_write; int eMrw_heldat; } eMrwlock_t; typedef union { char _fill[KMUTEX_FILL_SZ]; #ifdef KMUTEX_T struct { KMUTEX_T ipf_slk; const char *ipf_lname; } ipf_lkun_s; #endif eMmutex_t ipf_emu; } ipfmutex_t; typedef union { char _fill[KRWLOCK_FILL_SZ]; #ifdef KRWLOCK_T struct { KRWLOCK_T ipf_slk; const char *ipf_lname; int ipf_sr; int ipf_sw; u_int ipf_magic; } ipf_lkun_s; #endif eMrwlock_t ipf_emu; } ipfrwlock_t; #define ipf_lk ipf_lkun_s.ipf_slk #define ipf_lname ipf_lkun_s.ipf_lname #define ipf_isr ipf_lkun_s.ipf_sr #define ipf_isw ipf_lkun_s.ipf_sw #define ipf_magic ipf_lkun_s.ipf_magic #if !defined(__GNUC__) || defined(__FreeBSD_version) # ifndef INLINE # define INLINE # endif #else # define INLINE __inline__ #endif #if defined(__FreeBSD_version) && defined(_KERNEL) CTASSERT(sizeof(ipfrwlock_t) == KRWLOCK_FILL_SZ); CTASSERT(sizeof(ipfmutex_t) == KMUTEX_FILL_SZ); #endif /* * In a non-kernel environment, there are a lot of macros that need to be * filled in to be null-ops or to point to some compatibility function, * somewhere in userland. */ #ifndef _KERNEL typedef struct mb_s { struct mb_s *mb_next; char *mb_data; void *mb_ifp; int mb_len; int mb_flags; u_long mb_buf[2048]; } mb_t; # undef m_next # define m_next mb_next # undef m_len # define m_len mb_len # undef m_flags # define m_flags mb_flags # undef m_data # define m_data mb_data # undef M_MCAST # define M_MCAST 0x01 # undef M_BCAST # define M_BCAST 0x02 # undef M_MBCAST # define M_MBCAST 0x04 # define MSGDSIZE(m) msgdsize(m) # define M_LEN(m) (m)->mb_len # define M_ADJ(m,x) (m)->mb_len += x # define M_COPY(m) dupmbt(m) # define M_DUP(m) dupmbt(m) # define GETKTIME(x) gettimeofday((struct timeval *)(x), NULL) # define MTOD(m, t) ((t)(m)->mb_data) # define FREE_MB_T(m) freembt(m) # define ALLOC_MB_T(m,l) (m) = allocmbt(l) # define PREP_MB_T(f, m) do { \ (m)->mb_next = *(f)->fin_mp; \ *(fin)->fin_mp = (m); \ (f)->fin_m = (m); \ } while (0) # define SLEEP(x,y) 1; # define WAKEUP(x,y) ; # define POLLWAKEUP(y) ; # define IPF_PANIC(x,y) ; # define PANIC(x,y) ; # define SPL_SCHED(x) ; # define SPL_NET(x) ; # define SPL_IMP(x) ; # define SPL_X(x) ; # define KMALLOC(a,b) (a) = (b)malloc(sizeof(*a)) # define KMALLOCS(a,b,c) (a) = (b)malloc(c) # define KFREE(x) free(x) # define KFREES(x,s) free(x) # define GETIFP(x, v) get_unit(x,v) # define GETIFMTU_4(x) 2048 # define GETIFMTU_6(x) 2048 # define COPYIN(a,b,c) bcopywrap((a), (b), (c)) # define COPYOUT(a,b,c) bcopywrap((a), (b), (c)) # define COPYDATA(m, o, l, b) bcopy(MTOD((mb_t *)m, char *) + (o), \ (b), (l)) # define COPYBACK(m, o, l, b) bcopy((b), \ MTOD((mb_t *)m, char *) + (o), \ (l)) # define UIOMOVE(a,b,c,d) ipfuiomove((caddr_t)a,b,c,d) -extern void m_copydata __P((mb_t *, int, int, caddr_t)); -extern int ipfuiomove __P((caddr_t, int, int, struct uio *)); -extern int bcopywrap __P((void *, void *, size_t)); -extern mb_t *allocmbt __P((size_t)); -extern mb_t *dupmbt __P((mb_t *)); -extern void freembt __P((mb_t *)); +extern void m_copydata(mb_t *, int, int, caddr_t); +extern int ipfuiomove(caddr_t, int, int, struct uio *); +extern int bcopywrap(void *, void *, size_t); +extern mb_t *allocmbt(size_t); +extern mb_t *dupmbt(mb_t *); +extern void freembt(mb_t *); # define MUTEX_DESTROY(x) eMmutex_destroy(&(x)->ipf_emu, \ __FILE__, __LINE__) # define MUTEX_ENTER(x) eMmutex_enter(&(x)->ipf_emu, \ __FILE__, __LINE__) # define MUTEX_EXIT(x) eMmutex_exit(&(x)->ipf_emu, \ __FILE__, __LINE__) # define MUTEX_INIT(x,y) eMmutex_init(&(x)->ipf_emu, y, \ __FILE__, __LINE__) # define MUTEX_NUKE(x) bzero((x), sizeof(*(x))) # define MUTEX_DOWNGRADE(x) eMrwlock_downgrade(&(x)->ipf_emu, \ __FILE__, __LINE__) # define MUTEX_TRY_UPGRADE(x) eMrwlock_try_upgrade(&(x)->ipf_emu, \ __FILE__, __LINE__) # define READ_ENTER(x) eMrwlock_read_enter(&(x)->ipf_emu, \ __FILE__, __LINE__) # define RWLOCK_INIT(x, y) eMrwlock_init(&(x)->ipf_emu, y) # define RWLOCK_EXIT(x) eMrwlock_exit(&(x)->ipf_emu) # define RW_DESTROY(x) eMrwlock_destroy(&(x)->ipf_emu) # define WRITE_ENTER(x) eMrwlock_write_enter(&(x)->ipf_emu, \ __FILE__, \ __LINE__) # define USE_MUTEXES 1 -extern void eMmutex_destroy __P((eMmutex_t *, char *, int)); -extern void eMmutex_enter __P((eMmutex_t *, char *, int)); -extern void eMmutex_exit __P((eMmutex_t *, char *, int)); -extern void eMmutex_init __P((eMmutex_t *, char *, char *, int)); -extern void eMrwlock_destroy __P((eMrwlock_t *)); -extern void eMrwlock_exit __P((eMrwlock_t *)); -extern void eMrwlock_init __P((eMrwlock_t *, char *)); -extern void eMrwlock_read_enter __P((eMrwlock_t *, char *, int)); -extern void eMrwlock_write_enter __P((eMrwlock_t *, char *, int)); -extern void eMrwlock_downgrade __P((eMrwlock_t *, char *, int)); +extern void eMmutex_destroy(eMmutex_t *, char *, int); +extern void eMmutex_enter(eMmutex_t *, char *, int); +extern void eMmutex_exit(eMmutex_t *, char *, int); +extern void eMmutex_init(eMmutex_t *, char *, char *, int); +extern void eMrwlock_destroy(eMrwlock_t *); +extern void eMrwlock_exit(eMrwlock_t *); +extern void eMrwlock_init(eMrwlock_t *, char *); +extern void eMrwlock_read_enter(eMrwlock_t *, char *, int); +extern void eMrwlock_write_enter(eMrwlock_t *, char *, int); +extern void eMrwlock_downgrade(eMrwlock_t *, char *, int); #endif extern mb_t *allocmbt(size_t); #define MAX_IPV4HDR ((0xf << 2) + sizeof(struct icmp) + sizeof(ip_t) + 8) #ifndef IP_OFFMASK # define IP_OFFMASK 0x1fff #endif /* * On BSD's use quad_t as a guarantee for getting at least a 64bit sized * object. */ #if !defined(__amd64__) && !SOLARIS # define USE_QUAD_T # define U_QUAD_T unsigned long long # define QUAD_T long long #else /* BSD > 199306 */ # if !defined(U_QUAD_T) # define U_QUAD_T u_long # define QUAD_T long # endif #endif /* BSD > 199306 */ #ifdef USE_INET6 # if defined(__NetBSD__) || defined(__FreeBSD__) # include # include # if defined(_KERNEL) # include # endif typedef struct ip6_hdr ip6_t; # endif #endif #ifndef MAX # define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif #if defined(_KERNEL) # if defined(MENTAT) && !defined(INSTANCES) # define COPYDATA mb_copydata # define COPYBACK mb_copyback # else # define COPYDATA m_copydata # define COPYBACK m_copyback # endif # if (defined(__NetBSD_Version__) && (__NetBSD_Version__ < 105180000)) || \ defined(__FreeBSD__) # include # endif # if NETBSD_GE_REV(105180000) # include # else # include extern vm_map_t kmem_map; # endif # include # ifdef IPFILTER_M_IPFILTER # include MALLOC_DECLARE(M_IPFILTER); # define _M_IPF M_IPFILTER # else /* IPFILTER_M_IPFILTER */ # ifdef M_PFIL # define _M_IPF M_PFIL # else # ifdef M_IPFILTER # define _M_IPF M_IPFILTER # else # define _M_IPF M_TEMP # endif /* M_IPFILTER */ # endif /* M_PFIL */ # endif /* IPFILTER_M_IPFILTER */ # if !defined(KMALLOC) # define KMALLOC(a, b) (a) = (b)malloc(sizeof(*(a)), _M_IPF, M_NOWAIT) # endif # if !defined(KMALLOCS) # define KMALLOCS(a, b, c) (a) = (b)malloc((c), _M_IPF, M_NOWAIT) # endif # if !defined(KFREE) # define KFREE(x) free((x), _M_IPF) # endif # if !defined(KFREES) # define KFREES(x,s) free((x), _M_IPF) # endif # define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,d) # define SLEEP(id, n) tsleep((id), PPAUSE|PCATCH, n, 0) # define WAKEUP(id,x) wakeup(id+x) # if !defined(POLLWAKEUP) # define POLLWAKEUP(x) selwakeup(softc->ipf_selwait+x) # endif # define GETIFP(n, v) ifunit(n) # define GETIFMTU_4(x) ((struct ifnet *)x)->if_mtu # define GETIFMTU_6(x) ((struct ifnet *)x)->if_mtu # if !defined(USE_MUTEXES) && !defined(SPL_NET) # define SPL_IMP(x) x = splimp() # define SPL_NET(x) x = splnet() # if !defined(SPL_SCHED) # define SPL_SCHED(x) x = splsched() # endif # define SPL_X(x) (void) splx(x) # endif /* !USE_MUTEXES */ # ifndef FREE_MB_T # define FREE_MB_T(m) m_freem(m) # endif # ifndef ALLOC_MB_T # ifdef MGETHDR # define ALLOC_MB_T(m,l) do { \ MGETHDR((m), M_NOWAIT, MT_HEADER); \ if ((m) != NULL) { \ (m)->m_len = (l); \ (m)->m_pkthdr.len = (l); \ } \ } while (0) # else # define ALLOC_MB_T(m,l) do { \ MGET((m), M_NOWAIT, MT_HEADER); \ if ((m) != NULL) { \ (m)->m_len = (l); \ (m)->m_pkthdr.len = (l); \ } \ } while (0) # endif # endif # ifndef PREP_MB_T # define PREP_MB_T(f, m) do { \ mb_t *_o = *(f)->fin_mp; \ (m)->m_next = _o; \ *(fin)->fin_mp = (m); \ if (_o->m_flags & M_PKTHDR) { \ (m)->m_pkthdr.len += \ _o->m_pkthdr.len; \ (m)->m_pkthdr.rcvif = \ _o->m_pkthdr.rcvif; \ } \ } while (0) # endif # ifndef M_DUP # ifdef M_COPYALL # define M_DUP(m) m_dup(m, 0, M_COPYALL, 0) # else # define M_DUP(m) m_dup(m) # endif # endif # ifndef MTOD # define MTOD(m,t) mtod(m,t) # endif # ifndef COPYIN # define COPYIN(a,b,c) (bcopy((caddr_t)(a), (caddr_t)(b), (c)), 0) # define COPYOUT(a,b,c) (bcopy((caddr_t)(a), (caddr_t)(b), (c)), 0) # endif # if SOLARIS && !defined(KMALLOC) # define KMALLOC(a,b) (a) = (b)new_kmem_alloc(sizeof(*(a)), \ KMEM_NOSLEEP) # define KMALLOCS(a,b,c) (a) = (b)new_kmem_alloc((c), KMEM_NOSLEEP) # endif # ifndef GET_MINOR # define GET_MINOR(x) dev2unit(x) # endif # define PANIC(x,y) if (x) panic y #endif /* _KERNEL */ #if !defined(IFNAME) && !defined(_KERNEL) # define IFNAME(x) get_ifname((struct ifnet *)x) #endif #ifndef COPYIFNAME # define NEED_FRGETIFNAME -extern char *ipf_getifname __P((struct ifnet *, char *)); +extern char *ipf_getifname(struct ifnet *, char *); # define COPYIFNAME(v, x, b) \ ipf_getifname((struct ifnet *)x, b) #endif #ifndef ASSERT # ifdef _KERNEL # define ASSERT(x) # else # define ASSERT(x) do { if (!(x)) abort(); } while (0) # endif #endif #ifndef BCOPYIN # define BCOPYIN(a,b,c) (bcopy((caddr_t)(a), (caddr_t)(b), (c)), 0) # define BCOPYOUT(a,b,c) (bcopy((caddr_t)(a), (caddr_t)(b), (c)), 0) #endif /* * 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 ISDIGIT(x) isdigit((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)) #define TOUPPER(x) toupper((u_char)(x)) #define TOLOWER(x) tolower((u_char)(x)) /* * If mutexes aren't being used, turn all the mutex functions into null-ops. */ #if !defined(USE_MUTEXES) # define USE_SPL 1 # undef RW_DESTROY # undef MUTEX_INIT # undef MUTEX_NUKE # undef MUTEX_DESTROY # define MUTEX_ENTER(x) ; # define READ_ENTER(x) ; # define WRITE_ENTER(x) ; # define MUTEX_DOWNGRADE(x) ; # define MUTEX_TRY_UPGRADE(x) ; # define RWLOCK_INIT(x, y) ; # define RWLOCK_EXIT(x) ; # define RW_DESTROY(x) ; # define MUTEX_EXIT(x) ; # define MUTEX_INIT(x,y) ; # define MUTEX_DESTROY(x) ; # define MUTEX_NUKE(x) ; #endif /* !USE_MUTEXES */ #ifndef ATOMIC_INC # define ATOMIC_INC(x) (x)++ # define ATOMIC_DEC(x) (x)-- #endif #if defined(USE_SPL) && defined(_KERNEL) # define SPL_INT(x) int x #else # define SPL_INT(x) #endif /* * If there are no atomic operations for bit sizes defined, define them to all * use a generic one that works for all sizes. */ #ifndef ATOMIC_INCL # define ATOMIC_INCL ATOMIC_INC # define ATOMIC_INC64 ATOMIC_INC # define ATOMIC_INC32 ATOMIC_INC # define ATOMIC_DECL ATOMIC_DEC # define ATOMIC_DEC64 ATOMIC_DEC # define ATOMIC_DEC32 ATOMIC_DEC #endif #ifndef HDR_T_PRIVATE typedef struct tcphdr tcphdr_t; typedef struct udphdr udphdr_t; #endif typedef struct icmp icmphdr_t; typedef struct ip ip_t; typedef struct ether_header ether_header_t; typedef struct tcpiphdr tcpiphdr_t; #ifndef FR_GROUPLEN # define FR_GROUPLEN 16 #endif #ifndef offsetof # define offsetof(t,m) (size_t)((&((t *)0L)->m)) #endif #ifndef stsizeof # define stsizeof(t,m) sizeof(((t *)0L)->m) #endif /* * This set of macros has been brought about because on Tru64 it is not * possible to easily assign or examine values in a structure that are * bit fields. */ #ifndef IP_V # define IP_V(x) (x)->ip_v #endif #ifndef IP_V_A # define IP_V_A(x,y) (x)->ip_v = (y) #endif #ifndef IP_HL # define IP_HL(x) (x)->ip_hl #endif #ifndef IP_HL_A # define IP_HL_A(x,y) (x)->ip_hl = ((y) & 0xf) #endif #ifndef TCP_X2 # define TCP_X2(x) (x)->th_x2 #endif #ifndef TCP_X2_A # define TCP_X2_A(x,y) (x)->th_x2 = (y) #endif #ifndef TCP_OFF # define TCP_OFF(x) (x)->th_off #endif #ifndef TCP_OFF_A # define TCP_OFF_A(x,y) (x)->th_off = (y) #endif #define IPMINLEN(i, h) ((i)->ip_len >= (IP_HL(i) * 4 + sizeof(struct h))) #define TCPF_ALL (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG|\ TH_ECN|TH_CWR) #if !SOLARIS && !defined(m_act) # define m_act m_nextpkt #endif /* * Security Options for Intenet Protocol (IPSO) as defined in RFC 1108. * * Basic Option * * 00000001 - (Reserved 4) * 00111101 - Top Secret * 01011010 - Secret * 10010110 - Confidential * 01100110 - (Reserved 3) * 11001100 - (Reserved 2) * 10101011 - Unclassified * 11110001 - (Reserved 1) */ #define IPSO_CLASS_RES4 0x01 #define IPSO_CLASS_TOPS 0x3d #define IPSO_CLASS_SECR 0x5a #define IPSO_CLASS_CONF 0x96 #define IPSO_CLASS_RES3 0x66 #define IPSO_CLASS_RES2 0xcc #define IPSO_CLASS_UNCL 0xab #define IPSO_CLASS_RES1 0xf1 #define IPSO_AUTH_GENSER 0x80 #define IPSO_AUTH_ESI 0x40 #define IPSO_AUTH_SCI 0x20 #define IPSO_AUTH_NSA 0x10 #define IPSO_AUTH_DOE 0x08 #define IPSO_AUTH_UN 0x06 #define IPSO_AUTH_FTE 0x01 /* * IP option #defines */ #undef IPOPT_RR #define IPOPT_RR 7 #undef IPOPT_ZSU #define IPOPT_ZSU 10 /* ZSU */ #undef IPOPT_MTUP #define IPOPT_MTUP 11 /* MTUP */ #undef IPOPT_MTUR #define IPOPT_MTUR 12 /* MTUR */ #undef IPOPT_ENCODE #define IPOPT_ENCODE 15 /* ENCODE */ #undef IPOPT_TS #define IPOPT_TS 68 #undef IPOPT_TR #define IPOPT_TR 82 /* TR */ #undef IPOPT_SECURITY #define IPOPT_SECURITY 130 #undef IPOPT_LSRR #define IPOPT_LSRR 131 #undef IPOPT_E_SEC #define IPOPT_E_SEC 133 /* E-SEC */ #undef IPOPT_CIPSO #define IPOPT_CIPSO 134 /* CIPSO */ #undef IPOPT_SATID #define IPOPT_SATID 136 #ifndef IPOPT_SID # define IPOPT_SID IPOPT_SATID #endif #undef IPOPT_SSRR #define IPOPT_SSRR 137 #undef IPOPT_ADDEXT #define IPOPT_ADDEXT 147 /* ADDEXT */ #undef IPOPT_VISA #define IPOPT_VISA 142 /* VISA */ #undef IPOPT_IMITD #define IPOPT_IMITD 144 /* IMITD */ #undef IPOPT_EIP #define IPOPT_EIP 145 /* EIP */ #undef IPOPT_RTRALRT #define IPOPT_RTRALRT 148 /* RTRALRT */ #undef IPOPT_SDB #define IPOPT_SDB 149 #undef IPOPT_NSAPA #define IPOPT_NSAPA 150 #undef IPOPT_DPS #define IPOPT_DPS 151 #undef IPOPT_UMP #define IPOPT_UMP 152 #undef IPOPT_FINN #define IPOPT_FINN 205 /* FINN */ #undef IPOPT_AH #define IPOPT_AH 256+IPPROTO_AH # define ICMP_UNREACH_ADMIN_PROHIBIT ICMP_UNREACH_FILTER_PROHIB # define ICMP_UNREACH_FILTER ICMP_UNREACH_FILTER_PROHIB #ifndef IPVERSION # define IPVERSION 4 #endif #ifndef IPOPT_MINOFF # define IPOPT_MINOFF 4 #endif #ifndef IPOPT_COPIED # define IPOPT_COPIED(x) ((x)&0x80) #endif #ifndef IPOPT_EOL # define IPOPT_EOL 0 #endif #ifndef IPOPT_NOP # define IPOPT_NOP 1 #endif #ifndef IP_MF # define IP_MF ((u_short)0x2000) #endif #ifndef ETHERTYPE_IP # define ETHERTYPE_IP ((u_short)0x0800) #endif #ifndef TH_FIN # define TH_FIN 0x01 #endif #ifndef TH_SYN # define TH_SYN 0x02 #endif #ifndef TH_RST # define TH_RST 0x04 #endif #ifndef TH_PUSH # define TH_PUSH 0x08 #endif #ifndef TH_ACK # define TH_ACK 0x10 #endif #ifndef TH_URG # define TH_URG 0x20 #endif #undef TH_ACKMASK #define TH_ACKMASK (TH_FIN|TH_SYN|TH_RST|TH_ACK) #ifndef IPOPT_EOL # define IPOPT_EOL 0 #endif #ifndef IPOPT_NOP # define IPOPT_NOP 1 #endif #ifndef IPOPT_RR # define IPOPT_RR 7 #endif #ifndef IPOPT_TS # define IPOPT_TS 68 #endif #ifndef IPOPT_SECURITY # define IPOPT_SECURITY 130 #endif #ifndef IPOPT_LSRR # define IPOPT_LSRR 131 #endif #ifndef IPOPT_SATID # define IPOPT_SATID 136 #endif #ifndef IPOPT_SSRR # define IPOPT_SSRR 137 #endif #ifndef IPOPT_SECUR_UNCLASS # define IPOPT_SECUR_UNCLASS ((u_short)0x0000) #endif #ifndef IPOPT_SECUR_CONFID # define IPOPT_SECUR_CONFID ((u_short)0xf135) #endif #ifndef IPOPT_SECUR_EFTO # define IPOPT_SECUR_EFTO ((u_short)0x789a) #endif #ifndef IPOPT_SECUR_MMMM # define IPOPT_SECUR_MMMM ((u_short)0xbc4d) #endif #ifndef IPOPT_SECUR_RESTR # define IPOPT_SECUR_RESTR ((u_short)0xaf13) #endif #ifndef IPOPT_SECUR_SECRET # define IPOPT_SECUR_SECRET ((u_short)0xd788) #endif #ifndef IPOPT_SECUR_TOPSECRET # define IPOPT_SECUR_TOPSECRET ((u_short)0x6bc5) #endif #ifndef IPOPT_OLEN # define IPOPT_OLEN 1 #endif #ifndef IPPROTO_HOPOPTS # define IPPROTO_HOPOPTS 0 #endif #ifndef IPPROTO_IPIP # define IPPROTO_IPIP 4 #endif #ifndef IPPROTO_ENCAP # define IPPROTO_ENCAP 98 #endif #ifndef IPPROTO_IPV6 # define IPPROTO_IPV6 41 #endif #ifndef IPPROTO_ROUTING # define IPPROTO_ROUTING 43 #endif #ifndef IPPROTO_FRAGMENT # define IPPROTO_FRAGMENT 44 #endif #ifndef IPPROTO_GRE # define IPPROTO_GRE 47 /* GRE encaps RFC 1701 */ #endif #ifndef IPPROTO_ESP # define IPPROTO_ESP 50 #endif #ifndef IPPROTO_AH # define IPPROTO_AH 51 #endif #ifndef IPPROTO_ICMPV6 # define IPPROTO_ICMPV6 58 #endif #ifndef IPPROTO_NONE # define IPPROTO_NONE 59 #endif #ifndef IPPROTO_DSTOPTS # define IPPROTO_DSTOPTS 60 #endif #ifndef IPPROTO_MOBILITY # define IPPROTO_MOBILITY 135 #endif #ifndef ICMP_ROUTERADVERT # define ICMP_ROUTERADVERT 9 #endif #ifndef ICMP_ROUTERSOLICIT # define ICMP_ROUTERSOLICIT 10 #endif #ifndef ICMP6_DST_UNREACH # define ICMP6_DST_UNREACH 1 #endif #ifndef ICMP6_PACKET_TOO_BIG # define ICMP6_PACKET_TOO_BIG 2 #endif #ifndef ICMP6_TIME_EXCEEDED # define ICMP6_TIME_EXCEEDED 3 #endif #ifndef ICMP6_PARAM_PROB # define ICMP6_PARAM_PROB 4 #endif #ifndef ICMP6_ECHO_REQUEST # define ICMP6_ECHO_REQUEST 128 #endif #ifndef ICMP6_ECHO_REPLY # define ICMP6_ECHO_REPLY 129 #endif #ifndef ICMP6_MEMBERSHIP_QUERY # define ICMP6_MEMBERSHIP_QUERY 130 #endif #ifndef MLD6_LISTENER_QUERY # define MLD6_LISTENER_QUERY 130 #endif #ifndef ICMP6_MEMBERSHIP_REPORT # define ICMP6_MEMBERSHIP_REPORT 131 #endif #ifndef MLD6_LISTENER_REPORT # define MLD6_LISTENER_REPORT 131 #endif #ifndef ICMP6_MEMBERSHIP_REDUCTION # define ICMP6_MEMBERSHIP_REDUCTION 132 #endif #ifndef MLD6_LISTENER_DONE # define MLD6_LISTENER_DONE 132 #endif #ifndef ND_ROUTER_SOLICIT # define ND_ROUTER_SOLICIT 133 #endif #ifndef ND_ROUTER_ADVERT # define ND_ROUTER_ADVERT 134 #endif #ifndef ND_NEIGHBOR_SOLICIT # define ND_NEIGHBOR_SOLICIT 135 #endif #ifndef ND_NEIGHBOR_ADVERT # define ND_NEIGHBOR_ADVERT 136 #endif #ifndef ND_REDIRECT # define ND_REDIRECT 137 #endif #ifndef ICMP6_ROUTER_RENUMBERING # define ICMP6_ROUTER_RENUMBERING 138 #endif #ifndef ICMP6_WRUREQUEST # define ICMP6_WRUREQUEST 139 #endif #ifndef ICMP6_WRUREPLY # define ICMP6_WRUREPLY 140 #endif #ifndef ICMP6_FQDN_QUERY # define ICMP6_FQDN_QUERY 139 #endif #ifndef ICMP6_FQDN_REPLY # define ICMP6_FQDN_REPLY 140 #endif #ifndef ICMP6_NI_QUERY # define ICMP6_NI_QUERY 139 #endif #ifndef ICMP6_NI_REPLY # define ICMP6_NI_REPLY 140 #endif #ifndef MLD6_MTRACE_RESP # define MLD6_MTRACE_RESP 200 #endif #ifndef MLD6_MTRACE # define MLD6_MTRACE 201 #endif #ifndef ICMP6_HADISCOV_REQUEST # define ICMP6_HADISCOV_REQUEST 202 #endif #ifndef ICMP6_HADISCOV_REPLY # define ICMP6_HADISCOV_REPLY 203 #endif #ifndef ICMP6_MOBILEPREFIX_SOLICIT # define ICMP6_MOBILEPREFIX_SOLICIT 204 #endif #ifndef ICMP6_MOBILEPREFIX_ADVERT # define ICMP6_MOBILEPREFIX_ADVERT 205 #endif #ifndef ICMP6_MAXTYPE # define ICMP6_MAXTYPE 205 #endif #ifndef ICMP6_DST_UNREACH_NOROUTE # define ICMP6_DST_UNREACH_NOROUTE 0 #endif #ifndef ICMP6_DST_UNREACH_ADMIN # define ICMP6_DST_UNREACH_ADMIN 1 #endif #ifndef ICMP6_DST_UNREACH_NOTNEIGHBOR # define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 #endif #ifndef ICMP6_DST_UNREACH_BEYONDSCOPE # define ICMP6_DST_UNREACH_BEYONDSCOPE 2 #endif #ifndef ICMP6_DST_UNREACH_ADDR # define ICMP6_DST_UNREACH_ADDR 3 #endif #ifndef ICMP6_DST_UNREACH_NOPORT # define ICMP6_DST_UNREACH_NOPORT 4 #endif #ifndef ICMP6_TIME_EXCEED_TRANSIT # define ICMP6_TIME_EXCEED_TRANSIT 0 #endif #ifndef ICMP6_TIME_EXCEED_REASSEMBLY # define ICMP6_TIME_EXCEED_REASSEMBLY 1 #endif #ifndef ICMP6_NI_SUCCESS # define ICMP6_NI_SUCCESS 0 #endif #ifndef ICMP6_NI_REFUSED # define ICMP6_NI_REFUSED 1 #endif #ifndef ICMP6_NI_UNKNOWN # define ICMP6_NI_UNKNOWN 2 #endif #ifndef ICMP6_ROUTER_RENUMBERING_COMMAND # define ICMP6_ROUTER_RENUMBERING_COMMAND 0 #endif #ifndef ICMP6_ROUTER_RENUMBERING_RESULT # define ICMP6_ROUTER_RENUMBERING_RESULT 1 #endif #ifndef ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET # define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 #endif #ifndef ICMP6_PARAMPROB_HEADER # define ICMP6_PARAMPROB_HEADER 0 #endif #ifndef ICMP6_PARAMPROB_NEXTHEADER # define ICMP6_PARAMPROB_NEXTHEADER 1 #endif #ifndef ICMP6_PARAMPROB_OPTION # define ICMP6_PARAMPROB_OPTION 2 #endif #ifndef ICMP6_NI_SUBJ_IPV6 # define ICMP6_NI_SUBJ_IPV6 0 #endif #ifndef ICMP6_NI_SUBJ_FQDN # define ICMP6_NI_SUBJ_FQDN 1 #endif #ifndef ICMP6_NI_SUBJ_IPV4 # define ICMP6_NI_SUBJ_IPV4 2 #endif #ifndef MLD_MTRACE_RESP # define MLD_MTRACE_RESP 200 #endif #ifndef MLD_MTRACE # define MLD_MTRACE 201 #endif #ifndef MLD6_MTRACE_RESP # define MLD6_MTRACE_RESP MLD_MTRACE_RESP #endif #ifndef MLD6_MTRACE # define MLD6_MTRACE MLD_MTRACE #endif #if !defined(IPV6_FLOWINFO_MASK) # if (BYTE_ORDER == BIG_ENDIAN) || defined(_BIG_ENDIAN) # define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */ # else # if(BYTE_ORDER == LITTLE_ENDIAN) || !defined(_BIG_ENDIAN) # define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */ # endif /* LITTLE_ENDIAN */ # endif #endif #if !defined(IPV6_FLOWLABEL_MASK) # if (BYTE_ORDER == BIG_ENDIAN) || defined(_BIG_ENDIAN) # define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */ # else # if (BYTE_ORDER == LITTLE_ENDIAN) || !defined(_BIG_ENDIAN) # define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */ # endif /* LITTLE_ENDIAN */ # endif #endif /* * 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 #define TH_ECNALL (TH_ECN|TH_CWR) /* * TCP States */ #define IPF_TCPS_LISTEN 0 /* listening for connection */ #define IPF_TCPS_SYN_SENT 1 /* active, have sent syn */ #define IPF_TCPS_SYN_RECEIVED 2 /* have send and received syn */ #define IPF_TCPS_HALF_ESTAB 3 /* for connections not fully "up" */ /* states < IPF_TCPS_ESTABLISHED are those where connections not established */ #define IPF_TCPS_ESTABLISHED 4 /* established */ #define IPF_TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ /* states > IPF_TCPS_CLOSE_WAIT are those where user has closed */ #define IPF_TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ #define IPF_TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ #define IPF_TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ /* states > IPF_TCPS_CLOSE_WAIT && < IPF_TCPS_FIN_WAIT_2 await ACK of FIN */ #define IPF_TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ #define IPF_TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ #define IPF_TCPS_CLOSED 11 /* closed */ #define IPF_TCP_NSTATES 12 #define TCP_MSL 120 #undef ICMP_MAX_UNREACH #define ICMP_MAX_UNREACH 14 #undef ICMP_MAXTYPE #define ICMP_MAXTYPE 18 #ifndef LOG_FTP # define LOG_FTP (11<<3) #endif #ifndef LOG_AUTHPRIV # define LOG_AUTHPRIV (10<<3) #endif #ifndef LOG_AUDIT # define LOG_AUDIT (13<<3) #endif #ifndef LOG_NTP # define LOG_NTP (12<<3) #endif #ifndef LOG_SECURITY # define LOG_SECURITY (13<<3) #endif #ifndef LOG_LFMT # define LOG_LFMT (14<<3) #endif #ifndef LOG_CONSOLE # define LOG_CONSOLE (14<<3) #endif /* * ICMP error replies have an IP header (20 bytes), 8 bytes of ICMP data, * another IP header and then 64 bits of data, totalling 56. Of course, * the last 64 bits is dependent on that being available. */ #define ICMPERR_ICMPHLEN 8 #define ICMPERR_IPICMPHLEN (20 + 8) #define ICMPERR_MINPKTLEN (20 + 8 + 20) #define ICMPERR_MAXPKTLEN (20 + 8 + 20 + 8) #define ICMP6ERR_MINPKTLEN (40 + 8) #define ICMP6ERR_IPICMPHLEN (40 + 8 + 40) #ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) #endif #ifdef RESCUE # undef IPFILTER_BPF #endif #ifdef IPF_DEBUG # define DPRINT(x) printf x #else # define DPRINT(x) #endif #ifdef DTRACE_PROBE # ifdef _KERNEL # define DT(_n) DTRACE_PROBE(_n) # define DT1(_n,_a,_b) DTRACE_PROBE1(_n,_a,_b) # define DT2(_n,_a,_b,_c,_d) DTRACE_PROBE2(_n,_a,_b,_c,_d) # define DT3(_n,_a,_b,_c,_d,_e,_f) \ DTRACE_PROBE3(_n,_a,_b,_c,_d,_e,_f) # define DT4(_n,_a,_b,_c,_d,_e,_f,_g,_h) \ DTRACE_PROBE4(_n,_a,_b,_c,_d,_e,_f,_g,_h) # else # define DT(_n) # define DT1(_n,_a,_b) # define DT2(_n,_a,_b,_c,_d) # define DT3(_n,_a,_b,_c,_d,_e,_f) # define DT4(_n,_a,_b,_c,_d,_e,_f,_g,_h) # endif #else # define DT(_n) # define DT1(_n,_a,_b) # define DT2(_n,_a,_b,_c,_d) # define DT3(_n,_a,_b,_c,_d,_e,_f) # define DT4(_n,_a,_b,_c,_d,_e,_f,_g,_h) #endif struct ip6_routing { u_char ip6r_nxt; /* next header */ u_char ip6r_len; /* length in units of 8 octets */ u_char ip6r_type; /* always zero */ u_char ip6r_segleft; /* segments left */ u_32_t ip6r_reserved; /* reserved field */ }; #endif /* __IP_COMPAT_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_dns_pxy.c b/sys/contrib/ipfilter/netinet/ip_dns_pxy.c index ff1bc792bd59..22871a7ba02b 100644 --- a/sys/contrib/ipfilter/netinet/ip_dns_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_dns_pxy.c @@ -1,401 +1,401 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_dns_pxy.c,v 1.1.2.10 2012/07/22 08:04:23 darren_r Exp $ */ #define IPF_DNS_PROXY /* * map ... proxy port dns/udp 53 { block .cnn.com; } */ typedef struct ipf_dns_filter { struct ipf_dns_filter *idns_next; char *idns_name; int idns_namelen; int idns_pass; } ipf_dns_filter_t; typedef struct ipf_dns_softc_s { ipf_dns_filter_t *ipf_p_dns_list; ipfrwlock_t ipf_p_dns_rwlock; u_long ipf_p_dns_compress; u_long ipf_p_dns_toolong; u_long ipf_p_dns_nospace; } ipf_dns_softc_t; -int ipf_p_dns_allow_query __P((ipf_dns_softc_t *, dnsinfo_t *)); -int ipf_p_dns_ctl __P((ipf_main_softc_t *, void *, ap_ctl_t *)); -void ipf_p_dns_del __P((ipf_main_softc_t *, ap_session_t *)); -int ipf_p_dns_get_name __P((ipf_dns_softc_t *, char *, int, char *, int)); -int ipf_p_dns_inout __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_dns_match __P((fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_dns_match_names __P((ipf_dns_filter_t *, char *, int)); -int ipf_p_dns_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -void *ipf_p_dns_soft_create __P((ipf_main_softc_t *)); -void ipf_p_dns_soft_destroy __P((ipf_main_softc_t *, void *)); +int ipf_p_dns_allow_query(ipf_dns_softc_t *, dnsinfo_t *); +int ipf_p_dns_ctl(ipf_main_softc_t *, void *, ap_ctl_t *); +void ipf_p_dns_del(ipf_main_softc_t *, ap_session_t *); +int ipf_p_dns_get_name(ipf_dns_softc_t *, char *, int, char *, int); +int ipf_p_dns_inout(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_dns_match(fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_dns_match_names(ipf_dns_filter_t *, char *, int); +int ipf_p_dns_new(void *, fr_info_t *, ap_session_t *, nat_t *); +void *ipf_p_dns_soft_create(ipf_main_softc_t *); +void ipf_p_dns_soft_destroy(ipf_main_softc_t *, void *); typedef struct { u_char dns_id[2]; u_short dns_ctlword; u_short dns_qdcount; u_short dns_ancount; u_short dns_nscount; u_short dns_arcount; } ipf_dns_hdr_t; #define DNS_QR(x) ((ntohs(x) & 0x8000) >> 15) #define DNS_OPCODE(x) ((ntohs(x) & 0x7800) >> 11) #define DNS_AA(x) ((ntohs(x) & 0x0400) >> 10) #define DNS_TC(x) ((ntohs(x) & 0x0200) >> 9) #define DNS_RD(x) ((ntohs(x) & 0x0100) >> 8) #define DNS_RA(x) ((ntohs(x) & 0x0080) >> 7) #define DNS_Z(x) ((ntohs(x) & 0x0070) >> 4) #define DNS_RCODE(x) ((ntohs(x) & 0x000f) >> 0) void * ipf_p_dns_soft_create(softc) ipf_main_softc_t *softc; { ipf_dns_softc_t *softd; KMALLOC(softd, ipf_dns_softc_t *); if (softd == NULL) return NULL; bzero((char *)softd, sizeof(*softd)); RWLOCK_INIT(&softd->ipf_p_dns_rwlock, "ipf dns rwlock"); return softd; } void ipf_p_dns_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_dns_softc_t *softd = arg; ipf_dns_filter_t *idns; while ((idns = softd->ipf_p_dns_list) != NULL) { KFREES(idns->idns_name, idns->idns_namelen); idns->idns_name = NULL; idns->idns_namelen = 0; softd->ipf_p_dns_list = idns->idns_next; KFREE(idns); } RW_DESTROY(&softd->ipf_p_dns_rwlock); KFREE(softd); } int ipf_p_dns_ctl(softc, arg, ctl) ipf_main_softc_t *softc; void *arg; ap_ctl_t *ctl; { ipf_dns_softc_t *softd = arg; ipf_dns_filter_t *tmp, *idns, **idnsp; int error = 0; /* * To make locking easier. */ KMALLOC(tmp, ipf_dns_filter_t *); WRITE_ENTER(&softd->ipf_p_dns_rwlock); for (idnsp = &softd->ipf_p_dns_list; (idns = *idnsp) != NULL; idnsp = &idns->idns_next) { if (idns->idns_namelen != ctl->apc_dsize) continue; if (!strncmp(ctl->apc_data, idns->idns_name, idns->idns_namelen)) break; } switch (ctl->apc_cmd) { case APC_CMD_DEL : if (idns == NULL) { IPFERROR(80006); error = ESRCH; break; } *idnsp = idns->idns_next; idns->idns_next = NULL; KFREES(idns->idns_name, idns->idns_namelen); idns->idns_name = NULL; idns->idns_namelen = 0; KFREE(idns); break; case APC_CMD_ADD : if (idns != NULL) { IPFERROR(80007); error = EEXIST; break; } if (tmp == NULL) { IPFERROR(80008); error = ENOMEM; break; } idns = tmp; tmp = NULL; idns->idns_namelen = ctl->apc_dsize; idns->idns_name = ctl->apc_data; idns->idns_pass = ctl->apc_arg; idns->idns_next = NULL; *idnsp = idns; ctl->apc_data = NULL; ctl->apc_dsize = 0; break; default : IPFERROR(80009); error = EINVAL; break; } RWLOCK_EXIT(&softd->ipf_p_dns_rwlock); if (tmp != NULL) { KFREE(tmp); tmp = NULL; } return error; } /* ARGSUSED */ int ipf_p_dns_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { dnsinfo_t *di; int dlen; if (fin->fin_v != 4) return -1; dlen = fin->fin_dlen - sizeof(udphdr_t); if (dlen < sizeof(ipf_dns_hdr_t)) { /* * No real DNS packet is smaller than that. */ return -1; } aps->aps_psiz = sizeof(dnsinfo_t); KMALLOCS(di, dnsinfo_t *, sizeof(dnsinfo_t)); if (di == NULL) { printf("ipf_dns_new:KMALLOCS(%d) failed\n", sizeof(*di)); return -1; } MUTEX_INIT(&di->dnsi_lock, "dns lock"); aps->aps_data = di; dlen = fin->fin_dlen - sizeof(udphdr_t); COPYDATA(fin->fin_m, fin->fin_hlen + sizeof(udphdr_t), MIN(dlen, sizeof(di->dnsi_buffer)), di->dnsi_buffer); di->dnsi_id = (di->dnsi_buffer[0] << 8) | di->dnsi_buffer[1]; return 0; } /* ARGSUSED */ void ipf_p_dns_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { #ifdef USE_MUTEXES dnsinfo_t *di = aps->aps_data; MUTEX_DESTROY(&di->dnsi_lock); #endif KFREES(aps->aps_data, aps->aps_psiz); aps->aps_data = NULL; aps->aps_psiz = 0; } /* * Tries to match the base string (in our ACL) with the query from a packet. */ int ipf_p_dns_match_names(idns, query, qlen) ipf_dns_filter_t *idns; char *query; int qlen; { int blen; char *base; blen = idns->idns_namelen; base = idns->idns_name; if (blen > qlen) return 1; if (blen == qlen) return strncasecmp(base, query, qlen); /* * If the base string string is shorter than the query, allow the * tail of the base to match the same length tail of the query *if*: * - the base string starts with a '*' (*cnn.com) * - the base string represents a domain (.cnn.com) * as otherwise it would not be possible to block just "cnn.com" * without also impacting "foocnn.com", etc. */ if (*base == '*') { base++; blen--; } else if (*base != '.') return 1; return strncasecmp(base, query + qlen - blen, blen); } int ipf_p_dns_get_name(softd, start, len, buffer, buflen) ipf_dns_softc_t *softd; char *start; int len; char *buffer; int buflen; { char *s, *t, clen; int slen, blen; s = start; t = buffer; slen = len; blen = buflen - 1; /* Always make room for trailing \0 */ while (*s != '\0') { clen = *s; if ((clen & 0xc0) == 0xc0) { /* Doesn't do compression */ softd->ipf_p_dns_compress++; return 0; } if (clen > slen) { softd->ipf_p_dns_toolong++; return 0; /* Does the name run off the end? */ } if ((clen + 1) > blen) { softd->ipf_p_dns_nospace++; return 0; /* Enough room for name+.? */ } s++; bcopy(s, t, clen); t += clen; s += clen; *t++ = '.'; slen -= clen; blen -= (clen + 1); } *(t - 1) = '\0'; return s - start; } int ipf_p_dns_allow_query(softd, dnsi) ipf_dns_softc_t *softd; dnsinfo_t *dnsi; { ipf_dns_filter_t *idns; int len; len = strlen(dnsi->dnsi_buffer); for (idns = softd->ipf_p_dns_list; idns != NULL; idns = idns->idns_next) if (ipf_p_dns_match_names(idns, dnsi->dnsi_buffer, len) == 0) return idns->idns_pass; return 0; } /* ARGSUSED */ int ipf_p_dns_inout(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipf_dns_softc_t *softd = arg; ipf_dns_hdr_t *dns; dnsinfo_t *di; char *data; int dlen, q, rc = 0; if (fin->fin_dlen < sizeof(*dns)) return APR_ERR(1); dns = (ipf_dns_hdr_t *)((char *)fin->fin_dp + sizeof(udphdr_t)); q = dns->dns_qdcount; data = (char *)(dns + 1); dlen = fin->fin_dlen - sizeof(*dns) - sizeof(udphdr_t); di = aps->aps_data; READ_ENTER(&softd->ipf_p_dns_rwlock); MUTEX_ENTER(&di->dnsi_lock); for (; (dlen > 0) && (q > 0); q--) { int len; len = ipf_p_dns_get_name(softd, data, dlen, di->dnsi_buffer, sizeof(di->dnsi_buffer)); if (len == 0) { rc = 1; break; } rc = ipf_p_dns_allow_query(softd, di); if (rc != 0) break; data += len; dlen -= len; } MUTEX_EXIT(&di->dnsi_lock); RWLOCK_EXIT(&softd->ipf_p_dns_rwlock); return APR_ERR(rc); } /* ARGSUSED */ int ipf_p_dns_match(fin, aps, nat) fr_info_t *fin; ap_session_t *aps; nat_t *nat; { dnsinfo_t *di = aps->aps_data; ipf_dns_hdr_t *dnh; if ((fin->fin_dlen < sizeof(u_short)) || (fin->fin_flx & FI_FRAG)) return -1; dnh = (ipf_dns_hdr_t *)((char *)fin->fin_dp + sizeof(udphdr_t)); if (((dnh->dns_id[0] << 8) | dnh->dns_id[1]) != di->dnsi_id) return -1; return 0; } diff --git a/sys/contrib/ipfilter/netinet/ip_dstlist.c b/sys/contrib/ipfilter/netinet/ip_dstlist.c index 4f2e3bb05a18..0c65e4cd6896 100644 --- a/sys/contrib/ipfilter/netinet/ip_dstlist.c +++ b/sys/contrib/ipfilter/netinet/ip_dstlist.c @@ -1,1344 +1,1344 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # include # undef _KERNEL #else # include # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) # include # endif #endif #include # include #include #if defined(_KERNEL) && !defined(__SVR4) # include #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD_version) # include #endif #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" /* END OF INCLUDES */ #ifdef HAS_SYS_MD5_H # include #else # include "md5.h" #endif #if !defined(lint) static const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $"; #endif typedef struct ipf_dstl_softc_s { ippool_dst_t *dstlist[LOOKUP_POOL_SZ]; ippool_dst_t **tails[LOOKUP_POOL_SZ]; ipf_dstl_stat_t stats; } ipf_dstl_softc_t; -static void *ipf_dstlist_soft_create __P((ipf_main_softc_t *)); -static void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *)); -static int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *)); -static void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *)); -static int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int, - void *, u_int)); -static size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *, - iplookupflush_t *)); -static int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int, - void *)); -static int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, - ipflookupiter_t *)); -static int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *, - iplookupop_t *, int)); -static int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *, - iplookupop_t *, int)); -static int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *)); -static void *ipf_dstlist_table_find __P((void *, int, char *)); -static void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *)); -static void ipf_dstlist_table_remove __P((ipf_main_softc_t *, - ipf_dstl_softc_t *, ippool_dst_t *)); -static void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *, - ippool_dst_t *)); -static ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *)); -static void *ipf_dstlist_select_ref __P((void *, int, char *)); -static void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *)); -static int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *)); -static void ipf_dstlist_expire __P((ipf_main_softc_t *, void *)); -static void ipf_dstlist_sync __P((ipf_main_softc_t *, void *)); +static void *ipf_dstlist_soft_create(ipf_main_softc_t *); +static void ipf_dstlist_soft_destroy(ipf_main_softc_t *, void *); +static int ipf_dstlist_soft_init(ipf_main_softc_t *, void *); +static void ipf_dstlist_soft_fini(ipf_main_softc_t *, void *); +static int ipf_dstlist_addr_find(ipf_main_softc_t *, void *, int, + void *, u_int); +static size_t ipf_dstlist_flush(ipf_main_softc_t *, void *, + iplookupflush_t *); +static int ipf_dstlist_iter_deref(ipf_main_softc_t *, void *, int, int, + void *); +static int ipf_dstlist_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, + ipflookupiter_t *); +static int ipf_dstlist_node_add(ipf_main_softc_t *, void *, + iplookupop_t *, int); +static int ipf_dstlist_node_del(ipf_main_softc_t *, void *, + iplookupop_t *, int); +static int ipf_dstlist_stats_get(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_dstlist_table_add(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_dstlist_table_del(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_dstlist_table_deref(ipf_main_softc_t *, void *, void *); +static void *ipf_dstlist_table_find(void *, int, char *); +static void ipf_dstlist_table_free(ipf_dstl_softc_t *, ippool_dst_t *); +static void ipf_dstlist_table_remove(ipf_main_softc_t *, + ipf_dstl_softc_t *, ippool_dst_t *); +static void ipf_dstlist_table_clearnodes(ipf_dstl_softc_t *, + ippool_dst_t *); +static ipf_dstnode_t *ipf_dstlist_select(fr_info_t *, ippool_dst_t *); +static void *ipf_dstlist_select_ref(void *, int, char *); +static void ipf_dstlist_node_free(ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *); +static int ipf_dstlist_node_deref(void *, ipf_dstnode_t *); +static void ipf_dstlist_expire(ipf_main_softc_t *, void *); +static void ipf_dstlist_sync(ipf_main_softc_t *, void *); ipf_lookup_t ipf_dstlist_backend = { IPLT_DSTLIST, ipf_dstlist_soft_create, ipf_dstlist_soft_destroy, ipf_dstlist_soft_init, ipf_dstlist_soft_fini, ipf_dstlist_addr_find, ipf_dstlist_flush, ipf_dstlist_iter_deref, ipf_dstlist_iter_next, ipf_dstlist_node_add, ipf_dstlist_node_del, ipf_dstlist_stats_get, ipf_dstlist_table_add, ipf_dstlist_table_del, ipf_dstlist_table_deref, ipf_dstlist_table_find, ipf_dstlist_select_ref, ipf_dstlist_select_node, ipf_dstlist_expire, ipf_dstlist_sync }; /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_create */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocating a chunk of memory filled with 0's is enough for the current */ /* soft context used with destination lists. */ /* ------------------------------------------------------------------------ */ static void * ipf_dstlist_soft_create(softc) ipf_main_softc_t *softc; { ipf_dstl_softc_t *softd; int i; KMALLOC(softd, ipf_dstl_softc_t *); if (softd == NULL) { IPFERROR(120028); return NULL; } bzero((char *)softd, sizeof(*softd)); for (i = 0; i <= IPL_LOGMAX; i++) softd->tails[i] = &softd->dstlist[i]; return softd; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* For destination lists, the only thing we have to do when destroying the */ /* soft context is free it! */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_dstl_softc_t *softd = arg; KFREE(softd); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There is currently no soft context for destination list management. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There is currently no soft context for destination list management. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_dstl_softc_t *softd = arg; int i; for (i = -1; i <= IPL_LOGMAX; i++) { while (softd->dstlist[i + 1] != NULL) { ipf_dstlist_table_remove(softc, softd, softd->dstlist[i + 1]); } } ASSERT(softd->stats.ipls_numderefnodes == 0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_addr_find */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg1(I) - pointer to local context to use */ /* arg2(I) - pointer to local context to use */ /* arg3(I) - pointer to local context to use */ /* arg4(I) - pointer to local context to use */ /* */ /* There is currently no such thing as searching a destination list for an */ /* address so this function becomes a no-op. Its presence is required as */ /* ipf_lookup_res_name() stores the "addr_find" function pointer in the */ /* pointer passed in to it as funcptr, although it could be a generic null- */ /* op function rather than a specific one. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int ipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4) ipf_main_softc_t *softc; void *arg1, *arg3; int arg2; u_int arg4; { return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_flush */ /* Returns: int - number of objects deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* fop(I) - pointer to lookup flush operation data */ /* */ /* Flush all of the destination tables that match the data passed in with */ /* the iplookupflush_t. There are two ways to match objects: the device for */ /* which they are to be used with and their name. */ /* ------------------------------------------------------------------------ */ static size_t ipf_dstlist_flush(softc, arg, fop) ipf_main_softc_t *softc; void *arg; iplookupflush_t *fop; { ipf_dstl_softc_t *softd = arg; ippool_dst_t *node, *next; int n, i; for (n = 0, i = -1; i <= IPL_LOGMAX; i++) { if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i) continue; for (node = softd->dstlist[i + 1]; node != NULL; node = next) { next = node->ipld_next; if ((*fop->iplf_name != '\0') && strncmp(fop->iplf_name, node->ipld_name, FR_GROUPLEN)) continue; ipf_dstlist_table_remove(softc, softd, node); n++; } } return n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_iter_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* otype(I) - type of data structure to iterate through */ /* unit(I) - device we are working with */ /* data(I) - address of object in kernel space */ /* */ /* This function is called when the iteration token is being free'd and is */ /* responsible for dropping the reference count of the structure it points */ /* to. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_iter_deref(softc, arg, otype, unit, data) ipf_main_softc_t *softc; void *arg; int otype, unit; void *data; { if (data == NULL) { IPFERROR(120001); return EINVAL; } if (unit < -1 || unit > IPL_LOGMAX) { IPFERROR(120002); return EINVAL; } switch (otype) { case IPFLOOKUPITER_LIST : ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data); break; case IPFLOOKUPITER_NODE : ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data); break; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_iter_next */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* */ /* This function is responsible for either selecting the next destination */ /* list or node on a destination list to be returned as a user process */ /* iterates through the list of destination lists or nodes. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_iter_next(softc, arg, token, iter) ipf_main_softc_t *softc; void *arg; ipftoken_t *token; ipflookupiter_t *iter; { ipf_dstnode_t zn, *nextnode = NULL, *node = NULL; ippool_dst_t zero, *next = NULL, *dsttab = NULL; ipf_dstl_softc_t *softd = arg; int err = 0; void *hint; switch (iter->ili_otype) { case IPFLOOKUPITER_LIST : dsttab = token->ipt_data; if (dsttab == NULL) { next = softd->dstlist[(int)iter->ili_unit + 1]; } else { next = dsttab->ipld_next; } if (next != NULL) { ATOMIC_INC32(next->ipld_ref); token->ipt_data = next; hint = next->ipld_next; } else { bzero((char *)&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; hint = NULL; } break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { dsttab = ipf_dstlist_table_find(arg, iter->ili_unit, iter->ili_name); if (dsttab == NULL) { IPFERROR(120004); err = ESRCH; nextnode = NULL; } else { if (dsttab->ipld_dests == NULL) nextnode = NULL; else nextnode = *dsttab->ipld_dests; dsttab = NULL; } } else { nextnode = node->ipfd_next; } if (nextnode != NULL) { MUTEX_ENTER(&nextnode->ipfd_lock); nextnode->ipfd_ref++; MUTEX_EXIT(&nextnode->ipfd_lock); token->ipt_data = nextnode; hint = nextnode->ipfd_next; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; hint = NULL; } break; default : IPFERROR(120003); err = EINVAL; break; } if (err != 0) return err; switch (iter->ili_otype) { case IPFLOOKUPITER_LIST : if (dsttab != NULL) ipf_dstlist_table_deref(softc, arg, dsttab); err = COPYOUT(next, iter->ili_data, sizeof(*next)); if (err != 0) { IPFERROR(120005); err = EFAULT; } break; case IPFLOOKUPITER_NODE : if (node != NULL) ipf_dstlist_node_deref(arg, node); err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode)); if (err != 0) { IPFERROR(120006); err = EFAULT; } break; } if (hint == NULL) ipf_token_mark_complete(token); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Add a new node to a destination list. To do this, we only copy in the */ /* frdest_t structure because that contains the only data required from the */ /* application to create a new node. The frdest_t doesn't contain the name */ /* itself. When loading filter rules, fd_name is a 'pointer' to the name. */ /* In this case, the 'pointer' does not work, instead it is the length of */ /* the name and the name is immediately following the frdest_t structure. */ /* fd_name must include the trailing \0, so it should be strlen(str) + 1. */ /* For simple sanity checking, an upper bound on the size of fd_name is */ /* imposed - 128. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_node_add(softc, arg, op, uid) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; int uid; { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node, **nodes; ippool_dst_t *d; frdest_t dest; int err; if (op->iplo_size < sizeof(frdest_t)) { IPFERROR(120007); return EINVAL; } err = COPYIN(op->iplo_struct, &dest, sizeof(dest)); if (err != 0) { IPFERROR(120009); return EFAULT; } d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120010); return ESRCH; } switch (dest.fd_addr.adf_family) { case AF_INET : case AF_INET6 : break; default : IPFERROR(120019); return EINVAL; } if (dest.fd_name < -1 || dest.fd_name > 128) { IPFERROR(120018); return EINVAL; } KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name); if (node == NULL) { softd->stats.ipls_nomem++; IPFERROR(120008); return ENOMEM; } bzero((char *)node, sizeof(*node) + dest.fd_name); bcopy(&dest, &node->ipfd_dest, sizeof(dest)); node->ipfd_size = sizeof(*node) + dest.fd_name; if (dest.fd_name > 0) { /* * fd_name starts out as the length of the string to copy * in (including \0) and ends up being the offset from * fd_names (0). */ err = COPYIN((char *)op->iplo_struct + sizeof(dest), node->ipfd_names, dest.fd_name); if (err != 0) { IPFERROR(120017); KFREES(node, node->ipfd_size); return EFAULT; } node->ipfd_dest.fd_name = 0; } else { node->ipfd_dest.fd_name = -1; } if (d->ipld_nodes == d->ipld_maxnodes) { KMALLOCS(nodes, ipf_dstnode_t **, sizeof(*nodes) * (d->ipld_maxnodes + 1)); if (nodes == NULL) { softd->stats.ipls_nomem++; IPFERROR(120022); KFREES(node, node->ipfd_size); return ENOMEM; } if (d->ipld_dests != NULL) { bcopy(d->ipld_dests, nodes, sizeof(*nodes) * d->ipld_maxnodes); KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes); nodes[0]->ipfd_pnext = nodes; } d->ipld_dests = nodes; d->ipld_maxnodes++; } d->ipld_dests[d->ipld_nodes] = node; d->ipld_nodes++; if (d->ipld_nodes == 1) { node->ipfd_pnext = d->ipld_dests; } else if (d->ipld_nodes > 1) { node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next; } *node->ipfd_pnext = node; MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock"); node->ipfd_uid = uid; node->ipfd_ref = 1; if (node->ipfd_dest.fd_name == 0) (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET); #ifdef USE_INET6 if (node->ipfd_dest.fd_name == 0 && node->ipfd_dest.fd_ptr == (void *)-1) (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET6); #endif softd->stats.ipls_numnodes++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* node(I) - pointer to destionation node to free */ /* */ /* Dereference the use count by one. If it drops to zero then we can assume */ /* that it has been removed from any lists/tables and is ripe for freeing. */ /* The pointer to context is required for the purpose of maintaining */ /* statistics. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_node_deref(arg, node) void *arg; ipf_dstnode_t *node; { ipf_dstl_softc_t *softd = arg; int ref; MUTEX_ENTER(&node->ipfd_lock); ref = --node->ipfd_ref; MUTEX_EXIT(&node->ipfd_lock); if (ref > 0) return 0; if ((node->ipfd_flags & IPDST_DELETE) != 0) softd->stats.ipls_numderefnodes--; MUTEX_DESTROY(&node->ipfd_lock); KFREES(node, node->ipfd_size); softd->stats.ipls_numnodes--; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* */ /* Look for a matching destination node on the named table and free it if */ /* found. Because the name embedded in the frdest_t is variable in length, */ /* it is necessary to allocate some memory locally, to complete this op. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_node_del(softc, arg, op, uid) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; int uid; { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node; frdest_t frd, *temp; ippool_dst_t *d; size_t size; int err; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120012); return ESRCH; } err = COPYIN(op->iplo_struct, &frd, sizeof(frd)); if (err != 0) { IPFERROR(120011); return EFAULT; } size = sizeof(*temp) + frd.fd_name; KMALLOCS(temp, frdest_t *, size); if (temp == NULL) { softd->stats.ipls_nomem++; IPFERROR(120026); return ENOMEM; } err = COPYIN(op->iplo_struct, temp, size); if (err != 0) { IPFERROR(120027); KFREES(temp, size); return EFAULT; } MUTEX_ENTER(&d->ipld_lock); for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) { if ((uid != 0) && (node->ipfd_uid != uid)) continue; if (node->ipfd_size != size) continue; if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6, size - offsetof(frdest_t, fd_ip6))) { ipf_dstlist_node_free(softd, d, node); MUTEX_EXIT(&d->ipld_lock); KFREES(temp, size); return 0; } } MUTEX_EXIT(&d->ipld_lock); KFREES(temp, size); return ESRCH; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_free */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* node(I) - pointer to node to free */ /* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */ /* */ /* Free the destination node by first removing it from any lists and then */ /* checking if this was the last reference held to the object. While the */ /* array of pointers to nodes is compacted, its size isn't reduced (by way */ /* of allocating a new smaller one and copying) because the belief is that */ /* it is likely the array will again reach that size. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_node_free(softd, d, node) ipf_dstl_softc_t *softd; ippool_dst_t *d; ipf_dstnode_t *node; { int i; /* * Compact the array of pointers to nodes. */ for (i = 0; i < d->ipld_nodes; i++) if (d->ipld_dests[i] == node) break; if (d->ipld_nodes - i > 1) { bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i], sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1)); } d->ipld_nodes--; if (node->ipfd_pnext != NULL) *node->ipfd_pnext = node->ipfd_next; if (node->ipfd_next != NULL) node->ipfd_next->ipfd_pnext = node->ipfd_pnext; node->ipfd_pnext = NULL; node->ipfd_next = NULL; if ((node->ipfd_flags & IPDST_DELETE) == 0) { softd->stats.ipls_numderefnodes++; node->ipfd_flags |= IPDST_DELETE; } ipf_dstlist_node_deref(softd, node); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_stats_get */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Return the current statistics for destination lists. This may be for all */ /* of them or just information pertaining to a particular table. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int ipf_dstlist_stats_get(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ipf_dstl_softc_t *softd = arg; ipf_dstl_stat_t stats; int unit, i, err = 0; if (op->iplo_size != sizeof(ipf_dstl_stat_t)) { IPFERROR(120023); return EINVAL; } stats = softd->stats; unit = op->iplo_unit; if (unit == IPL_LOGALL) { for (i = 0; i <= IPL_LOGMAX; i++) stats.ipls_list[i] = softd->dstlist[i]; } else if (unit >= 0 && unit <= IPL_LOGMAX) { void *ptr; if (op->iplo_name[0] != '\0') ptr = ipf_dstlist_table_find(softd, unit, op->iplo_name); else ptr = softd->dstlist[unit + 1]; stats.ipls_list[unit] = ptr; } else { IPFERROR(120024); err = EINVAL; } if (err == 0) { err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); if (err != 0) { IPFERROR(120025); return EFAULT; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Add a new destination table to the list of those available for the given */ /* device. Because we seldom operate on these objects (find/add/delete), */ /* they are just kept in a simple linked list. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_table_add(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ipf_dstl_softc_t *softd = arg; ippool_dst_t user, *d, *new; int unit, err; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d != NULL) { IPFERROR(120013); return EEXIST; } err = COPYIN(op->iplo_struct, &user, sizeof(user)); if (err != 0) { IPFERROR(120021); return EFAULT; } KMALLOC(new, ippool_dst_t *); if (new == NULL) { softd->stats.ipls_nomem++; IPFERROR(120014); return ENOMEM; } bzero((char *)new, sizeof(*new)); MUTEX_INIT(&new->ipld_lock, "ipf dst table lock"); strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN); unit = op->iplo_unit; new->ipld_unit = unit; new->ipld_policy = user.ipld_policy; new->ipld_seed = ipf_random(); new->ipld_ref = 1; new->ipld_pnext = softd->tails[unit + 1]; *softd->tails[unit + 1] = new; softd->tails[unit + 1] = &new->ipld_next; softd->stats.ipls_numlists++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Find a named destinstion list table and delete it. If there are other */ /* references to it, the caller isn't told. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_table_del(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ippool_dst_t *d; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120015); return ESRCH; } if (d->ipld_dests != NULL) { IPFERROR(120016); return EBUSY; } ipf_dstlist_table_remove(softc, arg, d); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_remove */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* */ /* Remove a given destination list from existance. While the IPDST_DELETE */ /* flag is set every time we call this function and the reference count is */ /* non-zero, the "numdereflists" counter is always incremented because the */ /* decision about whether it will be freed or not is not made here. This */ /* means that the only action the code can take here is to treat it as if */ /* it will become a detached. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_table_remove(softc, softd, d) ipf_main_softc_t *softc; ipf_dstl_softc_t *softd; ippool_dst_t *d; { if (softd->tails[d->ipld_unit + 1] == &d->ipld_next) softd->tails[d->ipld_unit + 1] = d->ipld_pnext; if (d->ipld_pnext != NULL) *d->ipld_pnext = d->ipld_next; if (d->ipld_next != NULL) d->ipld_next->ipld_pnext = d->ipld_pnext; d->ipld_pnext = NULL; d->ipld_next = NULL; ipf_dstlist_table_clearnodes(softd, d); softd->stats.ipls_numdereflists++; d->ipld_flags |= IPDST_DELETE; ipf_dstlist_table_deref(softc, softd, d); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_free */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* */ /* Free up a destination list data structure and any other memory that was */ /* directly allocated as part of creating it. Individual destination list */ /* nodes are not freed. It is assumed the caller will have already emptied */ /* the destination list. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_table_free(softd, d) ipf_dstl_softc_t *softd; ippool_dst_t *d; { MUTEX_DESTROY(&d->ipld_lock); if ((d->ipld_flags & IPDST_DELETE) != 0) softd->stats.ipls_numdereflists--; softd->stats.ipls_numlists--; if (d->ipld_dests != NULL) { KFREES(d->ipld_dests, d->ipld_maxnodes * sizeof(*d->ipld_dests)); } KFREE(d); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Drops the reference count on a destination list table object and free's */ /* it if 0 has been reached. */ /* ------------------------------------------------------------------------ */ static int ipf_dstlist_table_deref(softc, arg, table) ipf_main_softc_t *softc; void *arg; void *table; { ippool_dst_t *d = table; d->ipld_ref--; if (d->ipld_ref > 0) return d->ipld_ref; ipf_dstlist_table_free(arg, d); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_clearnodes */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* dst(I) - pointer to destination list */ /* */ /* Free all of the destination nodes attached to the given table. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_table_clearnodes(softd, dst) ipf_dstl_softc_t *softd; ippool_dst_t *dst; { ipf_dstnode_t *node; if (dst->ipld_dests == NULL) return; while ((node = *dst->ipld_dests) != NULL) { ipf_dstlist_node_free(softd, dst, node); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_find */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - device we are working with */ /* name(I) - destination table name to find */ /* */ /* Return a pointer to a destination table that matches the unit+name that */ /* is passed in. */ /* ------------------------------------------------------------------------ */ static void * ipf_dstlist_table_find(arg, unit, name) void *arg; int unit; char *name; { ipf_dstl_softc_t *softd = arg; ippool_dst_t *d; for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) { if ((d->ipld_unit == unit) && !strncmp(d->ipld_name, name, FR_GROUPLEN)) { return d; } } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select_ref */ /* Returns: void * - NULL = failure, else pointer to table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - device we are working with */ /* name(I) - destination table name to find */ /* */ /* Attempt to find a destination table that matches the name passed in and */ /* if successful, bump up the reference count on it because we intend to */ /* store the pointer to it somewhere else. */ /* ------------------------------------------------------------------------ */ static void * ipf_dstlist_select_ref(arg, unit, name) void *arg; int unit; char *name; { ippool_dst_t *d; d = ipf_dstlist_table_find(arg, unit, name); if (d != NULL) { MUTEX_ENTER(&d->ipld_lock); d->ipld_ref++; MUTEX_EXIT(&d->ipld_lock); } return d; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select */ /* Returns: void * - NULL = failure, else pointer to table */ /* Parameters: fin(I) - pointer to packet information */ /* d(I) - pointer to destination list */ /* */ /* Find the next node in the destination list to be used according to the */ /* defined policy. Of these, "connection" is the most expensive policy to */ /* implement as it always looks for the node with the least number of */ /* connections associated with it. */ /* */ /* The hashes exclude the port numbers so that all protocols map to the */ /* same destination. Otherwise, someone doing a ping would target a */ /* different server than their TCP connection, etc. MD-5 is used to */ /* transform the addressese into something random that the other end could */ /* not easily guess and use in an attack. ipld_seed introduces an unknown */ /* into the hash calculation to increase the difficult of an attacker */ /* guessing the bucket. */ /* */ /* One final comment: mixing different address families in a single pool */ /* will currently result in failures as the address family of the node is */ /* only matched up with that in the packet as the last step. While this can */ /* be coded around for the weighted connection and round-robin models, it */ /* cannot be supported for the hash/random models as they do not search and */ /* nor is the algorithm conducive to searching. */ /* ------------------------------------------------------------------------ */ static ipf_dstnode_t * ipf_dstlist_select(fin, d) fr_info_t *fin; ippool_dst_t *d; { ipf_dstnode_t *node, *sel; int connects; u_32_t hash[4]; MD5_CTX ctx; int family; int x; if (d == NULL || d->ipld_dests == NULL || *d->ipld_dests == NULL) return NULL; family = fin->fin_family; MUTEX_ENTER(&d->ipld_lock); switch (d->ipld_policy) { case IPLDP_ROUNDROBIN: sel = d->ipld_selected; if (sel == NULL) { sel = *d->ipld_dests; } else { sel = sel->ipfd_next; if (sel == NULL) sel = *d->ipld_dests; } break; case IPLDP_CONNECTION: if (d->ipld_selected == NULL) { sel = *d->ipld_dests; break; } sel = d->ipld_selected; connects = 0x7fffffff; node = sel->ipfd_next; if (node == NULL) node = *d->ipld_dests; while (node != d->ipld_selected) { if (node->ipfd_states == 0) { sel = node; break; } if (node->ipfd_states < connects) { sel = node; connects = node->ipfd_states; } node = node->ipfd_next; if (node == NULL) node = *d->ipld_dests; } break; case IPLDP_RANDOM : x = ipf_random() % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_HASHED : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_src6, sizeof(fin->fin_src6)); MD5Update(&ctx, (u_char *)&fin->fin_dst6, sizeof(fin->fin_dst6)); MD5Final((u_char *)hash, &ctx); x = ntohl(hash[0]) % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_SRCHASH : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_src6, sizeof(fin->fin_src6)); MD5Final((u_char *)hash, &ctx); x = ntohl(hash[0]) % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_DSTHASH : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_dst6, sizeof(fin->fin_dst6)); MD5Final((u_char *)hash, &ctx); x = ntohl(hash[0]) % d->ipld_nodes; sel = d->ipld_dests[x]; break; default : sel = NULL; break; } if (sel && sel->ipfd_dest.fd_addr.adf_family != family) sel = NULL; d->ipld_selected = sel; MUTEX_EXIT(&d->ipld_lock); return sel; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select_node */ /* Returns: int - -1 == failure, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* group(I) - destination pool to search */ /* addr(I) - pointer to store selected address */ /* pfdp(O) - pointer to storage for selected destination node */ /* */ /* This function is only responsible for obtaining the next IP address for */ /* use and storing it in the caller's address space (addr). "addr" is only */ /* used for storage if pfdp is NULL. No permanent reference is currently */ /* kept on the node. */ /* ------------------------------------------------------------------------ */ int ipf_dstlist_select_node(fin, group, addr, pfdp) fr_info_t *fin; void *group; u_32_t *addr; frdest_t *pfdp; { #ifdef USE_MUTEXES ipf_main_softc_t *softc = fin->fin_main_soft; #endif ippool_dst_t *d = group; ipf_dstnode_t *node; frdest_t *fdp; READ_ENTER(&softc->ipf_poolrw); node = ipf_dstlist_select(fin, d); if (node == NULL) { RWLOCK_EXIT(&softc->ipf_poolrw); return -1; } if (pfdp != NULL) { bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp)); } else { if (fin->fin_family == AF_INET) { addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; } else if (fin->fin_family == AF_INET6) { addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1]; addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2]; addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3]; } } fdp = &node->ipfd_dest; if (fdp->fd_ptr == NULL) fdp->fd_ptr = fin->fin_ifp; MUTEX_ENTER(&node->ipfd_lock); node->ipfd_states++; MUTEX_EXIT(&node->ipfd_lock); RWLOCK_EXIT(&softc->ipf_poolrw); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There are currently no objects to expire in destination lists. */ /* ------------------------------------------------------------------------ */ static void ipf_dstlist_expire(softc, arg) ipf_main_softc_t *softc; void *arg; { return; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_sync */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* When a network interface appears or disappears, we need to revalidate */ /* all of the network interface names that have been configured as a target */ /* in a destination list. */ /* ------------------------------------------------------------------------ */ void ipf_dstlist_sync(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node; ippool_dst_t *list; int i; int j; for (i = 0; i < IPL_LOGMAX; i++) { for (list = softd->dstlist[i]; list != NULL; list = list->ipld_next) { for (j = 0; j < list->ipld_maxnodes; j++) { node = list->ipld_dests[j]; if (node == NULL) continue; if (node->ipfd_dest.fd_name == -1) continue; (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET); } } } } diff --git a/sys/contrib/ipfilter/netinet/ip_dstlist.h b/sys/contrib/ipfilter/netinet/ip_dstlist.h index e2885e5c47ad..c4acd78dfd94 100644 --- a/sys/contrib/ipfilter/netinet/ip_dstlist.h +++ b/sys/contrib/ipfilter/netinet/ip_dstlist.h @@ -1,68 +1,68 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_dstlist.h,v 1.5.2.6 2012/07/22 08:04:23 darren_r Exp $ */ #ifndef __IP_DSTLIST_H__ #define __IP_DSTLIST_H__ typedef struct ipf_dstnode { struct ipf_dstnode *ipfd_next; struct ipf_dstnode **ipfd_pnext; ipfmutex_t ipfd_lock; frdest_t ipfd_dest; u_long ipfd_syncat; int ipfd_flags; int ipfd_size; int ipfd_states; int ipfd_ref; int ipfd_uid; char ipfd_names[1]; } ipf_dstnode_t; typedef enum ippool_policy_e { IPLDP_NONE = 0, IPLDP_ROUNDROBIN, IPLDP_CONNECTION, IPLDP_RANDOM, IPLDP_HASHED, IPLDP_SRCHASH, IPLDP_DSTHASH } ippool_policy_t; typedef struct ippool_dst { struct ippool_dst *ipld_next; struct ippool_dst **ipld_pnext; ipfmutex_t ipld_lock; int ipld_seed; int ipld_unit; int ipld_ref; int ipld_flags; int ipld_nodes; int ipld_maxnodes; ippool_policy_t ipld_policy; ipf_dstnode_t **ipld_dests; ipf_dstnode_t *ipld_selected; char ipld_name[FR_GROUPLEN]; } ippool_dst_t; #define IPDST_DELETE 0x01 typedef struct dstlist_stat_s { void *ipls_list[LOOKUP_POOL_SZ]; int ipls_numlists; u_long ipls_nomem; int ipls_numnodes; int ipls_numdereflists; int ipls_numderefnodes; } ipf_dstl_stat_t; extern ipf_lookup_t ipf_dstlist_backend; -extern int ipf_dstlist_select_node __P((fr_info_t *, void *, u_32_t *, - frdest_t *)); +extern int ipf_dstlist_select_node(fr_info_t *, void *, u_32_t *, + frdest_t *); #endif /* __IP_DSTLIST_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_fil.h b/sys/contrib/ipfilter/netinet/ip_fil.h index 0c7448492464..3121e94f1488 100644 --- a/sys/contrib/ipfilter/netinet/ip_fil.h +++ b/sys/contrib/ipfilter/netinet/ip_fil.h @@ -1,1862 +1,1862 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_fil.h 1.35 6/5/96 * $FreeBSD$ * Id: ip_fil.h,v 2.170.2.51 2007/10/10 09:48:03 darrenr Exp $ */ #ifndef __IP_FIL_H__ #define __IP_FIL_H__ # include #include "netinet/ip_compat.h" #include "netinet/ipf_rb.h" #if NETBSD_GE_REV(104040000) # include #endif #if defined(BSD) && defined(_KERNEL) # include #endif #ifndef SOLARIS # if defined(sun) && defined(__SVR4) # define SOLARIS 1 # else # define SOLARIS 0 # endif #endif #ifndef __P # define __P(x) x #endif # define SIOCADAFR _IOW('r', 60, struct ipfobj) # define SIOCRMAFR _IOW('r', 61, struct ipfobj) # define SIOCSETFF _IOW('r', 62, u_int) # define SIOCGETFF _IOR('r', 63, u_int) # define SIOCGETFS _IOWR('r', 64, struct ipfobj) # define SIOCIPFFL _IOWR('r', 65, int) # define SIOCIPFFB _IOR('r', 66, int) # define SIOCADIFR _IOW('r', 67, struct ipfobj) # define SIOCRMIFR _IOW('r', 68, struct ipfobj) # define SIOCSWAPA _IOR('r', 69, u_int) # define SIOCINAFR _IOW('r', 70, struct ipfobj) # define SIOCINIFR _IOW('r', 71, struct ipfobj) # define SIOCFRENB _IOW('r', 72, u_int) # define SIOCFRSYN _IOW('r', 73, u_int) # define SIOCFRZST _IOWR('r', 74, struct ipfobj) # define SIOCZRLST _IOWR('r', 75, struct ipfobj) # define SIOCAUTHW _IOWR('r', 76, struct ipfobj) # define SIOCAUTHR _IOWR('r', 77, struct ipfobj) # define SIOCSTAT1 _IOWR('r', 78, struct ipfobj) # define SIOCSTLCK _IOWR('r', 79, u_int) # define SIOCSTPUT _IOWR('r', 80, struct ipfobj) # define SIOCSTGET _IOWR('r', 81, struct ipfobj) # define SIOCSTGSZ _IOWR('r', 82, struct ipfobj) # define SIOCSTAT2 _IOWR('r', 83, struct ipfobj) # define SIOCSETLG _IOWR('r', 84, int) # define SIOCGETLG _IOWR('r', 85, int) # define SIOCFUNCL _IOWR('r', 86, struct ipfunc_resolve) # define SIOCIPFGETNEXT _IOWR('r', 87, struct ipfobj) # define SIOCIPFGET _IOWR('r', 88, struct ipfobj) # define SIOCIPFSET _IOWR('r', 89, struct ipfobj) # define SIOCIPFL6 _IOWR('r', 90, int) # define SIOCIPFITER _IOWR('r', 91, struct ipfobj) # define SIOCGENITER _IOWR('r', 92, struct ipfobj) # define SIOCGTABL _IOWR('r', 93, struct ipfobj) # define SIOCIPFDELTOK _IOWR('r', 94, int) # define SIOCLOOKUPITER _IOWR('r', 95, struct ipfobj) # define SIOCGTQTAB _IOWR('r', 96, struct ipfobj) # define SIOCMATCHFLUSH _IOWR('r', 97, struct ipfobj) # define SIOCIPFINTERROR _IOR('r', 98, int) #define SIOCADDFR SIOCADAFR #define SIOCDELFR SIOCRMAFR #define SIOCINSFR SIOCINAFR #define SIOCATHST SIOCSTAT1 #define SIOCGFRST SIOCSTAT2 struct ipscan; struct ifnet; struct ipf_main_softc_s; -typedef int (* lookupfunc_t) __P((struct ipf_main_softc_s *, void *, - int, void *, u_int)); +typedef int (* lookupfunc_t)(struct ipf_main_softc_s *, void *, + int, void *, u_int); /* * i6addr is used as a container for both IPv4 and IPv6 addresses, as well * as other types of objects, depending on its qualifier. */ typedef union i6addr { u_32_t i6[4]; struct in_addr in4; #ifdef USE_INET6 struct in6_addr in6; #endif void *vptr[2]; lookupfunc_t lptr[2]; struct { u_short type; u_short subtype; int name; } i6un; } i6addr_t; #define in4_addr in4.s_addr #define iplookupnum i6[1] #define iplookupname i6un.name #define iplookuptype i6un.type #define iplookupsubtype i6un.subtype /* * NOTE: These DO overlap the above on 64bit systems and this IS recognised. */ #define iplookupptr vptr[0] #define iplookupfunc lptr[1] #define I60(x) (((u_32_t *)(x))[0]) #define I61(x) (((u_32_t *)(x))[1]) #define I62(x) (((u_32_t *)(x))[2]) #define I63(x) (((u_32_t *)(x))[3]) #define HI60(x) ntohl(((u_32_t *)(x))[0]) #define HI61(x) ntohl(((u_32_t *)(x))[1]) #define HI62(x) ntohl(((u_32_t *)(x))[2]) #define HI63(x) ntohl(((u_32_t *)(x))[3]) #define IP6_EQ(a,b) ((I63(a) == I63(b)) && (I62(a) == I62(b)) && \ (I61(a) == I61(b)) && (I60(a) == I60(b))) #define IP6_NEQ(a,b) ((I63(a) != I63(b)) || (I62(a) != I62(b)) || \ (I61(a) != I61(b)) || (I60(a) != I60(b))) #define IP6_ISZERO(a) ((I60(a) | I61(a) | I62(a) | I63(a)) == 0) #define IP6_NOTZERO(a) ((I60(a) | I61(a) | I62(a) | I63(a)) != 0) #define IP6_ISONES(a) ((I63(a) == 0xffffffff) && (I62(a) == 0xffffffff) && \ (I61(a) == 0xffffffff) && (I60(a) == 0xffffffff)) #define IP6_GT(a,b) (ntohl(HI60(a)) > ntohl(HI60(b)) || \ (HI60(a) == HI60(b) && \ (ntohl(HI61(a)) > ntohl(HI61(b)) || \ (HI61(a) == HI61(b) && \ (ntohl(HI62(a)) > ntohl(HI62(b)) || \ (HI62(a) == HI62(b) && \ ntohl(HI63(a)) > ntohl(HI63(b)))))))) #define IP6_LT(a,b) (ntohl(HI60(a)) < ntohl(HI60(b)) || \ (HI60(a) == HI60(b) && \ (ntohl(HI61(a)) < ntohl(HI61(b)) || \ (HI61(a) == HI61(b) && \ (ntohl(HI62(a)) < ntohl(HI62(b)) || \ (HI62(a) == HI62(b) && \ ntohl(HI63(a)) < ntohl(HI63(b)))))))) #define NLADD(n,x) htonl(ntohl(n) + (x)) #define IP6_INC(a) \ do { u_32_t *_i6 = (u_32_t *)(a); \ _i6[3] = NLADD(_i6[3], 1); \ if (_i6[3] == 0) { \ _i6[2] = NLADD(_i6[2], 1); \ if (_i6[2] == 0) { \ _i6[1] = NLADD(_i6[1], 1); \ if (_i6[1] == 0) { \ _i6[0] = NLADD(_i6[0], 1); \ } \ } \ } \ } while (0) #define IP6_ADD(a,x,d) \ do { i6addr_t *_s = (i6addr_t *)(a); \ i6addr_t *_d = (i6addr_t *)(d); \ _d->i6[0] = NLADD(_s->i6[0], x); \ if (ntohl(_d->i6[0]) < ntohl(_s->i6[0])) { \ _d->i6[1] = NLADD(_d->i6[1], 1); \ if (ntohl(_d->i6[1]) < ntohl(_s->i6[1])) { \ _d->i6[2] = NLADD(_d->i6[2], 1); \ if (ntohl(_d->i6[2]) < ntohl(_s->i6[2])) { \ _d->i6[3] = NLADD(_d->i6[3], 1); \ } \ } \ } \ } while (0) #define IP6_AND(a,b,d) do { i6addr_t *_s1 = (i6addr_t *)(a); \ i6addr_t *_s2 = (i6addr_t *)(b); \ i6addr_t *_d = (i6addr_t *)(d); \ _d->i6[0] = _s1->i6[0] & _s2->i6[0]; \ _d->i6[1] = _s1->i6[1] & _s2->i6[1]; \ _d->i6[2] = _s1->i6[2] & _s2->i6[2]; \ _d->i6[3] = _s1->i6[3] & _s2->i6[3]; \ } while (0) #define IP6_ANDASSIGN(a,m) \ do { i6addr_t *_d = (i6addr_t *)(a); \ i6addr_t *_m = (i6addr_t *)(m); \ _d->i6[0] &= _m->i6[0]; \ _d->i6[1] &= _m->i6[1]; \ _d->i6[2] &= _m->i6[2]; \ _d->i6[3] &= _m->i6[3]; \ } while (0) #define IP6_MASKEQ(a,m,b) \ (((I60(a) & I60(m)) == I60(b)) && \ ((I61(a) & I61(m)) == I61(b)) && \ ((I62(a) & I62(m)) == I62(b)) && \ ((I63(a) & I63(m)) == I63(b))) #define IP6_MASKNEQ(a,m,b) \ (((I60(a) & I60(m)) != I60(b)) || \ ((I61(a) & I61(m)) != I61(b)) || \ ((I62(a) & I62(m)) != I62(b)) || \ ((I63(a) & I63(m)) != I63(b))) #define IP6_MERGE(a,b,c) \ do { i6addr_t *_d, *_s1, *_s2; \ _d = (i6addr_t *)(a); \ _s1 = (i6addr_t *)(b); \ _s2 = (i6addr_t *)(c); \ _d->i6[0] |= _s1->i6[0] & ~_s2->i6[0]; \ _d->i6[1] |= _s1->i6[1] & ~_s2->i6[1]; \ _d->i6[2] |= _s1->i6[2] & ~_s2->i6[2]; \ _d->i6[3] |= _s1->i6[3] & ~_s2->i6[3]; \ } while (0) #define IP6_MASK(a,b,c) \ do { i6addr_t *_d, *_s1, *_s2; \ _d = (i6addr_t *)(a); \ _s1 = (i6addr_t *)(b); \ _s2 = (i6addr_t *)(c); \ _d->i6[0] = _s1->i6[0] & ~_s2->i6[0]; \ _d->i6[1] = _s1->i6[1] & ~_s2->i6[1]; \ _d->i6[2] = _s1->i6[2] & ~_s2->i6[2]; \ _d->i6[3] = _s1->i6[3] & ~_s2->i6[3]; \ } while (0) #define IP6_SETONES(a) \ do { i6addr_t *_d = (i6addr_t *)(a); \ _d->i6[0] = 0xffffffff; \ _d->i6[1] = 0xffffffff; \ _d->i6[2] = 0xffffffff; \ _d->i6[3] = 0xffffffff; \ } while (0) typedef union ipso_u { u_short ipso_ripso[2]; u_32_t ipso_doi; } ipso_t; typedef struct fr_ip { u_32_t fi_v:4; /* IP version */ u_32_t fi_xx:4; /* spare */ u_32_t fi_tos:8; /* IP packet TOS */ u_32_t fi_ttl:8; /* IP packet TTL */ u_32_t fi_p:8; /* IP packet protocol */ u_32_t fi_optmsk; /* bitmask composed from IP options */ i6addr_t fi_src; /* source address from packet */ i6addr_t fi_dst; /* destination address from packet */ ipso_t fi_ipso; /* IP security options */ u_32_t fi_flx; /* packet flags */ u_32_t fi_tcpmsk; /* TCP options set/reset */ u_32_t fi_ports[2]; /* TCP ports */ u_char fi_tcpf; /* TCP flags */ u_char fi_sensitivity; u_char fi_xxx[2]; /* pad */ } fr_ip_t; /* * For use in fi_flx */ #define FI_TCPUDP 0x0001 /* TCP/UCP implied comparison*/ #define FI_OPTIONS 0x0002 #define FI_FRAG 0x0004 #define FI_SHORT 0x0008 #define FI_NATED 0x0010 #define FI_MULTICAST 0x0020 #define FI_BROADCAST 0x0040 #define FI_MBCAST 0x0080 #define FI_STATE 0x0100 #define FI_BADNAT 0x0200 #define FI_BAD 0x0400 #define FI_OOW 0x0800 /* Out of state window, else match */ #define FI_ICMPERR 0x1000 #define FI_FRAGBODY 0x2000 #define FI_BADSRC 0x4000 #define FI_LOWTTL 0x8000 #define FI_CMP 0x5cfe3 /* Not FI_FRAG,FI_NATED,FI_FRAGTAIL */ #define FI_ICMPCMP 0x0003 /* Flags we can check for ICMP error packets */ #define FI_WITH 0x5effe /* Not FI_TCPUDP */ #define FI_V6EXTHDR 0x10000 #define FI_COALESCE 0x20000 #define FI_NEWNAT 0x40000 #define FI_ICMPQUERY 0x80000 #define FI_ENCAP 0x100000 /* encap/decap with NAT */ #define FI_AH 0x200000 /* AH header present */ #define FI_DOCKSUM 0x10000000 /* Proxy wants L4 recalculation */ #define FI_NOCKSUM 0x20000000 /* don't do a L4 checksum validation */ #define FI_NOWILD 0x40000000 /* Do not do wildcard searches */ #define FI_IGNORE 0x80000000 #define fi_secmsk fi_ipso.ipso_ripso[0] #define fi_auth fi_ipso.ipso_ripso[1] #define fi_doi fi_ipso.ipso_doi #define fi_saddr fi_src.in4.s_addr #define fi_daddr fi_dst.in4.s_addr #define fi_srcnum fi_src.iplookupnum #define fi_dstnum fi_dst.iplookupnum #define fi_srcname fi_src.iplookupname #define fi_dstname fi_dst.iplookupname #define fi_srctype fi_src.iplookuptype #define fi_dsttype fi_dst.iplookuptype #define fi_srcsubtype fi_src.iplookupsubtype #define fi_dstsubtype fi_dst.iplookupsubtype #define fi_srcptr fi_src.iplookupptr #define fi_dstptr fi_dst.iplookupptr #define fi_srcfunc fi_src.iplookupfunc #define fi_dstfunc fi_dst.iplookupfunc /* * These are both used by the state and NAT code to indicate that one port or * the other should be treated as a wildcard. * NOTE: When updating, check bit masks in ip_state.h and update there too. */ #define SI_W_SPORT 0x00000100 #define SI_W_DPORT 0x00000200 #define SI_WILDP (SI_W_SPORT|SI_W_DPORT) #define SI_W_SADDR 0x00000400 #define SI_W_DADDR 0x00000800 #define SI_WILDA (SI_W_SADDR|SI_W_DADDR) #define SI_NEWFR 0x00001000 #define SI_CLONE 0x00002000 #define SI_CLONED 0x00004000 #define SI_NEWCLONE 0x00008000 typedef struct { u_short fda_ports[2]; u_char fda_tcpf; /* TCP header flags (SYN, ACK, etc) */ } frdat_t; typedef enum fr_breasons_e { FRB_BLOCKED = 0, FRB_LOGFAIL = 1, FRB_PPSRATE = 2, FRB_JUMBO = 3, FRB_MAKEFRIP = 4, FRB_STATEADD = 5, FRB_UPDATEIPID = 6, FRB_LOGFAIL2 = 7, FRB_DECAPFRIP = 8, FRB_AUTHNEW = 9, FRB_AUTHCAPTURE = 10, FRB_COALESCE = 11, FRB_PULLUP = 12, FRB_AUTHFEEDBACK = 13, FRB_BADFRAG = 14, FRB_NATV4 = 15, FRB_NATV6 = 16, } fr_breason_t; #define FRB_MAX_VALUE 16 typedef enum ipf_cksum_e { FI_CK_BAD = -1, FI_CK_NEEDED = 0, FI_CK_SUMOK = 1, FI_CK_L4PART = 2, FI_CK_L4FULL = 4 } ipf_cksum_t; typedef struct fr_info { void *fin_main_soft; void *fin_ifp; /* interface packet is `on' */ struct frentry *fin_fr; /* last matching rule */ int fin_out; /* in or out ? 1 == out, 0 == in */ fr_ip_t fin_fi; /* IP Packet summary */ frdat_t fin_dat; /* TCP/UDP ports, ICMP code/type */ int fin_dlen; /* length of data portion of packet */ int fin_plen; u_32_t fin_rule; /* rule # last matched */ u_short fin_hlen; /* length of IP header in bytes */ char fin_group[FR_GROUPLEN]; /* group number, -1 for none */ void *fin_dp; /* start of data past IP header */ /* * Fields after fin_dp aren't used for compression of log records. * fin_fi contains the IP version (fin_family) * fin_rule isn't included because adding a new rule can change it but * not change fin_fr. fin_rule is the rule number reported. * It isn't necessary to include fin_crc because that is checked * for explicitly, before calling bcmp. */ u_32_t fin_crc; /* Simple calculation for logging */ int fin_family; /* AF_INET, etc. */ int fin_icode; /* ICMP error to return */ int fin_mtu; /* MTU input for ICMP need-frag */ int fin_rev; /* state only: 1 = reverse */ int fin_ipoff; /* # bytes from buffer start to hdr */ u_32_t fin_id; /* IP packet id field */ u_short fin_l4hlen; /* length of L4 header, if known */ u_short fin_off; int fin_depth; /* Group nesting depth */ int fin_error; /* Error code to return */ ipf_cksum_t fin_cksum; /* -1 = bad, 1 = good, 0 = not done */ fr_breason_t fin_reason; /* why auto blocked */ u_int fin_pktnum; void *fin_nattag; struct frdest *fin_dif; struct frdest *fin_tif; union { ip_t *fip_ip; #ifdef USE_INET6 ip6_t *fip_ip6; #endif } fin_ipu; mb_t **fin_mp; /* pointer to pointer to mbuf */ mb_t *fin_m; /* pointer to mbuf */ #ifdef MENTAT mb_t *fin_qfm; /* pointer to mblk where pkt starts */ void *fin_qpi; char fin_ifname[LIFNAMSIZ]; #endif void *fin_fraghdr; /* pointer to start of ipv6 frag hdr */ } fr_info_t; #define fin_ip fin_ipu.fip_ip #define fin_ip6 fin_ipu.fip_ip6 #define fin_v fin_fi.fi_v #define fin_p fin_fi.fi_p #define fin_flx fin_fi.fi_flx #define fin_optmsk fin_fi.fi_optmsk #define fin_secmsk fin_fi.fi_secmsk #define fin_doi fin_fi.fi_doi #define fin_auth fin_fi.fi_auth #define fin_src fin_fi.fi_src.in4 #define fin_saddr fin_fi.fi_saddr #define fin_dst fin_fi.fi_dst.in4 #define fin_daddr fin_fi.fi_daddr #define fin_data fin_fi.fi_ports #define fin_sport fin_fi.fi_ports[0] #define fin_dport fin_fi.fi_ports[1] #define fin_tcpf fin_fi.fi_tcpf #define fin_src6 fin_fi.fi_src #define fin_dst6 fin_fi.fi_dst #define fin_srcip6 fin_fi.fi_src.in6 #define fin_dstip6 fin_fi.fi_dst.in6 #define IPF_IN 0 #define IPF_OUT 1 -typedef struct frentry *(*ipfunc_t) __P((fr_info_t *, u_32_t *)); -typedef int (*ipfuncinit_t) __P((struct ipf_main_softc_s *, struct frentry *)); +typedef struct frentry *(*ipfunc_t)(fr_info_t *, u_32_t *); +typedef int (*ipfuncinit_t)(struct ipf_main_softc_s *, struct frentry *); typedef struct ipfunc_resolve { char ipfu_name[32]; ipfunc_t ipfu_addr; ipfuncinit_t ipfu_init; ipfuncinit_t ipfu_fini; } ipfunc_resolve_t; /* * Size for compares on fr_info structures */ #define FI_CSIZE offsetof(fr_info_t, fin_icode) #define FI_LCSIZE offsetof(fr_info_t, fin_dp) /* * Size for copying cache fr_info structure */ #define FI_COPYSIZE offsetof(fr_info_t, fin_dp) /* * Structure for holding IPFilter's tag information */ #define IPFTAG_LEN 16 typedef struct { union { u_32_t iptu_num[4]; char iptu_tag[IPFTAG_LEN]; } ipt_un; int ipt_not; } ipftag_t; #define ipt_tag ipt_un.iptu_tag #define ipt_num ipt_un.iptu_num /* * Structure to define address for pool lookups. */ typedef struct { u_char adf_len; sa_family_t adf_family; u_char adf_xxx[2]; i6addr_t adf_addr; } addrfamily_t; RBI_LINK(ipf_rb, host_node_s); typedef struct host_node_s { RBI_FIELD(ipf_rb) hn_entry; addrfamily_t hn_addr; int hn_active; } host_node_t; typedef RBI_HEAD(ipf_rb, host_node_s) ipf_rb_head_t; typedef struct host_track_s { ipf_rb_head_t ht_root; int ht_max_nodes; int ht_max_per_node; int ht_netmask; int ht_cur_nodes; } host_track_t; typedef enum fr_dtypes_e { FRD_NORMAL = 0, FRD_DSTLIST } fr_dtypes_t; /* * This structure is used to hold information about the next hop for where * to forward a packet. */ typedef struct frdest { void *fd_ptr; addrfamily_t fd_addr; fr_dtypes_t fd_type; int fd_name; } frdest_t; #define fd_ip6 fd_addr.adf_addr #define fd_ip fd_ip6.in4 typedef enum fr_ctypes_e { FR_NONE = 0, FR_EQUAL, FR_NEQUAL, FR_LESST, FR_GREATERT, FR_LESSTE, FR_GREATERTE, FR_OUTRANGE, FR_INRANGE, FR_INCRANGE } fr_ctypes_t; /* * This structure holds information about a port comparison. */ typedef struct frpcmp { fr_ctypes_t frp_cmp; /* data for port comparisons */ u_32_t frp_port; /* low port for <> and >< */ u_32_t frp_top; /* high port for <> and >< */ } frpcmp_t; /* * Structure containing all the relevant TCP/UDP things that can be checked in * a filter rule. */ typedef struct frtuc { u_char ftu_tcpfm; /* tcp flags mask */ u_char ftu_tcpf; /* tcp flags */ frpcmp_t ftu_src; /* source port */ frpcmp_t ftu_dst; /* destination port */ } frtuc_t; #define ftu_scmp ftu_src.frp_cmp #define ftu_dcmp ftu_dst.frp_cmp #define ftu_sport ftu_src.frp_port #define ftu_dport ftu_dst.frp_port #define ftu_stop ftu_src.frp_top #define ftu_dtop ftu_dst.frp_top #define FR_TCPFMAX 0x3f typedef enum fr_atypes_e { FRI_NONE = -1, /* For LHS of NAT */ FRI_NORMAL = 0, /* Normal address */ FRI_DYNAMIC, /* dynamic address */ FRI_LOOKUP, /* address is a pool # */ FRI_RANGE, /* address/mask is a range */ FRI_NETWORK, /* network address from if */ FRI_BROADCAST, /* broadcast address from if */ FRI_PEERADDR, /* Peer address for P-to-P */ FRI_NETMASKED, /* network address with netmask from if */ FRI_SPLIT, /* For NAT compatibility */ FRI_INTERFACE /* address is based on interface name */ } fr_atypes_t; /* * This structure makes up what is considered to be the IPFilter specific * matching components of a filter rule, as opposed to the data structures * used to define the result which are in frentry_t and not here. */ typedef struct fripf { fr_ip_t fri_ip; fr_ip_t fri_mip; /* mask structure */ u_short fri_icmpm; /* data for ICMP packets (mask) */ u_short fri_icmp; frtuc_t fri_tuc; fr_atypes_t fri_satype; /* addres type */ fr_atypes_t fri_datype; /* addres type */ int fri_sifpidx; /* doing dynamic addressing */ int fri_difpidx; /* index into fr_ifps[] to use when */ } fripf_t; #define fri_dlookup fri_mip.fi_dst #define fri_slookup fri_mip.fi_src #define fri_dstnum fri_mip.fi_dstnum #define fri_srcnum fri_mip.fi_srcnum #define fri_dstname fri_mip.fi_dstname #define fri_srcname fri_mip.fi_srcname #define fri_dstptr fri_mip.fi_dstptr #define fri_srcptr fri_mip.fi_srcptr typedef enum fr_rtypes_e { FR_T_NONE = 0, FR_T_IPF, /* IPF structures */ FR_T_BPFOPC, /* BPF opcode */ FR_T_CALLFUNC, /* callout to function in fr_func only */ FR_T_COMPIPF, /* compiled C code */ FR_T_IPFEXPR, /* IPF expression */ FR_T_BUILTIN = 0x40000000, /* rule is in kernel space */ FR_T_IPF_BUILTIN, FR_T_BPFOPC_BUILTIN, FR_T_CALLFUNC_BUILTIN, FR_T_COMPIPF_BUILTIN, FR_T_IPFEXPR_BUILTIN } fr_rtypes_t; -typedef struct frentry * (* frentfunc_t) __P((fr_info_t *)); +typedef struct frentry * (* frentfunc_t)(fr_info_t *); typedef struct frentry { ipfmutex_t fr_lock; struct frentry *fr_next; struct frentry **fr_pnext; struct frgroup *fr_grp; struct frgroup *fr_grphead; struct frgroup *fr_icmpgrp; struct ipscan *fr_isc; struct frentry *fr_dnext; /* 2 fr_die linked list pointers */ struct frentry **fr_pdnext; void *fr_ifas[4]; void *fr_ptr; /* for use with fr_arg */ int fr_comment; /* text comment for rule */ int fr_size; /* size of this structure */ int fr_ref; /* reference count */ int fr_statecnt; /* state count - for limit rules */ u_32_t fr_die; /* only used on loading the rule */ u_int fr_cksum; /* checksum on filter rules for performance */ /* * The line number from a file is here because we need to be able to * match the rule generated with ``grep rule ipf.conf | ipf -rf -'' * with the rule loaded using ``ipf -f ipf.conf'' - thus it can't be * on the other side of fr_func. */ int fr_flineno; /* line number from conf file */ /* * 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; /* * For PPS rate limiting * fr_lpu is used to always have the same size for this field, * allocating 64bits for seconds and 32bits for milliseconds. */ union { struct timeval frp_lastpkt; char frp_bytes[12]; } fr_lpu; int fr_curpps; union { void *fru_data; char *fru_caddr; fripf_t *fru_ipf; frentfunc_t fru_func; } fr_dun; /* * Fields after this may not change whilst in the kernel. */ ipfunc_t fr_func; /* call this function */ int fr_dsize; int fr_pps; fr_rtypes_t fr_type; u_32_t fr_flags; /* per-rule flags && options (see below) */ u_32_t fr_logtag; /* user defined log tag # */ u_32_t fr_collect; /* collection number */ u_int fr_arg; /* misc. numeric arg for rule */ u_int fr_loglevel; /* syslog log facility + priority */ u_char fr_family; u_char fr_icode; /* return ICMP code */ int fr_group; /* group to which this rule belongs */ int fr_grhead; /* group # which this rule starts */ int fr_isctag; int fr_rpc; /* XID Filtering */ ipftag_t fr_nattag; /* * These are all options related to stateful filtering */ host_track_t fr_srctrack; int fr_nostatelog; int fr_statemax; /* max reference count */ int fr_icmphead; /* ICMP group for state options */ u_int fr_age[2]; /* non-TCP state timeouts */ /* * These are compared separately. */ int fr_ifnames[4]; frdest_t fr_tifs[2]; /* "to"/"reply-to" interface */ frdest_t fr_dif; /* duplicate packet interface */ /* * How big is the name buffer at the end? */ int fr_namelen; char fr_names[1]; } frentry_t; #define fr_lastpkt fr_lpu.frp_lastpkt #define fr_caddr fr_dun.fru_caddr #define fr_data fr_dun.fru_data #define fr_dfunc fr_dun.fru_func #define fr_ipf fr_dun.fru_ipf #define fr_ip fr_ipf->fri_ip #define fr_mip fr_ipf->fri_mip #define fr_icmpm fr_ipf->fri_icmpm #define fr_icmp fr_ipf->fri_icmp #define fr_tuc fr_ipf->fri_tuc #define fr_satype fr_ipf->fri_satype #define fr_datype fr_ipf->fri_datype #define fr_sifpidx fr_ipf->fri_sifpidx #define fr_difpidx fr_ipf->fri_difpidx #define fr_proto fr_ip.fi_p #define fr_mproto fr_mip.fi_p #define fr_ttl fr_ip.fi_ttl #define fr_mttl fr_mip.fi_ttl #define fr_tos fr_ip.fi_tos #define fr_mtos fr_mip.fi_tos #define fr_tcpfm fr_tuc.ftu_tcpfm #define fr_tcpf fr_tuc.ftu_tcpf #define fr_scmp fr_tuc.ftu_scmp #define fr_dcmp fr_tuc.ftu_dcmp #define fr_dport fr_tuc.ftu_dport #define fr_sport fr_tuc.ftu_sport #define fr_stop fr_tuc.ftu_stop #define fr_dtop fr_tuc.ftu_dtop #define fr_dst fr_ip.fi_dst.in4 #define fr_dst6 fr_ip.fi_dst #define fr_daddr fr_ip.fi_dst.in4.s_addr #define fr_src fr_ip.fi_src.in4 #define fr_src6 fr_ip.fi_src #define fr_saddr fr_ip.fi_src.in4.s_addr #define fr_dmsk fr_mip.fi_dst.in4 #define fr_dmsk6 fr_mip.fi_dst #define fr_dmask fr_mip.fi_dst.in4.s_addr #define fr_smsk fr_mip.fi_src.in4 #define fr_smsk6 fr_mip.fi_src #define fr_smask fr_mip.fi_src.in4.s_addr #define fr_dstnum fr_ip.fi_dstnum #define fr_srcnum fr_ip.fi_srcnum #define fr_dlookup fr_ip.fi_dst #define fr_slookup fr_ip.fi_src #define fr_dstname fr_ip.fi_dstname #define fr_srcname fr_ip.fi_srcname #define fr_dsttype fr_ip.fi_dsttype #define fr_srctype fr_ip.fi_srctype #define fr_dstsubtype fr_ip.fi_dstsubtype #define fr_srcsubtype fr_ip.fi_srcsubtype #define fr_dstptr fr_mip.fi_dstptr #define fr_srcptr fr_mip.fi_srcptr #define fr_dstfunc fr_mip.fi_dstfunc #define fr_srcfunc fr_mip.fi_srcfunc #define fr_optbits fr_ip.fi_optmsk #define fr_optmask fr_mip.fi_optmsk #define fr_secbits fr_ip.fi_secmsk #define fr_secmask fr_mip.fi_secmsk #define fr_authbits fr_ip.fi_auth #define fr_authmask fr_mip.fi_auth #define fr_doi fr_ip.fi_doi #define fr_doimask fr_mip.fi_doi #define fr_flx fr_ip.fi_flx #define fr_mflx fr_mip.fi_flx #define fr_ifa fr_ifas[0] #define fr_oifa fr_ifas[2] #define fr_tif fr_tifs[0] #define fr_rif fr_tifs[1] #define FR_NOLOGTAG 0 #define FR_CMPSIZ (offsetof(struct frentry, fr_ifnames) - \ offsetof(struct frentry, fr_func)) #define FR_NAME(_f, _n) (_f)->fr_names + (_f)->_n #define FR_NUM(_a) (sizeof(_a) / sizeof(*_a)) /* * fr_flags */ #define FR_BLOCK 0x00001 /* do not allow packet to pass */ #define FR_PASS 0x00002 /* allow packet to pass */ #define FR_AUTH 0x00003 /* use authentication */ #define FR_PREAUTH 0x00004 /* require preauthentication */ #define FR_ACCOUNT 0x00005 /* Accounting rule */ #define FR_SKIP 0x00006 /* skip rule */ #define FR_DECAPSULATE 0x00008 /* decapsulate rule */ #define FR_CALL 0x00009 /* call rule */ #define FR_CMDMASK 0x0000f #define FR_LOG 0x00010 /* Log */ #define FR_LOGB 0x00011 /* Log-fail */ #define FR_LOGP 0x00012 /* Log-pass */ #define FR_LOGMASK (FR_LOG|FR_CMDMASK) #define FR_CALLNOW 0x00020 /* call another function (fr_func) if matches */ #define FR_NOTSRCIP 0x00040 #define FR_NOTDSTIP 0x00080 #define FR_QUICK 0x00100 /* match & stop processing list */ #define FR_KEEPFRAG 0x00200 /* keep fragment information */ #define FR_KEEPSTATE 0x00400 /* keep `connection' state information */ #define FR_FASTROUTE 0x00800 /* bypass normal routing */ #define FR_RETRST 0x01000 /* Return TCP RST packet - reset connection */ #define FR_RETICMP 0x02000 /* Return ICMP unreachable packet */ #define FR_FAKEICMP 0x03000 /* Return ICMP unreachable with fake source */ #define FR_OUTQUE 0x04000 /* outgoing packets */ #define FR_INQUE 0x08000 /* ingoing packets */ #define FR_LOGBODY 0x10000 /* Log the body */ #define FR_LOGFIRST 0x20000 /* Log the first byte if state held */ #define FR_LOGORBLOCK 0x40000 /* block the packet if it can't be logged */ #define FR_STLOOSE 0x80000 /* loose state checking */ #define FR_FRSTRICT 0x100000 /* strict frag. cache */ #define FR_STSTRICT 0x200000 /* strict keep state */ #define FR_NEWISN 0x400000 /* new ISN for outgoing TCP */ #define FR_NOICMPERR 0x800000 /* do not match ICMP errors in state */ #define FR_STATESYNC 0x1000000 /* synchronize state to slave */ #define FR_COPIED 0x2000000 /* copied from user space */ #define FR_INACTIVE 0x4000000 /* only used when flush'ing rules */ #define FR_NOMATCH 0x8000000 /* no match occured */ /* 0x10000000 FF_LOGPASS */ /* 0x20000000 FF_LOGBLOCK */ /* 0x40000000 FF_LOGNOMATCH */ /* 0x80000000 FF_BLOCKNONIP */ #define FR_RETMASK (FR_RETICMP|FR_RETRST|FR_FAKEICMP) #define FR_ISBLOCK(x) (((x) & FR_CMDMASK) == FR_BLOCK) #define FR_ISPASS(x) (((x) & FR_CMDMASK) == FR_PASS) #define FR_ISAUTH(x) (((x) & FR_CMDMASK) == FR_AUTH) #define FR_ISPREAUTH(x) (((x) & FR_CMDMASK) == FR_PREAUTH) #define FR_ISACCOUNT(x) (((x) & FR_CMDMASK) == FR_ACCOUNT) #define FR_ISSKIP(x) (((x) & FR_CMDMASK) == FR_SKIP) #define FR_ISDECAPS(x) (((x) & FR_CMDMASK) == FR_DECAPSULATE) #define FR_ISNOMATCH(x) ((x) & FR_NOMATCH) #define FR_INOUT (FR_INQUE|FR_OUTQUE) /* * recognized flags for SIOCGETFF and SIOCSETFF, and get put in fr_flags */ #define FF_LOGPASS 0x10000000 #define FF_LOGBLOCK 0x20000000 #define FF_LOGNOMATCH 0x40000000 #define FF_LOGGING (FF_LOGPASS|FF_LOGBLOCK|FF_LOGNOMATCH) #define FF_BLOCKNONIP 0x80000000 /* Solaris2 Only */ /* * Structure that passes information on what/how to flush to the kernel. */ typedef struct ipfflush { int ipflu_how; int ipflu_arg; } ipfflush_t; /* * */ typedef struct ipfgetctl { u_int ipfg_min; /* min value */ u_int ipfg_current; /* current value */ u_int ipfg_max; /* max value */ u_int ipfg_default; /* default value */ u_int ipfg_steps; /* value increments */ char ipfg_name[40]; /* tag name for this control */ } ipfgetctl_t; typedef struct ipfsetctl { int ipfs_which; /* 0 = min 1 = current 2 = max 3 = default */ u_int ipfs_value; /* min value */ char ipfs_name[40]; /* tag name for this control */ } ipfsetctl_t; /* * Some of the statistics below are in their own counters, but most are kept * in this single structure so that they can all easily be collected and * copied back as required. */ typedef struct ipf_statistics { u_long fr_icmp_coalesce; u_long fr_tcp_frag; u_long fr_tcp_pullup; u_long fr_tcp_short; u_long fr_tcp_small; u_long fr_tcp_bad_flags; u_long fr_udp_pullup; u_long fr_ip_freed; u_long fr_v6_ah_bad; u_long fr_v6_bad; u_long fr_v6_badfrag; u_long fr_v6_dst_bad; u_long fr_v6_esp_pullup; u_long fr_v6_ext_short; u_long fr_v6_ext_pullup; u_long fr_v6_ext_hlen; u_long fr_v6_frag_bad; u_long fr_v6_frag_pullup; u_long fr_v6_frag_size; u_long fr_v6_gre_pullup; u_long fr_v6_icmp6_pullup; u_long fr_v6_rh_bad; u_long fr_v6_badttl; /* TTL in packet doesn't reach minimum */ u_long fr_v4_ah_bad; u_long fr_v4_ah_pullup; u_long fr_v4_esp_pullup; u_long fr_v4_cipso_bad; u_long fr_v4_cipso_tlen; u_long fr_v4_gre_frag; u_long fr_v4_gre_pullup; u_long fr_v4_icmp_frag; u_long fr_v4_icmp_pullup; u_long fr_v4_badttl; /* TTL in packet doesn't reach minimum */ u_long fr_v4_badsrc; /* source received doesn't match route */ u_long fr_l4_badcksum; /* layer 4 header checksum failure */ u_long fr_badcoalesces; 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_short; /* packets which are short */ 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_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_cmiss; /* cached miss */ u_long fr_tcpbad; /* TCP checksum check failures */ u_long fr_pull[2]; /* good and bad pullup attempts */ u_long fr_bad; /* bad IP packets to the filter */ u_long fr_ipv6; /* IPv6 packets in/out */ u_long fr_ppshit; /* dropped because of pps ceiling */ u_long fr_ipud; /* IP id update failures */ u_long fr_blocked[FRB_MAX_VALUE + 1]; } ipf_statistics_t; /* * 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_32_t ipl_magic; u_int ipl_count; u_32_t ipl_seqnum; struct timeval ipl_time; size_t ipl_dsize; struct iplog *ipl_next; } iplog_t; #define ipl_sec ipl_time.tv_sec #define ipl_usec ipl_time.tv_usec #define IPL_MAGIC 0x49504c4d /* 'IPLM' */ #define IPL_MAGIC_NAT 0x49504c4e /* 'IPLN' */ #define IPL_MAGIC_STATE 0x49504c53 /* 'IPLS' */ #define IPLOG_SIZE sizeof(iplog_t) typedef struct ipflog { u_int fl_unit; u_32_t fl_rule; u_32_t fl_flags; u_32_t fl_lflags; u_32_t fl_logtag; ipftag_t fl_nattag; u_short fl_plen; /* extra data after hlen */ u_short fl_loglevel; /* syslog log level */ char fl_group[FR_GROUPLEN]; u_char fl_hlen; /* length of IP headers saved */ u_char fl_dir; u_char fl_breason; /* from fin_reason */ u_char fl_family; /* address family of packet logged */ char fl_ifname[LIFNAMSIZ]; } ipflog_t; #ifndef IPF_LOGGING # define IPF_LOGGING 0 #endif #ifndef IPF_DEFAULT_PASS # define IPF_DEFAULT_PASS FR_PASS #endif #define DEFAULT_IPFLOGSIZE 32768 #ifndef IPFILTER_LOGSIZE # define IPFILTER_LOGSIZE DEFAULT_IPFLOGSIZE #else # if IPFILTER_LOGSIZE < 8192 # error IPFILTER_LOGSIZE too small. Must be >= 8192 # endif #endif #define IPF_OPTCOPY 0x07ff00 /* bit mask of copied options */ /* * Device filenames for reading log information. Use ipf on Solaris2 because * ipl is already a name used by something else. */ #ifndef IPL_NAME # if SOLARIS # define IPL_NAME "/dev/ipf" # else # define IPL_NAME "/dev/ipl" # endif #endif /* * Pathnames for various IP Filter control devices. Used by LKM * and userland, so defined here. */ #define IPNAT_NAME "/dev/ipnat" #define IPSTATE_NAME "/dev/ipstate" #define IPAUTH_NAME "/dev/ipauth" #define IPSYNC_NAME "/dev/ipsync" #define IPSCAN_NAME "/dev/ipscan" #define IPLOOKUP_NAME "/dev/iplookup" #define IPL_LOGIPF 0 /* Minor device #'s for accessing logs */ #define IPL_LOGNAT 1 #define IPL_LOGSTATE 2 #define IPL_LOGAUTH 3 #define IPL_LOGSYNC 4 #define IPL_LOGSCAN 5 #define IPL_LOGLOOKUP 6 #define IPL_LOGCOUNT 7 #define IPL_LOGMAX 7 #define IPL_LOGSIZE IPL_LOGMAX + 1 #define IPL_LOGALL -1 #define IPL_LOGNONE -2 /* * For SIOCGETFS */ typedef struct friostat { ipf_statistics_t f_st[2]; frentry_t *f_ipf[2][2]; frentry_t *f_acct[2][2]; frentry_t *f_auth; struct frgroup *f_groups[IPL_LOGSIZE][2]; u_long f_froute[2]; u_long f_log_ok; u_long f_log_fail; u_long f_rb_no_mem; u_long f_rb_node_max; u_32_t f_ticks; int f_locks[IPL_LOGSIZE]; int f_defpass; /* default pass - from fr_pass */ int f_active; /* 1 or 0 - active rule set */ int f_running; /* 1 if running, else 0 */ int f_logging; /* 1 if enabled, else 0 */ int f_features; char f_version[32]; /* version string */ } friostat_t; #define f_fin f_ipf[0] #define f_fout f_ipf[1] #define f_acctin f_acct[0] #define f_acctout f_acct[1] #define IPF_FEAT_LKM 0x001 #define IPF_FEAT_LOG 0x002 #define IPF_FEAT_LOOKUP 0x004 #define IPF_FEAT_BPF 0x008 #define IPF_FEAT_COMPILED 0x010 #define IPF_FEAT_CKSUM 0x020 #define IPF_FEAT_SYNC 0x040 #define IPF_FEAT_SCAN 0x080 #define IPF_FEAT_IPV6 0x100 typedef struct optlist { u_short ol_val; int ol_bit; } optlist_t; /* * Group list structure. */ typedef struct frgroup { struct frgroup *fg_next; struct frentry *fg_head; struct frentry *fg_start; struct frgroup **fg_set; u_32_t fg_flags; int fg_ref; char fg_name[FR_GROUPLEN]; } frgroup_t; #define FG_NAME(g) (*(g)->fg_name == '\0' ? "" : (g)->fg_name) /* * Used by state and NAT tables */ typedef struct icmpinfo { u_short ici_id; u_short ici_seq; u_char ici_type; } icmpinfo_t; typedef struct udpinfo { u_short us_sport; u_short us_dport; } udpinfo_t; typedef struct tcpdata { u_32_t td_end; u_32_t td_maxend; u_32_t td_maxwin; u_32_t td_winscale; u_32_t td_maxseg; int td_winflags; } tcpdata_t; #define TCP_WSCALE_MAX 14 #define TCP_WSCALE_SEEN 0x00000001 #define TCP_WSCALE_FIRST 0x00000002 #define TCP_SACK_PERMIT 0x00000004 typedef struct tcpinfo { u_32_t ts_sport; u_32_t ts_dport; tcpdata_t ts_data[2]; } tcpinfo_t; /* * Structures to define a GRE header as seen in a packet. */ struct grebits { #if defined(sparc) u_32_t grb_ver:3; u_32_t grb_flags:3; u_32_t grb_A:1; u_32_t grb_recur:1; u_32_t grb_s:1; u_32_t grb_S:1; u_32_t grb_K:1; u_32_t grb_R:1; u_32_t grb_C:1; #else u_32_t grb_C:1; u_32_t grb_R:1; u_32_t grb_K:1; u_32_t grb_S:1; u_32_t grb_s:1; u_32_t grb_recur:1; u_32_t grb_A:1; u_32_t grb_flags:3; u_32_t grb_ver:3; #endif u_short grb_ptype; }; typedef struct grehdr { union { struct grebits gru_bits; u_short gru_flags; } gr_un; u_short gr_len; u_short gr_call; } grehdr_t; #define gr_flags gr_un.gru_flags #define gr_bits gr_un.gru_bits #define gr_ptype gr_bits.grb_ptype #define gr_C gr_bits.grb_C #define gr_R gr_bits.grb_R #define gr_K gr_bits.grb_K #define gr_S gr_bits.grb_S #define gr_s gr_bits.grb_s #define gr_recur gr_bits.grb_recur #define gr_A gr_bits.grb_A #define gr_ver gr_bits.grb_ver /* * GRE information tracked by "keep state" */ typedef struct greinfo { u_short gs_call[2]; u_short gs_flags; u_short gs_ptype; } greinfo_t; #define GRE_REV(x) ((ntohs(x) >> 13) & 7) /* * Format of an Authentication header */ typedef struct authhdr { u_char ah_next; u_char ah_plen; u_short ah_reserved; u_32_t ah_spi; u_32_t ah_seq; /* Following the sequence number field is 0 or more bytes of */ /* authentication data, as specified by ah_plen - RFC 2402. */ } authhdr_t; /* * Timeout tail queue list member */ typedef struct ipftqent { struct ipftqent **tqe_pnext; struct ipftqent *tqe_next; struct ipftq *tqe_ifq; void *tqe_parent; /* pointer back to NAT/state struct */ u_32_t tqe_die; /* when this entriy is to die */ u_32_t tqe_touched; int tqe_flags; int tqe_state[2]; /* current state of this entry */ } ipftqent_t; #define TQE_RULEBASED 0x00000001 #define TQE_DELETE 0x00000002 /* * Timeout tail queue head for IPFilter */ typedef struct ipftq { ipfmutex_t ifq_lock; u_int ifq_ttl; ipftqent_t *ifq_head; ipftqent_t **ifq_tail; struct ipftq *ifq_next; struct ipftq **ifq_pnext; int ifq_ref; u_int ifq_flags; } ipftq_t; #define IFQF_USER 0x01 /* User defined aging */ #define IFQF_DELETE 0x02 /* Marked for deletion */ #define IFQF_PROXY 0x04 /* Timeout queue in use by a proxy */ #define IPFTQ_INIT(x,y,z) do { \ (x)->ifq_ttl = (y); \ (x)->ifq_head = NULL; \ (x)->ifq_ref = 1; \ (x)->ifq_tail = &(x)->ifq_head; \ MUTEX_INIT(&(x)->ifq_lock, (z)); \ } while (0) #define IPF_HZ_MULT 1 #define IPF_HZ_DIVIDE 2 /* How many times a second ipfilter */ /* checks its timeout queues. */ #define IPF_TTLVAL(x) (((x) / IPF_HZ_MULT) * IPF_HZ_DIVIDE) typedef int (*ipftq_delete_fn_t)(struct ipf_main_softc_s *, void *); /* * Object structure description. For passing through in ioctls. */ typedef struct ipfobj { u_32_t ipfo_rev; /* IPFilter version number */ u_32_t ipfo_size; /* size of object at ipfo_ptr */ void *ipfo_ptr; /* pointer to object */ int ipfo_type; /* type of object being pointed to */ int ipfo_offset; /* bytes from ipfo_ptr where to start */ int ipfo_retval; /* return value */ u_char ipfo_xxxpad[28]; /* reserved for future use */ } ipfobj_t; #define IPFOBJ_FRENTRY 0 /* struct frentry */ #define IPFOBJ_IPFSTAT 1 /* struct friostat */ #define IPFOBJ_IPFINFO 2 /* struct fr_info */ #define IPFOBJ_AUTHSTAT 3 /* struct fr_authstat */ #define IPFOBJ_FRAGSTAT 4 /* struct ipfrstat */ #define IPFOBJ_IPNAT 5 /* struct ipnat */ #define IPFOBJ_NATSTAT 6 /* struct natstat */ #define IPFOBJ_STATESAVE 7 /* struct ipstate_save */ #define IPFOBJ_NATSAVE 8 /* struct nat_save */ #define IPFOBJ_NATLOOKUP 9 /* struct natlookup */ #define IPFOBJ_IPSTATE 10 /* struct ipstate */ #define IPFOBJ_STATESTAT 11 /* struct ips_stat */ #define IPFOBJ_FRAUTH 12 /* struct frauth */ #define IPFOBJ_TUNEABLE 13 /* struct ipftune */ #define IPFOBJ_NAT 14 /* struct nat */ #define IPFOBJ_IPFITER 15 /* struct ipfruleiter */ #define IPFOBJ_GENITER 16 /* struct ipfgeniter */ #define IPFOBJ_GTABLE 17 /* struct ipftable */ #define IPFOBJ_LOOKUPITER 18 /* struct ipflookupiter */ #define IPFOBJ_STATETQTAB 19 /* struct ipftq * NSTATES */ #define IPFOBJ_IPFEXPR 20 #define IPFOBJ_PROXYCTL 21 /* strct ap_ctl */ #define IPFOBJ_FRIPF 22 /* structfripf */ #define IPFOBJ_COUNT 23 /* How many #defines are above this? */ typedef union ipftunevalptr { void *ipftp_void; u_long *ipftp_long; u_int *ipftp_int; u_short *ipftp_short; u_char *ipftp_char; u_long ipftp_offset; } ipftunevalptr_t; typedef union ipftuneval { u_long ipftu_long; u_int ipftu_int; u_short ipftu_short; u_char ipftu_char; } ipftuneval_t; struct ipftuneable; -typedef int (* ipftunefunc_t) __P((struct ipf_main_softc_s *, struct ipftuneable *, ipftuneval_t *)); +typedef int (* ipftunefunc_t)(struct ipf_main_softc_s *, struct ipftuneable *, ipftuneval_t *); typedef struct ipftuneable { ipftunevalptr_t ipft_una; const char *ipft_name; u_long ipft_min; u_long ipft_max; int ipft_sz; int ipft_flags; struct ipftuneable *ipft_next; ipftunefunc_t ipft_func; } ipftuneable_t; #define ipft_addr ipft_una.ipftp_void #define ipft_plong ipft_una.ipftp_long #define ipft_pint ipft_una.ipftp_int #define ipft_pshort ipft_una.ipftp_short #define ipft_pchar ipft_una.ipftp_char #define IPFT_RDONLY 1 /* read-only */ #define IPFT_WRDISABLED 2 /* write when disabled only */ typedef struct ipftune { void *ipft_cookie; ipftuneval_t ipft_un; u_long ipft_min; u_long ipft_max; int ipft_sz; int ipft_flags; char ipft_name[80]; } ipftune_t; #define ipft_vlong ipft_un.ipftu_long #define ipft_vint ipft_un.ipftu_int #define ipft_vshort ipft_un.ipftu_short #define ipft_vchar ipft_un.ipftu_char /* * Hash table header */ #define IPFHASH(x,y) typedef struct { \ ipfrwlock_t ipfh_lock; \ struct x *ipfh_head; \ } y /* ** HPUX Port */ #if !defined(CDEV_MAJOR) && defined (__FreeBSD_version) # define CDEV_MAJOR 79 #endif #ifdef _KERNEL # define FR_VERBOSE(verb_pr) # define FR_DEBUG(verb_pr) #else -extern void ipfkdebug __P((char *, ...)); -extern void ipfkverbose __P((char *, ...)); +extern void ipfkdebug(char *, ...); +extern void ipfkverbose(char *, ...); # define FR_VERBOSE(verb_pr) ipfkverbose verb_pr # define FR_DEBUG(verb_pr) ipfkdebug verb_pr #endif /* * */ typedef struct ipfruleiter { int iri_inout; char iri_group[FR_GROUPLEN]; int iri_active; int iri_nrules; int iri_v; /* No longer used (compatibility) */ frentry_t *iri_rule; } ipfruleiter_t; /* * Values for iri_inout */ #define F_IN 0 #define F_OUT 1 #define F_ACIN 2 #define F_ACOUT 3 typedef struct ipfgeniter { int igi_type; int igi_nitems; void *igi_data; } ipfgeniter_t; #define IPFGENITER_IPF 0 #define IPFGENITER_NAT 1 #define IPFGENITER_IPNAT 2 #define IPFGENITER_FRAG 3 #define IPFGENITER_AUTH 4 #define IPFGENITER_STATE 5 #define IPFGENITER_NATFRAG 6 #define IPFGENITER_HOSTMAP 7 #define IPFGENITER_LOOKUP 8 typedef struct ipftable { int ita_type; void *ita_table; } ipftable_t; #define IPFTABLE_BUCKETS 1 #define IPFTABLE_BUCKETS_NATIN 2 #define IPFTABLE_BUCKETS_NATOUT 3 typedef struct ipf_v4_masktab_s { u_32_t imt4_active[33]; int imt4_masks[33]; int imt4_max; } ipf_v4_masktab_t; typedef struct ipf_v6_masktab_s { i6addr_t imt6_active[129]; int imt6_masks[129]; int imt6_max; } ipf_v6_masktab_t; /* * */ typedef struct ipftoken { struct ipftoken *ipt_next; struct ipftoken **ipt_pnext; void *ipt_ctx; void *ipt_data; u_long ipt_die; int ipt_type; int ipt_uid; int ipt_subtype; int ipt_ref; int ipt_complete; } ipftoken_t; /* * */ typedef struct ipfexp { int ipfe_cmd; int ipfe_not; int ipfe_narg; int ipfe_size; int ipfe_arg0[1]; } ipfexp_t; /* * Currently support commands (ipfe_cmd) * 32bits is split up follows: * aabbcccc * aa = 0 = packet matching, 1 = meta data matching * bb = IP protocol number * cccc = command */ #define IPF_EXP_IP_PR 0x00000001 #define IPF_EXP_IP_ADDR 0x00000002 #define IPF_EXP_IP_SRCADDR 0x00000003 #define IPF_EXP_IP_DSTADDR 0x00000004 #define IPF_EXP_IP6_ADDR 0x00000005 #define IPF_EXP_IP6_SRCADDR 0x00000006 #define IPF_EXP_IP6_DSTADDR 0x00000007 #define IPF_EXP_TCP_FLAGS 0x00060001 #define IPF_EXP_TCP_PORT 0x00060002 #define IPF_EXP_TCP_SPORT 0x00060003 #define IPF_EXP_TCP_DPORT 0x00060004 #define IPF_EXP_UDP_PORT 0x00110002 #define IPF_EXP_UDP_SPORT 0x00110003 #define IPF_EXP_UDP_DPORT 0x00110004 #define IPF_EXP_IDLE_GT 0x01000001 #define IPF_EXP_TCP_STATE 0x01060002 #define IPF_EXP_END 0xffffffff #define ONE_DAY IPF_TTLVAL(1 * 86400) /* 1 day */ #define FIVE_DAYS (5 * ONE_DAY) typedef struct ipf_main_softc_s { struct ipf_main_softc_s *ipf_next; ipfmutex_t ipf_rw; ipfmutex_t ipf_timeoutlock; ipfrwlock_t ipf_mutex; ipfrwlock_t ipf_frag; ipfrwlock_t ipf_global; ipfrwlock_t ipf_tokens; ipfrwlock_t ipf_state; ipfrwlock_t ipf_nat; ipfrwlock_t ipf_natfrag; ipfrwlock_t ipf_poolrw; int ipf_dynamic_softc; int ipf_refcnt; int ipf_running; int ipf_flags; int ipf_active; int ipf_control_forwarding; int ipf_update_ipid; int ipf_chksrc; /* causes a system crash if enabled */ int ipf_pass; int ipf_minttl; int ipf_icmpminfragmtu; int ipf_interror; /* Should be in a struct that is per */ /* thread or process. Does not belong */ /* here but there's a lot more work */ /* in doing that properly. For now, */ /* it is squatting. */ u_int ipf_tcpidletimeout; u_int ipf_tcpclosewait; u_int ipf_tcplastack; u_int ipf_tcptimewait; u_int ipf_tcptimeout; u_int ipf_tcpsynsent; u_int ipf_tcpsynrecv; u_int ipf_tcpclosed; u_int ipf_tcphalfclosed; u_int ipf_udptimeout; u_int ipf_udpacktimeout; u_int ipf_icmptimeout; u_int ipf_icmpacktimeout; u_int ipf_iptimeout; u_long ipf_ticks; u_long ipf_userifqs; u_long ipf_rb_no_mem; u_long ipf_rb_node_max; u_long ipf_frouteok[2]; ipftuneable_t *ipf_tuners; void *ipf_frag_soft; void *ipf_nat_soft; void *ipf_state_soft; void *ipf_auth_soft; void *ipf_proxy_soft; void *ipf_sync_soft; void *ipf_lookup_soft; void *ipf_log_soft; struct frgroup *ipf_groups[IPL_LOGSIZE][2]; frentry_t *ipf_rules[2][2]; frentry_t *ipf_acct[2][2]; frentry_t *ipf_rule_explist[2]; ipftoken_t *ipf_token_head; ipftoken_t **ipf_token_tail; #if defined(__FreeBSD_version) && defined(_KERNEL) struct callout ipf_slow_ch; #endif #if NETBSD_GE_REV(104040000) struct callout ipf_slow_ch; #endif #if SOLARIS timeout_id_t ipf_slow_ch; #endif #if defined(_KERNEL) # if SOLARIS struct pollhead ipf_poll_head[IPL_LOGSIZE]; void *ipf_dip; # if defined(INSTANCES) int ipf_get_loopback; u_long ipf_idnum; net_handle_t ipf_nd_v4; net_handle_t ipf_nd_v6; hook_t *ipf_hk_v4_in; hook_t *ipf_hk_v4_out; hook_t *ipf_hk_v4_nic; hook_t *ipf_hk_v6_in; hook_t *ipf_hk_v6_out; hook_t *ipf_hk_v6_nic; hook_t *ipf_hk_loop_v4_in; hook_t *ipf_hk_loop_v4_out; hook_t *ipf_hk_loop_v6_in; hook_t *ipf_hk_loop_v6_out; # endif # else struct selinfo ipf_selwait[IPL_LOGSIZE]; # endif #endif void *ipf_slow; ipf_statistics_t ipf_stats[2]; u_char ipf_iss_secret[32]; u_short ipf_ip_id; } ipf_main_softc_t; #define IPFERROR(_e) do { softc->ipf_interror = (_e); \ DT1(user_error, int, _e); \ } while (0) #ifndef _KERNEL -extern int ipf_check __P((void *, struct ip *, int, struct ifnet *, int, mb_t **)); -extern struct ifnet *get_unit __P((char *, int)); -extern char *get_ifname __P((struct ifnet *)); -extern int ipfioctl __P((ipf_main_softc_t *, int, ioctlcmd_t, - caddr_t, int)); -extern void m_freem __P((mb_t *)); -extern size_t msgdsize __P((mb_t *)); -extern int bcopywrap __P((void *, void *, size_t)); +extern int ipf_check(void *, struct ip *, int, struct ifnet *, int, mb_t **); +extern struct ifnet *get_unit(char *, int); +extern char *get_ifname(struct ifnet *); +extern int ipfioctl(ipf_main_softc_t *, int, ioctlcmd_t, + caddr_t, int); +extern void m_freem(mb_t *); +extern size_t msgdsize(mb_t *); +extern int bcopywrap(void *, void *, size_t); extern void ip_fillid(struct ip *); #else /* #ifndef _KERNEL */ # if defined(__NetBSD__) && defined(PFIL_HOOKS) -extern void ipfilterattach __P((int)); +extern void ipfilterattach(int); # endif -extern int ipl_enable __P((void)); -extern int ipl_disable __P((void)); +extern int ipl_enable(void); +extern int ipl_disable(void); # ifdef MENTAT /* XXX MENTAT is always defined for Solaris */ -extern int ipf_check __P((void *, struct ip *, int, struct ifnet *, int, void *, - mblk_t **)); +extern int ipf_check(void *, struct ip *, int, struct ifnet *, int, void *, + mblk_t **); # if SOLARIS extern void ipf_prependmbt(fr_info_t *, mblk_t *); -extern int ipfioctl __P((dev_t, int, intptr_t, int, cred_t *, int *)); +extern int ipfioctl(dev_t, int, intptr_t, int, cred_t *, int *); # endif -extern int ipf_qout __P((queue_t *, mblk_t *)); +extern int ipf_qout(queue_t *, mblk_t *); # else /* MENTAT */ /* XXX MENTAT is never defined for FreeBSD & NetBSD */ -extern int ipf_check __P((void *, struct ip *, int, struct ifnet *, int, mb_t **)); -extern int (*fr_checkp) __P((ip_t *, int, void *, int, mb_t **)); -extern size_t mbufchainlen __P((mb_t *)); +extern int ipf_check(void *, struct ip *, int, struct ifnet *, int, mb_t **); +extern int (*fr_checkp)(ip_t *, int, void *, int, mb_t **); +extern size_t mbufchainlen(mb_t *); # ifdef IPFILTER_LKM -extern int ipf_identify __P((char *)); +extern int ipf_identify(char *); # endif # if defined(__FreeBSD_version) -extern int ipfioctl __P((struct cdev*, u_long, caddr_t, int, struct thread *)); +extern int ipfioctl(struct cdev*, u_long, caddr_t, int, struct thread *); # elif defined(__NetBSD__) -extern int ipfioctl __P((dev_t, u_long, void *, int, struct lwp *)); +extern int ipfioctl(dev_t, u_long, void *, int, struct lwp *); # endif # endif /* MENTAT */ # if defined(__FreeBSD_version) -extern int ipf_pfil_hook __P((void)); -extern int ipf_pfil_unhook __P((void)); -extern void ipf_event_reg __P((void)); -extern void ipf_event_dereg __P((void)); +extern int ipf_pfil_hook(void); +extern int ipf_pfil_unhook(void); +extern void ipf_event_reg(void); +extern void ipf_event_dereg(void); # endif # if defined(INSTANCES) -extern ipf_main_softc_t *ipf_find_softc __P((u_long)); -extern int ipf_set_loopback __P((ipf_main_softc_t *, ipftuneable_t *, - ipftuneval_t *)); +extern ipf_main_softc_t *ipf_find_softc(u_long); +extern int ipf_set_loopback(ipf_main_softc_t *, ipftuneable_t *, + ipftuneval_t *); # endif #endif /* #ifndef _KERNEL */ -extern char *memstr __P((const char *, char *, size_t, size_t)); -extern int count4bits __P((u_32_t)); +extern char *memstr(const char *, char *, size_t, size_t); +extern int count4bits(u_32_t); #ifdef USE_INET6 -extern int count6bits __P((u_32_t *)); +extern int count6bits(u_32_t *); #endif -extern int frrequest __P((ipf_main_softc_t *, int, ioctlcmd_t, caddr_t, - int, int)); -extern char *getifname __P((struct ifnet *)); -extern int ipfattach __P((ipf_main_softc_t *)); -extern int ipfdetach __P((ipf_main_softc_t *)); -extern u_short ipf_cksum __P((u_short *, int)); -extern int copyinptr __P((ipf_main_softc_t *, void *, void *, size_t)); -extern int copyoutptr __P((ipf_main_softc_t *, void *, void *, size_t)); -extern int ipf_fastroute __P((mb_t *, mb_t **, fr_info_t *, frdest_t *)); -extern int ipf_inject __P((fr_info_t *, mb_t *)); -extern int ipf_inobj __P((ipf_main_softc_t *, void *, ipfobj_t *, - void *, int)); -extern int ipf_inobjsz __P((ipf_main_softc_t *, void *, void *, - int , int)); -extern int ipf_ioctlswitch __P((ipf_main_softc_t *, int, void *, - ioctlcmd_t, int, int, void *)); -extern int ipf_ipf_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, - int, int, void *)); -extern int ipf_ipftune __P((ipf_main_softc_t *, ioctlcmd_t, void *)); -extern int ipf_matcharray_load __P((ipf_main_softc_t *, caddr_t, - ipfobj_t *, int **)); -extern int ipf_matcharray_verify __P((int *, int)); -extern int ipf_outobj __P((ipf_main_softc_t *, void *, void *, int)); -extern int ipf_outobjk __P((ipf_main_softc_t *, ipfobj_t *, void *)); -extern int ipf_outobjsz __P((ipf_main_softc_t *, void *, void *, - int, int)); -extern void *ipf_pullup __P((mb_t *, fr_info_t *, int)); -extern int ipf_resolvedest __P((ipf_main_softc_t *, char *, - struct frdest *, int)); -extern int ipf_resolvefunc __P((ipf_main_softc_t *, void *)); -extern void *ipf_resolvenic __P((ipf_main_softc_t *, char *, int)); -extern int ipf_send_icmp_err __P((int, fr_info_t *, int)); -extern int ipf_send_reset __P((fr_info_t *)); -extern void ipf_apply_timeout __P((ipftq_t *, u_int)); -extern ipftq_t *ipf_addtimeoutqueue __P((ipf_main_softc_t *, ipftq_t **, - u_int)); -extern void ipf_deletequeueentry __P((ipftqent_t *)); -extern int ipf_deletetimeoutqueue __P((ipftq_t *)); -extern void ipf_freetimeoutqueue __P((ipf_main_softc_t *, ipftq_t *)); -extern void ipf_movequeue __P((u_long, ipftqent_t *, ipftq_t *, - ipftq_t *)); -extern void ipf_queueappend __P((u_long, ipftqent_t *, ipftq_t *, void *)); -extern void ipf_queueback __P((u_long, ipftqent_t *)); -extern int ipf_queueflush __P((ipf_main_softc_t *, ipftq_delete_fn_t, - ipftq_t *, ipftq_t *, u_int *, int, int)); -extern void ipf_queuefront __P((ipftqent_t *)); -extern int ipf_settimeout_tcp __P((ipftuneable_t *, ipftuneval_t *, - ipftq_t *)); -extern int ipf_checkv4sum __P((fr_info_t *)); -extern int ipf_checkl4sum __P((fr_info_t *)); -extern int ipf_ifpfillv4addr __P((int, struct sockaddr_in *, +extern int frrequest(ipf_main_softc_t *, int, ioctlcmd_t, caddr_t, + int, int); +extern char *getifname(struct ifnet *); +extern int ipfattach(ipf_main_softc_t *); +extern int ipfdetach(ipf_main_softc_t *); +extern u_short ipf_cksum(u_short *, int); +extern int copyinptr(ipf_main_softc_t *, void *, void *, size_t); +extern int copyoutptr(ipf_main_softc_t *, void *, void *, size_t); +extern int ipf_fastroute(mb_t *, mb_t **, fr_info_t *, frdest_t *); +extern int ipf_inject(fr_info_t *, mb_t *); +extern int ipf_inobj(ipf_main_softc_t *, void *, ipfobj_t *, + void *, int); +extern int ipf_inobjsz(ipf_main_softc_t *, void *, void *, + int , int); +extern int ipf_ioctlswitch(ipf_main_softc_t *, int, void *, + ioctlcmd_t, int, int, void *); +extern int ipf_ipf_ioctl(ipf_main_softc_t *, caddr_t, ioctlcmd_t, + int, int, void *); +extern int ipf_ipftune(ipf_main_softc_t *, ioctlcmd_t, void *); +extern int ipf_matcharray_load(ipf_main_softc_t *, caddr_t, + ipfobj_t *, int **); +extern int ipf_matcharray_verify(int *, int); +extern int ipf_outobj(ipf_main_softc_t *, void *, void *, int); +extern int ipf_outobjk(ipf_main_softc_t *, ipfobj_t *, void *); +extern int ipf_outobjsz(ipf_main_softc_t *, void *, void *, + int, int); +extern void *ipf_pullup(mb_t *, fr_info_t *, int); +extern int ipf_resolvedest(ipf_main_softc_t *, char *, + struct frdest *, int); +extern int ipf_resolvefunc(ipf_main_softc_t *, void *); +extern void *ipf_resolvenic(ipf_main_softc_t *, char *, int); +extern int ipf_send_icmp_err(int, fr_info_t *, int); +extern int ipf_send_reset(fr_info_t *); +extern void ipf_apply_timeout(ipftq_t *, u_int); +extern ipftq_t *ipf_addtimeoutqueue(ipf_main_softc_t *, ipftq_t **, + u_int); +extern void ipf_deletequeueentry(ipftqent_t *); +extern int ipf_deletetimeoutqueue(ipftq_t *); +extern void ipf_freetimeoutqueue(ipf_main_softc_t *, ipftq_t *); +extern void ipf_movequeue(u_long, ipftqent_t *, ipftq_t *, + ipftq_t *); +extern void ipf_queueappend(u_long, ipftqent_t *, ipftq_t *, void *); +extern void ipf_queueback(u_long, ipftqent_t *); +extern int ipf_queueflush(ipf_main_softc_t *, ipftq_delete_fn_t, + ipftq_t *, ipftq_t *, u_int *, int, int); +extern void ipf_queuefront(ipftqent_t *); +extern int ipf_settimeout_tcp(ipftuneable_t *, ipftuneval_t *, + ipftq_t *); +extern int ipf_checkv4sum(fr_info_t *); +extern int ipf_checkl4sum(fr_info_t *); +extern int ipf_ifpfillv4addr(int, struct sockaddr_in *, struct sockaddr_in *, struct in_addr *, - struct in_addr *)); -extern int ipf_coalesce __P((fr_info_t *)); + struct in_addr *); +extern int ipf_coalesce(fr_info_t *); #ifdef USE_INET6 -extern int ipf_checkv6sum __P((fr_info_t *)); -extern int ipf_ifpfillv6addr __P((int, struct sockaddr_in6 *, +extern int ipf_checkv6sum(fr_info_t *); +extern int ipf_ifpfillv6addr(int, struct sockaddr_in6 *, struct sockaddr_in6 *, i6addr_t *, - i6addr_t *)); + i6addr_t *); #endif -extern int ipf_tune_add __P((ipf_main_softc_t *, ipftuneable_t *)); -extern int ipf_tune_add_array __P((ipf_main_softc_t *, ipftuneable_t *)); -extern int ipf_tune_del __P((ipf_main_softc_t *, ipftuneable_t *)); -extern int ipf_tune_del_array __P((ipf_main_softc_t *, ipftuneable_t *)); -extern int ipf_tune_array_link __P((ipf_main_softc_t *, ipftuneable_t *)); -extern int ipf_tune_array_unlink __P((ipf_main_softc_t *, - ipftuneable_t *)); -extern ipftuneable_t *ipf_tune_array_copy __P((void *, size_t, - ipftuneable_t *)); - -extern int ipf_pr_pullup __P((fr_info_t *, int)); - -extern int ipf_flush __P((ipf_main_softc_t *, minor_t, int)); -extern frgroup_t *ipf_group_add __P((ipf_main_softc_t *, char *, void *, - u_32_t, minor_t, int)); -extern void ipf_group_del __P((ipf_main_softc_t *, frgroup_t *, - frentry_t *)); -extern int ipf_derefrule __P((ipf_main_softc_t *, frentry_t **)); -extern frgroup_t *ipf_findgroup __P((ipf_main_softc_t *, char *, minor_t, - int, frgroup_t ***)); - -extern int ipf_log_init __P((void)); -extern int ipf_log_bytesused __P((ipf_main_softc_t *, int)); -extern int ipf_log_canread __P((ipf_main_softc_t *, int)); -extern int ipf_log_clear __P((ipf_main_softc_t *, minor_t)); -extern u_long ipf_log_failures __P((ipf_main_softc_t *, int)); -extern int ipf_log_read __P((ipf_main_softc_t *, minor_t, uio_t *)); -extern int ipf_log_items __P((ipf_main_softc_t *, int, fr_info_t *, - void **, size_t *, int *, int)); -extern u_long ipf_log_logok __P((ipf_main_softc_t *, int)); -extern void ipf_log_unload __P((ipf_main_softc_t *)); -extern int ipf_log_pkt __P((fr_info_t *, u_int)); - -extern frentry_t *ipf_acctpkt __P((fr_info_t *, u_32_t *)); -extern u_short fr_cksum __P((fr_info_t *, ip_t *, int, void *)); -extern void ipf_deinitialise __P((ipf_main_softc_t *)); -extern int ipf_deliverlocal __P((ipf_main_softc_t *, int, void *, - i6addr_t *)); -extern frentry_t *ipf_dstgrpmap __P((fr_info_t *, u_32_t *)); -extern void ipf_fixskip __P((frentry_t **, frentry_t *, int)); -extern void ipf_forgetifp __P((ipf_main_softc_t *, void *)); -extern frentry_t *ipf_getrulen __P((ipf_main_softc_t *, int, char *, - u_32_t)); -extern int ipf_ifpaddr __P((ipf_main_softc_t *, int, int, void *, - i6addr_t *, i6addr_t *)); -extern void ipf_inet_mask_add __P((int, ipf_v4_masktab_t *)); -extern void ipf_inet_mask_del __P((int, ipf_v4_masktab_t *)); +extern int ipf_tune_add(ipf_main_softc_t *, ipftuneable_t *); +extern int ipf_tune_add_array(ipf_main_softc_t *, ipftuneable_t *); +extern int ipf_tune_del(ipf_main_softc_t *, ipftuneable_t *); +extern int ipf_tune_del_array(ipf_main_softc_t *, ipftuneable_t *); +extern int ipf_tune_array_link(ipf_main_softc_t *, ipftuneable_t *); +extern int ipf_tune_array_unlink(ipf_main_softc_t *, + ipftuneable_t *); +extern ipftuneable_t *ipf_tune_array_copy(void *, size_t, + ipftuneable_t *); + +extern int ipf_pr_pullup(fr_info_t *, int); + +extern int ipf_flush(ipf_main_softc_t *, minor_t, int); +extern frgroup_t *ipf_group_add(ipf_main_softc_t *, char *, void *, + u_32_t, minor_t, int); +extern void ipf_group_del(ipf_main_softc_t *, frgroup_t *, + frentry_t *); +extern int ipf_derefrule(ipf_main_softc_t *, frentry_t **); +extern frgroup_t *ipf_findgroup(ipf_main_softc_t *, char *, minor_t, + int, frgroup_t ***); + +extern int ipf_log_init(void); +extern int ipf_log_bytesused(ipf_main_softc_t *, int); +extern int ipf_log_canread(ipf_main_softc_t *, int); +extern int ipf_log_clear(ipf_main_softc_t *, minor_t); +extern u_long ipf_log_failures(ipf_main_softc_t *, int); +extern int ipf_log_read(ipf_main_softc_t *, minor_t, uio_t *); +extern int ipf_log_items(ipf_main_softc_t *, int, fr_info_t *, + void **, size_t *, int *, int); +extern u_long ipf_log_logok(ipf_main_softc_t *, int); +extern void ipf_log_unload(ipf_main_softc_t *); +extern int ipf_log_pkt(fr_info_t *, u_int); + +extern frentry_t *ipf_acctpkt(fr_info_t *, u_32_t *); +extern u_short fr_cksum(fr_info_t *, ip_t *, int, void *); +extern void ipf_deinitialise(ipf_main_softc_t *); +extern int ipf_deliverlocal(ipf_main_softc_t *, int, void *, + i6addr_t *); +extern frentry_t *ipf_dstgrpmap(fr_info_t *, u_32_t *); +extern void ipf_fixskip(frentry_t **, frentry_t *, int); +extern void ipf_forgetifp(ipf_main_softc_t *, void *); +extern frentry_t *ipf_getrulen(ipf_main_softc_t *, int, char *, + u_32_t); +extern int ipf_ifpaddr(ipf_main_softc_t *, int, int, void *, + i6addr_t *, i6addr_t *); +extern void ipf_inet_mask_add(int, ipf_v4_masktab_t *); +extern void ipf_inet_mask_del(int, ipf_v4_masktab_t *); #ifdef USE_INET6 -extern void ipf_inet6_mask_add __P((int, i6addr_t *, - ipf_v6_masktab_t *)); -extern void ipf_inet6_mask_del __P((int, i6addr_t *, - ipf_v6_masktab_t *)); +extern void ipf_inet6_mask_add(int, i6addr_t *, + ipf_v6_masktab_t *); +extern void ipf_inet6_mask_del(int, i6addr_t *, + ipf_v6_masktab_t *); #endif -extern int ipf_initialise __P((void)); -extern int ipf_lock __P((caddr_t, int *)); -extern int ipf_makefrip __P((int, ip_t *, fr_info_t *)); -extern int ipf_matchtag __P((ipftag_t *, ipftag_t *)); -extern int ipf_matchicmpqueryreply __P((int, icmpinfo_t *, - struct icmp *, int)); -extern u_32_t ipf_newisn __P((fr_info_t *)); -extern u_int ipf_pcksum __P((fr_info_t *, int, u_int)); +extern int ipf_initialise(void); +extern int ipf_lock(caddr_t, int *); +extern int ipf_makefrip(int, ip_t *, fr_info_t *); +extern int ipf_matchtag(ipftag_t *, ipftag_t *); +extern int ipf_matchicmpqueryreply(int, icmpinfo_t *, + struct icmp *, int); +extern u_32_t ipf_newisn(fr_info_t *); +extern u_int ipf_pcksum(fr_info_t *, int, u_int); #ifdef USE_INET6 -extern u_int ipf_pcksum6 __P((struct mbuf *, ip6_t *, - u_int32_t, u_int32_t)); +extern u_int ipf_pcksum6(struct mbuf *, ip6_t *, + u_int32_t, u_int32_t); #endif -extern void ipf_rule_expire __P((ipf_main_softc_t *)); -extern int ipf_scanlist __P((fr_info_t *, u_32_t)); -extern frentry_t *ipf_srcgrpmap __P((fr_info_t *, u_32_t *)); -extern int ipf_tcpudpchk __P((fr_ip_t *, frtuc_t *)); -extern int ipf_verifysrc __P((fr_info_t *fin)); -extern int ipf_zerostats __P((ipf_main_softc_t *, char *)); -extern int ipf_getnextrule __P((ipf_main_softc_t *, ipftoken_t *, - void *)); -extern int ipf_sync __P((ipf_main_softc_t *, void *)); -extern int ipf_token_deref __P((ipf_main_softc_t *, ipftoken_t *)); -extern void ipf_token_expire __P((ipf_main_softc_t *)); -extern ipftoken_t *ipf_token_find __P((ipf_main_softc_t *, int, int, - void *)); -extern int ipf_token_del __P((ipf_main_softc_t *, int, int, - void *)); -extern void ipf_token_mark_complete __P((ipftoken_t *)); -extern int ipf_genericiter __P((ipf_main_softc_t *, void *, - int, void *)); +extern void ipf_rule_expire(ipf_main_softc_t *); +extern int ipf_scanlist(fr_info_t *, u_32_t); +extern frentry_t *ipf_srcgrpmap(fr_info_t *, u_32_t *); +extern int ipf_tcpudpchk(fr_ip_t *, frtuc_t *); +extern int ipf_verifysrc(fr_info_t *fin); +extern int ipf_zerostats(ipf_main_softc_t *, char *); +extern int ipf_getnextrule(ipf_main_softc_t *, ipftoken_t *, + void *); +extern int ipf_sync(ipf_main_softc_t *, void *); +extern int ipf_token_deref(ipf_main_softc_t *, ipftoken_t *); +extern void ipf_token_expire(ipf_main_softc_t *); +extern ipftoken_t *ipf_token_find(ipf_main_softc_t *, int, int, + void *); +extern int ipf_token_del(ipf_main_softc_t *, int, int, + void *); +extern void ipf_token_mark_complete(ipftoken_t *); +extern int ipf_genericiter(ipf_main_softc_t *, void *, + int, void *); #ifdef IPFILTER_LOOKUP -extern void *ipf_resolvelookup __P((int, u_int, u_int, - lookupfunc_t *)); +extern void *ipf_resolvelookup(int, u_int, u_int, + lookupfunc_t *); #endif -extern u_32_t ipf_random __P((void)); - -extern int ipf_main_load __P((void)); -extern void *ipf_main_soft_create __P((void *)); -extern void ipf_main_soft_destroy __P((ipf_main_softc_t *)); -extern int ipf_main_soft_init __P((ipf_main_softc_t *)); -extern int ipf_main_soft_fini __P((ipf_main_softc_t *)); -extern int ipf_main_unload __P((void)); -extern int ipf_load_all __P((void)); -extern int ipf_unload_all __P((void)); -extern void ipf_destroy_all __P((ipf_main_softc_t *)); -extern ipf_main_softc_t *ipf_create_all __P((void *)); -extern int ipf_init_all __P((ipf_main_softc_t *)); -extern int ipf_fini_all __P((ipf_main_softc_t *)); -extern void ipf_log_soft_destroy __P((ipf_main_softc_t *, void *)); -extern void *ipf_log_soft_create __P((ipf_main_softc_t *)); -extern int ipf_log_soft_init __P((ipf_main_softc_t *, void *)); -extern int ipf_log_soft_fini __P((ipf_main_softc_t *, void *)); -extern int ipf_log_main_load __P((void)); -extern int ipf_log_main_unload __P((void)); +extern u_32_t ipf_random(void); + +extern int ipf_main_load(void); +extern void *ipf_main_soft_create(void *); +extern void ipf_main_soft_destroy(ipf_main_softc_t *); +extern int ipf_main_soft_init(ipf_main_softc_t *); +extern int ipf_main_soft_fini(ipf_main_softc_t *); +extern int ipf_main_unload(void); +extern int ipf_load_all(void); +extern int ipf_unload_all(void); +extern void ipf_destroy_all(ipf_main_softc_t *); +extern ipf_main_softc_t *ipf_create_all(void *); +extern int ipf_init_all(ipf_main_softc_t *); +extern int ipf_fini_all(ipf_main_softc_t *); +extern void ipf_log_soft_destroy(ipf_main_softc_t *, void *); +extern void *ipf_log_soft_create(ipf_main_softc_t *); +extern int ipf_log_soft_init(ipf_main_softc_t *, void *); +extern int ipf_log_soft_fini(ipf_main_softc_t *, void *); +extern int ipf_log_main_load(void); +extern int ipf_log_main_unload(void); extern char ipfilter_version[]; #ifdef USE_INET6 extern int icmptoicmp6types[ICMP_MAXTYPE+1]; extern int icmptoicmp6unreach[ICMP_MAX_UNREACH]; extern int icmpreplytype6[ICMP6_MAXTYPE + 1]; #endif #ifdef IPFILTER_COMPAT -extern int ipf_in_compat __P((ipf_main_softc_t *, ipfobj_t *, void *,int)); -extern int ipf_out_compat __P((ipf_main_softc_t *, ipfobj_t *, void *)); +extern int ipf_in_compat(ipf_main_softc_t *, ipfobj_t *, void *,int); +extern int ipf_out_compat(ipf_main_softc_t *, ipfobj_t *, void *); #endif extern int icmpreplytype4[ICMP_MAXTYPE + 1]; -extern int ipf_ht_node_add __P((ipf_main_softc_t *, host_track_t *, - int, i6addr_t *)); -extern int ipf_ht_node_del __P((host_track_t *, int, i6addr_t *)); -extern void ipf_rb_ht_flush __P((host_track_t *)); -extern void ipf_rb_ht_freenode __P((host_node_t *, void *)); -extern void ipf_rb_ht_init __P((host_track_t *)); +extern int ipf_ht_node_add(ipf_main_softc_t *, host_track_t *, + int, i6addr_t *); +extern int ipf_ht_node_del(host_track_t *, int, i6addr_t *); +extern void ipf_rb_ht_flush(host_track_t *); +extern void ipf_rb_ht_freenode(host_node_t *, void *); +extern void ipf_rb_ht_init(host_track_t *); #endif /* __IP_FIL_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c index 70a27873c3ff..8e90821aaec7 100644 --- a/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c +++ b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c @@ -1,1491 +1,1491 @@ /* $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 #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #if defined(__FreeBSD_version) && \ !defined(KLD_MODULE) && !defined(IPFILTER_LKM) # include "opt_inet6.h" #endif #if defined(__FreeBSD_version) && \ !defined(KLD_MODULE) && !defined(IPFILTER_LKM) # include "opt_random_ip_id.h" #endif #include #include #include #include #include #include #include #include # include #if defined(__FreeBSD_version) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include #endif #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_sync.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" #ifdef IPFILTER_SCAN #include "netinet/ip_scan.h" #endif #include "netinet/ip_pool.h" #include #include #ifdef CSUM_DATA_VALID #include #endif -extern int ip_optcopy __P((struct ip *, struct ip *)); +extern int ip_optcopy(struct ip *, struct ip *); # ifdef IPFILTER_M_IPFILTER MALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures"); # endif -static int ipf_send_ip __P((fr_info_t *, mb_t *)); -static void ipf_timer_func __P((void *arg)); +static int ipf_send_ip(fr_info_t *, mb_t *); +static void ipf_timer_func(void *arg); VNET_DEFINE(ipf_main_softc_t, ipfmain) = { .ipf_running = -2, }; #define V_ipfmain VNET(ipfmain) # include # include VNET_DEFINE_STATIC(eventhandler_tag, ipf_arrivetag); VNET_DEFINE_STATIC(eventhandler_tag, ipf_departtag); #define V_ipf_arrivetag VNET(ipf_arrivetag) #define V_ipf_departtag VNET(ipf_departtag) #if 0 /* * Disable the "cloner" event handler; we are getting interface * events before the firewall is fully initiallized and also no vnet * information thus leading to uninitialised memory accesses. * In addition it is unclear why we need it in first place. * If it turns out to be needed, well need a dedicated event handler * for it to deal with the ifc and the correct vnet. */ VNET_DEFINE_STATIC(eventhandler_tag, ipf_clonetag); #define V_ipf_clonetag VNET(ipf_clonetag) #endif static void ipf_ifevent(void *arg, struct ifnet *ifp); static void ipf_ifevent(arg, ifp) void *arg; struct ifnet *ifp; { CURVNET_SET(ifp->if_vnet); if (V_ipfmain.ipf_running > 0) ipf_sync(&V_ipfmain, NULL); CURVNET_RESTORE(); } static int ipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { struct ip *ip = mtod(*mp, struct ip *); int rv; /* * IPFilter expects evreything in network byte order */ #if (__FreeBSD_version < 1000019) ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); #endif CURVNET_SET(ifp->if_vnet); rv = ipf_check(&V_ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp); CURVNET_RESTORE(); #if (__FreeBSD_version < 1000019) if ((rv == 0) && (*mp != NULL)) { ip = mtod(*mp, struct ip *); ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); } #endif return rv; } # ifdef USE_INET6 # include static int ipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { int error; CURVNET_SET(ifp->if_vnet); error = ipf_check(&V_ipfmain, mtod(*mp, struct ip *), sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp); CURVNET_RESTORE(); return (error); } # endif #if defined(IPFILTER_LKM) int ipf_identify(s) char *s; { if (strcmp(s, "ipl") == 0) return 1; return 0; } #endif /* IPFILTER_LKM */ static void ipf_timer_func(arg) void *arg; { ipf_main_softc_t *softc = arg; SPL_INT(s); SPL_NET(s); READ_ENTER(&softc->ipf_global); if (softc->ipf_running > 0) ipf_slowtimer(softc); if (softc->ipf_running == -1 || softc->ipf_running == 1) { #if 0 softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2); #endif callout_init(&softc->ipf_slow_ch, 1); callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT, ipf_timer_func, softc); } RWLOCK_EXIT(&softc->ipf_global); SPL_X(s); } int ipfattach(softc) ipf_main_softc_t *softc; { #ifdef USE_SPL int s; #endif SPL_NET(s); if (softc->ipf_running > 0) { SPL_X(s); return EBUSY; } if (ipf_init_all(softc) < 0) { SPL_X(s); return EIO; } bzero((char *)V_ipfmain.ipf_selwait, sizeof(V_ipfmain.ipf_selwait)); softc->ipf_running = 1; if (softc->ipf_control_forwarding & 1) V_ipforwarding = 1; SPL_X(s); #if 0 softc->ipf_slow_ch = timeout(ipf_timer_func, softc, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); #endif callout_init(&softc->ipf_slow_ch, 1); callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT, ipf_timer_func, softc); return 0; } /* * Disable the filter by removing the hooks from the IP input/output * stream. */ int ipfdetach(softc) ipf_main_softc_t *softc; { #ifdef USE_SPL int s; #endif if (softc->ipf_control_forwarding & 2) V_ipforwarding = 0; SPL_NET(s); #if 0 if (softc->ipf_slow_ch.callout != NULL) untimeout(ipf_timer_func, softc, softc->ipf_slow_ch); bzero(&softc->ipf_slow, sizeof(softc->ipf_slow)); #endif callout_drain(&softc->ipf_slow_ch); ipf_fini_all(softc); softc->ipf_running = -2; SPL_X(s); return 0; } /* * Filter ioctl interface. */ int ipfioctl(dev, cmd, data, mode, p) struct thread *p; # define p_cred td_ucred # define p_uid td_ucred->cr_ruid struct cdev *dev; ioctlcmd_t cmd; caddr_t data; int mode; { int error = 0, unit = 0; SPL_INT(s); CURVNET_SET(TD_TO_VNET(p)); #if (BSD >= 199306) if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) { V_ipfmain.ipf_interror = 130001; CURVNET_RESTORE(); return EPERM; } #endif unit = GET_MINOR(dev); if ((IPL_LOGMAX < unit) || (unit < 0)) { V_ipfmain.ipf_interror = 130002; CURVNET_RESTORE(); return ENXIO; } if (V_ipfmain.ipf_running <= 0) { if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) { V_ipfmain.ipf_interror = 130003; CURVNET_RESTORE(); return EIO; } if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && cmd != SIOCIPFSET && cmd != SIOCFRENB && cmd != SIOCGETFS && cmd != SIOCGETFF && cmd != SIOCIPFINTERROR) { V_ipfmain.ipf_interror = 130004; CURVNET_RESTORE(); return EIO; } } SPL_NET(s); error = ipf_ioctlswitch(&V_ipfmain, unit, data, cmd, mode, p->p_uid, p); CURVNET_RESTORE(); if (error != -1) { SPL_X(s); return error; } SPL_X(s); return error; } /* * ipf_send_reset - this could conceivably be a call to tcp_respond(), but that * requires a large amount of setting up and isn't any more efficient. */ int ipf_send_reset(fin) fr_info_t *fin; { struct tcphdr *tcp, *tcp2; int tlen = 0, hlen; struct mbuf *m; #ifdef USE_INET6 ip6_t *ip6; #endif ip_t *ip; tcp = fin->fin_dp; if (tcp->th_flags & TH_RST) return -1; /* feedback loop */ if (ipf_checkl4sum(fin) == -1) return -1; tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); #ifdef USE_INET6 hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t); #else hlen = sizeof(ip_t); #endif #ifdef MGETHDR MGETHDR(m, M_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) return -1; if (sizeof(*tcp2) + hlen > MLEN) { if (!(MCLGET(m, M_NOWAIT))) { FREE_MB_T(m); return -1; } } m->m_len = sizeof(*tcp2) + hlen; #if (BSD >= 199103) m->m_data += max_linkhdr; m->m_pkthdr.len = m->m_len; m->m_pkthdr.rcvif = (struct ifnet *)0; #endif ip = mtod(m, struct ip *); bzero((char *)ip, hlen); #ifdef USE_INET6 ip6 = (ip6_t *)ip; #endif tcp2 = (struct tcphdr *)((char *)ip + hlen); tcp2->th_sport = tcp->th_dport; tcp2->th_dport = tcp->th_sport; if (tcp->th_flags & TH_ACK) { tcp2->th_seq = tcp->th_ack; tcp2->th_flags = TH_RST; tcp2->th_ack = 0; } else { tcp2->th_seq = 0; tcp2->th_ack = ntohl(tcp->th_seq); tcp2->th_ack += tlen; tcp2->th_ack = htonl(tcp2->th_ack); tcp2->th_flags = TH_RST|TH_ACK; } TCP_X2_A(tcp2, 0); TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2); tcp2->th_win = tcp->th_win; tcp2->th_sum = 0; tcp2->th_urp = 0; #ifdef USE_INET6 if (fin->fin_v == 6) { ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; ip6->ip6_plen = htons(sizeof(struct tcphdr)); ip6->ip6_nxt = IPPROTO_TCP; ip6->ip6_hlim = 0; ip6->ip6_src = fin->fin_dst6.in6; ip6->ip6_dst = fin->fin_src6.in6; tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*ip6), sizeof(*tcp2)); return ipf_send_ip(fin, m); } #endif ip->ip_p = IPPROTO_TCP; ip->ip_len = htons(sizeof(struct tcphdr)); ip->ip_src.s_addr = fin->fin_daddr; ip->ip_dst.s_addr = fin->fin_saddr; tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2)); ip->ip_len = htons(hlen + sizeof(*tcp2)); return ipf_send_ip(fin, m); } /* * ip_len must be in network byte order when called. */ static int ipf_send_ip(fin, m) fr_info_t *fin; mb_t *m; { fr_info_t fnew; ip_t *ip, *oip; int hlen; ip = mtod(m, ip_t *); bzero((char *)&fnew, sizeof(fnew)); fnew.fin_main_soft = fin->fin_main_soft; IP_V_A(ip, fin->fin_v); switch (fin->fin_v) { case 4 : oip = fin->fin_ip; hlen = sizeof(*oip); fnew.fin_v = 4; fnew.fin_p = ip->ip_p; fnew.fin_plen = ntohs(ip->ip_len); IP_HL_A(ip, sizeof(*oip) >> 2); ip->ip_tos = oip->ip_tos; ip->ip_id = fin->fin_ip->ip_id; ip->ip_off = htons(V_path_mtu_discovery ? IP_DF : 0); ip->ip_ttl = V_ip_defttl; ip->ip_sum = 0; break; #ifdef USE_INET6 case 6 : { ip6_t *ip6 = (ip6_t *)ip; ip6->ip6_vfc = 0x60; ip6->ip6_hlim = IPDEFTTL; hlen = sizeof(*ip6); fnew.fin_p = ip6->ip6_nxt; fnew.fin_v = 6; fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen; break; } #endif default : return EINVAL; } #ifdef IPSEC m->m_pkthdr.rcvif = NULL; #endif fnew.fin_ifp = fin->fin_ifp; fnew.fin_flx = FI_NOCKSUM; fnew.fin_m = m; fnew.fin_ip = ip; fnew.fin_mp = &m; fnew.fin_hlen = hlen; fnew.fin_dp = (char *)ip + hlen; (void) ipf_makefrip(hlen, ip, &fnew); return ipf_fastroute(m, &m, &fnew, NULL); } int ipf_send_icmp_err(type, fin, dst) int type; fr_info_t *fin; int dst; { int err, hlen, xtra, iclen, ohlen, avail, code; struct in_addr dst4; struct icmp *icmp; struct mbuf *m; i6addr_t dst6; void *ifp; #ifdef USE_INET6 ip6_t *ip6; #endif ip_t *ip, *ip2; if ((type < 0) || (type >= ICMP_MAXTYPE)) return -1; code = fin->fin_icode; #ifdef USE_INET6 /* See NetBSD ip_fil_netbsd.c r1.4: */ if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int))) return -1; #endif if (ipf_checkl4sum(fin) == -1) return -1; #ifdef MGETHDR MGETHDR(m, M_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) return -1; avail = MHLEN; xtra = 0; hlen = 0; ohlen = 0; dst4.s_addr = 0; ifp = fin->fin_ifp; if (fin->fin_v == 4) { if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT)) switch (ntohs(fin->fin_data[0]) >> 8) { case ICMP_ECHO : case ICMP_TSTAMP : case ICMP_IREQ : case ICMP_MASKREQ : break; default : FREE_MB_T(m); return 0; } if (dst == 0) { if (ipf_ifpaddr(&V_ipfmain, 4, FRI_NORMAL, ifp, &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } dst4 = dst6.in4; } else dst4.s_addr = fin->fin_daddr; hlen = sizeof(ip_t); ohlen = fin->fin_hlen; iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; if (fin->fin_hlen < fin->fin_plen) xtra = MIN(fin->fin_dlen, 8); else xtra = 0; } #ifdef USE_INET6 else if (fin->fin_v == 6) { hlen = sizeof(ip6_t); ohlen = sizeof(ip6_t); iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; type = icmptoicmp6types[type]; if (type == ICMP6_DST_UNREACH) code = icmptoicmp6unreach[code]; if (iclen + max_linkhdr + fin->fin_plen > avail) { if (!(MCLGET(m, M_NOWAIT))) { FREE_MB_T(m); return -1; } avail = MCLBYTES; } xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr); xtra = MIN(xtra, IPV6_MMTU - iclen); if (dst == 0) { if (ipf_ifpaddr(&V_ipfmain, 6, FRI_NORMAL, ifp, &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } } else dst6 = fin->fin_dst6; } #endif else { FREE_MB_T(m); return -1; } avail -= (max_linkhdr + iclen); if (avail < 0) { FREE_MB_T(m); return -1; } if (xtra > avail) xtra = avail; iclen += xtra; m->m_data += max_linkhdr; m->m_pkthdr.rcvif = (struct ifnet *)0; m->m_pkthdr.len = iclen; m->m_len = iclen; ip = mtod(m, ip_t *); icmp = (struct icmp *)((char *)ip + hlen); ip2 = (ip_t *)&icmp->icmp_ip; icmp->icmp_type = type; icmp->icmp_code = fin->fin_icode; icmp->icmp_cksum = 0; #ifdef icmp_nextmtu if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG) { if (fin->fin_mtu != 0) { icmp->icmp_nextmtu = htons(fin->fin_mtu); } else if (ifp != NULL) { icmp->icmp_nextmtu = htons(GETIFMTU_4(ifp)); } else { /* make up a number... */ icmp->icmp_nextmtu = htons(fin->fin_plen - 20); } } #endif bcopy((char *)fin->fin_ip, (char *)ip2, ohlen); #ifdef USE_INET6 ip6 = (ip6_t *)ip; if (fin->fin_v == 6) { ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; ip6->ip6_plen = htons(iclen - hlen); ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 0; ip6->ip6_src = dst6.in6; ip6->ip6_dst = fin->fin_src6.in6; if (xtra > 0) bcopy((char *)fin->fin_ip + ohlen, (char *)&icmp->icmp_ip + ohlen, xtra); icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), iclen - hlen); } else #endif { ip->ip_p = IPPROTO_ICMP; ip->ip_src.s_addr = dst4.s_addr; ip->ip_dst.s_addr = fin->fin_saddr; if (xtra > 0) bcopy((char *)fin->fin_ip + ohlen, (char *)&icmp->icmp_ip + ohlen, xtra); icmp->icmp_cksum = ipf_cksum((u_short *)icmp, sizeof(*icmp) + 8); ip->ip_len = htons(iclen); ip->ip_p = IPPROTO_ICMP; } err = ipf_send_ip(fin, m); return err; } /* * m0 - pointer to mbuf where the IP packet starts * mpp - pointer to the mbuf pointer that is the start of the mbuf chain */ int ipf_fastroute(m0, mpp, fin, fdp) mb_t *m0, **mpp; fr_info_t *fin; frdest_t *fdp; { register struct ip *ip, *mhip; register struct mbuf *m = *mpp; int len, off, error = 0, hlen, code; struct ifnet *ifp, *sifp; struct sockaddr_in dst; struct nhop4_extended nh4; u_long fibnum = 0; u_short ip_off; frdest_t node; frentry_t *fr; #ifdef M_WRITABLE /* * HOT FIX/KLUDGE: * * If the mbuf we're about to send is not writable (because of * a cluster reference, for example) we'll need to make a copy * of it since this routine modifies the contents. * * If you have non-crappy network hardware that can transmit data * from the mbuf, rather than making a copy, this is gonna be a * problem. */ if (M_WRITABLE(m) == 0) { m0 = m_dup(m, M_NOWAIT); if (m0 != NULL) { FREE_MB_T(m); m = m0; *mpp = m; } else { error = ENOBUFS; FREE_MB_T(m); goto done; } } #endif #ifdef USE_INET6 if (fin->fin_v == 6) { /* * currently "to " and "to :ip#" are not supported * for IPv6 */ return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); } #endif hlen = fin->fin_hlen; ip = mtod(m0, struct ip *); ifp = NULL; /* * Route packet. */ bzero(&dst, sizeof (dst)); dst.sin_family = AF_INET; dst.sin_addr = ip->ip_dst; dst.sin_len = sizeof(dst); fr = fin->fin_fr; if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) && (fdp->fd_type == FRD_DSTLIST)) { if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0) fdp = &node; } if (fdp != NULL) ifp = fdp->fd_ptr; else ifp = fin->fin_ifp; if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) { error = -2; goto bad; } if ((fdp != NULL) && (fdp->fd_ip.s_addr != 0)) dst.sin_addr = fdp->fd_ip; fibnum = M_GETFIB(m0); if (fib4_lookup_nh_ext(fibnum, dst.sin_addr, NHR_REF, 0, &nh4) != 0) { if (in_localaddr(ip->ip_dst)) error = EHOSTUNREACH; else error = ENETUNREACH; goto bad; } if (ifp == NULL) ifp = nh4.nh_ifp; if (nh4.nh_flags & NHF_GATEWAY) dst.sin_addr = nh4.nh_addr; /* * For input packets which are being "fastrouted", they won't * go back through output filtering and miss their chance to get * NAT'd and counted. Duplicated packets aren't considered to be * part of the normal packet stream, so do not NAT them or pass * them through stateful checking, etc. */ if ((fdp != &fr->fr_dif) && (fin->fin_out == 0)) { sifp = fin->fin_ifp; 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 bad; break; } fin->fin_ifp = sifp; fin->fin_out = 0; } else ip->ip_sum = 0; /* * If small enough for interface, can just send directly. */ if (ntohs(ip->ip_len) <= ifp->if_mtu) { if (!ip->ip_sum) ip->ip_sum = in_cksum(m, hlen); error = (*ifp->if_output)(ifp, m, (struct sockaddr *)&dst, NULL ); goto done; } /* * Too large for interface; fragment if possible. * Must be able to put at least 8 bytes per fragment. */ ip_off = ntohs(ip->ip_off); if (ip_off & IP_DF) { error = EMSGSIZE; goto bad; } len = (ifp->if_mtu - hlen) &~ 7; if (len < 8) { error = EMSGSIZE; goto bad; } { int mhlen, firstlen = len; struct mbuf **mnext = &m->m_act; /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. */ m0 = m; mhlen = sizeof (struct ip); for (off = hlen + len; off < ntohs(ip->ip_len); off += len) { #ifdef MGETHDR MGETHDR(m, M_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) { m = m0; error = ENOBUFS; goto bad; } m->m_data += max_linkhdr; mhip = mtod(m, struct ip *); bcopy((char *)ip, (char *)mhip, sizeof(*ip)); if (hlen > sizeof (struct ip)) { mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); IP_HL_A(mhip, mhlen >> 2); } m->m_len = mhlen; mhip->ip_off = ((off - hlen) >> 3) + ip_off; if (off + len >= ntohs(ip->ip_len)) len = ntohs(ip->ip_len) - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); *mnext = m; m->m_next = m_copym(m0, off, len, M_NOWAIT); if (m->m_next == 0) { error = ENOBUFS; /* ??? */ goto sendorfree; } m->m_pkthdr.len = mhlen + len; m->m_pkthdr.rcvif = NULL; mhip->ip_off = htons((u_short)mhip->ip_off); mhip->ip_sum = 0; mhip->ip_sum = in_cksum(m, mhlen); mnext = &m->m_act; } /* * Update first fragment by trimming what's been copied out * and updating header, then send each fragment (in order). */ m_adj(m0, hlen + firstlen - ip->ip_len); ip->ip_len = htons((u_short)(hlen + firstlen)); ip->ip_off = htons((u_short)IP_MF); ip->ip_sum = 0; ip->ip_sum = in_cksum(m0, hlen); sendorfree: for (m = m0; m; m = m0) { m0 = m->m_act; m->m_act = 0; if (error == 0) error = (*ifp->if_output)(ifp, m, (struct sockaddr *)&dst, NULL ); else FREE_MB_T(m); } } done: if (!error) V_ipfmain.ipf_frouteok[0]++; else V_ipfmain.ipf_frouteok[1]++; return 0; bad: if (error == EMSGSIZE) { sifp = fin->fin_ifp; code = fin->fin_icode; fin->fin_icode = ICMP_UNREACH_NEEDFRAG; fin->fin_ifp = ifp; (void) ipf_send_icmp_err(ICMP_UNREACH, fin, 1); fin->fin_ifp = sifp; fin->fin_icode = code; } FREE_MB_T(m); goto done; } int ipf_verifysrc(fin) fr_info_t *fin; { struct nhop4_basic nh4; if (fib4_lookup_nh_basic(0, fin->fin_src, 0, 0, &nh4) != 0) return (0); return (fin->fin_ifp == nh4.nh_ifp); } /* * 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; { #ifdef USE_INET6 struct in6_addr *ia6 = NULL; #endif struct sockaddr *sock, *mask; struct sockaddr_in *sin; struct ifaddr *ifa; struct ifnet *ifp; if ((ifptr == NULL) || (ifptr == (void *)-1)) return -1; sin = NULL; ifp = ifptr; if (v == 4) inp->in4.s_addr = 0; #ifdef USE_INET6 else if (v == 6) bzero((char *)inp, sizeof(*inp)); #endif ifa = CK_STAILQ_FIRST(&ifp->if_addrhead); sock = ifa->ifa_addr; while (sock != NULL && ifa != NULL) { sin = (struct sockaddr_in *)sock; if ((v == 4) && (sin->sin_family == AF_INET)) break; #ifdef USE_INET6 if ((v == 6) && (sin->sin_family == AF_INET6)) { ia6 = &((struct sockaddr_in6 *)sin)->sin6_addr; if (!IN6_IS_ADDR_LINKLOCAL(ia6) && !IN6_IS_ADDR_LOOPBACK(ia6)) break; } #endif ifa = CK_STAILQ_NEXT(ifa, ifa_link); if (ifa != NULL) sock = ifa->ifa_addr; } if (ifa == NULL || sin == NULL) return -1; mask = ifa->ifa_netmask; if (atype == FRI_BROADCAST) sock = ifa->ifa_broadaddr; else if (atype == FRI_PEERADDR) sock = ifa->ifa_dstaddr; if (sock == NULL) return -1; #ifdef USE_INET6 if (v == 6) { return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, (struct sockaddr_in6 *)mask, inp, inpmask); } #endif return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock, (struct sockaddr_in *)mask, &inp->in4, &inpmask->in4); } u_32_t ipf_newisn(fin) fr_info_t *fin; { u_32_t newiss; newiss = arc4random(); return newiss; } INLINE int ipf_checkv4sum(fin) fr_info_t *fin; { #ifdef CSUM_DATA_VALID int manual = 0; u_short sum; ip_t *ip; mb_t *m; if ((fin->fin_flx & FI_NOCKSUM) != 0) return 0; if ((fin->fin_flx & FI_SHORT) != 0) return 1; if (fin->fin_cksum != FI_CK_NEEDED) return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; m = fin->fin_m; if (m == NULL) { manual = 1; goto skipauto; } ip = fin->fin_ip; if ((m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)) == CSUM_IP_CHECKED) { fin->fin_cksum = FI_CK_BAD; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_csum_ip_checked, fr_info_t *, fin, u_int, m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)); return -1; } if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { /* Depending on the driver, UDP may have zero checksum */ if (fin->fin_p == IPPROTO_UDP && (fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) == 0) { udphdr_t *udp = fin->fin_dp; if (udp->uh_sum == 0) { /* * we're good no matter what the hardware * checksum flags and csum_data say (handling * of csum_data for zero UDP checksum is not * consistent across all drivers) */ fin->fin_cksum = 1; return 0; } } if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) sum = m->m_pkthdr.csum_data; else sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl(m->m_pkthdr.csum_data + fin->fin_dlen + fin->fin_p)); sum ^= 0xffff; if (sum != 0) { fin->fin_cksum = FI_CK_BAD; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_sum, fr_info_t *, fin, u_int, sum); } else { fin->fin_cksum = FI_CK_SUMOK; return 0; } } else { if (m->m_pkthdr.csum_flags == CSUM_DELAY_DATA) { fin->fin_cksum = FI_CK_L4FULL; return 0; } else if (m->m_pkthdr.csum_flags == CSUM_TCP || m->m_pkthdr.csum_flags == CSUM_UDP) { fin->fin_cksum = FI_CK_L4PART; return 0; } else if (m->m_pkthdr.csum_flags == CSUM_IP) { fin->fin_cksum = FI_CK_L4PART; return 0; } else { manual = 1; } } skipauto: if (manual != 0) { if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_manual, fr_info_t *, fin, u_int, manual); return -1; } } #else if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_checkl4sum, fr_info_t *, fin, u_int, -1); return -1; } #endif return 0; } #ifdef USE_INET6 INLINE int ipf_checkv6sum(fin) fr_info_t *fin; { if ((fin->fin_flx & FI_NOCKSUM) != 0) { DT(ipf_checkv6sum_fi_nocksum); return 0; } if ((fin->fin_flx & FI_SHORT) != 0) { DT(ipf_checkv6sum_fi_short); return 1; } if (fin->fin_cksum != FI_CK_NEEDED) { DT(ipf_checkv6sum_fi_ck_needed); return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; } if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv6sum_checkl4sum, fr_info_t *, fin, u_int, -1); return -1; } return 0; } #endif /* USE_INET6 */ size_t mbufchainlen(m0) struct mbuf *m0; { size_t len; if ((m0->m_flags & M_PKTHDR) != 0) { len = m0->m_pkthdr.len; } else { struct mbuf *m; for (m = m0, len = 0; m != NULL; m = m->m_next) len += m->m_len; } return len; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pullup */ /* Returns: NULL == pullup failed, else pointer to protocol header */ /* Parameters: xmin(I)- pointer to buffer where data packet starts */ /* fin(I) - pointer to packet information */ /* len(I) - number of bytes to pullup */ /* */ /* Attempt to move at least len bytes (from the start of the buffer) into a */ /* single buffer for ease of access. Operating system native functions are */ /* used to manage buffers - if necessary. If the entire packet ends up in */ /* a single buffer, set the FI_COALESCE flag even though ipf_coalesce() has */ /* not been called. Both fin_ip and fin_dp are updated before exiting _IF_ */ /* and ONLY if the pullup succeeds. */ /* */ /* We assume that 'xmin' is a pointer to a buffer that is part of the chain */ /* of buffers that starts at *fin->fin_mp. */ /* ------------------------------------------------------------------------ */ void * ipf_pullup(xmin, fin, len) mb_t *xmin; fr_info_t *fin; int len; { int dpoff, ipoff; mb_t *m = xmin; char *ip; if (m == NULL) return NULL; ip = (char *)fin->fin_ip; if ((fin->fin_flx & FI_COALESCE) != 0) return ip; ipoff = fin->fin_ipoff; if (fin->fin_dp != NULL) dpoff = (char *)fin->fin_dp - (char *)ip; else dpoff = 0; if (M_LEN(m) < len) { mb_t *n = *fin->fin_mp; /* * Assume that M_PKTHDR is set and just work with what is left * rather than check.. * Should not make any real difference, anyway. */ if (m != n) { /* * Record the mbuf that points to the mbuf that we're * about to go to work on so that we can update the * m_next appropriately later. */ for (; n->m_next != m; n = n->m_next) ; } else { n = NULL; } #ifdef MHLEN if (len > MHLEN) #else if (len > MLEN) #endif { #ifdef HAVE_M_PULLDOWN if (m_pulldown(m, 0, len, NULL) == NULL) m = NULL; #else FREE_MB_T(*fin->fin_mp); m = NULL; n = NULL; #endif } else { m = m_pullup(m, len); } if (n != NULL) n->m_next = m; if (m == NULL) { /* * When n is non-NULL, it indicates that m pointed to * a sub-chain (tail) of the mbuf and that the head * of this chain has not yet been free'd. */ if (n != NULL) { FREE_MB_T(*fin->fin_mp); } *fin->fin_mp = NULL; fin->fin_m = NULL; return NULL; } if (n == NULL) *fin->fin_mp = m; while (M_LEN(m) == 0) { m = m->m_next; } fin->fin_m = m; ip = MTOD(m, char *) + ipoff; fin->fin_ip = (ip_t *)ip; if (fin->fin_dp != NULL) fin->fin_dp = (char *)fin->fin_ip + dpoff; if (fin->fin_fraghdr != NULL) fin->fin_fraghdr = (char *)ip + ((char *)fin->fin_fraghdr - (char *)fin->fin_ip); } if (len == fin->fin_plen) fin->fin_flx |= FI_COALESCE; return ip; } int ipf_inject(fin, m) fr_info_t *fin; mb_t *m; { int error = 0; if (fin->fin_out == 0) { netisr_dispatch(NETISR_IP, m); } else { fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len); fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off); error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); } return error; } int ipf_pfil_unhook(void) { struct pfil_head *ph_inet; #ifdef USE_INET6 struct pfil_head *ph_inet6; #endif ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (ph_inet != NULL) pfil_remove_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); # ifdef USE_INET6 ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); if (ph_inet6 != NULL) pfil_remove_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); # endif return (0); } int ipf_pfil_hook(void) { struct pfil_head *ph_inet; #ifdef USE_INET6 struct pfil_head *ph_inet6; #endif ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); # ifdef USE_INET6 ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); # endif if (ph_inet == NULL # ifdef USE_INET6 && ph_inet6 == NULL # endif ) { return ENODEV; } if (ph_inet != NULL) pfil_add_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); # ifdef USE_INET6 if (ph_inet6 != NULL) pfil_add_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); # endif return (0); } void ipf_event_reg(void) { V_ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); V_ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); #if 0 V_ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ NULL, EVENTHANDLER_PRI_ANY); #endif } void ipf_event_dereg(void) { if (V_ipf_arrivetag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_arrival_event, V_ipf_arrivetag); } if (V_ipf_departtag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_departure_event, V_ipf_departtag); } #if 0 if (V_ipf_clonetag != NULL) { EVENTHANDLER_DEREGISTER(if_clone_event, V_ipf_clonetag); } #endif } u_32_t ipf_random() { return arc4random(); } u_int ipf_pcksum(fin, hlen, sum) fr_info_t *fin; int hlen; u_int sum; { struct mbuf *m; u_int sum2; int off; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; m->m_data += hlen; m->m_len -= hlen; sum2 = in_cksum(fin->fin_m, fin->fin_plen - off); m->m_len += hlen; m->m_data -= hlen; /* * Both sum and sum2 are partial sums, so combine them together. */ sum += ~sum2 & 0xffff; while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum2 = ~sum & 0xffff; return sum2; } #ifdef USE_INET6 u_int ipf_pcksum6(m, ip6, off, len) struct mbuf *m; ip6_t *ip6; u_int32_t off; u_int32_t len; { #ifdef _KERNEL int sum; if (m->m_len < sizeof(struct ip6_hdr)) { return 0xffff; } sum = in6_cksum(m, ip6->ip6_nxt, off, len); return(sum); #else u_short *sp; u_int sum; sp = (u_short *)&ip6->ip6_src; sum = *sp++; /* ip6_src */ sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; /* ip6_dst */ sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; return(ipf_pcksum(fin, off, sum)); #endif } #endif diff --git a/sys/contrib/ipfilter/netinet/ip_frag.c b/sys/contrib/ipfilter/netinet/ip_frag.c index 28d306da2a60..0636f27c81e8 100644 --- a/sys/contrib/ipfilter/netinet/ip_frag.c +++ b/sys/contrib/ipfilter/netinet/ip_frag.c @@ -1,1360 +1,1360 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD_version) # include # include #else # include #endif # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if !defined(__SVR4) # if defined(_KERNEL) # include # endif #else # include # ifdef _KERNEL # include # endif # include # include #endif #include #ifdef sun # include #endif #include #include #include # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_auth.h" #include "netinet/ip_lookup.h" #include "netinet/ip_proxy.h" #include "netinet/ip_sync.h" /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.77.2.12 2007/09/20 12:51:51 darrenr Exp $"; */ #endif #ifdef USE_MUTEXES -static ipfr_t *ipfr_frag_new __P((ipf_main_softc_t *, ipf_frag_softc_t *, +static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, u_32_t, ipfr_t **, - ipfrwlock_t *)); -static ipfr_t *ipf_frag_lookup __P((ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *)); -static void ipf_frag_deref __P((void *, ipfr_t **, ipfrwlock_t *)); -static int ipf_frag_next __P((ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, - ipfr_t **, ipfrwlock_t *)); + ipfrwlock_t *); +static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *); +static void ipf_frag_deref(void *, ipfr_t **, ipfrwlock_t *); +static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, + ipfr_t **, ipfrwlock_t *); #else -static ipfr_t *ipfr_frag_new __P((ipf_main_softc_t *, ipf_frag_softc_t *, - fr_info_t *, u_32_t, ipfr_t **)); -static ipfr_t *ipf_frag_lookup __P((ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **)); -static void ipf_frag_deref __P((void *, ipfr_t **)); -static int ipf_frag_next __P((ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, - ipfr_t **)); +static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, + fr_info_t *, u_32_t, ipfr_t **); +static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **); +static void ipf_frag_deref(void *, ipfr_t **); +static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, + ipfr_t **); #endif -static void ipf_frag_delete __P((ipf_main_softc_t *, ipfr_t *, ipfr_t ***)); -static void ipf_frag_free __P((ipf_frag_softc_t *, ipfr_t *)); +static void ipf_frag_delete(ipf_main_softc_t *, ipfr_t *, ipfr_t ***); +static void ipf_frag_free(ipf_frag_softc_t *, ipfr_t *); static frentry_t ipfr_block; static ipftuneable_t ipf_frag_tuneables[] = { { { (void *)offsetof(ipf_frag_softc_t, ipfr_size) }, "frag_size", 1, 0x7fffffff, stsizeof(ipf_frag_softc_t, ipfr_size), IPFT_WRDISABLED, NULL, NULL }, { { (void *)offsetof(ipf_frag_softc_t, ipfr_ttl) }, "frag_ttl", 1, 0x7fffffff, stsizeof(ipf_frag_softc_t, ipfr_ttl), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; #define FBUMP(x) softf->ipfr_stats.x++ #define FBUMPD(x) do { softf->ipfr_stats.x++; DT(x); } while (0) /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_main_load */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: Nil */ /* */ /* Initialise the filter rule associted with blocked packets - everyone can */ /* use it. */ /* ------------------------------------------------------------------------ */ int ipf_frag_main_load() { bzero((char *)&ipfr_block, sizeof(ipfr_block)); ipfr_block.fr_flags = FR_BLOCK|FR_QUICK; ipfr_block.fr_ref = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_main_unload */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_frag_main_unload() { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocate a new soft context structure to track fragment related info. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ void * ipf_frag_soft_create(softc) ipf_main_softc_t *softc; { ipf_frag_softc_t *softf; KMALLOC(softf, ipf_frag_softc_t *); if (softf == NULL) return NULL; bzero((char *)softf, sizeof(*softf)); RWLOCK_INIT(&softf->ipfr_ipidfrag, "frag ipid lock"); RWLOCK_INIT(&softf->ipfr_frag, "ipf fragment rwlock"); RWLOCK_INIT(&softf->ipfr_natfrag, "ipf NAT fragment rwlock"); softf->ipf_frag_tune = ipf_tune_array_copy(softf, sizeof(ipf_frag_tuneables), ipf_frag_tuneables); if (softf->ipf_frag_tune == NULL) { ipf_frag_soft_destroy(softc, softf); return NULL; } if (ipf_tune_array_link(softc, softf->ipf_frag_tune) == -1) { ipf_frag_soft_destroy(softc, softf); return NULL; } softf->ipfr_size = IPFT_SIZE; softf->ipfr_ttl = IPF_TTLVAL(60); softf->ipfr_lock = 1; softf->ipfr_tail = &softf->ipfr_list; softf->ipfr_nattail = &softf->ipfr_natlist; softf->ipfr_ipidtail = &softf->ipfr_ipidlist; return softf; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash tables for the fragment cache lookups. */ /* ------------------------------------------------------------------------ */ void ipf_frag_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_frag_softc_t *softf = arg; RW_DESTROY(&softf->ipfr_ipidfrag); RW_DESTROY(&softf->ipfr_frag); RW_DESTROY(&softf->ipfr_natfrag); if (softf->ipf_frag_tune != NULL) { ipf_tune_array_unlink(softc, softf->ipf_frag_tune); KFREES(softf->ipf_frag_tune, sizeof(ipf_frag_tuneables)); softf->ipf_frag_tune = NULL; } KFREE(softf); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_init */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash tables for the fragment cache lookups. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ int ipf_frag_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_frag_softc_t *softf = arg; KMALLOCS(softf->ipfr_heads, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_heads == NULL) return -1; bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); KMALLOCS(softf->ipfr_nattab, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_nattab == NULL) return -2; bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); KMALLOCS(softf->ipfr_ipidtab, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_ipidtab == NULL) return -3; bzero((char *)softf->ipfr_ipidtab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_lock = 0; softf->ipfr_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_fini */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Free all memory allocated whilst running and from initialisation. */ /* ------------------------------------------------------------------------ */ int ipf_frag_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_frag_softc_t *softf = arg; softf->ipfr_lock = 1; if (softf->ipfr_inited == 1) { ipf_frag_clear(softc); softf->ipfr_inited = 0; } if (softf->ipfr_heads != NULL) KFREES(softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_heads = NULL; if (softf->ipfr_nattab != NULL) KFREES(softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_nattab = NULL; if (softf->ipfr_ipidtab != NULL) KFREES(softf->ipfr_ipidtab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_ipidtab = NULL; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_set_lock */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to local context to use */ /* tmp(I) - new value for lock */ /* */ /* Stub function that allows for external manipulation of ipfr_lock */ /* ------------------------------------------------------------------------ */ void ipf_frag_setlock(arg, tmp) void *arg; int tmp; { ipf_frag_softc_t *softf = arg; softf->ipfr_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_stats */ /* Returns: ipfrstat_t* - pointer to struct with current frag stats */ /* Parameters: arg(I) - pointer to local context to use */ /* */ /* Updates ipfr_stats with current information and returns a pointer to it */ /* ------------------------------------------------------------------------ */ ipfrstat_t * ipf_frag_stats(arg) void *arg; { ipf_frag_softc_t *softf = arg; softf->ipfr_stats.ifs_table = softf->ipfr_heads; softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab; return &softf->ipfr_stats; } /* ------------------------------------------------------------------------ */ /* Function: ipfr_frag_new */ /* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* table(I) - pointer to frag table to add to */ /* lock(I) - pointer to lock to get a write hold of */ /* */ /* Add a new entry to the fragment cache, registering it as having come */ /* through this box, with the result of the filter operation. */ /* */ /* If this function succeeds, it returns with a write lock held on "lock". */ /* If it fails, no lock is held on return. */ /* ------------------------------------------------------------------------ */ static ipfr_t * ipfr_frag_new(softc, softf, fin, pass, table #ifdef USE_MUTEXES , lock #endif ) ipf_main_softc_t *softc; ipf_frag_softc_t *softf; fr_info_t *fin; u_32_t pass; ipfr_t *table[]; #ifdef USE_MUTEXES ipfrwlock_t *lock; #endif { ipfr_t *fra, frag, *fran; u_int idx, off; frentry_t *fr; if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { FBUMPD(ifs_maximum); return NULL; } if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { FBUMPD(ifs_newbad); return NULL; } if (pass & FR_FRSTRICT) { if (fin->fin_off != 0) { FBUMPD(ifs_newrestrictnot0); return NULL; } } memset(&frag, 0, sizeof(frag)); frag.ipfr_v = fin->fin_v; idx = fin->fin_v; frag.ipfr_p = fin->fin_p; idx += fin->fin_p; frag.ipfr_id = fin->fin_id; idx += fin->fin_id; frag.ipfr_source = fin->fin_fi.fi_src; idx += frag.ipfr_src.s_addr; frag.ipfr_dest = fin->fin_fi.fi_dst; idx += frag.ipfr_dst.s_addr; frag.ipfr_ifp = fin->fin_ifp; idx *= 127; idx %= softf->ipfr_size; frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; frag.ipfr_auth = fin->fin_fi.fi_auth; off = fin->fin_off >> 3; if (off == 0) { char *ptr; int end; #ifdef USE_INET6 if (fin->fin_v == 6) { ptr = (char *)fin->fin_fraghdr + sizeof(struct ip6_frag); } else #endif { ptr = fin->fin_dp; } end = fin->fin_plen - (ptr - (char *)fin->fin_ip); frag.ipfr_firstend = end >> 3; } else { frag.ipfr_firstend = 0; } /* * allocate some memory, if possible, if not, just record that we * failed to do so. */ KMALLOC(fran, ipfr_t *); if (fran == NULL) { FBUMPD(ifs_nomem); return NULL; } memset(fran, 0, sizeof(*fran)); WRITE_ENTER(lock); /* * first, make sure it isn't already there... */ for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ)) { RWLOCK_EXIT(lock); FBUMPD(ifs_exists); KFREE(fran); return NULL; } fra = fran; fran = NULL; fr = fin->fin_fr; fra->ipfr_rule = fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } /* * Insert the fragment into the fragment table, copy the struct used * in the search using bcopy rather than reassign each field. * Set the ttl to the default. */ if ((fra->ipfr_hnext = table[idx]) != NULL) table[idx]->ipfr_hprev = &fra->ipfr_hnext; fra->ipfr_hprev = table + idx; fra->ipfr_data = NULL; table[idx] = fra; bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); fra->ipfr_v = fin->fin_v; fra->ipfr_p = fin->fin_p; fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl; fra->ipfr_firstend = frag.ipfr_firstend; /* * Compute the offset of the expected start of the next packet. */ if (off == 0) fra->ipfr_seen0 = 1; fra->ipfr_off = off + (fin->fin_dlen >> 3); fra->ipfr_pass = pass; fra->ipfr_ref = 1; fra->ipfr_pkts = 1; fra->ipfr_bytes = fin->fin_plen; FBUMP(ifs_inuse); FBUMP(ifs_new); return fra; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_new */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Add a new entry to the fragment cache table based on the current packet */ /* ------------------------------------------------------------------------ */ int ipf_frag_new(softc, fin, pass) ipf_main_softc_t *softc; u_32_t pass; fr_info_t *fin; { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock != 0) return -1; #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag); #else fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads); #endif if (fra != NULL) { *softf->ipfr_tail = fra; fra->ipfr_prev = softf->ipfr_tail; softf->ipfr_tail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softc->ipf_frag); } return fra ? 0 : -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* */ /* Create a new NAT fragment cache entry based on the current packet and */ /* the NAT structure for this "session". */ /* ------------------------------------------------------------------------ */ int ipf_frag_natnew(softc, fin, pass, nat) ipf_main_softc_t *softc; fr_info_t *fin; u_32_t pass; nat_t *nat; { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock != 0) return 0; #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab, &softf->ipfr_natfrag); #else fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab); #endif if (fra != NULL) { fra->ipfr_data = nat; nat->nat_data = fra; *softf->ipfr_nattail = fra; fra->ipfr_prev = softf->ipfr_nattail; softf->ipfr_nattail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softf->ipfr_natfrag); return 0; } return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_ipidnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* ipid(I) - new IP ID for this fragmented packet */ /* */ /* Create a new fragment cache entry for this packet and store, as a data */ /* pointer, the new IP ID value. */ /* ------------------------------------------------------------------------ */ int ipf_frag_ipidnew(fin, ipid) fr_info_t *fin; u_32_t ipid; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock) return 0; #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); #else fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab); #endif if (fra != NULL) { fra->ipfr_data = (void *)(intptr_t)ipid; *softf->ipfr_ipidtail = fra; fra->ipfr_prev = softf->ipfr_ipidtail; softf->ipfr_ipidtail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softf->ipfr_ipidfrag); } return fra ? 0 : -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_lookup */ /* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ /* matching entry in the frag table, else NULL */ /* Parameters: fin(I) - pointer to packet information */ /* table(I) - pointer to fragment cache table to search */ /* */ /* Check the fragment cache to see if there is already a record of this */ /* packet with its filter result known. */ /* */ /* If this function succeeds, it returns with a write lock held on "lock". */ /* If it fails, no lock is held on return. */ /* ------------------------------------------------------------------------ */ static ipfr_t * ipf_frag_lookup(softc, softf, fin, table #ifdef USE_MUTEXES , lock #endif ) ipf_main_softc_t *softc; ipf_frag_softc_t *softf; fr_info_t *fin; ipfr_t *table[]; #ifdef USE_MUTEXES ipfrwlock_t *lock; #endif { ipfr_t *f, frag; u_int idx; /* * We don't want to let short packets match because they could be * compromising the security of other rules that want to match on * layer 4 fields (and can't because they have been fragmented off.) * Why do this check here? The counter acts as an indicator of this * kind of attack, whereas if it was elsewhere, it wouldn't know if * other matching packets had been seen. */ if (fin->fin_flx & FI_SHORT) { FBUMPD(ifs_short); return NULL; } if ((fin->fin_flx & FI_BAD) != 0) { FBUMPD(ifs_bad); return NULL; } /* * For fragments, we record protocol, packet id, TOS and both IP#'s * (these should all be the same for all fragments of a packet). * * build up a hash value to index the table with. */ memset(&frag, 0, sizeof(frag)); frag.ipfr_v = fin->fin_v; idx = fin->fin_v; frag.ipfr_p = fin->fin_p; idx += fin->fin_p; frag.ipfr_id = fin->fin_id; idx += fin->fin_id; frag.ipfr_source = fin->fin_fi.fi_src; idx += frag.ipfr_src.s_addr; frag.ipfr_dest = fin->fin_fi.fi_dst; idx += frag.ipfr_dst.s_addr; frag.ipfr_ifp = fin->fin_ifp; idx *= 127; idx %= softf->ipfr_size; frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; frag.ipfr_auth = fin->fin_fi.fi_auth; READ_ENTER(lock); /* * check the table, careful to only compare the right amount of data */ for (f = table[idx]; f; f = f->ipfr_hnext) { if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, IPFR_CMPSZ)) { u_short off; /* * XXX - We really need to be guarding against the * retransmission of (src,dst,id,offset-range) here * because a fragmented packet is never resent with * the same IP ID# (or shouldn't). */ off = fin->fin_off >> 3; if (f->ipfr_seen0) { if (off == 0) { FBUMPD(ifs_retrans0); continue; } /* * Case 3. See comment for frpr_fragment6. */ if ((f->ipfr_firstend != 0) && (off < f->ipfr_firstend)) { FBUMP(ifs_overlap); DT2(ifs_overlap, u_short, off, ipfr_t *, f); DT3(ipf_fi_bad_ifs_overlap, fr_info_t *, fin, u_short, off, ipfr_t *, f); fin->fin_flx |= FI_BAD; break; } } else if (off == 0) f->ipfr_seen0 = 1; if (f != table[idx] && MUTEX_TRY_UPGRADE(lock)) { ipfr_t **fp; /* * Move fragment info. to the top of the list * to speed up searches. First, delink... */ fp = f->ipfr_hprev; (*fp) = f->ipfr_hnext; if (f->ipfr_hnext != NULL) f->ipfr_hnext->ipfr_hprev = fp; /* * Then put back at the top of the chain. */ f->ipfr_hnext = table[idx]; table[idx]->ipfr_hprev = &f->ipfr_hnext; f->ipfr_hprev = table + idx; table[idx] = f; MUTEX_DOWNGRADE(lock); } /* * If we've follwed the fragments, and this is the * last (in order), shrink expiration time. */ if (off == f->ipfr_off) { f->ipfr_off = (fin->fin_dlen >> 3) + off; /* * Well, we could shrink the expiration time * but only if every fragment has been seen * in order upto this, the last. ipfr_badorder * is used here to count those out of order * and if it equals 0 when we get to the last * fragment then we can assume all of the * fragments have been seen and in order. */ #if 0 /* * Doing this properly requires moving it to * the head of the list which is infesible. */ if ((more == 0) && (f->ipfr_badorder == 0)) f->ipfr_ttl = softc->ipf_ticks + 1; #endif } else { f->ipfr_badorder++; FBUMPD(ifs_unordered); if (f->ipfr_pass & FR_FRSTRICT) { FBUMPD(ifs_strict); continue; } } f->ipfr_pkts++; f->ipfr_bytes += fin->fin_plen; FBUMP(ifs_hits); return f; } } RWLOCK_EXIT(lock); FBUMP(ifs_miss); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natknown */ /* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ /* match found, else NULL */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Functional interface for NAT lookups of the NAT fragment cache */ /* ------------------------------------------------------------------------ */ nat_t * ipf_frag_natknown(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; nat_t *nat; ipfr_t *ipf; if ((softf->ipfr_lock) || !softf->ipfr_natlist) return NULL; #ifdef USE_MUTEXES ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, &softf->ipfr_natfrag); #else ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); #endif if (ipf != NULL) { nat = ipf->ipfr_data; /* * This is the last fragment for this packet. */ if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { nat->nat_data = NULL; ipf->ipfr_data = NULL; } RWLOCK_EXIT(&softf->ipfr_natfrag); } else nat = NULL; return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_ipidknown */ /* Returns: u_32_t - IPv4 ID for this packet if match found, else */ /* return 0xfffffff to indicate no match. */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Functional interface for IP ID lookups of the IP ID fragment cache */ /* ------------------------------------------------------------------------ */ u_32_t ipf_frag_ipidknown(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *ipf; u_32_t id; if (softf->ipfr_lock || !softf->ipfr_ipidlist) return 0xffffffff; #ifdef USE_MUTEXES ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); #else ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); #endif if (ipf != NULL) { id = (u_32_t)(intptr_t)ipf->ipfr_data; RWLOCK_EXIT(&softf->ipfr_ipidfrag); } else id = 0xffffffff; return id; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_known */ /* Returns: frentry_t* - pointer to filter rule if a match is found in */ /* the frag cache table, else NULL. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(O) - pointer to where to store rule flags resturned */ /* */ /* Functional interface for normal lookups of the fragment cache. If a */ /* match is found, return the rule pointer and flags from the rule, except */ /* that if FR_LOGFIRST is set, reset FR_LOG. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_frag_known(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; frentry_t *fr = NULL; ipfr_t *fra; u_32_t pass; if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) return NULL; #ifdef USE_MUTEXES fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, &softc->ipf_frag); #else fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); #endif if (fra != NULL) { if (fin->fin_flx & FI_BAD) { fr = &ipfr_block; fin->fin_reason = FRB_BADFRAG; DT2(ipf_frb_badfrag, fr_info_t *, fin, uint, fra); } else { fr = fra->ipfr_rule; } fin->fin_fr = fr; if (fr != NULL) { pass = fr->fr_flags; if ((pass & FR_KEEPSTATE) != 0) { fin->fin_flx |= FI_STATE; /* * Reset the keep state flag here so that we * don't try and add a new state entry because * of a match here. That leads to blocking of * the packet later because the add fails. */ pass &= ~FR_KEEPSTATE; } if ((pass & FR_LOGFIRST) != 0) pass &= ~(FR_LOGFIRST|FR_LOG); *passp = pass; } RWLOCK_EXIT(&softc->ipf_frag); } return fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natforget */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ptr(I) - pointer to data structure */ /* */ /* Search through all of the fragment cache entries for NAT and wherever a */ /* pointer is found to match ptr, reset it to NULL. */ /* ------------------------------------------------------------------------ */ void ipf_frag_natforget(softc, ptr) ipf_main_softc_t *softc; void *ptr; { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fr; WRITE_ENTER(&softf->ipfr_natfrag); for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) if (fr->ipfr_data == ptr) fr->ipfr_data = NULL; RWLOCK_EXIT(&softf->ipfr_natfrag); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fra(I) - pointer to fragment structure to delete */ /* tail(IO) - pointer to the pointer to the tail of the frag */ /* list */ /* */ /* Remove a fragment cache table entry from the table & list. Also free */ /* the filter rule it is associated with it if it is no longer used as a */ /* result of decreasing the reference count. */ /* ------------------------------------------------------------------------ */ static void ipf_frag_delete(softc, fra, tail) ipf_main_softc_t *softc; ipfr_t *fra, ***tail; { ipf_frag_softc_t *softf = softc->ipf_frag_soft; if (fra->ipfr_next) fra->ipfr_next->ipfr_prev = fra->ipfr_prev; *fra->ipfr_prev = fra->ipfr_next; if (*tail == &fra->ipfr_next) *tail = fra->ipfr_prev; if (fra->ipfr_hnext) fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; *fra->ipfr_hprev = fra->ipfr_hnext; if (fra->ipfr_rule != NULL) { (void) ipf_derefrule(softc, &fra->ipfr_rule); } if (fra->ipfr_ref <= 0) ipf_frag_free(softf, fra); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_free */ /* Returns: Nil */ /* Parameters: softf(I) - pointer to fragment context information */ /* fra(I) - pointer to fragment structure to free */ /* */ /* Free up a fragment cache entry and bump relevent statistics. */ /* ------------------------------------------------------------------------ */ static void ipf_frag_free(softf, fra) ipf_frag_softc_t *softf; ipfr_t *fra; { KFREE(fra); FBUMP(ifs_expire); softf->ipfr_stats.ifs_inuse--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_clear */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Free memory in use by fragment state information kept. Do the normal */ /* fragment state stuff first and then the NAT-fragment table. */ /* ------------------------------------------------------------------------ */ void ipf_frag_clear(softc) ipf_main_softc_t *softc; { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; nat_t *nat; WRITE_ENTER(&softc->ipf_frag); while ((fra = softf->ipfr_list) != NULL) { fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_tail); } softf->ipfr_tail = &softf->ipfr_list; RWLOCK_EXIT(&softc->ipf_frag); WRITE_ENTER(&softc->ipf_nat); WRITE_ENTER(&softf->ipfr_natfrag); while ((fra = softf->ipfr_natlist) != NULL) { nat = fra->ipfr_data; if (nat != NULL) { if (nat->nat_data == fra) nat->nat_data = NULL; } fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } softf->ipfr_nattail = &softf->ipfr_natlist; RWLOCK_EXIT(&softf->ipfr_natfrag); RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Expire entries in the fragment cache table that have been there too long */ /* ------------------------------------------------------------------------ */ void ipf_frag_expire(softc) ipf_main_softc_t *softc; { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t **fp, *fra; nat_t *nat; SPL_INT(s); if (softf->ipfr_lock) return; SPL_NET(s); WRITE_ENTER(&softc->ipf_frag); /* * Go through the entire table, looking for entries to expire, * which is indicated by the ttl being less than or equal to ipf_ticks. */ for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_tail); } RWLOCK_EXIT(&softc->ipf_frag); WRITE_ENTER(&softf->ipfr_ipidfrag); for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); } RWLOCK_EXIT(&softf->ipfr_ipidfrag); /* * Same again for the NAT table, except that if the structure also * still points to a NAT structure, and the NAT structure points back * at the one to be free'd, NULL the reference from the NAT struct. * NOTE: We need to grab both mutex's early, and in this order so as * to prevent a deadlock if both try to expire at the same time. * The extra if() statement here is because it locks out all NAT * operations - no need to do that if there are no entries in this * list, right? */ if (softf->ipfr_natlist != NULL) { WRITE_ENTER(&softc->ipf_nat); WRITE_ENTER(&softf->ipfr_natfrag); for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; nat = fra->ipfr_data; if (nat != NULL) { if (nat->nat_data == fra) nat->nat_data = NULL; } fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } RWLOCK_EXIT(&softf->ipfr_natfrag); RWLOCK_EXIT(&softc->ipf_nat); } SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_pkt_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* */ /* This function is used to step through the fragment cache list used for */ /* filter rules. The hard work is done by the more generic ipf_frag_next. */ /* ------------------------------------------------------------------------ */ int ipf_frag_pkt_next(softc, token, itp) ipf_main_softc_t *softc; ipftoken_t *token; ipfgeniter_t *itp; { ipf_frag_softc_t *softf = softc->ipf_frag_soft; #ifdef USE_MUTEXES return ipf_frag_next(softc, token, itp, &softf->ipfr_list, &softf->ipfr_frag); #else return ipf_frag_next(softc, token, itp, &softf->ipfr_list); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_nat_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* */ /* This function is used to step through the fragment cache list used for */ /* NAT. The hard work is done by the more generic ipf_frag_next. */ /* ------------------------------------------------------------------------ */ int ipf_frag_nat_next(softc, token, itp) ipf_main_softc_t *softc; ipftoken_t *token; ipfgeniter_t *itp; { ipf_frag_softc_t *softf = softc->ipf_frag_soft;; #ifdef USE_MUTEXES return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, &softf->ipfr_natfrag); #else return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* top(I) - top of the fragment list */ /* lock(I) - fragment cache lock */ /* */ /* This function is used to interate through the list of entries in the */ /* fragment cache. It increases the reference count on the one currently */ /* being returned so that the caller can come back and resume from it later.*/ /* */ /* This function is used for both the NAT fragment cache as well as the ipf */ /* fragment cache - hence the reason for passing in top and lock. */ /* ------------------------------------------------------------------------ */ static int ipf_frag_next(softc, token, itp, top #ifdef USE_MUTEXES , lock #endif ) ipf_main_softc_t *softc; ipftoken_t *token; ipfgeniter_t *itp; ipfr_t **top; #ifdef USE_MUTEXES ipfrwlock_t *lock; #endif { ipfr_t *frag, *next, zero; int error = 0; if (itp->igi_data == NULL) { IPFERROR(20001); return EFAULT; } if (itp->igi_nitems != 1) { IPFERROR(20003); return EFAULT; } frag = token->ipt_data; READ_ENTER(lock); if (frag == NULL) next = *top; else next = frag->ipfr_next; if (next != NULL) { ATOMIC_INC(next->ipfr_ref); token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; } if (next->ipfr_next == NULL) ipf_token_mark_complete(token); RWLOCK_EXIT(lock); error = COPYOUT(next, itp->igi_data, sizeof(*next)); if (error != 0) IPFERROR(20002); if (frag != NULL) { #ifdef USE_MUTEXES ipf_frag_deref(softc, &frag, lock); #else ipf_frag_deref(softc, &frag); #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_pkt_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to frag cache pointer */ /* */ /* This function is the external interface for dropping a reference to a */ /* fragment cache entry used by filter rules. */ /* ------------------------------------------------------------------------ */ void ipf_frag_pkt_deref(softc, data) ipf_main_softc_t *softc; void *data; { ipfr_t **frp = data; #ifdef USE_MUTEXES ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); #else ipf_frag_deref(softc->ipf_frag_soft, frp); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_nat_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to frag cache pointer */ /* */ /* This function is the external interface for dropping a reference to a */ /* fragment cache entry used by NAT table entries. */ /* ------------------------------------------------------------------------ */ void ipf_frag_nat_deref(softc, data) ipf_main_softc_t *softc; void *data; { ipfr_t **frp = data; #ifdef USE_MUTEXES ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); #else ipf_frag_deref(softc->ipf_frag_soft, frp); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_deref */ /* Returns: Nil */ /* Parameters: frp(IO) - pointer to fragment structure to deference */ /* lock(I) - lock associated with the fragment */ /* */ /* This function dereferences a fragment structure (ipfr_t). The pointer */ /* passed in will always be reset back to NULL, even if the structure is */ /* not freed, to enforce the notion that the caller is no longer entitled */ /* to use the pointer it is dropping the reference to. */ /* ------------------------------------------------------------------------ */ static void ipf_frag_deref(arg, frp #ifdef USE_MUTEXES , lock #endif ) void *arg; ipfr_t **frp; #ifdef USE_MUTEXES ipfrwlock_t *lock; #endif { ipf_frag_softc_t *softf = arg; ipfr_t *fra; fra = *frp; *frp = NULL; WRITE_ENTER(lock); fra->ipfr_ref--; if (fra->ipfr_ref <= 0) ipf_frag_free(softf, fra); RWLOCK_EXIT(lock); } diff --git a/sys/contrib/ipfilter/netinet/ip_frag.h b/sys/contrib/ipfilter/netinet/ip_frag.h index 08f45ff313ae..b85e009b2155 100644 --- a/sys/contrib/ipfilter/netinet/ip_frag.h +++ b/sys/contrib/ipfilter/netinet/ip_frag.h @@ -1,125 +1,125 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_frag.h 1.5 3/24/96 * $FreeBSD$ * Id: ip_frag.h,v 2.23.2.1 2004/03/29 16:21:56 darrenr Exp */ #ifndef __IP_FRAG_H__ #define __IP_FRAG_H__ #define IPFT_SIZE 257 typedef struct ipfr { struct ipfr *ipfr_hnext, **ipfr_hprev; struct ipfr *ipfr_next, **ipfr_prev; void *ipfr_data; frentry_t *ipfr_rule; u_long ipfr_ttl; u_int ipfr_pkts; u_int ipfr_bytes; u_int ipfr_badorder; int ipfr_ref; u_short ipfr_off; u_short ipfr_firstend; u_char ipfr_p; u_char ipfr_seen0; /* * All of the fields, from ipfr_ifp to ipfr_pass, are compared * using bcmp to see if an identical entry is present. It is * therefore important for this set to remain together. */ void *ipfr_ifp; i6addr_t ipfr_source; i6addr_t ipfr_dest; u_32_t ipfr_optmsk; u_short ipfr_secmsk; u_short ipfr_auth; u_32_t ipfr_id; u_32_t ipfr_pass; int ipfr_v; } ipfr_t; #define ipfr_src ipfr_source.in4 #define ipfr_dst ipfr_dest.in4 typedef struct ipfrstat { u_long ifs_exists; /* add & already exists */ u_long ifs_nomem; u_long ifs_new; u_long ifs_hits; u_long ifs_expire; u_long ifs_inuse; u_long ifs_retrans0; u_long ifs_short; u_long ifs_bad; u_long ifs_overlap; u_long ifs_unordered; u_long ifs_strict; u_long ifs_miss; u_long ifs_maximum; u_long ifs_newbad; u_long ifs_newrestrictnot0; struct ipfr **ifs_table; struct ipfr **ifs_nattab; } ipfrstat_t; typedef struct ipf_frag_softc_s { ipfrwlock_t ipfr_ipidfrag; ipfrwlock_t ipfr_frag; ipfrwlock_t ipfr_natfrag; int ipfr_size; int ipfr_ttl; int ipfr_lock; int ipfr_inited; ipftuneable_t *ipf_frag_tune; ipfr_t *ipfr_list; ipfr_t **ipfr_tail; ipfr_t *ipfr_natlist; ipfr_t **ipfr_nattail; ipfr_t *ipfr_ipidlist; ipfr_t **ipfr_ipidtail; ipfr_t **ipfr_heads; ipfr_t **ipfr_nattab; ipfr_t **ipfr_ipidtab; ipfrstat_t ipfr_stats; } ipf_frag_softc_t; #define IPFR_CMPSZ (offsetof(ipfr_t, ipfr_pass) - \ offsetof(ipfr_t, ipfr_ifp)) -extern void *ipf_frag_soft_create __P((ipf_main_softc_t *)); -extern int ipf_frag_soft_init __P((ipf_main_softc_t *, void *)); -extern int ipf_frag_soft_fini __P((ipf_main_softc_t *, void *)); -extern void ipf_frag_soft_destroy __P((ipf_main_softc_t *, void *)); -extern int ipf_frag_main_load __P((void)); -extern int ipf_frag_main_unload __P((void)); -extern int ipf_frag_load __P((void)); -extern void ipf_frag_clear __P((ipf_main_softc_t *)); -extern void ipf_frag_expire __P((ipf_main_softc_t *)); -extern void ipf_frag_forget __P((void *)); -extern int ipf_frag_init __P((void)); -extern u_32_t ipf_frag_ipidknown __P((fr_info_t *)); -extern int ipf_frag_ipidnew __P((fr_info_t *, u_32_t)); -extern frentry_t *ipf_frag_known __P((fr_info_t *, u_32_t *)); -extern void ipf_frag_natforget __P((ipf_main_softc_t *, void *)); -extern int ipf_frag_natnew __P((ipf_main_softc_t *, fr_info_t *, u_32_t, struct nat *)); -extern nat_t *ipf_frag_natknown __P((fr_info_t *)); -extern int ipf_frag_new __P((ipf_main_softc_t *, fr_info_t *, u_32_t)); -extern ipfrstat_t *ipf_frag_stats __P((void *)); -extern void ipf_frag_setlock __P((void *, int)); -extern void ipf_frag_pkt_deref __P((ipf_main_softc_t *, void *)); -extern int ipf_frag_pkt_next __P((ipf_main_softc_t *, ipftoken_t *, - ipfgeniter_t *)); -extern void ipf_frag_nat_deref __P((ipf_main_softc_t *, void *)); -extern int ipf_frag_nat_next __P((ipf_main_softc_t *, ipftoken_t *, - ipfgeniter_t *)); -extern void ipf_slowtimer __P((ipf_main_softc_t *)); +extern void *ipf_frag_soft_create(ipf_main_softc_t *); +extern int ipf_frag_soft_init(ipf_main_softc_t *, void *); +extern int ipf_frag_soft_fini(ipf_main_softc_t *, void *); +extern void ipf_frag_soft_destroy(ipf_main_softc_t *, void *); +extern int ipf_frag_main_load(void); +extern int ipf_frag_main_unload(void); +extern int ipf_frag_load(void); +extern void ipf_frag_clear(ipf_main_softc_t *); +extern void ipf_frag_expire(ipf_main_softc_t *); +extern void ipf_frag_forget(void *); +extern int ipf_frag_init(void); +extern u_32_t ipf_frag_ipidknown(fr_info_t *); +extern int ipf_frag_ipidnew(fr_info_t *, u_32_t); +extern frentry_t *ipf_frag_known(fr_info_t *, u_32_t *); +extern void ipf_frag_natforget(ipf_main_softc_t *, void *); +extern int ipf_frag_natnew(ipf_main_softc_t *, fr_info_t *, u_32_t, struct nat *); +extern nat_t *ipf_frag_natknown(fr_info_t *); +extern int ipf_frag_new(ipf_main_softc_t *, fr_info_t *, u_32_t); +extern ipfrstat_t *ipf_frag_stats(void *); +extern void ipf_frag_setlock(void *, int); +extern void ipf_frag_pkt_deref(ipf_main_softc_t *, void *); +extern int ipf_frag_pkt_next(ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *); +extern void ipf_frag_nat_deref(ipf_main_softc_t *, void *); +extern int ipf_frag_nat_next(ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *); +extern void ipf_slowtimer(ipf_main_softc_t *); #endif /* __IP_FRAG_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c b/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c index c2cd6fc31e40..5d8f9f1f51f7 100644 --- a/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c @@ -1,2182 +1,2182 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Simple FTP transparent proxy for in-kernel use. For use with the NAT * code. * * $FreeBSD$ * Id: ip_ftp_pxy.c,v 2.88.2.19 2006/04/01 10:14:53 darrenr Exp $ */ #define IPF_FTP_PROXY #define IPF_MINPORTLEN 18 #define IPF_MINEPRTLEN 20 #define IPF_MAXPORTLEN 30 #define IPF_MIN227LEN 39 #define IPF_MAX227LEN 51 #define IPF_MIN229LEN 47 #define IPF_MAX229LEN 51 #define FTPXY_GO 0 #define FTPXY_INIT 1 #define FTPXY_USER_1 2 #define FTPXY_USOK_1 3 #define FTPXY_PASS_1 4 #define FTPXY_PAOK_1 5 #define FTPXY_AUTH_1 6 #define FTPXY_AUOK_1 7 #define FTPXY_ADAT_1 8 #define FTPXY_ADOK_1 9 #define FTPXY_ACCT_1 10 #define FTPXY_ACOK_1 11 #define FTPXY_USER_2 12 #define FTPXY_USOK_2 13 #define FTPXY_PASS_2 14 #define FTPXY_PAOK_2 15 #define FTPXY_JUNK_OK 0 #define FTPXY_JUNK_BAD 1 /* Ignore all commands for this connection */ #define FTPXY_JUNK_EOL 2 /* consume the rest of this line only */ #define FTPXY_JUNK_CONT 3 /* Saerching for next numeric */ /* * Values for FTP commands. Numerics cover 0-999 */ #define FTPXY_C_PASV 1000 #define FTPXY_C_PORT 1001 #define FTPXY_C_EPSV 1002 #define FTPXY_C_EPRT 1003 typedef struct ipf_ftp_softc_s { int ipf_p_ftp_pasvonly; /* Do not require logins before transfers */ int ipf_p_ftp_insecure; int ipf_p_ftp_pasvrdr; /* PASV must be last command prior to 227 */ int ipf_p_ftp_forcepasv; int ipf_p_ftp_debug; int ipf_p_ftp_single_xfer; void *ipf_p_ftp_tune; } ipf_ftp_softc_t; -void ipf_p_ftp_main_load __P((void)); -void ipf_p_ftp_main_unload __P((void)); -void *ipf_p_ftp_soft_create __P((ipf_main_softc_t *)); -void ipf_p_ftp_soft_destroy __P((ipf_main_softc_t *, void *)); - -int ipf_p_ftp_client __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_complete __P((char *, size_t)); -int ipf_p_ftp_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_ftp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -void ipf_p_ftp_del __P((ipf_main_softc_t *, ap_session_t *)); -int ipf_p_ftp_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_ftp_pasv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_epsv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_port __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_process __P((ipf_ftp_softc_t *, fr_info_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_server __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_valid __P((ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t)); -int ipf_p_ftp_server_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *, - size_t)); -int ipf_p_ftp_client_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *, - size_t)); -u_short ipf_p_ftp_atoi __P((char **)); -int ipf_p_ftp_pasvreply __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, u_int, char *, char *)); -int ipf_p_ftp_eprt __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_eprt4 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_eprt6 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int)); -int ipf_p_ftp_addport __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, - ftpinfo_t *, int, int, int)); -void ipf_p_ftp_setpending __P((ipf_main_softc_t *, ftpinfo_t *)); +void ipf_p_ftp_main_load(void); +void ipf_p_ftp_main_unload(void); +void *ipf_p_ftp_soft_create(ipf_main_softc_t *); +void ipf_p_ftp_soft_destroy(ipf_main_softc_t *, void *); + +int ipf_p_ftp_client(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_complete(char *, size_t); +int ipf_p_ftp_in(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_ftp_new(void *, fr_info_t *, ap_session_t *, nat_t *); +void ipf_p_ftp_del(ipf_main_softc_t *, ap_session_t *); +int ipf_p_ftp_out(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_ftp_pasv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_epsv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_port(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_process(ipf_ftp_softc_t *, fr_info_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_server(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_valid(ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t); +int ipf_p_ftp_server_valid(ipf_ftp_softc_t *, ftpside_t *, char *, + size_t); +int ipf_p_ftp_client_valid(ipf_ftp_softc_t *, ftpside_t *, char *, + size_t); +u_short ipf_p_ftp_atoi(char **); +int ipf_p_ftp_pasvreply(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, u_int, char *, char *); +int ipf_p_ftp_eprt(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_eprt4(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_eprt6(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int); +int ipf_p_ftp_addport(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int, int, int); +void ipf_p_ftp_setpending(ipf_main_softc_t *, ftpinfo_t *); /* * Debug levels */ #define DEBUG_SECURITY 0x01 #define DEBUG_ERROR 0x02 #define DEBUG_INFO 0x04 #define DEBUG_PARSE_ERR 0x08 #define DEBUG_PARSE_INFO 0x10 #define DEBUG_PARSE 0x20 static int ipf_p_ftp_proxy_init = 0; static frentry_t ftppxyfr; static ipftuneable_t ipf_ftp_tuneables[] = { { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) }, "ftp_debug", 0, 0x7f, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) }, "ftp_pasvonly", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) }, "ftp_insecure", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) }, "ftp_pasvrdr", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) }, "ftp_forcepasv", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) }, "ftp_single_xfer", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; void ipf_p_ftp_main_load() { bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); ftppxyfr.fr_ref = 1; ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); ipf_p_ftp_proxy_init = 1; } void ipf_p_ftp_main_unload() { if (ipf_p_ftp_proxy_init == 1) { MUTEX_DESTROY(&ftppxyfr.fr_lock); ipf_p_ftp_proxy_init = 0; } } /* * Initialize local structures. */ void * ipf_p_ftp_soft_create(softc) ipf_main_softc_t *softc; { ipf_ftp_softc_t *softf; KMALLOC(softf, ipf_ftp_softc_t *); if (softf == NULL) return NULL; bzero((char *)softf, sizeof(*softf)); #if defined(_KERNEL) softf->ipf_p_ftp_debug = 0; #else softf->ipf_p_ftp_debug = DEBUG_PARSE_ERR; #endif softf->ipf_p_ftp_forcepasv = 1; softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf, sizeof(ipf_ftp_tuneables), ipf_ftp_tuneables); if (softf->ipf_p_ftp_tune == NULL) { ipf_p_ftp_soft_destroy(softc, softf); return NULL; } if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) { ipf_p_ftp_soft_destroy(softc, softf); return NULL; } return softf; } void ipf_p_ftp_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_ftp_softc_t *softf = arg; if (softf->ipf_p_ftp_tune != NULL) { ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune); KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables)); softf->ipf_p_ftp_tune = NULL; } KFREE(softf); } int ipf_p_ftp_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ftpinfo_t *ftp; ftpside_t *f; KMALLOC(ftp, ftpinfo_t *); if (ftp == NULL) return -1; nat = nat; /* LINT */ aps->aps_data = ftp; aps->aps_psiz = sizeof(ftpinfo_t); aps->aps_sport = htons(fin->fin_sport); aps->aps_dport = htons(fin->fin_dport); bzero((char *)ftp, sizeof(*ftp)); f = &ftp->ftp_side[0]; f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; f = &ftp->ftp_side[1]; f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; ftp->ftp_passok = FTPXY_INIT; ftp->ftp_incok = 0; return 0; } void ipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp) { if (ftp->ftp_pendnat != NULL) ipf_nat_setpending(softc, ftp->ftp_pendnat); if (ftp->ftp_pendstate != NULL) { READ_ENTER(&softc->ipf_state); ipf_state_setpending(softc, ftp->ftp_pendstate); RWLOCK_EXIT(&softc->ipf_state); } } void ipf_p_ftp_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { ftpinfo_t *ftp; ftp = aps->aps_data; if (ftp != NULL) ipf_p_ftp_setpending(softc, ftp); } int ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; int dlen; { char newbuf[IPF_FTPBUFSZ], *s; u_int a1, a2, a3, a4; u_short a5, a6, sp; size_t nlen, olen; tcphdr_t *tcp; int inc, off; ftpside_t *f; mb_t *m; m = fin->fin_m; f = &ftp->ftp_side[0]; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; /* * Check for client sending out PORT message. */ if (dlen < IPF_MINPORTLEN) { DT3(ftp_PORT_error_dlen, nat_t *, nat, ftpside_t *, f, u_int, dlen); if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", dlen); return 0; } /* * Skip the PORT command + space */ s = f->ftps_rptr + 5; /* * Pick out the address components, two at a time. */ a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PORT_error_atoi_1, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1); return 0; } a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PORT_error_atoi_2, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2); return 0; } /* * Check that IP address in the PORT/PASV reply is the same as the * sender of the command - prevents using PORT for port scanning. */ a1 <<= 16; a1 |= a2; if (((nat->nat_dir == NAT_OUTBOUND) && (a1 != ntohl(nat->nat_osrcaddr))) || ((nat->nat_dir == NAT_INBOUND) && (a1 != ntohl(nat->nat_nsrcaddr)))) { DT3(ftp_PORT_error_address, nat_t *, nat, ftpside_t *, f, u_int, a1); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1"); return APR_ERR(1); } a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PORT_error_atoi_3, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3); return 0; } if (*s == ')') s++; /* * check for CR-LF at the end. */ if (*s == '\n') s--; if ((*s != '\r') || (*(s + 1) != '\n')) { DT2(ftp_PORT_error_no_crlf, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_port:missing %s\n", "cr-lf"); return 0; } s += 2; a6 = a5 & 0xff; /* * Calculate the source port. Verification of > 1024 is in * ipf_p_ftp_addport. */ a5 >>= 8; a5 &= 0xff; sp = a5 << 8 | a6; /* * Calculate new address parts for PORT command */ if (nat->nat_dir == NAT_INBOUND) a1 = ntohl(nat->nat_ndstaddr); else a1 = ntohl(ip->ip_src.s_addr); a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; olen = s - f->ftps_rptr; /* DO NOT change this to snprintf! */ #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n", "PORT", a1, a2, a3, a4, a5, a6); #else (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", "PORT", a1, a2, a3, a4, a5, a6); #endif nlen = strlen(newbuf); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) { DT3(ftp_PORT_error_inc, nat_t *, nat, ftpside_t *, f, int, inc); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } #if !defined(_KERNEL) M_ADJ(m, inc); #else /* * m_adj takes care of pkthdr.len, if required and treats inc<0 to * mean remove -len bytes from the end of the packet. * The mbuf chain will be extended if necessary by m_copyback(). */ if (inc < 0) M_ADJ(m, inc); #endif /* !defined(_KERNEL) */ COPYBACK(m, off, nlen, newbuf); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { fin->fin_plen += inc; ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += inc; } f->ftps_cmd = FTPXY_C_PORT; return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, sp, inc); } int ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, nport, inc) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; int dlen, nport, inc; { tcphdr_t tcph, *tcp2 = &tcph; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; int direction; fr_info_t fi; ipnat_t *ipn; nat_t *nat2; u_short sp; int flags; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) { if (softf->ipf_p_ftp_single_xfer != 0) { DT2(ftp_PORT_error_add_active, nat_t *, nat, ftpinfo_t *, ftp); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_addport:xfer active %p/%p\n", ftp->ftp_pendnat, ftp->ftp_pendstate); return 0; } ipf_p_ftp_setpending(softc, ftp); } /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = nport; /* * Don't allow the PORT command to specify a port < 1024 due to * security risks. */ if (sp < 1024) { DT3(ftp_PORT_error_port, nat_t *, nat, ftpinfo_t *, ftp, u_int, sp); if (softf->ipf_p_ftp_debug & DEBUG_SECURITY) printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp); return 0; } /* * The server may not make the connection back from port 20, but * it is the most likely so use it here to check for a conflicting * mapping. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1] - 1; fi.fin_src6 = nat->nat_ndst6; fi.fin_dst6 = nat->nat_nsrc6; if (nat->nat_v[0] == 6) { #ifndef USE_INET6 return APR_INC(inc); #endif } /* * Add skeleton NAT entry for connection which will come back the * other way. */ #ifdef USE_INET6 if (nat->nat_v[0] == 6) { if (nat->nat_dir == NAT_OUTBOUND) { nat2 = ipf_nat6_outlookup(&fi, IPN_TCP|NAT_SEARCH, nat->nat_pr[1], &nat->nat_osrc6.in6, &nat->nat_odst6.in6); } else { nat2 = ipf_nat6_inlookup(&fi, IPN_TCP|NAT_SEARCH, nat->nat_pr[0], &nat->nat_odst6.in6, &nat->nat_osrc6.in6); } } else #endif { if (nat->nat_dir == NAT_OUTBOUND) { nat2 = ipf_nat_outlookup(&fi, IPN_TCP|NAT_SEARCH, nat->nat_pr[1], nat->nat_osrcip, nat->nat_odstip); } else { nat2 = ipf_nat_inlookup(&fi, IPN_TCP|NAT_SEARCH, nat->nat_pr[0], nat->nat_odstip, nat->nat_osrcip); } } if (nat2 != NULL) return APR_INC(inc); ipn = ipf_proxy_rule_rev(nat); if (ipn == NULL) return APR_ERR(1); ipn->in_use = 0; fi.fin_fr = &ftppxyfr; fi.fin_dp = (char *)tcp2; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; fi.fin_data[1] = sp; fi.fin_data[0] = 0; bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_sport = 0; tcp2->th_dport = htons(sp); tcp2->th_win = htons(8192); TCP_OFF_A(tcp2, 5); tcp2->th_flags = TH_SYN; if (nat->nat_dir == NAT_INBOUND) { fi.fin_out = 1; direction = NAT_OUTBOUND; } else { fi.fin_out = 0; direction = NAT_INBOUND; } flags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; MUTEX_ENTER(&softn->ipf_nat_new); if (nat->nat_v[0] == 6) { #ifdef USE_INET6 nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, flags, direction); #endif } else { nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, flags, direction); } MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 == NULL) { KFREES(ipn, ipn->in_size); return APR_ERR(1); } (void) ipf_nat_proto(&fi, nat2, IPN_TCP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); fi.fin_ifp = NULL; if (nat2->nat_dir == NAT_INBOUND) fi.fin_dst6 = nat->nat_osrc6; if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, SI_W_SPORT) != 0) ipf_nat_setpending(softc, nat2); return APR_INC(inc); } int ipf_p_ftp_client(softf, fin, ip, nat, ftp, dlen) ipf_ftp_softc_t *softf; fr_info_t *fin; nat_t *nat; ftpinfo_t *ftp; ip_t *ip; int dlen; { char *rptr, *wptr, cmd[6], c; ftpside_t *f; int inc, i; inc = 0; f = &ftp->ftp_side[0]; rptr = f->ftps_rptr; wptr = f->ftps_wptr; for (i = 0; (i < 5) && (i < dlen); i++) { c = rptr[i]; if (ISALPHA(c)) { cmd[i] = TOUPPER(c); } else { cmd[i] = c; } } cmd[i] = '\0'; ftp->ftp_incok = 0; DT2(ftp_client_command, char [], cmd, int, ftp->ftp_passok); if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) { if (ftp->ftp_passok == FTPXY_ADOK_1 || ftp->ftp_passok == FTPXY_AUOK_1) { ftp->ftp_passok = FTPXY_USER_2; ftp->ftp_incok = 1; } else { ftp->ftp_passok = FTPXY_USER_1; ftp->ftp_incok = 1; } } else if (!strncmp(cmd, "AUTH ", 5)) { ftp->ftp_passok = FTPXY_AUTH_1; ftp->ftp_incok = 1; } else if (!strncmp(cmd, "PASS ", 5)) { if (ftp->ftp_passok == FTPXY_USOK_1) { ftp->ftp_passok = FTPXY_PASS_1; ftp->ftp_incok = 1; } else if (ftp->ftp_passok == FTPXY_USOK_2) { ftp->ftp_passok = FTPXY_PASS_2; ftp->ftp_incok = 1; } } else if ((ftp->ftp_passok == FTPXY_AUOK_1) && !strncmp(cmd, "ADAT ", 5)) { ftp->ftp_passok = FTPXY_ADAT_1; ftp->ftp_incok = 1; } else if ((ftp->ftp_passok == FTPXY_PAOK_1 || ftp->ftp_passok == FTPXY_PAOK_2) && !strncmp(cmd, "ACCT ", 5)) { ftp->ftp_passok = FTPXY_ACCT_1; ftp->ftp_incok = 1; } else if ((ftp->ftp_passok == FTPXY_GO) && !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); } else if ((ftp->ftp_passok == FTPXY_GO) && !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "EPRT ", 5)) { inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen); } else if (softf->ipf_p_ftp_insecure && !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); } if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n", cmd, ftp->ftp_passok, ftp->ftp_incok, inc); DT2(ftp_client_passok, char *, cmd, int, ftp->ftp_passok); while ((*rptr++ != '\n') && (rptr < wptr)) ; f->ftps_rptr = rptr; return inc; } int ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; int dlen; { u_int a1, a2, a3, a4, data_ip; char newbuf[IPF_FTPBUFSZ]; const char *brackets[2]; u_short a5, a6; ftpside_t *f; char *s; if ((softf->ipf_p_ftp_forcepasv != 0) && (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) { DT2(ftp_PASV_error_state, nat_t *, nat, ftpinfo_t *, ftp); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n", ftp->ftp_side[0].ftps_cmd); return 0; } f = &ftp->ftp_side[1]; #define PASV_REPLEN 24 /* * Check for PASV reply message. */ if (dlen < IPF_MIN227LEN) { DT3(ftp_PASV_error_short, nat_t *, nat, ftpinfo_t *, ftp, int, dlen); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", dlen); return 0; } else if (strncmp(f->ftps_rptr, "227 Entering Passive Mod", PASV_REPLEN)) { DT2(ftp_PASV_error_string, nat_t *, nat, ftpinfo_t *, ftp); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:%d reply wrong\n", 227); return 0; } brackets[0] = ""; brackets[1] = ""; /* * Skip the PASV reply + space */ s = f->ftps_rptr + PASV_REPLEN; while (*s && !ISDIGIT(*s)) { if (*s == '(') { brackets[0] = "("; brackets[1] = ")"; } s++; } /* * Pick out the address components, two at a time. */ a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PASV_error_atoi_1, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1); return 0; } a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PASV_error_atoi_2, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2); return 0; } /* * check that IP address in the PASV reply is the same as the * sender of the command - prevents using PASV for port scanning. */ a1 <<= 16; a1 |= a2; if (((nat->nat_dir == NAT_INBOUND) && (a1 != ntohl(nat->nat_ndstaddr))) || ((nat->nat_dir == NAT_OUTBOUND) && (a1 != ntohl(nat->nat_odstaddr)))) { DT3(ftp_PASV_error_address, nat_t *, nat, ftpside_t *, f, u_int, a1); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1"); return 0; } a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PASV_error_atoi_3, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3); return 0; } if (*s == ')') s++; if (*s == '.') s++; if (*s == '\n') s--; /* * check for CR-LF at the end. */ if ((*s != '\r') || (*(s + 1) != '\n')) { DT(pasv_missing_crlf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n"); return 0; } s += 2; a6 = a5 & 0xff; a5 >>= 8; /* * Calculate new address parts for 227 reply */ if (nat->nat_dir == NAT_INBOUND) { data_ip = nat->nat_odstaddr; a1 = ntohl(data_ip); } else data_ip = htonl(a1); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n", "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, a5, a6, brackets[1]); #else (void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n", "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, a5, a6, brackets[1]); #endif return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6), newbuf, s); } int ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, port, newmsg, s) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; u_int port; char *newmsg; char *s; { int inc, off, nflags; tcphdr_t *tcp, tcph, *tcp2; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; size_t nlen, olen; #ifdef USE_INET6 ip6_t *ip6; #endif ipnat_t *ipn; fr_info_t fi; ftpside_t *f; nat_t *nat2; mb_t *m; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) ipf_p_ftp_setpending(softc, ftp); m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; tcp2 = &tcph; inc = 0; f = &ftp->ftp_side[1]; olen = s - f->ftps_rptr; nlen = strlen(newmsg); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) { DT3(ftp_PASV_error_inc, nat_t *, nat, ftpside_t *, f, int, inc); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } ipn = ipf_proxy_rule_fwd(nat); if (ipn == NULL) return APR_ERR(1); ipn->in_use = 0; /* * Add skeleton NAT entry for connection which will come back the * other way. */ bzero((char *)tcp2, sizeof(*tcp2)); bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = 0; fi.fin_data[1] = port; nflags = IPN_TCP|SI_W_SPORT; fi.fin_fr = &ftppxyfr; fi.fin_dp = (char *)tcp2; fi.fin_out = 1 - fin->fin_out; fi.fin_dlen = sizeof(*tcp2); fi.fin_src6 = nat->nat_osrc6; fi.fin_dst6 = nat->nat_odst6; fi.fin_plen = fi.fin_hlen + sizeof(*tcp); fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; TCP_OFF_A(tcp2, 5); tcp2->th_flags = TH_SYN; tcp2->th_win = htons(8192); tcp2->th_dport = htons(port); MUTEX_ENTER(&softn->ipf_nat_new); #ifdef USE_INET6 if (nat->nat_v[0] == 6) nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, nflags, nat->nat_dir); else #endif nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, nflags, nat->nat_dir); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 == NULL) { KFREES(ipn, ipn->in_size); return APR_ERR(1); } (void) ipf_nat_proto(&fi, nat2, IPN_TCP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); fi.fin_ifp = NULL; if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_v[0] == 6) { #ifdef USE_INET6 fi.fin_dst6 = nat->nat_ndst6; #endif } else { fi.fin_daddr = nat->nat_ndstaddr; } } if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, SI_W_SPORT) != 0) ipf_nat_setpending(softc, nat2); #if !defined(_KERNEL) M_ADJ(m, inc); #else /* * m_adj takes care of pkthdr.len, if required and treats inc<0 to * mean remove -len bytes from the end of the packet. * The mbuf chain will be extended if necessary by m_copyback(). */ if (inc < 0) M_ADJ(m, inc); #endif /* !defined(_KERNEL) */ COPYBACK(m, off, nlen, newmsg); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { fin->fin_plen += inc; fin->fin_dlen += inc; if (nat->nat_v[0] == 6) { #ifdef USE_INET6 ip6 = (ip6_t *)fin->fin_ip; u_short len = ntohs(ip6->ip6_plen) + inc; ip6->ip6_plen = htons(len); #endif } else { ip->ip_len = htons(fin->fin_plen); } } return APR_INC(inc); } int ipf_p_ftp_server(softf, fin, ip, nat, ftp, dlen) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; int dlen; { char *rptr, *wptr; ftpside_t *f; int inc; inc = 0; f = &ftp->ftp_side[1]; rptr = f->ftps_rptr; wptr = f->ftps_wptr; DT2(ftp_server_response, char *, rptr, int, ftp->ftp_passok); if (*rptr == ' ') goto server_cmd_ok; if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2))) return 0; if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("ipf_p_ftp_server_1: cmd[%4.4s] passok %d\n", rptr, ftp->ftp_passok); if (ftp->ftp_passok == FTPXY_GO) { if (!strncmp(rptr, "227 ", 4)) inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); else if (!strncmp(rptr, "229 ", 4)) inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); else if (strncmp(rptr, "200", 3)) { /* * 200 is returned for a successful command. */ ; } } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) { inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) { inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); } else if (*rptr == '5' || *rptr == '4') ftp->ftp_passok = FTPXY_INIT; else if (ftp->ftp_incok) { if (*rptr == '3') { if (ftp->ftp_passok == FTPXY_ACCT_1) ftp->ftp_passok = FTPXY_GO; else ftp->ftp_passok++; } else if (*rptr == '2') { switch (ftp->ftp_passok) { case FTPXY_USER_1 : case FTPXY_USER_2 : case FTPXY_PASS_1 : case FTPXY_PASS_2 : case FTPXY_ACCT_1 : ftp->ftp_passok = FTPXY_GO; break; default : ftp->ftp_passok += 3; break; } } } ftp->ftp_incok = 0; server_cmd_ok: if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("ipf_p_ftp_server_2: cmd[%4.4s] passok %d\n", rptr, ftp->ftp_passok); DT3(ftp_server_passok, char *,rptr, int, ftp->ftp_incok, int, ftp->ftp_passok); while ((*rptr++ != '\n') && (rptr < wptr)) ; f->ftps_rptr = rptr; return inc; } /* * 0 FTPXY_JUNK_OK * 1 FTPXY_JUNK_BAD * 2 FTPXY_JUNK_EOL * 3 FTPXY_JUNK_CONT * * Look to see if the buffer starts with something which we recognise as * being the correct syntax for the FTP protocol. */ int ipf_p_ftp_client_valid(softf, ftps, buf, len) ipf_ftp_softc_t *softf; ftpside_t *ftps; char *buf; size_t len; { register char *s, c, pc; register size_t i = len; char cmd[5]; s = buf; if (ftps->ftps_junk == FTPXY_JUNK_BAD) return FTPXY_JUNK_BAD; if (i < 5) { DT1(client_valid, int, i); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i); return 2; } i--; c = *s++; if (ISALPHA(c)) { cmd[0] = TOUPPER(c); c = *s++; i--; if (ISALPHA(c)) { cmd[1] = TOUPPER(c); c = *s++; i--; if (ISALPHA(c)) { cmd[2] = TOUPPER(c); c = *s++; i--; if (ISALPHA(c)) { cmd[3] = TOUPPER(c); c = *s++; i--; if ((c != ' ') && (c != '\r')) goto bad_client_command; } else if ((c != ' ') && (c != '\r')) goto bad_client_command; } else goto bad_client_command; } else goto bad_client_command; } else { bad_client_command: DT4(client_junk, int, len, int, i, int, c, char *, buf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", "ipf_p_ftp_client_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); return FTPXY_JUNK_BAD; } for (; i; i--) { pc = c; c = *s++; if ((pc == '\r') && (c == '\n')) { cmd[4] = '\0'; if (!strcmp(cmd, "PASV")) { ftps->ftps_cmd = FTPXY_C_PASV; } else if (!strcmp(cmd, "EPSV")) { ftps->ftps_cmd = FTPXY_C_EPSV; } else { ftps->ftps_cmd = 0; } return 0; } } #if !defined(_KERNEL) printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); #endif return FTPXY_JUNK_EOL; } int ipf_p_ftp_server_valid(softf, ftps, buf, len) ipf_ftp_softc_t *softf; ftpside_t *ftps; char *buf; size_t len; { register char *s, c, pc; register size_t i = len; int cmd; s = buf; cmd = 0; if (ftps->ftps_junk == FTPXY_JUNK_BAD) return FTPXY_JUNK_BAD; if (i < 5) { DT1(server_valid, int, i); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i); return 2; } c = *s++; i--; if (c == ' ') { cmd = -1; goto search_eol; } if (ISDIGIT(c)) { cmd = (c - '0') * 100; c = *s++; i--; if (ISDIGIT(c)) { cmd += (c - '0') * 10; c = *s++; i--; if (ISDIGIT(c)) { cmd += (c - '0'); c = *s++; i--; if ((c != '-') && (c != ' ')) goto bad_server_command; if (c == '-') return FTPXY_JUNK_CONT; } else goto bad_server_command; } else goto bad_server_command; } else { bad_server_command: DT4(server_junk, int len, buf, int, i, int, c, char *, buf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", "ipf_p_ftp_server_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); if (ftps->ftps_junk == FTPXY_JUNK_CONT) return FTPXY_JUNK_CONT; return FTPXY_JUNK_BAD; } search_eol: for (; i; i--) { pc = c; c = *s++; if ((pc == '\r') && (c == '\n')) { if (cmd == -1) { if (ftps->ftps_junk == FTPXY_JUNK_CONT) return FTPXY_JUNK_CONT; } else { ftps->ftps_cmd = cmd; } return FTPXY_JUNK_OK; } } DT2(junk_eol, int, len, char *, buf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); return FTPXY_JUNK_EOL; } int ipf_p_ftp_valid(softf, ftp, side, buf, len) ipf_ftp_softc_t *softf; ftpinfo_t *ftp; int side; char *buf; size_t len; { ftpside_t *ftps; int ret; ftps = &ftp->ftp_side[side]; if (side == 0) ret = ipf_p_ftp_client_valid(softf, ftps, buf, len); else ret = ipf_p_ftp_server_valid(softf, ftps, buf, len); return ret; } /* * For map rules, the following applies: * rv == 0 for outbound processing, * rv == 1 for inbound processing. * For rdr rules, the following applies: * rv == 0 for inbound processing, * rv == 1 for outbound processing. */ int ipf_p_ftp_process(softf, fin, nat, ftp, rv) ipf_ftp_softc_t *softf; fr_info_t *fin; nat_t *nat; ftpinfo_t *ftp; int rv; { int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry; char *rptr, *wptr, *s; u_32_t thseq, thack; ap_session_t *aps; ftpside_t *f, *t; tcphdr_t *tcp; ip_t *ip; mb_t *m; m = fin->fin_m; ip = fin->fin_ip; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; f = &ftp->ftp_side[rv]; t = &ftp->ftp_side[1 - rv]; thseq = ntohl(tcp->th_seq); thack = ntohl(tcp->th_ack); mlen = MSGDSIZE(m) - off; DT3(process_debug, tcphdr_t *, tcp, int, off, int, mlen); if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("ipf_p_ftp_process: %d:%d,%d, mlen %d flags %x\n", fin->fin_out, fin->fin_sport, fin->fin_dport, mlen, tcp->th_flags); if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) { f->ftps_seq[0] = thseq + 1; t->ftps_seq[0] = thack; return 0; } else if (mlen < 0) { return 0; } aps = nat->nat_aps; sel = aps->aps_sel[1 - rv]; sel2 = aps->aps_sel[rv]; if (rv == 1) { seqoff = aps->aps_seqoff[sel]; if (aps->aps_seqmin[sel] > seqoff + thseq) seqoff = aps->aps_seqoff[!sel]; ackoff = aps->aps_ackoff[sel2]; if (aps->aps_ackmin[sel2] > ackoff + thack) ackoff = aps->aps_ackoff[!sel2]; } else { seqoff = aps->aps_ackoff[sel]; if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq, aps->aps_ackmin[sel]); if (aps->aps_ackmin[sel] > seqoff + thseq) seqoff = aps->aps_ackoff[!sel]; ackoff = aps->aps_seqoff[sel2]; if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("ackoff %d thack %x seqmin %x\n", ackoff, thack, aps->aps_seqmin[sel2]); if (ackoff > 0) { if (aps->aps_seqmin[sel2] > ackoff + thack) ackoff = aps->aps_seqoff[!sel2]; } else { if (aps->aps_seqmin[sel2] > thack) ackoff = aps->aps_seqoff[!sel2]; } } if (softf->ipf_p_ftp_debug & DEBUG_INFO) { printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n", rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff, thack, ackoff, mlen, fin->fin_plen, off); printf("sel %d seqmin %x/%x offset %d/%d\n", sel, aps->aps_seqmin[sel], aps->aps_seqmin[sel2], aps->aps_seqoff[sel], aps->aps_seqoff[sel2]); printf("sel %d ackmin %x/%x offset %d/%d\n", sel2, aps->aps_ackmin[sel], aps->aps_ackmin[sel2], aps->aps_ackoff[sel], aps->aps_ackoff[sel2]); } /* * XXX - Ideally, this packet should get dropped because we now know * that it is out of order (and there is no real danger in doing so * apart from causing packets to go through here ordered). */ if (softf->ipf_p_ftp_debug & DEBUG_INFO) { printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n", rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff); } ok = 0; if (t->ftps_seq[0] == 0) { t->ftps_seq[0] = thack; ok = 1; } else { if (ackoff == 0) { if (t->ftps_seq[0] == thack) ok = 1; else if (t->ftps_seq[1] == thack) { t->ftps_seq[0] = thack; ok = 1; } } else { if (t->ftps_seq[0] + ackoff == thack) { t->ftps_seq[0] = thack; ok = 1; } else if (t->ftps_seq[0] == thack + ackoff) { t->ftps_seq[0] = thack + ackoff; ok = 1; } else if (t->ftps_seq[1] + ackoff == thack) { t->ftps_seq[0] = thack; ok = 1; } else if (t->ftps_seq[1] == thack + ackoff) { t->ftps_seq[0] = thack + ackoff; ok = 1; } } } if (softf->ipf_p_ftp_debug & DEBUG_INFO) { if (!ok) printf("%s ok\n", "not"); } if (!mlen) { if (t->ftps_seq[0] + ackoff != thack && t->ftps_seq[1] + ackoff != thack) { DT3(thack, ftpside_t *t, t, int, ackoff, u_32_t, thack); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { printf("%s:seq[0](%u) + (%d) != (%u)\n", "ipf_p_ftp_process", t->ftps_seq[0], ackoff, thack); printf("%s:seq[0](%u) + (%d) != (%u)\n", "ipf_p_ftp_process", t->ftps_seq[1], ackoff, thack); } return APR_ERR(1); } if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n", f->ftps_seq[0], f->ftps_seq[1]); } if (tcp->th_flags & TH_FIN) { if (thseq == f->ftps_seq[1]) { f->ftps_seq[0] = f->ftps_seq[1] - seqoff; f->ftps_seq[1] = thseq + 1 - seqoff; } else { DT2(thseq, ftpside_t *t, t, u_32_t, thseq); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { printf("FIN: thseq %x seqoff %d ftps_seq %x\n", thseq, seqoff, f->ftps_seq[0]); } return APR_ERR(1); } } f->ftps_len = 0; return 0; } ok = 0; if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) { ok = 1; /* * Retransmitted data packet. */ } else if ((thseq + mlen == f->ftps_seq[0]) || (thseq + mlen == f->ftps_seq[1])) { ok = 1; } if (ok == 0) { DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen); inc = thseq - f->ftps_seq[0]; if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { printf("inc %d sel %d rv %d\n", inc, sel, rv); printf("th_seq %x ftps_seq %x/%x\n", thseq, f->ftps_seq[0], f->ftps_seq[1]); printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel], aps->aps_ackoff[sel]); printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel], aps->aps_seqoff[sel]); } return APR_ERR(1); } inc = 0; rptr = f->ftps_rptr; wptr = f->ftps_wptr; f->ftps_seq[0] = thseq; f->ftps_seq[1] = f->ftps_seq[0] + mlen; f->ftps_len = mlen; while (mlen > 0) { len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr)); if (len == 0) break; COPYDATA(m, off, len, wptr); mlen -= len; off += len; wptr += len; whilemore: if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n", "ipf_p_ftp_process", len, mlen, off, (u_long)wptr, f->ftps_junk, len, len, rptr); f->ftps_wptr = wptr; if (f->ftps_junk != FTPXY_JUNK_OK) { i = f->ftps_junk; f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr, wptr - rptr); DT2(junk_transit, int, i, int, f->ftps_junk); if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:junk %d -> %d\n", "ipf_p_ftp_process", i, f->ftps_junk); if (f->ftps_junk == FTPXY_JUNK_BAD) { DT(buffer_full); if (wptr - rptr == sizeof(f->ftps_buf)) { if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) printf("%s:full buffer\n", "ipf_p_ftp_process"); f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; rptr = f->ftps_rptr; wptr = f->ftps_wptr; continue; } } } while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) { len = wptr - rptr; f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr, len); if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { printf("%s=%d len %d rv %d ptr %lx/%lx ", "ipf_p_ftp_valid", f->ftps_junk, len, rv, (u_long)rptr, (u_long)wptr); printf("buf [%*.*s]\n", len, len, rptr); } if (f->ftps_junk == FTPXY_JUNK_OK) { f->ftps_cmds++; f->ftps_rptr = rptr; if (rv) inc += ipf_p_ftp_server(softf, fin, ip, nat, ftp, len); else inc += ipf_p_ftp_client(softf, fin, ip, nat, ftp, len); rptr = f->ftps_rptr; wptr = f->ftps_wptr; } } /* * Off to a bad start so lets just forget about using the * ftp proxy for this connection. */ if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) { /* f->ftps_seq[1] += inc; */ DT(ftp_junk_cmd); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("%s:cmds == 0 junk == 1\n", "ipf_p_ftp_process"); return APR_ERR(2); } retry = 0; if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) { for (s = rptr; s < wptr; s++) { if ((*s == '\r') && (s + 1 < wptr) && (*(s + 1) == '\n')) { rptr = s + 2; retry = 1; if (f->ftps_junk != FTPXY_JUNK_CONT) f->ftps_junk = FTPXY_JUNK_OK; break; } } } if (rptr == wptr) { rptr = wptr = f->ftps_buf; } else { /* * Compact the buffer back to the start. The junk * flag should already be set and because we're not * throwing away any data, it is preserved from its * current state. */ if (rptr > f->ftps_buf) { bcopy(rptr, f->ftps_buf, wptr - rptr); wptr -= rptr - f->ftps_buf; rptr = f->ftps_buf; } } f->ftps_rptr = rptr; f->ftps_wptr = wptr; if (retry) goto whilemore; } /* f->ftps_seq[1] += inc; */ if (tcp->th_flags & TH_FIN) f->ftps_seq[1]++; if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) { mlen = MSGDSIZE(m); mlen -= off; printf("ftps_seq[1] = %x inc %d len %d\n", f->ftps_seq[1], inc, mlen); } f->ftps_rptr = rptr; f->ftps_wptr = wptr; return APR_INC(inc); } int ipf_p_ftp_out(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipf_ftp_softc_t *softf = arg; ftpinfo_t *ftp; int rev; ftp = aps->aps_data; if (ftp == NULL) return 0; rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1; if (ftp->ftp_side[1 - rev].ftps_ifp == NULL) ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp; return ipf_p_ftp_process(softf, fin, nat, ftp, rev); } int ipf_p_ftp_in(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipf_ftp_softc_t *softf = arg; ftpinfo_t *ftp; int rev; ftp = aps->aps_data; if (ftp == NULL) return 0; rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1; if (ftp->ftp_side[rev].ftps_ifp == NULL) ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp; return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev); } /* * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in * pairs separated by commas (which are expected to be in the range 0 - 255), * returning a 16 bit number combining either side of the , as the MSB and * LSB. */ u_short ipf_p_ftp_atoi(ptr) char **ptr; { register char *s = *ptr, c; register u_char i = 0, j = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (c != ',') { *ptr = NULL; return 0; } while (((c = *s++) != '\0') && ISDIGIT(c)) { j *= 10; j += c - '0'; } *ptr = s; i &= 0xff; j &= 0xff; return (i << 8) | j; } int ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; int dlen; { ftpside_t *f; /* * Check for client sending out EPRT message. */ if (dlen < IPF_MINEPRTLEN) { DT1(epert_dlen, int, dlen); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n", dlen); return 0; } /* * Parse the EPRT command. Format is: * "EPRT |1|1.2.3.4|2000|" for IPv4 and * "EPRT |2|ef00::1:2|2000|" for IPv6 */ f = &ftp->ftp_side[0]; if (f->ftps_rptr[5] != '|') return 0; if (f->ftps_rptr[5] == f->ftps_rptr[7]) { if (f->ftps_rptr[6] == '1' && nat->nat_v[0] == 4) return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen); #ifdef USE_INET6 if (f->ftps_rptr[6] == '2' && nat->nat_v[0] == 6) return ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen); #endif } return 0; } int ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; int dlen; { int a1, a2, a3, a4, port, olen, nlen, inc, off; char newbuf[IPF_FTPBUFSZ]; char *s, c, delim; u_32_t addr, i; tcphdr_t *tcp; ftpside_t *f; mb_t *m; m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; f = &ftp->ftp_side[0]; delim = f->ftps_rptr[5]; s = f->ftps_rptr + 8; /* * get the IP address. */ i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 255) return 0; if (c != '.') return 0; addr = (i << 24); i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 255) return 0; if (c != '.') return 0; addr |= (addr << 16); i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 255) return 0; if (c != '.') return 0; addr |= (addr << 8); i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 255) return 0; if (c != delim) return 0; addr |= addr; /* * Get the port number */ i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 65535) return 0; if (c != delim) return 0; port = i; /* * Check for CR-LF at the end of the command string. */ if ((*s != '\r') || (*(s + 1) != '\n')) { DT(eprt4_no_crlf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf"); return 0; } s += 2; /* * Calculate new address parts for PORT command */ if (nat->nat_dir == NAT_INBOUND) a1 = ntohl(nat->nat_odstaddr); else a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; olen = s - f->ftps_rptr; /* DO NOT change this to snprintf! */ /* * While we could force the use of | as a delimiter here, it makes * sense to preserve whatever character is being used by the systems * involved in the communication. */ #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim); #else (void) sprintf(newbuf, "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim); #endif nlen = strlen(newbuf); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) { DT2(eprt4_len, int, inc, int, fin->fin_plen); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; #if !defined(_KERNEL) M_ADJ(m, inc); #else if (inc < 0) M_ADJ(m, inc); #endif /* the mbuf chain will be extended if necessary by m_copyback() */ COPYBACK(m, off, nlen, newbuf); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { fin->fin_plen += inc; ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += inc; } f->ftps_cmd = FTPXY_C_EPRT; return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); } int ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; int dlen; { char newbuf[IPF_FTPBUFSZ]; u_short ap = 0; ftpside_t *f; char *s; if ((softf->ipf_p_ftp_forcepasv != 0) && (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) { DT1(epsv_cmd, int, ftp->ftp_side[0].ftps_cmd); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n", ftp->ftp_side[0].ftps_cmd); return 0; } f = &ftp->ftp_side[1]; #define EPSV_REPLEN 33 /* * Check for EPSV reply message. */ if (dlen < IPF_MIN229LEN) { return (0); } else if (strncmp(f->ftps_rptr, "229 Entering Extended Passive Mode", EPSV_REPLEN)) { return (0); } /* * Skip the EPSV command + space */ s = f->ftps_rptr + 33; while (*s && !ISDIGIT(*s)) s++; /* * As per RFC 2428, there are no addres components in the EPSV * response. So we'll go straight to getting the port. */ while (*s && ISDIGIT(*s)) { ap *= 10; ap += *s++ - '0'; } if (*s == '|') s++; if (*s == ')') s++; if (*s == '\n') s--; /* * check for CR-LF at the end. */ if ((*s != '\r') || (*(s + 1) != '\n')) { return 0; } s += 2; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n", "229 Entering Extended Passive Mode", ap); #else (void) sprintf(newbuf, "%s (|||%u|)\r\n", "229 Entering Extended Passive Mode", ap); #endif return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap, newbuf, s); } #ifdef USE_INET6 int ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen) ipf_ftp_softc_t *softf; fr_info_t *fin; ip_t *ip; nat_t *nat; ftpinfo_t *ftp; int dlen; { int port, olen, nlen, inc, off, left, i; char newbuf[IPF_FTPBUFSZ]; char *s, c; i6addr_t addr, *a6; tcphdr_t *tcp; ip6_t *ip6; char delim; u_short whole; u_short part; ftpside_t *f; u_short *t; int fwd; mb_t *m; u_32_t a; m = fin->fin_m; ip6 = (ip6_t *)ip; f = &ftp->ftp_side[0]; s = f->ftps_rptr + 8; f = &ftp->ftp_side[0]; delim = f->ftps_rptr[5]; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; addr.i6[0] = 0; addr.i6[1] = 0; addr.i6[2] = 0; addr.i6[3] = 0; /* * Parse an IPv6 address. * Go forward until either :: or | is found. If :: is found, * reverse direction. Direction change is performed to ease * parsing an unknown number of 0s in the middle. */ whole = 0; t = (u_short *)&addr; fwd = 1; for (part = 0; (c = *s) != '\0'; ) { if (c == delim) { *t = htons((u_short)whole); break; } if (c == ':') { *t = part; if (fwd) { *t = htons((u_short)whole); t++; } else { *t = htons((u_short)(whole >> 16)); t--; } whole = 0; if (fwd == 1 && s[1] == ':') { while (*s && *s != '|') s++; if ((c = *s) != delim) break; t = (u_short *)&addr.i6[3]; t++; fwd = 0; } else if (fwd == 0 && s[-1] == ':') { break; } } else { if (c >= '0' && c <= '9') { c -= '0'; } else if (c >= 'a' && c <= 'f') { c -= 'a' + 10; } else if (c >= 'A' && c <= 'F') { c -= 'A' + 10; } if (fwd) { whole <<= 8; whole |= c; } else { whole >>= 8; whole |= ((u_32_t)c) << 24; } } if (fwd) s++; else s--; } if (c != ':' && c != delim) return 0; while (*s != '|') s++; s++; /* * Get the port number */ i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 65535) return 0; if (c != delim) return 0; port = (u_short)(i & 0xffff); /* * Check for CR-LF at the end of the command string. */ if ((*s != '\r') || (*(s + 1) != '\n')) { DT(eprt6_no_crlf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_eprt6:missing %s\n", "cr-lf"); return 0; } s += 2; /* * Calculate new address parts for PORT command */ a6 = (i6addr_t *)&ip6->ip6_src; olen = s - f->ftps_rptr; /* DO NOT change this to snprintf! */ /* * While we could force the use of | as a delimiter here, it makes * sense to preserve whatever character is being used by the systems * involved in the communication. */ s = newbuf; left = sizeof(newbuf); #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(newbuf, left, "EPRT %c2%c", delim, delim); left -= strlen(s) + 1; s += strlen(s); a = ntohl(a6->i6[0]); SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); a = ntohl(a6->i6[1]); SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); a = ntohl(a6->i6[2]); SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); a = ntohl(a6->i6[3]); SNPRINTF(s, left, "%x:%x", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); SNPRINTF(s, left, "|%d|\r\n", port); #else (void) sprintf(s, "EPRT %c2%c", delim, delim); s += strlen(s); a = ntohl(a6->i6[0]); sprintf(s, "%x:%x:", a >> 16, a & 0xffff); s += strlen(s); a = ntohl(a6->i6[1]); sprintf(s, "%x:%x:", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); a = ntohl(a6->i6[2]); sprintf(s, "%x:%x:", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); a = ntohl(a6->i6[3]); sprintf(s, "%x:%x", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); sprintf(s, "|%d|\r\n", port); #endif nlen = strlen(newbuf); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) { DT2(eprt6_len, int, inc, int, fin->fin_plen); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_eprt6:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; #if !defined(_KERNEL) M_ADJ(m, inc); #else if (inc < 0) M_ADJ(m, inc); #endif /* the mbuf chain will be extended if necessary by m_copyback() */ COPYBACK(m, off, nlen, newbuf); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { fin->fin_plen += inc; ip6->ip6_plen = htons(fin->fin_plen - fin->fin_hlen); fin->fin_dlen += inc; } f->ftps_cmd = FTPXY_C_EPRT; return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); } #endif diff --git a/sys/contrib/ipfilter/netinet/ip_htable.c b/sys/contrib/ipfilter/netinet/ip_htable.c index 0786355cd87a..bd76c2b5b0be 100644 --- a/sys/contrib/ipfilter/netinet/ip_htable.c +++ b/sys/contrib/ipfilter/netinet/ip_htable.c @@ -1,1463 +1,1463 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # define _KERNEL # include # undef _KERNEL #endif #include #if defined(__FreeBSD_version) # include #endif #if defined(__FreeBSD__) # include # include #endif #if !defined(__SVR4) # include #endif #if defined(_KERNEL) # include #else # include "ipf.h" #endif #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_lookup.h" #include "netinet/ip_htable.h" /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif # ifdef USE_INET6 -static iphtent_t *ipf_iphmfind6 __P((iphtable_t *, i6addr_t *)); +static iphtent_t *ipf_iphmfind6(iphtable_t *, i6addr_t *); # endif -static iphtent_t *ipf_iphmfind __P((iphtable_t *, struct in_addr *)); -static int ipf_iphmfindip __P((ipf_main_softc_t *, void *, int, void *, u_int)); -static int ipf_htable_clear __P((ipf_main_softc_t *, void *, iphtable_t *)); -static int ipf_htable_create __P((ipf_main_softc_t *, void *, iplookupop_t *)); -static int ipf_htable_deref __P((ipf_main_softc_t *, void *, void *)); -static int ipf_htable_destroy __P((ipf_main_softc_t *, void *, int, char *)); -static void *ipf_htable_exists __P((void *, int, char *)); -static size_t ipf_htable_flush __P((ipf_main_softc_t *, void *, - iplookupflush_t *)); -static void ipf_htable_free __P((void *, iphtable_t *)); -static int ipf_htable_iter_deref __P((ipf_main_softc_t *, void *, int, - int, void *)); -static int ipf_htable_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, - ipflookupiter_t *)); -static int ipf_htable_node_add __P((ipf_main_softc_t *, void *, - iplookupop_t *, int)); -static int ipf_htable_node_del __P((ipf_main_softc_t *, void *, - iplookupop_t *, int)); -static int ipf_htable_remove __P((ipf_main_softc_t *, void *, iphtable_t *)); -static void *ipf_htable_soft_create __P((ipf_main_softc_t *)); -static void ipf_htable_soft_destroy __P((ipf_main_softc_t *, void *)); -static int ipf_htable_soft_init __P((ipf_main_softc_t *, void *)); -static void ipf_htable_soft_fini __P((ipf_main_softc_t *, void *)); -static int ipf_htable_stats_get __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_htable_table_add __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_htable_table_del __P((ipf_main_softc_t *, void *, - iplookupop_t *)); -static int ipf_htent_deref __P((void *, iphtent_t *)); -static iphtent_t *ipf_htent_find __P((iphtable_t *, iphtent_t *)); -static int ipf_htent_insert __P((ipf_main_softc_t *, void *, iphtable_t *, - iphtent_t *)); -static int ipf_htent_remove __P((ipf_main_softc_t *, void *, iphtable_t *, - iphtent_t *)); -static void *ipf_htable_select_add_ref __P((void *, int, char *)); -static void ipf_htable_expire __P((ipf_main_softc_t *, void *)); +static iphtent_t *ipf_iphmfind(iphtable_t *, struct in_addr *); +static int ipf_iphmfindip(ipf_main_softc_t *, void *, int, void *, u_int); +static int ipf_htable_clear(ipf_main_softc_t *, void *, iphtable_t *); +static int ipf_htable_create(ipf_main_softc_t *, void *, iplookupop_t *); +static int ipf_htable_deref(ipf_main_softc_t *, void *, void *); +static int ipf_htable_destroy(ipf_main_softc_t *, void *, int, char *); +static void *ipf_htable_exists(void *, int, char *); +static size_t ipf_htable_flush(ipf_main_softc_t *, void *, + iplookupflush_t *); +static void ipf_htable_free(void *, iphtable_t *); +static int ipf_htable_iter_deref(ipf_main_softc_t *, void *, int, + int, void *); +static int ipf_htable_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, + ipflookupiter_t *); +static int ipf_htable_node_add(ipf_main_softc_t *, void *, + iplookupop_t *, int); +static int ipf_htable_node_del(ipf_main_softc_t *, void *, + iplookupop_t *, int); +static int ipf_htable_remove(ipf_main_softc_t *, void *, iphtable_t *); +static void *ipf_htable_soft_create(ipf_main_softc_t *); +static void ipf_htable_soft_destroy(ipf_main_softc_t *, void *); +static int ipf_htable_soft_init(ipf_main_softc_t *, void *); +static void ipf_htable_soft_fini(ipf_main_softc_t *, void *); +static int ipf_htable_stats_get(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_htable_table_add(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_htable_table_del(ipf_main_softc_t *, void *, + iplookupop_t *); +static int ipf_htent_deref(void *, iphtent_t *); +static iphtent_t *ipf_htent_find(iphtable_t *, iphtent_t *); +static int ipf_htent_insert(ipf_main_softc_t *, void *, iphtable_t *, + iphtent_t *); +static int ipf_htent_remove(ipf_main_softc_t *, void *, iphtable_t *, + iphtent_t *); +static void *ipf_htable_select_add_ref(void *, int, char *); +static void ipf_htable_expire(ipf_main_softc_t *, void *); typedef struct ipf_htable_softc_s { u_long ipht_nomem[LOOKUP_POOL_SZ]; u_long ipf_nhtables[LOOKUP_POOL_SZ]; u_long ipf_nhtnodes[LOOKUP_POOL_SZ]; iphtable_t *ipf_htables[LOOKUP_POOL_SZ]; iphtent_t *ipf_node_explist; } ipf_htable_softc_t; ipf_lookup_t ipf_htable_backend = { IPLT_HASH, ipf_htable_soft_create, ipf_htable_soft_destroy, ipf_htable_soft_init, ipf_htable_soft_fini, ipf_iphmfindip, ipf_htable_flush, ipf_htable_iter_deref, ipf_htable_iter_next, ipf_htable_node_add, ipf_htable_node_del, ipf_htable_stats_get, ipf_htable_table_add, ipf_htable_table_del, ipf_htable_deref, ipf_htable_exists, ipf_htable_select_add_ref, NULL, ipf_htable_expire, NULL }; /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise the routing table data structures where required. */ /* ------------------------------------------------------------------------ */ static void * ipf_htable_soft_create(softc) ipf_main_softc_t *softc; { ipf_htable_softc_t *softh; KMALLOC(softh, ipf_htable_softc_t *); if (softh == NULL) { IPFERROR(30026); return NULL; } bzero((char *)softh, sizeof(*softh)); return softh; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Clean up the pool by free'ing the radix tree associated with it and free */ /* up the pool context too. */ /* ------------------------------------------------------------------------ */ static void ipf_htable_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_htable_softc_t *softh = arg; KFREE(softh); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash table ready for use. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_htable_softc_t *softh = arg; bzero((char *)softh, sizeof(*softh)); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* Locks: WRITE(ipf_global) */ /* */ /* Clean up all the pool data structures allocated and call the cleanup */ /* function for the radix tree that supports the pools. ipf_pool_destroy is */ /* used to delete the pools one by one to ensure they're properly freed up. */ /* ------------------------------------------------------------------------ */ static void ipf_htable_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { iplookupflush_t fop; fop.iplf_type = IPLT_HASH; fop.iplf_unit = IPL_LOGALL; fop.iplf_arg = 0; fop.iplf_count = 0; *fop.iplf_name = '\0'; ipf_htable_flush(softc, arg, &fop); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_stats_get */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Copy the relevant statistics out of internal structures and into the */ /* structure used to export statistics. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_stats_get(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ipf_htable_softc_t *softh = arg; iphtstat_t stats; int err; if (op->iplo_size != sizeof(stats)) { IPFERROR(30001); return EINVAL; } stats.iphs_tables = softh->ipf_htables[op->iplo_unit + 1]; stats.iphs_numtables = softh->ipf_nhtables[op->iplo_unit + 1]; stats.iphs_numnodes = softh->ipf_nhtnodes[op->iplo_unit + 1]; stats.iphs_nomem = softh->ipht_nomem[op->iplo_unit + 1]; err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); if (err != 0) { IPFERROR(30013); return EFAULT; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_create */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Create a new hash table using the template passed. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_create(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ipf_htable_softc_t *softh = arg; iphtable_t htab, *iph, *oiph; char name[FR_GROUPLEN]; int err, i, unit; if (op->iplo_size != sizeof(htab)) { IPFERROR(30024); return EINVAL; } err = COPYIN(op->iplo_struct, &htab, sizeof(htab)); if (err != 0) { IPFERROR(30003); return EFAULT; } unit = op->iplo_unit; if (htab.iph_unit != unit) { IPFERROR(30005); return EINVAL; } if (htab.iph_size < 1) { IPFERROR(30025); return EINVAL; } if ((op->iplo_arg & IPHASH_ANON) == 0) { iph = ipf_htable_exists(softh, unit, op->iplo_name); if (iph != NULL) { if ((iph->iph_flags & IPHASH_DELETE) == 0) { IPFERROR(30004); return EEXIST; } iph->iph_flags &= ~IPHASH_DELETE; iph->iph_ref++; return 0; } } KMALLOC(iph, iphtable_t *); if (iph == NULL) { softh->ipht_nomem[op->iplo_unit + 1]++; IPFERROR(30002); return ENOMEM; } *iph = htab; if ((op->iplo_arg & IPHASH_ANON) != 0) { i = IPHASH_ANON; do { i++; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(name, sizeof(name), "%u", i); #else (void)sprintf(name, "%u", i); #endif for (oiph = softh->ipf_htables[unit + 1]; oiph != NULL; oiph = oiph->iph_next) if (strncmp(oiph->iph_name, name, sizeof(oiph->iph_name)) == 0) break; } while (oiph != NULL); (void)strncpy(iph->iph_name, name, sizeof(iph->iph_name)); (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); iph->iph_type |= IPHASH_ANON; } else { (void)strncpy(iph->iph_name, op->iplo_name, sizeof(iph->iph_name)); iph->iph_name[sizeof(iph->iph_name) - 1] = '\0'; } KMALLOCS(iph->iph_table, iphtent_t **, iph->iph_size * sizeof(*iph->iph_table)); if (iph->iph_table == NULL) { KFREE(iph); softh->ipht_nomem[unit + 1]++; IPFERROR(30006); return ENOMEM; } bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); iph->iph_maskset[0] = 0; iph->iph_maskset[1] = 0; iph->iph_maskset[2] = 0; iph->iph_maskset[3] = 0; iph->iph_ref = 1; iph->iph_list = NULL; iph->iph_tail = &iph->iph_list; iph->iph_next = softh->ipf_htables[unit + 1]; iph->iph_pnext = &softh->ipf_htables[unit + 1]; if (softh->ipf_htables[unit + 1] != NULL) softh->ipf_htables[unit + 1]->iph_pnext = &iph->iph_next; softh->ipf_htables[unit + 1] = iph; softh->ipf_nhtables[unit + 1]++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_table_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_table_del(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { return ipf_htable_destroy(softc, arg, op->iplo_unit, op->iplo_name); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_destroy */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Find the hash table that belongs to the relevant part of ipfilter with a */ /* matching name and attempt to destroy it. If it is in use, empty it out */ /* and mark it for deletion so that when all the references disappear, it */ /* can be removed. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_destroy(softc, arg, unit, name) ipf_main_softc_t *softc; void *arg; int unit; char *name; { iphtable_t *iph; iph = ipf_htable_find(arg, unit, name); if (iph == NULL) { IPFERROR(30007); return ESRCH; } if (iph->iph_unit != unit) { IPFERROR(30008); return EINVAL; } if (iph->iph_ref != 0) { ipf_htable_clear(softc, arg, iph); iph->iph_flags |= IPHASH_DELETE; return 0; } ipf_htable_remove(softc, arg, iph); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_clear */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* Clean out the hash table by walking the list of entries and removing */ /* each one, one by one. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_clear(softc, arg, iph) ipf_main_softc_t *softc; void *arg; iphtable_t *iph; { iphtent_t *ipe; while ((ipe = iph->iph_list) != NULL) if (ipf_htent_remove(softc, arg, iph, ipe) != 0) return 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_free */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* ------------------------------------------------------------------------ */ static void ipf_htable_free(arg, iph) void *arg; iphtable_t *iph; { ipf_htable_softc_t *softh = arg; if (iph->iph_next != NULL) iph->iph_next->iph_pnext = iph->iph_pnext; if (iph->iph_pnext != NULL) *iph->iph_pnext = iph->iph_next; iph->iph_pnext = NULL; iph->iph_next = NULL; softh->ipf_nhtables[iph->iph_unit + 1]--; KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); KFREE(iph); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_remove */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* It is necessary to unlink here as well as free (called by deref) so that */ /* the while loop in ipf_htable_flush() functions properly. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_remove(softc, arg, iph) ipf_main_softc_t *softc; void *arg; iphtable_t *iph; { if (ipf_htable_clear(softc, arg, iph) != 0) return 1; if (iph->iph_pnext != NULL) *iph->iph_pnext = iph->iph_next; if (iph->iph_next != NULL) iph->iph_next->iph_pnext = iph->iph_pnext; iph->iph_pnext = NULL; iph->iph_next = NULL; return ipf_htable_deref(softc, arg, iph); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - real uid of process doing operation */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_node_del(softc, arg, op, uid) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; int uid; { iphtable_t *iph; iphtent_t hte, *ent; int err; if (op->iplo_size != sizeof(hte)) { IPFERROR(30014); return EINVAL; } err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); if (err != 0) { IPFERROR(30015); return EFAULT; } iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); if (iph == NULL) { IPFERROR(30016); return ESRCH; } ent = ipf_htent_find(iph, &hte); if (ent == NULL) { IPFERROR(30022); return ESRCH; } if ((uid != 0) && (ent->ipe_uid != uid)) { IPFERROR(30023); return EACCES; } err = ipf_htent_remove(softc, arg, iph, ent); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_table_add(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { int err; if (ipf_htable_find(arg, op->iplo_unit, op->iplo_name) != NULL) { IPFERROR(30017); err = EEXIST; } else { err = ipf_htable_create(softc, arg, op); } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_remove */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table */ /* ipe(I) - pointer to hash table entry to remove */ /* */ /* Delete an entry from a hash table. */ /* ------------------------------------------------------------------------ */ static int ipf_htent_remove(softc, arg, iph, ipe) ipf_main_softc_t *softc; void *arg; iphtable_t *iph; iphtent_t *ipe; { if (iph->iph_tail == &ipe->ipe_next) iph->iph_tail = ipe->ipe_pnext; if (ipe->ipe_hnext != NULL) ipe->ipe_hnext->ipe_phnext = ipe->ipe_phnext; if (ipe->ipe_phnext != NULL) *ipe->ipe_phnext = ipe->ipe_hnext; ipe->ipe_phnext = NULL; ipe->ipe_hnext = NULL; if (ipe->ipe_dnext != NULL) ipe->ipe_dnext->ipe_pdnext = ipe->ipe_pdnext; if (ipe->ipe_pdnext != NULL) *ipe->ipe_pdnext = ipe->ipe_dnext; ipe->ipe_pdnext = NULL; ipe->ipe_dnext = NULL; if (ipe->ipe_next != NULL) ipe->ipe_next->ipe_pnext = ipe->ipe_pnext; if (ipe->ipe_pnext != NULL) *ipe->ipe_pnext = ipe->ipe_next; ipe->ipe_pnext = NULL; ipe->ipe_next = NULL; switch (iph->iph_type & ~IPHASH_ANON) { case IPHASH_GROUPMAP : if (ipe->ipe_group != NULL) ipf_group_del(softc, ipe->ipe_ptr, NULL); break; default : ipe->ipe_ptr = NULL; ipe->ipe_value = 0; break; } return ipf_htent_deref(arg, ipe); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* object(I) - pointer to hash table */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_deref(softc, arg, object) ipf_main_softc_t *softc; void *arg, *object; { ipf_htable_softc_t *softh = arg; iphtable_t *iph = object; int refs; iph->iph_ref--; refs = iph->iph_ref; if (iph->iph_ref == 0) { ipf_htable_free(softh, iph); } return refs; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_deref */ /* Parameters: arg(I) - pointer to local context to use */ /* ipe(I) - */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htent_deref(arg, ipe) void *arg; iphtent_t *ipe; { ipf_htable_softc_t *softh = arg; ipe->ipe_ref--; if (ipe->ipe_ref == 0) { softh->ipf_nhtnodes[ipe->ipe_unit + 1]--; KFREE(ipe); return 0; } return ipe->ipe_ref; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_exists */ /* Parameters: arg(I) - pointer to local context to use */ /* */ /* ------------------------------------------------------------------------ */ static void * ipf_htable_exists(arg, unit, name) void *arg; int unit; char *name; { ipf_htable_softc_t *softh = arg; iphtable_t *iph; if (unit == IPL_LOGALL) { int i; for (i = 0; i <= LOOKUP_POOL_MAX; i++) { for (iph = softh->ipf_htables[i]; iph != NULL; iph = iph->iph_next) { if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) break; } if (iph != NULL) break; } } else { for (iph = softh->ipf_htables[unit + 1]; iph != NULL; iph = iph->iph_next) { if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) break; } } return iph; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_select_add_ref */ /* Returns: void * - NULL = failure, else pointer to the hash table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the hash table */ /* */ /* ------------------------------------------------------------------------ */ static void * ipf_htable_select_add_ref(arg, unit, name) void *arg; int unit; char *name; { iphtable_t *iph; iph = ipf_htable_exists(arg, unit, name); if (iph != NULL) { ATOMIC_INC32(iph->iph_ref); } return iph; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_find */ /* Returns: void * - NULL = failure, else pointer to the hash table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the hash table */ /* */ /* This function is exposed becaues it is used in the group-map feature. */ /* ------------------------------------------------------------------------ */ iphtable_t * ipf_htable_find(arg, unit, name) void *arg; int unit; char *name; { iphtable_t *iph; iph = ipf_htable_exists(arg, unit, name); if ((iph != NULL) && (iph->iph_flags & IPHASH_DELETE) == 0) return iph; return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_flush */ /* Returns: size_t - number of entries flushed */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static size_t ipf_htable_flush(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupflush_t *op; { ipf_htable_softc_t *softh = arg; iphtable_t *iph; size_t freed; int i; freed = 0; for (i = -1; i <= IPL_LOGMAX; i++) { if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) { while ((iph = softh->ipf_htables[i + 1]) != NULL) { if (ipf_htable_remove(softc, arg, iph) == 0) { freed++; } else { iph->iph_flags |= IPHASH_DELETE; } } } } return freed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - real uid of process doing operation */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_node_add(softc, arg, op, uid) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; int uid; { iphtable_t *iph; iphtent_t hte; int err; if (op->iplo_size != sizeof(hte)) { IPFERROR(30018); return EINVAL; } err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); if (err != 0) { IPFERROR(30019); return EFAULT; } hte.ipe_uid = uid; iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); if (iph == NULL) { IPFERROR(30020); return ESRCH; } if (ipf_htent_find(iph, &hte) != NULL) { IPFERROR(30021); return EEXIST; } err = ipf_htent_insert(softc, arg, iph, &hte); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_insert */ /* Returns: int - 0 = success, -1 = error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* ipeo(I) - */ /* */ /* Add an entry to a hash table. */ /* ------------------------------------------------------------------------ */ static int ipf_htent_insert(softc, arg, iph, ipeo) ipf_main_softc_t *softc; void *arg; iphtable_t *iph; iphtent_t *ipeo; { ipf_htable_softc_t *softh = arg; iphtent_t *ipe; u_int hv; int bits; KMALLOC(ipe, iphtent_t *); if (ipe == NULL) return -1; bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe)); ipe->ipe_addr.i6[0] &= ipe->ipe_mask.i6[0]; if (ipe->ipe_family == AF_INET) { bits = count4bits(ipe->ipe_mask.in4_addr); ipe->ipe_addr.i6[1] = 0; ipe->ipe_addr.i6[2] = 0; ipe->ipe_addr.i6[3] = 0; ipe->ipe_mask.i6[1] = 0; ipe->ipe_mask.i6[2] = 0; ipe->ipe_mask.i6[3] = 0; hv = IPE_V4_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr, iph->iph_size); } else #ifdef USE_INET6 if (ipe->ipe_family == AF_INET6) { ipe->ipe_addr.i6[1] &= ipe->ipe_mask.i6[1]; ipe->ipe_addr.i6[2] &= ipe->ipe_mask.i6[2]; ipe->ipe_addr.i6[3] &= ipe->ipe_mask.i6[3]; bits = count6bits(ipe->ipe_mask.i6); hv = IPE_V6_HASH_FN(ipe->ipe_addr.i6, ipe->ipe_mask.i6, iph->iph_size); } else #endif { KFREE(ipe); return -1; } ipe->ipe_owner = iph; ipe->ipe_ref = 1; ipe->ipe_hnext = iph->iph_table[hv]; ipe->ipe_phnext = iph->iph_table + hv; if (iph->iph_table[hv] != NULL) iph->iph_table[hv]->ipe_phnext = &ipe->ipe_hnext; iph->iph_table[hv] = ipe; ipe->ipe_pnext = iph->iph_tail; *iph->iph_tail = ipe; iph->iph_tail = &ipe->ipe_next; ipe->ipe_next = NULL; if (ipe->ipe_die != 0) { /* * If the new node has a given expiration time, insert it * into the list of expiring nodes with the ones to be * removed first added to the front of the list. The * insertion is O(n) but it is kept sorted for quick scans * at expiration interval checks. */ iphtent_t *n; ipe->ipe_die = softc->ipf_ticks + IPF_TTLVAL(ipe->ipe_die); for (n = softh->ipf_node_explist; n != NULL; n = n->ipe_dnext) { if (ipe->ipe_die < n->ipe_die) break; if (n->ipe_dnext == NULL) { /* * We've got to the last node and everything * wanted to be expired before this new node, * so we have to tack it on the end... */ n->ipe_dnext = ipe; ipe->ipe_pdnext = &n->ipe_dnext; n = NULL; break; } } if (softh->ipf_node_explist == NULL) { softh->ipf_node_explist = ipe; ipe->ipe_pdnext = &softh->ipf_node_explist; } else if (n != NULL) { ipe->ipe_dnext = n; ipe->ipe_pdnext = n->ipe_pdnext; n->ipe_pdnext = &ipe->ipe_dnext; } } if (ipe->ipe_family == AF_INET) { ipf_inet_mask_add(bits, &iph->iph_v4_masks); } #ifdef USE_INET6 else if (ipe->ipe_family == AF_INET6) { ipf_inet6_mask_add(bits, &ipe->ipe_mask, &iph->iph_v6_masks); } #endif switch (iph->iph_type & ~IPHASH_ANON) { case IPHASH_GROUPMAP : ipe->ipe_ptr = ipf_group_add(softc, ipe->ipe_group, NULL, iph->iph_flags, IPL_LOGIPF, softc->ipf_active); break; default : ipe->ipe_ptr = NULL; ipe->ipe_value = 0; break; } ipe->ipe_unit = iph->iph_unit; softh->ipf_nhtnodes[ipe->ipe_unit + 1]++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_find */ /* Returns: int - 0 = success, else error */ /* Parameters: iph(I) - pointer to table to search */ /* ipeo(I) - pointer to entry to find */ /* */ /* While it isn't absolutely necessary to for the address and mask to be */ /* passed in through an iphtent_t structure, one is always present when it */ /* is time to call this function, so it is just more convenient. */ /* ------------------------------------------------------------------------ */ static iphtent_t * ipf_htent_find(iph, ipeo) iphtable_t *iph; iphtent_t *ipeo; { iphtent_t ipe, *ent; u_int hv; int bits; bcopy((char *)ipeo, (char *)&ipe, sizeof(ipe)); ipe.ipe_addr.i6[0] &= ipe.ipe_mask.i6[0]; ipe.ipe_addr.i6[1] &= ipe.ipe_mask.i6[1]; ipe.ipe_addr.i6[2] &= ipe.ipe_mask.i6[2]; ipe.ipe_addr.i6[3] &= ipe.ipe_mask.i6[3]; if (ipe.ipe_family == AF_INET) { bits = count4bits(ipe.ipe_mask.in4_addr); ipe.ipe_addr.i6[1] = 0; ipe.ipe_addr.i6[2] = 0; ipe.ipe_addr.i6[3] = 0; ipe.ipe_mask.i6[1] = 0; ipe.ipe_mask.i6[2] = 0; ipe.ipe_mask.i6[3] = 0; hv = IPE_V4_HASH_FN(ipe.ipe_addr.in4_addr, ipe.ipe_mask.in4_addr, iph->iph_size); } else #ifdef USE_INET6 if (ipe.ipe_family == AF_INET6) { bits = count6bits(ipe.ipe_mask.i6); hv = IPE_V6_HASH_FN(ipe.ipe_addr.i6, ipe.ipe_mask.i6, iph->iph_size); } else #endif return NULL; for (ent = iph->iph_table[hv]; ent != NULL; ent = ent->ipe_hnext) { if (ent->ipe_family != ipe.ipe_family) continue; if (IP6_NEQ(&ipe.ipe_addr, &ent->ipe_addr)) continue; if (IP6_NEQ(&ipe.ipe_mask, &ent->ipe_mask)) continue; break; } return ent; } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindgroup */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tptr(I) - */ /* aptr(I) - */ /* */ /* Search a hash table for a matching entry and return the pointer stored */ /* in it for use as the next group of rules to search. */ /* */ /* This function is exposed becaues it is used in the group-map feature. */ /* ------------------------------------------------------------------------ */ void * ipf_iphmfindgroup(softc, tptr, aptr) ipf_main_softc_t *softc; void *tptr, *aptr; { struct in_addr *addr; iphtable_t *iph; iphtent_t *ipe; void *rval; READ_ENTER(&softc->ipf_poolrw); iph = tptr; addr = aptr; ipe = ipf_iphmfind(iph, addr); if (ipe != NULL) rval = ipe->ipe_ptr; else rval = NULL; RWLOCK_EXIT(&softc->ipf_poolrw); return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindip */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tptr(I) - pointer to the pool to search */ /* ipversion(I) - IP protocol version (4 or 6) */ /* aptr(I) - pointer to address information */ /* bytes(I) - packet length */ /* */ /* Search the hash table for a given address and return a search result. */ /* ------------------------------------------------------------------------ */ static int ipf_iphmfindip(softc, tptr, ipversion, aptr, bytes) ipf_main_softc_t *softc; void *tptr, *aptr; int ipversion; u_int bytes; { struct in_addr *addr; iphtable_t *iph; iphtent_t *ipe; int rval; if (tptr == NULL || aptr == NULL) return -1; iph = tptr; addr = aptr; READ_ENTER(&softc->ipf_poolrw); if (ipversion == 4) { ipe = ipf_iphmfind(iph, addr); #ifdef USE_INET6 } else if (ipversion == 6) { ipe = ipf_iphmfind6(iph, (i6addr_t *)addr); #endif } else { ipe = NULL; } if (ipe != NULL) { rval = 0; ipe->ipe_hits++; ipe->ipe_bytes += bytes; } else { rval = 1; } RWLOCK_EXIT(&softc->ipf_poolrw); return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindip */ /* Parameters: iph(I) - pointer to hash table */ /* addr(I) - pointer to IPv4 address */ /* Locks: ipf_poolrw */ /* */ /* ------------------------------------------------------------------------ */ static iphtent_t * ipf_iphmfind(iph, addr) iphtable_t *iph; struct in_addr *addr; { u_32_t msk, ips; iphtent_t *ipe; u_int hv; int i; i = 0; maskloop: msk = iph->iph_v4_masks.imt4_active[i]; ips = addr->s_addr & msk; hv = IPE_V4_HASH_FN(ips, msk, iph->iph_size); for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_hnext) { if ((ipe->ipe_family != AF_INET) || (ipe->ipe_mask.in4_addr != msk) || (ipe->ipe_addr.in4_addr != ips)) { continue; } break; } if (ipe == NULL) { i++; if (i < iph->iph_v4_masks.imt4_max) goto maskloop; } return ipe; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_iter_next */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* token(I) - */ /* ilp(I) - */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_iter_next(softc, arg, token, ilp) ipf_main_softc_t *softc; void *arg; ipftoken_t *token; ipflookupiter_t *ilp; { ipf_htable_softc_t *softh = arg; iphtent_t *node, zn, *nextnode; iphtable_t *iph, zp, *nextiph; void *hnext; int err; err = 0; iph = NULL; node = NULL; nextiph = NULL; nextnode = NULL; READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : iph = token->ipt_data; if (iph == NULL) { nextiph = softh->ipf_htables[(int)ilp->ili_unit + 1]; } else { nextiph = iph->iph_next; } if (nextiph != NULL) { ATOMIC_INC(nextiph->iph_ref); token->ipt_data = nextiph; } else { bzero((char *)&zp, sizeof(zp)); nextiph = &zp; token->ipt_data = NULL; } hnext = nextiph->iph_next; break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { iph = ipf_htable_find(arg, ilp->ili_unit, ilp->ili_name); if (iph == NULL) { IPFERROR(30009); err = ESRCH; } else { nextnode = iph->iph_list; } } else { nextnode = node->ipe_next; } if (nextnode != NULL) { ATOMIC_INC(nextnode->ipe_ref); token->ipt_data = nextnode; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; } hnext = nextnode->ipe_next; break; default : IPFERROR(30010); err = EINVAL; hnext = NULL; break; } RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return err; switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : err = COPYOUT(nextiph, ilp->ili_data, sizeof(*nextiph)); if (err != 0) { IPFERROR(30011); err = EFAULT; } if (iph != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_htable_deref(softc, softh, iph); RWLOCK_EXIT(&softc->ipf_poolrw); } break; case IPFLOOKUPITER_NODE : err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); if (err != 0) { IPFERROR(30012); err = EFAULT; } if (node != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_htent_deref(softc, node); RWLOCK_EXIT(&softc->ipf_poolrw); } break; } if (hnext == NULL) ipf_token_mark_complete(token); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_iter_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* otype(I) - which data structure type is being walked */ /* unit(I) - ipfilter device to which we are working on */ /* data(I) - pointer to old data structure */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_iter_deref(softc, arg, otype, unit, data) ipf_main_softc_t *softc; void *arg; int otype; int unit; void *data; { if (data == NULL) return EFAULT; if (unit < -1 || unit > IPL_LOGMAX) return EINVAL; switch (otype) { case IPFLOOKUPITER_LIST : ipf_htable_deref(softc, arg, (iphtable_t *)data); break; case IPFLOOKUPITER_NODE : ipf_htent_deref(arg, (iphtent_t *)data); break; default : break; } return 0; } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfind6 */ /* Parameters: iph(I) - pointer to hash table */ /* addr(I) - pointer to IPv6 address */ /* Locks: ipf_poolrw */ /* */ /* ------------------------------------------------------------------------ */ static iphtent_t * ipf_iphmfind6(iph, addr) iphtable_t *iph; i6addr_t *addr; { i6addr_t *msk, ips; iphtent_t *ipe; u_int hv; int i; i = 0; maskloop: msk = iph->iph_v6_masks.imt6_active + i; ips.i6[0] = addr->i6[0] & msk->i6[0]; ips.i6[1] = addr->i6[1] & msk->i6[1]; ips.i6[2] = addr->i6[2] & msk->i6[2]; ips.i6[3] = addr->i6[3] & msk->i6[3]; hv = IPE_V6_HASH_FN(ips.i6, msk->i6, iph->iph_size); for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_next) { if ((ipe->ipe_family != AF_INET6) || IP6_NEQ(&ipe->ipe_mask, msk) || IP6_NEQ(&ipe->ipe_addr, &ips)) { continue; } break; } if (ipe == NULL) { i++; if (i < iph->iph_v6_masks.imt6_max) goto maskloop; } return ipe; } #endif static void ipf_htable_expire(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_htable_softc_t *softh = arg; iphtent_t *n; while ((n = softh->ipf_node_explist) != NULL) { if (n->ipe_die > softc->ipf_ticks) break; ipf_htent_remove(softc, softh, n->ipe_owner, n); } } #ifndef _KERNEL /* ------------------------------------------------------------------------ */ /* */ /* ------------------------------------------------------------------------ */ void ipf_htable_dump(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_htable_softc_t *softh = arg; iphtable_t *iph; int i; printf("List of configured hash tables\n"); for (i = 0; i < IPL_LOGSIZE; i++) for (iph = softh->ipf_htables[i]; iph != NULL; iph = iph->iph_next) printhash(iph, bcopywrap, NULL, opts, NULL); } #endif diff --git a/sys/contrib/ipfilter/netinet/ip_htable.h b/sys/contrib/ipfilter/netinet/ip_htable.h index 8038bbcc1044..55c289e57ff6 100644 --- a/sys/contrib/ipfilter/netinet/ip_htable.h +++ b/sys/contrib/ipfilter/netinet/ip_htable.h @@ -1,81 +1,81 @@ #ifndef __IP_HTABLE_H__ #define __IP_HTABLE_H__ #include "netinet/ip_lookup.h" typedef struct iphtent_s { struct iphtent_s *ipe_next, **ipe_pnext; struct iphtent_s *ipe_hnext, **ipe_phnext; struct iphtent_s *ipe_dnext, **ipe_pdnext; struct iphtable_s *ipe_owner; void *ipe_ptr; i6addr_t ipe_addr; i6addr_t ipe_mask; U_QUAD_T ipe_hits; U_QUAD_T ipe_bytes; u_long ipe_die; int ipe_uid; int ipe_ref; int ipe_unit; char ipe_family; char ipe_xxx[3]; union { char ipeu_char[16]; u_long ipeu_long; u_int ipeu_int; } ipe_un; } iphtent_t; #define ipe_value ipe_un.ipeu_int #define ipe_group ipe_un.ipeu_char #define IPE_V4_HASH_FN(a, m, s) ((((m) ^ (a)) - 1 - ((a) >> 8)) % (s)) #define IPE_V6_HASH_FN(a, m, s) (((((m)[0] ^ (a)[0]) - ((a)[0] >> 8)) + \ (((m)[1] & (a)[1]) - ((a)[1] >> 8)) + \ (((m)[2] & (a)[2]) - ((a)[2] >> 8)) + \ (((m)[3] & (a)[3]) - ((a)[3] >> 8))) % (s)) typedef struct iphtable_s { ipfrwlock_t iph_rwlock; struct iphtable_s *iph_next, **iph_pnext; struct iphtent_s **iph_table; struct iphtent_s *iph_list; struct iphtent_s **iph_tail; #ifdef USE_INET6 ipf_v6_masktab_t iph_v6_masks; #endif ipf_v4_masktab_t iph_v4_masks; size_t iph_size; /* size of hash table */ u_long iph_seed; /* hashing seed */ u_32_t iph_flags; u_int iph_unit; /* IPL_LOG* */ u_int iph_ref; u_int iph_type; /* lookup or group map - IPHASH_* */ u_int iph_maskset[4]; /* netmasks in use */ char iph_name[FR_GROUPLEN]; /* hash table number */ } iphtable_t; /* iph_type */ #define IPHASH_LOOKUP 0 #define IPHASH_GROUPMAP 1 #define IPHASH_DELETE 2 #define IPHASH_ANON 0x80000000 typedef struct iphtstat_s { iphtable_t *iphs_tables; u_long iphs_numtables; u_long iphs_numnodes; u_long iphs_nomem; u_long iphs_pad[16]; } iphtstat_t; -extern void *ipf_iphmfindgroup __P((ipf_main_softc_t *, void *, void *)); -extern iphtable_t *ipf_htable_find __P((void *, int, char *)); +extern void *ipf_iphmfindgroup(ipf_main_softc_t *, void *, void *); +extern iphtable_t *ipf_htable_find(void *, int, char *); extern ipf_lookup_t ipf_htable_backend; #ifndef _KERNEL -extern void ipf_htable_dump __P((ipf_main_softc_t *, void *)); +extern void ipf_htable_dump(ipf_main_softc_t *, void *); #endif #endif /* __IP_HTABLE_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c b/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c index 980476ad8bf9..ca0927d00dbf 100644 --- a/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c @@ -1,431 +1,431 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Simple ISAKMP transparent proxy for in-kernel use. For use with the NAT * code. * * $Id$ * */ #define IPF_IPSEC_PROXY /* * IPSec proxy */ typedef struct ipf_ipsec_softc_s { frentry_t ipsec_fr; int ipsec_proxy_init; int ipsec_proxy_ttl; ipftq_t *ipsec_nat_tqe; ipftq_t *ipsec_state_tqe; char ipsec_buffer[1500]; } ipf_ipsec_softc_t; -void *ipf_p_ipsec_soft_create __P((ipf_main_softc_t *)); -void ipf_p_ipsec_soft_destroy __P((ipf_main_softc_t *, void *)); -int ipf_p_ipsec_soft_init __P((ipf_main_softc_t *, void *)); -void ipf_p_ipsec_soft_fini __P((ipf_main_softc_t *, void *)); -int ipf_p_ipsec_init __P((void)); -void ipf_p_ipsec_fini __P((void)); -int ipf_p_ipsec_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -void ipf_p_ipsec_del __P((ipf_main_softc_t *, ap_session_t *)); -int ipf_p_ipsec_inout __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_ipsec_match __P((fr_info_t *, ap_session_t *, nat_t *)); +void *ipf_p_ipsec_soft_create(ipf_main_softc_t *); +void ipf_p_ipsec_soft_destroy(ipf_main_softc_t *, void *); +int ipf_p_ipsec_soft_init(ipf_main_softc_t *, void *); +void ipf_p_ipsec_soft_fini(ipf_main_softc_t *, void *); +int ipf_p_ipsec_init(void); +void ipf_p_ipsec_fini(void); +int ipf_p_ipsec_new(void *, fr_info_t *, ap_session_t *, nat_t *); +void ipf_p_ipsec_del(ipf_main_softc_t *, ap_session_t *); +int ipf_p_ipsec_inout(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_ipsec_match(fr_info_t *, ap_session_t *, nat_t *); /* * IPSec application proxy initialization. */ void * ipf_p_ipsec_soft_create(softc) ipf_main_softc_t *softc; { ipf_ipsec_softc_t *softi; KMALLOC(softi, ipf_ipsec_softc_t *); if (softi == NULL) return NULL; bzero((char *)softi, sizeof(*softi)); softi->ipsec_fr.fr_ref = 1; softi->ipsec_fr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&softi->ipsec_fr.fr_lock, "IPsec proxy rule lock"); softi->ipsec_proxy_init = 1; softi->ipsec_proxy_ttl = 60; return softi; } int ipf_p_ipsec_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_ipsec_softc_t *softi = arg; softi->ipsec_nat_tqe = ipf_state_add_tq(softc, softi->ipsec_proxy_ttl); if (softi->ipsec_nat_tqe == NULL) return -1; softi->ipsec_state_tqe = ipf_nat_add_tq(softc, softi->ipsec_proxy_ttl); if (softi->ipsec_state_tqe == NULL) { if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); softi->ipsec_nat_tqe = NULL; return -1; } softi->ipsec_nat_tqe->ifq_flags |= IFQF_PROXY; softi->ipsec_state_tqe->ifq_flags |= IFQF_PROXY; softi->ipsec_fr.fr_age[0] = softi->ipsec_proxy_ttl; softi->ipsec_fr.fr_age[1] = softi->ipsec_proxy_ttl; return 0; } void ipf_p_ipsec_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_ipsec_softc_t *softi = arg; if (arg == NULL) return; if (softi->ipsec_nat_tqe != NULL) { if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); } softi->ipsec_nat_tqe = NULL; if (softi->ipsec_state_tqe != NULL) { if (ipf_deletetimeoutqueue(softi->ipsec_state_tqe) == 0) ipf_freetimeoutqueue(softc, softi->ipsec_state_tqe); } softi->ipsec_state_tqe = NULL; } void ipf_p_ipsec_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_ipsec_softc_t *softi = arg; if (softi->ipsec_proxy_init == 1) { MUTEX_DESTROY(&softi->ipsec_fr.fr_lock); softi->ipsec_proxy_init = 0; } KFREE(softi); } /* * Setup for a new IPSEC proxy. */ int ipf_p_ipsec_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipf_ipsec_softc_t *softi = arg; ipf_main_softc_t *softc = fin->fin_main_soft; #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif int p, off, dlen, ttl; ipsec_pxy_t *ipsec; ipnat_t *ipn, *np; fr_info_t fi; char *ptr; int size; ip_t *ip; mb_t *m; if (fin->fin_v != 4) return -1; off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; bzero(softi->ipsec_buffer, sizeof(softi->ipsec_buffer)); ip = fin->fin_ip; m = fin->fin_m; dlen = M_LEN(m) - off; if (dlen < 16) return -1; COPYDATA(m, off, MIN(sizeof(softi->ipsec_buffer), dlen), softi->ipsec_buffer); if (ipf_nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_nsrcip, ip->ip_dst) != NULL) return -1; np = nat->nat_ptr; size = np->in_size; KMALLOC(ipsec, ipsec_pxy_t *); if (ipsec == NULL) return -1; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) { KFREE(ipsec); return -1; } aps->aps_data = ipsec; aps->aps_psiz = sizeof(*ipsec); bzero((char *)ipsec, sizeof(*ipsec)); bzero((char *)ipn, size); ipsec->ipsc_rule = ipn; /* * Create NAT rule against which the tunnel/transport mapping is * created. This is required because the current NAT rule does not * describe ESP but UDP instead. */ ipn->in_size = size; ttl = IPF_TTLVAL(softi->ipsec_nat_tqe->ifq_ttl); ipn->in_tqehead[0] = ipf_nat_add_tq(softc, ttl); ipn->in_tqehead[1] = ipf_nat_add_tq(softc, ttl); ipn->in_ifps[0] = fin->fin_ifp; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_ippip = 1; ipn->in_osrcip = nat->nat_osrcip; ipn->in_osrcmsk = 0xffffffff; ipn->in_nsrcip = nat->nat_nsrcip; ipn->in_nsrcmsk = 0xffffffff; ipn->in_odstip = nat->nat_odstip; ipn->in_odstmsk = 0xffffffff; ipn->in_ndstip = nat->nat_ndstip; ipn->in_ndstmsk = 0xffffffff; ipn->in_redir = NAT_MAP; ipn->in_pr[0] = IPPROTO_ESP; ipn->in_pr[1] = IPPROTO_ESP; ipn->in_flags = (np->in_flags | IPN_PROXYRULE); MUTEX_INIT(&ipn->in_lock, "IPSec proxy NAT rule"); ipn->in_namelen = np->in_namelen; bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); ipn->in_ifnames[0] = np->in_ifnames[0]; ipn->in_ifnames[1] = np->in_ifnames[1]; bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_fi.fi_p = IPPROTO_ESP; fi.fin_fr = &softi->ipsec_fr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; p = ip->ip_p; ip->ip_p = IPPROTO_ESP; fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); fi.fin_flx |= FI_IGNORE; ptr = softi->ipsec_buffer; bcopy(ptr, (char *)ipsec->ipsc_icookie, sizeof(ipsec_cookie_t)); ptr += sizeof(ipsec_cookie_t); bcopy(ptr, (char *)ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t)); /* * The responder cookie should only be non-zero if the initiator * cookie is non-zero. Therefore, it is safe to assume(!) that the * cookies are both set after copying if the responder is non-zero. */ if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0) ipsec->ipsc_rckset = 1; MUTEX_ENTER(&softn->ipf_nat_new); ipsec->ipsc_nat = ipf_nat_add(&fi, ipn, &ipsec->ipsc_nat, NAT_SLAVE|SI_WILDP, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (ipsec->ipsc_nat != NULL) { (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); ipf_nat_update(&fi, ipsec->ipsc_nat); MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); fi.fin_data[0] = 0; fi.fin_data[1] = 0; (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP); } ip->ip_p = p & 0xff; return 0; } /* * For outgoing IKE packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ int ipf_p_ipsec_inout(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipf_ipsec_softc_t *softi = arg; ipf_main_softc_t *softc = fin->fin_main_soft; ipsec_pxy_t *ipsec; fr_info_t fi; ip_t *ip; int p; if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) return 0; if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) return 0; ipsec = aps->aps_data; if (ipsec != NULL) { ip = fin->fin_ip; p = ip->ip_p; if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) { bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_fi.fi_p = IPPROTO_ESP; fi.fin_fr = &softi->ipsec_fr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; ip->ip_p = IPPROTO_ESP; fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); fi.fin_flx |= FI_IGNORE; } /* * Update NAT timeout/create NAT if missing. */ if (ipsec->ipsc_nat != NULL) ipf_queueback(softc->ipf_ticks, &ipsec->ipsc_nat->nat_tqe); else { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif MUTEX_ENTER(&softn->ipf_nat_new); ipsec->ipsc_nat = ipf_nat_add(&fi, ipsec->ipsc_rule, &ipsec->ipsc_nat, NAT_SLAVE|SI_WILDP, nat->nat_dir); MUTEX_EXIT(&softn->ipf_nat_new); if (ipsec->ipsc_nat != NULL) { (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); ipf_nat_update(&fi, ipsec->ipsc_nat); MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); } } /* * Update state timeout/create state if missing. */ READ_ENTER(&softc->ipf_state); if (ipsec->ipsc_state != NULL) { ipf_queueback(softc->ipf_ticks, &ipsec->ipsc_state->is_sti); ipsec->ipsc_state->is_die = nat->nat_age; RWLOCK_EXIT(&softc->ipf_state); } else { RWLOCK_EXIT(&softc->ipf_state); fi.fin_data[0] = 0; fi.fin_data[1] = 0; (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP); } ip->ip_p = p; } return 0; } /* * This extends the NAT matching to be based on the cookies associated with * a session and found at the front of IKE packets. The cookies are always * in the same order (not reversed depending on packet flow direction as with * UDP/TCP port numbers). */ int ipf_p_ipsec_match(fin, aps, nat) fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipsec_pxy_t *ipsec; u_32_t cookies[4]; mb_t *m; int off; nat = nat; /* LINT */ if ((fin->fin_dlen < sizeof(cookies)) || (fin->fin_flx & FI_FRAG)) return -1; off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; ipsec = aps->aps_data; m = fin->fin_m; COPYDATA(m, off, sizeof(cookies), (char *)cookies); if ((cookies[0] != ipsec->ipsc_icookie[0]) || (cookies[1] != ipsec->ipsc_icookie[1])) return -1; if (ipsec->ipsc_rckset == 0) { if ((cookies[2]|cookies[3]) == 0) { return 0; } ipsec->ipsc_rckset = 1; ipsec->ipsc_rcookie[0] = cookies[2]; ipsec->ipsc_rcookie[1] = cookies[3]; return 0; } if ((cookies[2] != ipsec->ipsc_rcookie[0]) || (cookies[3] != ipsec->ipsc_rcookie[1])) return -1; return 0; } /* * clean up after ourselves. */ void ipf_p_ipsec_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { ipsec_pxy_t *ipsec; ipsec = aps->aps_data; if (ipsec != NULL) { /* * Don't bother changing any of the NAT structure details, * *_del() is on a callback from aps_free(), from nat_delete() */ READ_ENTER(&softc->ipf_state); if (ipsec->ipsc_state != NULL) { ipsec->ipsc_state->is_die = softc->ipf_ticks + 1; ipsec->ipsc_state->is_me = NULL; ipf_queuefront(&ipsec->ipsc_state->is_sti); } RWLOCK_EXIT(&softc->ipf_state); ipsec->ipsc_state = NULL; ipsec->ipsc_nat = NULL; ipsec->ipsc_rule->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &ipsec->ipsc_rule); } } diff --git a/sys/contrib/ipfilter/netinet/ip_irc_pxy.c b/sys/contrib/ipfilter/netinet/ip_irc_pxy.c index 1b788720f3f7..304fe0c35351 100644 --- a/sys/contrib/ipfilter/netinet/ip_irc_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_irc_pxy.c @@ -1,446 +1,446 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ */ #define IPF_IRC_PROXY #define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ -void ipf_p_irc_main_load __P((void)); -void ipf_p_irc_main_unload __P((void)); -int ipf_p_irc_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_irc_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_irc_send __P((fr_info_t *, nat_t *)); -int ipf_p_irc_complete __P((ircinfo_t *, char *, size_t)); -u_short ipf_irc_atoi __P((char **)); +void ipf_p_irc_main_load(void); +void ipf_p_irc_main_unload(void); +int ipf_p_irc_new(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_irc_out(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_irc_send(fr_info_t *, nat_t *); +int ipf_p_irc_complete(ircinfo_t *, char *, size_t); +u_short ipf_irc_atoi(char **); static frentry_t ircnatfr; int irc_proxy_init = 0; /* * Initialize local structures. */ void ipf_p_irc_main_load() { bzero((char *)&ircnatfr, sizeof(ircnatfr)); ircnatfr.fr_ref = 1; ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock"); irc_proxy_init = 1; } void ipf_p_irc_main_unload() { if (irc_proxy_init == 1) { MUTEX_DESTROY(&ircnatfr.fr_lock); irc_proxy_init = 0; } } const char *ipf_p_irc_dcctypes[] = { "CHAT ", /* CHAT chat ipnumber portnumber */ "SEND ", /* SEND filename ipnumber portnumber */ "MOVE ", "TSEND ", "SCHAT ", NULL, }; /* * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n */ int ipf_p_irc_complete(ircp, buf, len) ircinfo_t *ircp; char *buf; size_t len; { register char *s, c; register size_t i; u_32_t l; int j, k; ircp->irc_ipnum = 0; ircp->irc_port = 0; if (len < 31) return 0; s = buf; c = *s++; i = len - 1; if ((c != ':') && (c != 'P')) return 0; if (c == ':') { /* * Loosely check that the source is a nickname of some sort */ s++; c = *s; ircp->irc_snick = s; if (!ISALPHA(c)) return 0; i--; for (c = *s; !ISSPACE(c) && (i > 0); i--) c = *s++; if (i < 31) return 0; if (c != 'P') return 0; } else ircp->irc_snick = NULL; /* * Check command string */ if (strncmp(s, "PRIVMSG ", 8)) return 0; i -= 8; s += 8; c = *s; ircp->irc_dnick = s; /* * Loosely check that the destination is a nickname of some sort */ if (!ISALPHA(c)) return 0; for (; !ISSPACE(c) && (i > 0); i--) c = *s++; if (i < 20) return 0; s++, i--; /* * Look for a ^A to start the DCC */ c = *s; if (c == ':') { s++; c = *s; } if (strncmp(s, "\001DCC ", 4)) return 0; i -= 4; s += 4; /* * Check for a recognised DCC command */ for (j = 0, k = 0; ipf_p_irc_dcctypes[j]; j++) { k = MIN(strlen(ipf_p_irc_dcctypes[j]), i); if (!strncmp(ipf_p_irc_dcctypes[j], s, k)) break; } if (!ipf_p_irc_dcctypes[j]) return 0; ircp->irc_type = s; i -= k; s += k; if (i < 11) return 0; /* * Check for the arg */ c = *s; if (ISSPACE(c)) return 0; ircp->irc_arg = s; for (; (c != ' ') && (c != '\001') && (i > 0); i--) c = *s++; if (c == '\001') /* In reality a ^A can quote another ^A...*/ return 0; if (i < 5) return 0; s++; i--; c = *s; if (!ISDIGIT(c)) return 0; ircp->irc_addr = s; /* * Get the IP# */ for (l = 0; ISDIGIT(c) && (i > 0); i--) { l *= 10; l += c - '0'; c = *s++; } if (i < 4) return 0; if (c != ' ') return 0; ircp->irc_ipnum = l; s++; i--; c = *s; if (!ISDIGIT(c)) return 0; /* * Get the port# */ for (l = 0; ISDIGIT(c) && (i > 0); i--) { l *= 10; l += c - '0'; c = *s++; } if (i < 3) return 0; if (strncmp(s, "\001\r\n", 3)) return 0; s += 3; ircp->irc_len = s - buf; ircp->irc_port = l; return 1; } int ipf_p_irc_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ircinfo_t *irc; if (fin->fin_v != 4) return -1; KMALLOC(irc, ircinfo_t *); if (irc == NULL) return -1; nat = nat; /* LINT */ aps->aps_data = irc; aps->aps_psiz = sizeof(ircinfo_t); bzero((char *)irc, sizeof(*irc)); return 0; } int ipf_p_irc_send(fin, nat) fr_info_t *fin; nat_t *nat; { char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; tcphdr_t *tcp, tcph, *tcp2 = &tcph; int off, inc = 0, i, dlen; ipf_main_softc_t *softc; size_t nlen = 0, olen; struct in_addr swip; u_short a5, sp; ircinfo_t *irc; fr_info_t fi; nat_t *nat2; u_int a1; ip_t *ip; mb_t *m; #ifdef MENTAT mb_t *m1; #endif softc = fin->fin_main_soft; m = fin->fin_m; ip = fin->fin_ip; tcp = (tcphdr_t *)fin->fin_dp; bzero(ctcpbuf, sizeof(ctcpbuf)); off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; dlen = MSGDSIZE(m) - off; if (dlen <= 0) return 0; COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf); if (dlen <= 0) return 0; ctcpbuf[sizeof(ctcpbuf) - 1] = '\0'; *newbuf = '\0'; irc = nat->nat_aps->aps_data; if (ipf_p_irc_complete(irc, ctcpbuf, dlen) == 0) return 0; /* * check that IP address in the DCC reply is the same as the * sender of the command - prevents use for port scanning. */ if (irc->irc_ipnum != ntohl(nat->nat_osrcaddr)) return 0; a5 = irc->irc_port; /* * Calculate new address parts for the DCC command */ a1 = ntohl(ip->ip_src.s_addr); olen = irc->irc_len; i = irc->irc_addr - ctcpbuf; i++; (void) strncpy(newbuf, ctcpbuf, i); /* DO NOT change these! */ #if defined(SNPRINTF) && defined(KERNEL) SNPRINTF(newbuf, sizeof(newbuf) - i, "%u %u\001\r\n", a1, a5); #else (void) sprintf(newbuf, "%u %u\001\r\n", a1, a5); #endif nlen = strlen(newbuf); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) return 0; #ifdef MENTAT for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { mblk_t *nm; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); PANIC((!nm),("ipf_p_irc_out: allocb failed")); nm->b_band = m1->b_band; nm->b_wptr += nlen; m1->b_wptr -= olen; PANIC((m1->b_wptr < m1->b_rptr), ("ipf_p_irc_out: cannot handle fragmented data block")); linkb(m1, nm); } else { # if SOLARIS && defined(ICK_VALID) if (m1->b_datap->db_struiolim == m1->b_wptr) m1->b_datap->db_struiolim += inc; m1->b_datap->db_struioflag &= ~STRUIO_IP; # endif m1->b_wptr += inc; } #else if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ #endif COPYBACK(m, off, nlen, newbuf); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { #if defined(MENTAT) register u_32_t sum1, sum2; sum1 = fin->fin_plen; sum2 = fin->fin_plen + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) sum2--; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); ipf_fix_outcksum(0, &ip->ip_sum, sum2, 0); #endif fin->fin_plen += inc; ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += inc; } /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = htons(a5); /* * Don't allow the PORT command to specify a port < 1024 due to * security crap. */ if (ntohs(sp) < 1024) return 0; /* * The server may not make the connection back from port 20, but * it is the most likely so use it here to check for a conflicting * mapping. */ bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1]; nat2 = ipf_nat_outlookup(fin, IPN_TCP, nat->nat_pr[1], nat->nat_nsrcip, ip->ip_dst); if (nat2 == NULL) { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; fi.fin_fr = &ircnatfr; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); swip = ip->ip_src; ip->ip_src = nat->nat_nsrcip; MUTEX_ENTER(&softn->ipf_nat_new); nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, 0); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); } ip->ip_src = swip; } return inc; } int ipf_p_irc_out(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { aps = aps; /* LINT */ return ipf_p_irc_send(fin, nat); } diff --git a/sys/contrib/ipfilter/netinet/ip_lookup.c b/sys/contrib/ipfilter/netinet/ip_lookup.c index 046939146075..f25174a752ec 100644 --- a/sys/contrib/ipfilter/netinet/ip_lookup.c +++ b/sys/contrib/ipfilter/netinet/ip_lookup.c @@ -1,998 +1,998 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if defined(__FreeBSD_version) && defined(_KERNEL) # include # include #else # include #endif #if !defined(_KERNEL) # include # include # include # define _KERNEL # include # undef _KERNEL #endif #include #include #if defined(__FreeBSD__) # include # include #endif #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #else # include "ipf.h" #endif #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #include "netinet/ip_dstlist.h" /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif /* * In this file, ip_pool.c, ip_htable.c and ip_dstlist.c, you will find the * range for unit is [-1,IPL_LOGMAX]. The -1 is considered to be a valid number * and represents a "wildcard" or "all" units (IPL_LOGALL). The reason for not * starting the numbering at 0 is because the numbers [0,IPL_LOGMAX] correspond * to the minor device number for their respective device. Thus where there is * array indexing on the unit, +1 is used to map [-1.IPL_LOGMAX] to * [0.POOL_LOOKUP_MAX]. */ -static int ipf_lookup_addnode __P((ipf_main_softc_t *, caddr_t, int)); -static int ipf_lookup_delnode __P((ipf_main_softc_t *, caddr_t, int)); -static int ipf_lookup_addtable __P((ipf_main_softc_t *, caddr_t)); -static int ipf_lookup_deltable __P((ipf_main_softc_t *, caddr_t)); -static int ipf_lookup_stats __P((ipf_main_softc_t *, caddr_t)); -static int ipf_lookup_flush __P((ipf_main_softc_t *, caddr_t)); -static int ipf_lookup_iterate __P((ipf_main_softc_t *, void *, int, void *)); -static int ipf_lookup_deltok __P((ipf_main_softc_t *, void *, int, void *)); +static int ipf_lookup_addnode(ipf_main_softc_t *, caddr_t, int); +static int ipf_lookup_delnode(ipf_main_softc_t *, caddr_t, int); +static int ipf_lookup_addtable(ipf_main_softc_t *, caddr_t); +static int ipf_lookup_deltable(ipf_main_softc_t *, caddr_t); +static int ipf_lookup_stats(ipf_main_softc_t *, caddr_t); +static int ipf_lookup_flush(ipf_main_softc_t *, caddr_t); +static int ipf_lookup_iterate(ipf_main_softc_t *, void *, int, void *); +static int ipf_lookup_deltok(ipf_main_softc_t *, void *, int, void *); #define MAX_BACKENDS 3 static ipf_lookup_t *backends[MAX_BACKENDS] = { &ipf_pool_backend, &ipf_htable_backend, &ipf_dstlist_backend }; typedef struct ipf_lookup_softc_s { void *ipf_back[MAX_BACKENDS]; } ipf_lookup_softc_t; /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise all of the subcomponents of the lookup infrstructure. */ /* ------------------------------------------------------------------------ */ void * ipf_lookup_soft_create(softc) ipf_main_softc_t *softc; { ipf_lookup_softc_t *softl; ipf_lookup_t **l; int i; KMALLOC(softl, ipf_lookup_softc_t *); if (softl == NULL) return NULL; bzero((char *)softl, sizeof(*softl)); for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { softl->ipf_back[i] = (*(*l)->ipfl_create)(softc); if (softl->ipf_back[i] == NULL) { ipf_lookup_soft_destroy(softc, softl); return NULL; } } return softl; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise all of the subcomponents of the lookup infrstructure. */ /* ------------------------------------------------------------------------ */ int ipf_lookup_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; int err = 0; int i; for (i = 0; i < MAX_BACKENDS; i++) { err = (*backends[i]->ipfl_init)(softc, softl->ipf_back[i]); if (err != 0) break; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_soft_fini */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Call the fini function in each backend to cleanup all allocated data. */ /* ------------------------------------------------------------------------ */ int ipf_lookup_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; int i; for (i = 0; i < MAX_BACKENDS; i++) { if (softl->ipf_back[i] != NULL) (*backends[i]->ipfl_fini)(softc, softl->ipf_back[i]); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Step through each of the backends and call their expire functions, */ /* allowing them to delete any lifetime limited data. */ /* ------------------------------------------------------------------------ */ void ipf_lookup_expire(softc) ipf_main_softc_t *softc; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; int i; WRITE_ENTER(&softc->ipf_poolrw); for (i = 0; i < MAX_BACKENDS; i++) (*backends[i]->ipfl_expire)(softc, softl->ipf_back[i]); RWLOCK_EXIT(&softc->ipf_poolrw); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_softc_destroy */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Free up all pool related memory that has been allocated whilst IPFilter */ /* has been running. Also, do any other deinitialisation required such */ /* ipf_lookup_init() can be called again, safely. */ /* ------------------------------------------------------------------------ */ void ipf_lookup_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; int i; for (i = 0; i < MAX_BACKENDS; i++) { if (softl->ipf_back[i] != NULL) (*backends[i]->ipfl_destroy)(softc, softl->ipf_back[i]); } KFREE(softl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_ioctl */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* data(IO) - pointer to ioctl data to be copied to/from user */ /* space. */ /* cmd(I) - ioctl command number */ /* mode(I) - file mode bits used with open */ /* uid(I) - uid of process doing ioctl */ /* ctx(I) - pointer that represents context for uid */ /* */ /* Handle ioctl commands sent to the ioctl device. For the most part, this */ /* involves just calling another function to handle the specifics of each */ /* command. */ /* ------------------------------------------------------------------------ */ int ipf_lookup_ioctl(softc, data, cmd, mode, uid, ctx) ipf_main_softc_t *softc; caddr_t data; ioctlcmd_t cmd; int mode, uid; void *ctx; { int err; SPL_INT(s); mode = mode; /* LINT */ SPL_NET(s); switch (cmd) { case SIOCLOOKUPADDNODE : case SIOCLOOKUPADDNODEW : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_addnode(softc, data, uid); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPDELNODE : case SIOCLOOKUPDELNODEW : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_delnode(softc, data, uid); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPADDTABLE : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_addtable(softc, data); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPDELTABLE : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_deltable(softc, data); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPSTAT : case SIOCLOOKUPSTATW : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_stats(softc, data); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPFLUSH : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_flush(softc, data); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPITER : err = ipf_lookup_iterate(softc, data, uid, ctx); break; case SIOCIPFDELTOK : err = ipf_lookup_deltok(softc, data, uid, ctx); break; default : IPFERROR(50001); err = EINVAL; break; } SPL_X(s); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_addnode */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Add a new data node to a lookup structure. First, check to see if the */ /* parent structure refered to by name exists and if it does, then go on to */ /* add a node to it. */ /* ------------------------------------------------------------------------ */ static int ipf_lookup_addnode(softc, data, uid) ipf_main_softc_t *softc; caddr_t data; int uid; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err; int i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50002); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50003); return EINVAL; } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_node_add)(softc, softl->ipf_back[i], &op, uid); break; } } if (i == MAX_BACKENDS) { IPFERROR(50012); err = EINVAL; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_delnode */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Delete a node from a lookup table by first looking for the table it is */ /* in and then deleting the entry that gets found. */ /* ------------------------------------------------------------------------ */ static int ipf_lookup_delnode(softc, data, uid) ipf_main_softc_t *softc; caddr_t data; int uid; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err; int i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50042); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50013); return EINVAL; } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_node_del)(softc, softl->ipf_back[i], &op, uid); break; } } if (i == MAX_BACKENDS) { IPFERROR(50021); err = EINVAL; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_addtable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Create a new lookup table, if one doesn't already exist using the name */ /* for this one. */ /* ------------------------------------------------------------------------ */ static int ipf_lookup_addtable(softc, data) ipf_main_softc_t *softc; caddr_t data; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err, i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50022); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50023); return EINVAL; } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_table_add)(softc, softl->ipf_back[i], &op); break; } } if (i == MAX_BACKENDS) { IPFERROR(50026); err = EINVAL; } /* * For anonymous pools, copy back the operation struct because in the * case of success it will contain the new table's name. */ if ((err == 0) && ((op.iplo_arg & LOOKUP_ANON) != 0)) { err = BCOPYOUT(&op, data, sizeof(op)); if (err != 0) { IPFERROR(50027); err = EFAULT; } } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_deltable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Decodes ioctl request to remove a particular hash table or pool and */ /* calls the relevant function to do the cleanup. */ /* ------------------------------------------------------------------------ */ static int ipf_lookup_deltable(softc, data) ipf_main_softc_t *softc; caddr_t data; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err, i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50028); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50029); return EINVAL; } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_table_del)(softc, softl->ipf_back[i], &op); break; } } if (i == MAX_BACKENDS) { IPFERROR(50030); err = EINVAL; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_stats */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Copy statistical information from inside the kernel back to user space. */ /* ------------------------------------------------------------------------ */ static int ipf_lookup_stats(softc, data) ipf_main_softc_t *softc; caddr_t data; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err; int i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50031); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50032); return EINVAL; } for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_stats_get)(softc, softl->ipf_back[i], &op); break; } } if (i == MAX_BACKENDS) { IPFERROR(50033); err = EINVAL; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_flush */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* A flush is called when we want to flush all the nodes from a particular */ /* entry in the hash table/pool or want to remove all groups from those. */ /* ------------------------------------------------------------------------ */ static int ipf_lookup_flush(softc, data) ipf_main_softc_t *softc; caddr_t data; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; int err, unit, num, type, i; iplookupflush_t flush; ipf_lookup_t **l; err = BCOPYIN(data, &flush, sizeof(flush)); if (err != 0) { IPFERROR(50034); return EFAULT; } unit = flush.iplf_unit; if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL)) { IPFERROR(50035); return EINVAL; } flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0'; type = flush.iplf_type; IPFERROR(50036); err = EINVAL; num = 0; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (type == (*l)->ipfl_type || type == IPLT_ALL) { err = 0; num += (*(*l)->ipfl_flush)(softc, softl->ipf_back[i], &flush); } } if (err == 0) { flush.iplf_count = num; err = BCOPYOUT(&flush, data, sizeof(flush)); if (err != 0) { IPFERROR(50037); err = EFAULT; } } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_delref */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* type(I) - table type to operate on */ /* ptr(I) - pointer to object to remove reference for */ /* */ /* This function organises calling the correct deref function for a given */ /* type of object being passed into it. */ /* ------------------------------------------------------------------------ */ void ipf_lookup_deref(softc, type, ptr) ipf_main_softc_t *softc; int type; void *ptr; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; int i; if (ptr == NULL) return; for (i = 0; i < MAX_BACKENDS; i++) { if (type == backends[i]->ipfl_type) { WRITE_ENTER(&softc->ipf_poolrw); (*backends[i]->ipfl_table_deref)(softc, softl->ipf_back[i], ptr); RWLOCK_EXIT(&softc->ipf_poolrw); break; } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_iterate */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* uid(I) - uid of caller */ /* ctx(I) - pointer to give the uid context */ /* */ /* Decodes ioctl request to step through either hash tables or pools. */ /* ------------------------------------------------------------------------ */ static int ipf_lookup_iterate(softc, data, uid, ctx) ipf_main_softc_t *softc; void *data; int uid; void *ctx; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipflookupiter_t iter; ipftoken_t *token; int err, i; SPL_INT(s); err = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_LOOKUPITER); if (err != 0) return err; if (iter.ili_unit < IPL_LOGALL && iter.ili_unit > IPL_LOGMAX) { IPFERROR(50038); return EINVAL; } if (iter.ili_ival != IPFGENITER_LOOKUP) { IPFERROR(50039); return EINVAL; } SPL_SCHED(s); token = ipf_token_find(softc, iter.ili_key, uid, ctx); if (token == NULL) { SPL_X(s); IPFERROR(50040); return ESRCH; } for (i = 0; i < MAX_BACKENDS; i++) { if (iter.ili_type == backends[i]->ipfl_type) { err = (*backends[i]->ipfl_iter_next)(softc, softl->ipf_back[i], token, &iter); break; } } SPL_X(s); if (i == MAX_BACKENDS) { IPFERROR(50041); err = EINVAL; } WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_iterderef */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* type(I) - backend type to iterate through */ /* data(I) - pointer to data from ioctl call */ /* */ /* Decodes ioctl request to remove a particular hash table or pool and */ /* calls the relevant function to do the cleanup. */ /* Because each of the backend types has a different data structure, */ /* iteration is limited to one type at a time (i.e. it is not permitted to */ /* go on from pool types to hash types as part of the "get next".) */ /* ------------------------------------------------------------------------ */ void ipf_lookup_iterderef(softc, type, data) ipf_main_softc_t *softc; u_32_t type; void *data; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; struct iplookupiterkey *lkey; iplookupiterkey_t key; int i; key.ilik_key = type; lkey = &key.ilik_unstr; if (lkey->ilik_ival != IPFGENITER_LOOKUP) return; WRITE_ENTER(&softc->ipf_poolrw); for (i = 0; i < MAX_BACKENDS; i++) { if (lkey->ilik_type == backends[i]->ipfl_type) { (*backends[i]->ipfl_iter_deref)(softc, softl->ipf_back[i], lkey->ilik_otype, lkey->ilik_unit, data); break; } } RWLOCK_EXIT(&softc->ipf_poolrw); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_deltok */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* uid(I) - uid of caller */ /* ctx(I) - pointer to give the uid context */ /* */ /* Deletes the token identified by the combination of (type,uid,ctx) */ /* "key" is a combination of the table type, iterator type and the unit for */ /* which the token was being used. */ /* ------------------------------------------------------------------------ */ int ipf_lookup_deltok(softc, data, uid, ctx) ipf_main_softc_t *softc; void *data; int uid; void *ctx; { int error, key; SPL_INT(s); SPL_SCHED(s); error = BCOPYIN(data, &key, sizeof(key)); if (error == 0) error = ipf_token_del(softc, key, uid, ctx); SPL_X(s); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_res_num */ /* Returns: void * - NULL = failure, else success. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which this is for */ /* type(I) - type of lookup these parameters are for. */ /* number(I) - table number to use when searching */ /* funcptr(IO) - pointer to pointer for storing IP address */ /* searching function. */ /* */ /* Search for the "table" number passed in amongst those configured for */ /* that particular type. If the type is recognised then the function to */ /* call to do the IP address search will be change, regardless of whether */ /* or not the "table" number exists. */ /* ------------------------------------------------------------------------ */ void * ipf_lookup_res_num(softc, unit, type, number, funcptr) ipf_main_softc_t *softc; int unit; u_int type; u_int number; lookupfunc_t *funcptr; { char name[FR_GROUPLEN]; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(name, sizeof(name), "%u", number); #else (void) sprintf(name, "%u", number); #endif return ipf_lookup_res_name(softc, unit, type, name, funcptr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_res_name */ /* Returns: void * - NULL = failure, else success. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which this is for */ /* type(I) - type of lookup these parameters are for. */ /* name(I) - table name to use when searching */ /* funcptr(IO) - pointer to pointer for storing IP address */ /* searching function. */ /* */ /* Search for the "table" number passed in amongst those configured for */ /* that particular type. If the type is recognised then the function to */ /* call to do the IP address search will be changed, regardless of whether */ /* or not the "table" number exists. */ /* ------------------------------------------------------------------------ */ void * ipf_lookup_res_name(softc, unit, type, name, funcptr) ipf_main_softc_t *softc; int unit; u_int type; char *name; lookupfunc_t *funcptr; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipf_lookup_t **l; void *ptr = NULL; int i; READ_ENTER(&softc->ipf_poolrw); for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (type == (*l)->ipfl_type) { ptr = (*(*l)->ipfl_select_add_ref)(softl->ipf_back[i], unit, name); if (ptr != NULL && funcptr != NULL) { *funcptr = (*l)->ipfl_addr_find; } break; } } if (i == MAX_BACKENDS) { ptr = NULL; if (funcptr != NULL) *funcptr = NULL; } RWLOCK_EXIT(&softc->ipf_poolrw); return ptr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_find_htable */ /* Returns: void * - NULL = failure, else success. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which this is for */ /* name(I) - table name to use when searching */ /* */ /* To support the group-map feature, where a hash table maps address */ /* networks to rule group numbers, we need to expose a function that uses */ /* only the hash table backend. */ /* ------------------------------------------------------------------------ */ void * ipf_lookup_find_htable(softc, unit, name) ipf_main_softc_t *softc; int unit; char *name; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipf_lookup_t **l; void *tab = NULL; int i; READ_ENTER(&softc->ipf_poolrw); for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) if (IPLT_HASH == (*l)->ipfl_type) { tab = ipf_htable_find(softl->ipf_back[i], unit, name); break; } RWLOCK_EXIT(&softc->ipf_poolrw); return tab; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_sync */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* This function is the interface that the machine dependent sync functions */ /* call when a network interface name change occurs. It then calls the sync */ /* functions of the lookup implementations - if they have one. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ void ipf_lookup_sync(softc, ifp) ipf_main_softc_t *softc; void *ifp; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipf_lookup_t **l; int i; READ_ENTER(&softc->ipf_poolrw); for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) if ((*l)->ipfl_sync != NULL) (*(*l)->ipfl_sync)(softc, softl->ipf_back[i]); RWLOCK_EXIT(&softc->ipf_poolrw); } #ifndef _KERNEL void ipf_lookup_dump(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipf_lookup_t **l; int i; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) if (IPLT_POOL == (*l)->ipfl_type) { ipf_pool_dump(softc, softl->ipf_back[i]); break; } for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) if (IPLT_HASH == (*l)->ipfl_type) { ipf_htable_dump(softc, softl->ipf_back[i]); break; } } #endif diff --git a/sys/contrib/ipfilter/netinet/ip_lookup.h b/sys/contrib/ipfilter/netinet/ip_lookup.h index 6b1642a91ec0..72dfbdb37c2a 100644 --- a/sys/contrib/ipfilter/netinet/ip_lookup.h +++ b/sys/contrib/ipfilter/netinet/ip_lookup.h @@ -1,140 +1,140 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ */ #ifndef __IP_LOOKUP_H__ #define __IP_LOOKUP_H__ # define SIOCLOOKUPADDTABLE _IOWR('r', 60, struct iplookupop) # define SIOCLOOKUPDELTABLE _IOWR('r', 61, struct iplookupop) # define SIOCLOOKUPSTAT _IOWR('r', 64, struct iplookupop) # define SIOCLOOKUPSTATW _IOW('r', 64, struct iplookupop) # define SIOCLOOKUPFLUSH _IOWR('r', 65, struct iplookupflush) # define SIOCLOOKUPADDNODE _IOWR('r', 67, struct iplookupop) # define SIOCLOOKUPADDNODEW _IOW('r', 67, struct iplookupop) # define SIOCLOOKUPDELNODE _IOWR('r', 68, struct iplookupop) # define SIOCLOOKUPDELNODEW _IOW('r', 68, struct iplookupop) #define LOOKUP_POOL_MAX (IPL_LOGSIZE) #define LOOKUP_POOL_SZ (IPL_LOGSIZE + 1) typedef struct iplookupop { int iplo_type; /* IPLT_* */ int iplo_unit; /* IPL_LOG* */ u_int iplo_arg; char iplo_name[FR_GROUPLEN]; size_t iplo_size; /* sizeof struct at iplo_struct */ void *iplo_struct; } iplookupop_t; #define LOOKUP_ANON 0x80000000 typedef struct iplookupflush { int iplf_type; /* IPLT_* */ int iplf_unit; /* IPL_LOG* */ u_int iplf_arg; u_int iplf_count; char iplf_name[FR_GROUPLEN]; } iplookupflush_t; typedef struct iplookuplink { int ipll_type; /* IPLT_* */ int ipll_unit; /* IPL_LOG* */ u_int ipll_num; char ipll_group[FR_GROUPLEN]; } iplookuplink_t; #define IPLT_ALL -1 #define IPLT_NONE 0 #define IPLT_POOL 1 #define IPLT_HASH 2 #define IPLT_DSTLIST 3 #define IPLT_ANON 0x80000000 typedef union { struct iplookupiterkey { u_char ilik_ival; u_char ilik_type; /* IPLT_* */ u_char ilik_otype; signed char ilik_unit; /* IPL_LOG* */ } ilik_unstr; u_32_t ilik_key; } iplookupiterkey_t; typedef struct ipflookupiter { int ili_nitems; iplookupiterkey_t ili_lkey; char ili_name[FR_GROUPLEN]; void *ili_data; } ipflookupiter_t; #define ili_key ili_lkey.ilik_key #define ili_ival ili_lkey.ilik_unstr.ilik_ival #define ili_unit ili_lkey.ilik_unstr.ilik_unit #define ili_type ili_lkey.ilik_unstr.ilik_type #define ili_otype ili_lkey.ilik_unstr.ilik_otype #define IPFLOOKUPITER_LIST 0 #define IPFLOOKUPITER_NODE 1 typedef struct ipf_lookup { int ipfl_type; - void *(*ipfl_create) __P((ipf_main_softc_t *)); - void (*ipfl_destroy) __P((ipf_main_softc_t *, void *)); - int (*ipfl_init) __P((ipf_main_softc_t *, void *)); - void (*ipfl_fini) __P((ipf_main_softc_t *, void *)); - int (*ipfl_addr_find) __P((ipf_main_softc_t *, void *, - int, void *, u_int)); - size_t (*ipfl_flush) __P((ipf_main_softc_t *, void *, - iplookupflush_t *)); - int (*ipfl_iter_deref) __P((ipf_main_softc_t *, void *, - int, int, void *)); - int (*ipfl_iter_next) __P((ipf_main_softc_t *, void *, - ipftoken_t *, ipflookupiter_t *)); - int (*ipfl_node_add) __P((ipf_main_softc_t *, void *, - iplookupop_t *, int)); - int (*ipfl_node_del) __P((ipf_main_softc_t *, void *, - iplookupop_t *, int)); - int (*ipfl_stats_get) __P((ipf_main_softc_t *, void *, - iplookupop_t *)); - int (*ipfl_table_add) __P((ipf_main_softc_t *, void *, - iplookupop_t *)); - int (*ipfl_table_del) __P((ipf_main_softc_t *, void *, - iplookupop_t *)); - int (*ipfl_table_deref) __P((ipf_main_softc_t *, void *, void *)); - void *(*ipfl_table_find) __P((void *, int, char *)); - void *(*ipfl_select_add_ref) __P((void *, int, char *)); - int (*ipfl_select_node) __P((fr_info_t *, void *, u_32_t *, - frdest_t *)); - void (*ipfl_expire) __P((ipf_main_softc_t *, void *)); - void (*ipfl_sync) __P((ipf_main_softc_t *, void *)); + void *(*ipfl_create)(ipf_main_softc_t *); + void (*ipfl_destroy)(ipf_main_softc_t *, void *); + int (*ipfl_init)(ipf_main_softc_t *, void *); + void (*ipfl_fini)(ipf_main_softc_t *, void *); + int (*ipfl_addr_find)(ipf_main_softc_t *, void *, + int, void *, u_int); + size_t (*ipfl_flush)(ipf_main_softc_t *, void *, + iplookupflush_t *); + int (*ipfl_iter_deref)(ipf_main_softc_t *, void *, + int, int, void *); + int (*ipfl_iter_next)(ipf_main_softc_t *, void *, + ipftoken_t *, ipflookupiter_t *); + int (*ipfl_node_add)(ipf_main_softc_t *, void *, + iplookupop_t *, int); + int (*ipfl_node_del)(ipf_main_softc_t *, void *, + iplookupop_t *, int); + int (*ipfl_stats_get)(ipf_main_softc_t *, void *, + iplookupop_t *); + int (*ipfl_table_add)(ipf_main_softc_t *, void *, + iplookupop_t *); + int (*ipfl_table_del)(ipf_main_softc_t *, void *, + iplookupop_t *); + int (*ipfl_table_deref)(ipf_main_softc_t *, void *, void *); + void *(*ipfl_table_find)(void *, int, char *); + void *(*ipfl_select_add_ref)(void *, int, char *); + int (*ipfl_select_node)(fr_info_t *, void *, u_32_t *, + frdest_t *); + void (*ipfl_expire)(ipf_main_softc_t *, void *); + void (*ipfl_sync)(ipf_main_softc_t *, void *); } ipf_lookup_t; -extern int ipf_lookup_init __P((void)); -extern int ipf_lookup_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *)); -extern void ipf_lookup_main_unload __P((void)); -extern void ipf_lookup_deref __P((ipf_main_softc_t *, int, void *)); -extern void ipf_lookup_iterderef __P((ipf_main_softc_t *, u_32_t, void *)); -extern void *ipf_lookup_res_name __P((ipf_main_softc_t *, int, u_int, char *, - lookupfunc_t *)); -extern void *ipf_lookup_res_num __P((ipf_main_softc_t *, int, u_int, u_int, - lookupfunc_t *)); -extern void ipf_lookup_soft_destroy __P((ipf_main_softc_t *, void *)); -extern void *ipf_lookup_soft_create __P((ipf_main_softc_t *)); -extern int ipf_lookup_soft_init __P((ipf_main_softc_t *, void *)); -extern int ipf_lookup_soft_fini __P((ipf_main_softc_t *, void *)); -extern void *ipf_lookup_find_htable __P((ipf_main_softc_t *, int, char *)); -extern void ipf_lookup_expire __P((ipf_main_softc_t *)); -extern void ipf_lookup_sync __P((ipf_main_softc_t *, void *)); +extern int ipf_lookup_init(void); +extern int ipf_lookup_ioctl(ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *); +extern void ipf_lookup_main_unload(void); +extern void ipf_lookup_deref(ipf_main_softc_t *, int, void *); +extern void ipf_lookup_iterderef(ipf_main_softc_t *, u_32_t, void *); +extern void *ipf_lookup_res_name(ipf_main_softc_t *, int, u_int, char *, + lookupfunc_t *); +extern void *ipf_lookup_res_num(ipf_main_softc_t *, int, u_int, u_int, + lookupfunc_t *); +extern void ipf_lookup_soft_destroy(ipf_main_softc_t *, void *); +extern void *ipf_lookup_soft_create(ipf_main_softc_t *); +extern int ipf_lookup_soft_init(ipf_main_softc_t *, void *); +extern int ipf_lookup_soft_fini(ipf_main_softc_t *, void *); +extern void *ipf_lookup_find_htable(ipf_main_softc_t *, int, char *); +extern void ipf_lookup_expire(ipf_main_softc_t *); +extern void ipf_lookup_sync(ipf_main_softc_t *, void *); #ifndef _KERNEL -extern void ipf_lookup_dump __P((ipf_main_softc_t *, void *)); +extern void ipf_lookup_dump(ipf_main_softc_t *, void *); #endif #endif /* __IP_LOOKUP_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_nat.c b/sys/contrib/ipfilter/netinet/ip_nat.c index 1aa587bf67f6..3d272e1710df 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.c +++ b/sys/contrib/ipfilter/netinet/ip_nat.c @@ -1,8582 +1,8582 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if defined(_KERNEL) && \ (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000)) # include #endif #if !defined(_KERNEL) # include # include # include # define KERNEL # ifdef _OpenBSD__ struct file; # endif # include # undef KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD_version) # include # include #else # include #endif # include # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef KERNEL # include # endif # include # include #endif #if defined(__FreeBSD_version) # include #endif #include #if defined(__FreeBSD_version) # include #endif #ifdef sun # include #endif #include #include #include #ifdef RFC1825 # include # include extern struct ifnet vpnif; #endif # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ipl.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_lookup.h" #include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" #if defined(__FreeBSD_version) # include #endif #ifdef HAS_SYS_MD5_H # include #else # include "md5.h" #endif /* END OF INCLUDES */ #undef SOCKADDR_IN #define SOCKADDR_IN struct sockaddr_in #if !defined(lint) static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */ #endif #define NATFSUM(n,v,f) ((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \ (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3]) #define NBUMP(x) softn->(x)++ #define NBUMPD(x, y) do { \ softn->x.y++; \ DT(y); \ } while (0) #define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ #define NBUMPSIDED(y,x) do { softn->ipf_nat_stats.ns_side[y].x++; \ DT(x); } while (0) #define NBUMPSIDEX(y,x,z) \ do { softn->ipf_nat_stats.ns_side[y].x++; \ DT(z); } while (0) #define NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \ DT1(x, fr_info_t *, fin); } while (0) static ipftuneable_t ipf_nat_tuneables[] = { /* nat */ { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) }, "nat_lock", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_lock), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) }, "nat_table_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_table_sz), 0, NULL, ipf_nat_rehash }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) }, "nat_table_max", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_table_max), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) }, "nat_rules_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz), 0, NULL, ipf_nat_rehash_rules }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) }, "rdr_rules_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 0, NULL, ipf_nat_rehash_rules }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) }, "hostmap_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 0, NULL, ipf_nat_hostmap_rehash }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) }, "nat_maxbucket",1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) }, "nat_logging", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_logging), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) }, "nat_doflush", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_doflush), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) }, "nat_table_wm_low", 1, 99, stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) }, "nat_table_wm_high", 2, 100, stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high), 0, NULL, NULL }, { { 0 }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* ======================================================================== */ /* How the NAT is organised and works. */ /* */ /* Inside (interface y) NAT Outside (interface x) */ /* -------------------- -+- ------------------------------------- */ /* Packet going | out, processsed by ipf_nat_checkout() for x */ /* ------------> | ------------> */ /* src=10.1.1.1 | src=192.1.1.1 */ /* | */ /* | in, processed by ipf_nat_checkin() for x */ /* <------------ | <------------ */ /* dst=10.1.1.1 | dst=192.1.1.1 */ /* -------------------- -+- ------------------------------------- */ /* ipf_nat_checkout() - changes ip_src and if required, sport */ /* - creates a new mapping, if required. */ /* ipf_nat_checkin() - changes ip_dst and if required, dport */ /* */ /* In the NAT table, internal source is recorded as "in" and externally */ /* seen as "out". */ /* ======================================================================== */ #if SOLARIS && !defined(INSTANCES) extern int pfil_delayed_copy; #endif -static int ipf_nat_flush_entry __P((ipf_main_softc_t *, void *)); -static int ipf_nat_getent __P((ipf_main_softc_t *, caddr_t, int)); -static int ipf_nat_getsz __P((ipf_main_softc_t *, caddr_t, int)); -static int ipf_nat_putent __P((ipf_main_softc_t *, caddr_t, int)); -static void ipf_nat_addmap __P((ipf_nat_softc_t *, ipnat_t *)); -static void ipf_nat_addrdr __P((ipf_nat_softc_t *, ipnat_t *)); -static int ipf_nat_builddivertmp __P((ipf_nat_softc_t *, ipnat_t *)); -static int ipf_nat_clearlist __P((ipf_main_softc_t *, ipf_nat_softc_t *)); -static int ipf_nat_cmp_rules __P((ipnat_t *, ipnat_t *)); -static int ipf_nat_decap __P((fr_info_t *, nat_t *)); -static void ipf_nat_delrule __P((ipf_main_softc_t *, ipf_nat_softc_t *, - ipnat_t *, int)); -static int ipf_nat_extraflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, int)); -static int ipf_nat_finalise __P((fr_info_t *, nat_t *)); -static int ipf_nat_flushtable __P((ipf_main_softc_t *, ipf_nat_softc_t *)); -static int ipf_nat_getnext __P((ipf_main_softc_t *, ipftoken_t *, - ipfgeniter_t *, ipfobj_t *)); -static int ipf_nat_gettable __P((ipf_main_softc_t *, ipf_nat_softc_t *, - char *)); -static hostmap_t *ipf_nat_hostmap __P((ipf_nat_softc_t *, ipnat_t *, +static int ipf_nat_flush_entry(ipf_main_softc_t *, void *); +static int ipf_nat_getent(ipf_main_softc_t *, caddr_t, int); +static int ipf_nat_getsz(ipf_main_softc_t *, caddr_t, int); +static int ipf_nat_putent(ipf_main_softc_t *, caddr_t, int); +static void ipf_nat_addmap(ipf_nat_softc_t *, ipnat_t *); +static void ipf_nat_addrdr(ipf_nat_softc_t *, ipnat_t *); +static int ipf_nat_builddivertmp(ipf_nat_softc_t *, ipnat_t *); +static int ipf_nat_clearlist(ipf_main_softc_t *, ipf_nat_softc_t *); +static int ipf_nat_cmp_rules(ipnat_t *, ipnat_t *); +static int ipf_nat_decap(fr_info_t *, nat_t *); +static void ipf_nat_delrule(ipf_main_softc_t *, ipf_nat_softc_t *, + ipnat_t *, int); +static int ipf_nat_extraflush(ipf_main_softc_t *, ipf_nat_softc_t *, int); +static int ipf_nat_finalise(fr_info_t *, nat_t *); +static int ipf_nat_flushtable(ipf_main_softc_t *, ipf_nat_softc_t *); +static int ipf_nat_getnext(ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *, ipfobj_t *); +static int ipf_nat_gettable(ipf_main_softc_t *, ipf_nat_softc_t *, + char *); +static hostmap_t *ipf_nat_hostmap(ipf_nat_softc_t *, ipnat_t *, struct in_addr, struct in_addr, - struct in_addr, u_32_t)); -static int ipf_nat_icmpquerytype __P((int)); -static int ipf_nat_iterator __P((ipf_main_softc_t *, ipftoken_t *, - ipfgeniter_t *, ipfobj_t *)); -static int ipf_nat_match __P((fr_info_t *, ipnat_t *)); -static int ipf_nat_matcharray __P((nat_t *, int *, u_long)); -static int ipf_nat_matchflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, - caddr_t)); -static void ipf_nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, - u_short *)); -static int ipf_nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); -static int ipf_nat_newdivert __P((fr_info_t *, nat_t *, natinfo_t *)); -static int ipf_nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); -static int ipf_nat_newrewrite __P((fr_info_t *, nat_t *, natinfo_t *)); -static int ipf_nat_nextaddr __P((fr_info_t *, nat_addr_t *, u_32_t *, - u_32_t *)); -static int ipf_nat_nextaddrinit __P((ipf_main_softc_t *, char *, - nat_addr_t *, int, void *)); -static int ipf_nat_resolverule __P((ipf_main_softc_t *, ipnat_t *)); -static int ipf_nat_ruleaddrinit __P((ipf_main_softc_t *, - ipf_nat_softc_t *, ipnat_t *)); -static void ipf_nat_rule_fini __P((ipf_main_softc_t *, ipnat_t *)); -static int ipf_nat_rule_init __P((ipf_main_softc_t *, ipf_nat_softc_t *, - ipnat_t *)); -static int ipf_nat_siocaddnat __P((ipf_main_softc_t *, ipf_nat_softc_t *, - ipnat_t *, int)); -static void ipf_nat_siocdelnat __P((ipf_main_softc_t *, ipf_nat_softc_t *, - ipnat_t *, int)); -static void ipf_nat_tabmove __P((ipf_nat_softc_t *, nat_t *)); + struct in_addr, u_32_t); +static int ipf_nat_icmpquerytype(int); +static int ipf_nat_iterator(ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *, ipfobj_t *); +static int ipf_nat_match(fr_info_t *, ipnat_t *); +static int ipf_nat_matcharray(nat_t *, int *, u_long); +static int ipf_nat_matchflush(ipf_main_softc_t *, ipf_nat_softc_t *, + caddr_t); +static void ipf_nat_mssclamp(tcphdr_t *, u_32_t, fr_info_t *, + u_short *); +static int ipf_nat_newmap(fr_info_t *, nat_t *, natinfo_t *); +static int ipf_nat_newdivert(fr_info_t *, nat_t *, natinfo_t *); +static int ipf_nat_newrdr(fr_info_t *, nat_t *, natinfo_t *); +static int ipf_nat_newrewrite(fr_info_t *, nat_t *, natinfo_t *); +static int ipf_nat_nextaddr(fr_info_t *, nat_addr_t *, u_32_t *, + u_32_t *); +static int ipf_nat_nextaddrinit(ipf_main_softc_t *, char *, + nat_addr_t *, int, void *); +static int ipf_nat_resolverule(ipf_main_softc_t *, ipnat_t *); +static int ipf_nat_ruleaddrinit(ipf_main_softc_t *, + ipf_nat_softc_t *, ipnat_t *); +static void ipf_nat_rule_fini(ipf_main_softc_t *, ipnat_t *); +static int ipf_nat_rule_init(ipf_main_softc_t *, ipf_nat_softc_t *, + ipnat_t *); +static int ipf_nat_siocaddnat(ipf_main_softc_t *, ipf_nat_softc_t *, + ipnat_t *, int); +static void ipf_nat_siocdelnat(ipf_main_softc_t *, ipf_nat_softc_t *, + ipnat_t *, int); +static void ipf_nat_tabmove(ipf_nat_softc_t *, nat_t *); /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_main_load */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* The only global NAT structure that needs to be initialised is the filter */ /* rule that is used with blocking packets. */ /* ------------------------------------------------------------------------ */ int ipf_nat_main_load() { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_main_unload */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_nat_main_unload() { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_create */ /* Returns: void * - NULL = failure, else pointer to NAT context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocate the initial soft context structure for NAT and populate it with */ /* some default values. Creating the tables is left until we call _init so */ /* that sizes can be changed before we get under way. */ /* ------------------------------------------------------------------------ */ void * ipf_nat_soft_create(softc) ipf_main_softc_t *softc; { ipf_nat_softc_t *softn; KMALLOC(softn, ipf_nat_softc_t *); if (softn == NULL) return NULL; bzero((char *)softn, sizeof(*softn)); softn->ipf_nat_tune = ipf_tune_array_copy(softn, sizeof(ipf_nat_tuneables), ipf_nat_tuneables); if (softn->ipf_nat_tune == NULL) { ipf_nat_soft_destroy(softc, softn); return NULL; } if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) { ipf_nat_soft_destroy(softc, softn); return NULL; } softn->ipf_nat_list_tail = &softn->ipf_nat_list; softn->ipf_nat_table_max = NAT_TABLE_MAX; softn->ipf_nat_table_sz = NAT_TABLE_SZ; softn->ipf_nat_maprules_sz = NAT_SIZE; softn->ipf_nat_rdrrules_sz = RDR_SIZE; softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE; softn->ipf_nat_doflush = 0; #ifdef IPFILTER_LOG softn->ipf_nat_logging = 1; #else softn->ipf_nat_logging = 0; #endif softn->ipf_nat_defage = DEF_NAT_AGE; softn->ipf_nat_defipage = IPF_TTLVAL(60); softn->ipf_nat_deficmpage = IPF_TTLVAL(3); softn->ipf_nat_table_wm_high = 99; softn->ipf_nat_table_wm_low = 90; return softn; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* ------------------------------------------------------------------------ */ void ipf_nat_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_nat_softc_t *softn = arg; if (softn->ipf_nat_tune != NULL) { ipf_tune_array_unlink(softc, softn->ipf_nat_tune); KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables)); softn->ipf_nat_tune = NULL; } KFREE(softn); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_init */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise all of the NAT locks, tables and other structures. */ /* ------------------------------------------------------------------------ */ int ipf_nat_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_nat_softc_t *softn = arg; ipftq_t *tq; int i; KMALLOCS(softn->ipf_nat_table[0], nat_t **, \ sizeof(nat_t *) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[0] != NULL) { bzero((char *)softn->ipf_nat_table[0], softn->ipf_nat_table_sz * sizeof(nat_t *)); } else { return -1; } KMALLOCS(softn->ipf_nat_table[1], nat_t **, \ sizeof(nat_t *) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[1] != NULL) { bzero((char *)softn->ipf_nat_table[1], softn->ipf_nat_table_sz * sizeof(nat_t *)); } else { return -2; } KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \ sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); if (softn->ipf_nat_map_rules != NULL) { bzero((char *)softn->ipf_nat_map_rules, softn->ipf_nat_maprules_sz * sizeof(ipnat_t *)); } else { return -3; } KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \ sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); if (softn->ipf_nat_rdr_rules != NULL) { bzero((char *)softn->ipf_nat_rdr_rules, softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *)); } else { return -4; } KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \ sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); if (softn->ipf_hm_maptable != NULL) { bzero((char *)softn->ipf_hm_maptable, sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); } else { return -5; } softn->ipf_hm_maplist = NULL; KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) { return -6; } bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) { return -7; } bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_maxbucket == 0) { for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1) softn->ipf_nat_maxbucket++; softn->ipf_nat_maxbucket *= 2; } ipf_sttab_init(softc, softn->ipf_nat_tcptq); /* * Increase this because we may have "keep state" following this too * and packet storms can occur if this is removed too quickly. */ softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next = &softn->ipf_nat_udptq; IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage, "nat ipftq udp tab"); softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq; IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage, "nat ipftq udpack tab"); softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq; IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage, "nat icmp ipftq tab"); softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq; IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage, "nat icmpack ipftq tab"); softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq; IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage, "nat ip ipftq tab"); softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending; IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab"); softn->ipf_nat_pending.ifq_next = NULL; for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) { if (tq->ifq_ttl < softn->ipf_nat_deficmpage) tq->ifq_ttl = softn->ipf_nat_deficmpage; #ifdef LARGE_NAT else if (tq->ifq_ttl > softn->ipf_nat_defage) tq->ifq_ttl = softn->ipf_nat_defage; #endif } /* * Increase this because we may have "keep state" following * this too and packet storms can occur if this is removed * too quickly. */ softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex"); MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex"); softn->ipf_nat_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Free all memory used by NAT structures allocated at runtime. */ /* ------------------------------------------------------------------------ */ int ipf_nat_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_nat_softc_t *softn = arg; ipftq_t *ifq, *ifqnext; (void) ipf_nat_clearlist(softc, softn); (void) ipf_nat_flushtable(softc, softn); /* * Proxy timeout queues are not cleaned here because although they * exist on the NAT list, ipf_proxy_unload is called after unload * and the proxies actually are responsible for them being created. * Should the proxy timeouts have their own list? There's no real * justification as this is the only complication. */ for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (ipf_deletetimeoutqueue(ifq) == 0) ipf_freetimeoutqueue(softc, ifq); } if (softn->ipf_nat_table[0] != NULL) { KFREES(softn->ipf_nat_table[0], sizeof(nat_t *) * softn->ipf_nat_table_sz); softn->ipf_nat_table[0] = NULL; } if (softn->ipf_nat_table[1] != NULL) { KFREES(softn->ipf_nat_table[1], sizeof(nat_t *) * softn->ipf_nat_table_sz); softn->ipf_nat_table[1] = NULL; } if (softn->ipf_nat_map_rules != NULL) { KFREES(softn->ipf_nat_map_rules, sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); softn->ipf_nat_map_rules = NULL; } if (softn->ipf_nat_rdr_rules != NULL) { KFREES(softn->ipf_nat_rdr_rules, sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); softn->ipf_nat_rdr_rules = NULL; } if (softn->ipf_hm_maptable != NULL) { KFREES(softn->ipf_hm_maptable, sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); softn->ipf_hm_maptable = NULL; } if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, sizeof(u_int) * softn->ipf_nat_table_sz); softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL; } if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, sizeof(u_int) * softn->ipf_nat_table_sz); softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL; } if (softn->ipf_nat_inited == 1) { softn->ipf_nat_inited = 0; ipf_sttab_destroy(softn->ipf_nat_tcptq); MUTEX_DESTROY(&softn->ipf_nat_new); MUTEX_DESTROY(&softn->ipf_nat_io); MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setlock */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to soft state information */ /* tmp(I) - new lock value */ /* */ /* Set the "lock status" of NAT to the value in tmp. */ /* ------------------------------------------------------------------------ */ void ipf_nat_setlock(arg, tmp) void *arg; int tmp; { ipf_nat_softc_t *softn = arg; softn->ipf_nat_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_addrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a redirect rule to the hash table of redirect rules and the list of */ /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ /* use by redirect rules. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_addrdr(softn, n) ipf_nat_softc_t *softn; ipnat_t *n; { ipnat_t **np; u_32_t j; u_int hv; u_int rhv; int k; if (n->in_odstatype == FRI_NORMAL) { k = count4bits(n->in_odstmsk); ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask); j = (n->in_odstaddr & n->in_odstmsk); rhv = NAT_HASH_FN(j, 0, 0xffffffff); } else { ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask); j = 0; rhv = 0; } hv = rhv % softn->ipf_nat_rdrrules_sz; np = softn->ipf_nat_rdr_rules + hv; while (*np != NULL) np = &(*np)->in_rnext; n->in_rnext = NULL; n->in_prnext = np; n->in_hv[0] = hv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_addmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a NAT map rule to the hash table of rules and the list of loaded */ /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ /* redirect rules. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_addmap(softn, n) ipf_nat_softc_t *softn; ipnat_t *n; { ipnat_t **np; u_32_t j; u_int hv; u_int rhv; int k; if (n->in_osrcatype == FRI_NORMAL) { k = count4bits(n->in_osrcmsk); ipf_inet_mask_add(k, &softn->ipf_nat_map_mask); j = (n->in_osrcaddr & n->in_osrcmsk); rhv = NAT_HASH_FN(j, 0, 0xffffffff); } else { ipf_inet_mask_add(0, &softn->ipf_nat_map_mask); j = 0; rhv = 0; } hv = rhv % softn->ipf_nat_maprules_sz; np = softn->ipf_nat_map_rules + hv; while (*np != NULL) np = &(*np)->in_mnext; n->in_mnext = NULL; n->in_pmnext = np; n->in_hv[1] = rhv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a redirect rule from the hash table of redirect rules. */ /* ------------------------------------------------------------------------ */ void ipf_nat_delrdr(softn, n) ipf_nat_softc_t *softn; ipnat_t *n; { if (n->in_odstatype == FRI_NORMAL) { int k = count4bits(n->in_odstmsk); ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask); } else { ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask); } if (n->in_rnext) n->in_rnext->in_prnext = n->in_prnext; *n->in_prnext = n->in_rnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a NAT map rule from the hash table of NAT map rules. */ /* ------------------------------------------------------------------------ */ void ipf_nat_delmap(softn, n) ipf_nat_softc_t *softn; ipnat_t *n; { if (n->in_osrcatype == FRI_NORMAL) { int k = count4bits(n->in_osrcmsk); ipf_inet_mask_del(k, &softn->ipf_nat_map_mask); } else { ipf_inet_mask_del(0, &softn->ipf_nat_map_mask); } if (n->in_mnext != NULL) n->in_mnext->in_pmnext = n->in_pmnext; *n->in_pmnext = n->in_mnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmap */ /* Returns: struct hostmap* - NULL if no hostmap could be created, */ /* else a pointer to the hostmapping to use */ /* Parameters: np(I) - pointer to NAT rule */ /* real(I) - real IP address */ /* map(I) - mapped IP address */ /* port(I) - destination port number */ /* Write Locks: ipf_nat */ /* */ /* Check if an ip address has already been allocated for a given mapping */ /* that is not doing port based translation. If is not yet allocated, then */ /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ /* ------------------------------------------------------------------------ */ static struct hostmap * ipf_nat_hostmap(softn, np, src, dst, map, port) ipf_nat_softc_t *softn; ipnat_t *np; struct in_addr src; struct in_addr dst; struct in_addr map; u_32_t port; { hostmap_t *hm; u_int hv, rhv; hv = (src.s_addr ^ dst.s_addr); hv += src.s_addr; hv += dst.s_addr; rhv = hv; hv %= softn->ipf_nat_hostmap_sz; for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext) if ((hm->hm_osrcip.s_addr == src.s_addr) && (hm->hm_odstip.s_addr == dst.s_addr) && ((np == NULL) || (np == hm->hm_ipnat)) && ((port == 0) || (port == hm->hm_port))) { softn->ipf_nat_stats.ns_hm_addref++; hm->hm_ref++; return hm; } if (np == NULL) { softn->ipf_nat_stats.ns_hm_nullnp++; return NULL; } KMALLOC(hm, hostmap_t *); if (hm) { hm->hm_next = softn->ipf_hm_maplist; hm->hm_pnext = &softn->ipf_hm_maplist; if (softn->ipf_hm_maplist != NULL) softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; softn->ipf_hm_maplist = hm; hm->hm_hnext = softn->ipf_hm_maptable[hv]; hm->hm_phnext = softn->ipf_hm_maptable + hv; if (softn->ipf_hm_maptable[hv] != NULL) softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; softn->ipf_hm_maptable[hv] = hm; hm->hm_ipnat = np; np->in_use++; hm->hm_osrcip = src; hm->hm_odstip = dst; hm->hm_nsrcip = map; hm->hm_ndstip.s_addr = 0; hm->hm_ref = 1; hm->hm_port = port; hm->hm_hv = rhv; hm->hm_v = 4; softn->ipf_nat_stats.ns_hm_new++; } else { softn->ipf_nat_stats.ns_hm_newfail++; } return hm; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmapdel */ /* Returns: Nil */ /* Parameters: hmp(I) - pointer to hostmap structure pointer */ /* Write Locks: ipf_nat */ /* */ /* Decrement the references to this hostmap structure by one. If this */ /* reaches zero then remove it and free it. */ /* ------------------------------------------------------------------------ */ void ipf_nat_hostmapdel(softc, hmp) ipf_main_softc_t *softc; struct hostmap **hmp; { struct hostmap *hm; hm = *hmp; *hmp = NULL; hm->hm_ref--; if (hm->hm_ref == 0) { ipf_nat_rule_deref(softc, &hm->hm_ipnat); if (hm->hm_hnext) hm->hm_hnext->hm_phnext = hm->hm_phnext; *hm->hm_phnext = hm->hm_hnext; if (hm->hm_next) hm->hm_next->hm_pnext = hm->hm_pnext; *hm->hm_pnext = hm->hm_next; KFREE(hm); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_outcksum */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* sp(I) - location of 16bit checksum to update */ /* n((I) - amount to adjust checksum by */ /* */ /* Adjusts the 16bit checksum by "n" for packets going out. */ /* ------------------------------------------------------------------------ */ void ipf_fix_outcksum(cksum, sp, n, partial) int cksum; u_short *sp; u_32_t n, partial; { u_short sumshort; u_32_t sum1; if (n == 0) return; if (cksum == 4) { *sp = 0; return; } if (cksum == 2) { sum1 = partial; sum1 = (sum1 & 0xffff) + (sum1 >> 16); *sp = htons(sum1); return; } sum1 = (~ntohs(*sp)) & 0xffff; sum1 += (n); sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_incksum */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* sp(I) - location of 16bit checksum to update */ /* n((I) - amount to adjust checksum by */ /* */ /* Adjusts the 16bit checksum by "n" for packets going in. */ /* ------------------------------------------------------------------------ */ void ipf_fix_incksum(cksum, sp, n, partial) int cksum; u_short *sp; u_32_t n, partial; { u_short sumshort; u_32_t sum1; if (n == 0) return; if (cksum == 4) { *sp = 0; return; } if (cksum == 2) { sum1 = partial; sum1 = (sum1 & 0xffff) + (sum1 >> 16); *sp = htons(sum1); return; } sum1 = (~ntohs(*sp)) & 0xffff; sum1 += ~(n) & 0xffff; sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_datacksum */ /* Returns: Nil */ /* Parameters: sp(I) - location of 16bit checksum to update */ /* n((I) - amount to adjust checksum by */ /* */ /* Fix_datacksum is used *only* for the adjustments of checksums in the */ /* data section of an IP packet. */ /* */ /* The only situation in which you need to do this is when NAT'ing an */ /* ICMP error message. Such a message, contains in its body the IP header */ /* of the original IP packet, that causes the error. */ /* */ /* You can't use fix_incksum or fix_outcksum in that case, because for the */ /* kernel the data section of the ICMP error is just data, and no special */ /* processing like hardware cksum or ntohs processing have been done by the */ /* kernel on the data section. */ /* ------------------------------------------------------------------------ */ void ipf_fix_datacksum(sp, n) u_short *sp; u_32_t n; { u_short sumshort; u_32_t sum1; if (n == 0) return; sum1 = (~ntohs(*sp)) & 0xffff; sum1 += (n); sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command integer */ /* mode(I) - file mode bits used with open */ /* uid(I) - uid of calling process */ /* ctx(I) - pointer used as key for finding context */ /* */ /* Processes an ioctl call made to operate on the IP Filter NAT device. */ /* ------------------------------------------------------------------------ */ int ipf_nat_ioctl(softc, data, cmd, mode, uid, ctx) ipf_main_softc_t *softc; ioctlcmd_t cmd; caddr_t data; int mode, uid; void *ctx; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int error = 0, ret, arg, getlock; ipnat_t *nat, *nt, *n; ipnat_t natd; SPL_INT(s); #if !SOLARIS && defined(_KERNEL) # if NETBSD_GE_REV(399002000) if ((mode & FWRITE) && kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) # else # if defined(__FreeBSD_version) if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) # else if ((securelevel >= 3) && (mode & FWRITE)) # endif # endif { IPFERROR(60001); return EPERM; } #endif getlock = (mode & NAT_LOCKHELD) ? 0 : 1; n = NULL; nt = NULL; nat = NULL; if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) || (cmd == (ioctlcmd_t)SIOCPURGENAT)) { if (mode & NAT_SYSSPACE) { bcopy(data, (char *)&natd, sizeof(natd)); nat = &natd; error = 0; } else { bzero(&natd, sizeof(natd)); error = ipf_inobj(softc, data, NULL, &natd, IPFOBJ_IPNAT); if (error != 0) goto done; if (natd.in_size < sizeof(ipnat_t)) { error = EINVAL; goto done; } KMALLOCS(nt, ipnat_t *, natd.in_size); if (nt == NULL) { IPFERROR(60070); error = ENOMEM; goto done; } bzero(nt, natd.in_size); error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT, natd.in_size); if (error) goto done; nat = nt; } /* * For add/delete, look to see if the NAT entry is * already present */ nat->in_flags &= IPN_USERFLAGS; if ((nat->in_redir & NAT_MAPBLK) == 0) { if (nat->in_osrcatype == FRI_NORMAL || nat->in_osrcatype == FRI_NONE) nat->in_osrcaddr &= nat->in_osrcmsk; if (nat->in_odstatype == FRI_NORMAL || nat->in_odstatype == FRI_NONE) nat->in_odstaddr &= nat->in_odstmsk; if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) { if (nat->in_nsrcatype == FRI_NORMAL) nat->in_nsrcaddr &= nat->in_nsrcmsk; if (nat->in_ndstatype == FRI_NORMAL) nat->in_ndstaddr &= nat->in_ndstmsk; } } error = ipf_nat_rule_init(softc, softn, nat); if (error != 0) goto done; MUTEX_ENTER(&softn->ipf_nat_io); for (n = softn->ipf_nat_list; n != NULL; n = n->in_next) if (ipf_nat_cmp_rules(nat, n) == 0) break; } switch (cmd) { #ifdef IPFILTER_LOG case SIOCIPFFB : { int tmp; if (!(mode & FWRITE)) { IPFERROR(60002); error = EPERM; } else { tmp = ipf_log_clear(softc, IPL_LOGNAT); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(60057); error = EFAULT; } } break; } case SIOCSETLG : if (!(mode & FWRITE)) { IPFERROR(60003); error = EPERM; } else { error = BCOPYIN(data, &softn->ipf_nat_logging, sizeof(softn->ipf_nat_logging)); if (error != 0) error = EFAULT; } break; case SIOCGETLG : error = BCOPYOUT(&softn->ipf_nat_logging, data, sizeof(softn->ipf_nat_logging)); if (error != 0) { IPFERROR(60004); error = EFAULT; } break; case FIONREAD : arg = ipf_log_bytesused(softc, IPL_LOGNAT); error = BCOPYOUT(&arg, data, sizeof(arg)); if (error != 0) { IPFERROR(60005); error = EFAULT; } break; #endif case SIOCADNAT : if (!(mode & FWRITE)) { IPFERROR(60006); error = EPERM; } else if (n != NULL) { natd.in_flineno = n->in_flineno; (void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT); IPFERROR(60007); error = EEXIST; } else if (nt == NULL) { IPFERROR(60008); error = ENOMEM; } if (error != 0) { MUTEX_EXIT(&softn->ipf_nat_io); break; } if (nat != nt) bcopy((char *)nat, (char *)nt, sizeof(*n)); error = ipf_nat_siocaddnat(softc, softn, nt, getlock); MUTEX_EXIT(&softn->ipf_nat_io); if (error == 0) { nat = NULL; nt = NULL; } break; case SIOCRMNAT : case SIOCPURGENAT : if (!(mode & FWRITE)) { IPFERROR(60009); error = EPERM; n = NULL; } else if (n == NULL) { IPFERROR(60010); error = ESRCH; } if (error != 0) { MUTEX_EXIT(&softn->ipf_nat_io); break; } if (cmd == (ioctlcmd_t)SIOCPURGENAT) { error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT, n->in_size); if (error) { MUTEX_EXIT(&softn->ipf_nat_io); goto done; } n->in_flags |= IPN_PURGE; } ipf_nat_siocdelnat(softc, softn, n, getlock); MUTEX_EXIT(&softn->ipf_nat_io); n = NULL; break; case SIOCGNATS : { natstat_t *nsp = &softn->ipf_nat_stats; nsp->ns_side[0].ns_table = softn->ipf_nat_table[0]; nsp->ns_side[1].ns_table = softn->ipf_nat_table[1]; nsp->ns_list = softn->ipf_nat_list; nsp->ns_maptable = softn->ipf_hm_maptable; nsp->ns_maplist = softn->ipf_hm_maplist; nsp->ns_nattab_sz = softn->ipf_nat_table_sz; nsp->ns_nattab_max = softn->ipf_nat_table_max; nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz; nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz; nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz; nsp->ns_instances = softn->ipf_nat_instances; nsp->ns_ticks = softc->ipf_ticks; #ifdef IPFILTER_LOGGING nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT); nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT); #else nsp->ns_log_ok = 0; nsp->ns_log_fail = 0; #endif error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT); break; } case SIOCGNATL : { natlookup_t nl; error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); if (error == 0) { void *ptr; if (getlock) { READ_ENTER(&softc->ipf_nat); } switch (nl.nl_v) { case 4 : ptr = ipf_nat_lookupredir(&nl); break; #ifdef USE_INET6 case 6 : ptr = ipf_nat6_lookupredir(&nl); break; #endif default: ptr = NULL; break; } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (ptr != NULL) { error = ipf_outobj(softc, data, &nl, IPFOBJ_NATLOOKUP); } else { IPFERROR(60011); error = ESRCH; } } break; } case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ if (!(mode & FWRITE)) { IPFERROR(60012); error = EPERM; break; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(60013); error = EFAULT; } else { if (arg == 0) ret = ipf_nat_flushtable(softc, softn); else if (arg == 1) ret = ipf_nat_clearlist(softc, softn); else ret = ipf_nat_extraflush(softc, softn, arg); ipf_proxy_flush(softc->ipf_proxy_soft, arg); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) { error = BCOPYOUT(&ret, data, sizeof(ret)); } break; case SIOCMATCHFLUSH : if (!(mode & FWRITE)) { IPFERROR(60014); error = EPERM; break; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } error = ipf_nat_matchflush(softc, softn, data); if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } break; case SIOCPROXY : error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); break; case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(60015); error = EPERM; } else { error = ipf_lock(data, &softn->ipf_nat_lock); } break; case SIOCSTPUT : if ((mode & FWRITE) != 0) { error = ipf_nat_putent(softc, data, getlock); } else { IPFERROR(60016); error = EACCES; } break; case SIOCSTGSZ : if (softn->ipf_nat_lock) { error = ipf_nat_getsz(softc, data, getlock); } else { IPFERROR(60017); error = EACCES; } break; case SIOCSTGET : if (softn->ipf_nat_lock) { error = ipf_nat_getent(softc, data, getlock); } else { IPFERROR(60018); error = EACCES; } break; case SIOCGENITER : { ipfgeniter_t iter; ipftoken_t *token; ipfobj_t obj; error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); token = ipf_token_find(softc, iter.igi_type, uid, ctx); if (token != NULL) { error = ipf_nat_iterator(softc, token, &iter, &obj); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } SPL_X(s); break; } case SIOCIPFDELTOK : error = BCOPYIN(data, &arg, sizeof(arg)); if (error == 0) { SPL_SCHED(s); error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } else { IPFERROR(60019); error = EFAULT; } break; case SIOCGTQTAB : error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, IPFOBJ_STATETQTAB); break; case SIOCGTABL : error = ipf_nat_gettable(softc, softn, data); break; default : IPFERROR(60020); error = EINVAL; break; } done: if (nat != NULL) ipf_nat_rule_fini(softc, nat); if (nt != NULL) KFREES(nt, nt->in_size); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_siocaddnat */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to new NAT rule */ /* np(I) - pointer to where to insert new NAT rule */ /* getlock(I) - flag indicating if lock on is held */ /* Mutex Locks: ipf_nat_io */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static int ipf_nat_siocaddnat(softc, softn, n, getlock) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; ipnat_t *n; int getlock; { int error = 0; if (ipf_nat_resolverule(softc, n) != 0) { IPFERROR(60022); return ENOENT; } if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { IPFERROR(60023); return EINVAL; } if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { /* * Prerecord whether or not the destination of the divert * is local or not to the interface the packet is going * to be sent out. */ n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], n->in_ifps[1], &n->in_ndstip6); } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } n->in_next = NULL; n->in_pnext = softn->ipf_nat_list_tail; *n->in_pnext = n; softn->ipf_nat_list_tail = &n->in_next; n->in_use++; if (n->in_redir & NAT_REDIRECT) { n->in_flags &= ~IPN_NOTDST; switch (n->in_v[0]) { case 4 : ipf_nat_addrdr(softn, n); break; #ifdef USE_INET6 case 6 : ipf_nat6_addrdr(softn, n); break; #endif default : break; } ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr); } if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { n->in_flags &= ~IPN_NOTSRC; switch (n->in_v[0]) { case 4 : ipf_nat_addmap(softn, n); break; #ifdef USE_INET6 case 6 : ipf_nat6_addmap(softn, n); break; #endif default : break; } ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map); } if (n->in_age[0] != 0) n->in_tqehead[0] = ipf_addtimeoutqueue(softc, &softn->ipf_nat_utqe, n->in_age[0]); if (n->in_age[1] != 0) n->in_tqehead[1] = ipf_addtimeoutqueue(softc, &softn->ipf_nat_utqe, n->in_age[1]); MUTEX_INIT(&n->in_lock, "ipnat rule lock"); n = NULL; ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); #if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 0; #endif if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_ruleaddrinit */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to NAT rule */ /* */ /* Initialise all of the NAT address structures in a NAT rule. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_ruleaddrinit(softc, softn, n) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; ipnat_t *n; { int idx, error; if ((n->in_ndst.na_atype == FRI_LOOKUP) && (n->in_ndst.na_type != IPLT_DSTLIST)) { IPFERROR(60071); return EINVAL; } if ((n->in_nsrc.na_atype == FRI_LOOKUP) && (n->in_nsrc.na_type != IPLT_DSTLIST)) { IPFERROR(60069); return EINVAL; } if (n->in_redir == NAT_BIMAP) { n->in_ndstaddr = n->in_osrcaddr; n->in_ndstmsk = n->in_osrcmsk; n->in_odstaddr = n->in_nsrcaddr; n->in_odstmsk = n->in_nsrcmsk; } if (n->in_redir & NAT_REDIRECT) idx = 1; else idx = 0; /* * Initialise all of the address fields. */ error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, n->in_ifps[idx]); if (error != 0) return error; if (n->in_redir & NAT_DIVERTUDP) ipf_nat_builddivertmp(softn, n); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_resolvrule */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* n(I) - pointer to NAT rule */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static int ipf_nat_resolverule(softc, n) ipf_main_softc_t *softc; ipnat_t *n; { char *base; base = n->in_names; n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], n->in_v[0]); if (n->in_ifnames[1] == -1) { n->in_ifnames[1] = n->in_ifnames[0]; n->in_ifps[1] = n->in_ifps[0]; } else { n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], n->in_v[1]); } if (n->in_plabel != -1) { if (n->in_redir & NAT_REDIRECT) n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, n->in_pr[0], base + n->in_plabel); else n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, n->in_pr[1], base + n->in_plabel); if (n->in_apr == NULL) return -1; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_siocdelnat */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to new NAT rule */ /* getlock(I) - flag indicating if lock on is held */ /* Mutex Locks: ipf_nat_io */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static void ipf_nat_siocdelnat(softc, softn, n, getlock) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; ipnat_t *n; int getlock; { if (getlock) { WRITE_ENTER(&softc->ipf_nat); } ipf_nat_delrule(softc, softn, n, 1); if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_getsz */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with kernel */ /* pointer get the size of. */ /* getlock(I) - flag indicating whether or not the caller */ /* holds a lock on ipf_nat */ /* */ /* Handle SIOCSTGSZ. */ /* Return the size of the nat list entry to be copied back to user space. */ /* The size of the entry is stored in the ng_sz field and the enture natget */ /* structure is copied back to the user. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_getsz(softc, data, getlock) ipf_main_softc_t *softc; caddr_t data; int getlock; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ap_session_t *aps; nat_t *nat, *n; natget_t ng; int error; error = BCOPYIN(data, &ng, sizeof(ng)); if (error != 0) { IPFERROR(60024); return EFAULT; } if (getlock) { READ_ENTER(&softc->ipf_nat); } nat = ng.ng_ptr; if (!nat) { nat = softn->ipf_nat_instances; ng.ng_sz = 0; /* * Empty list so the size returned is 0. Simple. */ if (nat == NULL) { if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } error = BCOPYOUT(&ng, data, sizeof(ng)); if (error != 0) { IPFERROR(60025); return EFAULT; } return 0; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } IPFERROR(60026); return ESRCH; } } /* * Incluse any space required for proxy data structures. */ ng.ng_sz = sizeof(nat_save_t); aps = nat->nat_aps; if (aps != NULL) { ng.ng_sz += sizeof(ap_session_t) - 4; if (aps->aps_data != 0) ng.ng_sz += aps->aps_psiz; } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } error = BCOPYOUT(&ng, data, sizeof(ng)); if (error != 0) { IPFERROR(60027); return EFAULT; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_getent */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with kernel pointer*/ /* to NAT structure to copy out. */ /* getlock(I) - flag indicating whether or not the caller */ /* holds a lock on ipf_nat */ /* */ /* Handle SIOCSTGET. */ /* Copies out NAT entry to user space. Any additional data held for a */ /* proxy is also copied, as to is the NAT rule which was responsible for it */ /* ------------------------------------------------------------------------ */ static int ipf_nat_getent(softc, data, getlock) ipf_main_softc_t *softc; caddr_t data; int getlock; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int error, outsize; ap_session_t *aps; nat_save_t *ipn, ipns; nat_t *n, *nat; error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); if (error != 0) return error; if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { IPFERROR(60028); return EINVAL; } KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); if (ipn == NULL) { IPFERROR(60029); return ENOMEM; } if (getlock) { READ_ENTER(&softc->ipf_nat); } ipn->ipn_dsize = ipns.ipn_dsize; nat = ipns.ipn_next; if (nat == NULL) { nat = softn->ipf_nat_instances; if (nat == NULL) { if (softn->ipf_nat_instances == NULL) { IPFERROR(60030); error = ENOENT; } goto finished; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { IPFERROR(60031); error = ESRCH; goto finished; } } ipn->ipn_next = nat->nat_next; /* * Copy the NAT structure. */ bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); /* * If we have a pointer to the NAT rule it belongs to, save that too. */ if (nat->nat_ptr != NULL) bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, ipn->ipn_ipnat.in_size); /* * If we also know the NAT entry has an associated filter rule, * save that too. */ if (nat->nat_fr != NULL) bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, sizeof(ipn->ipn_fr)); /* * Last but not least, if there is an application proxy session set * up for this NAT entry, then copy that out too, including any * private data saved along side it by the proxy. */ aps = nat->nat_aps; outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); if (aps != NULL) { char *s; if (outsize < sizeof(*aps)) { IPFERROR(60032); error = ENOBUFS; goto finished; } s = ipn->ipn_data; bcopy((char *)aps, s, sizeof(*aps)); s += sizeof(*aps); outsize -= sizeof(*aps); if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) bcopy(aps->aps_data, s, aps->aps_psiz); else { IPFERROR(60033); error = ENOBUFS; } } if (error == 0) { error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); } finished: if (ipn != NULL) { KFREES(ipn, ipns.ipn_dsize); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_putent */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with NAT */ /* structure information to load into the kernel */ /* getlock(I) - flag indicating whether or not a write lock */ /* on is already held. */ /* */ /* Handle SIOCSTPUT. */ /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ /* firewall rule data structures, if pointers to them indicate so. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_putent(softc, data, getlock) ipf_main_softc_t *softc; caddr_t data; int getlock; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; nat_save_t ipn, *ipnn; ap_session_t *aps; nat_t *n, *nat; frentry_t *fr; fr_info_t fin; ipnat_t *in; int error; error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE); if (error != 0) return error; /* * Initialise early because of code at junkput label. */ n = NULL; in = NULL; aps = NULL; nat = NULL; ipnn = NULL; fr = NULL; /* * New entry, copy in the rest of the NAT entry if it's size is more * than just the nat_t structure. */ if (ipn.ipn_dsize > sizeof(ipn)) { if (ipn.ipn_dsize > 81920) { IPFERROR(60034); error = ENOMEM; goto junkput; } KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); if (ipnn == NULL) { IPFERROR(60035); return ENOMEM; } bzero(ipnn, ipn.ipn_dsize); error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); if (error != 0) { goto junkput; } } else ipnn = &ipn; KMALLOC(nat, nat_t *); if (nat == NULL) { IPFERROR(60037); error = ENOMEM; goto junkput; } bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); switch (nat->nat_v[0]) { case 4: #ifdef USE_INET6 case 6 : #endif break; default : IPFERROR(60061); error = EPROTONOSUPPORT; goto junkput; /*NOTREACHED*/ } /* * Initialize all these so that ipf_nat_delete() doesn't cause a crash. */ bzero((char *)nat, offsetof(struct nat, nat_tqe)); nat->nat_tqe.tqe_pnext = NULL; nat->nat_tqe.tqe_next = NULL; nat->nat_tqe.tqe_ifq = NULL; nat->nat_tqe.tqe_parent = nat; /* * Restore the rule associated with this nat session */ in = ipnn->ipn_nat.nat_ptr; if (in != NULL) { KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); nat->nat_ptr = in; if (in == NULL) { IPFERROR(60038); error = ENOMEM; goto junkput; } bcopy((char *)&ipnn->ipn_ipnat, (char *)in, ipnn->ipn_ipnat.in_size); in->in_use = 1; in->in_flags |= IPN_DELETE; ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); if (ipf_nat_resolverule(softc, in) != 0) { IPFERROR(60039); error = ESRCH; goto junkput; } } /* * Check that the NAT entry doesn't already exist in the kernel. * * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do * this, we check to see if the inbound combination of addresses and * ports is already known. Similar logic is applied for NAT_INBOUND. * */ bzero((char *)&fin, sizeof(fin)); fin.fin_v = nat->nat_v[0]; fin.fin_p = nat->nat_pr[0]; fin.fin_rev = nat->nat_rev; fin.fin_ifp = nat->nat_ifps[0]; fin.fin_data[0] = ntohs(nat->nat_ndport); fin.fin_data[1] = ntohs(nat->nat_nsport); switch (nat->nat_dir) { case NAT_OUTBOUND : case NAT_DIVERTOUT : if (getlock) { READ_ENTER(&softc->ipf_nat); } fin.fin_v = nat->nat_v[1]; if (nat->nat_v[1] == 4) { n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip); #ifdef USE_INET6 } else if (nat->nat_v[1] == 6) { n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, &nat->nat_ndst6.in6, &nat->nat_nsrc6.in6); #endif } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { IPFERROR(60040); error = EEXIST; goto junkput; } break; case NAT_INBOUND : case NAT_DIVERTIN : if (getlock) { READ_ENTER(&softc->ipf_nat); } if (fin.fin_v == 4) { n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip); #ifdef USE_INET6 } else if (fin.fin_v == 6) { n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, &nat->nat_ndst6.in6, &nat->nat_nsrc6.in6); #endif } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { IPFERROR(60041); error = EEXIST; goto junkput; } break; default : IPFERROR(60042); error = EINVAL; goto junkput; } /* * Restore ap_session_t structure. Include the private data allocated * if it was there. */ aps = nat->nat_aps; if (aps != NULL) { KMALLOC(aps, ap_session_t *); nat->nat_aps = aps; if (aps == NULL) { IPFERROR(60043); error = ENOMEM; goto junkput; } bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); if (in != NULL) aps->aps_apr = in->in_apr; else aps->aps_apr = NULL; if (aps->aps_psiz != 0) { if (aps->aps_psiz > 81920) { IPFERROR(60044); error = ENOMEM; goto junkput; } KMALLOCS(aps->aps_data, void *, aps->aps_psiz); if (aps->aps_data == NULL) { IPFERROR(60045); error = ENOMEM; goto junkput; } bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, aps->aps_psiz); } else { aps->aps_psiz = 0; aps->aps_data = NULL; } } /* * If there was a filtering rule associated with this entry then * build up a new one. */ fr = nat->nat_fr; if (fr != NULL) { if ((nat->nat_flags & SI_NEWFR) != 0) { KMALLOC(fr, frentry_t *); nat->nat_fr = fr; if (fr == NULL) { IPFERROR(60046); error = ENOMEM; goto junkput; } ipnn->ipn_nat.nat_fr = fr; fr->fr_ref = 1; (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); fr->fr_ref = 1; fr->fr_dsize = 0; fr->fr_data = NULL; fr->fr_type = FR_T_NONE; MUTEX_NUKE(&fr->fr_lock); MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); } else { if (getlock) { READ_ENTER(&softc->ipf_nat); } for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n->nat_fr == fr) break; if (n != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n == NULL) { IPFERROR(60047); error = ESRCH; goto junkput; } } } if (ipnn != &ipn) { KFREES(ipnn, ipn.ipn_dsize); ipnn = NULL; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } if (fin.fin_v == 4) error = ipf_nat_finalise(&fin, nat); #ifdef USE_INET6 else error = ipf_nat6_finalise(&fin, nat); #endif if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) return 0; IPFERROR(60048); error = ENOMEM; junkput: if (fr != NULL) { (void) ipf_derefrule(softc, &fr); } if ((ipnn != NULL) && (ipnn != &ipn)) { KFREES(ipnn, ipn.ipn_dsize); } if (nat != NULL) { if (aps != NULL) { if (aps->aps_data != NULL) { KFREES(aps->aps_data, aps->aps_psiz); } KFREE(aps); } if (in != NULL) { if (in->in_apr) ipf_proxy_deref(in->in_apr); KFREES(in, in->in_size); } KFREE(nat); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nat(I) - pointer to NAT structure to delete */ /* logtype(I) - type of LOG record to create before deleting */ /* Write Lock: ipf_nat */ /* */ /* Delete a nat entry from the various lists and table. If NAT logging is */ /* enabled then generate a NAT log record for this event. */ /* ------------------------------------------------------------------------ */ void ipf_nat_delete(softc, nat, logtype) ipf_main_softc_t *softc; struct nat *nat; int logtype; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int madeorphan = 0, bkt, removed = 0; nat_stat_side_t *nss; struct ipnat *ipn; if (logtype != 0 && softn->ipf_nat_logging != 0) ipf_nat_log(softc, softn, nat, logtype); /* * Take it as a general indication that all the pointers are set if * nat_pnext is set. */ if (nat->nat_pnext != NULL) { removed = 1; bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; nss = &softn->ipf_nat_stats.ns_side[0]; if (nss->ns_bucketlen[bkt] > 0) nss->ns_bucketlen[bkt]--; if (nss->ns_bucketlen[bkt] == 0) { nss->ns_inuse--; } bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; nss = &softn->ipf_nat_stats.ns_side[1]; if (nss->ns_bucketlen[bkt] > 0) nss->ns_bucketlen[bkt]--; if (nss->ns_bucketlen[bkt] == 0) { nss->ns_inuse--; } *nat->nat_pnext = nat->nat_next; if (nat->nat_next != NULL) { nat->nat_next->nat_pnext = nat->nat_pnext; nat->nat_next = NULL; } nat->nat_pnext = NULL; *nat->nat_phnext[0] = nat->nat_hnext[0]; if (nat->nat_hnext[0] != NULL) { nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; nat->nat_hnext[0] = NULL; } nat->nat_phnext[0] = NULL; *nat->nat_phnext[1] = nat->nat_hnext[1]; if (nat->nat_hnext[1] != NULL) { nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; nat->nat_hnext[1] = NULL; } nat->nat_phnext[1] = NULL; if ((nat->nat_flags & SI_WILDP) != 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); } madeorphan = 1; } if (nat->nat_me != NULL) { *nat->nat_me = NULL; nat->nat_me = NULL; nat->nat_ref--; ASSERT(nat->nat_ref >= 0); } if (nat->nat_tqe.tqe_ifq != NULL) { /* * No call to ipf_freetimeoutqueue() is made here, they are * garbage collected in ipf_nat_expire(). */ (void) ipf_deletequeueentry(&nat->nat_tqe); } if (nat->nat_sync) { ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); nat->nat_sync = NULL; } if (logtype == NL_EXPIRE) softn->ipf_nat_stats.ns_expire++; MUTEX_ENTER(&nat->nat_lock); /* * NL_DESTROY should only be passed in when we've got nat_ref >= 2. * This happens when a nat'd packet is blocked and we want to throw * away the NAT session. */ if (logtype == NL_DESTROY) { if (nat->nat_ref > 2) { nat->nat_ref -= 2; MUTEX_EXIT(&nat->nat_lock); if (removed) softn->ipf_nat_stats.ns_orphans++; return; } } else if (nat->nat_ref > 1) { nat->nat_ref--; MUTEX_EXIT(&nat->nat_lock); if (madeorphan == 1) softn->ipf_nat_stats.ns_orphans++; return; } ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); nat->nat_ref = 0; if (madeorphan == 0) softn->ipf_nat_stats.ns_orphans--; /* * At this point, nat_ref can be either 0 or -1 */ softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; if (nat->nat_fr != NULL) { (void) ipf_derefrule(softc, &nat->nat_fr); } if (nat->nat_hm != NULL) { ipf_nat_hostmapdel(softc, &nat->nat_hm); } /* * If there is an active reference from the nat entry to its parent * rule, decrement the rule's reference count and free it too if no * longer being used. */ ipn = nat->nat_ptr; nat->nat_ptr = NULL; if (ipn != NULL) { ipn->in_space++; ipf_nat_rule_deref(softc, &ipn); } if (nat->nat_aps != NULL) { ipf_proxy_free(softc, nat->nat_aps); nat->nat_aps = NULL; } MUTEX_DESTROY(&nat->nat_lock); softn->ipf_nat_stats.ns_active--; /* * If there's a fragment table entry too for this nat entry, then * dereference that as well. This is after nat_lock is released * because of Tru64. */ ipf_frag_natforget(softc, (void *)nat); KFREE(nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_flushtable */ /* Returns: int - number of NAT rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* Write Lock: ipf_nat */ /* */ /* Deletes all currently active NAT sessions. In deleting each NAT entry a */ /* log record should be emitted in ipf_nat_delete() if NAT logging is */ /* enabled. */ /* ------------------------------------------------------------------------ */ /* * nat_flushtable - clear the NAT table of all mapping entries. */ static int ipf_nat_flushtable(softc, softn) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; { nat_t *nat; int j = 0; /* * ALL NAT mappings deleted, so lets just make the deletions * quicker. */ if (softn->ipf_nat_table[0] != NULL) bzero((char *)softn->ipf_nat_table[0], sizeof(softn->ipf_nat_table[0]) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[1] != NULL) bzero((char *)softn->ipf_nat_table[1], sizeof(softn->ipf_nat_table[1]) * softn->ipf_nat_table_sz); while ((nat = softn->ipf_nat_instances) != NULL) { ipf_nat_delete(softc, nat, NL_FLUSH); j++; } return j; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_clearlist */ /* Returns: int - number of NAT/RDR rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* */ /* Delete all rules in the current list of rules. There is nothing elegant */ /* about this cleanup: simply free all entries on the list of rules and */ /* clear out the tables used for hashed NAT rule lookups. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_clearlist(softc, softn) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; { ipnat_t *n; int i = 0; if (softn->ipf_nat_map_rules != NULL) { bzero((char *)softn->ipf_nat_map_rules, sizeof(*softn->ipf_nat_map_rules) * softn->ipf_nat_maprules_sz); } if (softn->ipf_nat_rdr_rules != NULL) { bzero((char *)softn->ipf_nat_rdr_rules, sizeof(*softn->ipf_nat_rdr_rules) * softn->ipf_nat_rdrrules_sz); } while ((n = softn->ipf_nat_list) != NULL) { ipf_nat_delrule(softc, softn, n, 0); i++; } #if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 1; #endif return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delrule */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* np(I) - pointer to NAT rule to delete */ /* purge(I) - 1 == allow purge, 0 == prevent purge */ /* Locks: WRITE(ipf_nat) */ /* */ /* Preventing "purge" from occuring is allowed because when all of the NAT */ /* rules are being removed, allowing the "purge" to walk through the list */ /* of NAT sessions, possibly multiple times, would be a large performance */ /* hit, on the order of O(N^2). */ /* ------------------------------------------------------------------------ */ static void ipf_nat_delrule(softc, softn, np, purge) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; ipnat_t *np; int purge; { if (np->in_pnext != NULL) { *np->in_pnext = np->in_next; if (np->in_next != NULL) np->in_next->in_pnext = np->in_pnext; if (softn->ipf_nat_list_tail == &np->in_next) softn->ipf_nat_list_tail = np->in_pnext; } if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { nat_t *next; nat_t *nat; for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { next = nat->nat_next; if (nat->nat_ptr == np) ipf_nat_delete(softc, nat, NL_PURGE); } } if ((np->in_flags & IPN_DELETE) == 0) { if (np->in_redir & NAT_REDIRECT) { switch (np->in_v[0]) { case 4 : ipf_nat_delrdr(softn, np); break; #ifdef USE_INET6 case 6 : ipf_nat6_delrdr(softn, np); break; #endif } } if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { switch (np->in_v[0]) { case 4 : ipf_nat_delmap(softn, np); break; #ifdef USE_INET6 case 6 : ipf_nat6_delmap(softn, np); break; #endif } } } np->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &np); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_newmap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* Given an empty NAT structure, populate it with new information about a */ /* new NAT session, as defined by the matching NAT rule. */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_newmap(fin, nat, ni) fr_info_t *fin; nat_t *nat; natinfo_t *ni; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short st_port, dport, sport, port, sp, dp; struct in_addr in, inb; hostmap_t *hm; u_32_t flags; u_32_t st_ip; ipnat_t *np; nat_t *natl; int l; /* * If it's an outbound packet which doesn't match any existing * record, then create a new port */ l = 0; hm = NULL; np = ni->nai_np; st_ip = np->in_snip; st_port = np->in_spnext; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { sport = fin->fin_data[1]; dport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* * Do a loop until we either run out of entries to try or we find * a NAT mapping that isn't currently being used. This is done * because the change to the source is not (usually) being fixed. */ do { port = 0; in.s_addr = htonl(np->in_snip); if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, in, 0); if (hm != NULL) in.s_addr = hm->hm_nsrcip.s_addr; } else if ((l == 1) && (hm != NULL)) { ipf_nat_hostmapdel(softc, &hm); } in.s_addr = ntohl(in.s_addr); nat->nat_hm = hm; if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { if (l > 0) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); DT4(ns_exhausted_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } } if (np->in_redir == NAT_BIMAP && np->in_osrcmsk == np->in_nsrcmsk) { /* * map the address block in a 1:1 fashion */ in.s_addr = np->in_nsrcaddr; in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; in.s_addr = ntohl(in.s_addr); } else if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && !(flags & IPN_TCPUDP))) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); DT4(ns_exhausted_2, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } /* * map-block - Calculate destination address. */ in.s_addr = ntohl(fin->fin_saddr); in.s_addr &= ntohl(~np->in_osrcmsk); inb.s_addr = in.s_addr; in.s_addr /= np->in_ippip; in.s_addr &= ntohl(~np->in_nsrcmsk); in.s_addr += ntohl(np->in_nsrcaddr); /* * Calculate destination port. */ if ((flags & IPN_TCPUDP) && (np->in_ppip != 0)) { port = ntohs(sport) + l; port %= np->in_ppip; port += np->in_ppip * (inb.s_addr % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { i6addr_t in6; /* * 0/32 - use the interface's IP address. */ if ((l > 0) || ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, &in6, NULL) == -1) { NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); DT4(ns_new_ifpaddr_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } in.s_addr = ntohl(in6.in4.s_addr); } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { /* * 0/0 - use the original source address/port. */ if (l > 0) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); DT4(ns_exhausted_3, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } in.s_addr = ntohl(fin->fin_saddr); } else if ((np->in_nsrcmsk != 0xffffffff) && (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) np->in_snip++; natl = NULL; if ((flags & IPN_TCPUDP) && ((np->in_redir & NAT_MAPBLK) == 0) && (np->in_flags & IPN_AUTOPORTMAP)) { /* * "ports auto" (without map-block) */ if ((l > 0) && (l % np->in_ppip == 0)) { if ((l > np->in_ppip) && np->in_nsrcmsk != 0xffffffff) np->in_snip++; } if (np->in_ppip != 0) { port = ntohs(sport); port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * (ntohl(fin->fin_saddr) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if (((np->in_redir & NAT_MAPBLK) == 0) && (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { /* * Standard port translation. Select next port. */ if (np->in_flags & IPN_SEQUENTIAL) { port = np->in_spnext; } else { port = ipf_random() % (np->in_spmax - np->in_spmin + 1); port += np->in_spmin; } port = htons(port); np->in_spnext++; if (np->in_spnext > np->in_spmax) { np->in_spnext = np->in_spmin; if (np->in_nsrcmsk != 0xffffffff) np->in_snip++; } } if (np->in_flags & IPN_SIPRANGE) { if (np->in_snip > ntohl(np->in_nsrcmsk)) np->in_snip = ntohl(np->in_nsrcaddr); } else { if ((np->in_nsrcmsk != 0xffffffff) && ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > ntohl(np->in_nsrcaddr)) np->in_snip = ntohl(np->in_nsrcaddr) + 1; } if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) port = sport; /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. */ inb.s_addr = htonl(in.s_addr); sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[0] = fin->fin_data[1]; fin->fin_data[1] = ntohs(port); natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, fin->fin_dst, inb); fin->fin_data[0] = sp; fin->fin_data[1] = dp; /* * Has the search wrapped around and come back to the * start ? */ if ((natl != NULL) && (np->in_spnext != 0) && (st_port == np->in_spnext) && (np->in_snip != 0) && (st_ip == np->in_snip)) { NBUMPSIDED(1, ns_wrap); DT4(ns_wrap, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } l++; } while (natl != NULL); /* Setup the NAT table */ nat->nat_osrcip = fin->fin_src; nat->nat_nsrcaddr = htonl(in.s_addr); nat->nat_odstip = fin->fin_dst; nat->nat_ndstip = fin->fin_dst; if (nat->nat_hm == NULL) nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, nat->nat_nsrcip, 0); if (flags & IPN_TCPUDP) { nat->nat_osport = sport; nat->nat_nsport = port; /* sport */ nat->nat_odport = dport; nat->nat_ndport = dport; ((tcphdr_t *)fin->fin_dp)->th_sport = port; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((icmphdr_t *)fin->fin_dp)->icmp_id = port; nat->nat_nicmpid = port; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_newrdr */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_newrdr(fin, nat, ni) fr_info_t *fin; nat_t *nat; natinfo_t *ni; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short nport, dport, sport; struct in_addr in, inb; u_short sp, dp; hostmap_t *hm; u_32_t flags; ipnat_t *np; nat_t *natl; int move; move = 1; hm = NULL; in.s_addr = 0; np = ni->nai_np; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { dport = fin->fin_data[1]; sport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* TRACE sport, dport */ /* * If the matching rule has IPN_STICKY set, then we want to have the * same rule kick in as before. Why would this happen? If you have * a collection of rdr rules with "round-robin sticky", the current * packet might match a different one to the previous connection but * we want the same destination to be used. */ if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && ((np->in_flags & IPN_STICKY) != 0)) { hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, in, (u_32_t)dport); if (hm != NULL) { in.s_addr = ntohl(hm->hm_ndstip.s_addr); np = hm->hm_ipnat; ni->nai_np = np; move = 0; ipf_nat_hostmapdel(softc, &hm); } } /* * Otherwise, it's an inbound packet. Most likely, we don't * want to rewrite source ports and source addresses. Instead, * we want to rewrite to a fixed internal address and fixed * internal port. */ if (np->in_flags & IPN_SPLIT) { in.s_addr = np->in_dnip; inb.s_addr = htonl(in.s_addr); if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, inb, (u_32_t)dport); if (hm != NULL) { in.s_addr = hm->hm_ndstip.s_addr; move = 0; } } if (hm == NULL || hm->hm_ref == 1) { if (np->in_ndstaddr == htonl(in.s_addr)) { np->in_dnip = ntohl(np->in_ndstmsk); move = 0; } else { np->in_dnip = ntohl(np->in_ndstaddr); } } if (hm != NULL) ipf_nat_hostmapdel(softc, &hm); } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { i6addr_t in6; /* * 0/32 - use the interface's IP address. */ if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, &in6, NULL) == -1) { NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); DT3(ns_new_ifpaddr_2, fr_info_t *, fin, nat_t *, nat, natinfo_t, ni); return -1; } in.s_addr = ntohl(in6.in4.s_addr); } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) { /* * 0/0 - use the original destination address/port. */ in.s_addr = ntohl(fin->fin_daddr); } else if (np->in_redir == NAT_BIMAP && np->in_ndstmsk == np->in_odstmsk) { /* * map the address block in a 1:1 fashion */ in.s_addr = np->in_ndstaddr; in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk; in.s_addr = ntohl(in.s_addr); } else { in.s_addr = ntohl(np->in_ndstaddr); } if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) nport = dport; else { /* * Whilst not optimized for the case where * pmin == pmax, the gain is not significant. */ if (((np->in_flags & IPN_FIXEDDPORT) == 0) && (np->in_odport != np->in_dtop)) { nport = ntohs(dport) - np->in_odport + np->in_dpmax; nport = htons(nport); } else { nport = htons(np->in_dpnext); np->in_dpnext++; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } /* * When the redirect-to address is set to 0.0.0.0, just * assume a blank `forwarding' of the packet. We don't * setup any translation for this either. */ if (in.s_addr == 0) { if (nport == dport) { NBUMPSIDED(0, ns_xlate_null); return -1; } in.s_addr = ntohl(fin->fin_daddr); } /* * Check to see if this redirect mapping already exists and if * it does, return "failure" (allowing it to be created will just * cause one or both of these "connections" to stop working.) */ inb.s_addr = htonl(in.s_addr); sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[1] = fin->fin_data[0]; fin->fin_data[0] = ntohs(nport); natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, inb, fin->fin_src); fin->fin_data[0] = sp; fin->fin_data[1] = dp; if (natl != NULL) { DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); NBUMPSIDE(0, ns_xlate_exists); return -1; } inb.s_addr = htonl(in.s_addr); nat->nat_ndstaddr = htonl(in.s_addr); nat->nat_odstip = fin->fin_dst; nat->nat_nsrcip = fin->fin_src; nat->nat_osrcip = fin->fin_src; if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, inb, (u_32_t)dport); if (flags & IPN_TCPUDP) { nat->nat_odport = dport; nat->nat_ndport = nport; nat->nat_osport = sport; nat->nat_nsport = sport; ((tcphdr_t *)fin->fin_dp)->th_dport = nport; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; nat->nat_nicmpid = nport; } return move; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_add */ /* Returns: nat_t* - NULL == failure to create new NAT structure, */ /* else pointer to new NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* natsave(I) - pointer to where to store NAT struct pointer */ /* flags(I) - flags describing the current packet */ /* direction(I) - direction of packet (in/out) */ /* Write Lock: ipf_nat */ /* */ /* Attempts to create a new NAT entry. Does not actually change the packet */ /* in any way. */ /* */ /* This function is in three main parts: (1) deal with creating a new NAT */ /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ /* and (3) building that structure and putting it into the NAT table(s). */ /* */ /* NOTE: natsave should NOT be used to point back to an ipstate_t struct */ /* as it can result in memory being corrupted. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_add(fin, np, natsave, flags, direction) fr_info_t *fin; ipnat_t *np; nat_t **natsave; u_int flags; int direction; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm = NULL; nat_t *nat, *natl; natstat_t *nsp; u_int nflags; natinfo_t ni; int move; nsp = &softn->ipf_nat_stats; if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > softn->ipf_nat_table_wm_high) { softn->ipf_nat_doflush = 1; } if (nsp->ns_active >= softn->ipf_nat_table_max) { NBUMPSIDED(fin->fin_out, ns_table_max); DT2(ns_table_max, nat_stat_t *, nsp, ipf_nat_softc_t *, softn); return NULL; } move = 1; nflags = np->in_flags & flags; nflags &= NAT_FROMRULE; ni.nai_np = np; ni.nai_dport = 0; ni.nai_sport = 0; /* Give me a new nat */ KMALLOC(nat, nat_t *); if (nat == NULL) { DT(ns_memfail); NBUMPSIDED(fin->fin_out, ns_memfail); /* * Try to automatically tune the max # of entries in the * table allowed to be less than what will cause kmem_alloc() * to fail and try to eliminate panics due to out of memory * conditions arising. */ if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && (nsp->ns_active > 100)) { softn->ipf_nat_table_max = nsp->ns_active - 100; printf("table_max reduced to %d\n", softn->ipf_nat_table_max); } return NULL; } if (flags & IPN_ICMPQUERY) { /* * In the ICMP query NAT code, we translate the ICMP id fields * to make them unique. This is indepedent of the ICMP type * (e.g. in the unlikely event that a host sends an echo and * an tstamp request with the same id, both packets will have * their ip address/id field changed in the same way). */ /* The icmp_id field is used by the sender to identify the * process making the icmp request. (the receiver justs * copies it back in its response). So, it closely matches * the concept of source port. We overlay sport, so we can * maximally reuse the existing code. */ ni.nai_sport = fin->fin_data[1]; ni.nai_dport = 0; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; nat->nat_redir = np->in_redir; nat->nat_dir = direction; nat->nat_pr[0] = fin->fin_p; nat->nat_pr[1] = fin->fin_p; /* * Search the current table for a match and create a new mapping * if there is none found. */ if (np->in_redir & NAT_DIVERTUDP) { move = ipf_nat_newdivert(fin, nat, &ni); } else if (np->in_redir & NAT_REWRITE) { move = ipf_nat_newrewrite(fin, nat, &ni); } else if (direction == NAT_OUTBOUND) { /* * We can now arrange to call this for the same connection * because ipf_nat_new doesn't protect the code path into * this function. */ natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat_newmap(fin, nat, &ni); } else { /* * NAT_INBOUND is used for redirects rules */ natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat_newrdr(fin, nat, &ni); } if (move == -1) goto badnat; np = ni.nai_np; nat->nat_mssclamp = np->in_mssclamp; nat->nat_me = natsave; nat->nat_fr = fin->fin_fr; nat->nat_rev = fin->fin_rev; nat->nat_ptr = np; nat->nat_dlocal = np->in_dlocal; if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { if (ipf_proxy_new(fin, nat) == -1) { NBUMPSIDED(fin->fin_out, ns_appr_fail); DT3(ns_appr_fail, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); goto badnat; } } nat->nat_ifps[0] = np->in_ifps[0]; if (np->in_ifps[0] != NULL) { COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); } nat->nat_ifps[1] = np->in_ifps[1]; if (np->in_ifps[1] != NULL) { COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); } if (ipf_nat_finalise(fin, nat) == -1) { goto badnat; } np->in_use++; if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { ipf_nat_delrdr(softn, np); ipf_nat_addrdr(softn, np); } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { ipf_nat_delmap(softn, np); ipf_nat_addmap(softn, np); } } if (flags & SI_WILDP) nsp->ns_wilds++; nsp->ns_proto[nat->nat_pr[0]]++; goto done; badnat: DT3(ns_badnatnew, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); NBUMPSIDE(fin->fin_out, ns_badnatnew); if ((hm = nat->nat_hm) != NULL) ipf_nat_hostmapdel(softc, &hm); KFREE(nat); nat = NULL; done: if (nat != NULL && np != NULL) np->in_hits++; if (natsave != NULL) *natsave = nat; return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_finalise */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* Write Lock: ipf_nat */ /* */ /* This is the tail end of constructing a new NAT entry and is the same */ /* for both IPv4 and IPv6. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int ipf_nat_finalise(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; frentry_t *fr; u_32_t flags; #if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC) qpktinfo_t *qpi = fin->fin_qpi; #endif flags = nat->nat_flags; switch (nat->nat_pr[0]) { case IPPROTO_ICMP : sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); break; default : sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ ntohs(nat->nat_osport)); sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ ntohs(nat->nat_nsport)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ ntohs(nat->nat_odport)); sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ ntohs(nat->nat_ndport)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; } /* * Compute the partial checksum, just in case. * This is only ever placed into outbound packets so care needs * to be taken over which pair of addresses are used. */ if (nat->nat_dir == NAT_OUTBOUND) { sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); } else { sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); } sum1 += nat->nat_pr[1]; nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); nat->nat_v[0] = 4; nat->nat_v[1] = 4; if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } if ((nat->nat_flags & SI_CLONE) == 0) nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); if (ipf_nat_insert(softc, softn, nat) == 0) { if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, nat, NL_NEW); fr = nat->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } return 0; } NBUMPSIDED(fin->fin_out, ns_unfinalised); DT2(ns_unfinalised, fr_info_t *, fin, nat_t *, nat); /* * nat_insert failed, so cleanup time... */ if (nat->nat_sync != NULL) ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_insert */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* Insert a NAT entry into the hash tables for searching and add it to the */ /* list of active NAT entries. Adjust global counters when complete. */ /* ------------------------------------------------------------------------ */ int ipf_nat_insert(softc, softn, nat) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; nat_t *nat; { u_int hv0, hv1; u_int sp, dp; ipnat_t *in; int ret; /* * Try and return an error as early as possible, so calculate the hash * entry numbers first and then proceed. */ if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_osport; dp = nat->nat_odport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_oicmpid; } else { sp = 0; dp = 0; } hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); /* * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, * nat_odport, hv0 */ if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_nsport; dp = nat->nat_ndport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_nicmpid; } else { sp = 0; dp = 0; } hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); /* * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, * nat_ndport, hv1 */ } else { hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ } nat->nat_hv[0] = hv0; nat->nat_hv[1] = hv1; MUTEX_INIT(&nat->nat_lock, "nat entry lock"); in = nat->nat_ptr; nat->nat_ref = nat->nat_me ? 2 : 1; nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1], 4); } else if (in->in_ifnames[1] != -1) { char *name; name = in->in_names + in->in_ifnames[1]; if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], LIFNAMSIZ); nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = nat->nat_ifps[0]; } } if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } ret = ipf_nat_hashtab_add(softc, softn, nat); if (ret == -1) MUTEX_DESTROY(&nat->nat_lock); return ret; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hashtab_add */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* */ /* Handle the insertion of a NAT entry into the table/list. */ /* ------------------------------------------------------------------------ */ int ipf_nat_hashtab_add(softc, softn, nat) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; nat_t *nat; { nat_t **natp; u_int hv0; u_int hv1; hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { u_int swap; swap = hv0; hv0 = hv1; hv1 = swap; } if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= softn->ipf_nat_maxbucket) { DT1(ns_bucket_max_0, int, softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); NBUMPSIDE(0, ns_bucket_max); return -1; } if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= softn->ipf_nat_maxbucket) { DT1(ns_bucket_max_1, int, softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); NBUMPSIDE(1, ns_bucket_max); return -1; } /* * The ordering of operations in the list and hash table insertion * is very important. The last operation for each task should be * to update the top of the list, after all the "nexts" have been * done so that walking the list while it is being done does not * find strange pointers. * * Global list of NAT instances */ nat->nat_next = softn->ipf_nat_instances; nat->nat_pnext = &softn->ipf_nat_instances; if (softn->ipf_nat_instances) softn->ipf_nat_instances->nat_pnext = &nat->nat_next; softn->ipf_nat_instances = nat; /* * Inbound hash table. */ natp = &softn->ipf_nat_table[0][hv0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; if (*natp) { (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; } else { NBUMPSIDE(0, ns_inuse); } *natp = nat; NBUMPSIDE(0, ns_bucketlen[hv0]); /* * Outbound hash table. */ natp = &softn->ipf_nat_table[1][hv1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; else { NBUMPSIDE(1, ns_inuse); } *natp = nat; NBUMPSIDE(1, ns_bucketlen[hv1]); ipf_nat_setqueue(softc, softn, nat); if (nat->nat_dir & NAT_OUTBOUND) { NBUMPSIDE(1, ns_added); } else { NBUMPSIDE(0, ns_added); } softn->ipf_nat_stats.ns_active++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmperrorlookup */ /* Returns: nat_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* dir(I) - direction of packet (in/out) */ /* */ /* Check if the ICMP error message is related to an existing TCP, UDP or */ /* ICMP query nat entry. It is assumed that the packet is already of the */ /* the required length. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_icmperrorlookup(fin, dir) fr_info_t *fin; int dir; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; int flags = 0, type, minlen; icmphdr_t *icmp, *orgicmp; nat_stat_side_t *nside; tcphdr_t *tcp = NULL; u_short data[2]; nat_t *nat; ip_t *oip; u_int p; icmp = fin->fin_dp; type = icmp->icmp_type; nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. Also, if it's not an error type, then return. */ if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { ATOMIC_INCL(nside->ns_icmp_basic); return NULL; } /* * Check packet size */ oip = (ip_t *)((char *)fin->fin_dp + 8); minlen = IP_HL(oip) << 2; if ((minlen < sizeof(ip_t)) || (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { ATOMIC_INCL(nside->ns_icmp_size); return NULL; } /* * Is the buffer big enough for all of it ? It's the size of the IP * header claimed in the encapsulated part which is of concern. It * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we * do the pullup early in ipf_check() and thus can't gaurantee it is * all here now. */ #ifdef ipf_nat_KERNEL { mb_t *m; m = fin->fin_m; # if defined(MENTAT) if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) { ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; } # else if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)fin->fin_ip + M_LEN(m)) { ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; } # endif } #endif if (fin->fin_daddr != oip->ip_src.s_addr) { ATOMIC_INCL(nside->ns_icmp_address); return NULL; } p = oip->ip_p; if (p == IPPROTO_TCP) flags = IPN_TCP; else if (p == IPPROTO_UDP) flags = IPN_UDP; else if (p == IPPROTO_ICMP) { orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); /* see if this is related to an ICMP query */ if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; fin->fin_data[0] = 0; fin->fin_data[1] = orgicmp->icmp_id; flags = IPN_ICMPERR|IPN_ICMPQUERY; /* * NOTE : dir refers to the direction of the original * ip packet. By definition the icmp error * message flows in the opposite direction. */ if (dir == NAT_INBOUND) nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, oip->ip_src); else nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, oip->ip_src); fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } } if (flags & IPN_TCPUDP) { minlen += 8; /* + 64bits of data to get ports */ /* TRACE (fin,minlen) */ if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { ATOMIC_INCL(nside->ns_icmp_short); return NULL; } data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); fin->fin_data[0] = ntohs(tcp->th_dport); fin->fin_data[1] = ntohs(tcp->th_sport); if (dir == NAT_INBOUND) { nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, oip->ip_src); } else { nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, oip->ip_src); } fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } if (dir == NAT_INBOUND) nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); else nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmperror */ /* Returns: nat_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* nflags(I) - NAT flags for this packet */ /* dir(I) - direction of packet (in/out) */ /* */ /* Fix up an ICMP packet which is an error message for an existing NAT */ /* session. This will correct both packet header data and checksums. */ /* */ /* This should *ONLY* be used for incoming ICMP error packets to make sure */ /* a NAT'd ICMP packet gets correctly recognised. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_icmperror(fin, nflags, dir) fr_info_t *fin; u_int *nflags; int dir; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd, sumd2; struct in_addr a1, a2, a3, a4; int flags, dlen, odst; icmphdr_t *icmp; u_short *csump; tcphdr_t *tcp; nat_t *nat; ip_t *oip; void *dp; if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { NBUMPSIDED(fin->fin_out, ns_icmp_short); return NULL; } /* * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. */ if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { NBUMPSIDED(fin->fin_out, ns_icmp_notfound); return NULL; } tcp = NULL; csump = NULL; flags = 0; sumd2 = 0; *nflags = IPN_ICMPERR; icmp = fin->fin_dp; oip = (ip_t *)&icmp->icmp_ip; dp = (((char *)oip) + (IP_HL(oip) << 2)); if (oip->ip_p == IPPROTO_TCP) { tcp = (tcphdr_t *)dp; csump = (u_short *)&tcp->th_sum; flags = IPN_TCP; } else if (oip->ip_p == IPPROTO_UDP) { udphdr_t *udp; udp = (udphdr_t *)dp; tcp = (tcphdr_t *)dp; csump = (u_short *)&udp->uh_sum; flags = IPN_UDP; } else if (oip->ip_p == IPPROTO_ICMP) flags = IPN_ICMPQUERY; dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the * IP address change as it will be modified again in ipf_nat_checkout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice * for the IP#. */ /* * Step 1 * Fix the IP addresses in the offending IP packet. You also need * to adjust the IP header checksum of that offending IP packet. * * Normally, you would expect that the ICMP checksum of the * ICMP error message needs to be adjusted as well for the * IP address change in oip. * However, this is a NOP, because the ICMP checksum is * calculated over the complete ICMP packet, which includes the * changed oip IP addresses and oip->ip_sum. However, these * two changes cancel each other out (if the delta for * the IP address is x, then the delta for ip_sum is minus x), * so no change in the icmp_cksum is necessary. * * Inbound ICMP * ------------ * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * Outbound ICMP * ------------- * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip */ if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { a1.s_addr = ntohl(nat->nat_osrcaddr); a4.s_addr = ntohl(oip->ip_src.s_addr); a3.s_addr = ntohl(nat->nat_odstaddr); a2.s_addr = ntohl(oip->ip_dst.s_addr); oip->ip_src.s_addr = htonl(a1.s_addr); oip->ip_dst.s_addr = htonl(a3.s_addr); odst = 1; } else { a1.s_addr = ntohl(nat->nat_ndstaddr); a2.s_addr = ntohl(oip->ip_dst.s_addr); a3.s_addr = ntohl(nat->nat_nsrcaddr); a4.s_addr = ntohl(oip->ip_src.s_addr); oip->ip_dst.s_addr = htonl(a3.s_addr); oip->ip_src.s_addr = htonl(a1.s_addr); odst = 0; } sum1 = 0; sum2 = 0; sumd = 0; CALC_SUMD(a2.s_addr, a3.s_addr, sum1); CALC_SUMD(a4.s_addr, a1.s_addr, sum2); sumd = sum2 + sum1; if (sumd != 0) ipf_fix_datacksum(&oip->ip_sum, sumd); sumd2 = sumd; sum1 = 0; sum2 = 0; /* * Fix UDP pseudo header checksum to compensate for the * IP address change. */ if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { u_32_t sum3, sum4, sumt; /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as * well, based on the NAT specification. Of course such * a change may be reflected in the ICMP checksum as well. * * Since the port fields are part of the TCP/UDP checksum * of the offending IP packet, you need to adjust that checksum * as well... except that the change in the port numbers should * be offset by the checksum change. However, the TCP/UDP * checksum will also need to change if there has been an * IP address change. */ if (odst == 1) { sum1 = ntohs(nat->nat_osport); sum4 = ntohs(tcp->th_sport); sum3 = ntohs(nat->nat_odport); sum2 = ntohs(tcp->th_dport); tcp->th_sport = htons(sum1); tcp->th_dport = htons(sum3); } else { sum1 = ntohs(nat->nat_ndport); sum2 = ntohs(tcp->th_dport); sum3 = ntohs(nat->nat_nsport); sum4 = ntohs(tcp->th_sport); tcp->th_dport = htons(sum3); tcp->th_sport = htons(sum1); } CALC_SUMD(sum4, sum1, sumt); sumd += sumt; CALC_SUMD(sum2, sum3, sumt); sumd += sumt; if (sumd != 0 || sumd2 != 0) { /* * At this point, sumd is the delta to apply to the * TCP/UDP header, given the changes in both the IP * address and the ports and sumd2 is the delta to * apply to the ICMP header, given the IP address * change delta that may need to be applied to the * TCP/UDP checksum instead. * * If we will both the IP and TCP/UDP checksums * then the ICMP checksum changes by the address * delta applied to the TCP/UDP checksum. If we * do not change the TCP/UDP checksum them we * apply the delta in ports to the ICMP checksum. */ if (oip->ip_p == IPPROTO_UDP) { if ((dlen >= 8) && (*csump != 0)) { ipf_fix_datacksum(csump, sumd); } else { CALC_SUMD(sum1, sum4, sumd2); CALC_SUMD(sum3, sum2, sumt); sumd2 += sumt; } } else if (oip->ip_p == IPPROTO_TCP) { if (dlen >= 18) { ipf_fix_datacksum(csump, sumd); } else { CALC_SUMD(sum1, sum4, sumd2); CALC_SUMD(sum3, sum2, sumt); sumd2 += sumt; } } if (sumd2 != 0) { sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); } } } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { icmphdr_t *orgicmp; /* * XXX - what if this is bogus hl and we go off the end ? * In this case, ipf_nat_icmperrorlookup() will have * returned NULL. */ orgicmp = (icmphdr_t *)dp; if (odst == 1) { if (orgicmp->icmp_id != nat->nat_osport) { /* * Fix ICMP checksum (of the offening ICMP * query packet) to compensate the change * in the ICMP id of the offending ICMP * packet. * * Since you modify orgicmp->icmp_id with * a delta (say x) and you compensate that * in origicmp->icmp_cksum with a delta * minus x, you don't have to adjust the * overall icmp->icmp_cksum */ sum1 = ntohs(orgicmp->icmp_id); sum2 = ntohs(nat->nat_oicmpid); CALC_SUMD(sum1, sum2, sumd); orgicmp->icmp_id = nat->nat_oicmpid; ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); } } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ } return nat; } /* * MAP-IN MAP-OUT RDR-IN RDR-OUT * osrc X == src == src X * odst X == dst == dst X * nsrc == dst X X == dst * ndst == src X X == src * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND */ /* * NB: these lookups don't lock access to the list, it assumed that it has * already been done! */ /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_inlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* mapdst(I) - destination IP address */ /* */ /* Lookup a nat entry based on the mapped destination ip address/port and */ /* real source address/port. We use this lookup when receiving a packet, */ /* we're looking for a table entry, based on the destination address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_inlookup(fin, flags, p, src, mapdst) fr_info_t *fin; u_int flags, p; struct in_addr src , mapdst; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; grehdr_t *gre; ipnat_t *ipn; u_int sflags; nat_t *nat; int nflags; u_32_t dst; void *ifp; u_int hv, rhv; ifp = fin->fin_ifp; gre = NULL; dst = mapdst.s_addr; sflags = flags & NAT_TCPUDPICMP; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMP : sport = 0; dport = fin->fin_data[1]; break; default : sport = 0; dport = 0; break; } if ((flags & SI_WILDP) != 0) goto find_in_wild_ports; rhv = NAT_HASH_FN(dst, dport, 0xffffffff); rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); hv = rhv % softn->ipf_nat_table_sz; nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, dport, src, sport, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : case NAT_DIVERTIN : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_osport != sport) continue; if (nat->nat_odport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; case NAT_DIVERTOUT : if (nat->nat_dlocal) continue; case NAT_OUTBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_dlocal) continue; if (nat->nat_dlocal) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; } if ((nat->nat_flags & IPN_TCPUDP) != 0) { ipn = nat->nat_ptr; if ((ipn != NULL) && (nat->nat_aps != NULL)) if (ipf_proxy_match(fin, nat) != 0) continue; } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_4(ifp); } return nat; } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_in_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); return NULL; } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); return NULL; } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN(dst, 0, 0xffffffff); hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, src, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != fin->fin_p) continue; switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) { case NAT_INBOUND : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst) continue; break; case NAT_OUTBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst) continue; break; } nflags = nat->nat_flags; if (!(nflags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, NAT_INBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nflags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else if (nat->nat_dir == NAT_OUTBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_4(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE(0, ns_lookup_miss); } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_tabmove */ /* Returns: Nil */ /* Parameters: softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* This function is only called for TCP/UDP NAT table entries where the */ /* original was placed in the table without hashing on the ports and we now */ /* want to include hashing on port numbers. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_tabmove(softn, nat) ipf_nat_softc_t *softn; nat_t *nat; { u_int hv0, hv1, rhv0, rhv1; natstat_t *nsp; nat_t **natp; if (nat->nat_flags & SI_CLONE) return; nsp = &softn->ipf_nat_stats; /* * Remove the NAT entry from the old location */ if (nat->nat_hnext[0]) nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; *nat->nat_phnext[0] = nat->nat_hnext[0]; nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] % softn->ipf_nat_table_sz]--; if (nat->nat_hnext[1]) nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; *nat->nat_phnext[1] = nat->nat_hnext[1]; nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] % softn->ipf_nat_table_sz]--; /* * Add into the NAT table in the new position */ rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, 0xffffffff); rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, 0xffffffff); hv0 = rhv0 % softn->ipf_nat_table_sz; hv1 = rhv1 % softn->ipf_nat_table_sz; if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { u_int swap; swap = hv0; hv0 = hv1; hv1 = swap; } /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ nat->nat_hv[0] = rhv0; natp = &softn->ipf_nat_table[0][hv0]; if (*natp) (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; nsp->ns_side[0].ns_bucketlen[hv0]++; nat->nat_hv[1] = rhv1; natp = &softn->ipf_nat_table[1][hv1]; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; nsp->ns_side[1].ns_bucketlen[hv1]++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_outlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* dst(I) - destination IP address */ /* rw(I) - 1 == write lock on held, 0 == read lock. */ /* */ /* Lookup a nat entry based on the source 'real' ip address/port and */ /* destination address/port. We use this lookup when sending a packet out, */ /* we're looking for a table entry, based on the source address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_outlookup(fin, flags, p, src, dst) fr_info_t *fin; u_int flags, p; struct in_addr src , dst; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; u_int sflags; ipnat_t *ipn; nat_t *nat; void *ifp; u_int hv; ifp = fin->fin_ifp; sflags = flags & IPN_TCPUDPICMP; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMP : sport = 0; dport = fin->fin_data[1]; break; default : sport = 0; dport = 0; break; } if ((flags & SI_WILDP) != 0) goto find_out_wild_ports; hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); nat = softn->ipf_nat_table[0][hv]; /* TRACE src, sport, dst, dport, hv, nat */ for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : case NAT_DIVERTIN : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst.s_addr) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; case NAT_OUTBOUND : case NAT_DIVERTOUT : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst.s_addr) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_odport != dport) continue; if (nat->nat_osport != sport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; } ipn = nat->nat_ptr; if ((ipn != NULL) && (nat->nat_aps != NULL)) if (ipf_proxy_match(fin, nat) != 0) continue; if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_4(ifp); } return nat; } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_out_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); return NULL; } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); return NULL; } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != fin->fin_p) continue; switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) { case NAT_INBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst.s_addr) continue; break; case NAT_OUTBOUND : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst.s_addr) continue; break; } if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, NAT_OUTBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nat->nat_flags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_OUTBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_4(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE(1, ns_lookup_miss); } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_lookupredir */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: np(I) - pointer to description of packet to find NAT table */ /* entry for. */ /* */ /* Lookup the NAT tables to search for a matching redirect */ /* The contents of natlookup_t should imitate those found in a packet that */ /* would be translated - ie a packet coming in for RDR or going out for MAP.*/ /* We can do the lookup in one of two ways, imitating an inbound or */ /* outbound packet. By default we assume outbound, unless IPN_IN is set. */ /* For IN, the fields are set as follows: */ /* nl_real* = source information */ /* nl_out* = destination information (translated) */ /* For an out packet, the fields are set like this: */ /* nl_in* = source information (untranslated) */ /* nl_out* = destination information (translated) */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_lookupredir(np) natlookup_t *np; { fr_info_t fi; nat_t *nat; bzero((char *)&fi, sizeof(fi)); if (np->nl_flags & IPN_IN) { fi.fin_data[0] = ntohs(np->nl_realport); fi.fin_data[1] = ntohs(np->nl_outport); } else { fi.fin_data[0] = ntohs(np->nl_inport); fi.fin_data[1] = ntohs(np->nl_outport); } if (np->nl_flags & IPN_TCP) fi.fin_p = IPPROTO_TCP; else if (np->nl_flags & IPN_UDP) fi.fin_p = IPPROTO_UDP; else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) fi.fin_p = IPPROTO_ICMP; /* * We can do two sorts of lookups: * - IPN_IN: we have the `real' and `out' address, look for `in'. * - default: we have the `in' and `out' address, look for `real'. */ if (np->nl_flags & IPN_IN) { if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, np->nl_realip, np->nl_outip))) { np->nl_inip = nat->nat_odstip; np->nl_inport = nat->nat_odport; } } else { /* * If nl_inip is non null, this is a lookup based on the real * ip address. Else, we use the fake. */ if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, np->nl_inip, np->nl_outip))) { if ((np->nl_flags & IPN_FINDFORWARD) != 0) { fr_info_t fin; bzero((char *)&fin, sizeof(fin)); fin.fin_p = nat->nat_pr[0]; fin.fin_data[0] = ntohs(nat->nat_ndport); fin.fin_data[1] = ntohs(nat->nat_nsport); if (ipf_nat_inlookup(&fin, np->nl_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip) != NULL) { np->nl_flags &= ~IPN_FINDFORWARD; } } np->nl_realip = nat->nat_odstip; np->nl_realport = nat->nat_odport; } } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_match */ /* Returns: int - 0 == no match, 1 == match */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* */ /* Pull the matching of a packet against a NAT rule out of that complex */ /* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_match(fin, np) fr_info_t *fin; ipnat_t *np; { ipf_main_softc_t *softc = fin->fin_main_soft; frtuc_t *ft; int match; match = 0; switch (np->in_osrcatype) { case FRI_NORMAL : match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); break; case FRI_LOOKUP : match = (*np->in_osrcfunc)(softc, np->in_osrcptr, 4, &fin->fin_saddr, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTSRC) != 0); if (match) return 0; match = 0; switch (np->in_odstatype) { case FRI_NORMAL : match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); break; case FRI_LOOKUP : match = (*np->in_odstfunc)(softc, np->in_odstptr, 4, &fin->fin_daddr, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTDST) != 0); if (match) return 0; ft = &np->in_tuc; if (!(fin->fin_flx & FI_TCPUDP) || (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { if (ft->ftu_scmp || ft->ftu_dcmp) return 0; return 1; } return ipf_tcpudpchk(&fin->fin_fi, ft); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_update */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* */ /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ /* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ /* */ /* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ /* already be set. */ /* ------------------------------------------------------------------------ */ void ipf_nat_update(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *ifq, *ifq2; ipftqent_t *tqe; ipnat_t *np = nat->nat_ptr; tqe = &nat->nat_tqe; ifq = tqe->tqe_ifq; /* * We allow over-riding of NAT timeouts from NAT rules, even for * TCP, however, if it is TCP and there is no rule timeout set, * then do not update the timeout here. */ if (np != NULL) { np->in_bytes[fin->fin_rev] += fin->fin_plen; ifq2 = np->in_tqehead[fin->fin_rev]; } else { ifq2 = NULL; } if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, 0, 2); } else { if (ifq2 == NULL) { if (nat->nat_pr[0] == IPPROTO_UDP) ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : &softn->ipf_nat_udptq; else if (nat->nat_pr[0] == IPPROTO_ICMP || nat->nat_pr[0] == IPPROTO_ICMPV6) ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: &softn->ipf_nat_icmptq; else ifq2 = &softn->ipf_nat_iptq; } ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_checkout */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an outcoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int ipf_nat_checkout(fin, passp) fr_info_t *fin; u_32_t *passp; { ipnat_t *np = NULL, *npnext; struct ifnet *ifp, *sifp; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; icmphdr_t *icmp = NULL; tcphdr_t *tcp = NULL; int rval, natfailed; u_int nflags = 0; u_32_t ipa, iph; int natadd = 1; frentry_t *fr; nat_t *nat; if (fin->fin_v == 6) { #ifdef USE_INET6 return ipf_nat6_checkout(fin, passp); #else return 0; #endif } softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if (softn->ipf_nat_lock != 0) return 0; if (softn->ipf_nat_stats.ns_rules == 0 && softn->ipf_nat_instances == NULL) return 0; natfailed = 0; fr = fin->fin_fr; sifp = fin->fin_ifp; if (fr != NULL) { ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; if ((ifp != NULL) && (ifp != (void *)-1)) fin->fin_ifp = ifp; } ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMP : icmp = fin->fin_dp; /* * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) nflags = IPN_ICMPQUERY; break; default : break; } if ((nflags & IPN_TCPUDP)) tcp = fin->fin_dp; } ipa = fin->fin_saddr; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, msk, nmsk = 0; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = softn->ipf_nat_map_active_masks[nmsk]; iph = ipa & msk; hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); retry_roundrobin: for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { npnext = np->in_mnext; if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) continue; if (np->in_v[0] != 4) continue; if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat_match(fin, np)) { case 0 : continue; case -1 : rval = -3; goto outmatchfail; case 1 : default : break; } } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) continue; if ((fr != NULL) && !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) continue; if (np->in_plabel != -1) { if (((np->in_flags & IPN_FILTER) == 0) && (np->in_odport != fin->fin_data[1])) continue; if (ipf_proxy_ok(fin, tcp, np) == 0) continue; } if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); /* * If we've matched a round-robin rule but it has * moved in the list since we got it, start over as * this is now no longer correct. */ if (npnext != np->in_mnext) { if ((np->in_flags & IPN_ROUNDR) != 0) { MUTEX_EXIT(&softn->ipf_nat_new); goto retry_roundrobin; } npnext = np->in_mnext; } nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { natfailed = 0; break; } natfailed = -2; } if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { nmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat_out(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[1] += fin->fin_plen; nat->nat_pkts[1]++; fin->fin_pktnum = nat->nat_pkts[1]; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; outmatchfail: RWLOCK_EXIT(&softc->ipf_nat); switch (rval) { case -3 : /* ipf_nat_match() failure */ /* FALLTHROUGH */ case -2 : /* retry_roundrobin loop failure */ /* FALLTHROUGH */ case -1 : /* proxy failure detected by ipf_nat_out() */ if (passp != NULL) { DT2(frb_natv4out, fr_info_t *, fin, int, rval); NBUMPSIDED(1, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV4; } fin->fin_flx |= FI_BADNAT; NBUMPSIDED(1, ns_badnat); rval = -1; /* We only return -1 on error. */ break; case 0 : NBUMPSIDE(1, ns_ignored); break; case 1 : NBUMPSIDE(1, ns_translated); break; } fin->fin_ifp = sifp; return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_out */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* */ /* Translate a packet coming "out" on an interface. */ /* ------------------------------------------------------------------------ */ int ipf_nat_out(fin, nat, natadd, nflags) fr_info_t *fin; nat_t *nat; int natadd; u_32_t nflags; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; icmphdr_t *icmp; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; icmp = NULL; np = nat->nat_ptr; if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. * This is only done for STREAMS based IP implementations where the * checksum has already been calculated by IP. In all other cases, * IPFilter is called before the checksum needs calculating so there * is no call to modify whatever is in the header now. */ if (nflags == IPN_ICMPERR) { u_32_t s1, s2, sumd, msumd; s1 = LONG_SUM(ntohl(fin->fin_saddr)); if (nat->nat_dir == NAT_OUTBOUND) { s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); } else { s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); } CALC_SUMD(s1, s2, sumd); msumd = sumd; s1 = LONG_SUM(ntohl(fin->fin_daddr)); if (nat->nat_dir == NAT_OUTBOUND) { s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); } else { s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); } CALC_SUMD(s1, s2, sumd); msumd += sumd; ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); } #if !defined(_KERNEL) || defined(MENTAT) || \ defined(BRIDGE_IPF) || defined(__FreeBSD__) else { /* * Strictly speaking, this isn't necessary on BSD * kernels because they do checksum calculation after * this code has run BUT if ipfilter is being used * to do NAT as a bridge, that code doesn't exist. */ switch (nat->nat_dir) { case NAT_OUTBOUND : ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, &fin->fin_ip->ip_sum, nat->nat_ipsumd, 0); break; case NAT_INBOUND : ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, &fin->fin_ip->ip_sum, nat->nat_ipsumd, 0); break; default : break; } } #endif /* * Address assignment is after the checksum modification because * we are using the address in the packet for determining the * correct checksum offset (the ICMP error could be coming from * anyone...) */ switch (nat->nat_dir) { case NAT_OUTBOUND : fin->fin_ip->ip_src = nat->nat_nsrcip; fin->fin_saddr = nat->nat_nsrcaddr; fin->fin_ip->ip_dst = nat->nat_ndstip; fin->fin_daddr = nat->nat_ndstaddr; break; case NAT_INBOUND : fin->fin_ip->ip_src = nat->nat_odstip; fin->fin_saddr = nat->nat_ndstaddr; fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_nsrcaddr; break; case NAT_DIVERTIN : { mb_t *m; skip = ipf_nat_decap(fin, nat); if (skip <= 0) { NBUMPSIDED(1, ns_decap_fail); return -1; } m = fin->fin_m; #if defined(MENTAT) && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); MUTEX_EXIT(&nat->nat_lock); fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; /* NOTREACHED */ } case NAT_DIVERTOUT : { u_32_t s1, s2, sumd; udphdr_t *uh; ip_t *ip; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDED(1, ns_divert_dup); return -1; } ip = MTOD(m, ip_t *); ip_fillid(ip); s2 = ntohs(ip->ip_id); s1 = ip->ip_len; ip->ip_len = ntohs(ip->ip_len); ip->ip_len += fin->fin_plen; ip->ip_len = htons(ip->ip_len); s2 += ntohs(ip->ip_len); CALC_SUMD(s1, s2, sumd); uh = (udphdr_t *)(ip + 1); uh->uh_ulen += fin->fin_plen; uh->uh_ulen = htons(uh->uh_ulen); #if !defined(_KERNEL) || defined(MENTAT) || \ defined(BRIDGE_IPF) || defined(__FreeBSD__) ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); #endif PREP_MB_T(fin, m); fin->fin_src = ip->ip_src; fin->fin_dst = ip->ip_dst; fin->fin_ip = ip; fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } default : break; } if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { u_short *csump; if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; switch (nat->nat_dir) { case NAT_OUTBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_INBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; icmp->icmp_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); /* * The above comments do not hold for layer 4 (or higher) * checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_outcksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); else ipf_fix_incksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a redirect rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if ((np != NULL) && (np->in_apr != NULL)) { i = ipf_proxy_check(fin, nat); if (i == 0) { i = 1; } else if (i == -1) { NBUMPSIDED(1, ns_ipf_proxy_fail); } } else { i = 1; } fin->fin_flx |= FI_NATED; return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_checkin */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an incoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int ipf_nat_checkin(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc; ipf_nat_softc_t *softn; u_int nflags, natadd; ipnat_t *np, *npnext; int rval, natfailed; struct ifnet *ifp; struct in_addr in; icmphdr_t *icmp; tcphdr_t *tcp; u_short dport; nat_t *nat; u_32_t iph; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if (softn->ipf_nat_lock != 0) return 0; if (softn->ipf_nat_stats.ns_rules == 0 && softn->ipf_nat_instances == NULL) return 0; tcp = NULL; icmp = NULL; dport = 0; natadd = 1; nflags = 0; natfailed = 0; ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMP : icmp = fin->fin_dp; /* * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) { nflags = IPN_ICMPQUERY; dport = icmp->icmp_id; } break; default : break; } if ((nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; dport = fin->fin_data[1]; } } in = fin->fin_dst; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, fin->fin_src, in))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, msk, rmsk = 0; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = softn->ipf_nat_rdr_active_masks[rmsk]; iph = in.s_addr & msk; hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); retry_roundrobin: /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { npnext = np->in_rnext; if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) continue; if (np->in_v[0] != 4) continue; if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat_match(fin, np)) { case 0 : continue; case -1 : rval = -3; goto inmatchfail; case 1 : default : break; } } else { if ((in.s_addr & np->in_odstmsk) != np->in_odstaddr) continue; if (np->in_odport && ((np->in_dtop < dport) || (dport < np->in_odport))) continue; } if (np->in_plabel != -1) { if (!ipf_proxy_ok(fin, tcp, np)) { continue; } } if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); /* * If we've matched a round-robin rule but it has * moved in the list since we got it, start over as * this is now no longer correct. */ if (npnext != np->in_rnext) { if ((np->in_flags & IPN_ROUNDR) != 0) { MUTEX_EXIT(&softn->ipf_nat_new); goto retry_roundrobin; } npnext = np->in_rnext; } nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { natfailed = 0; break; } natfailed = -2; } if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { rmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat_in(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[0] += fin->fin_plen; nat->nat_pkts[0]++; fin->fin_pktnum = nat->nat_pkts[0]; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; inmatchfail: RWLOCK_EXIT(&softc->ipf_nat); switch (rval) { case -3 : /* ipf_nat_match() failure */ /* FALLTHROUGH */ case -2 : /* retry_roundrobin loop failure */ /* FALLTHROUGH */ case -1 : /* proxy failure detected by ipf_nat_in() */ if (passp != NULL) { DT2(frb_natv4in, fr_info_t *, fin, int, rval); NBUMPSIDED(0, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV4; } fin->fin_flx |= FI_BADNAT; NBUMPSIDED(0, ns_badnat); rval = -1; /* We only return -1 on error. */ break; case 0 : NBUMPSIDE(0, ns_ignored); break; case 1 : NBUMPSIDE(0, ns_translated); break; } return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_in */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* Locks Held: ipf_nat(READ) */ /* */ /* Translate a packet coming "in" on an interface. */ /* ------------------------------------------------------------------------ */ int ipf_nat_in(fin, nat, natadd, nflags) fr_info_t *fin; nat_t *nat; int natadd; u_32_t nflags; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sumd, ipsumd, sum1, sum2; icmphdr_t *icmp; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (np != NULL) { if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a map rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if (np->in_apr != NULL) { i = ipf_proxy_check(fin, nat); if (i == -1) { NBUMPSIDED(0, ns_ipf_proxy_fail); return -1; } } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); ipsumd = nat->nat_ipsumd; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. * Why only do this for some platforms on inbound packets ? * Because for those that it is done, IP processing is yet to happen * and so the IPv4 header checksum has not yet been evaluated. * Perhaps it should always be done for the benefit of things like * fast forwarding (so that it doesn't need to be recomputed) but with * header checksum offloading, perhaps it is a moot point. */ switch (nat->nat_dir) { case NAT_INBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip->ip_src = nat->nat_nsrcip; fin->fin_saddr = nat->nat_nsrcaddr; } else { sum1 = nat->nat_osrcaddr; sum2 = nat->nat_nsrcaddr; CALC_SUMD(sum1, sum2, sumd); ipsumd -= sumd; } fin->fin_ip->ip_dst = nat->nat_ndstip; fin->fin_daddr = nat->nat_ndstaddr; #if !defined(_KERNEL) || defined(MENTAT) ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); #endif break; case NAT_OUTBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip->ip_src = nat->nat_odstip; fin->fin_saddr = nat->nat_odstaddr; } else { sum1 = nat->nat_odstaddr; sum2 = nat->nat_ndstaddr; CALC_SUMD(sum1, sum2, sumd); ipsumd -= sumd; } fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_osrcaddr; #if !defined(_KERNEL) || defined(MENTAT) ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); #endif break; case NAT_DIVERTIN : { udphdr_t *uh; ip_t *ip; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDED(0, ns_divert_dup); return -1; } ip = MTOD(m, ip_t *); ip_fillid(ip); sum1 = ntohs(ip->ip_len); ip->ip_len = ntohs(ip->ip_len); ip->ip_len += fin->fin_plen; ip->ip_len = htons(ip->ip_len); uh = (udphdr_t *)(ip + 1); uh->uh_ulen += fin->fin_plen; uh->uh_ulen = htons(uh->uh_ulen); sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len); sum2 += ntohs(ip->ip_off) & IP_DF; CALC_SUMD(sum1, sum2, sumd); #if !defined(_KERNEL) || defined(MENTAT) ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); #endif PREP_MB_T(fin, m); fin->fin_ip = ip; fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } case NAT_DIVERTOUT : { mb_t *m; skip = ipf_nat_decap(fin, nat); if (skip <= 0) { NBUMPSIDED(0, ns_decap_fail); return -1; } m = fin->fin_m; #if defined(MENTAT) && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif ipf_nat_update(fin, nat); nflags &= ~IPN_TCPUDPICMP; fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; /* NOTREACHED */ } } if (nflags & IPN_TCPUDP) tcp = fin->fin_dp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { u_short *csump; if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { switch (nat->nat_dir) { case NAT_INBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_OUTBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; icmp->icmp_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); /* * The above comments do not hold for layer 4 (or higher) * checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); else ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); } } fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_proto */ /* Returns: u_short* - pointer to transport header checksum to update, */ /* NULL if the transport protocol is not recognised */ /* as needing a checksum update. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* nflags(I) - NAT flags set for this packet */ /* */ /* Return the pointer to the checksum field for each protocol so understood.*/ /* If support for making other changes to a protocol header is required, */ /* that is not strictly 'address' translation, such as clamping the MSS in */ /* TCP down to a specific value, then do it from here. */ /* ------------------------------------------------------------------------ */ u_short * ipf_nat_proto(fin, nat, nflags) fr_info_t *fin; nat_t *nat; u_int nflags; { icmphdr_t *icmp; u_short *csump; tcphdr_t *tcp; udphdr_t *udp; csump = NULL; if (fin->fin_out == 0) { fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); } else { fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); } switch (fin->fin_p) { case IPPROTO_TCP : tcp = fin->fin_dp; if ((nflags & IPN_TCP) != 0) csump = &tcp->th_sum; /* * Do a MSS CLAMPING on a SYN packet, * only deal IPv4 for now. */ if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); break; case IPPROTO_UDP : udp = fin->fin_dp; if ((nflags & IPN_UDP) != 0) { if (udp->uh_sum != 0) csump = &udp->uh_sum; } break; case IPPROTO_ICMP : icmp = fin->fin_dp; if ((nflags & IPN_ICMPQUERY) != 0) { if (icmp->icmp_cksum != 0) csump = &icmp->icmp_cksum; } break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : { struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; icmp6 = fin->fin_dp; if ((nflags & IPN_ICMPQUERY) != 0) { if (icmp6->icmp6_cksum != 0) csump = &icmp6->icmp6_cksum; } break; } #endif } return csump; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Check all of the timeout queues for entries at the top which need to be */ /* expired. */ /* ------------------------------------------------------------------------ */ void ipf_nat_expire(softc) ipf_main_softc_t *softc; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; int i; SPL_INT(s); SPL_NET(s); WRITE_ENTER(&softc->ipf_nat); for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (((ifq->ifq_flags & IFQF_DELETE) != 0) && (ifq->ifq_ref == 0)) { ipf_freetimeoutqueue(softc, ifq); } } if (softn->ipf_nat_doflush != 0) { ipf_nat_extraflush(softc, softn, 2); softn->ipf_nat_doflush = 0; } RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_sync */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ifp(I) - pointer to network interface */ /* */ /* Walk through all of the currently active NAT sessions, looking for those */ /* which need to have their translated address updated. */ /* ------------------------------------------------------------------------ */ void ipf_nat_sync(softc, ifp) ipf_main_softc_t *softc; void *ifp; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; i6addr_t in; ipnat_t *n; nat_t *nat; void *ifp2; int idx; SPL_INT(s); if (softc->ipf_running <= 0) return; /* * Change IP addresses for NAT sessions for any protocol except TCP * since it will break the TCP connection anyway. The only rules * which will get changed are those which are "map ... -> 0/32", * where the rule specifies the address is taken from the interface. */ SPL_NET(s); WRITE_ENTER(&softc->ipf_nat); if (softc->ipf_running <= 0) { RWLOCK_EXIT(&softc->ipf_nat); return; } for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { if ((nat->nat_flags & IPN_TCP) != 0) continue; n = nat->nat_ptr; if (n != NULL) { if (n->in_v[1] == 4) { if (n->in_redir & NAT_MAP) { if ((n->in_nsrcaddr != 0) || (n->in_nsrcmsk != 0xffffffff)) continue; } else if (n->in_redir & NAT_REDIRECT) { if ((n->in_ndstaddr != 0) || (n->in_ndstmsk != 0xffffffff)) continue; } } #ifdef USE_INET6 if (n->in_v[1] == 4) { if (n->in_redir & NAT_MAP) { if (!IP6_ISZERO(&n->in_nsrcaddr) || !IP6_ISONES(&n->in_nsrcmsk)) continue; } else if (n->in_redir & NAT_REDIRECT) { if (!IP6_ISZERO(&n->in_ndstaddr) || !IP6_ISONES(&n->in_ndstmsk)) continue; } } #endif } if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || (ifp == nat->nat_ifps[1]))) { nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], nat->nat_v[0]); if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], nat->nat_v[1]); } else { nat->nat_ifps[1] = nat->nat_ifps[0]; } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } ifp2 = nat->nat_ifps[0]; if (ifp2 == NULL) continue; /* * Change the map-to address to be the same as the * new one. */ sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, &in, NULL) != -1) { if (nat->nat_v[0] == 4) nat->nat_nsrcip = in.in4; } sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); if (sum1 == sum2) continue; /* * Readjust the checksum adjustment to take into * account the new IP#. */ CALC_SUMD(sum1, sum2, sumd); /* XXX - dont change for TCP when solaris does * hardware checksumming. */ sumd += nat->nat_sumd[0]; nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); nat->nat_sumd[1] = nat->nat_sumd[0]; } } for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { char *base = n->in_names; if ((ifp == NULL) || (n->in_ifps[0] == ifp)) n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], n->in_v[0]); if ((ifp == NULL) || (n->in_ifps[1] == ifp)) n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], n->in_v[1]); if (n->in_redir & NAT_REDIRECT) idx = 1; else idx = 0; if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && (n->in_ifps[idx] != NULL && n->in_ifps[idx] != (void *)-1)) { ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 0, n->in_ifps[idx]); } } RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmpquerytype */ /* Returns: int - 1 == success, 0 == failure */ /* Parameters: icmptype(I) - ICMP type number */ /* */ /* Tests to see if the ICMP type number passed is a query/response type or */ /* not. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_icmpquerytype(icmptype) int icmptype; { /* * For the ICMP query NAT code, it is essential that both the query * and the reply match on the NAT rule. Because the NAT structure * does not keep track of the icmptype, and a single NAT structure * is used for all icmp types with the same src, dest and id, we * simply define the replies as queries as well. The funny thing is, * altough it seems silly to call a reply a query, this is exactly * as it is defined in the IPv4 specification */ switch (icmptype) { case ICMP_ECHOREPLY: case ICMP_ECHO: /* route advertisement/solicitation is currently unsupported: */ /* it would require rewriting the ICMP data section */ case ICMP_TSTAMP: case ICMP_TSTAMPREPLY: case ICMP_IREQ: case ICMP_IREQREPLY: case ICMP_MASKREQ: case ICMP_MASKREPLY: return 1; default: return 0; } } /* ------------------------------------------------------------------------ */ /* Function: nat_log */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* action(I) - action related to NAT structure being performed */ /* */ /* Creates a NAT log entry. */ /* ------------------------------------------------------------------------ */ void ipf_nat_log(softc, softn, nat, action) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; struct nat *nat; u_int action; { #ifdef IPFILTER_LOG # ifndef LARGE_NAT struct ipnat *np; int rulen; # endif struct natlog natl; void *items[1]; size_t sizes[1]; int types[1]; bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, sizeof(natl.nl_osrcip)); bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, sizeof(natl.nl_nsrcip)); bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, sizeof(natl.nl_odstip)); bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, sizeof(natl.nl_ndstip)); natl.nl_bytes[0] = nat->nat_bytes[0]; natl.nl_bytes[1] = nat->nat_bytes[1]; natl.nl_pkts[0] = nat->nat_pkts[0]; natl.nl_pkts[1] = nat->nat_pkts[1]; natl.nl_odstport = nat->nat_odport; natl.nl_osrcport = nat->nat_osport; natl.nl_nsrcport = nat->nat_nsport; natl.nl_ndstport = nat->nat_ndport; natl.nl_p[0] = nat->nat_pr[0]; natl.nl_p[1] = nat->nat_pr[1]; natl.nl_v[0] = nat->nat_v[0]; natl.nl_v[1] = nat->nat_v[1]; natl.nl_type = nat->nat_redir; natl.nl_action = action; natl.nl_rule = -1; bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], sizeof(nat->nat_ifnames[0])); bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], sizeof(nat->nat_ifnames[1])); # ifndef LARGE_NAT if (nat->nat_ptr != NULL) { for (rulen = 0, np = softn->ipf_nat_list; np != NULL; np = np->in_next, rulen++) if (np == nat->nat_ptr) { natl.nl_rule = rulen; break; } } # endif items[0] = &natl; sizes[0] = sizeof(natl); types[0] = 0; (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* inp(I) - pointer to pointer to NAT rule */ /* Write Locks: ipf_nat */ /* */ /* Dropping the refernce count for a rule means that whatever held the */ /* pointer to this rule (*inp) is no longer interested in it and when the */ /* reference count drops to zero, any resources allocated for the rule can */ /* be released and the rule itself free'd. */ /* ------------------------------------------------------------------------ */ void ipf_nat_rule_deref(softc, inp) ipf_main_softc_t *softc; ipnat_t **inp; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipnat_t *n; n = *inp; *inp = NULL; n->in_use--; if (n->in_use > 0) return; if (n->in_apr != NULL) ipf_proxy_deref(n->in_apr); ipf_nat_rule_fini(softc, n); if (n->in_redir & NAT_REDIRECT) { if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); } } if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); } } if (n->in_tqehead[0] != NULL) { if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { ipf_freetimeoutqueue(softc, n->in_tqehead[1]); } } if (n->in_tqehead[1] != NULL) { if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { ipf_freetimeoutqueue(softc, n->in_tqehead[1]); } } if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); } MUTEX_DESTROY(&n->in_lock); KFREES(n, n->in_size); #if SOLARIS && !defined(INSTANCES) if (softn->ipf_nat_stats.ns_rules == 0) pfil_delayed_copy = 1; #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* natp(I) - pointer to pointer to NAT table entry */ /* */ /* Decrement the reference counter for this NAT table entry and free it if */ /* there are no more things using it. */ /* */ /* IF nat_ref == 1 when this function is called, then we have an orphan nat */ /* structure *because* it only gets called on paths _after_ nat_ref has been*/ /* incremented. If nat_ref == 1 then we shouldn't decrement it here */ /* because nat_delete() will do that and send nat_ref to -1. */ /* */ /* Holding the lock on nat_lock is required to serialise nat_delete() being */ /* called from a NAT flush ioctl with a deref happening because of a packet.*/ /* ------------------------------------------------------------------------ */ void ipf_nat_deref(softc, natp) ipf_main_softc_t *softc; nat_t **natp; { nat_t *nat; nat = *natp; *natp = NULL; MUTEX_ENTER(&nat->nat_lock); if (nat->nat_ref > 1) { nat->nat_ref--; ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); return; } MUTEX_EXIT(&nat->nat_lock); WRITE_ENTER(&softc->ipf_nat); ipf_nat_delete(softc, nat, NL_EXPIRE); RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* Write Lock: ipf_nat */ /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_clone(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; frentry_t *fr; nat_t *clone; ipnat_t *np; KMALLOC(clone, nat_t *); if (clone == NULL) { NBUMPSIDED(fin->fin_out, ns_clone_nomem); return NULL; } bcopy((char *)nat, (char *)clone, sizeof(*clone)); MUTEX_NUKE(&clone->nat_lock); clone->nat_rev = fin->fin_rev; clone->nat_aps = NULL; /* * Initialize all these so that ipf_nat_delete() doesn't cause a crash. */ clone->nat_tqe.tqe_pnext = NULL; clone->nat_tqe.tqe_next = NULL; clone->nat_tqe.tqe_ifq = NULL; clone->nat_tqe.tqe_parent = clone; clone->nat_flags &= ~SI_CLONE; clone->nat_flags |= SI_CLONED; if (clone->nat_hm) clone->nat_hm->hm_ref++; if (ipf_nat_insert(softc, softn, clone) == -1) { KFREE(clone); NBUMPSIDED(fin->fin_out, ns_insert_fail); return NULL; } np = clone->nat_ptr; if (np != NULL) { if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, clone, NL_CLONE); np->in_use++; } fr = clone->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } /* * Because the clone is created outside the normal loop of things and * TCP has special needs in terms of state, initialise the timeout * state of the new NAT from here. */ if (clone->nat_pr[0] == IPPROTO_TCP) { (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, clone->nat_flags, 2); } clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, clone, NL_CLONE); return clone; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_wildok */ /* Returns: int - 1 == packet's ports match wildcards */ /* 0 == packet's ports don't match wildcards */ /* Parameters: nat(I) - NAT entry */ /* sport(I) - source port */ /* dport(I) - destination port */ /* flags(I) - wildcard flags */ /* dir(I) - packet direction */ /* */ /* Use NAT entry and packet direction to determine which combination of */ /* wildcard flags should be used. */ /* ------------------------------------------------------------------------ */ int ipf_nat_wildok(nat, sport, dport, flags, dir) nat_t *nat; int sport, dport, flags, dir; { /* * When called by dir is set to * nat_inlookup NAT_INBOUND (0) * nat_outlookup NAT_OUTBOUND (1) * * We simply combine the packet's direction in dir with the original * "intended" direction of that NAT entry in nat->nat_dir to decide * which combination of wildcard flags to allow. */ switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) { case 3: /* outbound packet / outbound entry */ if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return 1; break; case 2: /* outbound packet / inbound entry */ if (((nat->nat_osport == dport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == sport) || (flags & SI_W_DPORT))) return 1; break; case 1: /* inbound packet / outbound entry */ if (((nat->nat_osport == dport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == sport) || (flags & SI_W_DPORT))) return 1; break; case 0: /* inbound packet / inbound entry */ if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return 1; break; default: break; } return(0); } /* ------------------------------------------------------------------------ */ /* Function: nat_mssclamp */ /* Returns: Nil */ /* Parameters: tcp(I) - pointer to TCP header */ /* maxmss(I) - value to clamp the TCP MSS to */ /* fin(I) - pointer to packet information */ /* csump(I) - pointer to TCP checksum */ /* */ /* Check for MSS option and clamp it if necessary. If found and changed, */ /* then the TCP header checksum will be updated to reflect the change in */ /* the MSS. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_mssclamp(tcp, maxmss, fin, csump) tcphdr_t *tcp; u_32_t maxmss; fr_info_t *fin; u_short *csump; { u_char *cp, *ep, opt; int hlen, advance; u_32_t mss, sumd; hlen = TCP_OFF(tcp) << 2; if (hlen > sizeof(*tcp)) { cp = (u_char *)tcp + sizeof(*tcp); ep = (u_char *)tcp + hlen; while (cp < ep) { opt = cp[0]; if (opt == TCPOPT_EOL) break; else if (opt == TCPOPT_NOP) { cp++; continue; } if (cp + 1 >= ep) break; advance = cp[1]; if ((cp + advance > ep) || (advance <= 0)) break; switch (opt) { case TCPOPT_MAXSEG: if (advance != 4) break; mss = cp[2] * 256 + cp[3]; if (mss > maxmss) { cp[2] = maxmss / 256; cp[3] = maxmss & 0xff; CALC_SUMD(mss, maxmss, sumd); ipf_fix_outcksum(0, csump, sumd, 0); } break; default: /* ignore unknown options */ break; } cp += advance; } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setqueue */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I)- pointer to NAT structure */ /* Locks: ipf_nat (read or write) */ /* */ /* Put the NAT entry on its default queue entry, using rev as a helped in */ /* determining which queue it should be placed on. */ /* ------------------------------------------------------------------------ */ void ipf_nat_setqueue(softc, softn, nat) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; nat_t *nat; { ipftq_t *oifq, *nifq; int rev = nat->nat_rev; if (nat->nat_ptr != NULL) nifq = nat->nat_ptr->in_tqehead[rev]; else nifq = NULL; if (nifq == NULL) { switch (nat->nat_pr[0]) { case IPPROTO_UDP : nifq = &softn->ipf_nat_udptq; break; case IPPROTO_ICMP : nifq = &softn->ipf_nat_icmptq; break; case IPPROTO_TCP : nifq = softn->ipf_nat_tcptq + nat->nat_tqe.tqe_state[rev]; break; default : nifq = &softn->ipf_nat_iptq; break; } } oifq = nat->nat_tqe.tqe_ifq; /* * If it's currently on a timeout queue, move it from one queue to * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); else ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); return; } /* ------------------------------------------------------------------------ */ /* Function: nat_getnext */ /* Returns: int - 0 == ok, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter_t structure */ /* */ /* Fetch the next nat/ipnat structure pointer from the linked list and */ /* copy it out to the storage space pointed to by itp_data. The next item */ /* in the list to look at is put back in the ipftoken struture. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_getnext(softc, t, itp, objp) ipf_main_softc_t *softc; ipftoken_t *t; ipfgeniter_t *itp; ipfobj_t *objp; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm, *nexthm = NULL, zerohm; ipnat_t *ipn, *nextipnat = NULL, zeroipn; nat_t *nat, *nextnat = NULL, zeronat; int error = 0; void *nnext; if (itp->igi_nitems != 1) { IPFERROR(60075); return ENOSPC; } READ_ENTER(&softc->ipf_nat); switch (itp->igi_type) { case IPFGENITER_HOSTMAP : hm = t->ipt_data; if (hm == NULL) { nexthm = softn->ipf_hm_maplist; } else { nexthm = hm->hm_next; } if (nexthm != NULL) { ATOMIC_INC32(nexthm->hm_ref); t->ipt_data = nexthm; } else { bzero(&zerohm, sizeof(zerohm)); nexthm = &zerohm; t->ipt_data = NULL; } nnext = nexthm->hm_next; break; case IPFGENITER_IPNAT : ipn = t->ipt_data; if (ipn == NULL) { nextipnat = softn->ipf_nat_list; } else { nextipnat = ipn->in_next; } if (nextipnat != NULL) { ATOMIC_INC32(nextipnat->in_use); t->ipt_data = nextipnat; } else { bzero(&zeroipn, sizeof(zeroipn)); nextipnat = &zeroipn; t->ipt_data = NULL; } nnext = nextipnat->in_next; break; case IPFGENITER_NAT : nat = t->ipt_data; if (nat == NULL) { nextnat = softn->ipf_nat_instances; } else { nextnat = nat->nat_next; } if (nextnat != NULL) { MUTEX_ENTER(&nextnat->nat_lock); nextnat->nat_ref++; MUTEX_EXIT(&nextnat->nat_lock); t->ipt_data = nextnat; } else { bzero(&zeronat, sizeof(zeronat)); nextnat = &zeronat; t->ipt_data = NULL; } nnext = nextnat->nat_next; break; default : RWLOCK_EXIT(&softc->ipf_nat); IPFERROR(60055); return EINVAL; } RWLOCK_EXIT(&softc->ipf_nat); objp->ipfo_ptr = itp->igi_data; switch (itp->igi_type) { case IPFGENITER_HOSTMAP : error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); if (error != 0) { IPFERROR(60049); error = EFAULT; } if (hm != NULL) { WRITE_ENTER(&softc->ipf_nat); ipf_nat_hostmapdel(softc, &hm); RWLOCK_EXIT(&softc->ipf_nat); } break; case IPFGENITER_IPNAT : objp->ipfo_size = nextipnat->in_size; objp->ipfo_type = IPFOBJ_IPNAT; error = ipf_outobjk(softc, objp, nextipnat); if (ipn != NULL) { WRITE_ENTER(&softc->ipf_nat); ipf_nat_rule_deref(softc, &ipn); RWLOCK_EXIT(&softc->ipf_nat); } break; case IPFGENITER_NAT : objp->ipfo_size = sizeof(nat_t); objp->ipfo_type = IPFOBJ_NAT; error = ipf_outobjk(softc, objp, nextnat); if (nat != NULL) ipf_nat_deref(softc, &nat); break; } if (nnext == NULL) ipf_token_mark_complete(t); return error; } /* ------------------------------------------------------------------------ */ /* Function: nat_extraflush */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* which(I) - how to flush the active NAT table */ /* Write Locks: ipf_nat */ /* */ /* Flush nat tables. Three actions currently defined: */ /* which == 0 : flush all nat table entries */ /* which == 1 : flush TCP connections which have started to close but are */ /* stuck for some reason. */ /* which == 2 : flush TCP connections which have been idle for a long time, */ /* starting at > 4 days idle and working back in successive half-*/ /* days to at most 12 hours old. If this fails to free enough */ /* slots then work backwards in half hour slots to 30 minutes. */ /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_extraflush(softc, softn, which) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; int which; { nat_t *nat, **natp; ipftqent_t *tqn; ipftq_t *ifq; int removed; SPL_INT(s); removed = 0; SPL_NET(s); switch (which) { case 0 : softn->ipf_nat_stats.ns_flush_all++; /* * Style 0 flush removes everything... */ for (natp = &softn->ipf_nat_instances; ((nat = *natp) != NULL); ) { ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } break; case 1 : softn->ipf_nat_stats.ns_flush_closing++; /* * Since we're only interested in things that are closing, * we can start with the appropriate timeout queue. */ for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; tqn != NULL; ) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; if (nat->nat_pr[0] != IPPROTO_TCP || nat->nat_pr[1] != IPPROTO_TCP) break; ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } /* * Also need to look through the user defined queues. */ for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; tqn != NULL; ) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; if (nat->nat_pr[0] != IPPROTO_TCP || nat->nat_pr[1] != IPPROTO_TCP) continue; if ((nat->nat_tcpstate[0] > IPF_TCPS_ESTABLISHED) && (nat->nat_tcpstate[1] > IPF_TCPS_ESTABLISHED)) { ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } } break; /* * Args 5-11 correspond to flushing those particular states * for TCP connections. */ case IPF_TCPS_CLOSE_WAIT : case IPF_TCPS_FIN_WAIT_1 : case IPF_TCPS_CLOSING : case IPF_TCPS_LAST_ACK : case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : softn->ipf_nat_stats.ns_flush_state++; tqn = softn->ipf_nat_tcptq[which].ifq_head; while (tqn != NULL) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } break; default : if (which < 30) break; softn->ipf_nat_stats.ns_flush_timeout++; /* * Take a large arbitrary number to mean the number of seconds * for which which consider to be the maximum value we'll allow * the expiration to be. */ which = IPF_TTLVAL(which); for (natp = &softn->ipf_nat_instances; ((nat = *natp) != NULL); ) { if (softc->ipf_ticks - nat->nat_touched > which) { ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } else natp = &nat->nat_next; } break; } if (which != 2) { SPL_X(s); return removed; } softn->ipf_nat_stats.ns_flush_queue++; /* * Asked to remove inactive entries because the table is full, try * again, 3 times, if first attempt failed with a different criteria * each time. The order tried in must be in decreasing age. * Another alternative is to implement random drop and drop N entries * at random until N have been freed up. */ if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > IPF_TTLVAL(5)) { softn->ipf_nat_last_force_flush = softc->ipf_ticks; removed = ipf_queueflush(softc, ipf_nat_flush_entry, softn->ipf_nat_tcptq, softn->ipf_nat_utqe, &softn->ipf_nat_stats.ns_active, softn->ipf_nat_table_sz, softn->ipf_nat_table_wm_low); } SPL_X(s); return removed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_flush_entry */ /* Returns: 0 - always succeeds */ /* Parameters: softc(I) - pointer to soft context main structure */ /* entry(I) - pointer to NAT entry */ /* Write Locks: ipf_nat */ /* */ /* This function is a stepping stone between ipf_queueflush() and */ /* nat_dlete(). It is used so we can provide a uniform interface via the */ /* ipf_queueflush() function. Since the nat_delete() function returns void */ /* we translate that to mean it always succeeds in deleting something. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_flush_entry(softc, entry) ipf_main_softc_t *softc; void *entry; { ipf_nat_delete(softc, entry, NL_FLUSH); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_iterator */ /* Returns: int - 0 == ok, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter_t structure */ /* obj(I) - pointer to data description structure */ /* */ /* This function acts as a handler for the SIOCGENITER ioctls that use a */ /* generic structure to iterate through a list. There are three different */ /* linked lists of NAT related information to go through: NAT rules, active */ /* NAT mappings and the NAT fragment cache. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_iterator(softc, token, itp, obj) ipf_main_softc_t *softc; ipftoken_t *token; ipfgeniter_t *itp; ipfobj_t *obj; { int error; if (itp->igi_data == NULL) { IPFERROR(60052); return EFAULT; } switch (itp->igi_type) { case IPFGENITER_HOSTMAP : case IPFGENITER_IPNAT : case IPFGENITER_NAT : error = ipf_nat_getnext(softc, token, itp, obj); break; case IPFGENITER_NATFRAG : error = ipf_frag_nat_next(softc, token, itp); break; default : IPFERROR(60053); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setpending */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nat(I) - pointer to NAT structure */ /* Locks: ipf_nat (read or write) */ /* */ /* Put the NAT entry on to the pending queue - this queue has a very short */ /* lifetime where items are put that can't be deleted straight away because */ /* of locking issues but we want to delete them ASAP, anyway. In calling */ /* this function, it is assumed that the owner (if there is one, as shown */ /* by nat_me) is no longer interested in it. */ /* ------------------------------------------------------------------------ */ void ipf_nat_setpending(softc, nat) ipf_main_softc_t *softc; nat_t *nat; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *oifq; oifq = nat->nat_tqe.tqe_ifq; if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, &softn->ipf_nat_pending); else ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, &softn->ipf_nat_pending, nat); if (nat->nat_me != NULL) { *nat->nat_me = NULL; nat->nat_me = NULL; nat->nat_ref--; ASSERT(nat->nat_ref >= 0); } } /* ------------------------------------------------------------------------ */ /* Function: nat_newrewrite */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* This function is responsible for setting up an active NAT session where */ /* we are changing both the source and destination parameters at the same */ /* time. The loop in here works differently to elsewhere - each iteration */ /* is responsible for changing a single parameter that can be incremented. */ /* So one pass may increase the source IP#, next source port, next dest. IP#*/ /* and the last destination port for a total of 4 iterations to try each. */ /* This is done to try and exhaustively use the translation space available.*/ /* ------------------------------------------------------------------------ */ static int ipf_nat_newrewrite(fin, nat, nai) fr_info_t *fin; nat_t *nat; natinfo_t *nai; { int src_search = 1; int dst_search = 1; fr_info_t frnat; u_32_t flags; u_short swap; ipnat_t *np; nat_t *natl; int l = 0; int changed; natl = NULL; changed = -1; np = nai->nai_np; flags = nat->nat_flags; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_hm = NULL; do { changed = -1; /* TRACE (l, src_search, dst_search, np) */ DT4(ipf_nat_rewrite_1, int, l, int, src_search, int, dst_search, ipnat_t *, np); if ((src_search == 0) && (np->in_spnext == 0) && (dst_search == 0) && (np->in_dpnext == 0)) { if (l > 0) return -1; } /* * Find a new source address */ if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, &frnat.fin_saddr) == -1) { return -1; } if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (np->in_nsrcmsk == 0xffffffff) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (np->in_nsrcmsk != 0xffffffff) { if (np->in_stepnext == 0 && changed == -1) { np->in_snip++; np->in_stepnext++; changed = 0; } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_spnext != 0) frnat.fin_data[0] = np->in_spnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDSPORT) != 0) { np->in_stepnext = 2; } else if ((np->in_stepnext == 1) && (changed == -1) && (natl != NULL)) { np->in_spnext++; np->in_stepnext++; changed = 1; if (np->in_spnext > np->in_spmax) np->in_spnext = np->in_spmin; } } else { np->in_stepnext = 2; } np->in_stepnext &= 0x3; /* * Find a new destination address */ /* TRACE (fin, np, l, frnat) */ DT4(ipf_nat_rewrite_2, frinfo_t *, fin, ipnat_t *, np, int, l, frinfo_t *, &frnat); if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, &frnat.fin_daddr) == -1) return -1; if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (np->in_ndstmsk == 0xffffffff) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (np->in_ndstmsk != 0xffffffff) { if ((np->in_stepnext == 2) && (changed == -1) && (natl != NULL)) { changed = 2; np->in_stepnext++; np->in_dnip++; } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_dpnext != 0) frnat.fin_data[1] = np->in_dpnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDDPORT) != 0) { np->in_stepnext = 0; } else if (np->in_stepnext == 3 && changed == -1) { np->in_dpnext++; np->in_stepnext++; changed = 3; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } else { if (np->in_stepnext == 3) np->in_stepnext = 0; } /* TRACE (frnat) */ DT1(ipf_nat_rewrite_3, frinfo_t *, &frnat); /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. * * fin_data[] is swapped around because we are doing a * lookup of the packet is if it were moving in the opposite * direction of the one we are working with now. */ if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } if (fin->fin_out == 1) { natl = ipf_nat_inlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, frnat.fin_dst, frnat.fin_src); } else { natl = ipf_nat_outlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, frnat.fin_dst, frnat.fin_src); } if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } /* TRACE natl, in_stepnext, l */ DT3(ipf_nat_rewrite_2, nat_t *, natl, ipnat_t *, np , int, l); if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ return -1; np->in_stepnext &= 0x3; l++; changed = -1; } while (natl != NULL); nat->nat_osrcip = fin->fin_src; nat->nat_odstip = fin->fin_dst; nat->nat_nsrcip = frnat.fin_src; nat->nat_ndstip = frnat.fin_dst; if ((flags & IPN_TCPUDP) != 0) { nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } else if ((flags & IPN_ICMPQUERY) != 0) { nat->nat_oicmpid = fin->fin_data[1]; nat->nat_nicmpid = frnat.fin_data[1]; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: nat_newdivert */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* Create a new NAT divert session as defined by the NAT rule. This is */ /* somewhat different to other NAT session creation routines because we */ /* do not iterate through either port numbers or IP addresses, searching */ /* for a unique mapping, however, a complimentary duplicate check is made. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_newdivert(fin, nat, nai) fr_info_t *fin; nat_t *nat; natinfo_t *nai; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; fr_info_t frnat; ipnat_t *np; nat_t *natl; int p; np = nai->nai_np; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_pr[0] = 0; nat->nat_osrcaddr = fin->fin_saddr; nat->nat_odstaddr = fin->fin_daddr; frnat.fin_saddr = htonl(np->in_snip); frnat.fin_daddr = htonl(np->in_dnip); if ((nat->nat_flags & IPN_TCPUDP) != 0) { nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { nat->nat_oicmpid = fin->fin_data[1]; } if (np->in_redir & NAT_DIVERTUDP) { frnat.fin_data[0] = np->in_spnext; frnat.fin_data[1] = np->in_dpnext; frnat.fin_flx |= FI_TCPUDP; p = IPPROTO_UDP; } else { frnat.fin_flx &= ~FI_TCPUDP; p = IPPROTO_IPIP; } if (fin->fin_out == 1) { natl = ipf_nat_inlookup(&frnat, 0, p, frnat.fin_dst, frnat.fin_src); } else { natl = ipf_nat_outlookup(&frnat, 0, p, frnat.fin_dst, frnat.fin_src); } if (natl != NULL) { NBUMPSIDED(fin->fin_out, ns_divert_exist); DT3(ns_divert_exist, fr_info_t *, fin, nat_t *, nat, natinfo_t, nai); return -1; } nat->nat_nsrcaddr = frnat.fin_saddr; nat->nat_ndstaddr = frnat.fin_daddr; if ((nat->nat_flags & IPN_TCPUDP) != 0) { nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { nat->nat_nicmpid = frnat.fin_data[1]; } nat->nat_pr[fin->fin_out] = fin->fin_p; nat->nat_pr[1 - fin->fin_out] = p; if (np->in_redir & NAT_REDIRECT) nat->nat_dir = NAT_DIVERTIN; else nat->nat_dir = NAT_DIVERTOUT; return 0; } /* ------------------------------------------------------------------------ */ /* Function: nat_builddivertmp */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: softn(I) - pointer to NAT context structure */ /* np(I) - pointer to a NAT rule */ /* */ /* For divert rules, a skeleton packet representing what will be prepended */ /* to the real packet is created. Even though we don't have the full */ /* packet here, a checksum is calculated that we update later when we */ /* fill in the final details. At present a 0 checksum for UDP is being set */ /* here because it is expected that divert will be used for localhost. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_builddivertmp(softn, np) ipf_nat_softc_t *softn; ipnat_t *np; { udphdr_t *uh; size_t len; ip_t *ip; if ((np->in_redir & NAT_DIVERTUDP) != 0) len = sizeof(ip_t) + sizeof(udphdr_t); else len = sizeof(ip_t); ALLOC_MB_T(np->in_divmp, len); if (np->in_divmp == NULL) { NBUMPD(ipf_nat_stats, ns_divert_build); return -1; } /* * First, the header to get the packet diverted to the new destination */ ip = MTOD(np->in_divmp, ip_t *); IP_V_A(ip, 4); IP_HL_A(ip, 5); ip->ip_tos = 0; if ((np->in_redir & NAT_DIVERTUDP) != 0) ip->ip_p = IPPROTO_UDP; else ip->ip_p = IPPROTO_IPIP; ip->ip_ttl = 255; ip->ip_off = 0; ip->ip_sum = 0; ip->ip_len = htons(len); ip->ip_id = 0; ip->ip_src.s_addr = htonl(np->in_snip); ip->ip_dst.s_addr = htonl(np->in_dnip); ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); if (np->in_redir & NAT_DIVERTUDP) { uh = (udphdr_t *)(ip + 1); uh->uh_sum = 0; uh->uh_ulen = 8; uh->uh_sport = htons(np->in_spnext); uh->uh_dport = htons(np->in_dpnext); } return 0; } #define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) /* ------------------------------------------------------------------------ */ /* Function: nat_decap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* This function is responsible for undoing a packet's encapsulation in the */ /* reverse of an encap/divert rule. After removing the outer encapsulation */ /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ /* match the "new" packet as it may still be used by IPFilter elsewhere. */ /* We use "dir" here as the basis for some of the expectations about the */ /* outer header. If we return an error, the goal is to leave the original */ /* packet information undisturbed - this falls short at the end where we'd */ /* need to back a backup copy of "fin" - expensive. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_decap(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; char *hdr; int hlen; int skip; mb_t *m; if ((fin->fin_flx & FI_ICMPERR) != 0) { /* * ICMP packets don't get decapsulated, instead what we need * to do is change the ICMP reply from including (in the data * portion for errors) the encapsulated packet that we sent * out to something that resembles the original packet prior * to encapsulation. This isn't done here - all we're doing * here is changing the outer address to ensure that it gets * targetted back to the correct system. */ if (nat->nat_dir & NAT_OUTBOUND) { u_32_t sum1, sum2, sumd; sum1 = ntohl(fin->fin_daddr); sum2 = ntohl(nat->nat_osrcaddr); CALC_SUMD(sum1, sum2, sumd); fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_osrcaddr; #if !defined(_KERNEL) || defined(MENTAT) ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); #endif } return 0; } m = fin->fin_m; skip = fin->fin_hlen; switch (nat->nat_dir) { case NAT_DIVERTIN : case NAT_DIVERTOUT : if (fin->fin_plen < MINDECAP) return -1; skip += sizeof(udphdr_t); break; case NAT_ENCAPIN : case NAT_ENCAPOUT : if (fin->fin_plen < (skip + sizeof(ip_t))) return -1; break; default : return -1; /* NOTREACHED */ } /* * The aim here is to keep the original packet details in "fin" for * as long as possible so that returning with an error is for the * original packet and there is little undoing work to do. */ if (M_LEN(m) < skip + sizeof(ip_t)) { if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) return -1; } hdr = MTOD(fin->fin_m, char *); fin->fin_ip = (ip_t *)(hdr + skip); hlen = IP_HL(fin->fin_ip) << 2; if (ipf_pr_pullup(fin, skip + hlen) == -1) { NBUMPSIDED(fin->fin_out, ns_decap_pullup); return -1; } fin->fin_hlen = hlen; fin->fin_dlen -= skip; fin->fin_plen -= skip; fin->fin_ipoff += skip; if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { NBUMPSIDED(fin->fin_out, ns_decap_bad); return -1; } return skip; } /* ------------------------------------------------------------------------ */ /* Function: nat_nextaddr */ /* Returns: int - -1 == bad input (no new address), */ /* 0 == success and dst has new address */ /* Parameters: fin(I) - pointer to packet information */ /* na(I) - how to generate new address */ /* old(I) - original address being replaced */ /* dst(O) - where to put the new address */ /* Write Lock: ipf_nat */ /* */ /* This function uses the contents of the "na" structure, in combination */ /* with "old" to produce a new address to store in "dst". Not all of the */ /* possible uses of "na" will result in a new address. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_nextaddr(fin, na, old, dst) fr_info_t *fin; nat_addr_t *na; u_32_t *old, *dst; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t amin, amax, new; i6addr_t newip; int error; new = 0; amin = na->na_addr[0].in4.s_addr; switch (na->na_atype) { case FRI_RANGE : amax = na->na_addr[1].in4.s_addr; break; case FRI_NETMASKED : case FRI_DYNAMIC : case FRI_NORMAL : /* * Compute the maximum address by adding the inverse of the * netmask to the minimum address. */ amax = ~na->na_addr[1].in4.s_addr; amax |= amin; break; case FRI_LOOKUP : break; case FRI_BROADCAST : case FRI_PEERADDR : case FRI_NETWORK : default : DT4(ns_na_atype, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); return -1; } error = -1; if (na->na_atype == FRI_LOOKUP) { if (na->na_type == IPLT_DSTLIST) { error = ipf_dstlist_select_node(fin, na->na_ptr, dst, NULL); } else { NBUMPSIDE(fin->fin_out, ns_badnextaddr); DT4(ns_badnextaddr_1, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); } } else if (na->na_atype == IPLT_NONE) { /* * 0/0 as the new address means leave it alone. */ if (na->na_addr[0].in4.s_addr == 0 && na->na_addr[1].in4.s_addr == 0) { new = *old; /* * 0/32 means get the interface's address */ } else if (na->na_addr[0].in4.s_addr == 0 && na->na_addr[1].in4.s_addr == 0xffffffff) { if (ipf_ifpaddr(softc, 4, na->na_atype, fin->fin_ifp, &newip, NULL) == -1) { NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); DT4(ns_ifpaddrfail, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); return -1; } new = newip.in4.s_addr; } else { new = htonl(na->na_nextip); } *dst = new; error = 0; } else { NBUMPSIDE(fin->fin_out, ns_badnextaddr); DT4(ns_badnextaddr_2, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); } return error; } /* ------------------------------------------------------------------------ */ /* Function: nat_nextaddrinit */ /* Returns: int - 0 == success, else error number */ /* Parameters: softc(I) - pointer to soft context main structure */ /* na(I) - NAT address information for generating new addr*/ /* initial(I) - flag indicating if it is the first call for */ /* this "na" structure. */ /* ifp(I) - network interface to derive address */ /* information from. */ /* */ /* This function is expected to be called in two scenarious: when a new NAT */ /* rule is loaded into the kernel and when the list of NAT rules is sync'd */ /* up with the valid network interfaces (possibly due to them changing.) */ /* To distinguish between these, the "initial" parameter is used. If it is */ /* 1 then this indicates the rule has just been reloaded and 0 for when we */ /* are updating information. This difference is important because in */ /* instances where we are not updating address information associated with */ /* a network interface, we don't want to disturb what the "next" address to */ /* come out of ipf_nat_nextaddr() will be. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_nextaddrinit(softc, base, na, initial, ifp) ipf_main_softc_t *softc; char *base; nat_addr_t *na; int initial; void *ifp; { switch (na->na_atype) { case FRI_LOOKUP : if (na->na_subtype == 0) { na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, na->na_type, na->na_num, &na->na_func); } else if (na->na_subtype == 1) { na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, na->na_type, base + na->na_num, &na->na_func); } if (na->na_func == NULL) { IPFERROR(60060); return ESRCH; } if (na->na_ptr == NULL) { IPFERROR(60056); return ESRCH; } break; case FRI_DYNAMIC : case FRI_BROADCAST : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (ifp != NULL) (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, &na->na_addr[0], &na->na_addr[1]); break; case FRI_SPLIT : case FRI_RANGE : if (initial) na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); break; case FRI_NONE : na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; return 0; case FRI_NORMAL : na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; break; default : IPFERROR(60054); return EINVAL; } if (initial && (na->na_atype == FRI_NORMAL)) { if (na->na_addr[0].in4.s_addr == 0) { if ((na->na_addr[1].in4.s_addr == 0xffffffff) || (na->na_addr[1].in4.s_addr == 0)) { return 0; } } if (na->na_addr[1].in4.s_addr == 0xffffffff) { na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); } else { na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_matchflush */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to current NAT session */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_nat_matchflush(softc, softn, data) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; caddr_t data; { int *array, flushed, error; nat_t *nat, *natnext; ipfobj_t obj; error = ipf_matcharray_load(softc, data, &obj, &array); if (error != 0) return error; flushed = 0; for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { natnext = nat->nat_next; if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { ipf_nat_delete(softc, nat, NL_FLUSH); flushed++; } } obj.ipfo_retval = flushed; error = BCOPYOUT(&obj, data, sizeof(obj)); KFREES(array, array[0] * sizeof(*array)); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_matcharray */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_nat_matcharray(nat, array, ticks) nat_t *nat; int *array; u_long ticks; { int i, n, *x, e, p; e = 0; n = array[0]; x = array + 1; for (; n > 0; x += 3 + x[2]) { if (x[0] == IPF_EXP_END) break; e = 0; n -= x[2] + 3; if (n < 0) break; p = x[0] >> 16; if (p != 0 && p != nat->nat_pr[1]) break; switch (x[0]) { case IPF_EXP_IP_PR : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_pr[1] == x[i + 3]); } break; case IPF_EXP_IP_SRCADDR : if (nat->nat_v[0] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_osrcaddr & x[i + 4]) == x[i + 3]); } } if (nat->nat_v[1] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_nsrcaddr & x[i + 4]) == x[i + 3]); } } break; case IPF_EXP_IP_DSTADDR : if (nat->nat_v[0] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_odstaddr & x[i + 4]) == x[i + 3]); } } if (nat->nat_v[1] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_ndstaddr & x[i + 4]) == x[i + 3]); } } break; case IPF_EXP_IP_ADDR : for (i = 0; !e && i < x[2]; i++) { if (nat->nat_v[0] == 4) { e |= ((nat->nat_osrcaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[1] == 4) { e |= ((nat->nat_nsrcaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[0] == 4) { e |= ((nat->nat_odstaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[1] == 4) { e |= ((nat->nat_ndstaddr & x[i + 4]) == x[i + 3]); } } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (nat->nat_v[0] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_osrc6, x + i + 7, x + i + 3); } } if (nat->nat_v[1] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_nsrc6, x + i + 7, x + i + 3); } } break; case IPF_EXP_IP6_DSTADDR : if (nat->nat_v[0] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_odst6, x + i + 7, x + i + 3); } } if (nat->nat_v[1] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_ndst6, x + i + 7, x + i + 3); } } break; case IPF_EXP_IP6_ADDR : for (i = 0; !e && i < x[3]; i++) { if (nat->nat_v[0] == 6) { e |= IP6_MASKEQ(&nat->nat_osrc6, x + i + 7, x + i + 3); } if (nat->nat_v[0] == 6) { e |= IP6_MASKEQ(&nat->nat_odst6, x + i + 7, x + i + 3); } if (nat->nat_v[1] == 6) { e |= IP6_MASKEQ(&nat->nat_nsrc6, x + i + 7, x + i + 3); } if (nat->nat_v[1] == 6) { e |= IP6_MASKEQ(&nat->nat_ndst6, x + i + 7, x + i + 3); } } break; #endif case IPF_EXP_UDP_PORT : case IPF_EXP_TCP_PORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_nsport == x[i + 3]) || (nat->nat_ndport == x[i + 3]); } break; case IPF_EXP_UDP_SPORT : case IPF_EXP_TCP_SPORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_nsport == x[i + 3]); } break; case IPF_EXP_UDP_DPORT : case IPF_EXP_TCP_DPORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_ndport == x[i + 3]); } break; case IPF_EXP_TCP_STATE : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_tcpstate[0] == x[i + 3]) || (nat->nat_tcpstate[1] == x[i + 3]); } break; case IPF_EXP_IDLE_GT : e |= (ticks - nat->nat_touched > x[3]); break; } e ^= x[1]; if (!e) break; } return e; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_gettable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* data(I) - pointer to ioctl data */ /* */ /* This function handles ioctl requests for tables of nat information. */ /* At present the only table it deals with is the hash bucket statistics. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_gettable(softc, softn, data) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; char *data; { ipftable_t table; int error; error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return error; switch (table.ita_type) { case IPFTABLE_BUCKETS_NATIN : error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, table.ita_table, softn->ipf_nat_table_sz * sizeof(u_int)); break; case IPFTABLE_BUCKETS_NATOUT : error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, table.ita_table, softn->ipf_nat_table_sz * sizeof(u_int)); break; default : IPFERROR(60058); return EINVAL; } if (error != 0) { IPFERROR(60059); error = EFAULT; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_settimeout */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* Apply the timeout change to the NAT timeout queues. */ /* ------------------------------------------------------------------------ */ int ipf_nat_settimeout(softc, t, p) struct ipf_main_softc_s *softc; ipftuneable_t *t; ipftuneval_t *p; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; if (!strncmp(t->ipft_name, "tcp_", 4)) return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq); if (!strcmp(t->ipft_name, "udp_timeout")) { ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_timeout")) { ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "ip_timeout")) { ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); } else { IPFERROR(60062); return ESRCH; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rehash */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* To change the size of the basic NAT table, we need to first allocate the */ /* new tables (lest it fails and we've got nowhere to store all of the NAT */ /* sessions currently active) and then walk through the entire list and */ /* insert them into the table. There are two tables here: an inbound one */ /* and an outbound one. Each NAT entry goes into each table once. */ /* ------------------------------------------------------------------------ */ int ipf_nat_rehash(softc, t, p) ipf_main_softc_t *softc; ipftuneable_t *t; ipftuneval_t *p; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; nat_t **newtab[2], *nat, **natp; u_int *bucketlens[2]; u_int maxbucket; u_int newsize; int error; u_int hv; int i; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == softn->ipf_nat_table_sz) return 0; newtab[0] = NULL; newtab[1] = NULL; bucketlens[0] = NULL; bucketlens[1] = NULL; /* * 4 tables depend on the NAT table size: the inbound looking table, * the outbound lookup table and the hash chain length for each. */ KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); if (newtab[0] == NULL) { error = 60063; goto badrehash; } KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); if (newtab[1] == NULL) { error = 60064; goto badrehash; } KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); if (bucketlens[0] == NULL) { error = 60065; goto badrehash; } KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); if (bucketlens[1] == NULL) { error = 60066; goto badrehash; } /* * Recalculate the maximum length based on the new size. */ for (maxbucket = 0, i = newsize; i > 0; i >>= 1) maxbucket++; maxbucket *= 2; bzero((char *)newtab[0], newsize * sizeof(nat_t *)); bzero((char *)newtab[1], newsize * sizeof(nat_t *)); bzero((char *)bucketlens[0], newsize * sizeof(u_int)); bzero((char *)bucketlens[1], newsize * sizeof(u_int)); WRITE_ENTER(&softc->ipf_nat); if (softn->ipf_nat_table[0] != NULL) { KFREES(softn->ipf_nat_table[0], softn->ipf_nat_table_sz * sizeof(*softn->ipf_nat_table[0])); } softn->ipf_nat_table[0] = newtab[0]; if (softn->ipf_nat_table[1] != NULL) { KFREES(softn->ipf_nat_table[1], softn->ipf_nat_table_sz * sizeof(*softn->ipf_nat_table[1])); } softn->ipf_nat_table[1] = newtab[1]; if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; #ifdef USE_INET6 if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0]; if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1]; #endif softn->ipf_nat_maxbucket = maxbucket; softn->ipf_nat_table_sz = newsize; /* * Walk through the entire list of NAT table entries and put them * in the new NAT table, somewhere. Because we have a new table, * we need to restart the counter of how many chains are in use. */ softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; #ifdef USE_INET6 softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0; softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0; #endif for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { nat->nat_hnext[0] = NULL; nat->nat_phnext[0] = NULL; hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; natp = &softn->ipf_nat_table[0][hv]; if (*natp) { (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; } else { NBUMPSIDE(0, ns_inuse); } nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; NBUMPSIDE(0, ns_bucketlen[hv]); nat->nat_hnext[1] = NULL; nat->nat_phnext[1] = NULL; hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; natp = &softn->ipf_nat_table[1][hv]; if (*natp) { (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; } else { NBUMPSIDE(1, ns_inuse); } nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; NBUMPSIDE(1, ns_bucketlen[hv]); } RWLOCK_EXIT(&softc->ipf_nat); return 0; badrehash: if (bucketlens[1] != NULL) { KFREES(bucketlens[0], newsize * sizeof(u_int)); } if (bucketlens[0] != NULL) { KFREES(bucketlens[0], newsize * sizeof(u_int)); } if (newtab[0] != NULL) { KFREES(newtab[0], newsize * sizeof(nat_t *)); } if (newtab[1] != NULL) { KFREES(newtab[1], newsize * sizeof(nat_t *)); } IPFERROR(error); return ENOMEM; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rehash_rules */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* All of the NAT rules hang off of a hash table that is searched with a */ /* hash on address after the netmask is applied. There is a different table*/ /* for both inbound rules (rdr) and outbound (map.) The resizing will only */ /* affect one of these two tables. */ /* ------------------------------------------------------------------------ */ int ipf_nat_rehash_rules(softc, t, p) ipf_main_softc_t *softc; ipftuneable_t *t; ipftuneval_t *p; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipnat_t **newtab, *np, ***old, **npp; u_int newsize; u_int mask; u_int hv; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == *t->ipft_pint) return 0; /* * All inbound rules have the NAT_REDIRECT bit set in in_redir and * all outbound rules have either NAT_MAP or MAT_MAPBLK set. * This if statement allows for some more generic code to be below, * rather than two huge gobs of code that almost do the same thing. */ if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { old = &softn->ipf_nat_rdr_rules; mask = NAT_REDIRECT; } else { old = &softn->ipf_nat_map_rules; mask = NAT_MAP|NAT_MAPBLK; } KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); if (newtab == NULL) { IPFERROR(60067); return ENOMEM; } bzero((char *)newtab, newsize * sizeof(ipnat_t *)); WRITE_ENTER(&softc->ipf_nat); if (*old != NULL) { KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); } *old = newtab; *t->ipft_pint = newsize; for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { if ((np->in_redir & mask) == 0) continue; if (np->in_redir & NAT_REDIRECT) { np->in_rnext = NULL; hv = np->in_hv[0] % newsize; for (npp = newtab + hv; *npp != NULL; ) npp = &(*npp)->in_rnext; np->in_prnext = npp; *npp = np; } if (np->in_redir & NAT_MAP) { np->in_mnext = NULL; hv = np->in_hv[1] % newsize; for (npp = newtab + hv; *npp != NULL; ) npp = &(*npp)->in_mnext; np->in_pmnext = npp; *npp = np; } } RWLOCK_EXIT(&softc->ipf_nat); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmap_rehash */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* Allocate and populate a new hash table that will contain a reference to */ /* all of the active IP# translations currently in place. */ /* ------------------------------------------------------------------------ */ int ipf_nat_hostmap_rehash(softc, t, p) ipf_main_softc_t *softc; ipftuneable_t *t; ipftuneval_t *p; { ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm, **newtab; u_int newsize; u_int hv; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == *t->ipft_pint) return 0; KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); if (newtab == NULL) { IPFERROR(60068); return ENOMEM; } bzero((char *)newtab, newsize * sizeof(hostmap_t *)); WRITE_ENTER(&softc->ipf_nat); if (softn->ipf_hm_maptable != NULL) { KFREES(softn->ipf_hm_maptable, softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); } softn->ipf_hm_maptable = newtab; softn->ipf_nat_hostmap_sz = newsize; for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; hm->hm_hnext = softn->ipf_hm_maptable[hv]; hm->hm_phnext = softn->ipf_hm_maptable + hv; if (softn->ipf_hm_maptable[hv] != NULL) softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; softn->ipf_hm_maptable[hv] = hm; } RWLOCK_EXIT(&softc->ipf_nat); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_add_tq */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* ------------------------------------------------------------------------ */ ipftq_t * ipf_nat_add_tq(softc, ttl) ipf_main_softc_t *softc; int ttl; { ipf_nat_softc_t *softs = softc->ipf_nat_soft; return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_uncreate */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* This function is used to remove a NAT entry from the NAT table when we */ /* decide that the create was actually in error. It is thus assumed that */ /* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ /* with the translated packet (not the original), we have to reverse the */ /* lookup. Although doing the lookup is expensive (relatively speaking), it */ /* is not anticipated that this will be a frequent occurance for normal */ /* traffic patterns. */ /* ------------------------------------------------------------------------ */ void ipf_nat_uncreate(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; int nflags; nat_t *nat; switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; default : nflags = 0; break; } WRITE_ENTER(&softc->ipf_nat); if (fin->fin_out == 0) { nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_dst, fin->fin_src); } else { nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); } if (nat != NULL) { NBUMPSIDE(fin->fin_out, ns_uncreate[0]); ipf_nat_delete(softc, nat, NL_DESTROY); } else { NBUMPSIDE(fin->fin_out, ns_uncreate[1]); } RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_cmp_rules */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: n1(I) - first rule to compare */ /* n2(I) - first rule to compare */ /* */ /* Compare two rules using pointers to each rule. A straight bcmp will not */ /* work as some fields (such as in_dst, in_pkts) actually do change once */ /* the rule has been loaded into the kernel. Whilst this function returns */ /* various non-zero returns, they're strictly to aid in debugging. Use of */ /* this function should simply care if the result is zero or not. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_cmp_rules(n1, n2) ipnat_t *n1, *n2; { if (n1->in_size != n2->in_size) return 1; if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) return 2; if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) return 3; if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) return 5; if (n1->in_ndst.na_function != n2->in_ndst.na_function) return 6; if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, sizeof(n1->in_ndst.na_addr))) return 7; if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) return 8; if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) return 9; if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, sizeof(n1->in_nsrc.na_addr))) return 10; if (n1->in_odst.na_atype != n2->in_odst.na_atype) return 11; if (n1->in_odst.na_function != n2->in_odst.na_function) return 12; if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, sizeof(n1->in_odst.na_addr))) return 13; if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) return 14; if (n1->in_osrc.na_function != n2->in_osrc.na_function) return 15; if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, sizeof(n1->in_osrc.na_addr))) return 16; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_init */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - first rule to compare */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_nat_rule_init(softc, softn, n) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; ipnat_t *n; { int error = 0; if ((n->in_flags & IPN_SIPRANGE) != 0) n->in_nsrcatype = FRI_RANGE; if ((n->in_flags & IPN_DIPRANGE) != 0) n->in_ndstatype = FRI_RANGE; if ((n->in_flags & IPN_SPLIT) != 0) n->in_ndstatype = FRI_SPLIT; if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) n->in_spnext = n->in_spmin; if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { n->in_dpnext = n->in_dpmin; } else if (n->in_redir == NAT_REDIRECT) { n->in_dpnext = n->in_dpmin; } n->in_stepnext = 0; switch (n->in_v[0]) { case 4 : error = ipf_nat_ruleaddrinit(softc, softn, n); if (error != 0) return error; break; #ifdef USE_INET6 case 6 : error = ipf_nat6_ruleaddrinit(softc, softn, n); if (error != 0) return error; break; #endif default : break; } if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { /* * Prerecord whether or not the destination of the divert * is local or not to the interface the packet is going * to be sent out. */ n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], n->in_ifps[1], &n->in_ndstip6); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_fini */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* n(I) - rule to work on */ /* */ /* This function is used to release any objects that were referenced during */ /* the rule initialisation. This is useful both when free'ing the rule and */ /* when handling ioctls that need to initialise these fields but not */ /* actually use them after the ioctl processing has finished. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_rule_fini(softc, n) ipf_main_softc_t *softc; ipnat_t *n; { if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); if (n->in_divmp != NULL) FREE_MB_T(n->in_divmp); } diff --git a/sys/contrib/ipfilter/netinet/ip_nat.h b/sys/contrib/ipfilter/netinet/ip_nat.h index e726bc926937..bcec72f21f8b 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.h +++ b/sys/contrib/ipfilter/netinet/ip_nat.h @@ -1,767 +1,767 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_nat.h 1.5 2/4/96 * $FreeBSD$ * Id: ip_nat.h,v 2.90.2.20 2007/09/25 08:27:32 darrenr Exp $ */ #ifndef __IP_NAT_H__ #define __IP_NAT_H__ #ifndef SOLARIS # if defined(sun) && defined(__SVR4) # define SOLARIS 1 # else # define SOLARIS 0 # endif #endif #define SIOCADNAT _IOW('r', 60, struct ipfobj) #define SIOCRMNAT _IOW('r', 61, struct ipfobj) #define SIOCGNATS _IOWR('r', 62, struct ipfobj) #define SIOCGNATL _IOWR('r', 63, struct ipfobj) #define SIOCPURGENAT _IOWR('r', 100, struct ipfobj) #undef LARGE_NAT /* define this if you're setting up a system to NAT * LARGE numbers of networks/hosts - i.e. in the * hundreds or thousands. In such a case, you should * also change the RDR_SIZE and NAT_SIZE below to more * appropriate sizes. The figures below were used for * a setup with 1000-2000 networks to NAT. */ #ifndef NAT_SIZE # ifdef LARGE_NAT # define NAT_SIZE 2047 # else # define NAT_SIZE 127 # endif #endif #ifndef RDR_SIZE # ifdef LARGE_NAT # define RDR_SIZE 2047 # else # define RDR_SIZE 127 # endif #endif #ifndef HOSTMAP_SIZE # ifdef LARGE_NAT # define HOSTMAP_SIZE 8191 # else # define HOSTMAP_SIZE 2047 # endif #endif #ifndef NAT_TABLE_MAX /* * This is newly introduced and for the sake of "least surprise", the numbers * present aren't what we'd normally use for creating a proper hash table. */ # ifdef LARGE_NAT # define NAT_TABLE_MAX 180000 # else # define NAT_TABLE_MAX 30000 # endif #endif #ifndef NAT_TABLE_SZ # ifdef LARGE_NAT # define NAT_TABLE_SZ 16383 # else # define NAT_TABLE_SZ 2047 # endif #endif #ifndef APR_LABELLEN #define APR_LABELLEN 16 #endif #define NAT_HW_CKSUM 0x80000000 #define NAT_HW_CKSUM_PART 0x40000000 #define DEF_NAT_AGE 1200 /* 10 minutes (600 seconds) */ struct ipstate; struct ap_session; /* * This structure is used in the active NAT table and represents an * active NAT session. */ typedef struct nat { ipfmutex_t nat_lock; struct nat *nat_next; struct nat **nat_pnext; struct nat *nat_hnext[2]; struct nat **nat_phnext[2]; struct hostmap *nat_hm; void *nat_data; struct nat **nat_me; struct ipstate *nat_state; struct ap_session *nat_aps; /* proxy session */ frentry_t *nat_fr; /* filter rule ptr if appropriate */ struct ipnat *nat_ptr; /* pointer back to the rule */ void *nat_ifps[2]; void *nat_sync; ipftqent_t nat_tqe; int nat_mtu[2]; u_32_t nat_flags; u_32_t nat_sumd[2]; /* ip checksum delta for data segment*/ u_32_t nat_ipsumd; /* ip checksum delta for ip header */ u_32_t nat_mssclamp; /* if != zero clamp MSS to this */ i6addr_t nat_odst6; i6addr_t nat_osrc6; i6addr_t nat_ndst6; i6addr_t nat_nsrc6; U_QUAD_T nat_pkts[2]; U_QUAD_T nat_bytes[2]; union { udpinfo_t nat_unu; tcpinfo_t nat_unt; icmpinfo_t nat_uni; greinfo_t nat_ugre; } nat_unold, nat_unnew; int nat_use; int nat_pr[2]; /* protocol for NAT */ int nat_dir; int nat_ref; /* reference count */ u_int nat_hv[2]; char nat_ifnames[2][LIFNAMSIZ]; int nat_rev; /* 0 = forward, 1 = reverse */ int nat_dlocal; int nat_v[2]; /* 0 = old, 1 = new */ u_int nat_redir; /* copy of in_redir */ } nat_t; #define nat_osrcip nat_osrc6.in4 #define nat_odstip nat_odst6.in4 #define nat_nsrcip nat_nsrc6.in4 #define nat_ndstip nat_ndst6.in4 #define nat_osrcaddr nat_osrc6.in4.s_addr #define nat_odstaddr nat_odst6.in4.s_addr #define nat_nsrcaddr nat_nsrc6.in4.s_addr #define nat_ndstaddr nat_ndst6.in4.s_addr #define nat_age nat_tqe.tqe_die #define nat_osport nat_unold.nat_unt.ts_sport #define nat_odport nat_unold.nat_unt.ts_dport #define nat_nsport nat_unnew.nat_unt.ts_sport #define nat_ndport nat_unnew.nat_unt.ts_dport #define nat_oicmpid nat_unold.nat_uni.ici_id #define nat_nicmpid nat_unnew.nat_uni.ici_id #define nat_type nat_unold.nat_uni.ici_type #define nat_oseq nat_unold.nat_uni.ici_seq #define nat_nseq nat_unnew.nat_uni.ici_seq #define nat_tcpstate nat_tqe.tqe_state #define nat_die nat_tqe.tqe_die #define nat_touched nat_tqe.tqe_touched /* * Values for nat_dir */ #define NAT_INBOUND 0 #define NAT_OUTBOUND 1 #define NAT_ENCAPIN 2 #define NAT_ENCAPOUT 3 #define NAT_DIVERTIN 4 #define NAT_DIVERTOUT 5 /* * Definitions for nat_flags */ #define NAT_TCP 0x0001 /* IPN_TCP */ #define NAT_UDP 0x0002 /* IPN_UDP */ #define NAT_ICMPERR 0x0004 /* IPN_ICMPERR */ #define NAT_ICMPQUERY 0x0008 /* IPN_ICMPQUERY */ #define NAT_SEARCH 0x0010 #define NAT_SLAVE 0x0020 /* Slave connection for a proxy */ #define NAT_NOTRULEPORT 0x0040 /* Don't use the port # in the NAT rule */ #define NAT_TCPUDP (NAT_TCP|NAT_UDP) #define NAT_TCPUDPICMP (NAT_TCP|NAT_UDP|NAT_ICMPERR) #define NAT_TCPUDPICMPQ (NAT_TCP|NAT_UDP|NAT_ICMPQUERY) #define NAT_FROMRULE (NAT_TCP|NAT_UDP) /* 0x0100 reserved for FI_W_SPORT */ /* 0x0200 reserved for FI_W_DPORT */ /* 0x0400 reserved for FI_W_SADDR */ /* 0x0800 reserved for FI_W_DADDR */ /* 0x1000 reserved for FI_W_NEWFR */ /* 0x2000 reserved for SI_CLONE */ /* 0x4000 reserved for SI_CLONED */ /* 0x8000 reserved for SI_IGNOREPKT */ #define NAT_DEBUG 0x800000 typedef struct nat_addr_s { i6addr_t na_addr[2]; i6addr_t na_nextaddr; int na_atype; int na_function; } nat_addr_t; #define na_nextip na_nextaddr.in4.s_addr #define na_nextip6 na_nextaddr.in6 #define na_num na_addr[0].iplookupnum #define na_type na_addr[0].iplookuptype #define na_subtype na_addr[0].iplookupsubtype #define na_ptr na_addr[1].iplookupptr #define na_func na_addr[1].iplookupfunc /* * This structure represents an actual NAT rule, loaded by ipnat. */ typedef struct ipnat { ipfmutex_t in_lock; struct ipnat *in_next; /* NAT rule list next */ struct ipnat **in_pnext; /* prior rdr next ptr */ struct ipnat *in_rnext; /* rdr rule hash next */ struct ipnat **in_prnext; /* prior rdr next ptr */ struct ipnat *in_mnext; /* map rule hash next */ struct ipnat **in_pmnext; /* prior map next ptr */ struct ipftq *in_tqehead[2]; void *in_ifps[2]; void *in_apr; char *in_comment; mb_t *in_divmp; void *in_pconf; U_QUAD_T in_pkts[2]; U_QUAD_T in_bytes[2]; u_long in_space; u_long in_hits; int in_size; int in_use; u_int in_hv[2]; int in_flineno; /* conf. file line number */ int in_stepnext; int in_dlocal; u_short in_dpnext; u_short in_spnext; /* From here to the end is covered by IPN_CMPSIZ */ u_char in_v[2]; /* 0 = old, 1 = new */ u_32_t in_flags; u_32_t in_mssclamp; /* if != 0 clamp MSS to this */ u_int in_age[2]; int in_redir; /* see below for values */ int in_pr[2]; /* protocol. */ nat_addr_t in_ndst; nat_addr_t in_nsrc; nat_addr_t in_osrc; nat_addr_t in_odst; frtuc_t in_tuc; u_short in_ppip; /* ports per IP. */ u_short in_ippip; /* IP #'s per IP# */ u_short in_ndports[2]; u_short in_nsports[2]; int in_ifnames[2]; int in_plabel; /* proxy label. */ int in_pconfig; /* proxy label. */ ipftag_t in_tag; int in_namelen; char in_names[1]; } ipnat_t; /* * MAP-IN MAP-OUT RDR-IN RDR-OUT * osrc X == src == src X * odst X == dst == dst X * nsrc == dst X X == dst * ndst == src X X == src */ #define in_dpmin in_ndports[0] /* Also holds static redir port */ #define in_dpmax in_ndports[1] #define in_spmin in_nsports[0] /* Also holds static redir port */ #define in_spmax in_nsports[1] #define in_ndport in_ndports[0] #define in_nsport in_nsports[0] #define in_dipnext in_ndst.na_nextaddr.in4 #define in_dipnext6 in_ndst.na_nextaddr #define in_dnip in_ndst.na_nextaddr.in4.s_addr #define in_dnip6 in_ndst.na_nextaddr #define in_sipnext in_nsrc.na_nextaddr.in4 #define in_snip in_nsrc.na_nextaddr.in4.s_addr #define in_snip6 in_nsrc.na_nextaddr #define in_odstip in_odst.na_addr[0].in4 #define in_odstip6 in_odst.na_addr[0] #define in_odstaddr in_odst.na_addr[0].in4.s_addr #define in_odstmsk in_odst.na_addr[1].in4.s_addr #define in_odstmsk6 in_odst.na_addr[1] #define in_odstatype in_odst.na_atype #define in_osrcip in_osrc.na_addr[0].in4 #define in_osrcip6 in_osrc.na_addr[0] #define in_osrcaddr in_osrc.na_addr[0].in4.s_addr #define in_osrcmsk in_osrc.na_addr[1].in4.s_addr #define in_osrcmsk6 in_osrc.na_addr[1] #define in_osrcatype in_osrc.na_atype #define in_ndstip in_ndst.na_addr[0].in4 #define in_ndstip6 in_ndst.na_addr[0] #define in_ndstaddr in_ndst.na_addr[0].in4.s_addr #define in_ndstmsk in_ndst.na_addr[1].in4.s_addr #define in_ndstmsk6 in_ndst.na_addr[1] #define in_ndstatype in_ndst.na_atype #define in_ndstafunc in_ndst.na_function #define in_nsrcip in_nsrc.na_addr[0].in4 #define in_nsrcip6 in_nsrc.na_addr[0] #define in_nsrcaddr in_nsrc.na_addr[0].in4.s_addr #define in_nsrcmsk in_nsrc.na_addr[1].in4.s_addr #define in_nsrcmsk6 in_nsrc.na_addr[1] #define in_nsrcatype in_nsrc.na_atype #define in_nsrcafunc in_nsrc.na_function #define in_scmp in_tuc.ftu_scmp #define in_dcmp in_tuc.ftu_dcmp #define in_stop in_tuc.ftu_stop #define in_dtop in_tuc.ftu_dtop #define in_osport in_tuc.ftu_sport #define in_odport in_tuc.ftu_dport #define in_ndstnum in_ndst.na_addr[0].iplookupnum #define in_ndsttype in_ndst.na_addr[0].iplookuptype #define in_ndstptr in_ndst.na_addr[1].iplookupptr #define in_ndstfunc in_ndst.na_addr[1].iplookupfunc #define in_nsrcnum in_nsrc.na_addr[0].iplookupnum #define in_nsrctype in_nsrc.na_addr[0].iplookuptype #define in_nsrcptr in_nsrc.na_addr[1].iplookupptr #define in_nsrcfunc in_nsrc.na_addr[1].iplookupfunc #define in_odstnum in_odst.na_addr[0].iplookupnum #define in_odsttype in_odst.na_addr[0].iplookuptype #define in_odstptr in_odst.na_addr[1].iplookupptr #define in_odstfunc in_odst.na_addr[1].iplookupfunc #define in_osrcnum in_osrc.na_addr[0].iplookupnum #define in_osrctype in_osrc.na_addr[0].iplookuptype #define in_osrcptr in_osrc.na_addr[1].iplookupptr #define in_osrcfunc in_osrc.na_addr[1].iplookupfunc #define in_icmpidmin in_nsports[0] #define in_icmpidmax in_nsports[1] /* * Bit definitions for in_flags */ #define IPN_ANY 0x00000 #define IPN_TCP 0x00001 #define IPN_UDP 0x00002 #define IPN_TCPUDP (IPN_TCP|IPN_UDP) #define IPN_ICMPERR 0x00004 #define IPN_TCPUDPICMP (IPN_TCP|IPN_UDP|IPN_ICMPERR) #define IPN_ICMPQUERY 0x00008 #define IPN_TCPUDPICMPQ (IPN_TCP|IPN_UDP|IPN_ICMPQUERY) #define IPN_RF (IPN_TCPUDP|IPN_DELETE|IPN_ICMPERR) #define IPN_AUTOPORTMAP 0x00010 #define IPN_FILTER 0x00020 #define IPN_SPLIT 0x00040 #define IPN_ROUNDR 0x00080 #define IPN_SIPRANGE 0x00100 #define IPN_DIPRANGE 0x00200 #define IPN_NOTSRC 0x00400 #define IPN_NOTDST 0x00800 #define IPN_NO 0x01000 #define IPN_DYNSRCIP 0x02000 /* dynamic src IP# */ #define IPN_DYNDSTIP 0x04000 /* dynamic dst IP# */ #define IPN_DELETE 0x08000 #define IPN_STICKY 0x10000 #define IPN_FRAG 0x20000 #define IPN_FIXEDSPORT 0x40000 #define IPN_FIXEDDPORT 0x80000 #define IPN_FINDFORWARD 0x100000 #define IPN_IN 0x200000 #define IPN_SEQUENTIAL 0x400000 #define IPN_PURGE 0x800000 #define IPN_PROXYRULE 0x1000000 #define IPN_USERFLAGS (IPN_TCPUDP|IPN_AUTOPORTMAP|IPN_SIPRANGE|IPN_SPLIT|\ IPN_ROUNDR|IPN_FILTER|IPN_NOTSRC|IPN_NOTDST|IPN_NO|\ IPN_FRAG|IPN_STICKY|IPN_FIXEDDPORT|IPN_ICMPQUERY|\ IPN_DIPRANGE|IPN_SEQUENTIAL|IPN_PURGE) /* * Values for in_redir */ #define NAT_MAP 0x01 #define NAT_REDIRECT 0x02 #define NAT_BIMAP (NAT_MAP|NAT_REDIRECT) #define NAT_MAPBLK 0x04 #define NAT_REWRITE 0x08 #define NAT_ENCAP 0x10 #define NAT_DIVERTUDP 0x20 #define MAPBLK_MINPORT 1024 /* don't use reserved ports for src port */ #define USABLE_PORTS (65536 - MAPBLK_MINPORT) #define IPN_CMPSIZ (sizeof(ipnat_t) - offsetof(ipnat_t, in_v)) typedef struct natlookup { i6addr_t nl_inipaddr; i6addr_t nl_outipaddr; i6addr_t nl_realipaddr; int nl_v; int nl_flags; u_short nl_inport; u_short nl_outport; u_short nl_realport; } natlookup_t; #define nl_inip nl_inipaddr.in4 #define nl_outip nl_outipaddr.in4 #define nl_realip nl_realipaddr.in4 #define nl_inip6 nl_inipaddr.in6 #define nl_outip6 nl_outipaddr.in6 #define nl_realip6 nl_realipaddr.in6 typedef struct nat_save { void *ipn_next; struct nat ipn_nat; struct ipnat ipn_ipnat; struct frentry ipn_fr; int ipn_dsize; char ipn_data[4]; } nat_save_t; #define ipn_rule ipn_nat.nat_fr typedef struct natget { void *ng_ptr; int ng_sz; } natget_t; /* * This structure gets used to help NAT sessions keep the same NAT rule (and * thus translation for IP address) when: * (a) round-robin redirects are in use * (b) different IP add */ typedef struct hostmap { struct hostmap *hm_hnext; struct hostmap **hm_phnext; struct hostmap *hm_next; struct hostmap **hm_pnext; struct ipnat *hm_ipnat; i6addr_t hm_osrcip6; i6addr_t hm_odstip6; i6addr_t hm_nsrcip6; i6addr_t hm_ndstip6; u_32_t hm_port; int hm_ref; int hm_hv; int hm_v; } hostmap_t; #define hm_osrcip hm_osrcip6.in4 #define hm_odstip hm_odstip6.in4 #define hm_nsrcip hm_nsrcip6.in4 #define hm_ndstip hm_ndstip6.in4 #define hm_osrc6 hm_osrcip6.in6 #define hm_odst6 hm_odstip6.in6 #define hm_nsrc6 hm_nsrcip6.in6 #define hm_ndst6 hm_ndstip6.in6 /* * Structure used to pass information in to nat_newmap and nat_newrdr. */ typedef struct natinfo { ipnat_t *nai_np; u_32_t nai_sum1; u_32_t nai_sum2; struct in_addr nai_ip; /* In host byte order */ u_short nai_port; u_short nai_nport; u_short nai_sport; u_short nai_dport; } natinfo_t; typedef struct nat_stat_side { u_int *ns_bucketlen; nat_t **ns_table; u_long ns_added; u_long ns_appr_fail; u_long ns_badnat; u_long ns_badnatnew; u_long ns_badnextaddr; u_long ns_bucket_max; u_long ns_clone_nomem; u_long ns_decap_bad; u_long ns_decap_fail; u_long ns_decap_pullup; u_long ns_divert_dup; u_long ns_divert_exist; u_long ns_drop; u_long ns_encap_dup; u_long ns_encap_pullup; u_long ns_exhausted; u_long ns_icmp_address; u_long ns_icmp_basic; u_long ns_icmp_mbuf; u_long ns_icmp_notfound; u_long ns_icmp_rebuild; u_long ns_icmp_short; u_long ns_icmp_size; u_long ns_ifpaddrfail; u_long ns_ignored; u_long ns_insert_fail; u_long ns_inuse; u_long ns_log; u_long ns_lookup_miss; u_long ns_lookup_nowild; u_long ns_new_ifpaddr; u_long ns_memfail; u_long ns_table_max; u_long ns_translated; u_long ns_unfinalised; u_long ns_wrap; u_long ns_xlate_null; u_long ns_xlate_exists; u_long ns_ipf_proxy_fail; u_long ns_uncreate[2]; } nat_stat_side_t; typedef struct natstat { nat_t *ns_instances; ipnat_t *ns_list; hostmap_t *ns_maplist; hostmap_t **ns_maptable; u_int ns_active; u_long ns_addtrpnt; u_long ns_divert_build; u_long ns_expire; u_long ns_flush_all; u_long ns_flush_closing; u_long ns_flush_queue; u_long ns_flush_state; u_long ns_flush_timeout; u_long ns_hm_new; u_long ns_hm_newfail; u_long ns_hm_addref; u_long ns_hm_nullnp; u_long ns_log_ok; u_long ns_log_fail; u_int ns_hostmap_sz; u_int ns_nattab_sz; u_int ns_nattab_max; u_int ns_orphans; u_int ns_rules; u_int ns_rules_map; u_int ns_rules_rdr; u_int ns_rultab_sz; u_int ns_rdrtab_sz; u_32_t ns_ticks; u_int ns_trpntab_sz; u_int ns_wilds; u_long ns_proto[256]; nat_stat_side_t ns_side[2]; #ifdef USE_INET6 nat_stat_side_t ns_side6[2]; #endif } natstat_t; typedef struct natlog { i6addr_t nl_osrcip; i6addr_t nl_odstip; i6addr_t nl_nsrcip; i6addr_t nl_ndstip; u_short nl_osrcport; u_short nl_odstport; u_short nl_nsrcport; u_short nl_ndstport; int nl_action; int nl_type; int nl_rule; U_QUAD_T nl_pkts[2]; U_QUAD_T nl_bytes[2]; u_char nl_p[2]; u_char nl_v[2]; u_char nl_ifnames[2][LIFNAMSIZ]; } natlog_t; #define NL_NEW 0 #define NL_CLONE 1 #define NL_PURGE 0xfffc #define NL_DESTROY 0xfffd #define NL_FLUSH 0xfffe #define NL_EXPIRE 0xffff #define NAT_HASH_FN(_k,_l,_m) (((_k) + ((_k) >> 12) + _l) % (_m)) #define NAT_HASH_FN6(_k,_l,_m) ((((u_32_t *)(_k))[3] \ + (((u_32_t *)(_k))[3] >> 12) \ + (((u_32_t *)(_k))[2]) \ + (((u_32_t *)(_k))[2] >> 12) \ + (((u_32_t *)(_k))[1]) \ + (((u_32_t *)(_k))[1] >> 12) \ + (((u_32_t *)(_k))[0]) \ + (((u_32_t *)(_k))[0] >> 12) \ + _l) % (_m)) #define LONG_SUM(_i) (((_i) & 0xffff) + ((_i) >> 16)) #define LONG_SUM6(_i) (LONG_SUM(ntohl(((u_32_t *)(_i))[0])) + \ LONG_SUM(ntohl(((u_32_t *)(_i))[1])) + \ LONG_SUM(ntohl(((u_32_t *)(_i))[2])) + \ LONG_SUM(ntohl(((u_32_t *)(_i))[3]))) #define CALC_SUMD(s1, s2, sd) { \ (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ /* Do it twice */ \ (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ /* Because ~1 == -2, We really need ~1 == -1 */ \ if ((s1) > (s2)) (s2)--; \ (sd) = (s2) - (s1); \ (sd) = ((sd) & 0xffff) + ((sd) >> 16); } #define NAT_SYSSPACE 0x80000000 #define NAT_LOCKHELD 0x40000000 /* * This is present in ip_nat.h because it needs to be shared between * ip_nat.c and ip_nat6.c */ typedef struct ipf_nat_softc_s { ipfmutex_t ipf_nat_new; ipfmutex_t ipf_nat_io; int ipf_nat_doflush; int ipf_nat_logging; int ipf_nat_lock; int ipf_nat_inited; int ipf_nat_table_wm_high; int ipf_nat_table_wm_low; u_int ipf_nat_table_max; u_int ipf_nat_table_sz; u_int ipf_nat_maprules_sz; u_int ipf_nat_rdrrules_sz; u_int ipf_nat_hostmap_sz; u_int ipf_nat_maxbucket; u_int ipf_nat_last_force_flush; u_int ipf_nat_defage; u_int ipf_nat_defipage; u_int ipf_nat_deficmpage; ipf_v4_masktab_t ipf_nat_map_mask; ipf_v6_masktab_t ipf_nat6_map_mask; ipf_v4_masktab_t ipf_nat_rdr_mask; ipf_v6_masktab_t ipf_nat6_rdr_mask; nat_t **ipf_nat_table[2]; nat_t *ipf_nat_instances; ipnat_t *ipf_nat_list; ipnat_t **ipf_nat_list_tail; ipnat_t **ipf_nat_map_rules; ipnat_t **ipf_nat_rdr_rules; ipftq_t *ipf_nat_utqe; hostmap_t **ipf_hm_maptable ; hostmap_t *ipf_hm_maplist ; ipftuneable_t *ipf_nat_tune; ipftq_t ipf_nat_udptq; ipftq_t ipf_nat_udpacktq; ipftq_t ipf_nat_icmptq; ipftq_t ipf_nat_icmpacktq; ipftq_t ipf_nat_iptq; ipftq_t ipf_nat_pending; ipftq_t ipf_nat_tcptq[IPF_TCP_NSTATES]; natstat_t ipf_nat_stats; } ipf_nat_softc_t ; #define ipf_nat_map_max ipf_nat_map_mask.imt4_max #define ipf_nat_rdr_max ipf_nat_rdr_mask.imt4_max #define ipf_nat6_map_max ipf_nat6_map_mask.imt6_max #define ipf_nat6_rdr_max ipf_nat6_rdr_mask.imt6_max #define ipf_nat_map_active_masks ipf_nat_map_mask.imt4_active #define ipf_nat_rdr_active_masks ipf_nat_rdr_mask.imt4_active #define ipf_nat6_map_active_masks ipf_nat6_map_mask.imt6_active #define ipf_nat6_rdr_active_masks ipf_nat6_rdr_mask.imt6_active extern frentry_t ipfnatblock; -extern void ipf_fix_datacksum __P((u_short *, u_32_t)); -extern void ipf_fix_incksum __P((int, u_short *, u_32_t, u_32_t)); -extern void ipf_fix_outcksum __P((int, u_short *, u_32_t, u_32_t)); - -extern int ipf_nat_checkin __P((fr_info_t *, u_32_t *)); -extern int ipf_nat_checkout __P((fr_info_t *, u_32_t *)); -extern void ipf_nat_delete __P((ipf_main_softc_t *, struct nat *, int)); -extern void ipf_nat_deref __P((ipf_main_softc_t *, nat_t **)); -extern void ipf_nat_expire __P((ipf_main_softc_t *)); -extern int ipf_nat_hashtab_add __P((ipf_main_softc_t *, - ipf_nat_softc_t *, nat_t *)); -extern void ipf_nat_hostmapdel __P((ipf_main_softc_t *, hostmap_t **)); -extern int ipf_nat_hostmap_rehash __P((ipf_main_softc_t *, - ipftuneable_t *, ipftuneval_t *)); -extern nat_t *ipf_nat_icmperrorlookup __P((fr_info_t *, int)); -extern nat_t *ipf_nat_icmperror __P((fr_info_t *, u_int *, int)); -extern int ipf_nat_init __P((void)); -extern nat_t *ipf_nat_inlookup __P((fr_info_t *, u_int, u_int, - struct in_addr, struct in_addr)); -extern int ipf_nat_in __P((fr_info_t *, nat_t *, int, u_32_t)); -extern int ipf_nat_insert __P((ipf_main_softc_t *, ipf_nat_softc_t *, - nat_t *)); -extern int ipf_nat_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, - int, int, void *)); -extern void ipf_nat_log __P((ipf_main_softc_t *, ipf_nat_softc_t *, - struct nat *, u_int)); -extern nat_t *ipf_nat_lookupredir __P((natlookup_t *)); -extern nat_t *ipf_nat_maplookup __P((void *, u_int, struct in_addr, - struct in_addr)); -extern nat_t *ipf_nat_add __P((fr_info_t *, ipnat_t *, nat_t **, - u_int, int)); -extern int ipf_nat_out __P((fr_info_t *, nat_t *, int, u_32_t)); -extern nat_t *ipf_nat_outlookup __P((fr_info_t *, u_int, u_int, - struct in_addr, struct in_addr)); -extern u_short *ipf_nat_proto __P((fr_info_t *, nat_t *, u_int)); -extern void ipf_nat_rule_deref __P((ipf_main_softc_t *, ipnat_t **)); -extern void ipf_nat_setqueue __P((ipf_main_softc_t *, ipf_nat_softc_t *, - nat_t *)); -extern void ipf_nat_setpending __P((ipf_main_softc_t *, nat_t *)); -extern nat_t *ipf_nat_tnlookup __P((fr_info_t *, int)); -extern void ipf_nat_update __P((fr_info_t *, nat_t *)); -extern int ipf_nat_rehash __P((ipf_main_softc_t *, ipftuneable_t *, - ipftuneval_t *)); -extern int ipf_nat_rehash_rules __P((ipf_main_softc_t *, ipftuneable_t *, - ipftuneval_t *)); -extern int ipf_nat_settimeout __P((struct ipf_main_softc_s *, - ipftuneable_t *, ipftuneval_t *)); -extern void ipf_nat_sync __P((ipf_main_softc_t *, void *)); - -extern nat_t *ipf_nat_clone __P((fr_info_t *, nat_t *)); -extern void ipf_nat_delmap __P((ipf_nat_softc_t *, ipnat_t *)); -extern void ipf_nat_delrdr __P((ipf_nat_softc_t *, ipnat_t *)); -extern int ipf_nat_wildok __P((nat_t *, int, int, int, int)); -extern void ipf_nat_setlock __P((void *, int)); -extern void ipf_nat_load __P((void)); -extern void *ipf_nat_soft_create __P((ipf_main_softc_t *)); -extern int ipf_nat_soft_init __P((ipf_main_softc_t *, void *)); -extern void ipf_nat_soft_destroy __P((ipf_main_softc_t *, void *)); -extern int ipf_nat_soft_fini __P((ipf_main_softc_t *, void *)); -extern int ipf_nat_main_load __P((void)); -extern int ipf_nat_main_unload __P((void)); -extern ipftq_t *ipf_nat_add_tq __P((ipf_main_softc_t *, int)); -extern void ipf_nat_uncreate __P((fr_info_t *)); +extern void ipf_fix_datacksum(u_short *, u_32_t); +extern void ipf_fix_incksum(int, u_short *, u_32_t, u_32_t); +extern void ipf_fix_outcksum(int, u_short *, u_32_t, u_32_t); + +extern int ipf_nat_checkin(fr_info_t *, u_32_t *); +extern int ipf_nat_checkout(fr_info_t *, u_32_t *); +extern void ipf_nat_delete(ipf_main_softc_t *, struct nat *, int); +extern void ipf_nat_deref(ipf_main_softc_t *, nat_t **); +extern void ipf_nat_expire(ipf_main_softc_t *); +extern int ipf_nat_hashtab_add(ipf_main_softc_t *, + ipf_nat_softc_t *, nat_t *); +extern void ipf_nat_hostmapdel(ipf_main_softc_t *, hostmap_t **); +extern int ipf_nat_hostmap_rehash(ipf_main_softc_t *, + ipftuneable_t *, ipftuneval_t *); +extern nat_t *ipf_nat_icmperrorlookup(fr_info_t *, int); +extern nat_t *ipf_nat_icmperror(fr_info_t *, u_int *, int); +extern int ipf_nat_init(void); +extern nat_t *ipf_nat_inlookup(fr_info_t *, u_int, u_int, + struct in_addr, struct in_addr); +extern int ipf_nat_in(fr_info_t *, nat_t *, int, u_32_t); +extern int ipf_nat_insert(ipf_main_softc_t *, ipf_nat_softc_t *, + nat_t *); +extern int ipf_nat_ioctl(ipf_main_softc_t *, caddr_t, ioctlcmd_t, + int, int, void *); +extern void ipf_nat_log(ipf_main_softc_t *, ipf_nat_softc_t *, + struct nat *, u_int); +extern nat_t *ipf_nat_lookupredir(natlookup_t *); +extern nat_t *ipf_nat_maplookup(void *, u_int, struct in_addr, + struct in_addr); +extern nat_t *ipf_nat_add(fr_info_t *, ipnat_t *, nat_t **, + u_int, int); +extern int ipf_nat_out(fr_info_t *, nat_t *, int, u_32_t); +extern nat_t *ipf_nat_outlookup(fr_info_t *, u_int, u_int, + struct in_addr, struct in_addr); +extern u_short *ipf_nat_proto(fr_info_t *, nat_t *, u_int); +extern void ipf_nat_rule_deref(ipf_main_softc_t *, ipnat_t **); +extern void ipf_nat_setqueue(ipf_main_softc_t *, ipf_nat_softc_t *, + nat_t *); +extern void ipf_nat_setpending(ipf_main_softc_t *, nat_t *); +extern nat_t *ipf_nat_tnlookup(fr_info_t *, int); +extern void ipf_nat_update(fr_info_t *, nat_t *); +extern int ipf_nat_rehash(ipf_main_softc_t *, ipftuneable_t *, + ipftuneval_t *); +extern int ipf_nat_rehash_rules(ipf_main_softc_t *, ipftuneable_t *, + ipftuneval_t *); +extern int ipf_nat_settimeout(struct ipf_main_softc_s *, + ipftuneable_t *, ipftuneval_t *); +extern void ipf_nat_sync(ipf_main_softc_t *, void *); + +extern nat_t *ipf_nat_clone(fr_info_t *, nat_t *); +extern void ipf_nat_delmap(ipf_nat_softc_t *, ipnat_t *); +extern void ipf_nat_delrdr(ipf_nat_softc_t *, ipnat_t *); +extern int ipf_nat_wildok(nat_t *, int, int, int, int); +extern void ipf_nat_setlock(void *, int); +extern void ipf_nat_load(void); +extern void *ipf_nat_soft_create(ipf_main_softc_t *); +extern int ipf_nat_soft_init(ipf_main_softc_t *, void *); +extern void ipf_nat_soft_destroy(ipf_main_softc_t *, void *); +extern int ipf_nat_soft_fini(ipf_main_softc_t *, void *); +extern int ipf_nat_main_load(void); +extern int ipf_nat_main_unload(void); +extern ipftq_t *ipf_nat_add_tq(ipf_main_softc_t *, int); +extern void ipf_nat_uncreate(fr_info_t *); #ifdef USE_INET6 -extern nat_t *ipf_nat6_add __P((fr_info_t *, ipnat_t *, nat_t **, - u_int, int)); -extern void ipf_nat6_addrdr __P((ipf_nat_softc_t *, ipnat_t *)); -extern void ipf_nat6_addmap __P((ipf_nat_softc_t *, ipnat_t *)); -extern void ipf_nat6_addencap __P((ipf_nat_softc_t *, ipnat_t *)); -extern int ipf_nat6_checkout __P((fr_info_t *, u_32_t *)); -extern int ipf_nat6_checkin __P((fr_info_t *, u_32_t *)); -extern void ipf_nat6_delmap __P((ipf_nat_softc_t *, ipnat_t *)); -extern void ipf_nat6_delrdr __P((ipf_nat_softc_t *, ipnat_t *)); -extern int ipf_nat6_finalise __P((fr_info_t *, nat_t *)); -extern nat_t *ipf_nat6_icmperror __P((fr_info_t *, u_int *, int)); -extern nat_t *ipf_nat6_icmperrorlookup __P((fr_info_t *, int)); -extern nat_t *ipf_nat6_inlookup __P((fr_info_t *, u_int, u_int, - struct in6_addr *, struct in6_addr *)); -extern u_32_t ipf_nat6_ip6subtract __P((i6addr_t *, i6addr_t *)); -extern frentry_t *ipf_nat6_ipfin __P((fr_info_t *, u_32_t *)); -extern frentry_t *ipf_nat6_ipfout __P((fr_info_t *, u_32_t *)); -extern nat_t *ipf_nat6_lookupredir __P((natlookup_t *)); -extern int ipf_nat6_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); -extern int ipf_nat6_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); -extern nat_t *ipf_nat6_outlookup __P((fr_info_t *, u_int, u_int, - struct in6_addr *, struct in6_addr *)); -extern int ipf_nat6_newrewrite __P((fr_info_t *, nat_t *, natinfo_t *)); -extern int ipf_nat6_newdivert __P((fr_info_t *, nat_t *, natinfo_t *)); -extern int ipf_nat6_ruleaddrinit __P((ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *)); +extern nat_t *ipf_nat6_add(fr_info_t *, ipnat_t *, nat_t **, + u_int, int); +extern void ipf_nat6_addrdr(ipf_nat_softc_t *, ipnat_t *); +extern void ipf_nat6_addmap(ipf_nat_softc_t *, ipnat_t *); +extern void ipf_nat6_addencap(ipf_nat_softc_t *, ipnat_t *); +extern int ipf_nat6_checkout(fr_info_t *, u_32_t *); +extern int ipf_nat6_checkin(fr_info_t *, u_32_t *); +extern void ipf_nat6_delmap(ipf_nat_softc_t *, ipnat_t *); +extern void ipf_nat6_delrdr(ipf_nat_softc_t *, ipnat_t *); +extern int ipf_nat6_finalise(fr_info_t *, nat_t *); +extern nat_t *ipf_nat6_icmperror(fr_info_t *, u_int *, int); +extern nat_t *ipf_nat6_icmperrorlookup(fr_info_t *, int); +extern nat_t *ipf_nat6_inlookup(fr_info_t *, u_int, u_int, + struct in6_addr *, struct in6_addr *); +extern u_32_t ipf_nat6_ip6subtract(i6addr_t *, i6addr_t *); +extern frentry_t *ipf_nat6_ipfin(fr_info_t *, u_32_t *); +extern frentry_t *ipf_nat6_ipfout(fr_info_t *, u_32_t *); +extern nat_t *ipf_nat6_lookupredir(natlookup_t *); +extern int ipf_nat6_newmap(fr_info_t *, nat_t *, natinfo_t *); +extern int ipf_nat6_newrdr(fr_info_t *, nat_t *, natinfo_t *); +extern nat_t *ipf_nat6_outlookup(fr_info_t *, u_int, u_int, + struct in6_addr *, struct in6_addr *); +extern int ipf_nat6_newrewrite(fr_info_t *, nat_t *, natinfo_t *); +extern int ipf_nat6_newdivert(fr_info_t *, nat_t *, natinfo_t *); +extern int ipf_nat6_ruleaddrinit(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *); #endif #endif /* __IP_NAT_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_nat6.c b/sys/contrib/ipfilter/netinet/ip_nat6.c index 19f57868db43..388ee9a67e73 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat6.c +++ b/sys/contrib/ipfilter/netinet/ip_nat6.c @@ -1,4092 +1,4092 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if defined(_KERNEL) && defined(__NetBSD_Version__) && \ (__NetBSD_Version__ >= 399002000) # include #endif #if !defined(_KERNEL) # include # include # include # define _KERNEL # ifdef ipf_nat6__OpenBSD__ struct file; # endif # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD_version) # include # include #else # include #endif # include # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD_version) # include #endif #include #if defined(__FreeBSD_version) # include #endif #ifdef sun # include #endif #include #include #include #include #ifdef RFC1825 # include # include extern struct ifnet vpnif; #endif # include #include #include #include #include "netinet/ip_compat.h" #include #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_lookup.h" #include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" #if defined(__FreeBSD_version) # include #endif #ifdef HAS_SYS_MD5_H # include #else # include "md5.h" #endif /* END OF INCLUDES */ #undef SOCKADDR_IN #define SOCKADDR_IN struct sockaddr_in #if !defined(lint) static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.22.2.20 2012/07/22 08:04:23 darren_r Exp $"; #endif #ifdef USE_INET6 -static struct hostmap *ipf_nat6_hostmap __P((ipf_nat_softc_t *, ipnat_t *, +static struct hostmap *ipf_nat6_hostmap(ipf_nat_softc_t *, ipnat_t *, i6addr_t *, i6addr_t *, - i6addr_t *, u_32_t)); -static int ipf_nat6_match __P((fr_info_t *, ipnat_t *)); -static void ipf_nat6_tabmove __P((ipf_nat_softc_t *, nat_t *)); -static int ipf_nat6_decap __P((fr_info_t *, nat_t *)); -static int ipf_nat6_nextaddr __P((fr_info_t *, nat_addr_t *, i6addr_t *, - i6addr_t *)); -static int ipf_nat6_icmpquerytype __P((int)); -static int ipf_nat6_out __P((fr_info_t *, nat_t *, int, u_32_t)); -static int ipf_nat6_in __P((fr_info_t *, nat_t *, int, u_32_t)); -static int ipf_nat6_builddivertmp __P((ipf_nat_softc_t *, ipnat_t *)); -static int ipf_nat6_nextaddrinit __P((ipf_main_softc_t *, char *, - nat_addr_t *, int, void *)); -static int ipf_nat6_insert __P((ipf_main_softc_t *, ipf_nat_softc_t *, - nat_t *)); + i6addr_t *, u_32_t); +static int ipf_nat6_match(fr_info_t *, ipnat_t *); +static void ipf_nat6_tabmove(ipf_nat_softc_t *, nat_t *); +static int ipf_nat6_decap(fr_info_t *, nat_t *); +static int ipf_nat6_nextaddr(fr_info_t *, nat_addr_t *, i6addr_t *, + i6addr_t *); +static int ipf_nat6_icmpquerytype(int); +static int ipf_nat6_out(fr_info_t *, nat_t *, int, u_32_t); +static int ipf_nat6_in(fr_info_t *, nat_t *, int, u_32_t); +static int ipf_nat6_builddivertmp(ipf_nat_softc_t *, ipnat_t *); +static int ipf_nat6_nextaddrinit(ipf_main_softc_t *, char *, + nat_addr_t *, int, void *); +static int ipf_nat6_insert(ipf_main_softc_t *, ipf_nat_softc_t *, + nat_t *); #define NINCLSIDE6(y,x) ATOMIC_INCL(softn->ipf_nat_stats.ns_side6[y].x) #define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ #define NBUMPSIDE6(y,x) softn->ipf_nat_stats.ns_side6[y].x++ #define NBUMPSIDE6D(y,x) \ do { \ softn->ipf_nat_stats.ns_side6[y].x++; \ DT(x); \ } while (0) #define NBUMPSIDE6DX(y,x,z) \ do { \ softn->ipf_nat_stats.ns_side6[y].x++; \ DT(z); \ } while (0) /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_ruleaddrinit */ /* Returns: int - 0 == success, else failure */ /* Parameters: in(I) - NAT rule that requires address fields to be init'd */ /* */ /* For each of the source/destination address fields in a NAT rule, call */ /* ipf_nat6_nextaddrinit() to prepare the structure for active duty. Other */ /* IPv6 specific actions can also be taken care of here. */ /* ------------------------------------------------------------------------ */ int ipf_nat6_ruleaddrinit(softc, softn, n) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; ipnat_t *n; { int idx, error; if (n->in_redir == NAT_BIMAP) { n->in_ndstip6 = n->in_osrcip6; n->in_ndstmsk6 = n->in_osrcmsk6; n->in_odstip6 = n->in_nsrcip6; n->in_odstmsk6 = n->in_nsrcmsk6; } if (n->in_redir & NAT_REDIRECT) idx = 1; else idx = 0; /* * Initialise all of the address fields. */ error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_odst, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, n->in_ifps[idx]); if (error != 0) return error; if (n->in_redir & NAT_DIVERTUDP) ipf_nat6_builddivertmp(softn, n); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_addrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a redirect rule to the hash table of redirect rules and the list of */ /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ /* use by redirect rules. */ /* ------------------------------------------------------------------------ */ void ipf_nat6_addrdr(softn, n) ipf_nat_softc_t *softn; ipnat_t *n; { i6addr_t *mask; ipnat_t **np; i6addr_t j; u_int hv; int k; if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { k = count6bits(n->in_nsrcmsk6.i6); mask = &n->in_nsrcmsk6; IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); } else if (n->in_odstatype == FRI_NORMAL) { k = count6bits(n->in_odstmsk6.i6); mask = &n->in_odstmsk6; IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); } else { k = 0; hv = 0; mask = NULL; } ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_rdr_mask); np = softn->ipf_nat_rdr_rules + hv; while (*np != NULL) np = &(*np)->in_rnext; n->in_rnext = NULL; n->in_prnext = np; n->in_hv[0] = hv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_addmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a NAT map rule to the hash table of rules and the list of loaded */ /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ /* redirect rules. */ /* ------------------------------------------------------------------------ */ void ipf_nat6_addmap(softn, n) ipf_nat_softc_t *softn; ipnat_t *n; { i6addr_t *mask; ipnat_t **np; i6addr_t j; u_int hv; int k; if (n->in_osrcatype == FRI_NORMAL) { k = count6bits(n->in_osrcmsk6.i6); mask = &n->in_osrcmsk6; IP6_AND(&n->in_osrcip6, &n->in_osrcmsk6, &j); hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_maprules_sz); } else { k = 0; hv = 0; mask = NULL; } ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_map_mask); np = softn->ipf_nat_map_rules + hv; while (*np != NULL) np = &(*np)->in_mnext; n->in_mnext = NULL; n->in_pmnext = np; n->in_hv[1] = hv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_del_rdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a NAT rdr rule from the hash table of NAT rdr rules. */ /* ------------------------------------------------------------------------ */ void ipf_nat6_delrdr(softn, n) ipf_nat_softc_t *softn; ipnat_t *n; { i6addr_t *mask; int k; if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { k = count6bits(n->in_nsrcmsk6.i6); mask = &n->in_nsrcmsk6; } else if (n->in_odstatype == FRI_NORMAL) { k = count6bits(n->in_odstmsk6.i6); mask = &n->in_odstmsk6; } else { k = 0; mask = NULL; } ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_rdr_mask); if (n->in_rnext != NULL) n->in_rnext->in_prnext = n->in_prnext; *n->in_prnext = n->in_rnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_delmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a NAT map rule from the hash table of NAT map rules. */ /* ------------------------------------------------------------------------ */ void ipf_nat6_delmap(softn, n) ipf_nat_softc_t *softn; ipnat_t *n; { i6addr_t *mask; int k; if (n->in_osrcatype == FRI_NORMAL) { k = count6bits(n->in_osrcmsk6.i6); mask = &n->in_osrcmsk6; } else { k = 0; mask = NULL; } ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_map_mask); if (n->in_mnext != NULL) n->in_mnext->in_pmnext = n->in_pmnext; *n->in_pmnext = n->in_mnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_hostmap */ /* Returns: struct hostmap* - NULL if no hostmap could be created, */ /* else a pointer to the hostmapping to use */ /* Parameters: np(I) - pointer to NAT rule */ /* real(I) - real IP address */ /* map(I) - mapped IP address */ /* port(I) - destination port number */ /* Write Locks: ipf_nat */ /* */ /* Check if an ip address has already been allocated for a given mapping */ /* that is not doing port based translation. If is not yet allocated, then */ /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ /* ------------------------------------------------------------------------ */ static struct hostmap * ipf_nat6_hostmap(softn, np, src, dst, map, port) ipf_nat_softc_t *softn; ipnat_t *np; i6addr_t *src, *dst, *map; u_32_t port; { hostmap_t *hm; u_int hv; hv = (src->i6[3] ^ dst->i6[3]); hv += (src->i6[2] ^ dst->i6[2]); hv += (src->i6[1] ^ dst->i6[1]); hv += (src->i6[0] ^ dst->i6[0]); hv += src->i6[3]; hv += src->i6[2]; hv += src->i6[1]; hv += src->i6[0]; hv += dst->i6[3]; hv += dst->i6[2]; hv += dst->i6[1]; hv += dst->i6[0]; hv %= HOSTMAP_SIZE; for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_next) if (IP6_EQ(&hm->hm_osrc6, src) && IP6_EQ(&hm->hm_odst6, dst) && ((np == NULL) || (np == hm->hm_ipnat)) && ((port == 0) || (port == hm->hm_port))) { softn->ipf_nat_stats.ns_hm_addref++; hm->hm_ref++; return hm; } if (np == NULL) { softn->ipf_nat_stats.ns_hm_nullnp++; return NULL; } KMALLOC(hm, hostmap_t *); if (hm) { hm->hm_next = softn->ipf_hm_maplist; hm->hm_pnext = &softn->ipf_hm_maplist; if (softn->ipf_hm_maplist != NULL) softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; softn->ipf_hm_maplist = hm; hm->hm_hnext = softn->ipf_hm_maptable[hv]; hm->hm_phnext = softn->ipf_hm_maptable + hv; if (softn->ipf_hm_maptable[hv] != NULL) softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; softn->ipf_hm_maptable[hv] = hm; hm->hm_ipnat = np; np->in_use++; hm->hm_osrcip6 = *src; hm->hm_odstip6 = *dst; hm->hm_nsrcip6 = *map; hm->hm_ndstip6.i6[0] = 0; hm->hm_ndstip6.i6[1] = 0; hm->hm_ndstip6.i6[2] = 0; hm->hm_ndstip6.i6[3] = 0; hm->hm_ref = 1; hm->hm_port = port; hm->hm_hv = hv; hm->hm_v = 6; softn->ipf_nat_stats.ns_hm_new++; } else { softn->ipf_nat_stats.ns_hm_newfail++; } return hm; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_newmap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* Given an empty NAT structure, populate it with new information about a */ /* new NAT session, as defined by the matching NAT rule. */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ int ipf_nat6_newmap(fin, nat, ni) fr_info_t *fin; nat_t *nat; natinfo_t *ni; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short st_port, dport, sport, port, sp, dp; i6addr_t in, st_ip; hostmap_t *hm; u_32_t flags; ipnat_t *np; nat_t *natl; int l; /* * If it's an outbound packet which doesn't match any existing * record, then create a new port */ l = 0; hm = NULL; np = ni->nai_np; st_ip = np->in_snip6; st_port = np->in_spnext; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { sport = fin->fin_data[1]; dport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* * Do a loop until we either run out of entries to try or we find * a NAT mapping that isn't currently being used. This is done * because the change to the source is not (usually) being fixed. */ do { port = 0; in = np->in_nsrc.na_nextaddr; if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, &fin->fin_dst6, &in, 0); if (hm != NULL) in = hm->hm_nsrcip6; } else if ((l == 1) && (hm != NULL)) { ipf_nat_hostmapdel(softc, &hm); } nat->nat_hm = hm; if (IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0)) { if (l > 0) { NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_1); return -1; } } if ((np->in_redir == NAT_BIMAP) && IP6_EQ(&np->in_osrcmsk6, &np->in_nsrcmsk6)) { i6addr_t temp; /* * map the address block in a 1:1 fashion */ temp.i6[0] = fin->fin_src6.i6[0] & ~np->in_osrcmsk6.i6[0]; temp.i6[1] = fin->fin_src6.i6[1] & ~np->in_osrcmsk6.i6[1]; temp.i6[2] = fin->fin_src6.i6[2] & ~np->in_osrcmsk6.i6[0]; temp.i6[3] = fin->fin_src6.i6[3] & ~np->in_osrcmsk6.i6[3]; in = np->in_nsrcip6; IP6_MERGE(&in, &temp, &np->in_osrc); #ifdef NEED_128BIT_MATH } else if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && !(flags & IPN_TCPUDP))) { NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_2); return -1; } /* * map-block - Calculate destination address. */ IP6_MASK(&in, &fin->fin_src6, &np->in_osrcmsk6); in = ntohl(in); inb = in; in.s_addr /= np->in_ippip; in.s_addr &= ntohl(~np->in_nsrcmsk6); in.s_addr += ntohl(np->in_nsrcaddr6); /* * Calculate destination port. */ if ((flags & IPN_TCPUDP) && (np->in_ppip != 0)) { port = ntohs(sport) + l; port %= np->in_ppip; port += np->in_ppip * (inb.s_addr % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } #endif } else if (IP6_ISZERO(&np->in_nsrcaddr) && IP6_ISONES(&np->in_nsrcmsk)) { /* * 0/32 - use the interface's IP address. */ if ((l > 0) || ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) { NBUMPSIDE6DX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); return -1; } } else if (IP6_ISZERO(&np->in_nsrcip6) && IP6_ISZERO(&np->in_nsrcmsk6)) { /* * 0/0 - use the original source address/port. */ if (l > 0) { NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_3); return -1; } in = fin->fin_src6; } else if (!IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) { IP6_INC(&np->in_snip6); } natl = NULL; if ((flags & IPN_TCPUDP) && ((np->in_redir & NAT_MAPBLK) == 0) && (np->in_flags & IPN_AUTOPORTMAP)) { #ifdef NEED_128BIT_MATH /* * "ports auto" (without map-block) */ if ((l > 0) && (l % np->in_ppip == 0)) { if ((l > np->in_ppip) && !IP6_ISONES(&np->in_nsrcmsk)) { IP6_INC(&np->in_snip6) } } if (np->in_ppip != 0) { port = ntohs(sport); port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * (ntohl(fin->fin_src6) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } #endif } else if (((np->in_redir & NAT_MAPBLK) == 0) && (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { /* * Standard port translation. Select next port. */ if (np->in_flags & IPN_SEQUENTIAL) { port = np->in_spnext; } else { port = ipf_random() % (np->in_spmax - np->in_spmin + 1); port += np->in_spmin; } port = htons(port); np->in_spnext++; if (np->in_spnext > np->in_spmax) { np->in_spnext = np->in_spmin; if (!IP6_ISONES(&np->in_nsrcmsk6)) { IP6_INC(&np->in_snip6); } } } if (np->in_flags & IPN_SIPRANGE) { if (IP6_GT(&np->in_snip, &np->in_nsrcmsk)) np->in_snip6 = np->in_nsrcip6; } else { i6addr_t a1, a2; a1 = np->in_snip6; IP6_INC(&a1); IP6_AND(&a1, &np->in_nsrcmsk6, &a2); if (!IP6_ISONES(&np->in_nsrcmsk6) && IP6_GT(&a2, &np->in_nsrcip6)) { IP6_ADD(&np->in_nsrcip6, 1, &np->in_snip6); } } if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) port = sport; /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. */ sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[0] = fin->fin_data[1]; fin->fin_data[1] = ntohs(port); natl = ipf_nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, &fin->fin_dst6.in6, &in.in6); fin->fin_data[0] = sp; fin->fin_data[1] = dp; /* * Has the search wrapped around and come back to the * start ? */ if ((natl != NULL) && (np->in_spnext != 0) && (st_port == np->in_spnext) && (!IP6_ISZERO(&np->in_snip6) && IP6_EQ(&st_ip, &np->in_snip6))) { NBUMPSIDE6D(1, ns_wrap); return -1; } l++; } while (natl != NULL); /* Setup the NAT table */ nat->nat_osrc6 = fin->fin_src6; nat->nat_nsrc6 = in; nat->nat_odst6 = fin->fin_dst6; nat->nat_ndst6 = fin->fin_dst6; if (nat->nat_hm == NULL) nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, &fin->fin_dst6, &nat->nat_nsrc6, 0); if (flags & IPN_TCPUDP) { nat->nat_osport = sport; nat->nat_nsport = port; /* sport */ nat->nat_odport = dport; nat->nat_ndport = dport; ((tcphdr_t *)fin->fin_dp)->th_sport = port; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port; nat->nat_nicmpid = port; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_newrdr */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ int ipf_nat6_newrdr(fin, nat, ni) fr_info_t *fin; nat_t *nat; natinfo_t *ni; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short nport, dport, sport; u_short sp, dp; hostmap_t *hm; u_32_t flags; i6addr_t in; ipnat_t *np; nat_t *natl; int move; move = 1; hm = NULL; in.i6[0] = 0; in.i6[1] = 0; in.i6[2] = 0; in.i6[3] = 0; np = ni->nai_np; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { dport = fin->fin_data[1]; sport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* TRACE sport, dport */ /* * If the matching rule has IPN_STICKY set, then we want to have the * same rule kick in as before. Why would this happen? If you have * a collection of rdr rules with "round-robin sticky", the current * packet might match a different one to the previous connection but * we want the same destination to be used. */ if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && ((np->in_flags & IPN_STICKY) != 0)) { hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, &fin->fin_dst6, &in, (u_32_t)dport); if (hm != NULL) { in = hm->hm_ndstip6; np = hm->hm_ipnat; ni->nai_np = np; move = 0; } } /* * Otherwise, it's an inbound packet. Most likely, we don't * want to rewrite source ports and source addresses. Instead, * we want to rewrite to a fixed internal address and fixed * internal port. */ if (np->in_flags & IPN_SPLIT) { in = np->in_dnip6; if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, &fin->fin_dst6, &in, (u_32_t)dport); if (hm != NULL) { in = hm->hm_ndstip6; move = 0; } } if (hm == NULL || hm->hm_ref == 1) { if (IP6_EQ(&np->in_ndstip6, &in)) { np->in_dnip6 = np->in_ndstmsk6; move = 0; } else { np->in_dnip6 = np->in_ndstip6; } } } else if (IP6_ISZERO(&np->in_ndstaddr) && IP6_ISONES(&np->in_ndstmsk)) { /* * 0/32 - use the interface's IP address. */ if (ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) { NBUMPSIDE6DX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); return -1; } } else if (IP6_ISZERO(&np->in_ndstip6) && IP6_ISZERO(&np->in_ndstmsk6)) { /* * 0/0 - use the original destination address/port. */ in = fin->fin_dst6; } else if (np->in_redir == NAT_BIMAP && IP6_EQ(&np->in_ndstmsk6, &np->in_odstmsk6)) { i6addr_t temp; /* * map the address block in a 1:1 fashion */ temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_osrcmsk6.i6[0]; temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_osrcmsk6.i6[1]; temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_osrcmsk6.i6[0]; temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_osrcmsk6.i6[3]; in = np->in_ndstip6; IP6_MERGE(&in, &temp, &np->in_ndstmsk6); } else { in = np->in_ndstip6; } if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) nport = dport; else { /* * Whilst not optimized for the case where * pmin == pmax, the gain is not significant. */ if (((np->in_flags & IPN_FIXEDDPORT) == 0) && (np->in_odport != np->in_dtop)) { nport = ntohs(dport) - np->in_odport + np->in_dpmax; nport = htons(nport); } else { nport = htons(np->in_dpnext); np->in_dpnext++; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } /* * When the redirect-to address is set to 0.0.0.0, just * assume a blank `forwarding' of the packet. We don't * setup any translation for this either. */ if (IP6_ISZERO(&in)) { if (nport == dport) { NBUMPSIDE6D(0, ns_xlate_null); return -1; } in = fin->fin_dst6; } /* * Check to see if this redirect mapping already exists and if * it does, return "failure" (allowing it to be created will just * cause one or both of these "connections" to stop working.) */ sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[1] = fin->fin_data[0]; fin->fin_data[0] = ntohs(nport); natl = ipf_nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, &in.in6, &fin->fin_src6.in6); fin->fin_data[0] = sp; fin->fin_data[1] = dp; if (natl != NULL) { NBUMPSIDE6D(0, ns_xlate_exists); return -1; } nat->nat_ndst6 = in; nat->nat_odst6 = fin->fin_dst6; nat->nat_nsrc6 = fin->fin_src6; nat->nat_osrc6 = fin->fin_src6; if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, &fin->fin_dst6, &in, (u_32_t)dport); if (flags & IPN_TCPUDP) { nat->nat_odport = dport; nat->nat_ndport = nport; nat->nat_osport = sport; nat->nat_nsport = sport; ((tcphdr_t *)fin->fin_dp)->th_dport = nport; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport; nat->nat_nicmpid = nport; } return move; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_add */ /* Returns: nat6_t* - NULL == failure to create new NAT structure, */ /* else pointer to new NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* natsave(I) - pointer to where to store NAT struct pointer */ /* flags(I) - flags describing the current packet */ /* direction(I) - direction of packet (in/out) */ /* Write Lock: ipf_nat */ /* */ /* Attempts to create a new NAT entry. Does not actually change the packet */ /* in any way. */ /* */ /* This fucntion is in three main parts: (1) deal with creating a new NAT */ /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ /* and (3) building that structure and putting it into the NAT table(s). */ /* */ /* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ /* as it can result in memory being corrupted. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat6_add(fin, np, natsave, flags, direction) fr_info_t *fin; ipnat_t *np; nat_t **natsave; u_int flags; int direction; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm = NULL; nat_t *nat, *natl; natstat_t *nsp; u_int nflags; natinfo_t ni; int move; #if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC) qpktinfo_t *qpi = fin->fin_qpi; #endif nsp = &softn->ipf_nat_stats; if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > softn->ipf_nat_table_wm_high) { softn->ipf_nat_doflush = 1; } if (nsp->ns_active >= softn->ipf_nat_table_max) { NBUMPSIDE6(fin->fin_out, ns_table_max); return NULL; } move = 1; nflags = np->in_flags & flags; nflags &= NAT_FROMRULE; ni.nai_np = np; ni.nai_dport = 0; ni.nai_sport = 0; /* Give me a new nat */ KMALLOC(nat, nat_t *); if (nat == NULL) { NBUMPSIDE6(fin->fin_out, ns_memfail); /* * Try to automatically tune the max # of entries in the * table allowed to be less than what will cause kmem_alloc() * to fail and try to eliminate panics due to out of memory * conditions arising. */ if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && (nsp->ns_active > 100)) { softn->ipf_nat_table_max = nsp->ns_active - 100; printf("table_max reduced to %d\n", softn->ipf_nat_table_max); } return NULL; } if (flags & IPN_ICMPQUERY) { /* * In the ICMP query NAT code, we translate the ICMP id fields * to make them unique. This is indepedent of the ICMP type * (e.g. in the unlikely event that a host sends an echo and * an tstamp request with the same id, both packets will have * their ip address/id field changed in the same way). */ /* The icmp6_id field is used by the sender to identify the * process making the icmp request. (the receiver justs * copies it back in its response). So, it closely matches * the concept of source port. We overlay sport, so we can * maximally reuse the existing code. */ ni.nai_sport = fin->fin_data[1]; ni.nai_dport = 0; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; nat->nat_redir = np->in_redir; nat->nat_dir = direction; nat->nat_pr[0] = fin->fin_p; nat->nat_pr[1] = fin->fin_p; /* * Search the current table for a match and create a new mapping * if there is none found. */ if (np->in_redir & NAT_DIVERTUDP) { move = ipf_nat6_newdivert(fin, nat, &ni); } else if (np->in_redir & NAT_REWRITE) { move = ipf_nat6_newrewrite(fin, nat, &ni); } else if (direction == NAT_OUTBOUND) { /* * We can now arrange to call this for the same connection * because ipf_nat6_new doesn't protect the code path into * this function. */ natl = ipf_nat6_outlookup(fin, nflags, (u_int)fin->fin_p, &fin->fin_src6.in6, &fin->fin_dst6.in6); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat6_newmap(fin, nat, &ni); } else { /* * NAT_INBOUND is used for redirects rules */ natl = ipf_nat6_inlookup(fin, nflags, (u_int)fin->fin_p, &fin->fin_src6.in6, &fin->fin_dst6.in6); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat6_newrdr(fin, nat, &ni); } if (move == -1) goto badnat; np = ni.nai_np; nat->nat_mssclamp = np->in_mssclamp; nat->nat_me = natsave; nat->nat_fr = fin->fin_fr; nat->nat_rev = fin->fin_rev; nat->nat_ptr = np; nat->nat_dlocal = np->in_dlocal; if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { if (ipf_proxy_new(fin, nat) == -1) { NBUMPSIDE6D(fin->fin_out, ns_appr_fail); goto badnat; } } nat->nat_ifps[0] = np->in_ifps[0]; if (np->in_ifps[0] != NULL) { COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); } nat->nat_ifps[1] = np->in_ifps[1]; if (np->in_ifps[1] != NULL) { COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); } if (ipf_nat6_finalise(fin, nat) == -1) { goto badnat; } np->in_use++; if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { ipf_nat6_delrdr(softn, np); ipf_nat6_addrdr(softn, np); } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { ipf_nat6_delmap(softn, np); ipf_nat6_addmap(softn, np); } } if (flags & SI_WILDP) nsp->ns_wilds++; softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]++; goto done; badnat: NBUMPSIDE6(fin->fin_out, ns_badnatnew); if ((hm = nat->nat_hm) != NULL) ipf_nat_hostmapdel(softc, &hm); KFREE(nat); nat = NULL; done: if (nat != NULL && np != NULL) np->in_hits++; if (natsave != NULL) *natsave = nat; return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_finalise */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* Write Lock: ipf_nat */ /* */ /* This is the tail end of constructing a new NAT entry and is the same */ /* for both IPv4 and IPv6. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ int ipf_nat6_finalise(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; frentry_t *fr; u_32_t flags; flags = nat->nat_flags; switch (fin->fin_p) { case IPPROTO_ICMPV6 : sum1 = LONG_SUM6(&nat->nat_osrc6); sum1 += ntohs(nat->nat_oicmpid); sum2 = LONG_SUM6(&nat->nat_nsrc6); sum2 += ntohs(nat->nat_nicmpid); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM6(&nat->nat_odst6); sum2 = LONG_SUM6(&nat->nat_ndst6); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; case IPPROTO_TCP : case IPPROTO_UDP : sum1 = LONG_SUM6(&nat->nat_osrc6); sum1 += ntohs(nat->nat_osport); sum2 = LONG_SUM6(&nat->nat_nsrc6); sum2 += ntohs(nat->nat_nsport); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM6(&nat->nat_odst6); sum1 += ntohs(nat->nat_odport); sum2 = LONG_SUM6(&nat->nat_ndst6); sum2 += ntohs(nat->nat_ndport); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; default : sum1 = LONG_SUM6(&nat->nat_osrc6); sum2 = LONG_SUM6(&nat->nat_nsrc6); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM6(&nat->nat_odst6); sum2 = LONG_SUM6(&nat->nat_ndst6); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; } /* * Compute the partial checksum, just in case. * This is only ever placed into outbound packets so care needs * to be taken over which pair of addresses are used. */ if (nat->nat_dir == NAT_OUTBOUND) { sum1 = LONG_SUM6(&nat->nat_nsrc6); sum1 += LONG_SUM6(&nat->nat_ndst6); } else { sum1 = LONG_SUM6(&nat->nat_osrc6); sum1 += LONG_SUM6(&nat->nat_odst6); } sum1 += nat->nat_pr[1]; nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); if ((nat->nat_flags & SI_CLONE) == 0) nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); } nat->nat_v[0] = 6; nat->nat_v[1] = 6; if (ipf_nat6_insert(softc, softn, nat) == 0) { if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, nat, NL_NEW); fr = nat->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } return 0; } NBUMPSIDE6D(fin->fin_out, ns_unfinalised); /* * nat6_insert failed, so cleanup time... */ if (nat->nat_sync != NULL) ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_insert */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* Insert a NAT entry into the hash tables for searching and add it to the */ /* list of active NAT entries. Adjust global counters when complete. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_insert(softc, softn, nat) ipf_main_softc_t *softc; ipf_nat_softc_t *softn; nat_t *nat; { u_int hv1, hv2; u_32_t sp, dp; ipnat_t *in; /* * Try and return an error as early as possible, so calculate the hash * entry numbers first and then proceed. */ if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_osport; dp = nat->nat_odport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_oicmpid; } else { sp = 0; dp = 0; } hv1 = NAT_HASH_FN6(&nat->nat_osrc6, sp, 0xffffffff); hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1 + dp, softn->ipf_nat_table_sz); /* * TRACE nat6_osrc6, nat6_osport, nat6_odst6, * nat6_odport, hv1 */ if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_nsport; dp = nat->nat_ndport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_nicmpid; } else { sp = 0; dp = 0; } hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, sp, 0xffffffff); hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2 + dp, softn->ipf_nat_table_sz); /* * TRACE nat6_nsrcaddr, nat6_nsport, nat6_ndstaddr, * nat6_ndport, hv1 */ } else { hv1 = NAT_HASH_FN6(&nat->nat_osrc6, 0, 0xffffffff); hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1, softn->ipf_nat_table_sz); /* TRACE nat6_osrcip6, nat6_odstip6, hv1 */ hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, 0, 0xffffffff); hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2, softn->ipf_nat_table_sz); /* TRACE nat6_nsrcip6, nat6_ndstip6, hv2 */ } nat->nat_hv[0] = hv1; nat->nat_hv[1] = hv2; MUTEX_INIT(&nat->nat_lock, "nat entry lock"); in = nat->nat_ptr; nat->nat_ref = nat->nat_me ? 2 : 1; nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], nat->nat_v[0]); if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1], nat->nat_v[1]); } else if (in->in_ifnames[1] != -1) { char *name; name = in->in_names + in->in_ifnames[1]; if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], LIFNAMSIZ); nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = nat->nat_ifps[0]; } } if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); } return ipf_nat_hashtab_add(softc, softn, nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_icmperrorlookup */ /* Returns: nat6_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* dir(I) - direction of packet (in/out) */ /* */ /* Check if the ICMP error message is related to an existing TCP, UDP or */ /* ICMP query nat entry. It is assumed that the packet is already of the */ /* the required length. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat6_icmperrorlookup(fin, dir) fr_info_t *fin; int dir; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6, *orgicmp; int flags = 0, type, minlen; nat_stat_side_t *nside; tcphdr_t *tcp = NULL; u_short data[2]; ip6_t *oip6; nat_t *nat; u_int p; minlen = 40; icmp6 = fin->fin_dp; type = icmp6->icmp6_type; nside = &softn->ipf_nat_stats.ns_side6[fin->fin_out]; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. Also, if it's not an error type, then return. */ if (!(fin->fin_flx & FI_ICMPERR)) { ATOMIC_INCL(nside->ns_icmp_basic); return NULL; } /* * Check packet size */ if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) { ATOMIC_INCL(nside->ns_icmp_size); return NULL; } oip6 = (ip6_t *)((char *)fin->fin_dp + 8); /* * Is the buffer big enough for all of it ? It's the size of the IP * header claimed in the encapsulated part which is of concern. It * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we * do the pullup early in ipf_check() and thus can't gaurantee it is * all here now. */ #ifdef _KERNEL { mb_t *m; m = fin->fin_m; # if defined(MENTAT) if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) { ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; } # else if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)fin->fin_ip + M_LEN(m)) { ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; } # endif } #endif if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) { ATOMIC_INCL(nside->ns_icmp_address); return NULL; } p = oip6->ip6_nxt; if (p == IPPROTO_TCP) flags = IPN_TCP; else if (p == IPPROTO_UDP) flags = IPN_UDP; else if (p == IPPROTO_ICMPV6) { orgicmp = (struct icmp6_hdr *)(oip6 + 1); /* see if this is related to an ICMP query */ if (ipf_nat6_icmpquerytype(orgicmp->icmp6_type)) { data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; fin->fin_data[0] = 0; fin->fin_data[1] = orgicmp->icmp6_id; flags = IPN_ICMPERR|IPN_ICMPQUERY; /* * NOTE : dir refers to the direction of the original * ip packet. By definition the icmp error * message flows in the opposite direction. */ if (dir == NAT_INBOUND) nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst, &oip6->ip6_src); else nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst, &oip6->ip6_src); fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } } if (flags & IPN_TCPUDP) { minlen += 8; /* + 64bits of data to get ports */ /* TRACE (fin,minlen) */ if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { ATOMIC_INCL(nside->ns_icmp_short); return NULL; } data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; tcp = (tcphdr_t *)(oip6 + 1); fin->fin_data[0] = ntohs(tcp->th_dport); fin->fin_data[1] = ntohs(tcp->th_sport); if (dir == NAT_INBOUND) { nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst, &oip6->ip6_src); } else { nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst, &oip6->ip6_src); } fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } if (dir == NAT_INBOUND) nat = ipf_nat6_inlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src); else nat = ipf_nat6_outlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src); return nat; } /* result = ip1 - ip2 */ u_32_t ipf_nat6_ip6subtract(ip1, ip2) i6addr_t *ip1, *ip2; { i6addr_t l1, l2, d; u_short *s1, *s2, *ds; u_32_t r; int i, neg; neg = 0; l1 = *ip1; l2 = *ip2; s1 = (u_short *)&l1; s2 = (u_short *)&l2; ds = (u_short *)&d; for (i = 7; i > 0; i--) { if (s1[i] > s2[i]) { ds[i] = s2[i] + 0x10000 - s1[i]; s2[i - 1] += 0x10000; } else { ds[i] = s2[i] - s1[i]; } } if (s2[0] > s1[0]) { ds[0] = s2[0] + 0x10000 - s1[0]; neg = 1; } else { ds[0] = s2[0] - s1[0]; } for (i = 0, r = 0; i < 8; i++) { r += ds[i]; } return r; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_icmperror */ /* Returns: nat6_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* nflags(I) - NAT flags for this packet */ /* dir(I) - direction of packet (in/out) */ /* */ /* Fix up an ICMP packet which is an error message for an existing NAT */ /* session. This will correct both packet header data and checksums. */ /* */ /* This should *ONLY* be used for incoming ICMP error packets to make sure */ /* a NAT'd ICMP packet gets correctly recognised. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat6_icmperror(fin, nflags, dir) fr_info_t *fin; u_int *nflags; int dir; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd, sumd2; i6addr_t a1, a2, a3, a4; struct icmp6_hdr *icmp6; int flags, dlen, odst; u_short *csump; tcphdr_t *tcp; ip6_t *oip6; nat_t *nat; void *dp; if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { NBUMPSIDE6D(fin->fin_out, ns_icmp_short); return NULL; } /* * ipf_nat6_icmperrorlookup() will return NULL for `defective' packets. */ if ((fin->fin_v != 6) || !(nat = ipf_nat6_icmperrorlookup(fin, dir))) { NBUMPSIDE6D(fin->fin_out, ns_icmp_notfound); return NULL; } tcp = NULL; csump = NULL; flags = 0; sumd2 = 0; *nflags = IPN_ICMPERR; icmp6 = fin->fin_dp; oip6 = (ip6_t *)((u_char *)icmp6 + sizeof(*icmp6)); dp = (u_char *)oip6 + sizeof(*oip6); if (oip6->ip6_nxt == IPPROTO_TCP) { tcp = (tcphdr_t *)dp; csump = (u_short *)&tcp->th_sum; flags = IPN_TCP; } else if (oip6->ip6_nxt == IPPROTO_UDP) { udphdr_t *udp; udp = (udphdr_t *)dp; tcp = (tcphdr_t *)dp; csump = (u_short *)&udp->uh_sum; flags = IPN_UDP; } else if (oip6->ip6_nxt == IPPROTO_ICMPV6) flags = IPN_ICMPQUERY; dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the * IP address change as it will be modified again in ipf_nat6_checkout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice * for the IP#. */ /* * Step 1 * Fix the IP addresses in the offending IP packet. You also need * to adjust the IP header checksum of that offending IP packet. * * Normally, you would expect that the ICMP checksum of the * ICMP error message needs to be adjusted as well for the * IP address change in oip. * However, this is a NOP, because the ICMP checksum is * calculated over the complete ICMP packet, which includes the * changed oip IP addresses and oip6->ip6_sum. However, these * two changes cancel each other out (if the delta for * the IP address is x, then the delta for ip_sum is minus x), * so no change in the icmp_cksum is necessary. * * Inbound ICMP * ------------ * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(b)=nat6_newdstip *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(b)=nat6_olddstip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(d)=nat6_newdstip *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(d)=nat6_olddstip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip * * Outbound ICMP * ------------- * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) * - OIP_SRC(a)=nat6_newsrcip, OIP_DST(c)=nat6_newdstip *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat6_olddstip, OIP_DST(d)=nat6_oldsrcip *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat6_newsrcip, OIP_DST(a)=nat6_newdstip *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip */ if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { a1 = nat->nat_osrc6; a4.in6 = oip6->ip6_src; a3 = nat->nat_odst6; a2.in6 = oip6->ip6_dst; oip6->ip6_src = a1.in6; oip6->ip6_dst = a3.in6; odst = 1; } else { a1 = nat->nat_ndst6; a2.in6 = oip6->ip6_dst; a3 = nat->nat_nsrc6; a4.in6 = oip6->ip6_src; oip6->ip6_dst = a3.in6; oip6->ip6_src = a1.in6; odst = 0; } sumd = 0; if (IP6_NEQ(&a3, &a2) || IP6_NEQ(&a1, &a4)) { if (IP6_GT(&a3, &a2)) { sumd = ipf_nat6_ip6subtract(&a2, &a3); sumd--; } else { sumd = ipf_nat6_ip6subtract(&a2, &a3); } if (IP6_GT(&a1, &a4)) { sumd += ipf_nat6_ip6subtract(&a4, &a1); sumd--; } else { sumd += ipf_nat6_ip6subtract(&a4, &a1); } sumd = ~sumd; } sumd2 = sumd; sum1 = 0; sum2 = 0; /* * Fix UDP pseudo header checksum to compensate for the * IP address change. */ if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { u_32_t sum3, sum4; /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as * well, based on the NAT specification. Of course such * a change may be reflected in the ICMP checksum as well. * * Since the port fields are part of the TCP/UDP checksum * of the offending IP packet, you need to adjust that checksum * as well... except that the change in the port numbers should * be offset by the checksum change. However, the TCP/UDP * checksum will also need to change if there has been an * IP address change. */ if (odst == 1) { sum1 = ntohs(nat->nat_osport); sum4 = ntohs(tcp->th_sport); sum3 = ntohs(nat->nat_odport); sum2 = ntohs(tcp->th_dport); tcp->th_sport = htons(sum1); tcp->th_dport = htons(sum3); } else { sum1 = ntohs(nat->nat_ndport); sum2 = ntohs(tcp->th_dport); sum3 = ntohs(nat->nat_nsport); sum4 = ntohs(tcp->th_sport); tcp->th_dport = htons(sum3); tcp->th_sport = htons(sum1); } sumd += sum1 - sum4; sumd += sum3 - sum2; if (sumd != 0 || sumd2 != 0) { /* * At this point, sumd is the delta to apply to the * TCP/UDP header, given the changes in both the IP * address and the ports and sumd2 is the delta to * apply to the ICMP header, given the IP address * change delta that may need to be applied to the * TCP/UDP checksum instead. * * If we will both the IP and TCP/UDP checksums * then the ICMP checksum changes by the address * delta applied to the TCP/UDP checksum. If we * do not change the TCP/UDP checksum them we * apply the delta in ports to the ICMP checksum. */ if (oip6->ip6_nxt == IPPROTO_UDP) { if ((dlen >= 8) && (*csump != 0)) { ipf_fix_datacksum(csump, sumd); } else { sumd2 = sum4 - sum1; if (sum1 > sum4) sumd2--; sumd2 += sum2 - sum3; if (sum3 > sum2) sumd2--; } } else if (oip6->ip6_nxt == IPPROTO_TCP) { if (dlen >= 18) { ipf_fix_datacksum(csump, sumd); } else { sumd2 = sum4 - sum1; if (sum1 > sum4) sumd2--; sumd2 += sum2 - sum3; if (sum3 > sum2) sumd2--; } } if (sumd2 != 0) { sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); ipf_fix_incksum(0, &icmp6->icmp6_cksum, sumd2, 0); } } } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { struct icmp6_hdr *orgicmp; /* * XXX - what if this is bogus hl and we go off the end ? * In this case, ipf_nat6_icmperrorlookup() will have * returned NULL. */ orgicmp = (struct icmp6_hdr *)dp; if (odst == 1) { if (orgicmp->icmp6_id != nat->nat_osport) { /* * Fix ICMP checksum (of the offening ICMP * query packet) to compensate the change * in the ICMP id of the offending ICMP * packet. * * Since you modify orgicmp->icmp6_id with * a delta (say x) and you compensate that * in origicmp->icmp6_cksum with a delta * minus x, you don't have to adjust the * overall icmp->icmp6_cksum */ sum1 = ntohs(orgicmp->icmp6_id); sum2 = ntohs(nat->nat_osport); CALC_SUMD(sum1, sum2, sumd); orgicmp->icmp6_id = nat->nat_oicmpid; ipf_fix_datacksum(&orgicmp->icmp6_cksum, sumd); } } /* nat6_dir == NAT_INBOUND is impossible for icmp queries */ } return nat; } /* * MAP-IN MAP-OUT RDR-IN RDR-OUT * osrc X == src == src X * odst X == dst == dst X * nsrc == dst X X == dst * ndst == src X X == src * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND */ /* * NB: these lookups don't lock access to the list, it assumed that it has * already been done! */ /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_inlookup */ /* Returns: nat6_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* mapdst(I) - destination IP address */ /* */ /* Lookup a nat entry based on the mapped destination ip address/port and */ /* real source address/port. We use this lookup when receiving a packet, */ /* we're looking for a table entry, based on the destination address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat6_inlookup(fin, flags, p, src, mapdst) fr_info_t *fin; u_int flags, p; struct in6_addr *src , *mapdst; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; grehdr_t *gre; ipnat_t *ipn; u_int sflags; nat_t *nat; int nflags; i6addr_t dst; void *ifp; u_int hv; ifp = fin->fin_ifp; sport = 0; dport = 0; gre = NULL; dst.in6 = *mapdst; sflags = flags & NAT_TCPUDPICMP; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMPV6 : if (flags & IPN_ICMPERR) sport = fin->fin_data[1]; else dport = fin->fin_data[1]; break; default : break; } if ((flags & SI_WILDP) != 0) goto find_in_wild_ports; hv = NAT_HASH_FN6(&dst, dport, 0xffffffff); hv = NAT_HASH_FN6(src, hv + sport, softn->ipf_nat_table_sz); nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, dport, src, sport, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : if (nat->nat_v[0] != 6) continue; if (IP6_NEQ(&nat->nat_osrc6, src) || IP6_NEQ(&nat->nat_odst6, &dst)) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_osport != sport) continue; if (nat->nat_odport != dport) continue; } else if (p == IPPROTO_ICMPV6) { if (nat->nat_osport != dport) { continue; } } break; case NAT_OUTBOUND : if (nat->nat_v[1] != 6) continue; if (IP6_NEQ(&nat->nat_ndst6, src) || IP6_NEQ(&nat->nat_nsrc6, &dst)) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMPV6) { if (nat->nat_osport != dport) { continue; } } break; } if ((nat->nat_flags & IPN_TCPUDP) != 0) { ipn = nat->nat_ptr; #ifdef IPF_V6_PROXIES if ((ipn != NULL) && (nat->nat_aps != NULL)) if (appr_match(fin, nat) != 0) continue; #endif } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_6(ifp); } return nat; } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_in_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_1); return NULL; } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDE6D(0, ns_lookup_nowild); return NULL; } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN6(&dst, 0, 0xffffffff); hv = NAT_HASH_FN6(src, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, src, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != fin->fin_p) continue; switch (nat->nat_dir) { case NAT_INBOUND : if (nat->nat_v[0] != 6) continue; if (IP6_NEQ(&nat->nat_osrc6, src) || IP6_NEQ(&nat->nat_odst6, &dst)) continue; break; case NAT_OUTBOUND : if (nat->nat_v[1] != 6) continue; if (IP6_NEQ(&nat->nat_ndst6, src) || IP6_NEQ(&nat->nat_nsrc6, &dst)) continue; break; } nflags = nat->nat_flags; if (!(nflags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, NAT_INBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nflags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_6(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat6_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_2); } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_tabmove */ /* Returns: Nil */ /* Parameters: nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* This function is only called for TCP/UDP NAT table entries where the */ /* original was placed in the table without hashing on the ports and we now */ /* want to include hashing on port numbers. */ /* ------------------------------------------------------------------------ */ static void ipf_nat6_tabmove(softn, nat) ipf_nat_softc_t *softn; nat_t *nat; { nat_t **natp; u_int hv0, hv1; if (nat->nat_flags & SI_CLONE) return; /* * Remove the NAT entry from the old location */ if (nat->nat_hnext[0]) nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; *nat->nat_phnext[0] = nat->nat_hnext[0]; softn->ipf_nat_stats.ns_side[0].ns_bucketlen[nat->nat_hv[0]]--; if (nat->nat_hnext[1]) nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; *nat->nat_phnext[1] = nat->nat_hnext[1]; softn->ipf_nat_stats.ns_side[1].ns_bucketlen[nat->nat_hv[1]]--; /* * Add into the NAT table in the new position */ hv0 = NAT_HASH_FN6(&nat->nat_osrc6, nat->nat_osport, 0xffffffff); hv0 = NAT_HASH_FN6(&nat->nat_odst6, hv0 + nat->nat_odport, softn->ipf_nat_table_sz); hv1 = NAT_HASH_FN6(&nat->nat_nsrc6, nat->nat_nsport, 0xffffffff); hv1 = NAT_HASH_FN6(&nat->nat_ndst6, hv1 + nat->nat_ndport, softn->ipf_nat_table_sz); if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { u_int swap; swap = hv0; hv0 = hv1; hv1 = swap; } /* TRACE nat_osrc6, nat_osport, nat_odst6, nat_odport, hv0 */ /* TRACE nat_nsrc6, nat_nsport, nat_ndst6, nat_ndport, hv1 */ nat->nat_hv[0] = hv0; natp = &softn->ipf_nat_table[0][hv0]; if (*natp) (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]++; nat->nat_hv[1] = hv1; natp = &softn->ipf_nat_table[1][hv1]; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_outlookup */ /* Returns: nat6_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* dst(I) - destination IP address */ /* rw(I) - 1 == write lock on held, 0 == read lock. */ /* */ /* Lookup a nat entry based on the source 'real' ip address/port and */ /* destination address/port. We use this lookup when sending a packet out, */ /* we're looking for a table entry, based on the source address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat6_outlookup(fin, flags, p, src, dst) fr_info_t *fin; u_int flags, p; struct in6_addr *src , *dst; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; u_int sflags; ipnat_t *ipn; nat_t *nat; void *ifp; u_int hv; ifp = fin->fin_ifp; sflags = flags & IPN_TCPUDPICMP; sport = 0; dport = 0; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMPV6 : if (flags & IPN_ICMPERR) sport = fin->fin_data[1]; else dport = fin->fin_data[1]; break; default : break; } if ((flags & SI_WILDP) != 0) goto find_out_wild_ports; hv = NAT_HASH_FN6(src, sport, 0xffffffff); hv = NAT_HASH_FN6(dst, hv + dport, softn->ipf_nat_table_sz); nat = softn->ipf_nat_table[0][hv]; /* TRACE src, sport, dst, dport, hv, nat */ for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : if (nat->nat_v[1] != 6) continue; if (IP6_NEQ(&nat->nat_ndst6, src) || IP6_NEQ(&nat->nat_nsrc6, dst)) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMPV6) { if (nat->nat_osport != dport) { continue; } } break; case NAT_OUTBOUND : if (nat->nat_v[0] != 6) continue; if (IP6_NEQ(&nat->nat_osrc6, src) || IP6_NEQ(&nat->nat_odst6, dst)) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_odport != dport) continue; if (nat->nat_osport != sport) continue; } else if (p == IPPROTO_ICMPV6) { if (nat->nat_osport != dport) { continue; } } break; } ipn = nat->nat_ptr; #ifdef IPF_V6_PROXIES if ((ipn != NULL) && (nat->nat_aps != NULL)) if (appr_match(fin, nat) != 0) continue; #endif if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_6(ifp); } return nat; } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_out_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_3); return NULL; } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDE6D(1, ns_lookup_nowild); return NULL; } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN6(src, 0, 0xffffffff); hv = NAT_HASH_FN6(dst, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != fin->fin_p) continue; switch (nat->nat_dir) { case NAT_INBOUND : if (nat->nat_v[1] != 6) continue; if (IP6_NEQ(&nat->nat_ndst6, src) || IP6_NEQ(&nat->nat_nsrc6, dst)) continue; break; case NAT_OUTBOUND : if (nat->nat_v[0] != 6) continue; if (IP6_NEQ(&nat->nat_osrc6, src) || IP6_NEQ(&nat->nat_odst6, dst)) continue; break; } if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, NAT_OUTBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nat->nat_flags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_OUTBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_6(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat6_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_4); } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_lookupredir */ /* Returns: nat6_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: np(I) - pointer to description of packet to find NAT table */ /* entry for. */ /* */ /* Lookup the NAT tables to search for a matching redirect */ /* The contents of natlookup_t should imitate those found in a packet that */ /* would be translated - ie a packet coming in for RDR or going out for MAP.*/ /* We can do the lookup in one of two ways, imitating an inbound or */ /* outbound packet. By default we assume outbound, unless IPN_IN is set. */ /* For IN, the fields are set as follows: */ /* nl_real* = source information */ /* nl_out* = destination information (translated) */ /* For an out packet, the fields are set like this: */ /* nl_in* = source information (untranslated) */ /* nl_out* = destination information (translated) */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat6_lookupredir(np) natlookup_t *np; { fr_info_t fi; nat_t *nat; bzero((char *)&fi, sizeof(fi)); if (np->nl_flags & IPN_IN) { fi.fin_data[0] = ntohs(np->nl_realport); fi.fin_data[1] = ntohs(np->nl_outport); } else { fi.fin_data[0] = ntohs(np->nl_inport); fi.fin_data[1] = ntohs(np->nl_outport); } if (np->nl_flags & IPN_TCP) fi.fin_p = IPPROTO_TCP; else if (np->nl_flags & IPN_UDP) fi.fin_p = IPPROTO_UDP; else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) fi.fin_p = IPPROTO_ICMPV6; /* * We can do two sorts of lookups: * - IPN_IN: we have the `real' and `out' address, look for `in'. * - default: we have the `in' and `out' address, look for `real'. */ if (np->nl_flags & IPN_IN) { if ((nat = ipf_nat6_inlookup(&fi, np->nl_flags, fi.fin_p, &np->nl_realip6, &np->nl_outip6))) { np->nl_inip6 = nat->nat_odst6.in6; np->nl_inport = nat->nat_odport; } } else { /* * If nl_inip is non null, this is a lookup based on the real * ip address. Else, we use the fake. */ if ((nat = ipf_nat6_outlookup(&fi, np->nl_flags, fi.fin_p, &np->nl_inip6, &np->nl_outip6))) { if ((np->nl_flags & IPN_FINDFORWARD) != 0) { fr_info_t fin; bzero((char *)&fin, sizeof(fin)); fin.fin_p = nat->nat_pr[0]; fin.fin_data[0] = ntohs(nat->nat_ndport); fin.fin_data[1] = ntohs(nat->nat_nsport); if (ipf_nat6_inlookup(&fin, np->nl_flags, fin.fin_p, &nat->nat_ndst6.in6, &nat->nat_nsrc6.in6) != NULL) { np->nl_flags &= ~IPN_FINDFORWARD; } } np->nl_realip6 = nat->nat_odst6.in6; np->nl_realport = nat->nat_odport; } } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_match */ /* Returns: int - 0 == no match, 1 == match */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* */ /* Pull the matching of a packet against a NAT rule out of that complex */ /* loop inside ipf_nat6_checkin() and lay it out properly in its own */ /* function. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_match(fin, np) fr_info_t *fin; ipnat_t *np; { frtuc_t *ft; int match; match = 0; switch (np->in_osrcatype) { case FRI_NORMAL : match = IP6_MASKNEQ(&fin->fin_src6, &np->in_osrcmsk6, &np->in_osrcip6); break; case FRI_LOOKUP : match = (*np->in_osrcfunc)(fin->fin_main_soft, np->in_osrcptr, 6, &fin->fin_src6, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTSRC) != 0); if (match) return 0; match = 0; switch (np->in_odstatype) { case FRI_NORMAL : match = IP6_MASKNEQ(&fin->fin_dst6, &np->in_odstmsk6, &np->in_odstip6); break; case FRI_LOOKUP : match = (*np->in_odstfunc)(fin->fin_main_soft, np->in_odstptr, 6, &fin->fin_dst6, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTDST) != 0); if (match) return 0; ft = &np->in_tuc; if (!(fin->fin_flx & FI_TCPUDP) || (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { if (ft->ftu_scmp || ft->ftu_dcmp) return 0; return 1; } return ipf_tcpudpchk(&fin->fin_fi, ft); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_checkout */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an outcoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int ipf_nat6_checkout(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6 = NULL; struct ifnet *ifp, *sifp; tcphdr_t *tcp = NULL; int rval, natfailed; ipnat_t *np = NULL; u_int nflags = 0; i6addr_t ipa, iph; int natadd = 1; frentry_t *fr; nat_t *nat; if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) return 0; icmp6 = NULL; natfailed = 0; fr = fin->fin_fr; sifp = fin->fin_ifp; if (fr != NULL) { ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; if ((ifp != NULL) && (ifp != (void *)-1)) fin->fin_ifp = ifp; } ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMPV6 : icmp6 = fin->fin_dp; /* * Apart from ECHO request and reply, all other * informational messages should not be translated * so as to keep IPv6 working. */ if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) return 0; /* * This is an incoming packet, so the destination is * the icmp6_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) nflags = IPN_ICMPQUERY; break; default : break; } if ((nflags & IPN_TCPUDP)) tcp = fin->fin_dp; } ipa = fin->fin_src6; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat6_icmperror(fin, &nflags, NAT_OUTBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat6_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, &fin->fin_src6.in6, &fin->fin_dst6.in6))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, nmsk = 0; i6addr_t *msk; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = &softn->ipf_nat6_map_active_masks[nmsk]; IP6_AND(&ipa, msk, &iph); hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_maprules_sz); for (np = softn->ipf_nat_map_rules[hv]; np; np = np->in_mnext) { if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) continue; if (np->in_v[0] != 6) continue; if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat6_match(fin, np)) { case 0 : continue; case -1 : rval = -1; goto outmatchfail; case 1 : default : break; } } else if (!IP6_MASKEQ(&ipa, &np->in_osrcmsk, &np->in_osrcip6)) continue; if ((fr != NULL) && !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) continue; #ifdef IPF_V6_PROXIES if (np->in_plabel != -1) { if (((np->in_flags & IPN_FILTER) == 0) && (np->in_odport != fin->fin_data[1])) continue; if (appr_ok(fin, tcp, np) == 0) continue; } #endif if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { np->in_hits++; break; } natfailed = -1; } if ((np == NULL) && (nmsk < softn->ipf_nat6_map_max)) { nmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat6_out(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[1] += fin->fin_plen; nat->nat_pkts[1]++; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; outmatchfail: RWLOCK_EXIT(&softc->ipf_nat); switch (rval) { case -1 : if (passp != NULL) { NBUMPSIDE6D(1, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV6; } fin->fin_flx |= FI_BADNAT; NBUMPSIDE6D(1, ns_badnat); break; case 0 : NBUMPSIDE6D(1, ns_ignored); break; case 1 : NBUMPSIDE6D(1, ns_translated); break; } fin->fin_ifp = sifp; return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_out */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* */ /* Translate a packet coming "out" on an interface. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_out(fin, nat, natadd, nflags) fr_info_t *fin; nat_t *nat; int natadd; u_32_t nflags; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; icmp6 = NULL; np = nat->nat_ptr; if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* * Address assignment is after the checksum modification because * we are using the address in the packet for determining the * correct checksum offset (the ICMP error could be coming from * anyone...) */ switch (nat->nat_dir) { case NAT_OUTBOUND : fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; fin->fin_src6 = nat->nat_nsrc6; fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; fin->fin_dst6 = nat->nat_ndst6; break; case NAT_INBOUND : fin->fin_ip6->ip6_src = nat->nat_odst6.in6; fin->fin_src6 = nat->nat_ndst6; fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; fin->fin_dst6 = nat->nat_nsrc6; break; case NAT_DIVERTIN : { mb_t *m; skip = ipf_nat6_decap(fin, nat); if (skip <= 0) { NBUMPSIDE6D(1, ns_decap_fail); return -1; } m = fin->fin_m; #if defined(MENTAT) && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); MUTEX_EXIT(&nat->nat_lock); fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; /* NOTREACHED */ } case NAT_DIVERTOUT : { udphdr_t *uh; ip6_t *ip6; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDE6D(1, ns_divert_dup); return -1; } ip6 = MTOD(m, ip6_t *); ip6->ip6_plen = htons(fin->fin_plen + 8); uh = (udphdr_t *)(ip6 + 1); uh->uh_ulen = htons(fin->fin_plen); PREP_MB_T(fin, m); fin->fin_ip6 = ip6; fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv4 hdr */ fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv4 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } default : break; } if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { u_short *csump; if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; switch (nat->nat_dir) { case NAT_OUTBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_INBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { icmp6 = fin->fin_dp; icmp6->icmp6_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); /* * The above comments do not hold for layer 4 (or higher) * checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_outcksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); else ipf_fix_incksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a redirect rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if ((np != NULL) && (np->in_apr != NULL)) { i = ipf_proxy_check(fin, nat); if (i == 0) { i = 1; } else if (i == -1) { NBUMPSIDE6D(1, ns_ipf_proxy_fail); } } else { i = 1; } fin->fin_flx |= FI_NATED; return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_checkin */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an incoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int ipf_nat6_checkin(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6; u_int nflags, natadd; int rval, natfailed; struct ifnet *ifp; i6addr_t ipa, iph; tcphdr_t *tcp; u_short dport; ipnat_t *np; nat_t *nat; if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) return 0; tcp = NULL; icmp6 = NULL; dport = 0; natadd = 1; nflags = 0; natfailed = 0; ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMPV6 : icmp6 = fin->fin_dp; /* * Apart from ECHO request and reply, all other * informational messages should not be translated * so as to keep IPv6 working. */ if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) return 0; /* * This is an incoming packet, so the destination is * the icmp6_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) { nflags = IPN_ICMPQUERY; dport = icmp6->icmp6_id; } break; default : break; } if ((nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; dport = fin->fin_data[1]; } } ipa = fin->fin_dst6; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat6_icmperror(fin, &nflags, NAT_INBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat6_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, &fin->fin_src6.in6, &ipa.in6))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, rmsk = 0; i6addr_t *msk; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = &softn->ipf_nat6_rdr_active_masks[rmsk]; IP6_AND(&ipa, msk, &iph); hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_rdrrules_sz); for (np = softn->ipf_nat_rdr_rules[hv]; np; np = np->in_rnext) { if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) continue; if (np->in_v[0] != 6) continue; if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat6_match(fin, np)) { case 0 : continue; case -1 : rval = -1; goto inmatchfail; case 1 : default : break; } } else { if (!IP6_MASKEQ(&ipa, &np->in_odstmsk6, &np->in_odstip6)) { continue; } if (np->in_odport && ((np->in_dtop < dport) || (dport < np->in_odport))) continue; } #ifdef IPF_V6_PROXIES if (np->in_plabel != -1) { if (!appr_ok(fin, tcp, np)) { continue; } } #endif if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_INBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { np->in_hits++; break; } natfailed = -1; } if ((np == NULL) && (rmsk < softn->ipf_nat6_rdr_max)) { rmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat6_in(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[0] += fin->fin_plen; nat->nat_pkts[0]++; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; inmatchfail: RWLOCK_EXIT(&softc->ipf_nat); switch (rval) { case -1 : if (passp != NULL) { NBUMPSIDE6D(0, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV6; } fin->fin_flx |= FI_BADNAT; NBUMPSIDE6D(0, ns_badnat); break; case 0 : NBUMPSIDE6D(0, ns_ignored); break; case 1 : NBUMPSIDE6D(0, ns_translated); break; } return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_in */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* Locks Held: (READ) */ /* */ /* Translate a packet coming "in" on an interface. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_in(fin, nat, natadd, nflags) fr_info_t *fin; nat_t *nat; int natadd; u_32_t nflags; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6; u_short *csump; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; csump = NULL; np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (np != NULL) { if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a map rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if (np->in_apr != NULL) { i = ipf_proxy_check(fin, nat); if (i == -1) { NBUMPSIDE6D(0, ns_ipf_proxy_fail); return -1; } } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. * Why only do this for some platforms on inbound packets ? * Because for those that it is done, IP processing is yet to happen * and so the IPv4 header checksum has not yet been evaluated. * Perhaps it should always be done for the benefit of things like * fast forwarding (so that it doesn't need to be recomputed) but with * header checksum offloading, perhaps it is a moot point. */ switch (nat->nat_dir) { case NAT_INBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; fin->fin_src6 = nat->nat_nsrc6; } fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; fin->fin_dst6 = nat->nat_ndst6; break; case NAT_OUTBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip6->ip6_src = nat->nat_odst6.in6; fin->fin_src6 = nat->nat_odst6; } fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; fin->fin_dst6 = nat->nat_osrc6; break; case NAT_DIVERTIN : { udphdr_t *uh; ip6_t *ip6; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDE6D(0, ns_divert_dup); return -1; } ip6 = MTOD(m, ip6_t *); ip6->ip6_plen = htons(fin->fin_plen + sizeof(udphdr_t)); uh = (udphdr_t *)(ip6 + 1); uh->uh_ulen = ntohs(fin->fin_plen); PREP_MB_T(fin, m); fin->fin_ip6 = ip6; fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv6 hdr */ fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv6 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } case NAT_DIVERTOUT : { mb_t *m; skip = ipf_nat6_decap(fin, nat); if (skip <= 0) { NBUMPSIDE6D(0, ns_decap_fail); return -1; } m = fin->fin_m; #if defined(MENTAT) && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif ipf_nat_update(fin, nat); fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; /* NOTREACHED */ } } if (nflags & IPN_TCPUDP) tcp = fin->fin_dp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { switch (nat->nat_dir) { case NAT_INBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_OUTBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { icmp6 = fin->fin_dp; icmp6->icmp6_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); } /* * The above comments do not hold for layer 4 (or higher) checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); else ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); } fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_newrewrite */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* This function is responsible for setting up an active NAT session where */ /* we are changing both the source and destination parameters at the same */ /* time. The loop in here works differently to elsewhere - each iteration */ /* is responsible for changing a single parameter that can be incremented. */ /* So one pass may increase the source IP#, next source port, next dest. IP#*/ /* and the last destination port for a total of 4 iterations to try each. */ /* This is done to try and exhaustively use the translation space available.*/ /* ------------------------------------------------------------------------ */ int ipf_nat6_newrewrite(fin, nat, nai) fr_info_t *fin; nat_t *nat; natinfo_t *nai; { int src_search = 1; int dst_search = 1; fr_info_t frnat; u_32_t flags; u_short swap; ipnat_t *np; nat_t *natl; int l = 0; int changed; natl = NULL; changed = -1; np = nai->nai_np; flags = nat->nat_flags; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_hm = NULL; do { changed = -1; /* TRACE (l, src_search, dst_search, np) */ if ((src_search == 0) && (np->in_spnext == 0) && (dst_search == 0) && (np->in_dpnext == 0)) { if (l > 0) return -1; } /* * Find a new source address */ if (ipf_nat6_nextaddr(fin, &np->in_nsrc, &frnat.fin_src6, &frnat.fin_src6) == -1) { return -1; } if (IP6_ISZERO(&np->in_nsrcip6) && IP6_ISONES(&np->in_nsrcmsk6)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (IP6_ISZERO(&np->in_nsrcip6) && IP6_ISZERO(&np->in_nsrcmsk6)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (IP6_ISONES(&np->in_nsrcmsk)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (!IP6_ISONES(&np->in_nsrcmsk6)) { if (np->in_stepnext == 0 && changed == -1) { IP6_INC(&np->in_snip); np->in_stepnext++; changed = 0; } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_spnext != 0) frnat.fin_data[0] = np->in_spnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDSPORT) != 0) { np->in_stepnext = 2; } else if ((np->in_stepnext == 1) && (changed == -1) && (natl != NULL)) { np->in_spnext++; np->in_stepnext++; changed = 1; if (np->in_spnext > np->in_spmax) np->in_spnext = np->in_spmin; } } else { np->in_stepnext = 2; } np->in_stepnext &= 0x3; /* * Find a new destination address */ /* TRACE (fin, np, l, frnat) */ if (ipf_nat6_nextaddr(fin, &np->in_ndst, &frnat.fin_dst6, &frnat.fin_dst6) == -1) return -1; if (IP6_ISZERO(&np->in_ndstip6) && IP6_ISONES(&np->in_ndstmsk6)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (IP6_ISZERO(&np->in_ndstip6) && IP6_ISZERO(&np->in_ndstmsk6)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (IP6_ISONES(&np->in_ndstmsk6)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (!IP6_ISONES(&np->in_ndstmsk6)) { if ((np->in_stepnext == 2) && (changed == -1) && (natl != NULL)) { changed = 2; np->in_stepnext++; IP6_INC(&np->in_dnip6); } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_dpnext != 0) frnat.fin_data[1] = np->in_dpnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDDPORT) != 0) { np->in_stepnext = 0; } else if (np->in_stepnext == 3 && changed == -1) { np->in_dpnext++; np->in_stepnext++; changed = 3; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } else { if (np->in_stepnext == 3) np->in_stepnext = 0; } /* TRACE (frnat) */ /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. * * fin_data[] is swapped around because we are doing a * lookup of the packet is if it were moving in the opposite * direction of the one we are working with now. */ if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } if (fin->fin_out == 1) { natl = ipf_nat6_inlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, &frnat.fin_dst6.in6, &frnat.fin_src6.in6); } else { natl = ipf_nat6_outlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, &frnat.fin_dst6.in6, &frnat.fin_src6.in6); } if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } /* TRACE natl, in_stepnext, l */ if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ return -1; np->in_stepnext &= 0x3; l++; changed = -1; } while (natl != NULL); nat->nat_osrc6 = fin->fin_src6; nat->nat_odst6 = fin->fin_dst6; nat->nat_nsrc6 = frnat.fin_src6; nat->nat_ndst6 = frnat.fin_dst6; if ((flags & IPN_TCPUDP) != 0) { nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } else if ((flags & IPN_ICMPQUERY) != 0) { nat->nat_oicmpid = fin->fin_data[1]; nat->nat_nicmpid = frnat.fin_data[1]; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_newdivert */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* Create a new NAT divert session as defined by the NAT rule. This is */ /* somewhat different to other NAT session creation routines because we */ /* do not iterate through either port numbers or IP addresses, searching */ /* for a unique mapping, however, a complimentary duplicate check is made. */ /* ------------------------------------------------------------------------ */ int ipf_nat6_newdivert(fin, nat, nai) fr_info_t *fin; nat_t *nat; natinfo_t *nai; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; fr_info_t frnat; ipnat_t *np; nat_t *natl; int p; np = nai->nai_np; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_pr[0] = 0; nat->nat_osrc6 = fin->fin_src6; nat->nat_odst6 = fin->fin_dst6; nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); frnat.fin_src6 = np->in_snip6; frnat.fin_dst6 = np->in_dnip6; if (np->in_redir & NAT_DIVERTUDP) { frnat.fin_data[0] = np->in_spnext; frnat.fin_data[1] = np->in_dpnext; frnat.fin_flx |= FI_TCPUDP; p = IPPROTO_UDP; } else { frnat.fin_flx &= ~FI_TCPUDP; p = IPPROTO_IPIP; } if (fin->fin_out == 1) { natl = ipf_nat6_inlookup(&frnat, 0, p, &frnat.fin_dst6.in6, &frnat.fin_src6.in6); } else { natl = ipf_nat6_outlookup(&frnat, 0, p, &frnat.fin_dst6.in6, &frnat.fin_src6.in6); } if (natl != NULL) { NBUMPSIDE6D(fin->fin_out, ns_divert_exist); return -1; } nat->nat_nsrc6 = frnat.fin_src6; nat->nat_ndst6 = frnat.fin_dst6; if (np->in_redir & NAT_DIVERTUDP) { nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } nat->nat_pr[fin->fin_out] = fin->fin_p; nat->nat_pr[1 - fin->fin_out] = p; if (np->in_redir & NAT_REDIRECT) nat->nat_dir = NAT_DIVERTIN; else nat->nat_dir = NAT_DIVERTOUT; return 0; } /* ------------------------------------------------------------------------ */ /* Function: nat6_builddivertmp */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: np(I) - pointer to a NAT rule */ /* */ /* For divert rules, a skeleton packet representing what will be prepended */ /* to the real packet is created. Even though we don't have the full */ /* packet here, a checksum is calculated that we update later when we */ /* fill in the final details. At present a 0 checksum for UDP is being set */ /* here because it is expected that divert will be used for localhost. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_builddivertmp(softn, np) ipf_nat_softc_t *softn; ipnat_t *np; { udphdr_t *uh; size_t len; ip6_t *ip6; if ((np->in_redir & NAT_DIVERTUDP) != 0) len = sizeof(ip6_t) + sizeof(udphdr_t); else len = sizeof(ip6_t); ALLOC_MB_T(np->in_divmp, len); if (np->in_divmp == NULL) { ATOMIC_INCL(softn->ipf_nat_stats.ns_divert_build); return -1; } /* * First, the header to get the packet diverted to the new destination */ ip6 = MTOD(np->in_divmp, ip6_t *); ip6->ip6_vfc = 0x60; if ((np->in_redir & NAT_DIVERTUDP) != 0) ip6->ip6_nxt = IPPROTO_UDP; else ip6->ip6_nxt = IPPROTO_IPIP; ip6->ip6_hlim = 255; ip6->ip6_plen = 0; ip6->ip6_src = np->in_snip6.in6; ip6->ip6_dst = np->in_dnip6.in6; if (np->in_redir & NAT_DIVERTUDP) { uh = (udphdr_t *)((u_char *)ip6 + sizeof(*ip6)); uh->uh_sum = 0; uh->uh_ulen = 8; uh->uh_sport = htons(np->in_spnext); uh->uh_dport = htons(np->in_dpnext); } return 0; } #define MINDECAP (sizeof(ip6_t) + sizeof(udphdr_t) + sizeof(ip6_t)) /* ------------------------------------------------------------------------ */ /* Function: nat6_decap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* This function is responsible for undoing a packet's encapsulation in the */ /* reverse of an encap/divert rule. After removing the outer encapsulation */ /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ /* match the "new" packet as it may still be used by IPFilter elsewhere. */ /* We use "dir" here as the basis for some of the expectations about the */ /* outer header. If we return an error, the goal is to leave the original */ /* packet information undisturbed - this falls short at the end where we'd */ /* need to back a backup copy of "fin" - expensive. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_decap(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; char *hdr; int skip; mb_t *m; if ((fin->fin_flx & FI_ICMPERR) != 0) { return 0; } m = fin->fin_m; skip = fin->fin_hlen; switch (nat->nat_dir) { case NAT_DIVERTIN : case NAT_DIVERTOUT : if (fin->fin_plen < MINDECAP) return -1; skip += sizeof(udphdr_t); break; case NAT_ENCAPIN : case NAT_ENCAPOUT : if (fin->fin_plen < (skip + sizeof(ip6_t))) return -1; break; default : return -1; /* NOTREACHED */ } /* * The aim here is to keep the original packet details in "fin" for * as long as possible so that returning with an error is for the * original packet and there is little undoing work to do. */ if (M_LEN(m) < skip + sizeof(ip6_t)) { if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) return -1; } hdr = MTOD(fin->fin_m, char *); fin->fin_ip6 = (ip6_t *)(hdr + skip); if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) { NBUMPSIDE6D(fin->fin_out, ns_decap_pullup); return -1; } fin->fin_hlen = sizeof(ip6_t); fin->fin_dlen -= skip; fin->fin_plen -= skip; fin->fin_ipoff += skip; if (ipf_makefrip(sizeof(ip6_t), (ip_t *)hdr, fin) == -1) { NBUMPSIDE6D(fin->fin_out, ns_decap_bad); return -1; } return skip; } /* ------------------------------------------------------------------------ */ /* Function: nat6_nextaddr */ /* Returns: int - -1 == bad input (no new address), */ /* 0 == success and dst has new address */ /* Parameters: fin(I) - pointer to packet information */ /* na(I) - how to generate new address */ /* old(I) - original address being replaced */ /* dst(O) - where to put the new address */ /* Write Lock: ipf_nat */ /* */ /* This function uses the contents of the "na" structure, in combination */ /* with "old" to produce a new address to store in "dst". Not all of the */ /* possible uses of "na" will result in a new address. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_nextaddr(fin, na, old, dst) fr_info_t *fin; nat_addr_t *na; i6addr_t *old, *dst; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; i6addr_t newip, new; u_32_t amin, amax; int error; new.i6[0] = 0; new.i6[1] = 0; new.i6[2] = 0; new.i6[3] = 0; amin = na->na_addr[0].in4.s_addr; switch (na->na_atype) { case FRI_RANGE : amax = na->na_addr[1].in4.s_addr; break; case FRI_NETMASKED : case FRI_DYNAMIC : case FRI_NORMAL : /* * Compute the maximum address by adding the inverse of the * netmask to the minimum address. */ amax = ~na->na_addr[1].in4.s_addr; amax |= amin; break; case FRI_LOOKUP : break; case FRI_BROADCAST : case FRI_PEERADDR : case FRI_NETWORK : default : return -1; } error = -1; switch (na->na_function) { case IPLT_DSTLIST : error = ipf_dstlist_select_node(fin, na->na_ptr, dst->i6, NULL); break; case IPLT_NONE : /* * 0/0 as the new address means leave it alone. */ if (na->na_addr[0].in4.s_addr == 0 && na->na_addr[1].in4.s_addr == 0) { new = *old; /* * 0/32 means get the interface's address */ } else if (IP6_ISZERO(&na->na_addr[0].in6) && IP6_ISONES(&na->na_addr[1].in6)) { if (ipf_ifpaddr(softc, 6, na->na_atype, fin->fin_ifp, &newip, NULL) == -1) { NBUMPSIDE6(fin->fin_out, ns_ifpaddrfail); return -1; } new = newip; } else { new.in6 = na->na_nextip6; } *dst = new; error = 0; break; default : NBUMPSIDE6(fin->fin_out, ns_badnextaddr); break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_nextaddrinit */ /* Returns: int - 0 == success, else error number */ /* Parameters: na(I) - NAT address information for generating new addr*/ /* base(I) - start of where to find strings */ /* initial(I) - flag indicating if it is the first call for */ /* this "na" structure. */ /* ifp(I) - network interface to derive address */ /* information from. */ /* */ /* This function is expected to be called in two scenarious: when a new NAT */ /* rule is loaded into the kernel and when the list of NAT rules is sync'd */ /* up with the valid network interfaces (possibly due to them changing.) */ /* To distinguish between these, the "initial" parameter is used. If it is */ /* 1 then this indicates the rule has just been reloaded and 0 for when we */ /* are updating information. This difference is important because in */ /* instances where we are not updating address information associated with */ /* a network interface, we don't want to disturb what the "next" address to */ /* come out of ipf_nat6_nextaddr() will be. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_nextaddrinit(softc, base, na, initial, ifp) ipf_main_softc_t *softc; char *base; nat_addr_t *na; int initial; void *ifp; { switch (na->na_atype) { case FRI_LOOKUP : if (na->na_subtype == 0) { na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, na->na_type, na->na_num, &na->na_func); } else if (na->na_subtype == 1) { na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, na->na_type, base + na->na_num, &na->na_func); } if (na->na_func == NULL) { IPFERROR(60072); return ESRCH; } if (na->na_ptr == NULL) { IPFERROR(60073); return ESRCH; } break; case FRI_DYNAMIC : case FRI_BROADCAST : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (ifp != NULL) (void )ipf_ifpaddr(softc, 6, na->na_atype, ifp, &na->na_addr[0], &na->na_addr[1]); break; case FRI_SPLIT : case FRI_RANGE : if (initial) na->na_nextip6 = na->na_addr[0].in6; break; case FRI_NONE : IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); return 0; case FRI_NORMAL : IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); break; default : IPFERROR(60074); return EINVAL; } if (initial && (na->na_atype == FRI_NORMAL)) { if (IP6_ISZERO(&na->na_addr[0].in6)) { if (IP6_ISONES(&na->na_addr[1].in6) || IP6_ISZERO(&na->na_addr[1].in6)) { return 0; } } na->na_nextip6 = na->na_addr[0].in6; if (!IP6_ISONES(&na->na_addr[1].in6)) { IP6_INC(&na->na_nextip6); } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_icmpquerytype */ /* Returns: int - 1 == success, 0 == failure */ /* Parameters: icmptype(I) - ICMP type number */ /* */ /* Tests to see if the ICMP type number passed is a query/response type or */ /* not. */ /* ------------------------------------------------------------------------ */ static int ipf_nat6_icmpquerytype(icmptype) int icmptype; { /* * For the ICMP query NAT code, it is essential that both the query * and the reply match on the NAT rule. Because the NAT structure * does not keep track of the icmptype, and a single NAT structure * is used for all icmp types with the same src, dest and id, we * simply define the replies as queries as well. The funny thing is, * altough it seems silly to call a reply a query, this is exactly * as it is defined in the IPv4 specification */ switch (icmptype) { case ICMP6_ECHO_REPLY: case ICMP6_ECHO_REQUEST: /* route aedvertisement/solliciation is currently unsupported: */ /* it would require rewriting the ICMP data section */ case ICMP6_MEMBERSHIP_QUERY: case ICMP6_MEMBERSHIP_REPORT: case ICMP6_MEMBERSHIP_REDUCTION: case ICMP6_WRUREQUEST: case ICMP6_WRUREPLY: case MLD6_MTRACE_RESP: case MLD6_MTRACE: return 1; default: return 0; } } #endif /* USE_INET6 */ diff --git a/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c b/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c index 4e8bdc66a2f2..d3f226325f42 100644 --- a/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c @@ -1,120 +1,120 @@ /* * Simple netbios-dgm transparent proxy for in-kernel use. * For use with the NAT code. * $Id$ */ /*- * Copyright (c) 2002-2003 Paul J. Ledbetter III * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $Id$ */ #define IPF_NETBIOS_PROXY -void ipf_p_netbios_main_load __P((void)); -void ipf_p_netbios_main_unload __P((void)); -int ipf_p_netbios_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_netbios_main_load(void); +void ipf_p_netbios_main_unload(void); +int ipf_p_netbios_out(void *, fr_info_t *, ap_session_t *, nat_t *); static frentry_t netbiosfr; int netbios_proxy_init = 0; /* * Initialize local structures. */ void ipf_p_netbios_main_load() { bzero((char *)&netbiosfr, sizeof(netbiosfr)); netbiosfr.fr_ref = 1; netbiosfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&netbiosfr.fr_lock, "NETBIOS proxy rule lock"); netbios_proxy_init = 1; } void ipf_p_netbios_main_unload() { if (netbios_proxy_init == 1) { MUTEX_DESTROY(&netbiosfr.fr_lock); netbios_proxy_init = 0; } } int ipf_p_netbios_out(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { char dgmbuf[6]; int off, dlen; udphdr_t *udp; ip_t *ip; mb_t *m; aps = aps; /* LINT */ nat = nat; /* LINT */ m = fin->fin_m; dlen = fin->fin_dlen - sizeof(*udp); /* * no net bios datagram could possibly be shorter than this */ if (dlen < 11) return 0; ip = fin->fin_ip; udp = (udphdr_t *)fin->fin_dp; off = (char *)udp - (char *)ip + sizeof(*udp) + fin->fin_ipoff; /* * move past the * ip header; * udp header; * 4 bytes into the net bios dgm header. * According to rfc1002, this should be the exact location of * the source address/port */ off += 4; /* Copy NATed source Address/port*/ dgmbuf[0] = (char)((ip->ip_src.s_addr ) &0xFF); dgmbuf[1] = (char)((ip->ip_src.s_addr >> 8) &0xFF); dgmbuf[2] = (char)((ip->ip_src.s_addr >> 16)&0xFF); dgmbuf[3] = (char)((ip->ip_src.s_addr >> 24)&0xFF); dgmbuf[4] = (char)((udp->uh_sport )&0xFF); dgmbuf[5] = (char)((udp->uh_sport >> 8)&0xFF); /* replace data in packet */ COPYBACK(m, off, sizeof(dgmbuf), dgmbuf); return 0; } diff --git a/sys/contrib/ipfilter/netinet/ip_pool.c b/sys/contrib/ipfilter/netinet/ip_pool.c index fd511fcd2d89..fc2e78bfe27b 100644 --- a/sys/contrib/ipfilter/netinet/ip_pool.c +++ b/sys/contrib/ipfilter/netinet/ip_pool.c @@ -1,1483 +1,1483 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # include # undef _KERNEL #else # include # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) # include # endif #endif #include #if defined(_KERNEL) && !defined(SOLARIS2) # include #endif #if defined(__SVR4) # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD_version) # include #endif #include #include #include #if !defined(_KERNEL) # include "ipf.h" #endif #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_pool.h" #include "netinet/radix_ipf.h" /* END OF INCLUDES */ #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 typedef struct ipf_pool_softc_s { void *ipf_radix; ip_pool_t *ipf_pool_list[LOOKUP_POOL_SZ]; ipf_pool_stat_t ipf_pool_stats; ip_pool_node_t *ipf_node_explist; } ipf_pool_softc_t; -static void ipf_pool_clearnodes __P((ipf_main_softc_t *, ipf_pool_softc_t *, - ip_pool_t *)); -static int ipf_pool_create __P((ipf_main_softc_t *, ipf_pool_softc_t *, iplookupop_t *)); -static int ipf_pool_deref __P((ipf_main_softc_t *, void *, void *)); -static int ipf_pool_destroy __P((ipf_main_softc_t *, ipf_pool_softc_t *, int, char *)); -static void *ipf_pool_exists __P((ipf_pool_softc_t *, int, char *)); -static void *ipf_pool_find __P((void *, int, char *)); -static ip_pool_node_t *ipf_pool_findeq __P((ipf_pool_softc_t *, ip_pool_t *, - addrfamily_t *, addrfamily_t *)); -static void ipf_pool_free __P((ipf_main_softc_t *, ipf_pool_softc_t *, - ip_pool_t *)); -static int ipf_pool_insert_node __P((ipf_main_softc_t *, ipf_pool_softc_t *, - ip_pool_t *, struct ip_pool_node *)); -static int ipf_pool_iter_deref __P((ipf_main_softc_t *, void *, int, int, void *)); -static int ipf_pool_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, - ipflookupiter_t *)); -static size_t ipf_pool_flush __P((ipf_main_softc_t *, void *, iplookupflush_t *)); -static int ipf_pool_node_add __P((ipf_main_softc_t *, void *, iplookupop_t *, - int)); -static int ipf_pool_node_del __P((ipf_main_softc_t *, void *, iplookupop_t *, - int)); -static void ipf_pool_node_deref __P((ipf_pool_softc_t *, ip_pool_node_t *)); -static int ipf_pool_remove_node __P((ipf_main_softc_t *, ipf_pool_softc_t *, - ip_pool_t *, ip_pool_node_t *)); -static int ipf_pool_search __P((ipf_main_softc_t *, void *, int, - void *, u_int)); -static void *ipf_pool_soft_create __P((ipf_main_softc_t *)); -static void ipf_pool_soft_destroy __P((ipf_main_softc_t *, void *)); -static void ipf_pool_soft_fini __P((ipf_main_softc_t *, void *)); -static int ipf_pool_soft_init __P((ipf_main_softc_t *, void *)); -static int ipf_pool_stats_get __P((ipf_main_softc_t *, void *, iplookupop_t *)); -static int ipf_pool_table_add __P((ipf_main_softc_t *, void *, iplookupop_t *)); -static int ipf_pool_table_del __P((ipf_main_softc_t *, void *, iplookupop_t *)); -static void *ipf_pool_select_add_ref __P((void *, int, char *)); -static void ipf_pool_expire __P((ipf_main_softc_t *, void *)); +static void ipf_pool_clearnodes(ipf_main_softc_t *, ipf_pool_softc_t *, + ip_pool_t *); +static int ipf_pool_create(ipf_main_softc_t *, ipf_pool_softc_t *, iplookupop_t *); +static int ipf_pool_deref(ipf_main_softc_t *, void *, void *); +static int ipf_pool_destroy(ipf_main_softc_t *, ipf_pool_softc_t *, int, char *); +static void *ipf_pool_exists(ipf_pool_softc_t *, int, char *); +static void *ipf_pool_find(void *, int, char *); +static ip_pool_node_t *ipf_pool_findeq(ipf_pool_softc_t *, ip_pool_t *, + addrfamily_t *, addrfamily_t *); +static void ipf_pool_free(ipf_main_softc_t *, ipf_pool_softc_t *, + ip_pool_t *); +static int ipf_pool_insert_node(ipf_main_softc_t *, ipf_pool_softc_t *, + ip_pool_t *, struct ip_pool_node *); +static int ipf_pool_iter_deref(ipf_main_softc_t *, void *, int, int, void *); +static int ipf_pool_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, + ipflookupiter_t *); +static size_t ipf_pool_flush(ipf_main_softc_t *, void *, iplookupflush_t *); +static int ipf_pool_node_add(ipf_main_softc_t *, void *, iplookupop_t *, + int); +static int ipf_pool_node_del(ipf_main_softc_t *, void *, iplookupop_t *, + int); +static void ipf_pool_node_deref(ipf_pool_softc_t *, ip_pool_node_t *); +static int ipf_pool_remove_node(ipf_main_softc_t *, ipf_pool_softc_t *, + ip_pool_t *, ip_pool_node_t *); +static int ipf_pool_search(ipf_main_softc_t *, void *, int, + void *, u_int); +static void *ipf_pool_soft_create(ipf_main_softc_t *); +static void ipf_pool_soft_destroy(ipf_main_softc_t *, void *); +static void ipf_pool_soft_fini(ipf_main_softc_t *, void *); +static int ipf_pool_soft_init(ipf_main_softc_t *, void *); +static int ipf_pool_stats_get(ipf_main_softc_t *, void *, iplookupop_t *); +static int ipf_pool_table_add(ipf_main_softc_t *, void *, iplookupop_t *); +static int ipf_pool_table_del(ipf_main_softc_t *, void *, iplookupop_t *); +static void *ipf_pool_select_add_ref(void *, int, char *); +static void ipf_pool_expire(ipf_main_softc_t *, void *); ipf_lookup_t ipf_pool_backend = { IPLT_POOL, ipf_pool_soft_create, ipf_pool_soft_destroy, ipf_pool_soft_init, ipf_pool_soft_fini, ipf_pool_search, ipf_pool_flush, ipf_pool_iter_deref, ipf_pool_iter_next, ipf_pool_node_add, ipf_pool_node_del, ipf_pool_stats_get, ipf_pool_table_add, ipf_pool_table_del, ipf_pool_deref, ipf_pool_find, ipf_pool_select_add_ref, NULL, ipf_pool_expire, NULL }; #ifdef TEST_POOL -void treeprint __P((ip_pool_t *)); +void treeprint(ip_pool_t *); int main(argc, argv) int argc; char *argv[]; { ip_pool_node_t node; addrfamily_t a, b; iplookupop_t op; ip_pool_t *ipo; i6addr_t ip; RWLOCK_INIT(softc->ipf_poolrw, "poolrw"); ipf_pool_init(); bzero((char *)&ip, sizeof(ip)); bzero((char *)&op, sizeof(op)); bzero((char *)&node, sizeof(node)); strcpy(op.iplo_name, "0"); if (ipf_pool_create(&op) == 0) ipo = ipf_pool_exists(0, "0"); node.ipn_addr.adf_family = AF_INET; node.ipn_addr.adf_addr.in4.s_addr = 0x0a010203; node.ipn_mask.adf_addr.in4.s_addr = 0xffffffff; node.ipn_info = 1; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a000000; node.ipn_mask.adf_addr.in4.s_addr = 0xff000000; node.ipn_info = 0; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a010100; node.ipn_mask.adf_addr.in4.s_addr = 0xffffff00; node.ipn_info = 1; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a010200; node.ipn_mask.adf_addr.in4.s_addr = 0xffffff00; node.ipn_info = 0; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a010000; node.ipn_mask.adf_addr.in4.s_addr = 0xffff0000; node.ipn_info = 1; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a01020f; node.ipn_mask.adf_addr.in4.s_addr = 0xffffffff; node.ipn_info = 1; ipf_pool_insert_node(ipo, &node); #ifdef DEBUG_POOL treeprint(ipo); #endif ip.in4.s_addr = 0x0a00aabb; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a000001; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a000101; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010001; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010101; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010201; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010203; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a01020f; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0b00aabb; printf("search(%#x) = %d (-1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); #ifdef DEBUG_POOL treeprint(ipo); #endif ipf_pool_fini(); return 0; } void treeprint(ipo) ip_pool_t *ipo; { ip_pool_node_t *c; for (c = ipo->ipo_list; c != NULL; c = c->ipn_next) printf("Node %p(%s) (%#x/%#x) = %d hits %lu\n", c, c->ipn_name, c->ipn_addr.adf_addr.in4.s_addr, c->ipn_mask.adf_addr.in4.s_addr, c->ipn_info, c->ipn_hits); } #endif /* TEST_POOL */ /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise the routing table data structures where required. */ /* ------------------------------------------------------------------------ */ static void * ipf_pool_soft_create(softc) ipf_main_softc_t *softc; { ipf_pool_softc_t *softp; KMALLOC(softp, ipf_pool_softc_t *); if (softp == NULL) { IPFERROR(70032); return NULL; } bzero((char *)softp, sizeof(*softp)); softp->ipf_radix = ipf_rx_create(); if (softp->ipf_radix == NULL) { IPFERROR(70033); KFREE(softp); return NULL; } return softp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the routing table data structures where required. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_pool_softc_t *softp = arg; ipf_rx_init(softp->ipf_radix); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* Locks: WRITE(ipf_global) */ /* */ /* Clean up all the pool data structures allocated and call the cleanup */ /* function for the radix tree that supports the pools. ipf_pool_destroy is */ /* used to delete the pools one by one to ensure they're properly freed up. */ /* ------------------------------------------------------------------------ */ static void ipf_pool_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_pool_softc_t *softp = arg; ip_pool_t *p, *q; int i; softc = arg; for (i = -1; i <= IPL_LOGMAX; i++) { for (q = softp->ipf_pool_list[i + 1]; (p = q) != NULL; ) { q = p->ipo_next; (void) ipf_pool_destroy(softc, arg, i, p->ipo_name); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Clean up the pool by free'ing the radix tree associated with it and free */ /* up the pool context too. */ /* ------------------------------------------------------------------------ */ static void ipf_pool_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_pool_softc_t *softp = arg; ipf_rx_destroy(softp->ipf_radix); KFREE(softp); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_node_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* When adding a new node, a check is made to ensure that the address/mask */ /* pair supplied has been appropriately prepared by applying the mask to */ /* the address prior to calling for the pair to be added. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_node_add(softc, arg, op, uid) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; int uid; { ip_pool_node_t node, *m; ip_pool_t *p; int err; if (op->iplo_size != sizeof(node)) { IPFERROR(70014); return EINVAL; } err = COPYIN(op->iplo_struct, &node, sizeof(node)); if (err != 0) { IPFERROR(70015); return EFAULT; } p = ipf_pool_find(arg, op->iplo_unit, op->iplo_name); if (p == NULL) { IPFERROR(70017); return ESRCH; } if (node.ipn_addr.adf_family == AF_INET) { if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + sizeof(struct in_addr)) { IPFERROR(70028); return EINVAL; } } #ifdef USE_INET6 else if (node.ipn_addr.adf_family == AF_INET6) { if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + sizeof(struct in6_addr)) { IPFERROR(70034); return EINVAL; } } #endif if (node.ipn_mask.adf_len != node.ipn_addr.adf_len) { IPFERROR(70029); return EINVAL; } /* * Check that the address/mask pair works. */ if (node.ipn_addr.adf_family == AF_INET) { if ((node.ipn_addr.adf_addr.in4.s_addr & node.ipn_mask.adf_addr.in4.s_addr) != node.ipn_addr.adf_addr.in4.s_addr) { IPFERROR(70035); return EINVAL; } } #ifdef USE_INET6 else if (node.ipn_addr.adf_family == AF_INET6) { if (IP6_MASKNEQ(&node.ipn_addr.adf_addr.in6, &node.ipn_mask.adf_addr.in6, &node.ipn_addr.adf_addr.in6)) { IPFERROR(70036); return EINVAL; } } #endif /* * add an entry to a pool - return an error if it already * exists remove an entry from a pool - if it exists * - in both cases, the pool *must* exist! */ m = ipf_pool_findeq(arg, p, &node.ipn_addr, &node.ipn_mask); if (m != NULL) { IPFERROR(70018); return EEXIST; } err = ipf_pool_insert_node(softc, arg, p, &node); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_pool_node_del(softc, arg, op, uid) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; int uid; { ip_pool_node_t node, *m; ip_pool_t *p; int err; if (op->iplo_size != sizeof(node)) { IPFERROR(70019); return EINVAL; } node.ipn_uid = uid; err = COPYIN(op->iplo_struct, &node, sizeof(node)); if (err != 0) { IPFERROR(70020); return EFAULT; } if (node.ipn_addr.adf_family == AF_INET) { if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + sizeof(struct in_addr)) { IPFERROR(70030); return EINVAL; } } #ifdef USE_INET6 else if (node.ipn_addr.adf_family == AF_INET6) { if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + sizeof(struct in6_addr)) { IPFERROR(70037); return EINVAL; } } #endif if (node.ipn_mask.adf_len != node.ipn_addr.adf_len) { IPFERROR(70031); return EINVAL; } p = ipf_pool_find(arg, op->iplo_unit, op->iplo_name); if (p == NULL) { IPFERROR(70021); return ESRCH; } m = ipf_pool_findeq(arg, p, &node.ipn_addr, &node.ipn_mask); if (m == NULL) { IPFERROR(70022); return ENOENT; } if ((uid != 0) && (uid != m->ipn_uid)) { IPFERROR(70024); return EACCES; } err = ipf_pool_remove_node(softc, arg, p, m); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_table_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_pool_table_add(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { int err; if (((op->iplo_arg & LOOKUP_ANON) == 0) && (ipf_pool_find(arg, op->iplo_unit, op->iplo_name) != NULL)) { IPFERROR(70023); err = EEXIST; } else { err = ipf_pool_create(softc, arg, op); } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_table_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_pool_table_del(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { return ipf_pool_destroy(softc, arg, op->iplo_unit, op->iplo_name); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_statistics */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* Copy the current statistics out into user space, collecting pool list */ /* pointers as appropriate for later use. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_stats_get(softc, arg, op) ipf_main_softc_t *softc; void *arg; iplookupop_t *op; { ipf_pool_softc_t *softp = arg; ipf_pool_stat_t stats; int unit, i, err = 0; if (op->iplo_size != sizeof(ipf_pool_stat_t)) { IPFERROR(70001); return EINVAL; } bcopy((char *)&softp->ipf_pool_stats, (char *)&stats, sizeof(stats)); unit = op->iplo_unit; if (unit == IPL_LOGALL) { for (i = 0; i <= LOOKUP_POOL_MAX; i++) stats.ipls_list[i] = softp->ipf_pool_list[i]; } else if (unit >= 0 && unit <= IPL_LOGMAX) { unit++; /* -1 => 0 */ if (op->iplo_name[0] != '\0') stats.ipls_list[unit] = ipf_pool_exists(softp, unit - 1, op->iplo_name); else stats.ipls_list[unit] = softp->ipf_pool_list[unit]; } else { IPFERROR(70025); err = EINVAL; } if (err == 0) { err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); if (err != 0) { IPFERROR(70026); return EFAULT; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_exists */ /* Returns: int - 0 = success, else error */ /* Parameters: softp(I) - pointer to soft context pool information */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the pool */ /* */ /* Find a matching pool inside the collection of pools for a particular */ /* device, indicated by the unit number. */ /* ------------------------------------------------------------------------ */ static void * ipf_pool_exists(softp, unit, name) ipf_pool_softc_t *softp; int unit; char *name; { ip_pool_t *p; int i; if (unit == IPL_LOGALL) { for (i = 0; i <= LOOKUP_POOL_MAX; i++) { for (p = softp->ipf_pool_list[i]; p != NULL; p = p->ipo_next) { if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0) break; } if (p != NULL) break; } } else { for (p = softp->ipf_pool_list[unit + 1]; p != NULL; p = p->ipo_next) if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0) break; } return p; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_find */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the pool */ /* */ /* Find a matching pool inside the collection of pools for a particular */ /* device, indicated by the unit number. If it is marked for deletion then */ /* pretend it does not exist. */ /* ------------------------------------------------------------------------ */ static void * ipf_pool_find(arg, unit, name) void *arg; int unit; char *name; { ipf_pool_softc_t *softp = arg; ip_pool_t *p; p = ipf_pool_exists(softp, unit, name); if ((p != NULL) && (p->ipo_flags & IPOOL_DELETE)) return NULL; return p; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_select_add_ref */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the pool */ /* */ /* ------------------------------------------------------------------------ */ static void * ipf_pool_select_add_ref(arg, unit, name) void *arg; int unit; char *name; { ip_pool_t *p; p = ipf_pool_find(arg, -1, name); if (p == NULL) p = ipf_pool_find(arg, unit, name); if (p != NULL) { ATOMIC_INC32(p->ipo_ref); } return p; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_findeq */ /* Returns: int - 0 = success, else error */ /* Parameters: softp(I) - pointer to soft context pool information */ /* ipo(I) - pointer to the pool getting the new node. */ /* addr(I) - pointer to address information to match on */ /* mask(I) - pointer to the address mask to match */ /* */ /* Searches for an exact match of an entry in the pool. */ /* ------------------------------------------------------------------------ */ -extern void printhostmask __P((int, u_32_t *, u_32_t *)); +extern void printhostmask(int, u_32_t *, u_32_t *)); static ip_pool_node_t * ipf_pool_findeq(softp, ipo, addr, mask) ipf_pool_softc_t *softp; ip_pool_t *ipo; addrfamily_t *addr, *mask; { ipf_rdx_node_t *n; n = ipo->ipo_head->lookup(ipo->ipo_head, addr, mask); return (ip_pool_node_t *)n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_search */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tptr(I) - pointer to the pool to search */ /* version(I) - IP protocol version (4 or 6) */ /* dptr(I) - pointer to address information */ /* bytes(I) - length of packet */ /* */ /* Search the pool for a given address and return a search result. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_search(softc, tptr, ipversion, dptr, bytes) ipf_main_softc_t *softc; void *tptr; int ipversion; void *dptr; u_int bytes; { ipf_rdx_node_t *rn; ip_pool_node_t *m; i6addr_t *addr; addrfamily_t v; ip_pool_t *ipo; int rv; ipo = tptr; if (ipo == NULL) return -1; rv = 1; m = NULL; addr = (i6addr_t *)dptr; bzero(&v, sizeof(v)); if (ipversion == 4) { v.adf_family = AF_INET; v.adf_len = offsetof(addrfamily_t, adf_addr) + sizeof(struct in_addr); v.adf_addr.in4 = addr->in4; #ifdef USE_INET6 } else if (ipversion == 6) { v.adf_family = AF_INET6; v.adf_len = offsetof(addrfamily_t, adf_addr) + sizeof(struct in6_addr); v.adf_addr.in6 = addr->in6; #endif } else return -1; READ_ENTER(&softc->ipf_poolrw); rn = ipo->ipo_head->matchaddr(ipo->ipo_head, &v); if ((rn != NULL) && (rn->root == 0)) { m = (ip_pool_node_t *)rn; ipo->ipo_hits++; m->ipn_bytes += bytes; m->ipn_hits++; rv = m->ipn_info; } RWLOCK_EXIT(&softc->ipf_poolrw); return rv; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_insert_node */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* ipo(I) - pointer to the pool getting the new node. */ /* node(I) - structure with address/mask to add */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Add another node to the pool given by ipo. The three parameters passed */ /* in (addr, mask, info) shold all be stored in the node. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_insert_node(softc, softp, ipo, node) ipf_main_softc_t *softc; ipf_pool_softc_t *softp; ip_pool_t *ipo; struct ip_pool_node *node; { ipf_rdx_node_t *rn; ip_pool_node_t *x; if ((node->ipn_addr.adf_len > sizeof(*rn)) || (node->ipn_addr.adf_len < 4)) { IPFERROR(70003); return EINVAL; } if ((node->ipn_mask.adf_len > sizeof(*rn)) || (node->ipn_mask.adf_len < 4)) { IPFERROR(70004); return EINVAL; } KMALLOC(x, ip_pool_node_t *); if (x == NULL) { IPFERROR(70002); return ENOMEM; } *x = *node; bzero((char *)x->ipn_nodes, sizeof(x->ipn_nodes)); x->ipn_owner = ipo; x->ipn_hits = 0; x->ipn_next = NULL; x->ipn_pnext = NULL; x->ipn_dnext = NULL; x->ipn_pdnext = NULL; if (x->ipn_die != 0) { /* * If the new node has a given expiration time, insert it * into the list of expiring nodes with the ones to be * removed first added to the front of the list. The * insertion is O(n) but it is kept sorted for quick scans * at expiration interval checks. */ ip_pool_node_t *n; x->ipn_die = softc->ipf_ticks + IPF_TTLVAL(x->ipn_die); for (n = softp->ipf_node_explist; n != NULL; n = n->ipn_dnext) { if (x->ipn_die < n->ipn_die) break; if (n->ipn_dnext == NULL) { /* * We've got to the last node and everything * wanted to be expired before this new node, * so we have to tack it on the end... */ n->ipn_dnext = x; x->ipn_pdnext = &n->ipn_dnext; n = NULL; break; } } if (softp->ipf_node_explist == NULL) { softp->ipf_node_explist = x; x->ipn_pdnext = &softp->ipf_node_explist; } else if (n != NULL) { x->ipn_dnext = n; x->ipn_pdnext = n->ipn_pdnext; n->ipn_pdnext = &x->ipn_dnext; } } rn = ipo->ipo_head->addaddr(ipo->ipo_head, &x->ipn_addr, &x->ipn_mask, x->ipn_nodes); #ifdef DEBUG_POOL printf("Added %p at %p\n", x, rn); #endif if (rn == NULL) { KFREE(x); IPFERROR(70005); return ENOMEM; } x->ipn_ref = 1; x->ipn_pnext = ipo->ipo_tail; *ipo->ipo_tail = x; ipo->ipo_tail = &x->ipn_next; softp->ipf_pool_stats.ipls_nodes++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_create */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* op(I) - pointer to iplookup struct with call details */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Creates a new group according to the paramters passed in via the */ /* iplookupop structure. Does not check to see if the group already exists */ /* when being inserted - assume this has already been done. If the pool is */ /* marked as being anonymous, give it a new, unique, identifier. Call any */ /* other functions required to initialise the structure. */ /* */ /* If the structure is flagged for deletion then reset the flag and return, */ /* as this likely means we've tried to free a pool that is in use (flush) */ /* and now want to repopulate it with "new" data. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_create(softc, softp, op) ipf_main_softc_t *softc; ipf_pool_softc_t *softp; iplookupop_t *op; { char name[FR_GROUPLEN]; int poolnum, unit; ip_pool_t *h; unit = op->iplo_unit; if ((op->iplo_arg & LOOKUP_ANON) == 0) { h = ipf_pool_exists(softp, unit, op->iplo_name); if (h != NULL) { if ((h->ipo_flags & IPOOL_DELETE) == 0) { IPFERROR(70006); return EEXIST; } h->ipo_flags &= ~IPOOL_DELETE; return 0; } } KMALLOC(h, ip_pool_t *); if (h == NULL) { IPFERROR(70007); return ENOMEM; } bzero(h, sizeof(*h)); if (ipf_rx_inithead(softp->ipf_radix, &h->ipo_head) != 0) { KFREE(h); IPFERROR(70008); return ENOMEM; } if ((op->iplo_arg & LOOKUP_ANON) != 0) { ip_pool_t *p; h->ipo_flags |= IPOOL_ANON; poolnum = LOOKUP_ANON; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(name, sizeof(name), "%x", poolnum); #else (void)sprintf(name, "%x", poolnum); #endif for (p = softp->ipf_pool_list[unit + 1]; p != NULL; ) { if (strncmp(name, p->ipo_name, sizeof(p->ipo_name)) == 0) { poolnum++; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(name, sizeof(name), "%x", poolnum); #else (void)sprintf(name, "%x", poolnum); #endif p = softp->ipf_pool_list[unit + 1]; } else p = p->ipo_next; } (void)strncpy(h->ipo_name, name, sizeof(h->ipo_name)); (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); } else { (void)strncpy(h->ipo_name, op->iplo_name, sizeof(h->ipo_name)); } h->ipo_radix = softp->ipf_radix; h->ipo_ref = 1; h->ipo_list = NULL; h->ipo_tail = &h->ipo_list; h->ipo_unit = unit; h->ipo_next = softp->ipf_pool_list[unit + 1]; if (softp->ipf_pool_list[unit + 1] != NULL) softp->ipf_pool_list[unit + 1]->ipo_pnext = &h->ipo_next; h->ipo_pnext = &softp->ipf_pool_list[unit + 1]; softp->ipf_pool_list[unit + 1] = h; softp->ipf_pool_stats.ipls_pools++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_remove_node */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ipo(I) - pointer to the pool to remove the node from. */ /* ipe(I) - address being deleted as a node */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Remove a node from the pool given by ipo. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_remove_node(softc, softp, ipo, ipe) ipf_main_softc_t *softc; ipf_pool_softc_t *softp; ip_pool_t *ipo; ip_pool_node_t *ipe; { void *ptr; if (ipo->ipo_tail == &ipe->ipn_next) ipo->ipo_tail = ipe->ipn_pnext; if (ipe->ipn_pnext != NULL) *ipe->ipn_pnext = ipe->ipn_next; if (ipe->ipn_next != NULL) ipe->ipn_next->ipn_pnext = ipe->ipn_pnext; if (ipe->ipn_pdnext != NULL) *ipe->ipn_pdnext = ipe->ipn_dnext; if (ipe->ipn_dnext != NULL) ipe->ipn_dnext->ipn_pdnext = ipe->ipn_pdnext; ptr = ipo->ipo_head->deladdr(ipo->ipo_head, &ipe->ipn_addr, &ipe->ipn_mask); if (ptr != NULL) { ipf_pool_node_deref(softp, ipe); return 0; } IPFERROR(70027); return ESRCH; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_destroy */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the pool */ /* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Search for a pool using paramters passed in and if it's not otherwise */ /* busy, free it. If it is busy, clear all of its nodes, mark it for being */ /* deleted and return an error saying it is busy. */ /* */ /* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ /* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_destroy(softc, softp, unit, name) ipf_main_softc_t *softc; ipf_pool_softc_t *softp; int unit; char *name; { ip_pool_t *ipo; ipo = ipf_pool_exists(softp, unit, name); if (ipo == NULL) { IPFERROR(70009); return ESRCH; } if (ipo->ipo_ref != 1) { ipf_pool_clearnodes(softc, softp, ipo); ipo->ipo_flags |= IPOOL_DELETE; return 0; } ipf_pool_free(softc, softp, ipo); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_flush */ /* Returns: int - number of pools deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* fp(I) - which pool(s) to flush */ /* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Free all pools associated with the device that matches the unit number */ /* passed in with operation. */ /* */ /* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ /* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ static size_t ipf_pool_flush(softc, arg, fp) ipf_main_softc_t *softc; void *arg; iplookupflush_t *fp; { ipf_pool_softc_t *softp = arg; int i, num = 0, unit, err; ip_pool_t *p, *q; unit = fp->iplf_unit; for (i = -1; i <= IPL_LOGMAX; i++) { if (unit != IPLT_ALL && i != unit) continue; for (q = softp->ipf_pool_list[i + 1]; (p = q) != NULL; ) { q = p->ipo_next; err = ipf_pool_destroy(softc, softp, i, p->ipo_name); if (err == 0) num++; } } return num; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_free */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* ipo(I) - pointer to pool structure */ /* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Deletes the pool strucutre passed in from the list of pools and deletes */ /* all of the address information stored in it, including any tree data */ /* structures also allocated. */ /* */ /* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ /* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ static void ipf_pool_free(softc, softp, ipo) ipf_main_softc_t *softc; ipf_pool_softc_t *softp; ip_pool_t *ipo; { ipf_pool_clearnodes(softc, softp, ipo); if (ipo->ipo_next != NULL) ipo->ipo_next->ipo_pnext = ipo->ipo_pnext; *ipo->ipo_pnext = ipo->ipo_next; ipf_rx_freehead(ipo->ipo_head); KFREE(ipo); softp->ipf_pool_stats.ipls_pools--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_clearnodes */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* ipo(I) - pointer to pool structure */ /* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Deletes all nodes stored in a pool structure. */ /* ------------------------------------------------------------------------ */ static void ipf_pool_clearnodes(softc, softp, ipo) ipf_main_softc_t *softc; ipf_pool_softc_t *softp; ip_pool_t *ipo; { ip_pool_node_t *n, **next; for (next = &ipo->ipo_list; (n = *next) != NULL; ) ipf_pool_remove_node(softc, softp, ipo, n); ipo->ipo_list = NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_deref */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* pool(I) - pointer to pool structure */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Drop the number of known references to this pool structure by one and if */ /* we arrive at zero known references, free it. */ /* ------------------------------------------------------------------------ */ static int ipf_pool_deref(softc, arg, pool) ipf_main_softc_t *softc; void *arg, *pool; { ip_pool_t *ipo = pool; ipo->ipo_ref--; if (ipo->ipo_ref == 0) ipf_pool_free(softc, arg, ipo); else if ((ipo->ipo_ref == 1) && (ipo->ipo_flags & IPOOL_DELETE)) ipf_pool_destroy(softc, arg, ipo->ipo_unit, ipo->ipo_name); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_node_deref */ /* Returns: void */ /* Parameters: softp(I) - pointer to soft context pool information */ /* ipn(I) - pointer to pool structure */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Drop a reference to the pool node passed in and if we're the last, free */ /* it all up and adjust the stats accordingly. */ /* ------------------------------------------------------------------------ */ static void ipf_pool_node_deref(softp, ipn) ipf_pool_softc_t *softp; ip_pool_node_t *ipn; { ipn->ipn_ref--; if (ipn->ipn_ref == 0) { KFREE(ipn); softp->ipf_pool_stats.ipls_nodes--; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_iter_next */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* token(I) - pointer to pool structure */ /* ilp(IO) - pointer to pool iterating structure */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_pool_iter_next(softc, arg, token, ilp) ipf_main_softc_t *softc; void *arg; ipftoken_t *token; ipflookupiter_t *ilp; { ipf_pool_softc_t *softp = arg; ip_pool_node_t *node, zn, *nextnode; ip_pool_t *ipo, zp, *nextipo; void *pnext; int err; err = 0; node = NULL; nextnode = NULL; ipo = NULL; nextipo = NULL; READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : ipo = token->ipt_data; if (ipo == NULL) { nextipo = softp->ipf_pool_list[(int)ilp->ili_unit + 1]; } else { nextipo = ipo->ipo_next; } if (nextipo != NULL) { ATOMIC_INC32(nextipo->ipo_ref); token->ipt_data = nextipo; } else { bzero((char *)&zp, sizeof(zp)); nextipo = &zp; token->ipt_data = NULL; } pnext = nextipo->ipo_next; break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { ipo = ipf_pool_exists(arg, ilp->ili_unit, ilp->ili_name); if (ipo == NULL) { IPFERROR(70010); err = ESRCH; } else { nextnode = ipo->ipo_list; ipo = NULL; } } else { nextnode = node->ipn_next; } if (nextnode != NULL) { ATOMIC_INC32(nextnode->ipn_ref); token->ipt_data = nextnode; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; } pnext = nextnode->ipn_next; break; default : IPFERROR(70011); pnext = NULL; err = EINVAL; break; } RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return err; switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : err = COPYOUT(nextipo, ilp->ili_data, sizeof(*nextipo)); if (err != 0) { IPFERROR(70012); err = EFAULT; } if (ipo != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_pool_deref(softc, softp, ipo); RWLOCK_EXIT(&softc->ipf_poolrw); } break; case IPFLOOKUPITER_NODE : err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); if (err != 0) { IPFERROR(70013); err = EFAULT; } if (node != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_pool_node_deref(softp, node); RWLOCK_EXIT(&softc->ipf_poolrw); } break; } if (pnext == NULL) ipf_token_mark_complete(token); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_iterderef */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_pool_iter_deref(softc, arg, otype, unit, data) ipf_main_softc_t *softc; void *arg; int otype; int unit; void *data; { ipf_pool_softc_t *softp = arg; if (data == NULL) return EINVAL; if (unit < 0 || unit > IPL_LOGMAX) return EINVAL; switch (otype) { case IPFLOOKUPITER_LIST : ipf_pool_deref(softc, softp, (ip_pool_t *)data); break; case IPFLOOKUPITER_NODE : ipf_pool_node_deref(softp, (ip_pool_node_t *)data); break; default : break; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* At present this function exists just to support temporary addition of */ /* nodes to the address pool. */ /* ------------------------------------------------------------------------ */ static void ipf_pool_expire(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_pool_softc_t *softp = arg; ip_pool_node_t *n; while ((n = softp->ipf_node_explist) != NULL) { /* * Because the list is kept sorted on insertion, the fist * one that dies in the future means no more work to do. */ if (n->ipn_die > softc->ipf_ticks) break; ipf_pool_remove_node(softc, softp, n->ipn_owner, n); } } #ifndef _KERNEL void ipf_pool_dump(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_pool_softc_t *softp = arg; ip_pool_t *ipl; int i; printf("List of configured pools\n"); for (i = 0; i <= LOOKUP_POOL_MAX; i++) for (ipl = softp->ipf_pool_list[i]; ipl != NULL; ipl = ipl->ipo_next) printpool(ipl, bcopywrap, NULL, opts, NULL); } #endif diff --git a/sys/contrib/ipfilter/netinet/ip_pool.h b/sys/contrib/ipfilter/netinet/ip_pool.h index 8524e607949c..11675dfcbeeb 100644 --- a/sys/contrib/ipfilter/netinet/ip_pool.h +++ b/sys/contrib/ipfilter/netinet/ip_pool.h @@ -1,67 +1,67 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ */ #ifndef __IP_POOL_H__ #define __IP_POOL_H__ #include "netinet/ip_lookup.h" #include "radix_ipf.h" #define IP_POOL_NOMATCH 0 #define IP_POOL_POSITIVE 1 typedef struct ip_pool_node { ipf_rdx_node_t ipn_nodes[2]; addrfamily_t ipn_addr; addrfamily_t ipn_mask; int ipn_uid; int ipn_info; int ipn_ref; char ipn_name[FR_GROUPLEN]; U_QUAD_T ipn_hits; U_QUAD_T ipn_bytes; u_long ipn_die; struct ip_pool_node *ipn_next, **ipn_pnext; struct ip_pool_node *ipn_dnext, **ipn_pdnext; struct ip_pool_s *ipn_owner; } ip_pool_node_t; typedef struct ip_pool_s { struct ip_pool_s *ipo_next; struct ip_pool_s **ipo_pnext; ipf_rdx_head_t *ipo_head; ip_pool_node_t *ipo_list; ip_pool_node_t **ipo_tail; ip_pool_node_t *ipo_nextaddr; void *ipo_radix; u_long ipo_hits; int ipo_unit; int ipo_flags; int ipo_ref; char ipo_name[FR_GROUPLEN]; } ip_pool_t; #define IPOOL_DELETE 0x01 #define IPOOL_ANON 0x02 typedef struct ipf_pool_stat { u_long ipls_pools; u_long ipls_tables; u_long ipls_nodes; ip_pool_t *ipls_list[LOOKUP_POOL_SZ]; } ipf_pool_stat_t; extern ipf_lookup_t ipf_pool_backend; #ifndef _KERNEL -extern void ipf_pool_dump __P((ipf_main_softc_t *, void *)); +extern void ipf_pool_dump(ipf_main_softc_t *, void *); #endif #endif /* __IP_POOL_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c b/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c index 959b107cd6af..e18748a05fae 100644 --- a/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c @@ -1,580 +1,580 @@ /* * Copyright (C) 2012 by Darren Reed. * * Simple PPTP transparent proxy for in-kernel use. For use with the NAT * code. * * $Id$ * */ #define IPF_PPTP_PROXY /* * PPTP proxy */ typedef struct pptp_side { u_32_t pptps_nexthdr; u_32_t pptps_next; int pptps_state; int pptps_gothdr; int pptps_len; int pptps_bytes; char *pptps_wptr; char pptps_buffer[512]; } pptp_side_t; typedef struct pptp_pxy { nat_t *pptp_nat; struct ipstate *pptp_state; u_short pptp_call[2]; pptp_side_t pptp_side[2]; ipnat_t *pptp_rule; } pptp_pxy_t; typedef struct pptp_hdr { u_short pptph_len; u_short pptph_type; u_32_t pptph_cookie; } pptp_hdr_t; #define PPTP_MSGTYPE_CTL 1 #define PPTP_MTCTL_STARTREQ 1 #define PPTP_MTCTL_STARTREP 2 #define PPTP_MTCTL_STOPREQ 3 #define PPTP_MTCTL_STOPREP 4 #define PPTP_MTCTL_ECHOREQ 5 #define PPTP_MTCTL_ECHOREP 6 #define PPTP_MTCTL_OUTREQ 7 #define PPTP_MTCTL_OUTREP 8 #define PPTP_MTCTL_INREQ 9 #define PPTP_MTCTL_INREP 10 #define PPTP_MTCTL_INCONNECT 11 #define PPTP_MTCTL_CLEAR 12 #define PPTP_MTCTL_DISCONNECT 13 #define PPTP_MTCTL_WANERROR 14 #define PPTP_MTCTL_LINKINFO 15 -void ipf_p_pptp_main_load __P((void)); -void ipf_p_pptp_main_unload __P((void)); -int ipf_p_pptp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -void ipf_p_pptp_del __P((ipf_main_softc_t *, ap_session_t *)); -int ipf_p_pptp_inout __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -void ipf_p_pptp_donatstate __P((fr_info_t *, nat_t *, pptp_pxy_t *)); -int ipf_p_pptp_message __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); -int ipf_p_pptp_nextmessage __P((fr_info_t *, nat_t *, pptp_pxy_t *, int)); -int ipf_p_pptp_mctl __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); +void ipf_p_pptp_main_load(void); +void ipf_p_pptp_main_unload(void); +int ipf_p_pptp_new(void *, fr_info_t *, ap_session_t *, nat_t *); +void ipf_p_pptp_del(ipf_main_softc_t *, ap_session_t *); +int ipf_p_pptp_inout(void *, fr_info_t *, ap_session_t *, nat_t *); +void ipf_p_pptp_donatstate(fr_info_t *, nat_t *, pptp_pxy_t *); +int ipf_p_pptp_message(fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *); +int ipf_p_pptp_nextmessage(fr_info_t *, nat_t *, pptp_pxy_t *, int); +int ipf_p_pptp_mctl(fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *); static frentry_t pptpfr; static int pptp_proxy_init = 0; static int ipf_p_pptp_debug = 0; static int ipf_p_pptp_gretimeout = IPF_TTLVAL(120); /* 2 minutes */ /* * PPTP application proxy initialization. */ void ipf_p_pptp_main_load() { bzero((char *)&pptpfr, sizeof(pptpfr)); pptpfr.fr_ref = 1; pptpfr.fr_age[0] = ipf_p_pptp_gretimeout; pptpfr.fr_age[1] = ipf_p_pptp_gretimeout; pptpfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&pptpfr.fr_lock, "PPTP proxy rule lock"); pptp_proxy_init = 1; } void ipf_p_pptp_main_unload() { if (pptp_proxy_init == 1) { MUTEX_DESTROY(&pptpfr.fr_lock); pptp_proxy_init = 0; } } /* * Setup for a new PPTP proxy. * * NOTE: The printf's are broken up with %s in them to prevent them being * optimised into puts statements on FreeBSD (this doesn't exist in the kernel) */ int ipf_p_pptp_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { pptp_pxy_t *pptp; ipnat_t *ipn; ipnat_t *np; int size; ip_t *ip; if (fin->fin_v != 4) return -1; ip = fin->fin_ip; np = nat->nat_ptr; size = np->in_size; if (ipf_nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_osrcip, ip->ip_dst) != NULL) { if (ipf_p_pptp_debug > 0) printf("ipf_p_pptp_new: GRE session already exists\n"); return -1; } KMALLOC(pptp, pptp_pxy_t *); if (pptp == NULL) { if (ipf_p_pptp_debug > 0) printf("ipf_p_pptp_new: malloc for aps_data failed\n"); return -1; } KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) { KFREE(pptp); return -1; } aps->aps_data = pptp; aps->aps_psiz = sizeof(*pptp); bzero((char *)pptp, sizeof(*pptp)); bzero((char *)ipn, size); pptp->pptp_rule = ipn; /* * Create NAT rule against which the tunnel/transport mapping is * created. This is required because the current NAT rule does not * describe GRE but TCP instead. */ ipn->in_size = size; ipn->in_ifps[0] = fin->fin_ifp; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_nsrcaddr = fin->fin_saddr; ipn->in_dnip = ntohl(nat->nat_ndstaddr); ipn->in_ndstaddr = nat->nat_ndstaddr; ipn->in_redir = np->in_redir; ipn->in_osrcaddr = nat->nat_osrcaddr; ipn->in_odstaddr = nat->nat_odstaddr; ipn->in_osrcmsk = 0xffffffff; ipn->in_nsrcmsk = 0xffffffff; ipn->in_odstmsk = 0xffffffff; ipn->in_ndstmsk = 0xffffffff; ipn->in_flags = (np->in_flags | IPN_PROXYRULE); MUTEX_INIT(&ipn->in_lock, "pptp proxy NAT rule"); ipn->in_namelen = np->in_namelen; bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); ipn->in_ifnames[0] = np->in_ifnames[0]; ipn->in_ifnames[1] = np->in_ifnames[1]; ipn->in_pr[0] = IPPROTO_GRE; ipn->in_pr[1] = IPPROTO_GRE; pptp->pptp_side[0].pptps_wptr = pptp->pptp_side[0].pptps_buffer; pptp->pptp_side[1].pptps_wptr = pptp->pptp_side[1].pptps_buffer; return 0; } void ipf_p_pptp_donatstate(fin, nat, pptp) fr_info_t *fin; nat_t *nat; pptp_pxy_t *pptp; { ipf_main_softc_t *softc = fin->fin_main_soft; fr_info_t fi; grehdr_t gre; nat_t *nat2; u_char p; ip_t *ip; ip = fin->fin_ip; p = ip->ip_p; nat2 = pptp->pptp_nat; if ((nat2 == NULL) || (pptp->pptp_state == NULL)) { bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)&gre, sizeof(gre)); fi.fin_fi.fi_p = IPPROTO_GRE; fi.fin_fr = &pptpfr; if ((nat->nat_dir == NAT_OUTBOUND && fin->fin_out) || (nat->nat_dir == NAT_INBOUND && !fin->fin_out)) { fi.fin_data[0] = pptp->pptp_call[0]; fi.fin_data[1] = pptp->pptp_call[1]; } else { fi.fin_data[0] = pptp->pptp_call[1]; fi.fin_data[1] = pptp->pptp_call[0]; } ip = fin->fin_ip; ip->ip_p = IPPROTO_GRE; fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); fi.fin_flx |= FI_IGNORE; fi.fin_dp = &gre; gre.gr_flags = htons(1 << 13); fi.fin_fi.fi_saddr = nat->nat_osrcaddr; fi.fin_fi.fi_daddr = nat->nat_odstaddr; } /* * Update NAT timeout/create NAT if missing. */ if (nat2 != NULL) ipf_queueback(softc->ipf_ticks, &nat2->nat_tqe); else { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif MUTEX_ENTER(&softn->ipf_nat_new); nat2 = ipf_nat_add(&fi, pptp->pptp_rule, &pptp->pptp_nat, NAT_SLAVE, nat->nat_dir); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, 0); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); } } READ_ENTER(&softc->ipf_state); if (pptp->pptp_state != NULL) { ipf_queueback(softc->ipf_ticks, &pptp->pptp_state->is_sti); RWLOCK_EXIT(&softc->ipf_state); } else { RWLOCK_EXIT(&softc->ipf_state); if (nat2 != NULL) { if (nat->nat_dir == NAT_INBOUND) fi.fin_fi.fi_daddr = nat2->nat_ndstaddr; else fi.fin_fi.fi_saddr = nat2->nat_osrcaddr; } fi.fin_ifp = NULL; (void) ipf_state_add(softc, &fi, &pptp->pptp_state, 0); } ip->ip_p = p; return; } /* * Try and build up the next PPTP message in the TCP stream and if we can * build it up completely (fits in our buffer) then pass it off to the message * parsing function. */ int ipf_p_pptp_nextmessage(fin, nat, pptp, rev) fr_info_t *fin; nat_t *nat; pptp_pxy_t *pptp; int rev; { static const char *funcname = "ipf_p_pptp_nextmessage"; pptp_side_t *pptps; u_32_t start, end; pptp_hdr_t *hdr; tcphdr_t *tcp; int dlen, off; u_short len; char *msg; tcp = fin->fin_dp; dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); start = ntohl(tcp->th_seq); pptps = &pptp->pptp_side[rev]; off = (char *)tcp - (char *)fin->fin_ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; if (dlen <= 0) return 0; /* * If the complete data packet is before what we expect to see * "next", just ignore it as the chances are we've already seen it. * The next if statement following this one really just causes packets * ahead of what we've seen to be dropped, implying that something in * the middle went missing and we want to see that first. */ end = start + dlen; if (pptps->pptps_next > end && pptps->pptps_next > start) return 0; if (pptps->pptps_next != start) { if (ipf_p_pptp_debug > 5) printf("%s: next (%x) != start (%x)\n", funcname, pptps->pptps_next, start); return -1; } msg = (char *)fin->fin_dp + (TCP_OFF(tcp) << 2); while (dlen > 0) { off += pptps->pptps_bytes; if (pptps->pptps_gothdr == 0) { /* * PPTP has an 8 byte header that inclues the cookie. * The start of every message should include one and * it should match 1a2b3c4d. Byte order is ignored, * deliberately, when printing out the error. */ len = MIN(8 - pptps->pptps_bytes, dlen); COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); pptps->pptps_bytes += len; pptps->pptps_wptr += len; hdr = (pptp_hdr_t *)pptps->pptps_buffer; if (pptps->pptps_bytes == 8) { pptps->pptps_next += 8; if (ntohl(hdr->pptph_cookie) != 0x1a2b3c4d) { if (ipf_p_pptp_debug > 1) printf("%s: bad cookie (%x)\n", funcname, hdr->pptph_cookie); return -1; } } dlen -= len; msg += len; off += len; pptps->pptps_gothdr = 1; len = ntohs(hdr->pptph_len); pptps->pptps_len = len; pptps->pptps_nexthdr += len; /* * If a message is too big for the buffer, just set * the fields for the next message to come along. * The messages defined in RFC 2637 will not exceed * 512 bytes (in total length) so this is likely a * bad data packet, anyway. */ if (len > sizeof(pptps->pptps_buffer)) { if (ipf_p_pptp_debug > 3) printf("%s: message too big (%d)\n", funcname, len); pptps->pptps_next = pptps->pptps_nexthdr; pptps->pptps_wptr = pptps->pptps_buffer; pptps->pptps_gothdr = 0; pptps->pptps_bytes = 0; pptps->pptps_len = 0; break; } } len = MIN(pptps->pptps_len - pptps->pptps_bytes, dlen); COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); pptps->pptps_bytes += len; pptps->pptps_wptr += len; pptps->pptps_next += len; if (pptps->pptps_len > pptps->pptps_bytes) break; ipf_p_pptp_message(fin, nat, pptp, pptps); pptps->pptps_wptr = pptps->pptps_buffer; pptps->pptps_gothdr = 0; pptps->pptps_bytes = 0; pptps->pptps_len = 0; start += len; msg += len; dlen -= len; } return 0; } /* * handle a complete PPTP message */ int ipf_p_pptp_message(fin, nat, pptp, pptps) fr_info_t *fin; nat_t *nat; pptp_pxy_t *pptp; pptp_side_t *pptps; { pptp_hdr_t *hdr = (pptp_hdr_t *)pptps->pptps_buffer; switch (ntohs(hdr->pptph_type)) { case PPTP_MSGTYPE_CTL : ipf_p_pptp_mctl(fin, nat, pptp, pptps); break; default : break; } return 0; } /* * handle a complete PPTP control message */ int ipf_p_pptp_mctl(fin, nat, pptp, pptps) fr_info_t *fin; nat_t *nat; pptp_pxy_t *pptp; pptp_side_t *pptps; { u_short *buffer = (u_short *)(pptps->pptps_buffer); pptp_side_t *pptpo; if (pptps == &pptp->pptp_side[0]) pptpo = &pptp->pptp_side[1]; else pptpo = &pptp->pptp_side[0]; /* * Breakout to handle all the various messages. Most are just state * transition. */ switch (ntohs(buffer[4])) { case PPTP_MTCTL_STARTREQ : pptps->pptps_state = PPTP_MTCTL_STARTREQ; break; case PPTP_MTCTL_STARTREP : if (pptpo->pptps_state == PPTP_MTCTL_STARTREQ) pptps->pptps_state = PPTP_MTCTL_STARTREP; break; case PPTP_MTCTL_STOPREQ : pptps->pptps_state = PPTP_MTCTL_STOPREQ; break; case PPTP_MTCTL_STOPREP : if (pptpo->pptps_state == PPTP_MTCTL_STOPREQ) pptps->pptps_state = PPTP_MTCTL_STOPREP; break; case PPTP_MTCTL_ECHOREQ : pptps->pptps_state = PPTP_MTCTL_ECHOREQ; break; case PPTP_MTCTL_ECHOREP : if (pptpo->pptps_state == PPTP_MTCTL_ECHOREQ) pptps->pptps_state = PPTP_MTCTL_ECHOREP; break; case PPTP_MTCTL_OUTREQ : pptps->pptps_state = PPTP_MTCTL_OUTREQ; break; case PPTP_MTCTL_OUTREP : if (pptpo->pptps_state == PPTP_MTCTL_OUTREQ) { pptps->pptps_state = PPTP_MTCTL_OUTREP; pptp->pptp_call[0] = buffer[7]; pptp->pptp_call[1] = buffer[6]; ipf_p_pptp_donatstate(fin, nat, pptp); } break; case PPTP_MTCTL_INREQ : pptps->pptps_state = PPTP_MTCTL_INREQ; break; case PPTP_MTCTL_INREP : if (pptpo->pptps_state == PPTP_MTCTL_INREQ) { pptps->pptps_state = PPTP_MTCTL_INREP; pptp->pptp_call[0] = buffer[7]; pptp->pptp_call[1] = buffer[6]; ipf_p_pptp_donatstate(fin, nat, pptp); } break; case PPTP_MTCTL_INCONNECT : pptps->pptps_state = PPTP_MTCTL_INCONNECT; break; case PPTP_MTCTL_CLEAR : pptps->pptps_state = PPTP_MTCTL_CLEAR; break; case PPTP_MTCTL_DISCONNECT : pptps->pptps_state = PPTP_MTCTL_DISCONNECT; break; case PPTP_MTCTL_WANERROR : pptps->pptps_state = PPTP_MTCTL_WANERROR; break; case PPTP_MTCTL_LINKINFO : pptps->pptps_state = PPTP_MTCTL_LINKINFO; break; } return 0; } /* * For outgoing PPTP packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ int ipf_p_pptp_inout(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { pptp_pxy_t *pptp; tcphdr_t *tcp; int rev; if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) rev = 1; else if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) rev = 1; else rev = 0; tcp = (tcphdr_t *)fin->fin_dp; if ((tcp->th_flags & TH_OPENING) == TH_OPENING) { pptp = (pptp_pxy_t *)aps->aps_data; pptp->pptp_side[1 - rev].pptps_next = ntohl(tcp->th_ack); pptp->pptp_side[1 - rev].pptps_nexthdr = ntohl(tcp->th_ack); pptp->pptp_side[rev].pptps_next = ntohl(tcp->th_seq) + 1; pptp->pptp_side[rev].pptps_nexthdr = ntohl(tcp->th_seq) + 1; } return ipf_p_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, rev); } /* * clean up after ourselves. */ void ipf_p_pptp_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { pptp_pxy_t *pptp; pptp = aps->aps_data; if (pptp != NULL) { /* * Don't bother changing any of the NAT structure details, * *_del() is on a callback from aps_free(), from nat_delete() */ READ_ENTER(&softc->ipf_state); if (pptp->pptp_state != NULL) { ipf_state_setpending(softc, pptp->pptp_state); } RWLOCK_EXIT(&softc->ipf_state); if (pptp->pptp_nat != NULL) ipf_nat_setpending(softc, pptp->pptp_nat); pptp->pptp_rule->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &pptp->pptp_rule); } } diff --git a/sys/contrib/ipfilter/netinet/ip_proxy.c b/sys/contrib/ipfilter/netinet/ip_proxy.c index 84d600ecd113..227ca817f6e4 100644 --- a/sys/contrib/ipfilter/netinet/ip_proxy.c +++ b/sys/contrib/ipfilter/netinet/ip_proxy.c @@ -1,1466 +1,1466 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include # include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # include # define _KERNEL # include # undef _KERNEL #endif # include #include #if defined(_KERNEL) #ifdef __FreeBSD_version # include # endif # include # if !defined(__SVR4) # include # endif #endif #if defined(_KERNEL) && defined(__FreeBSD_version) # include # include #else # include #endif #if defined(__SVR4) # include # ifdef _KERNEL # include # endif # include # include #endif #ifdef __FreeBSD_version # include #endif #include #if defined(__FreeBSD_version) && defined(_KERNEL) #include #else #define CURVNET_SET(arg) #define CURVNET_RESTORE() #define VNET_DEFINE(_t, _v) _t _v #define VNET_DECLARE(_t, _v) extern _t _v #define VNET(arg) arg #endif #ifdef sun # include #endif #include #include #include # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #if defined(__FreeBSD_version) # include #endif /* END OF INCLUDES */ #include "netinet/ip_ftp_pxy.c" #include "netinet/ip_tftp_pxy.c" #include "netinet/ip_rcmd_pxy.c" #include "netinet/ip_pptp_pxy.c" #if defined(_KERNEL) # include "netinet/ip_irc_pxy.c" # include "netinet/ip_raudio_pxy.c" # include "netinet/ip_netbios_pxy.c" #endif #include "netinet/ip_ipsec_pxy.c" #include "netinet/ip_rpcb_pxy.c" #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif #define AP_SESS_SIZE 53 -static int ipf_proxy_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int )); -static aproxy_t *ipf_proxy_create_clone __P((ipf_main_softc_t *, aproxy_t *)); +static int ipf_proxy_fixseqack(fr_info_t *, ip_t *, ap_session_t *, int ); +static aproxy_t *ipf_proxy_create_clone(ipf_main_softc_t *, aproxy_t *); typedef struct ipf_proxy_softc_s { int ips_proxy_debug; int ips_proxy_session_size; ap_session_t **ips_sess_tab; ap_session_t *ips_sess_list; aproxy_t *ips_proxies; int ips_init_run; ipftuneable_t *ipf_proxy_tune; } ipf_proxy_softc_t; static ipftuneable_t ipf_proxy_tuneables[] = { { { (void *)offsetof(ipf_proxy_softc_t, ips_proxy_debug) }, "proxy_debug", 0, 0x1f, stsizeof(ipf_proxy_softc_t, ips_proxy_debug), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL} }; static aproxy_t *ap_proxylist = NULL; static aproxy_t ips_proxies[] = { #ifdef IPF_FTP_PROXY { NULL, NULL, "ftp", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_ftp_main_load, ipf_p_ftp_main_unload, ipf_p_ftp_soft_create, ipf_p_ftp_soft_destroy, NULL, NULL, ipf_p_ftp_new, ipf_p_ftp_del, ipf_p_ftp_in, ipf_p_ftp_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_TFTP_PROXY { NULL, NULL, "tftp", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_tftp_main_load, ipf_p_tftp_main_unload, ipf_p_tftp_soft_create, ipf_p_tftp_soft_destroy, NULL, NULL, ipf_p_tftp_new, ipf_p_tftp_del, ipf_p_tftp_in, ipf_p_tftp_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_IRC_PROXY { NULL, NULL, "irc", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_irc_main_load, ipf_p_irc_main_unload, NULL, NULL, NULL, NULL, ipf_p_irc_new, NULL, NULL, ipf_p_irc_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RCMD_PROXY { NULL, NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_rcmd_main_load, ipf_p_rcmd_main_unload, NULL, NULL, NULL, NULL, ipf_p_rcmd_new, ipf_p_rcmd_del, ipf_p_rcmd_in, ipf_p_rcmd_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RAUDIO_PROXY { NULL, NULL, "raudio", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_raudio_main_load, ipf_p_raudio_main_unload, NULL, NULL, NULL, NULL, ipf_p_raudio_new, NULL, ipf_p_raudio_in, ipf_p_raudio_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_MSNRPC_PROXY { NULL, NULL, "msnrpc", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_msnrpc_init, ipf_p_msnrpc_fini, NULL, NULL, NULL, NULL, ipf_p_msnrpc_new, NULL, ipf_p_msnrpc_in, ipf_p_msnrpc_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_NETBIOS_PROXY { NULL, NULL, "netbios", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_netbios_main_load, ipf_p_netbios_main_unload, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ipf_p_netbios_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_IPSEC_PROXY { NULL, NULL, "ipsec", (char)IPPROTO_UDP, 0, 0, 0, NULL, NULL, ipf_p_ipsec_soft_create, ipf_p_ipsec_soft_destroy, ipf_p_ipsec_soft_init, ipf_p_ipsec_soft_fini, ipf_p_ipsec_new, ipf_p_ipsec_del, ipf_p_ipsec_inout, ipf_p_ipsec_inout, ipf_p_ipsec_match, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_DNS_PROXY { NULL, NULL, "dns", (char)IPPROTO_UDP, 0, 0, 0, NULL, NULL, ipf_p_dns_soft_create, ipf_p_dns_soft_destroy, NULL, NULL, ipf_p_dns_new, ipf_p_ipsec_del, ipf_p_dns_inout, ipf_p_dns_inout, ipf_p_dns_match, ipf_p_dns_ctl, NULL, NULL, NULL }, #endif #ifdef IPF_PPTP_PROXY { NULL, NULL, "pptp", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_pptp_main_load, ipf_p_pptp_main_unload, NULL, NULL, NULL, NULL, ipf_p_pptp_new, ipf_p_pptp_del, ipf_p_pptp_inout, ipf_p_pptp_inout, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RPCB_PROXY # ifndef _KERNEL { NULL, NULL, "rpcbt", (char)IPPROTO_TCP, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, ipf_p_rpcb_new, ipf_p_rpcb_del, ipf_p_rpcb_in, ipf_p_rpcb_out, NULL, NULL, NULL, NULL, NULL }, # endif { NULL, NULL, "rpcbu", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_rpcb_main_load, ipf_p_rpcb_main_unload, NULL, NULL, NULL, NULL, ipf_p_rpcb_new, ipf_p_rpcb_del, ipf_p_rpcb_in, ipf_p_rpcb_out, NULL, NULL, NULL, NULL, NULL }, #endif { NULL, NULL, "", '\0', 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_main_load */ /* Returns: int - 0 == success, else failure. */ /* Parameters: Nil */ /* */ /* Initialise hook for kernel application proxies. */ /* Call the initialise routine for all the compiled in kernel proxies. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_main_load() { aproxy_t *ap; for (ap = ips_proxies; ap->apr_p; ap++) { if (ap->apr_load != NULL) (*ap->apr_load)(); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_main_unload */ /* Returns: int - 0 == success, else failure. */ /* Parameters: Nil */ /* */ /* Unload hook for kernel application proxies. */ /* Call the finialise routine for all the compiled in kernel proxies. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_main_unload() { aproxy_t *ap; for (ap = ips_proxies; ap->apr_p; ap++) if (ap->apr_unload != NULL) (*ap->apr_unload)(); for (ap = ap_proxylist; ap; ap = ap->apr_next) if (ap->apr_unload != NULL) (*ap->apr_unload)(); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: void * - */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Build the structure to hold all of the run time data to support proxies. */ /* ------------------------------------------------------------------------ */ void * ipf_proxy_soft_create(softc) ipf_main_softc_t *softc; { ipf_proxy_softc_t *softp; aproxy_t *last; aproxy_t *apn; aproxy_t *ap; KMALLOC(softp, ipf_proxy_softc_t *); if (softp == NULL) return softp; bzero((char *)softp, sizeof(*softp)); #if defined(_KERNEL) softp->ips_proxy_debug = 0; #else softp->ips_proxy_debug = 2; #endif softp->ips_proxy_session_size = AP_SESS_SIZE; softp->ipf_proxy_tune = ipf_tune_array_copy(softp, sizeof(ipf_proxy_tuneables), ipf_proxy_tuneables); if (softp->ipf_proxy_tune == NULL) { ipf_proxy_soft_destroy(softc, softp); return NULL; } if (ipf_tune_array_link(softc, softp->ipf_proxy_tune) == -1) { ipf_proxy_soft_destroy(softc, softp); return NULL; } last = NULL; for (ap = ips_proxies; ap->apr_p; ap++) { apn = ipf_proxy_create_clone(softc, ap); if (apn == NULL) goto failed; if (last != NULL) last->apr_next = apn; else softp->ips_proxies = apn; last = apn; } for (ap = ips_proxies; ap != NULL; ap = ap->apr_next) { apn = ipf_proxy_create_clone(softc, ap); if (apn == NULL) goto failed; if (last != NULL) last->apr_next = apn; else softp->ips_proxies = apn; last = apn; } return softp; failed: ipf_proxy_soft_destroy(softc, softp); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: void * - */ /* Parameters: softc(I) - pointer to soft context main structure */ /* orig(I) - pointer to proxy definition to copy */ /* */ /* This function clones a proxy definition given by orig and returns a */ /* a pointer to that copy. */ /* ------------------------------------------------------------------------ */ static aproxy_t * ipf_proxy_create_clone(softc, orig) ipf_main_softc_t *softc; aproxy_t *orig; { aproxy_t *apn; KMALLOC(apn, aproxy_t *); if (apn == NULL) return NULL; bcopy((char *)orig, (char *)apn, sizeof(*apn)); apn->apr_next = NULL; apn->apr_soft = NULL; if (apn->apr_create != NULL) { apn->apr_soft = (*apn->apr_create)(softc); if (apn->apr_soft == NULL) { KFREE(apn); return NULL; } } apn->apr_parent = orig; orig->apr_clones++; return apn; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: int - 0 == success, else failure. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* Initialise the proxy context and walk through each of the proxies and */ /* call its initialisation function. This allows for proxies to do any */ /* local setup prior to actual use. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_proxy_softc_t *softp; aproxy_t *ap; u_int size; int err; softp = arg; size = softp->ips_proxy_session_size * sizeof(ap_session_t *); KMALLOCS(softp->ips_sess_tab, ap_session_t **, size); if (softp->ips_sess_tab == NULL) return -1; bzero(softp->ips_sess_tab, size); for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) { if (ap->apr_init != NULL) { err = (*ap->apr_init)(softc, ap->apr_soft); if (err != 0) return -2; } } softp->ips_init_run = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: int - 0 == success, else failure. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* This function should always succeed. It is responsible for ensuring that */ /* the proxy context can be safely called when ipf_proxy_soft_destroy is */ /* called and suring all of the proxies have similarly been instructed. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) { if (ap->apr_fini != NULL) { (*ap->apr_fini)(softc, ap->apr_soft); } } if (softp->ips_sess_tab != NULL) { KFREES(softp->ips_sess_tab, softp->ips_proxy_session_size * sizeof(ap_session_t *)); softp->ips_sess_tab = NULL; } softp->ips_init_run = 0; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* Free up all of the local data structures allocated during creation. */ /* ------------------------------------------------------------------------ */ void ipf_proxy_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; while ((ap = softp->ips_proxies) != NULL) { softp->ips_proxies = ap->apr_next; if (ap->apr_destroy != NULL) (*ap->apr_destroy)(softc, ap->apr_soft); ap->apr_parent->apr_clones--; KFREE(ap); } if (softp->ipf_proxy_tune != NULL) { ipf_tune_array_unlink(softc, softp->ipf_proxy_tune); KFREES(softp->ipf_proxy_tune, sizeof(ipf_proxy_tuneables)); softp->ipf_proxy_tune = NULL; } KFREE(softp); } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_flush */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to proxy contect data */ /* how(I) - indicates the type of flush operation */ /* */ /* Walk through all of the proxies and pass on the flush command as either */ /* a flush or a clear. */ /* ------------------------------------------------------------------------ */ void ipf_proxy_flush(arg, how) void *arg; int how; { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; switch (how) { case 0 : for (ap = softp->ips_proxies; ap; ap = ap->apr_next) if (ap->apr_flush != NULL) (*ap->apr_flush)(ap, how); break; case 1 : for (ap = softp->ips_proxies; ap; ap = ap->apr_next) if (ap->apr_clear != NULL) (*ap->apr_clear)(ap); break; default : break; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_add */ /* Returns: int - 0 == success, else failure. */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Dynamically add a new kernel proxy. Ensure that it is unique in the */ /* collection compiled in and dynamically added. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_add(arg, ap) void *arg; aproxy_t *ap; { ipf_proxy_softc_t *softp = arg; aproxy_t *a; for (a = ips_proxies; a->apr_p; a++) if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_add: %s/%d present (B)\n", a->apr_label, a->apr_p); return -1; } for (a = ap_proxylist; (a != NULL); a = a->apr_next) if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_add: %s/%d present (D)\n", a->apr_label, a->apr_p); return -1; } ap->apr_next = ap_proxylist; ap_proxylist = ap; if (ap->apr_load != NULL) (*ap->apr_load)(); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ctl */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy context */ /* ctl(I) - pointer to proxy control structure */ /* */ /* Check to see if the proxy this control request has come through for */ /* exists, and if it does and it has a control function then invoke that */ /* control function. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_ctl(softc, arg, ctl) ipf_main_softc_t *softc; void *arg; ap_ctl_t *ctl; { ipf_proxy_softc_t *softp = arg; aproxy_t *a; int error; a = ipf_proxy_lookup(arg, ctl->apc_p, ctl->apc_label); if (a == NULL) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_ctl: can't find %s/%d\n", ctl->apc_label, ctl->apc_p); IPFERROR(80001); error = ESRCH; } else if (a->apr_ctl == NULL) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_ctl: no ctl function for %s/%d\n", ctl->apc_label, ctl->apc_p); IPFERROR(80002); error = ENXIO; } else { error = (*a->apr_ctl)(softc, a->apr_soft, ctl); if ((error != 0) && (softp->ips_proxy_debug & 0x02)) printf("ipf_proxy_ctl: %s/%d ctl error %d\n", a->apr_label, a->apr_p, error); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_del */ /* Returns: int - 0 == success, else failure. */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Delete a proxy that has been added dynamically from those available. */ /* If it is in use, return 1 (do not destroy NOW), not in use 0 or -1 */ /* if it cannot be matched. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_del(ap) aproxy_t *ap; { aproxy_t *a, **app; for (app = &ap_proxylist; ((a = *app) != NULL); app = &a->apr_next) { if (a == ap) { a->apr_flags |= APR_DELETE; if (ap->apr_ref == 0 && ap->apr_clones == 0) { *app = a->apr_next; return 0; } return 1; } } return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ok */ /* Returns: int - 1 == good match else not. */ /* Parameters: fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP/UDP header */ /* nat(I) - pointer to current NAT session */ /* */ /* This function extends the NAT matching to ensure that a packet that has */ /* arrived matches the proxy information attached to the NAT rule. Notably, */ /* if the proxy is scheduled to be deleted then packets will not match the */ /* rule even if the rule is still active. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_ok(fin, tcp, np) fr_info_t *fin; tcphdr_t *tcp; ipnat_t *np; { aproxy_t *apr = np->in_apr; u_short dport = np->in_odport; if ((apr == NULL) || (apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) return 0; if ((tcp == NULL) && dport) return 0; return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ioctl */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode bits for device */ /* ctx(I) - pointer to context information */ /* */ /* ------------------------------------------------------------------------ */ int ipf_proxy_ioctl(softc, data, cmd, mode, ctx) ipf_main_softc_t *softc; caddr_t data; ioctlcmd_t cmd; int mode; void *ctx; { ap_ctl_t ctl; caddr_t ptr; int error; mode = mode; /* LINT */ switch (cmd) { case SIOCPROXY : error = ipf_inobj(softc, data, NULL, &ctl, IPFOBJ_PROXYCTL); if (error != 0) { return error; } ptr = NULL; if (ctl.apc_dsize > 0) { KMALLOCS(ptr, caddr_t, ctl.apc_dsize); if (ptr == NULL) { IPFERROR(80003); error = ENOMEM; } else { error = copyinptr(softc, ctl.apc_data, ptr, ctl.apc_dsize); if (error == 0) ctl.apc_data = ptr; } } else { ctl.apc_data = NULL; error = 0; } if (error == 0) error = ipf_proxy_ctl(softc, softc->ipf_proxy_soft, &ctl); if ((error != 0) && (ptr != NULL)) { KFREES(ptr, ctl.apc_dsize); } break; default : IPFERROR(80004); error = EINVAL; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_match */ /* Returns: int - 0 == success, else error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* If a proxy has a match function, call that to do extended packet */ /* matching. Whilst other parts of the NAT code are rather lenient when it */ /* comes to the quality of the packet that it will transform, the proxy */ /* matching is not because they need to work with data, not just headers. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_match(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; aproxy_t *apr; ipnat_t *ipn; int result; ipn = nat->nat_ptr; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_match(%lx,%lx) aps %lx ptr %lx\n", (u_long)fin, (u_long)nat, (u_long)nat->nat_aps, (u_long)ipn); if ((fin->fin_flx & (FI_SHORT|FI_BAD)) != 0) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match: flx 0x%x (BAD|SHORT)\n", fin->fin_flx); return -1; } apr = ipn->in_apr; if ((apr == NULL) || (apr->apr_flags & APR_DELETE)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match:apr %lx apr_flags 0x%x\n", (u_long)apr, apr ? apr->apr_flags : 0); return -1; } if (apr->apr_match != NULL) { result = (*apr->apr_match)(fin, nat->nat_aps, nat); if (result != 0) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match: result %d\n", result); return -1; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_new */ /* Returns: int - 0 == success, else error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* Allocate a new application proxy structure and fill it in with the */ /* relevant details. call the init function once complete, prior to */ /* returning. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_new(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; register ap_session_t *aps; aproxy_t *apr; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_new(%lx,%lx) \n", (u_long)fin, (u_long)nat); if ((nat->nat_ptr == NULL) || (nat->nat_aps != NULL)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: nat_ptr %lx nat_aps %lx\n", (u_long)nat->nat_ptr, (u_long)nat->nat_aps); return -1; } apr = nat->nat_ptr->in_apr; if ((apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: apr_flags 0x%x p %d/%d\n", apr->apr_flags, fin->fin_p, apr->apr_p); return -1; } KMALLOC(aps, ap_session_t *); if (!aps) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: malloc failed (%lu)\n", (u_long)sizeof(ap_session_t)); return -1; } bzero((char *)aps, sizeof(*aps)); aps->aps_data = NULL; aps->aps_apr = apr; aps->aps_psiz = 0; if (apr->apr_new != NULL) if ((*apr->apr_new)(apr->apr_soft, fin, aps, nat) == -1) { if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) { KFREES(aps->aps_data, aps->aps_psiz); } KFREE(aps); if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: new(%lx) failed\n", (u_long)apr->apr_new); return -1; } aps->aps_nat = nat; aps->aps_next = softp->ips_sess_list; softp->ips_sess_list = aps; nat->nat_aps = aps; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_check */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* Check to see if a packet should be passed through an active proxy */ /* routine if one has been setup for it. We don't need to check the */ /* checksum here if IPFILTER_CKSUM is defined because if it is, a failed */ /* check causes FI_BAD to be set. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_check(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; #if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) mb_t *m; #endif tcphdr_t *tcp = NULL; udphdr_t *udp = NULL; ap_session_t *aps; aproxy_t *apr; short adjlen; int dosum; ip_t *ip; short rv; int err; #if !defined(_KERNEL) || defined(MENTAT) u_32_t s1, s2, sd; #endif if (fin->fin_flx & FI_BAD) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: flx 0x%x (BAD)\n", fin->fin_flx); return -1; } #ifndef IPFILTER_CKSUM if ((fin->fin_out == 0) && (ipf_checkl4sum(fin) == -1)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: l4 checksum failure %d\n", fin->fin_p); if (fin->fin_p == IPPROTO_TCP) softc->ipf_stats[fin->fin_out].fr_tcpbad++; return -1; } #endif aps = nat->nat_aps; if (aps != NULL) { /* * If there is data in this packet to be proxied then try and * get it all into the one buffer, else drop it. */ #if defined(MENTAT) || defined(HAVE_M_PULLDOWN) if ((fin->fin_dlen > 0) && !(fin->fin_flx & FI_COALESCE)) if (ipf_coalesce(fin) == -1) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: %s %x\n", "coalesce failed", fin->fin_flx); return -1; } #endif ip = fin->fin_ip; if (fin->fin_cksum > FI_CK_SUMOK) dosum = 0; else dosum = 1; switch (fin->fin_p) { case IPPROTO_TCP : tcp = (tcphdr_t *)fin->fin_dp; #if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) m = fin->fin_qfm; if (dohwcksum && (m->b_ick_flag == ICK_VALID)) dosum = 0; #endif break; case IPPROTO_UDP : udp = (udphdr_t *)fin->fin_dp; break; default : break; } apr = aps->aps_apr; err = 0; if (fin->fin_out != 0) { if (apr->apr_outpkt != NULL) err = (*apr->apr_outpkt)(apr->apr_soft, fin, aps, nat); } else { if (apr->apr_inpkt != NULL) err = (*apr->apr_inpkt)(apr->apr_soft, fin, aps, nat); } rv = APR_EXIT(err); if (((softp->ips_proxy_debug & 0x08) && (rv != 0)) || (softp->ips_proxy_debug & 0x04)) printf("ipf_proxy_check: out %d err %x rv %d\n", fin->fin_out, err, rv); if (rv == 1) return -1; if (rv == 2) { ipf_proxy_deref(apr); nat->nat_aps = NULL; return -1; } /* * If err != 0 then the data size of the packet has changed * so we need to recalculate the header checksums for the * packet. */ adjlen = APR_INC(err); #if !defined(_KERNEL) || defined(MENTAT) s1 = LONG_SUM(fin->fin_plen - adjlen); s2 = LONG_SUM(fin->fin_plen); CALC_SUMD(s1, s2, sd); if ((err != 0) && (fin->fin_cksum < FI_CK_L4PART) && fin->fin_v == 4) ipf_fix_outcksum(0, &ip->ip_sum, sd, 0); #endif if (fin->fin_flx & FI_DOCKSUM) dosum = 1; /* * For TCP packets, we may need to adjust the sequence and * acknowledgement numbers to reflect changes in size of the * data stream. * * For both TCP and UDP, recalculate the layer 4 checksum, * regardless, as we can't tell (here) if data has been * changed or not. */ if (tcp != NULL) { err = ipf_proxy_fixseqack(fin, ip, aps, adjlen); if (fin->fin_cksum == FI_CK_L4PART) { u_short sum = ntohs(tcp->th_sum); sum += adjlen; tcp->th_sum = htons(sum); } else if (fin->fin_cksum < FI_CK_L4PART) { tcp->th_sum = fr_cksum(fin, ip, IPPROTO_TCP, tcp); } } else if ((udp != NULL) && (udp->uh_sum != 0)) { if (fin->fin_cksum == FI_CK_L4PART) { u_short sum = ntohs(udp->uh_sum); sum += adjlen; udp->uh_sum = htons(sum); } else if (dosum) { udp->uh_sum = fr_cksum(fin, ip, IPPROTO_UDP, udp); } } aps->aps_bytes += fin->fin_plen; aps->aps_pkts++; return 1; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_lookup */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: arg(I) - pointer to proxy context information */ /* pr(I) - protocol number for proxy */ /* name(I) - proxy name */ /* */ /* Search for an proxy by the protocol it is being used with and its name. */ /* ------------------------------------------------------------------------ */ aproxy_t * ipf_proxy_lookup(arg, pr, name) void *arg; u_int pr; char *name; { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_lookup(%d,%s)\n", pr, name); for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) if ((ap->apr_p == pr) && !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) { ap->apr_ref++; return ap; } if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_lookup: failed for %d/%s\n", pr, name); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_deref */ /* Returns: Nil */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Drop the reference counter associated with the proxy. */ /* ------------------------------------------------------------------------ */ void ipf_proxy_deref(ap) aproxy_t *ap; { ap->apr_ref--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_free */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* aps(I) - pointer to current proxy session */ /* Locks Held: ipf_nat_new, ipf_nat(W) */ /* */ /* Free up proxy session information allocated to be used with a NAT */ /* session. */ /* ------------------------------------------------------------------------ */ void ipf_proxy_free(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; ap_session_t *a, **ap; aproxy_t *apr; if (!aps) return; for (ap = &softp->ips_sess_list; ((a = *ap) != NULL); ap = &a->aps_next) if (a == aps) { *ap = a->aps_next; break; } apr = aps->aps_apr; if ((apr != NULL) && (apr->apr_del != NULL)) (*apr->apr_del)(softc, aps); if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) KFREES(aps->aps_data, aps->aps_psiz); KFREE(aps); } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_fixseqack */ /* Returns: int - 2 if TCP ack/seq is changed, else 0 */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to IP header */ /* nat(I) - pointer to current NAT session */ /* inc(I) - delta to apply to TCP sequence numbering */ /* */ /* Adjust the TCP sequence/acknowledge numbers in the TCP header based on */ /* whether or not the new header is past the point at which an adjustment */ /* occurred. This might happen because of (say) an FTP string being changed */ /* and the new string being a different length to the old. */ /* ------------------------------------------------------------------------ */ static int ipf_proxy_fixseqack(fin, ip, aps, inc) fr_info_t *fin; ip_t *ip; ap_session_t *aps; int inc; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; int sel, ch = 0, out, nlen; u_32_t seq1, seq2; tcphdr_t *tcp; short inc2; tcp = (tcphdr_t *)fin->fin_dp; out = fin->fin_out; /* * ip_len has already been adjusted by 'inc'. */ nlen = fin->fin_dlen; nlen -= (TCP_OFF(tcp) << 2); inc2 = inc; inc = (int)inc2; if (out != 0) { seq1 = (u_32_t)ntohl(tcp->th_seq); sel = aps->aps_sel[out]; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); sel = aps->aps_sel[out] = !sel; } if (aps->aps_seqoff[sel]) { seq2 = aps->aps_seqmin[sel] - aps->aps_seqoff[sel]; if (seq1 > seq2) { seq2 = aps->aps_seqoff[sel]; seq1 += seq2; tcp->th_seq = htonl(seq1); ch = 1; } } if (inc && (seq1 > aps->aps_seqmin[!sel])) { aps->aps_seqmin[sel] = seq1 + nlen - 1; aps->aps_seqoff[sel] = aps->aps_seqoff[sel] + inc; if (softp->ips_proxy_debug & 0x10) printf("proxy seq set %d at %x to %d + %d\n", sel, aps->aps_seqmin[sel], aps->aps_seqoff[sel], inc); } /***/ seq1 = ntohl(tcp->th_ack); sel = aps->aps_sel[1 - out]; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); sel = aps->aps_sel[1 - out] = !sel; } if (aps->aps_ackoff[sel] && (seq1 > aps->aps_ackmin[sel])) { seq2 = aps->aps_ackoff[sel]; tcp->th_ack = htonl(seq1 - seq2); ch = 1; } } else { seq1 = ntohl(tcp->th_seq); sel = aps->aps_sel[out]; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy in switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); sel = aps->aps_sel[out] = !sel; } if (aps->aps_ackoff[sel]) { seq2 = aps->aps_ackmin[sel] - aps->aps_ackoff[sel]; if (seq1 > seq2) { seq2 = aps->aps_ackoff[sel]; seq1 += seq2; tcp->th_seq = htonl(seq1); ch = 1; } } if (inc && (seq1 > aps->aps_ackmin[!sel])) { aps->aps_ackmin[!sel] = seq1 + nlen - 1; aps->aps_ackoff[!sel] = aps->aps_ackoff[sel] + inc; if (softp->ips_proxy_debug & 0x10) printf("proxy ack set %d at %x to %d + %d\n", !sel, aps->aps_seqmin[!sel], aps->aps_seqoff[sel], inc); } /***/ seq1 = ntohl(tcp->th_ack); sel = aps->aps_sel[1 - out]; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy in switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); sel = aps->aps_sel[1 - out] = !sel; } if (aps->aps_seqoff[sel] != 0) { if (softp->ips_proxy_debug & 0x10) printf("sel %d seqoff %d seq1 %x seqmin %x\n", sel, aps->aps_seqoff[sel], seq1, aps->aps_seqmin[sel]); if (seq1 > aps->aps_seqmin[sel]) { seq2 = aps->aps_seqoff[sel]; tcp->th_ack = htonl(seq1 - seq2); ch = 1; } } } if (softp->ips_proxy_debug & 0x10) printf("ipf_proxy_fixseqack: seq %u ack %u\n", (u_32_t)ntohl(tcp->th_seq), (u_32_t)ntohl(tcp->th_ack)); return ch ? 2 : 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_rule_rev */ /* Returns: ipnat_t * - NULL = failure, else pointer to new rule */ /* Parameters: nat(I) - pointer to NAT session to create rule from */ /* */ /* This function creates a NAT rule that is based upon the reverse packet */ /* flow associated with this NAT session. Thus if this NAT session was */ /* created with a map rule then this function will create a rdr rule. */ /* Only address fields and network interfaces are assigned in this function */ /* and the address fields are formed such that an exact is required. If the */ /* original rule had a netmask, that is not replicated here not is it */ /* desired. The ultimate goal here is to create a NAT rule to support a NAT */ /* session being created that does not have a user configured rule. The */ /* classic example is supporting the FTP proxy, where a data channel needs */ /* to be setup, based on the addresses used for the control connection. In */ /* that case, this function is used to handle creating NAT rules to support */ /* data connections with the PORT and EPRT commands. */ /* ------------------------------------------------------------------------ */ ipnat_t * ipf_proxy_rule_rev(nat) nat_t *nat; { ipnat_t *old; ipnat_t *ipn; int size; old = nat->nat_ptr; size = old->in_size; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) return NULL; bzero((char *)ipn, size); ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_apr = NULL; ipn->in_size = size; ipn->in_pr[0] = old->in_pr[1]; ipn->in_pr[1] = old->in_pr[0]; ipn->in_v[0] = old->in_v[1]; ipn->in_v[1] = old->in_v[0]; ipn->in_ifps[0] = old->in_ifps[1]; ipn->in_ifps[1] = old->in_ifps[0]; ipn->in_flags = (old->in_flags | IPN_PROXYRULE); ipn->in_nsrcip6 = nat->nat_odst6; ipn->in_osrcip6 = nat->nat_ndst6; if ((old->in_redir & NAT_REDIRECT) != 0) { ipn->in_redir = NAT_MAP; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_nsrcaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_nsrc6; #endif } ipn->in_ndstip6 = nat->nat_nsrc6; ipn->in_odstip6 = nat->nat_osrc6; } else { ipn->in_redir = NAT_REDIRECT; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_osrcaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_osrc6; #endif } ipn->in_ndstip6 = nat->nat_osrc6; ipn->in_odstip6 = nat->nat_nsrc6; } IP6_SETONES(&ipn->in_osrcmsk6); IP6_SETONES(&ipn->in_nsrcmsk6); IP6_SETONES(&ipn->in_odstmsk6); IP6_SETONES(&ipn->in_ndstmsk6); ipn->in_namelen = old->in_namelen; ipn->in_ifnames[0] = old->in_ifnames[1]; ipn->in_ifnames[1] = old->in_ifnames[0]; bcopy(old->in_names, ipn->in_names, ipn->in_namelen); MUTEX_INIT(&ipn->in_lock, "ipnat rev rule lock"); return ipn; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_rule_fwd */ /* Returns: ipnat_t * - NULL = failure, else pointer to new rule */ /* Parameters: nat(I) - pointer to NAT session to create rule from */ /* */ /* The purpose and rationale of this function is much the same as the above */ /* function, ipf_proxy_rule_rev, except that a rule is created that matches */ /* the same direction as that of the existing NAT session. Thus if this NAT */ /* session was created with a map rule then this function will also create */ /* a data structure to represent a map rule. Whereas ipf_proxy_rule_rev is */ /* used to support PORT/EPRT, this function supports PASV/EPSV. */ /* ------------------------------------------------------------------------ */ ipnat_t * ipf_proxy_rule_fwd(nat) nat_t *nat; { ipnat_t *old; ipnat_t *ipn; int size; old = nat->nat_ptr; size = old->in_size; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) return NULL; bzero((char *)ipn, size); ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_apr = NULL; ipn->in_size = size; ipn->in_pr[0] = old->in_pr[0]; ipn->in_pr[1] = old->in_pr[1]; ipn->in_v[0] = old->in_v[0]; ipn->in_v[1] = old->in_v[1]; ipn->in_ifps[0] = nat->nat_ifps[0]; ipn->in_ifps[1] = nat->nat_ifps[1]; ipn->in_flags = (old->in_flags | IPN_PROXYRULE); ipn->in_nsrcip6 = nat->nat_nsrc6; ipn->in_osrcip6 = nat->nat_osrc6; ipn->in_ndstip6 = nat->nat_ndst6; ipn->in_odstip6 = nat->nat_odst6; ipn->in_redir = old->in_redir; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_dnip = ntohl(nat->nat_ndstaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_nsrc6; ipn->in_dnip6 = nat->nat_ndst6; #endif } IP6_SETONES(&ipn->in_osrcmsk6); IP6_SETONES(&ipn->in_nsrcmsk6); IP6_SETONES(&ipn->in_odstmsk6); IP6_SETONES(&ipn->in_ndstmsk6); ipn->in_namelen = old->in_namelen; ipn->in_ifnames[0] = old->in_ifnames[0]; ipn->in_ifnames[1] = old->in_ifnames[1]; bcopy(old->in_names, ipn->in_names, ipn->in_namelen); MUTEX_INIT(&ipn->in_lock, "ipnat fwd rule lock"); return ipn; } diff --git a/sys/contrib/ipfilter/netinet/ip_proxy.h b/sys/contrib/ipfilter/netinet/ip_proxy.h index 3f4fc2a8af6f..0f67b5e2cca5 100644 --- a/sys/contrib/ipfilter/netinet/ip_proxy.h +++ b/sys/contrib/ipfilter/netinet/ip_proxy.h @@ -1,463 +1,463 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $FreeBSD$ * Id: ip_proxy.h,v 2.31.2.2 2005/03/12 19:33:48 darrenr Exp */ #ifndef __IP_PROXY_H__ #define __IP_PROXY_H__ #ifndef SOLARIS # if defined(sun) && defined(__SVR4) # define SOLARIS 1 # else # define SOLARIS 0 # endif #endif #define SIOCPROXY _IOWR('r', 64, struct ap_control) #ifndef APR_LABELLEN #define APR_LABELLEN 16 #endif #define AP_SESS_SIZE 53 struct nat; struct ipnat; struct ipstate; typedef struct ap_tcp { u_short apt_sport; /* source port */ u_short apt_dport; /* destination port */ short apt_sel[2]; /* {seq,ack}{off,min} set selector */ short apt_seqoff[2]; /* sequence # difference */ u_32_t apt_seqmin[2]; /* don't change seq-off until after this */ short apt_ackoff[2]; /* sequence # difference */ u_32_t apt_ackmin[2]; /* don't change seq-off until after this */ u_char apt_state[2]; /* connection state */ } ap_tcp_t; typedef struct ap_udp { u_short apu_sport; /* source port */ u_short apu_dport; /* destination port */ } ap_udp_t; typedef struct ap_session { struct aproxy *aps_apr; union { struct ap_tcp apu_tcp; struct ap_udp apu_udp; } aps_un; U_QUAD_T aps_bytes; /* bytes sent */ U_QUAD_T aps_pkts; /* packets sent */ void *aps_nat; /* pointer back to nat struct */ void *aps_data; /* private data */ int aps_psiz; /* size of private data */ struct ap_session *aps_next; } ap_session_t; #define aps_sport aps_un.apu_tcp.apt_sport #define aps_dport aps_un.apu_tcp.apt_dport #define aps_sel aps_un.apu_tcp.apt_sel #define aps_seqoff aps_un.apu_tcp.apt_seqoff #define aps_seqmin aps_un.apu_tcp.apt_seqmin #define aps_state aps_un.apu_tcp.apt_state #define aps_ackoff aps_un.apu_tcp.apt_ackoff #define aps_ackmin aps_un.apu_tcp.apt_ackmin typedef struct ap_control { char apc_label[APR_LABELLEN]; char apc_config[APR_LABELLEN]; u_char apc_p; /* * The following fields are upto the proxy's apr_ctl routine to deal * with. When the proxy gets this in kernel space, apc_data will * point to a malloc'd region of memory of apc_dsize bytes. If the * proxy wants to keep that memory, it must set apc_data to NULL * before it returns. It is expected if this happens that it will * take care to free it in apr_fini or otherwise as appropriate. * apc_cmd is provided as a standard place to put simple commands, * with apc_arg being available to put a simple arg. */ u_long apc_cmd; u_long apc_arg; void *apc_data; size_t apc_dsize; } ap_ctl_t; #define APC_CMD_ADD 0 #define APC_CMD_DEL 1 typedef struct aproxy { struct aproxy *apr_next; struct aproxy *apr_parent; char apr_label[APR_LABELLEN]; /* Proxy label # */ u_char apr_p; /* protocol */ int apr_flags; int apr_ref; int apr_clones; - void (* apr_load) __P((void)); - void (* apr_unload) __P((void)); - void *(* apr_create) __P((ipf_main_softc_t *)); - void (* apr_destroy) __P((ipf_main_softc_t *, void *)); - int (* apr_init) __P((ipf_main_softc_t *, void *)); - void (* apr_fini) __P((ipf_main_softc_t *, void *)); - int (* apr_new) __P((void *, fr_info_t *, ap_session_t *, - struct nat *)); - void (* apr_del) __P((ipf_main_softc_t *, ap_session_t *)); - int (* apr_inpkt) __P((void *, fr_info_t *, ap_session_t *, - struct nat *)); - int (* apr_outpkt) __P((void *, fr_info_t *, ap_session_t *, - struct nat *)); - int (* apr_match) __P((fr_info_t *, ap_session_t *, struct nat *)); - int (* apr_ctl) __P((ipf_main_softc_t *, void *, ap_ctl_t *)); - int (* apr_clear) __P((struct aproxy *)); - int (* apr_flush) __P((struct aproxy *, int)); + void (* apr_load)(void); + void (* apr_unload)(void); + void *(* apr_create)(ipf_main_softc_t *); + void (* apr_destroy)(ipf_main_softc_t *, void *); + int (* apr_init)(ipf_main_softc_t *, void *); + void (* apr_fini)(ipf_main_softc_t *, void *); + int (* apr_new)(void *, fr_info_t *, ap_session_t *, + struct nat *); + void (* apr_del)(ipf_main_softc_t *, ap_session_t *); + int (* apr_inpkt)(void *, fr_info_t *, ap_session_t *, + struct nat *); + int (* apr_outpkt)(void *, fr_info_t *, ap_session_t *, + struct nat *); + int (* apr_match)(fr_info_t *, ap_session_t *, struct nat *); + int (* apr_ctl)(ipf_main_softc_t *, void *, ap_ctl_t *); + int (* apr_clear)(struct aproxy *); + int (* apr_flush)(struct aproxy *, int); void *apr_soft; } aproxy_t; #define APR_DELETE 1 #define APR_ERR(x) ((x) << 16) #define APR_EXIT(x) (((x) >> 16) & 0xffff) #define APR_INC(x) ((x) & 0xffff) #ifdef _KERNEL /* * Generic #define's to cover missing things in the kernel */ # ifndef isdigit # define isdigit(x) ((x) >= '0' && (x) <= '9') # endif # ifndef isupper # define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z')) # endif # ifndef islower # define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z')) # endif # ifndef isalpha # define isalpha(x) (isupper(x) || islower(x)) # endif # ifndef toupper # define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A') # endif # ifndef isspace # define isspace(x) (((x) == ' ') || ((x) == '\r') || ((x) == '\n') || \ ((x) == '\t') || ((x) == '\b')) # endif #endif /* _KERNEL */ /* * For the ftp proxy. */ #define FTP_BUFSZ 160 #define IPF_FTPBUFSZ 160 typedef struct ftpside { char *ftps_rptr; char *ftps_wptr; void *ftps_ifp; u_32_t ftps_seq[2]; u_32_t ftps_len; int ftps_junk; int ftps_cmds; int ftps_cmd; char ftps_buf[FTP_BUFSZ]; } ftpside_t; typedef struct ftpinfo { int ftp_passok; int ftp_incok; void *ftp_pendstate; nat_t *ftp_pendnat; ftpside_t ftp_side[2]; } ftpinfo_t; /* * IPsec proxy */ typedef u_32_t ipsec_cookie_t[2]; typedef struct ipsec_pxy { ipsec_cookie_t ipsc_icookie; ipsec_cookie_t ipsc_rcookie; int ipsc_rckset; nat_t *ipsc_nat; struct ipstate *ipsc_state; ipnat_t *ipsc_rule; } ipsec_pxy_t; /* * For the irc proxy. */ typedef struct ircinfo { size_t irc_len; char *irc_snick; char *irc_dnick; char *irc_type; char *irc_arg; char *irc_addr; u_32_t irc_ipnum; u_short irc_port; } ircinfo_t; /* * For the DNS "proxy" */ typedef struct dnsinfo { ipfmutex_t dnsi_lock; u_short dnsi_id; char dnsi_buffer[512]; } dnsinfo_t; /* * Real audio proxy structure and #defines */ typedef struct raudio_s { int rap_seenpna; int rap_seenver; int rap_version; int rap_eos; /* End Of Startup */ int rap_gotid; int rap_gotlen; int rap_mode; int rap_sdone; u_short rap_plport; u_short rap_prport; u_short rap_srport; char rap_svr[19]; u_32_t rap_sbf; /* flag to indicate which of the 19 bytes have * been filled */ u_32_t rap_sseq; } raudio_t; #define RA_ID_END 0 #define RA_ID_UDP 1 #define RA_ID_ROBUST 7 #define RAP_M_UDP 1 #define RAP_M_ROBUST 2 #define RAP_M_TCP 4 #define RAP_M_UDP_ROBUST (RAP_M_UDP|RAP_M_ROBUST) /* * MSN RPC proxy */ typedef struct msnrpcinfo { u_int mri_flags; int mri_cmd[2]; u_int mri_valid; struct in_addr mri_raddr; u_short mri_rport; } msnrpcinfo_t; /* * Sun RPCBIND proxy */ #define RPCB_MAXMSG 888 #define RPCB_RES_PMAP 0 /* Response contains a v2 port. */ #define RPCB_RES_STRING 1 /* " " " v3 (GETADDR) string. */ #define RPCB_RES_LIST 2 /* " " " v4 (GETADDRLIST) list. */ #define RPCB_MAXREQS 32 /* Arbitrary limit on tracked transactions */ #define RPCB_REQMIN 40 #define RPCB_REQMAX 888 #define RPCB_REPMIN 20 #define RPCB_REPMAX 604 /* XXX double check this! */ /* * These macros determine the number of bytes between p and the end of * r->rs_buf relative to l. */ #define RPCB_BUF_END(r) (char *)((r)->rm_msgbuf + (r)->rm_buflen) #define RPCB_BUF_GEQ(r, p, l) \ ((RPCB_BUF_END((r)) > (char *)(p)) && \ ((RPCB_BUF_END((r)) - (char *)(p)) >= (l))) #define RPCB_BUF_EQ(r, p, l) \ (RPCB_BUF_END((r)) == ((char *)(p) + (l))) /* * The following correspond to RPC(B) detailed in RFC183[13]. */ #define RPCB_CALL 0 #define RPCB_REPLY 1 #define RPCB_MSG_VERSION 2 #define RPCB_PROG 100000 #define RPCB_GETPORT 3 #define RPCB_GETADDR 3 #define RPCB_GETADDRLIST 11 #define RPCB_MSG_ACCEPTED 0 #define RPCB_MSG_DENIED 1 /* BEGIN (Generic XDR structures) */ typedef struct xdr_string { u_32_t *xs_len; char *xs_str; } xdr_string_t; typedef struct xdr_auth { /* u_32_t xa_flavor; */ xdr_string_t xa_string; } xdr_auth_t; typedef struct xdr_uaddr { u_32_t xu_ip; u_short xu_port; xdr_string_t xu_str; } xdr_uaddr_t; typedef struct xdr_proto { u_int xp_proto; xdr_string_t xp_str; } xdr_proto_t; #define xu_xslen xu_str.xs_len #define xu_xsstr xu_str.xs_str #define xp_xslen xp_str.xs_len #define xp_xsstr xp_str.xs_str /* END (Generic XDR structures) */ /* BEGIN (RPC call structures) */ typedef struct pmap_args { /* u_32_t pa_prog; */ /* u_32_t pa_vers; */ u_32_t *pa_prot; /* u_32_t pa_port; */ } pmap_args_t; typedef struct rpcb_args { /* u_32_t *ra_prog; */ /* u_32_t *ra_vers; */ xdr_proto_t ra_netid; xdr_uaddr_t ra_maddr; /* xdr_string_t ra_owner; */ } rpcb_args_t; typedef struct rpc_call { /* u_32_t rc_rpcvers; */ /* u_32_t rc_prog; */ u_32_t *rc_vers; u_32_t *rc_proc; xdr_auth_t rc_authcred; xdr_auth_t rc_authverf; union { pmap_args_t ra_pmapargs; rpcb_args_t ra_rpcbargs; } rpcb_args; } rpc_call_t; #define rc_pmapargs rpcb_args.ra_pmapargs #define rc_rpcbargs rpcb_args.ra_rpcbargs /* END (RPC call structures) */ /* BEGIN (RPC reply structures) */ typedef struct rpcb_entry { xdr_uaddr_t re_maddr; xdr_proto_t re_netid; /* u_32_t re_semantics; */ xdr_string_t re_family; xdr_proto_t re_proto; u_32_t *re_more; /* 1 == another entry follows */ } rpcb_entry_t; typedef struct rpcb_listp { u_32_t *rl_list; /* 1 == list follows */ int rl_cnt; rpcb_entry_t rl_entries[2]; /* TCP / UDP only */ } rpcb_listp_t; typedef struct rpc_resp { /* u_32_t rr_acceptdeny; */ /* Omitted 'message denied' fork; we don't care about rejects. */ xdr_auth_t rr_authverf; /* u_32_t *rr_astat; */ union { u_32_t *resp_pmap; xdr_uaddr_t resp_getaddr; rpcb_listp_t resp_getaddrlist; } rpcb_reply; } rpc_resp_t; #define rr_v2 rpcb_reply.resp_pmap #define rr_v3 rpcb_reply.resp_getaddr #define rr_v4 rpcb_reply.resp_getaddrlist /* END (RPC reply structures) */ /* BEGIN (RPC message structure & macros) */ typedef struct rpc_msg { char rm_msgbuf[RPCB_MAXMSG]; /* RPCB data buffer */ u_int rm_buflen; u_32_t *rm_xid; /* u_32_t Call vs Reply */ union { rpc_call_t rb_call; rpc_resp_t rb_resp; } rm_body; } rpc_msg_t; #define rm_call rm_body.rb_call #define rm_resp rm_body.rb_resp /* END (RPC message structure & macros) */ /* * These code paths aren't hot enough to warrant per transaction * mutexes. */ typedef struct rpcb_xact { struct rpcb_xact *rx_next; struct rpcb_xact **rx_pnext; u_32_t rx_xid; /* RPC transmission ID */ u_int rx_type; /* RPCB response type */ u_int rx_ref; /* reference count */ u_int rx_proto; /* transport protocol (v2 only) */ } rpcb_xact_t; typedef struct rpcb_session { ipfmutex_t rs_rxlock; rpcb_xact_t *rs_rxlist; } rpcb_session_t; /* * For an explanation, please see the following: * RFC1832 - Sections 3.11, 4.4, and 4.5. */ #define XDRALIGN(x) ((((x) % 4) != 0) ? ((((x) + 3) / 4) * 4) : (x)) -extern int ipf_proxy_add __P((void *, aproxy_t *)); -extern int ipf_proxy_check __P((fr_info_t *, struct nat *)); -extern int ipf_proxy_ctl __P((ipf_main_softc_t *, void *, ap_ctl_t *)); -extern int ipf_proxy_del __P((aproxy_t *)); -extern void ipf_proxy_deref __P((aproxy_t *)); -extern void ipf_proxy_flush __P((void *, int)); -extern int ipf_proxy_init __P((void)); -extern int ipf_proxy_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, void *)); -extern aproxy_t *ipf_proxy_lookup __P((void *, u_int, char *)); -extern int ipf_proxy_match __P((fr_info_t *, struct nat *)); -extern int ipf_proxy_new __P((fr_info_t *, struct nat *)); -extern int ipf_proxy_ok __P((fr_info_t *, tcphdr_t *, struct ipnat *)); -extern void ipf_proxy_free __P((ipf_main_softc_t *, ap_session_t *)); -extern int ipf_proxy_main_load __P((void)); -extern int ipf_proxy_main_unload __P((void)); -extern ipnat_t *ipf_proxy_rule_fwd __P((nat_t *)); -extern ipnat_t *ipf_proxy_rule_rev __P((nat_t *)); -extern void *ipf_proxy_soft_create __P((ipf_main_softc_t *)); -extern void ipf_proxy_soft_destroy __P((ipf_main_softc_t *, void *)); -extern int ipf_proxy_soft_init __P((ipf_main_softc_t *, void *)); -extern int ipf_proxy_soft_fini __P((ipf_main_softc_t *, void *)); +extern int ipf_proxy_add(void *, aproxy_t *); +extern int ipf_proxy_check(fr_info_t *, struct nat *); +extern int ipf_proxy_ctl(ipf_main_softc_t *, void *, ap_ctl_t *); +extern int ipf_proxy_del(aproxy_t *); +extern void ipf_proxy_deref(aproxy_t *); +extern void ipf_proxy_flush(void *, int); +extern int ipf_proxy_init(void); +extern int ipf_proxy_ioctl(ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, void *); +extern aproxy_t *ipf_proxy_lookup(void *, u_int, char *); +extern int ipf_proxy_match(fr_info_t *, struct nat *); +extern int ipf_proxy_new(fr_info_t *, struct nat *); +extern int ipf_proxy_ok(fr_info_t *, tcphdr_t *, struct ipnat *); +extern void ipf_proxy_free(ipf_main_softc_t *, ap_session_t *); +extern int ipf_proxy_main_load(void); +extern int ipf_proxy_main_unload(void); +extern ipnat_t *ipf_proxy_rule_fwd(nat_t *); +extern ipnat_t *ipf_proxy_rule_rev(nat_t *); +extern void *ipf_proxy_soft_create(ipf_main_softc_t *); +extern void ipf_proxy_soft_destroy(ipf_main_softc_t *, void *); +extern int ipf_proxy_soft_init(ipf_main_softc_t *, void *); +extern int ipf_proxy_soft_fini(ipf_main_softc_t *, void *); #endif /* __IP_PROXY_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c b/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c index 62202ffc4488..0853b90d31e0 100644 --- a/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c @@ -1,345 +1,345 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_raudio_pxy.c,v 1.40.2.4 2006/07/14 06:12:17 darrenr Exp $ */ #define IPF_RAUDIO_PROXY -void ipf_p_raudio_main_load __P((void)); -void ipf_p_raudio_main_unload __P((void)); -int ipf_p_raudio_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_raudio_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_raudio_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_raudio_main_load(void); +void ipf_p_raudio_main_unload(void); +int ipf_p_raudio_new(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_raudio_in(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_raudio_out(void *, fr_info_t *, ap_session_t *, nat_t *); static frentry_t raudiofr; int raudio_proxy_init = 0; /* * Real Audio application proxy initialization. */ void ipf_p_raudio_main_load() { bzero((char *)&raudiofr, sizeof(raudiofr)); raudiofr.fr_ref = 1; raudiofr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&raudiofr.fr_lock, "Real Audio proxy rule lock"); raudio_proxy_init = 1; } void ipf_p_raudio_main_unload() { if (raudio_proxy_init == 1) { MUTEX_DESTROY(&raudiofr.fr_lock); raudio_proxy_init = 0; } } /* * Setup for a new proxy to handle Real Audio. */ int ipf_p_raudio_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { raudio_t *rap; nat = nat; /* LINT */ if (fin->fin_v != 4) return -1; KMALLOCS(aps->aps_data, void *, sizeof(raudio_t)); if (aps->aps_data == NULL) return -1; bzero(aps->aps_data, sizeof(raudio_t)); rap = aps->aps_data; aps->aps_psiz = sizeof(raudio_t); rap->rap_mode = RAP_M_TCP; /* default is for TCP */ return 0; } int ipf_p_raudio_out(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { raudio_t *rap = aps->aps_data; unsigned char membuf[512 + 1], *s; u_short id = 0; tcphdr_t *tcp; int off, dlen; int len = 0; mb_t *m; nat = nat; /* LINT */ /* * If we've already processed the start messages, then nothing left * for the proxy to do. */ if (rap->rap_eos == 1) return 0; m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)fin->fin_ip; off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; dlen = MSGDSIZE(m) - off; if (dlen <= 0) return 0; if (dlen > sizeof(membuf)) dlen = sizeof(membuf); bzero((char *)membuf, sizeof(membuf)); COPYDATA(m, off, dlen, (char *)membuf); /* * In all the startup parsing, ensure that we don't go outside * the packet buffer boundary. */ /* * Look for the start of connection "PNA" string if not seen yet. */ if (rap->rap_seenpna == 0) { s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); if (s == NULL) return 0; s += 3; rap->rap_seenpna = 1; } else s = membuf; /* * Directly after the PNA will be the version number of this * connection. */ if (rap->rap_seenpna == 1 && rap->rap_seenver == 0) { if ((s + 1) - membuf < dlen) { rap->rap_version = (*s << 8) | *(s + 1); s += 2; rap->rap_seenver = 1; } else return 0; } /* * Now that we've been past the PNA and version number, we're into the * startup messages block. This ends when a message with an ID of 0. */ while ((rap->rap_eos == 0) && ((s + 1) - membuf < dlen)) { if (rap->rap_gotid == 0) { id = (*s << 8) | *(s + 1); s += 2; rap->rap_gotid = 1; if (id == RA_ID_END) { rap->rap_eos = 1; break; } } else if (rap->rap_gotlen == 0) { len = (*s << 8) | *(s + 1); s += 2; rap->rap_gotlen = 1; } if (rap->rap_gotid == 1 && rap->rap_gotlen == 1) { if (id == RA_ID_UDP) { rap->rap_mode &= ~RAP_M_TCP; rap->rap_mode |= RAP_M_UDP; rap->rap_plport = (*s << 8) | *(s + 1); } else if (id == RA_ID_ROBUST) { rap->rap_mode |= RAP_M_ROBUST; rap->rap_prport = (*s << 8) | *(s + 1); } s += len; rap->rap_gotlen = 0; rap->rap_gotid = 0; } } return 0; } int ipf_p_raudio_in(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { unsigned char membuf[IPF_MAXPORTLEN + 1], *s; tcphdr_t *tcp, tcph, *tcp2 = &tcph; raudio_t *rap = aps->aps_data; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; struct in_addr swa, swb; int off, dlen, slen; int a1, a2, a3, a4; u_short sp, dp; fr_info_t fi; tcp_seq seq; nat_t *nat2; u_char swp; ip_t *ip; mb_t *m; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; /* * Wait until we've seen the end of the start messages and even then * only proceed further if we're using UDP. If they want to use TCP * then data is sent back on the same channel that is already open. */ if (rap->rap_sdone != 0) return 0; m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)fin->fin_ip; off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; dlen = MSGDSIZE(m) - off; if (dlen <= 0) return 0; if (dlen > sizeof(membuf)) dlen = sizeof(membuf); bzero((char *)membuf, sizeof(membuf)); COPYDATA(m, off, dlen, (char *)membuf); seq = ntohl(tcp->th_seq); /* * Check to see if the data in this packet is of interest to us. * We only care for the first 19 bytes coming back from the server. */ if (rap->rap_sseq == 0) { s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); if (s == NULL) return 0; a1 = s - membuf; dlen -= a1; a1 = 0; rap->rap_sseq = seq; a2 = MIN(dlen, sizeof(rap->rap_svr)); } else if (seq <= rap->rap_sseq + sizeof(rap->rap_svr)) { /* * seq # which is the start of data and from that the offset * into the buffer array. */ a1 = seq - rap->rap_sseq; a2 = MIN(dlen, sizeof(rap->rap_svr)); a2 -= a1; s = membuf; } else return 0; for (a3 = a1, a4 = a2; (a4 > 0) && (a3 < 19) && (a3 >= 0); a4--,a3++) { rap->rap_sbf |= (1 << a3); rap->rap_svr[a3] = *s++; } if ((rap->rap_sbf != 0x7ffff) || (!rap->rap_eos)) /* 19 bits */ return 0; rap->rap_sdone = 1; s = (u_char *)rap->rap_svr + 11; if (((*s << 8) | *(s + 1)) == RA_ID_ROBUST) { s += 2; rap->rap_srport = (*s << 8) | *(s + 1); } ip = fin->fin_ip; swp = ip->ip_p; swa = ip->ip_src; swb = ip->ip_dst; ip->ip_p = IPPROTO_UDP; ip->ip_src = nat->nat_ndstip; ip->ip_dst = nat->nat_odstip; bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); TCP_OFF_A(tcp2, 5); fi.fin_flx |= FI_IGNORE; fi.fin_dp = (char *)tcp2; fi.fin_fr = &raudiofr; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); tcp2->th_win = htons(8192); slen = ip->ip_len; ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && (rap->rap_srport != 0)) { dp = rap->rap_srport; sp = rap->rap_prport; tcp2->th_sport = htons(sp); tcp2->th_dport = htons(dp); fi.fin_data[0] = dp; fi.fin_data[1] = sp; fi.fin_out = 0; MUTEX_ENTER(&softn->ipf_nat_new); nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_UDP | (sp ? 0 : SI_W_SPORT), NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, IPN_UDP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); (void) ipf_state_add(softc, &fi, NULL, (sp ? 0 : SI_W_SPORT)); } } if ((rap->rap_mode & RAP_M_UDP) == RAP_M_UDP) { sp = rap->rap_plport; tcp2->th_sport = htons(sp); tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = sp; fi.fin_data[1] = 0; fi.fin_out = 1; MUTEX_ENTER(&softn->ipf_nat_new); nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_UDP|SI_W_DPORT, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, IPN_UDP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); } } ip->ip_p = swp; ip->ip_len = slen; ip->ip_src = swa; ip->ip_dst = swb; return 0; } diff --git a/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c b/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c index 29aec0044f3a..5dfdc040de4c 100644 --- a/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c @@ -1,352 +1,352 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ * * Simple RCMD transparent proxy for in-kernel use. For use with the NAT * code. * $FreeBSD$ */ #define IPF_RCMD_PROXY typedef struct rcmdinfo { u_32_t rcmd_port; /* Port number seen */ u_32_t rcmd_portseq; /* Sequence number where port is first seen */ ipnat_t *rcmd_rule; /* Template rule for back connection */ } rcmdinfo_t; -void ipf_p_rcmd_main_load __P((void)); -void ipf_p_rcmd_main_unload __P((void)); - -int ipf_p_rcmd_init __P((void)); -void ipf_p_rcmd_fini __P((void)); -void ipf_p_rcmd_del __P((ipf_main_softc_t *, ap_session_t *)); -int ipf_p_rcmd_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_rcmd_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_rcmd_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -u_short ipf_rcmd_atoi __P((char *)); -int ipf_p_rcmd_portmsg __P((fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_rcmd_main_load(void); +void ipf_p_rcmd_main_unload(void); + +int ipf_p_rcmd_init(void); +void ipf_p_rcmd_fini(void); +void ipf_p_rcmd_del(ipf_main_softc_t *, ap_session_t *); +int ipf_p_rcmd_new(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_rcmd_out(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_rcmd_in(void *, fr_info_t *, ap_session_t *, nat_t *); +u_short ipf_rcmd_atoi(char *); +int ipf_p_rcmd_portmsg(fr_info_t *, ap_session_t *, nat_t *); static frentry_t rcmdfr; static int rcmd_proxy_init = 0; /* * RCMD application proxy initialization. */ void ipf_p_rcmd_main_load() { bzero((char *)&rcmdfr, sizeof(rcmdfr)); rcmdfr.fr_ref = 1; rcmdfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&rcmdfr.fr_lock, "RCMD proxy rule lock"); rcmd_proxy_init = 1; } void ipf_p_rcmd_main_unload() { if (rcmd_proxy_init == 1) { MUTEX_DESTROY(&rcmdfr.fr_lock); rcmd_proxy_init = 0; } } /* * Setup for a new RCMD proxy. */ int ipf_p_rcmd_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; rcmdinfo_t *rc; ipnat_t *ipn; ipnat_t *np; int size; fin = fin; /* LINT */ np = nat->nat_ptr; size = np->in_size; KMALLOC(rc, rcmdinfo_t *); if (rc == NULL) { #ifdef IP_RCMD_PROXY_DEBUG printf("ipf_p_rcmd_new:KMALLOCS(%d) failed\n", sizeof(*rc)); #endif return -1; } aps->aps_sport = tcp->th_sport; aps->aps_dport = tcp->th_dport; ipn = ipf_proxy_rule_rev(nat); if (ipn == NULL) { KFREE(rc); return -1; } aps->aps_data = rc; aps->aps_psiz = sizeof(*rc); bzero((char *)rc, sizeof(*rc)); rc->rcmd_rule = ipn; return 0; } void ipf_p_rcmd_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { rcmdinfo_t *rci; rci = aps->aps_data; if (rci != NULL) { rci->rcmd_rule->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &rci->rcmd_rule); } } /* * ipf_rcmd_atoi - implement a simple version of atoi */ u_short ipf_rcmd_atoi(ptr) char *ptr; { register char *s = ptr, c; register u_short i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } return i; } int ipf_p_rcmd_portmsg(fin, aps, nat) fr_info_t *fin; ap_session_t *aps; nat_t *nat; { tcphdr_t *tcp, tcph, *tcp2 = &tcph; int off, dlen, nflags, direction; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; char portbuf[8], *s; rcmdinfo_t *rc; fr_info_t fi; u_short sp; nat_t *nat2; #ifdef USE_INET6 ip6_t *ip6; #endif int tcpsz; int slen = 0; /* silence gcc */ ip_t *ip; mb_t *m; tcp = (tcphdr_t *)fin->fin_dp; m = fin->fin_m; ip = fin->fin_ip; tcpsz = TCP_OFF(tcp) << 2; #ifdef USE_INET6 ip6 = (ip6_t *)fin->fin_ip; #endif softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; off = (char *)tcp - (char *)ip + tcpsz + fin->fin_ipoff; dlen = fin->fin_dlen - tcpsz; if (dlen <= 0) return 0; rc = (rcmdinfo_t *)aps->aps_data; if ((rc->rcmd_portseq != 0) && (tcp->th_seq != rc->rcmd_portseq)) return 0; bzero(portbuf, sizeof(portbuf)); COPYDATA(m, off, MIN(sizeof(portbuf), dlen), portbuf); portbuf[sizeof(portbuf) - 1] = '\0'; s = portbuf; sp = ipf_rcmd_atoi(s); if (sp == 0) { #ifdef IP_RCMD_PROXY_DEBUG printf("ipf_p_rcmd_portmsg:sp == 0 dlen %d [%s]\n", dlen, portbuf); #endif return 0; } if (rc->rcmd_port != 0 && sp != rc->rcmd_port) { #ifdef IP_RCMD_PROXY_DEBUG printf("ipf_p_rcmd_portmsg:sp(%d) != rcmd_port(%d)\n", sp, rc->rcmd_port); #endif return 0; } rc->rcmd_port = sp; rc->rcmd_portseq = tcp->th_seq; /* * Initialise the packet info structure so we can search the NAT * table to see if there already is soemthing present that matches * up with what we want to add. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = 0; fi.fin_data[1] = sp; fi.fin_src6 = nat->nat_ndst6; fi.fin_dst6 = nat->nat_nsrc6; if (nat->nat_v[0] == 6) { #ifdef USE_INET6 if (nat->nat_dir == NAT_OUTBOUND) { nat2 = ipf_nat6_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_pr[1], &nat->nat_osrc6.in6, &nat->nat_odst6.in6); } else { nat2 = ipf_nat6_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_pr[0], &nat->nat_osrc6.in6, &nat->nat_odst6.in6); } #else nat2 = (void *)-1; #endif } else { if (nat->nat_dir == NAT_OUTBOUND) { nat2 = ipf_nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_pr[1], nat->nat_osrcip, nat->nat_odstip); } else { nat2 = ipf_nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_pr[0], nat->nat_osrcip, nat->nat_odstip); } } if (nat2 != NULL) return APR_ERR(1); /* * Add skeleton NAT entry for connection which will come * back the other way. */ if (nat->nat_v[0] == 6) { #ifdef USE_INET6 slen = ip6->ip6_plen; ip6->ip6_plen = htons(sizeof(*tcp)); #endif } else { slen = ip->ip_len; ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); } /* * Fill out the fake TCP header with a few fields that ipfilter * considers to be important. */ bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); TCP_OFF_A(tcp2, 5); tcp2->th_flags = TH_SYN; fi.fin_dp = (char *)tcp2; fi.fin_fr = &rcmdfr; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; if (nat->nat_dir == NAT_OUTBOUND) { fi.fin_out = 0; direction = NAT_INBOUND; } else { fi.fin_out = 1; direction = NAT_OUTBOUND; } nflags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; MUTEX_ENTER(&softn->ipf_nat_new); if (fin->fin_v == 4) nat2 = ipf_nat_add(&fi, rc->rcmd_rule, NULL, nflags, direction); #ifdef USE_INET6 else nat2 = ipf_nat6_add(&fi, rc->rcmd_rule, NULL, nflags, direction); #endif MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, IPN_TCP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); fi.fin_ifp = NULL; if (nat2->nat_dir == NAT_INBOUND) fi.fin_dst6 = nat->nat_osrc6; (void) ipf_state_add(softc, &fi, NULL, SI_W_SPORT); } if (nat->nat_v[0] == 6) { #ifdef USE_INET6 ip6->ip6_plen = slen; #endif } else { ip->ip_len = slen; } if (nat2 == NULL) return APR_ERR(1); return 0; } int ipf_p_rcmd_out(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { if (nat->nat_dir == NAT_OUTBOUND) return ipf_p_rcmd_portmsg(fin, aps, nat); return 0; } int ipf_p_rcmd_in(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { if (nat->nat_dir == NAT_INBOUND) return ipf_p_rcmd_portmsg(fin, aps, nat); return 0; } diff --git a/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c b/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c index 727e731cd0e3..4c7996e59300 100644 --- a/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c @@ -1,1475 +1,1475 @@ /* * Copyright (C) 2002-2012 by Ryan Beasley * * See the IPFILTER.LICENCE file for details on licencing. */ /* * Overview: * This is an in-kernel application proxy for Sun's RPCBIND (nee portmap) * protocol as defined in RFC1833. It is far from complete, mostly * lacking in less-likely corner cases, but it's definitely functional. * * Invocation: * rdr /32 port -> port udp proxy rpcbu * * If the host running IP Filter is the same as the RPC server, it's * perfectly legal for both the internal and external addresses and ports * to match. * * When triggered by appropriate IP NAT rules, this proxy works by * examining data contained in received packets. Requests and replies are * modified, NAT and state table entries created, etc., as necessary. */ /* * TODO / NOTES * * o Must implement locking to protect proxy session data. * o Fragmentation isn't supported. * o Only supports UDP. * o Doesn't support multiple RPC records in a single request. * o Errors should be more fine-grained. (e.g., malloc failure vs. * illegal RPCB request / reply) * o Even with the limit on the total amount of recorded transactions, * should there be a timeout on transaction removal? * o There is a potential collision between cloning, wildcard NAT and * state entries. There should be an appr_getport routine for * to avoid this. * o The enclosed hack of STREAMS support is pretty sick and most likely * broken. * * $Id$ */ #define IPF_RPCB_PROXY /* * Function prototypes */ -void ipf_p_rpcb_main_load __P((void)); -void ipf_p_rpcb_main_unload __P((void)); -int ipf_p_rpcb_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -void ipf_p_rpcb_del __P((ipf_main_softc_t *, ap_session_t *)); -int ipf_p_rpcb_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_rpcb_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); - -static void ipf_p_rpcb_flush __P((rpcb_session_t *)); -static int ipf_p_rpcb_decodereq __P((fr_info_t *, nat_t *, - rpcb_session_t *, rpc_msg_t *)); -static int ipf_p_rpcb_skipauth __P((rpc_msg_t *, xdr_auth_t *, u_32_t **)); -static int ipf_p_rpcb_insert __P((rpcb_session_t *, rpcb_xact_t *)); -static int ipf_p_rpcb_xdrrpcb __P((rpc_msg_t *, u_32_t *, rpcb_args_t *)); -static int ipf_p_rpcb_getuaddr __P((rpc_msg_t *, xdr_uaddr_t *, - u_32_t **)); -static u_int ipf_p_rpcb_atoi __P((char *)); -static int ipf_p_rpcb_modreq __P((fr_info_t *, nat_t *, rpc_msg_t *, - mb_t *, u_int)); -static int ipf_p_rpcb_decoderep __P((fr_info_t *, nat_t *, - rpcb_session_t *, rpc_msg_t *, rpcb_xact_t **)); -static rpcb_xact_t * ipf_p_rpcb_lookup __P((rpcb_session_t *, u_32_t)); -static void ipf_p_rpcb_deref __P((rpcb_session_t *, rpcb_xact_t *)); -static int ipf_p_rpcb_getproto __P((rpc_msg_t *, xdr_proto_t *, - u_32_t **)); -static int ipf_p_rpcb_getnat __P((fr_info_t *, nat_t *, u_int, u_int)); -static int ipf_p_rpcb_modv3 __P((fr_info_t *, nat_t *, rpc_msg_t *, - mb_t *, u_int)); -static int ipf_p_rpcb_modv4 __P((fr_info_t *, nat_t *, rpc_msg_t *, - mb_t *, u_int)); -static void ipf_p_rpcb_fixlen __P((fr_info_t *, int)); +void ipf_p_rpcb_main_load(void); +void ipf_p_rpcb_main_unload(void); +int ipf_p_rpcb_new(void *, fr_info_t *, ap_session_t *, nat_t *); +void ipf_p_rpcb_del(ipf_main_softc_t *, ap_session_t *); +int ipf_p_rpcb_in(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_rpcb_out(void *, fr_info_t *, ap_session_t *, nat_t *); + +static void ipf_p_rpcb_flush(rpcb_session_t *); +static int ipf_p_rpcb_decodereq(fr_info_t *, nat_t *, + rpcb_session_t *, rpc_msg_t *); +static int ipf_p_rpcb_skipauth(rpc_msg_t *, xdr_auth_t *, u_32_t **); +static int ipf_p_rpcb_insert(rpcb_session_t *, rpcb_xact_t *); +static int ipf_p_rpcb_xdrrpcb(rpc_msg_t *, u_32_t *, rpcb_args_t *); +static int ipf_p_rpcb_getuaddr(rpc_msg_t *, xdr_uaddr_t *, + u_32_t **); +static u_int ipf_p_rpcb_atoi(char *); +static int ipf_p_rpcb_modreq(fr_info_t *, nat_t *, rpc_msg_t *, + mb_t *, u_int); +static int ipf_p_rpcb_decoderep(fr_info_t *, nat_t *, + rpcb_session_t *, rpc_msg_t *, rpcb_xact_t **); +static rpcb_xact_t * ipf_p_rpcb_lookup(rpcb_session_t *, u_32_t); +static void ipf_p_rpcb_deref(rpcb_session_t *, rpcb_xact_t *); +static int ipf_p_rpcb_getproto(rpc_msg_t *, xdr_proto_t *, + u_32_t **); +static int ipf_p_rpcb_getnat(fr_info_t *, nat_t *, u_int, u_int); +static int ipf_p_rpcb_modv3(fr_info_t *, nat_t *, rpc_msg_t *, + mb_t *, u_int); +static int ipf_p_rpcb_modv4(fr_info_t *, nat_t *, rpc_msg_t *, + mb_t *, u_int); +static void ipf_p_rpcb_fixlen(fr_info_t *, int); /* * Global variables */ static frentry_t rpcbfr; /* Skeleton rule for reference by entities this proxy creates. */ VNET_DEFINE_STATIC(int, rpcbcnt); #define V_rpcbcnt VNET(rpcbcnt) /* Upper bound of allocated RPCB sessions. */ /* XXX rpcbcnt still requires locking. */ static int rpcb_proxy_init = 0; /* * Since rpc_msg contains only pointers, one should use this macro as a * handy way to get to the goods. (In case you're wondering about the name, * this started as BYTEREF -> BREF -> B.) */ #define B(r) (u_32_t)ntohl(*(r)) /* * Public subroutines */ /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_main_load */ /* Returns: void */ /* Parameters: (void) */ /* */ /* Initialize the filter rule entry and session limiter. */ /* -------------------------------------------------------------------- */ void ipf_p_rpcb_main_load() { V_rpcbcnt = 0; bzero((char *)&rpcbfr, sizeof(rpcbfr)); rpcbfr.fr_ref = 1; rpcbfr.fr_flags = FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&rpcbfr.fr_lock, "ipf Sun RPCB proxy rule lock"); rpcb_proxy_init = 1; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_main_unload */ /* Returns: void */ /* Parameters: (void) */ /* */ /* Destroy rpcbfr's mutex to avoid a lock leak. */ /* -------------------------------------------------------------------- */ void ipf_p_rpcb_main_unload() { if (rpcb_proxy_init == 1) { MUTEX_DESTROY(&rpcbfr.fr_lock); rpcb_proxy_init = 0; } } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_new */ /* Returns: int - -1 == failure, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Allocate resources for per-session proxy structures. */ /* -------------------------------------------------------------------- */ int ipf_p_rpcb_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { rpcb_session_t *rs; nat = nat; /* LINT */ if (fin->fin_v != 4) return -1; KMALLOC(rs, rpcb_session_t *); if (rs == NULL) return(-1); bzero((char *)rs, sizeof(*rs)); MUTEX_INIT(&rs->rs_rxlock, "ipf Sun RPCB proxy session lock"); aps->aps_data = rs; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_del */ /* Returns: void */ /* Parameters: aps(I) - pointer to proxy session structure */ /* */ /* Free up a session's list of RPCB requests. */ /* -------------------------------------------------------------------- */ void ipf_p_rpcb_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { rpcb_session_t *rs; rs = (rpcb_session_t *)aps->aps_data; MUTEX_ENTER(&rs->rs_rxlock); ipf_p_rpcb_flush(rs); MUTEX_EXIT(&rs->rs_rxlock); MUTEX_DESTROY(&rs->rs_rxlock); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_in */ /* Returns: int - APR_ERR(1) == drop the packet, */ /* APR_ERR(2) == kill the proxy session, */ /* else change in packet length (in bytes) */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to packet header */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Given a presumed RPCB request, perform some minor tests and pass off */ /* for decoding. Also pass packet off for a rewrite if necessary. */ /* -------------------------------------------------------------------- */ int ipf_p_rpcb_in(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { rpc_msg_t rpcmsg, *rm; rpcb_session_t *rs; u_int off, dlen; mb_t *m; int rv; /* Disallow fragmented or illegally short packets. */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0) return(APR_ERR(1)); /* Perform basic variable initialization. */ rs = (rpcb_session_t *)aps->aps_data; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; off += sizeof(udphdr_t) + fin->fin_ipoff; dlen = fin->fin_dlen - sizeof(udphdr_t); /* Disallow packets outside legal range for supported requests. */ if ((dlen < RPCB_REQMIN) || (dlen > RPCB_REQMAX)) return(APR_ERR(1)); /* Copy packet over to convenience buffer. */ rm = &rpcmsg; bzero((char *)rm, sizeof(*rm)); COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf); rm->rm_buflen = dlen; /* Send off to decode request. */ rv = ipf_p_rpcb_decodereq(fin, nat, rs, rm); switch(rv) { case -1: return(APR_ERR(1)); /*NOTREACHED*/ break; case 0: break; case 1: rv = ipf_p_rpcb_modreq(fin, nat, rm, m, off); break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_req)", rv)); } return(rv); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_out */ /* Returns: int - APR_ERR(1) == drop the packet, */ /* APR_ERR(2) == kill the proxy session, */ /* else change in packet length (in bytes) */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to packet header */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Given a presumed RPCB reply, perform some minor tests and pass off */ /* for decoding. If the message indicates a successful request with */ /* valid addressing information, create NAT and state structures to */ /* allow direct communication between RPC client and server. */ /* -------------------------------------------------------------------- */ int ipf_p_rpcb_out(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { rpc_msg_t rpcmsg, *rm; rpcb_session_t *rs; rpcb_xact_t *rx; u_int off, dlen; int rv, diff; mb_t *m; /* Disallow fragmented or illegally short packets. */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0) return(APR_ERR(1)); /* Perform basic variable initialization. */ rs = (rpcb_session_t *)aps->aps_data; rx = NULL; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; off += sizeof(udphdr_t) + fin->fin_ipoff; dlen = fin->fin_dlen - sizeof(udphdr_t); diff = 0; /* Disallow packets outside legal range for supported requests. */ if ((dlen < RPCB_REPMIN) || (dlen > RPCB_REPMAX)) return(APR_ERR(1)); /* Copy packet over to convenience buffer. */ rm = &rpcmsg; bzero((char *)rm, sizeof(*rm)); COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf); rm->rm_buflen = dlen; rx = NULL; /* XXX gcc */ /* Send off to decode reply. */ rv = ipf_p_rpcb_decoderep(fin, nat, rs, rm, &rx); switch(rv) { case -1: /* Bad packet */ if (rx != NULL) { MUTEX_ENTER(&rs->rs_rxlock); ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } return(APR_ERR(1)); /*NOTREACHED*/ break; case 0: /* Negative reply / request rejected */ break; case 1: /* Positive reply */ /* * With the IP address embedded in a GETADDR(LIST) reply, * we'll need to rewrite the packet in the very possible * event that the internal & external addresses aren't the * same. (i.e., this box is either a router or rpcbind * only listens on loopback.) */ if (nat->nat_odstaddr != nat->nat_ndstaddr) { if (rx->rx_type == RPCB_RES_STRING) diff = ipf_p_rpcb_modv3(fin, nat, rm, m, off); else if (rx->rx_type == RPCB_RES_LIST) diff = ipf_p_rpcb_modv4(fin, nat, rm, m, off); } break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_decoderep)", rv)); } if (rx != NULL) { MUTEX_ENTER(&rs->rs_rxlock); /* XXX Gross hack - I'm overloading the reference * counter to deal with both threads and retransmitted * requests. One deref signals that this thread is * finished with rx, and the other signals that we've * processed its reply. */ ipf_p_rpcb_deref(rs, rx); ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } return(diff); } /* * Private support subroutines */ /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_flush */ /* Returns: void */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* */ /* Simply flushes the list of outstanding transactions, if any. */ /* -------------------------------------------------------------------- */ static void ipf_p_rpcb_flush(rs) rpcb_session_t *rs; { rpcb_xact_t *r1, *r2; r1 = rs->rs_rxlist; if (r1 == NULL) return; while (r1 != NULL) { r2 = r1; r1 = r1->rx_next; KFREE(r2); } } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_decodereq */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == request successfully decoded, */ /* 1 == request successfully decoded; requires */ /* address rewrite/modification */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session structure */ /* rs(I) - pointer to RPCB session structure */ /* rm(I) - pointer to RPC message structure */ /* */ /* Take a presumed RPCB request, decode it, and store the results in */ /* the transaction list. If the internal target address needs to be */ /* modified, store its location in ptr. */ /* WARNING: It's the responsibility of the caller to make sure there */ /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_decodereq(fin, nat, rs, rm) fr_info_t *fin; nat_t *nat; rpcb_session_t *rs; rpc_msg_t *rm; { rpcb_args_t *ra; u_32_t xdr, *p; rpc_call_t *rc; rpcb_xact_t rx; int mod; p = (u_32_t *)rm->rm_msgbuf; mod = 0; bzero((char *)&rx, sizeof(rx)); rc = &rm->rm_call; rm->rm_xid = p; rx.rx_xid = B(p++); /* Record this message's XID. */ /* Parse out and test the RPC header. */ if ((B(p++) != RPCB_CALL) || (B(p++) != RPCB_MSG_VERSION) || (B(p++) != RPCB_PROG)) return(-1); /* Record the RPCB version and procedure. */ rc->rc_vers = p++; rc->rc_proc = p++; /* Bypass RPC authentication stuff. */ if (ipf_p_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0) return(-1); if (ipf_p_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0) return(-1); /* Compare RPCB version and procedure numbers. */ switch(B(rc->rc_vers)) { case 2: /* This proxy only supports PMAP_GETPORT. */ if (B(rc->rc_proc) != RPCB_GETPORT) return(-1); /* Portmap requests contain four 4 byte parameters. */ if (RPCB_BUF_EQ(rm, p, 16) == 0) return(-1); p += 2; /* Skip requested program and version numbers. */ /* Sanity check the requested protocol. */ xdr = B(p); if (!(xdr == IPPROTO_UDP || xdr == IPPROTO_TCP)) return(-1); rx.rx_type = RPCB_RES_PMAP; rx.rx_proto = xdr; break; case 3: case 4: /* GETADDRLIST is exclusive to v4; GETADDR for v3 & v4 */ switch(B(rc->rc_proc)) { case RPCB_GETADDR: rx.rx_type = RPCB_RES_STRING; rx.rx_proto = (u_int)fin->fin_p; break; case RPCB_GETADDRLIST: if (B(rc->rc_vers) != 4) return(-1); rx.rx_type = RPCB_RES_LIST; break; default: return(-1); } ra = &rc->rc_rpcbargs; /* Decode the 'struct rpcb' request. */ if (ipf_p_rpcb_xdrrpcb(rm, p, ra) != 0) return(-1); /* Are the target address & port valid? */ if ((ra->ra_maddr.xu_ip != nat->nat_ndstaddr) || (ra->ra_maddr.xu_port != nat->nat_ndport)) return(-1); /* Do we need to rewrite this packet? */ if ((nat->nat_ndstaddr != nat->nat_odstaddr) || (nat->nat_ndport != nat->nat_odport)) mod = 1; break; default: return(-1); } MUTEX_ENTER(&rs->rs_rxlock); if (ipf_p_rpcb_insert(rs, &rx) != 0) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } MUTEX_EXIT(&rs->rs_rxlock); return(mod); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_skipauth */ /* Returns: int -- -1 == illegal auth parameters (lengths) */ /* 0 == valid parameters, pointer advanced */ /* Parameters: rm(I) - pointer to RPC message structure */ /* auth(I) - pointer to RPC auth structure */ /* buf(IO) - pointer to location within convenience buffer */ /* */ /* Record auth data length & location of auth data, then advance past */ /* it. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_skipauth(rm, auth, buf) rpc_msg_t *rm; xdr_auth_t *auth; u_32_t **buf; { u_32_t *p, xdr; p = *buf; /* Make sure we have enough space for expected fixed auth parms. */ if (RPCB_BUF_GEQ(rm, p, 8) == 0) return(-1); p++; /* We don't care about auth_flavor. */ auth->xa_string.xs_len = p; xdr = B(p++); /* Length of auth_data */ /* Test for absurdity / illegality of auth_data length. */ if ((XDRALIGN(xdr) < xdr) || (RPCB_BUF_GEQ(rm, p, XDRALIGN(xdr)) == 0)) return(-1); auth->xa_string.xs_str = (char *)p; p += XDRALIGN(xdr); /* Advance our location. */ *buf = (u_32_t *)p; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_insert */ /* Returns: int -- -1 == list insertion failed, */ /* 0 == item successfully added */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* rx(I) - pointer to RPCB transaction structure */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_insert(rs, rx) rpcb_session_t *rs; rpcb_xact_t *rx; { rpcb_xact_t *rxp; rxp = ipf_p_rpcb_lookup(rs, rx->rx_xid); if (rxp != NULL) { ++rxp->rx_ref; return(0); } if (V_rpcbcnt == RPCB_MAXREQS) return(-1); KMALLOC(rxp, rpcb_xact_t *); if (rxp == NULL) return(-1); bcopy((char *)rx, (char *)rxp, sizeof(*rx)); if (rs->rs_rxlist != NULL) rs->rs_rxlist->rx_pnext = &rxp->rx_next; rxp->rx_pnext = &rs->rs_rxlist; rxp->rx_next = rs->rs_rxlist; rs->rs_rxlist = rxp; rxp->rx_ref = 1; ++V_rpcbcnt; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_xdrrpcb */ /* Returns: int -- -1 == failure to properly decode the request */ /* 0 == rpcb successfully decoded */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* p(I) - pointer to location within session buffer */ /* rpcb(O) - pointer to rpcb (xdr type) structure */ /* */ /* Decode a XDR encoded rpcb structure and record its contents in rpcb */ /* within only the context of TCP/UDP over IP networks. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_xdrrpcb(rm, p, ra) rpc_msg_t *rm; u_32_t *p; rpcb_args_t *ra; { if (!RPCB_BUF_GEQ(rm, p, 20)) return(-1); /* Bypass target program & version. */ p += 2; /* Decode r_netid. Must be "tcp" or "udp". */ if (ipf_p_rpcb_getproto(rm, &ra->ra_netid, &p) != 0) return(-1); /* Decode r_maddr. */ if (ipf_p_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0) return(-1); /* Advance to r_owner and make sure it's empty. */ if (!RPCB_BUF_EQ(rm, p, 4) || (B(p) != 0)) return(-1); return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getuaddr */ /* Returns: int -- -1 == illegal string, */ /* 0 == string parsed; contents recorded */ /* Parameters: rm(I) - pointer to RPC message structure */ /* xu(I) - pointer to universal address structure */ /* p(IO) - pointer to location within message buffer */ /* */ /* Decode the IP address / port at p and record them in xu. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_getuaddr(rm, xu, p) rpc_msg_t *rm; xdr_uaddr_t *xu; u_32_t **p; { char *c, *i, *b, *pp; u_int d, dd, l, t; char uastr[24]; /* Test for string length. */ if (!RPCB_BUF_GEQ(rm, *p, 4)) return(-1); xu->xu_xslen = (*p)++; xu->xu_xsstr = (char *)*p; /* Length check */ l = B(xu->xu_xslen); if (l < 11 || l > 23 || !RPCB_BUF_GEQ(rm, *p, XDRALIGN(l))) return(-1); /* Advance p */ *(char **)p += XDRALIGN(l); /* Copy string to local buffer & terminate C style */ bcopy(xu->xu_xsstr, uastr, l); uastr[l] = '\0'; i = (char *)&xu->xu_ip; pp = (char *)&xu->xu_port; /* * Expected format: a.b.c.d.e.f where [a-d] correspond to bytes of * an IP address and [ef] are the bytes of a L4 port. */ if (!(ISDIGIT(uastr[0]) && ISDIGIT(uastr[l-1]))) return(-1); b = uastr; for (c = &uastr[1], d = 0, dd = 0; c < &uastr[l-1]; c++) { if (ISDIGIT(*c)) { dd = 0; continue; } if (*c == '.') { if (dd != 0) return(-1); /* Check for ASCII byte. */ *c = '\0'; t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); /* Aim b at beginning of the next byte. */ b = c + 1; /* Switch off IP addr vs port parsing. */ if (d < 4) i[d++] = t & 0xff; else pp[d++ - 4] = t & 0xff; dd = 1; continue; } return(-1); } if (d != 5) /* String must contain exactly 5 periods. */ return(-1); /* Handle the last byte (port low byte) */ t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); pp[d - 4] = t & 0xff; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_atoi (XXX should be generic for all proxies) */ /* Returns: int -- integer representation of supplied string */ /* Parameters: ptr(I) - input string */ /* */ /* Simple version of atoi(3) ripped from ip_rcmd_pxy.c. */ /* -------------------------------------------------------------------- */ static u_int ipf_p_rpcb_atoi(ptr) char *ptr; { register char *s = ptr, c; register u_int i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } return i; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modreq */ /* Returns: int -- change in datagram length */ /* APR_ERR(2) - critical failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - current offset within mbuf chain */ /* */ /* When external and internal addresses differ, we rewrite the former */ /* with the latter. (This is exclusive to protocol versions 3 & 4). */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_modreq(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; mb_t *m; u_int off; { u_int len, xlen, pos, bogo; rpcb_args_t *ra; char uaddr[24]; udphdr_t *udp; char *i, *p; int diff; ra = &rm->rm_call.rc_rpcbargs; i = (char *)&nat->nat_odstaddr; p = (char *)&nat->nat_odport; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(uaddr, sizeof(uaddr), #else (void) sprintf(uaddr, #endif "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Determine mbuf offset to start writing to. */ pos = (char *)ra->ra_maddr.xu_xslen - rm->rm_msgbuf; off += pos; /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); off += xlen; /* Write in zero r_owner. */ bogo = 0; COPYBACK(m, off, 4, (caddr_t)&bogo); /* Determine difference in data lengths. */ diff = xlen - XDRALIGN(B(ra->ra_maddr.xu_xslen)); /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) { udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + diff); fin->fin_plen += diff; fin->fin_ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += diff; /* XXX Storage lengths. */ } return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_decoderep */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == valid, negative reply */ /* 1 == vaddlid, positive reply; needs no changes */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session structure */ /* rs(I) - pointer to RPCB session structure */ /* rm(I) - pointer to RPC message structure */ /* rxp(O) - pointer to RPCB transaction structure */ /* */ /* Take a presumed RPCB reply, extract the XID, search for the original */ /* request information, and determine whether the request was accepted */ /* or rejected. With a valid accepted reply, go ahead and create NAT */ /* and state entries, and finish up by rewriting the packet as */ /* required. */ /* */ /* WARNING: It's the responsibility of the caller to make sure there */ /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_decoderep(fin, nat, rs, rm, rxp) fr_info_t *fin; nat_t *nat; rpcb_session_t *rs; rpc_msg_t *rm; rpcb_xact_t **rxp; { rpcb_listp_t *rl; rpcb_entry_t *re; rpcb_xact_t *rx; u_32_t xdr, *p; rpc_resp_t *rr; int rv, cnt; p = (u_32_t *)rm->rm_msgbuf; bzero((char *)&rx, sizeof(rx)); rr = &rm->rm_resp; rm->rm_xid = p; xdr = B(p++); /* Record this message's XID. */ /* Lookup XID */ MUTEX_ENTER(&rs->rs_rxlock); if ((rx = ipf_p_rpcb_lookup(rs, xdr)) == NULL) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } ++rx->rx_ref; /* per thread reference */ MUTEX_EXIT(&rs->rs_rxlock); *rxp = rx; /* Test call vs reply */ if (B(p++) != RPCB_REPLY) return(-1); /* Test reply_stat */ switch(B(p++)) { case RPCB_MSG_DENIED: return(0); case RPCB_MSG_ACCEPTED: break; default: return(-1); } /* Bypass RPC authentication stuff. */ if (ipf_p_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0) return(-1); /* Test accept status */ if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); if (B(p++) != 0) return(0); /* Parse out the expected reply */ switch(rx->rx_type) { case RPCB_RES_PMAP: /* There must be only one 4 byte argument. */ if (!RPCB_BUF_EQ(rm, p, 4)) return(-1); rr->rr_v2 = p; xdr = B(rr->rr_v2); /* Reply w/ a 0 port indicates service isn't registered */ if (xdr == 0) return(0); /* Is the value sane? */ if (xdr > 65535) return(-1); /* Create NAT & state table entries. */ if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0) return(-1); break; case RPCB_RES_STRING: /* Expecting a XDR string; need 4 bytes for length */ if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); rr->rr_v3.xu_str.xs_len = p++; rr->rr_v3.xu_str.xs_str = (char *)p; xdr = B(rr->rr_v3.xu_xslen); /* A null string indicates an unregistered service */ if ((xdr == 0) && RPCB_BUF_EQ(rm, p, 0)) return(0); /* Decode the target IP address / port. */ if (ipf_p_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0) return(-1); /* Validate the IP address and port contained. */ if (nat->nat_odstaddr != rr->rr_v3.xu_ip) return(-1); /* Create NAT & state table entries. */ if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)rr->rr_v3.xu_port) != 0) return(-1); break; case RPCB_RES_LIST: if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); /* rpcb_entry_list_ptr */ switch(B(p)) { case 0: return(0); /*NOTREACHED*/ break; case 1: break; default: return(-1); } rl = &rr->rr_v4; rl->rl_list = p++; cnt = 0; for(;;) { re = &rl->rl_entries[rl->rl_cnt]; if (ipf_p_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0) return(-1); if (ipf_p_rpcb_getproto(rm, &re->re_netid, &p) != 0) return(-1); /* re_semantics & re_pfamily length */ if (!RPCB_BUF_GEQ(rm, p, 12)) return(-1); p++; /* Skipping re_semantics. */ xdr = B(p++); if ((xdr != 4) || strncmp((char *)p, "inet", 4)) return(-1); p++; if (ipf_p_rpcb_getproto(rm, &re->re_proto, &p) != 0) return(-1); if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); re->re_more = p; if (B(re->re_more) > 1) /* 0,1 only legal values */ return(-1); ++rl->rl_cnt; ++cnt; if (B(re->re_more) == 0) break; /* Replies in max out at 2; TCP and/or UDP */ if (cnt > 2) return(-1); p++; } for(rl->rl_cnt = 0; rl->rl_cnt < cnt; rl->rl_cnt++) { re = &rl->rl_entries[rl->rl_cnt]; rv = ipf_p_rpcb_getnat(fin, nat, re->re_proto.xp_proto, (u_int)re->re_maddr.xu_port); if (rv != 0) return(-1); } break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rx_type %d", rx->rx_type)); } return(1); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_lookup */ /* Returns: rpcb_xact_t * - NULL == no matching record, */ /* else pointer to relevant entry */ /* Parameters: rs(I) - pointer to RPCB session */ /* xid(I) - XID to look for */ /* -------------------------------------------------------------------- */ static rpcb_xact_t * ipf_p_rpcb_lookup(rs, xid) rpcb_session_t *rs; u_32_t xid; { rpcb_xact_t *rx; if (rs->rs_rxlist == NULL) return(NULL); for (rx = rs->rs_rxlist; rx != NULL; rx = rx->rx_next) if (rx->rx_xid == xid) break; return(rx); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_deref */ /* Returns: (void) */ /* Parameters: rs(I) - pointer to RPCB session */ /* rx(I) - pointer to RPC transaction struct to remove */ /* force(I) - indicates to delete entry regardless of */ /* reference count */ /* Locking: rs->rs_rxlock must be held write only */ /* */ /* Free the RPCB transaction record rx from the chain of entries. */ /* -------------------------------------------------------------------- */ static void ipf_p_rpcb_deref(rs, rx) rpcb_session_t *rs; rpcb_xact_t *rx; { rs = rs; /* LINT */ if (rx == NULL) return; if (--rx->rx_ref != 0) return; if (rx->rx_next != NULL) rx->rx_next->rx_pnext = rx->rx_pnext; *rx->rx_pnext = rx->rx_next; KFREE(rx); --V_rpcbcnt; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getproto */ /* Returns: int - -1 == illegal protocol/netid, */ /* 0 == legal protocol/netid */ /* Parameters: rm(I) - pointer to RPC message structure */ /* xp(I) - pointer to netid structure */ /* p(IO) - pointer to location within packet buffer */ /* */ /* Decode netid/proto stored at p and record its numeric value. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_getproto(rm, xp, p) rpc_msg_t *rm; xdr_proto_t *xp; u_32_t **p; { u_int len; /* Must have 4 bytes for length & 4 bytes for "tcp" or "udp". */ if (!RPCB_BUF_GEQ(rm, p, 8)) return(-1); xp->xp_xslen = (*p)++; xp->xp_xsstr = (char *)*p; /* Test the string length. */ len = B(xp->xp_xslen); if (len != 3) return(-1); /* Test the actual string & record the protocol accordingly. */ if (!strncmp((char *)xp->xp_xsstr, "tcp\0", 4)) xp->xp_proto = IPPROTO_TCP; else if (!strncmp((char *)xp->xp_xsstr, "udp\0", 4)) xp->xp_proto = IPPROTO_UDP; else { return(-1); } /* Advance past the string. */ (*p)++; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getnat */ /* Returns: int -- -1 == failed to create table entries, */ /* 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT table entry */ /* proto(I) - transport protocol for new entries */ /* port(I) - new port to use w/ wildcard table entries */ /* */ /* Create state and NAT entries to handle an anticipated connection */ /* attempt between RPC client and server. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_getnat(fin, nat, proto, port) fr_info_t *fin; nat_t *nat; u_int proto; u_int port; { ipf_main_softc_t *softc = fin->fin_main_soft; ipnat_t *ipn, ipnat; tcphdr_t tcp; ipstate_t *is; fr_info_t fi; nat_t *natl; int nflags; ipn = nat->nat_ptr; /* Generate dummy fr_info */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_out = 0; fi.fin_p = proto; fi.fin_sport = 0; fi.fin_dport = port & 0xffff; fi.fin_flx |= FI_IGNORE; fi.fin_saddr = nat->nat_osrcaddr; fi.fin_daddr = nat->nat_odstaddr; bzero((char *)&tcp, sizeof(tcp)); tcp.th_dport = htons(port); if (proto == IPPROTO_TCP) { tcp.th_win = htons(8192); TCP_OFF_A(&tcp, sizeof(tcphdr_t) >> 2); fi.fin_dlen = sizeof(tcphdr_t); tcp.th_flags = TH_SYN; nflags = NAT_TCP; } else { fi.fin_dlen = sizeof(udphdr_t); nflags = NAT_UDP; } nflags |= SI_W_SPORT|NAT_SEARCH; fi.fin_dp = &tcp; fi.fin_plen = fi.fin_hlen + fi.fin_dlen; /* * Search for existing NAT & state entries. Pay close attention to * mutexes / locks grabbed from lookup routines, as not doing so could * lead to bad things. * * If successful, fr_stlookup returns with ipf_state locked. We have * no use for this lock, so simply unlock it if necessary. */ is = ipf_state_lookup(&fi, &tcp, NULL); if (is != NULL) { RWLOCK_EXIT(&softc->ipf_state); } RWLOCK_EXIT(&softc->ipf_nat); WRITE_ENTER(&softc->ipf_nat); natl = ipf_nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst); if ((natl != NULL) && (is != NULL)) { MUTEX_DOWNGRADE(&softc->ipf_nat); return(0); } /* Slightly modify the following structures for actual use in creating * NAT and/or state entries. We're primarily concerned with stripping * flags that may be detrimental to the creation process or simply * shouldn't be associated with a table entry. */ fi.fin_fr = &rpcbfr; fi.fin_flx &= ~FI_IGNORE; nflags &= ~NAT_SEARCH; if (natl == NULL) { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif /* XXX Since we're just copying the original ipn contents * back, would we be better off just sending a pointer to * the 'temp' copy off to nat_new instead? */ /* Generate template/bogus NAT rule. */ bcopy((char *)ipn, (char *)&ipnat, sizeof(ipnat)); ipn->in_flags = nflags & IPN_TCPUDP; ipn->in_apr = NULL; ipn->in_pr[0] = proto; ipn->in_pr[1] = proto; ipn->in_dpmin = fi.fin_dport; ipn->in_dpmax = fi.fin_dport; ipn->in_dpnext = fi.fin_dport; ipn->in_space = 1; ipn->in_ippip = 1; if (ipn->in_flags & IPN_FILTER) { ipn->in_scmp = 0; ipn->in_dcmp = 0; } ipn->in_plabel = -1; /* Create NAT entry. return NULL if this fails. */ MUTEX_ENTER(&softn->ipf_nat_new); natl = ipf_nat_add(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE, NAT_INBOUND); MUTEX_EXIT(&softn->ipf_nat_new); bcopy((char *)&ipnat, (char *)ipn, sizeof(ipnat)); if (natl == NULL) { MUTEX_DOWNGRADE(&softc->ipf_nat); return(-1); } natl->nat_ptr = ipn; fi.fin_saddr = natl->nat_nsrcaddr; fi.fin_daddr = natl->nat_ndstaddr; ipn->in_use++; (void) ipf_nat_proto(&fi, natl, nflags); MUTEX_ENTER(&natl->nat_lock); ipf_nat_update(&fi, natl); MUTEX_EXIT(&natl->nat_lock); } MUTEX_DOWNGRADE(&softc->ipf_nat); if (is == NULL) { /* Create state entry. Return NULL if this fails. */ fi.fin_flx |= FI_NATED; fi.fin_flx &= ~FI_STATE; nflags &= NAT_TCPUDP; nflags |= SI_W_SPORT|SI_CLONE; if (ipf_state_add(softc, &fi, NULL, nflags) != 0) { /* * XXX nat_delete is private to ip_nat.c. Should * check w/ Darren about this one. * * nat_delete(natl, NL_EXPIRE); */ return(-1); } } return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modv3 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - offset within mbuf chain */ /* */ /* Write a new universal address string to this packet, adjusting */ /* lengths as necessary. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_modv3(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; mb_t *m; u_int off; { u_int len, xlen, pos, bogo; rpc_resp_t *rr; char uaddr[24]; char *i, *p; int diff; rr = &rm->rm_resp; i = (char *)&nat->nat_ndstaddr; p = (char *)&rr->rr_v3.xu_port; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(uaddr, sizeof(uaddr), #else (void) sprintf(uaddr, #endif "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Determine mbuf offset to write to. */ pos = (char *)rr->rr_v3.xu_xslen - rm->rm_msgbuf; off += pos; /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); /* Determine difference in data lengths. */ diff = xlen - XDRALIGN(B(rr->rr_v3.xu_xslen)); /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modv4 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - offset within mbuf chain */ /* */ /* Write new rpcb_entry list, adjusting lengths as necessary. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_modv4(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; mb_t *m; u_int off; { u_int len, xlen, pos, bogo; rpcb_listp_t *rl; rpcb_entry_t *re; rpc_resp_t *rr; char uaddr[24]; int diff, cnt; char *i, *p; diff = 0; rr = &rm->rm_resp; rl = &rr->rr_v4; i = (char *)&nat->nat_ndstaddr; /* Determine mbuf offset to write to. */ re = &rl->rl_entries[0]; pos = (char *)re->re_maddr.xu_xslen - rm->rm_msgbuf; off += pos; for (cnt = 0; cnt < rl->rl_cnt; cnt++) { re = &rl->rl_entries[cnt]; p = (char *)&re->re_maddr.xu_port; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(uaddr, sizeof(uaddr), #else (void) sprintf(uaddr, #endif "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); off += xlen; /* Record any change in length. */ diff += xlen - XDRALIGN(B(re->re_maddr.xu_xslen)); /* If the length changed, copy back the rest of this entry. */ len = ((char *)re->re_more + 4) - (char *)re->re_netid.xp_xslen; if (diff != 0) { COPYBACK(m, off, len, (caddr_t)re->re_netid.xp_xslen); } off += len; } /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_fixlen */ /* Returns: (void) */ /* Parameters: fin(I) - pointer to packet information */ /* len(I) - change in packet length */ /* */ /* Adjust various packet related lengths held in structure and packet */ /* header fields. */ /* -------------------------------------------------------------------- */ static void ipf_p_rpcb_fixlen(fin, len) fr_info_t *fin; int len; { udphdr_t *udp; udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + len); fin->fin_plen += len; fin->fin_ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += len; } #undef B diff --git a/sys/contrib/ipfilter/netinet/ip_rules.h b/sys/contrib/ipfilter/netinet/ip_rules.h index 37e364613d50..73ba0cf72b8c 100644 --- a/sys/contrib/ipfilter/netinet/ip_rules.h +++ b/sys/contrib/ipfilter/netinet/ip_rules.h @@ -1,16 +1,16 @@ /* $FreeBSD$ */ -extern int ipfrule_add __P((void)); -extern int ipfrule_remove __P((void)); +extern int ipfrule_add(void); +extern int ipfrule_remove(void); -extern frentry_t *ipfrule_match_out_ __P((fr_info_t *, u_32_t *)); +extern frentry_t *ipfrule_match_out_(fr_info_t *, u_32_t *); extern frentry_t *ipf_rules_out_[1]; -extern int ipfrule_add_out_ __P((void)); -extern int ipfrule_remove_out_ __P((void)); +extern int ipfrule_add_out_(void); +extern int ipfrule_remove_out_(void); -extern frentry_t *ipfrule_match_in_ __P((fr_info_t *, u_32_t *)); +extern frentry_t *ipfrule_match_in_(fr_info_t *, u_32_t *); extern frentry_t *ipf_rules_in_[1]; -extern int ipfrule_add_in_ __P((void)); -extern int ipfrule_remove_in_ __P((void)); +extern int ipfrule_add_in_(void); +extern int ipfrule_remove_in_(void); diff --git a/sys/contrib/ipfilter/netinet/ip_scan.c b/sys/contrib/ipfilter/netinet/ip_scan.c index 34bc844eb354..fd3ad538915d 100644 --- a/sys/contrib/ipfilter/netinet/ip_scan.c +++ b/sys/contrib/ipfilter/netinet/ip_scan.c @@ -1,617 +1,617 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) # include # include # define _KERNEL # include # undef _KERNEL #else # include # if !defined(__SVR4) # include # endif #endif #include # include #ifdef __FreeBSD__ # include # include #else # include #endif #include #include #include #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_state.h" #include "netinet/ip_scan.h" /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id$"; #endif #ifdef IPFILTER_SCAN /* endif at bottom of file */ ipscan_t *ipf_scan_list = NULL, *ipf_scan_tail = NULL; ipscanstat_t ipf_scan_stat; # ifdef USE_MUTEXES ipfrwlock_t ipf_scan_rwlock; # endif # ifndef isalpha # define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \ ((x) >= 'a' && 'z' >= (x))) # endif -int ipf_scan_add __P((caddr_t)); -int ipf_scan_remove __P((caddr_t)); -struct ipscan *ipf_scan_lookup __P((char *)); -int ipf_scan_matchstr __P((sinfo_t *, char *, int)); -int ipf_scan_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *)); -int ipf_scan_match __P((ipstate_t *)); +int ipf_scan_add(caddr_t); +int ipf_scan_remove(caddr_t); +struct ipscan *ipf_scan_lookup(char *); +int ipf_scan_matchstr(sinfo_t *, char *, int); +int ipf_scan_matchisc(ipscan_t *, ipstate_t *, int, int, int *); +int ipf_scan_match(ipstate_t *); static int ipf_scan_inited = 0; int ipf_scan_init() { RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock"); ipf_scan_inited = 1; return 0; } void ipf_scan_unload(ipf_main_softc_t *arg) { if (ipf_scan_inited == 1) { RW_DESTROY(&ipf_scan_rwlock); ipf_scan_inited = 0; } } int ipf_scan_add(data) caddr_t data; { ipscan_t *i, *isc; int err; KMALLOC(isc, ipscan_t *); if (!isc) { ipf_interror = 90001; return ENOMEM; } err = copyinptr(data, isc, sizeof(*isc)); if (err) { KFREE(isc); return err; } WRITE_ENTER(&ipf_scan_rwlock); i = ipf_scan_lookup(isc->ipsc_tag); if (i != NULL) { RWLOCK_EXIT(&ipf_scan_rwlock); KFREE(isc); ipf_interror = 90002; return EEXIST; } if (ipf_scan_tail) { ipf_scan_tail->ipsc_next = isc; isc->ipsc_pnext = &ipf_scan_tail->ipsc_next; ipf_scan_tail = isc; } else { ipf_scan_list = isc; ipf_scan_tail = isc; isc->ipsc_pnext = &ipf_scan_list; } isc->ipsc_next = NULL; isc->ipsc_hits = 0; isc->ipsc_fref = 0; isc->ipsc_sref = 0; isc->ipsc_active = 0; ipf_scan_stat.iscs_entries++; RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } int ipf_scan_remove(data) caddr_t data; { ipscan_t isc, *i; int err; err = copyinptr(data, &isc, sizeof(isc)); if (err) return err; WRITE_ENTER(&ipf_scan_rwlock); i = ipf_scan_lookup(isc.ipsc_tag); if (i == NULL) err = ENOENT; else { if (i->ipsc_fref) { RWLOCK_EXIT(&ipf_scan_rwlock); ipf_interror = 90003; return EBUSY; } *i->ipsc_pnext = i->ipsc_next; if (i->ipsc_next) i->ipsc_next->ipsc_pnext = i->ipsc_pnext; else { if (i->ipsc_pnext == &ipf_scan_list) ipf_scan_tail = NULL; else ipf_scan_tail = *(*i->ipsc_pnext)->ipsc_pnext; } ipf_scan_stat.iscs_entries--; KFREE(i); } RWLOCK_EXIT(&ipf_scan_rwlock); return err; } struct ipscan * ipf_scan_lookup(tag) char *tag; { ipscan_t *i; for (i = ipf_scan_list; i; i = i->ipsc_next) if (!strcmp(i->ipsc_tag, tag)) return i; return NULL; } int ipf_scan_attachfr(fr) struct frentry *fr; { ipscan_t *i; if (fr->fr_isctag != -1) { READ_ENTER(&ipf_scan_rwlock); i = ipf_scan_lookup(fr->fr_isctag + fr->fr_names); if (i != NULL) { ATOMIC_INC32(i->ipsc_fref); } RWLOCK_EXIT(&ipf_scan_rwlock); if (i == NULL) { ipf_interror = 90004; return ENOENT; } fr->fr_isc = i; } return 0; } int ipf_scan_attachis(is) struct ipstate *is; { frentry_t *fr; ipscan_t *i; READ_ENTER(&ipf_scan_rwlock); fr = is->is_rule; if (fr != NULL) { i = fr->fr_isc; if ((i != NULL) && (i != (ipscan_t *)-1)) { is->is_isc = i; ATOMIC_INC32(i->ipsc_sref); if (i->ipsc_clen) is->is_flags |= IS_SC_CLIENT; else is->is_flags |= IS_SC_MATCHC; if (i->ipsc_slen) is->is_flags |= IS_SC_SERVER; else is->is_flags |= IS_SC_MATCHS; } } RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } int ipf_scan_detachfr(fr) struct frentry *fr; { ipscan_t *i; i = fr->fr_isc; if (i != NULL) { ATOMIC_DEC32(i->ipsc_fref); } return 0; } int ipf_scan_detachis(is) struct ipstate *is; { ipscan_t *i; READ_ENTER(&ipf_scan_rwlock); if ((i = is->is_isc) && (i != (ipscan_t *)-1)) { ATOMIC_DEC32(i->ipsc_sref); is->is_isc = NULL; is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER); } RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } /* * 'string' compare for scanning */ int ipf_scan_matchstr(sp, str, n) sinfo_t *sp; char *str; int n; { char *s, *t, *up; int i = n; if (i > sp->s_len) i = sp->s_len; up = str; for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++) switch ((int)*t) { case '.' : if (*s != *up) return 1; break; case '?' : if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f))) return 1; break; case '*' : break; } return 0; } /* * Returns 3 if both server and client match, 2 if just server, * 1 if just client */ int ipf_scan_matchisc(isc, is, cl, sl, maxm) ipscan_t *isc; ipstate_t *is; int cl, sl, maxm[2]; { int i, j, k, n, ret = 0, flags; flags = is->is_flags; /* * If we've already matched more than what is on offer, then * assume we have a better match already and forget this one. */ if (maxm != NULL) { if (isc->ipsc_clen < maxm[0]) return 0; if (isc->ipsc_slen < maxm[1]) return 0; j = maxm[0]; k = maxm[1]; } else { j = 0; k = 0; } if (!isc->ipsc_clen) ret = 1; else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) && cl && isc->ipsc_clen) { i = 0; n = MIN(cl, isc->ipsc_clen); if ((n > 0) && (!maxm || (n >= maxm[1]))) { if (!ipf_scan_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) { i++; ret |= 1; if (n > j) j = n; } } } if (!isc->ipsc_slen) ret |= 2; else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) && sl && isc->ipsc_slen) { i = 0; n = MIN(cl, isc->ipsc_slen); if ((n > 0) && (!maxm || (n >= maxm[1]))) { if (!ipf_scan_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) { i++; ret |= 2; if (n > k) k = n; } } } if (maxm && (ret == 3)) { maxm[0] = j; maxm[1] = k; } return ret; } int ipf_scan_match(is) ipstate_t *is; { int i, j, k, n, cl, sl, maxm[2]; ipscan_t *isc, *lm; tcpdata_t *t; for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1) cl++; for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1) sl++; j = 0; isc = is->is_isc; if (isc != NULL) { /* * Known object to scan for. */ i = ipf_scan_matchisc(isc, is, cl, sl, NULL); if (i & 1) { is->is_flags |= IS_SC_MATCHC; is->is_flags &= ~IS_SC_CLIENT; } else if (cl >= isc->ipsc_clen) is->is_flags &= ~IS_SC_CLIENT; if (i & 2) { is->is_flags |= IS_SC_MATCHS; is->is_flags &= ~IS_SC_SERVER; } else if (sl >= isc->ipsc_slen) is->is_flags &= ~IS_SC_SERVER; } else { i = 0; lm = NULL; maxm[0] = 0; maxm[1] = 0; for (k = 0, isc = ipf_scan_list; isc; isc = isc->ipsc_next) { i = ipf_scan_matchisc(isc, is, cl, sl, maxm); if (i) { /* * We only want to remember the best match * and the number of times we get a best * match. */ if ((j == 3) && (i < 3)) continue; if ((i == 3) && (j != 3)) k = 1; else k++; j = i; lm = isc; } } if (k == 1) isc = lm; if (isc == NULL) return 0; /* * No matches or partial matches, so reset the respective * search flag. */ if (!(j & 1)) is->is_flags &= ~IS_SC_CLIENT; if (!(j & 2)) is->is_flags &= ~IS_SC_SERVER; /* * If we found the best match, then set flags appropriately. */ if ((j == 3) && (k == 1)) { is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT); is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC); } } /* * If the acknowledged side of a connection has moved past the data in * which we are interested, then reset respective flag. */ t = &is->is_tcp.ts_data[0]; if (t->td_end > is->is_s0[0] + 15) is->is_flags &= ~IS_SC_CLIENT; t = &is->is_tcp.ts_data[1]; if (t->td_end > is->is_s0[1] + 15) is->is_flags &= ~IS_SC_SERVER; /* * Matching complete ? */ j = ISC_A_NONE; if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) { j = isc->ipsc_action; ipf_scan_stat.iscs_acted++; } else if ((is->is_isc != NULL) && ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) && !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) { /* * Matching failed... */ j = isc->ipsc_else; ipf_scan_stat.iscs_else++; } switch (j) { case ISC_A_CLOSE : /* * If as a result of a successful match we are to * close a connection, change the "keep state" info. * to block packets and generate TCP RST's. */ is->is_pass &= ~FR_RETICMP; is->is_pass |= FR_RETRST; break; default : break; } return i; } /* * check if a packet matches what we're scanning for */ int ipf_scan_packet(fin, is) fr_info_t *fin; ipstate_t *is; { int i, j, rv, dlen, off, thoff; u_32_t seq, s0; tcphdr_t *tcp; rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src); tcp = fin->fin_dp; seq = ntohl(tcp->th_seq); if (!is->is_s0[rv]) return 1; /* * check if this packet has more data that falls within the first * 16 bytes sent in either direction. */ s0 = is->is_s0[rv]; off = seq - s0; if ((off > 15) || (off < 0)) return 1; thoff = TCP_OFF(tcp) << 2; dlen = fin->fin_dlen - thoff; if (dlen <= 0) return 1; if (dlen > 16) dlen = 16; if (off + dlen > 16) dlen = 16 - off; j = 0xffff >> (16 - dlen); i = (0xffff & j) << off; #ifdef _KERNEL COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff, dlen, (caddr_t)is->is_sbuf[rv] + off); #endif is->is_smsk[rv] |= i; for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1) j++; if (j == 0) return 1; (void) ipf_scan_match(is); #if 0 /* * There is the potential here for plain text passwords to get * buffered and stored for some time... */ if (!(is->is_flags & IS_SC_CLIENT)) bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0])); if (!(is->is_flags & IS_SC_SERVER)) bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1])); #endif return 0; } int ipf_scan_ioctl(data, cmd, mode, uid, ctx) caddr_t data; ioctlcmd_t cmd; int mode, uid; void *ctx; { ipscanstat_t ipscs; int err = 0; switch (cmd) { case SIOCADSCA : err = ipf_scan_add(data); break; case SIOCRMSCA : err = ipf_scan_remove(data); break; case SIOCGSCST : bcopy((char *)&ipf_scan_stat, (char *)&ipscs, sizeof(ipscs)); ipscs.iscs_list = ipf_scan_list; err = BCOPYOUT(&ipscs, data, sizeof(ipscs)); if (err != 0) { ipf_interror = 90005; err = EFAULT; } break; default : err = EINVAL; break; } return err; } #endif /* IPFILTER_SCAN */ diff --git a/sys/contrib/ipfilter/netinet/ip_scan.h b/sys/contrib/ipfilter/netinet/ip_scan.h index f15aeb844ac2..2cd31fef6590 100644 --- a/sys/contrib/ipfilter/netinet/ip_scan.h +++ b/sys/contrib/ipfilter/netinet/ip_scan.h @@ -1,100 +1,100 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_fil.h 1.35 6/5/96 * $Id$ */ #ifndef __IP_SCAN_H__ #define __IP_SCAN_H__ 1 #ifdef sun # include #endif #define IPSCAN_NAME "/dev/ipscan" #define IPL_SCAN IPSCAN_NAME #define ISC_TLEN 16 struct fr_info; struct frentry; struct ip; struct ipstate; # define SIOCADSCA _IOWR('r', 60, struct ipscan *) # define SIOCRMSCA _IOWR('r', 61, struct ipscan *) # define SIOCGSCST _IOWR('r', 62, struct ipscan *) struct action { int act_val; /* what to do */ struct in_addr act_ip; /* redirect IP# */ u_short act_port; /* redirect port number */ int act_else; /* what to do */ struct in_addr act_eip; /* redirect IP# */ u_short act_eport; /* redirect port number */ }; typedef struct sinfo { char s_txt[ISC_TLEN]; /* text to match */ char s_msk[ISC_TLEN]; /* mask of the above to check */ int s_len; /* length of server text */ } sinfo_t; typedef struct ipscan { struct ipscan *ipsc_next; struct ipscan **ipsc_pnext; char ipsc_tag[ISC_TLEN]; /* table entry protocol tag */ sinfo_t ipsc_si[2]; /* client/server side information */ int ipsc_hits; /* times this has been matched */ int ipsc_active; /* # of active matches */ int ipsc_fref; /* # of references from filter rules */ int ipsc_sref; /* # of references from state entries */ struct action ipsc_act; } ipscan_t; #define ipsc_cl ipsc_si[0] #define ipsc_sl ipsc_si[1] #define ipsc_ctxt ipsc_cl.s_txt #define ipsc_cmsk ipsc_cl.s_msk #define ipsc_clen ipsc_cl.s_len #define ipsc_stxt ipsc_sl.s_txt #define ipsc_smsk ipsc_sl.s_msk #define ipsc_slen ipsc_sl.s_len #define ipsc_action ipsc_act.act_val #define ipsc_ip ipsc_act.act_ip #define ipsc_port ipsc_act.act_port #define ipsc_else ipsc_act.act_else #define ipsc_eip ipsc_act.act_eip #define ipsc_eport ipsc_act.act_eport #define ISC_A_NONE 0 #define ISC_A_TRACK 1 #define ISC_A_CLOSE 2 #define ISC_A_REDIRECT 3 typedef struct ipscanstat { struct ipscan *iscs_list; u_long iscs_acted; u_long iscs_else; int iscs_entries; } ipscanstat_t; -extern int ipf_scan_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *)); -extern int ipf_scan_init __P((void)); -extern int ipf_scan_attachis __P((struct ipstate *)); -extern int ipf_scan_attachfr __P((struct frentry *)); -extern int ipf_scan_detachis __P((struct ipstate *)); -extern int ipf_scan_detachfr __P((struct frentry *)); -extern int ipf_scan_packet __P((struct fr_info *, struct ipstate *)); -extern void ipf_scan_unload __P((ipf_main_softc_t *)); +extern int ipf_scan_ioctl(ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *); +extern int ipf_scan_init(void); +extern int ipf_scan_attachis(struct ipstate *); +extern int ipf_scan_attachfr(struct frentry *); +extern int ipf_scan_detachis(struct ipstate *); +extern int ipf_scan_detachfr(struct frentry *); +extern int ipf_scan_packet(struct fr_info *, struct ipstate *); +extern void ipf_scan_unload(ipf_main_softc_t *); #endif /* __IP_SCAN_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_state.c b/sys/contrib/ipfilter/netinet/ip_state.c index d723d50f1659..45a641fd86a2 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.c +++ b/sys/contrib/ipfilter/netinet/ip_state.c @@ -1,5394 +1,5394 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Copyright 2008 Sun Microsystems. * * $Id$ */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if defined(_KERNEL) && defined(__FreeBSD_version) && \ !defined(KLD_MODULE) #include "opt_inet6.h" #endif #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD_version) # include # include #else # include #endif #include # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #include #ifdef sun # include #endif #include #include #include #include # include #include #include #if !defined(_KERNEL) # include "ipf.h" #endif #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_lookup.h" #include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" #ifdef USE_INET6 #include #endif #ifdef __FreeBSD_version # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include # include # endif #endif /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id$"; #endif static ipftuneable_t ipf_state_tuneables[] = { { { (void *)offsetof(ipf_state_softc_t, ipf_state_max) }, "state_max", 1, 0x7fffffff, stsizeof(ipf_state_softc_t, ipf_state_max), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_size) }, "state_size", 1, 0x7fffffff, stsizeof(ipf_state_softc_t, ipf_state_size), 0, NULL, ipf_state_rehash }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_lock) }, "state_lock", 0, 1, stsizeof(ipf_state_softc_t, ipf_state_lock), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_maxbucket) }, "state_maxbucket", 1, 0x7fffffff, stsizeof(ipf_state_softc_t, ipf_state_maxbucket), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_logging) }, "state_logging",0, 1, stsizeof(ipf_state_softc_t, ipf_state_logging), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_high) }, "state_wm_high",2, 100, stsizeof(ipf_state_softc_t, ipf_state_wm_high), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_low) }, "state_wm_low", 1, 99, stsizeof(ipf_state_softc_t, ipf_state_wm_low), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_freq) }, "state_wm_freq",2, 999999, stsizeof(ipf_state_softc_t, ipf_state_wm_freq), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; #define SINCL(x) ATOMIC_INCL(softs->x) #define SBUMP(x) (softs->x)++ #define SBUMPD(x, y) do { (softs->x.y)++; DT(y); } while (0) #define SBUMPDX(x, y, z)do { (softs->x.y)++; DT(z); } while (0) #ifdef USE_INET6 -static ipstate_t *ipf_checkicmp6matchingstate __P((fr_info_t *)); +static ipstate_t *ipf_checkicmp6matchingstate(fr_info_t *); #endif -static int ipf_allowstateicmp __P((fr_info_t *, ipstate_t *, i6addr_t *)); -static ipstate_t *ipf_matchsrcdst __P((fr_info_t *, ipstate_t *, i6addr_t *, - i6addr_t *, tcphdr_t *, u_32_t)); -static ipstate_t *ipf_checkicmpmatchingstate __P((fr_info_t *)); -static int ipf_state_flush_entry __P((ipf_main_softc_t *, void *)); -static ips_stat_t *ipf_state_stats __P((ipf_main_softc_t *)); -static int ipf_state_del __P((ipf_main_softc_t *, ipstate_t *, int)); -static int ipf_state_remove __P((ipf_main_softc_t *, caddr_t)); -static int ipf_state_match __P((ipstate_t *is1, ipstate_t *is2)); -static int ipf_state_matchaddresses __P((ipstate_t *is1, ipstate_t *is2)); -static int ipf_state_matchipv4addrs __P((ipstate_t *is1, ipstate_t *is2)); -static int ipf_state_matchipv6addrs __P((ipstate_t *is1, ipstate_t *is2)); -static int ipf_state_matchisps __P((ipstate_t *is1, ipstate_t *is2)); -static int ipf_state_matchports __P((udpinfo_t *is1, udpinfo_t *is2)); -static int ipf_state_matcharray __P((ipstate_t *, int *, u_long)); -static void ipf_ipsmove __P((ipf_state_softc_t *, ipstate_t *, u_int)); -static int ipf_state_tcp __P((ipf_main_softc_t *, ipf_state_softc_t *, - fr_info_t *, tcphdr_t *, ipstate_t *)); -static int ipf_tcpoptions __P((ipf_state_softc_t *, fr_info_t *, - tcphdr_t *, tcpdata_t *)); -static ipstate_t *ipf_state_clone __P((fr_info_t *, tcphdr_t *, ipstate_t *)); -static void ipf_fixinisn __P((fr_info_t *, ipstate_t *)); -static void ipf_fixoutisn __P((fr_info_t *, ipstate_t *)); -static void ipf_checknewisn __P((fr_info_t *, ipstate_t *)); -static int ipf_state_iter __P((ipf_main_softc_t *, ipftoken_t *, - ipfgeniter_t *, ipfobj_t *)); -static int ipf_state_gettable __P((ipf_main_softc_t *, ipf_state_softc_t *, - char *)); -static int ipf_state_tcpinwindow __P((struct fr_info *, struct tcpdata *, - struct tcpdata *, tcphdr_t *, int)); - -static int ipf_state_getent __P((ipf_main_softc_t *, ipf_state_softc_t *, - caddr_t)); -static int ipf_state_putent __P((ipf_main_softc_t *, ipf_state_softc_t *, - caddr_t)); +static int ipf_allowstateicmp(fr_info_t *, ipstate_t *, i6addr_t *); +static ipstate_t *ipf_matchsrcdst(fr_info_t *, ipstate_t *, i6addr_t *, + i6addr_t *, tcphdr_t *, u_32_t); +static ipstate_t *ipf_checkicmpmatchingstate(fr_info_t *); +static int ipf_state_flush_entry(ipf_main_softc_t *, void *); +static ips_stat_t *ipf_state_stats(ipf_main_softc_t *); +static int ipf_state_del(ipf_main_softc_t *, ipstate_t *, int); +static int ipf_state_remove(ipf_main_softc_t *, caddr_t); +static int ipf_state_match(ipstate_t *is1, ipstate_t *is2); +static int ipf_state_matchaddresses(ipstate_t *is1, ipstate_t *is2); +static int ipf_state_matchipv4addrs(ipstate_t *is1, ipstate_t *is2); +static int ipf_state_matchipv6addrs(ipstate_t *is1, ipstate_t *is2); +static int ipf_state_matchisps(ipstate_t *is1, ipstate_t *is2); +static int ipf_state_matchports(udpinfo_t *is1, udpinfo_t *is2); +static int ipf_state_matcharray(ipstate_t *, int *, u_long); +static void ipf_ipsmove(ipf_state_softc_t *, ipstate_t *, u_int); +static int ipf_state_tcp(ipf_main_softc_t *, ipf_state_softc_t *, + fr_info_t *, tcphdr_t *, ipstate_t *); +static int ipf_tcpoptions(ipf_state_softc_t *, fr_info_t *, + tcphdr_t *, tcpdata_t *); +static ipstate_t *ipf_state_clone(fr_info_t *, tcphdr_t *, ipstate_t *); +static void ipf_fixinisn(fr_info_t *, ipstate_t *); +static void ipf_fixoutisn(fr_info_t *, ipstate_t *); +static void ipf_checknewisn(fr_info_t *, ipstate_t *); +static int ipf_state_iter(ipf_main_softc_t *, ipftoken_t *, + ipfgeniter_t *, ipfobj_t *); +static int ipf_state_gettable(ipf_main_softc_t *, ipf_state_softc_t *, + char *); +static int ipf_state_tcpinwindow(struct fr_info *, struct tcpdata *, + struct tcpdata *, tcphdr_t *, int); + +static int ipf_state_getent(ipf_main_softc_t *, ipf_state_softc_t *, + caddr_t); +static int ipf_state_putent(ipf_main_softc_t *, ipf_state_softc_t *, + caddr_t); #define ONE_DAY IPF_TTLVAL(1 * 86400) /* 1 day */ #define FIVE_DAYS (5 * ONE_DAY) #define DOUBLE_HASH(x) (((x) + softs->ipf_state_seed[(x) % \ softs->ipf_state_size]) % softs->ipf_state_size) /* ------------------------------------------------------------------------ */ /* Function: ipf_state_main_load */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_state_main_load() { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_main_unload */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_state_main_unload() { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_soft_create */ /* Returns: void * - NULL = failure, else pointer to soft context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Create a new state soft context structure and populate it with the list */ /* of tunables and other default settings. */ /* ------------------------------------------------------------------------ */ void * ipf_state_soft_create(softc) ipf_main_softc_t *softc; { ipf_state_softc_t *softs; KMALLOC(softs, ipf_state_softc_t *); if (softs == NULL) return NULL; bzero((char *)softs, sizeof(*softs)); softs->ipf_state_tune = ipf_tune_array_copy(softs, sizeof(ipf_state_tuneables), ipf_state_tuneables); if (softs->ipf_state_tune == NULL) { ipf_state_soft_destroy(softc, softs); return NULL; } if (ipf_tune_array_link(softc, softs->ipf_state_tune) == -1) { ipf_state_soft_destroy(softc, softs); return NULL; } #ifdef IPFILTER_LOG softs->ipf_state_logging = 1; #else softs->ipf_state_logging = 0; #endif softs->ipf_state_size = IPSTATE_SIZE, softs->ipf_state_maxbucket = 0; softs->ipf_state_wm_freq = IPF_TTLVAL(10); softs->ipf_state_max = IPSTATE_MAX; softs->ipf_state_wm_last = 0; softs->ipf_state_wm_high = 99; softs->ipf_state_wm_low = 90; softs->ipf_state_inited = 0; softs->ipf_state_lock = 0; softs->ipf_state_doflush = 0; return softs; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Undo only what we did in soft create: unlink and free the tunables and */ /* free the soft context structure itself. */ /* ------------------------------------------------------------------------ */ void ipf_state_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_state_softc_t *softs = arg; if (softs->ipf_state_tune != NULL) { ipf_tune_array_unlink(softc, softs->ipf_state_tune); KFREES(softs->ipf_state_tune, sizeof(ipf_state_tuneables)); softs->ipf_state_tune = NULL; } KFREE(softs); } static void * ipf_state_seed_alloc(u_int state_size, u_int state_max) { u_int i; u_long *state_seed; KMALLOCS(state_seed, u_long *, state_size * sizeof(*state_seed)); if (state_seed == NULL) return NULL; for (i = 0; i < state_size; i++) { /* * XXX - ipf_state_seed[X] should be a random number of sorts. */ #ifdef __FreeBSD_version state_seed[i] = arc4random(); #else state_seed[i] = ((u_long)state_seed + i) * state_size; state_seed[i] ^= 0xa5a55a5a; state_seed[i] *= (u_long)state_seed; state_seed[i] ^= 0x5a5aa5a5; state_seed[i] *= state_max; #endif } return state_seed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_soft_init */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the state soft context structure so it is ready for use. */ /* This involves: */ /* - allocating a hash table and zero'ing it out */ /* - building a secondary table of seeds for double hashing to make it more */ /* difficult to attempt to attack the hash table itself (for DoS) */ /* - initialise all of the timeout queues, including a table for TCP, some */ /* pairs of query/response for UDP and other IP protocols (typically the */ /* reply queue has a shorter timeout than the query) */ /* ------------------------------------------------------------------------ */ int ipf_state_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_state_softc_t *softs = arg; int i; KMALLOCS(softs->ipf_state_table, ipstate_t **, softs->ipf_state_size * sizeof(ipstate_t *)); if (softs->ipf_state_table == NULL) return -1; bzero((char *)softs->ipf_state_table, softs->ipf_state_size * sizeof(ipstate_t *)); softs->ipf_state_seed = ipf_state_seed_alloc(softs->ipf_state_size, softs->ipf_state_max); if (softs->ipf_state_seed == NULL) return -2; KMALLOCS(softs->ipf_state_stats.iss_bucketlen, u_int *, softs->ipf_state_size * sizeof(u_int)); if (softs->ipf_state_stats.iss_bucketlen == NULL) return -3; bzero((char *)softs->ipf_state_stats.iss_bucketlen, softs->ipf_state_size * sizeof(u_int)); if (softs->ipf_state_maxbucket == 0) { for (i = softs->ipf_state_size; i > 0; i >>= 1) softs->ipf_state_maxbucket++; softs->ipf_state_maxbucket *= 2; } ipf_sttab_init(softc, softs->ipf_state_tcptq); softs->ipf_state_stats.iss_tcptab = softs->ipf_state_tcptq; softs->ipf_state_tcptq[IPF_TCP_NSTATES - 1].ifq_next = &softs->ipf_state_udptq; IPFTQ_INIT(&softs->ipf_state_udptq, softc->ipf_udptimeout, "ipftq udp tab"); softs->ipf_state_udptq.ifq_next = &softs->ipf_state_udpacktq; IPFTQ_INIT(&softs->ipf_state_udpacktq, softc->ipf_udpacktimeout, "ipftq udpack tab"); softs->ipf_state_udpacktq.ifq_next = &softs->ipf_state_icmptq; IPFTQ_INIT(&softs->ipf_state_icmptq, softc->ipf_icmptimeout, "ipftq icmp tab"); softs->ipf_state_icmptq.ifq_next = &softs->ipf_state_icmpacktq; IPFTQ_INIT(&softs->ipf_state_icmpacktq, softc->ipf_icmpacktimeout, "ipftq icmpack tab"); softs->ipf_state_icmpacktq.ifq_next = &softs->ipf_state_iptq; IPFTQ_INIT(&softs->ipf_state_iptq, softc->ipf_iptimeout, "ipftq iptimeout tab"); softs->ipf_state_iptq.ifq_next = &softs->ipf_state_pending; IPFTQ_INIT(&softs->ipf_state_pending, IPF_HZ_DIVIDE, "ipftq pending"); softs->ipf_state_pending.ifq_next = &softs->ipf_state_deletetq; IPFTQ_INIT(&softs->ipf_state_deletetq, 1, "ipftq delete"); softs->ipf_state_deletetq.ifq_next = NULL; MUTEX_INIT(&softs->ipf_stinsert, "ipf state insert mutex"); softs->ipf_state_wm_last = softc->ipf_ticks; softs->ipf_state_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_soft_fini */ /* Returns: int - 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Release and destroy any resources acquired or initialised so that */ /* IPFilter can be unloaded or re-initialised. */ /* ------------------------------------------------------------------------ */ int ipf_state_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_state_softc_t *softs = arg; ipftq_t *ifq, *ifqnext; ipstate_t *is; while ((is = softs->ipf_state_list) != NULL) ipf_state_del(softc, is, ISL_UNLOAD); /* * Proxy timeout queues are not cleaned here because although they * exist on the state list, appr_unload is called after * ipf_state_unload and the proxies actually are responsible for them * being created. Should the proxy timeouts have their own list? * There's no real justification as this is the only complication. */ for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (ipf_deletetimeoutqueue(ifq) == 0) ipf_freetimeoutqueue(softc, ifq); } softs->ipf_state_stats.iss_inuse = 0; softs->ipf_state_stats.iss_active = 0; if (softs->ipf_state_inited == 1) { softs->ipf_state_inited = 0; ipf_sttab_destroy(softs->ipf_state_tcptq); MUTEX_DESTROY(&softs->ipf_state_udptq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_icmptq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_udpacktq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_icmpacktq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_iptq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_deletetq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_pending.ifq_lock); MUTEX_DESTROY(&softs->ipf_stinsert); } if (softs->ipf_state_table != NULL) { KFREES(softs->ipf_state_table, softs->ipf_state_size * sizeof(*softs->ipf_state_table)); softs->ipf_state_table = NULL; } if (softs->ipf_state_seed != NULL) { KFREES(softs->ipf_state_seed, softs->ipf_state_size * sizeof(*softs->ipf_state_seed)); softs->ipf_state_seed = NULL; } if (softs->ipf_state_stats.iss_bucketlen != NULL) { KFREES(softs->ipf_state_stats.iss_bucketlen, softs->ipf_state_size * sizeof(u_int)); softs->ipf_state_stats.iss_bucketlen = NULL; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_setlock */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to local context to use */ /* tmp(I) - new value for lock */ /* */ /* Stub function that allows for external manipulation of ipf_state_lock */ /* ------------------------------------------------------------------------ */ void ipf_state_setlock(arg, tmp) void *arg; int tmp; { ipf_state_softc_t *softs = arg; softs->ipf_state_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_stats */ /* Returns: ips_state_t* - pointer to state stats structure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Put all the current numbers and pointers into a single struct and return */ /* a pointer to it. */ /* ------------------------------------------------------------------------ */ static ips_stat_t * ipf_state_stats(softc) ipf_main_softc_t *softc; { ipf_state_softc_t *softs = softc->ipf_state_soft; ips_stat_t *issp = &softs->ipf_state_stats; issp->iss_state_size = softs->ipf_state_size; issp->iss_state_max = softs->ipf_state_max; issp->iss_table = softs->ipf_state_table; issp->iss_list = softs->ipf_state_list; issp->iss_ticks = softc->ipf_ticks; #ifdef IPFILTER_LOGGING issp->iss_log_ok = ipf_log_logok(softc, IPF_LOGSTATE); issp->iss_log_fail = ipf_log_failures(softc, IPF_LOGSTATE); #else issp->iss_log_ok = 0; issp->iss_log_fail = 0; #endif return issp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_remove */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to state structure to delete from table */ /* */ /* Search for a state structure that matches the one passed, according to */ /* the IP addresses and other protocol specific information. */ /* ------------------------------------------------------------------------ */ static int ipf_state_remove(softc, data) ipf_main_softc_t *softc; caddr_t data; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *sp, st; int error; sp = &st; error = ipf_inobj(softc, data, NULL, &st, IPFOBJ_IPSTATE); if (error) return EFAULT; WRITE_ENTER(&softc->ipf_state); for (sp = softs->ipf_state_list; sp; sp = sp->is_next) if ((sp->is_p == st.is_p) && (sp->is_v == st.is_v) && !bcmp((caddr_t)&sp->is_src, (caddr_t)&st.is_src, sizeof(st.is_src)) && !bcmp((caddr_t)&sp->is_dst, (caddr_t)&st.is_dst, sizeof(st.is_dst)) && !bcmp((caddr_t)&sp->is_ps, (caddr_t)&st.is_ps, sizeof(st.is_ps))) { ipf_state_del(softc, sp, ISL_REMOVE); RWLOCK_EXIT(&softc->ipf_state); return 0; } RWLOCK_EXIT(&softc->ipf_state); IPFERROR(100001); return ESRCH; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command integer */ /* mode(I) - file mode bits used with open */ /* uid(I) - uid of process making the ioctl call */ /* ctx(I) - pointer specific to context of the call */ /* */ /* Processes an ioctl call made to operate on the IP Filter state device. */ /* ------------------------------------------------------------------------ */ int ipf_state_ioctl(softc, data, cmd, mode, uid, ctx) ipf_main_softc_t *softc; caddr_t data; ioctlcmd_t cmd; int mode, uid; void *ctx; { ipf_state_softc_t *softs = softc->ipf_state_soft; int arg, ret, error = 0; SPL_INT(s); switch (cmd) { /* * Delete an entry from the state table. */ case SIOCDELST : error = ipf_state_remove(softc, data); break; /* * Flush the state table */ case SIOCIPFFL : error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(100002); error = EFAULT; } else { WRITE_ENTER(&softc->ipf_state); ret = ipf_state_flush(softc, arg, 4); RWLOCK_EXIT(&softc->ipf_state); error = BCOPYOUT(&ret, data, sizeof(ret)); if (error != 0) { IPFERROR(100003); error = EFAULT; } } break; #ifdef USE_INET6 case SIOCIPFL6 : error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(100004); error = EFAULT; } else { WRITE_ENTER(&softc->ipf_state); ret = ipf_state_flush(softc, arg, 6); RWLOCK_EXIT(&softc->ipf_state); error = BCOPYOUT(&ret, data, sizeof(ret)); if (error != 0) { IPFERROR(100005); error = EFAULT; } } break; #endif case SIOCMATCHFLUSH : WRITE_ENTER(&softc->ipf_state); error = ipf_state_matchflush(softc, data); RWLOCK_EXIT(&softc->ipf_state); break; #ifdef IPFILTER_LOG /* * Flush the state log. */ case SIOCIPFFB : if (!(mode & FWRITE)) { IPFERROR(100008); error = EPERM; } else { int tmp; tmp = ipf_log_clear(softc, IPL_LOGSTATE); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(100009); error = EFAULT; } } break; /* * Turn logging of state information on/off. */ case SIOCSETLG : if (!(mode & FWRITE)) { IPFERROR(100010); error = EPERM; } else { error = BCOPYIN(data, &softs->ipf_state_logging, sizeof(softs->ipf_state_logging)); if (error != 0) { IPFERROR(100011); error = EFAULT; } } break; /* * Return the current state of logging. */ case SIOCGETLG : error = BCOPYOUT(&softs->ipf_state_logging, data, sizeof(softs->ipf_state_logging)); if (error != 0) { IPFERROR(100012); error = EFAULT; } break; /* * Return the number of bytes currently waiting to be read. */ case FIONREAD : arg = ipf_log_bytesused(softc, IPL_LOGSTATE); error = BCOPYOUT(&arg, data, sizeof(arg)); if (error != 0) { IPFERROR(100013); error = EFAULT; } break; #endif /* * Get the current state statistics. */ case SIOCGETFS : error = ipf_outobj(softc, data, ipf_state_stats(softc), IPFOBJ_STATESTAT); break; /* * Lock/Unlock the state table. (Locking prevents any changes, which * means no packets match). */ case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(100014); error = EPERM; } else { error = ipf_lock(data, &softs->ipf_state_lock); } break; /* * Add an entry to the current state table. */ case SIOCSTPUT : if (!softs->ipf_state_lock || !(mode &FWRITE)) { IPFERROR(100015); error = EACCES; break; } error = ipf_state_putent(softc, softs, data); break; /* * Get a state table entry. */ case SIOCSTGET : if (!softs->ipf_state_lock) { IPFERROR(100016); error = EACCES; break; } error = ipf_state_getent(softc, softs, data); break; /* * Return a copy of the hash table bucket lengths */ case SIOCSTAT1 : error = BCOPYOUT(softs->ipf_state_stats.iss_bucketlen, data, softs->ipf_state_size * sizeof(u_int)); if (error != 0) { IPFERROR(100017); error = EFAULT; } break; case SIOCGENITER : { ipftoken_t *token; ipfgeniter_t iter; ipfobj_t obj; error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); token = ipf_token_find(softc, IPFGENITER_STATE, uid, ctx); if (token != NULL) { error = ipf_state_iter(softc, token, &iter, &obj); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } else { IPFERROR(100018); error = ESRCH; } SPL_X(s); break; } case SIOCGTABL : error = ipf_state_gettable(softc, softs, data); break; case SIOCIPFDELTOK : error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(100019); error = EFAULT; } else { SPL_SCHED(s); error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } break; case SIOCGTQTAB : error = ipf_outobj(softc, data, softs->ipf_state_tcptq, IPFOBJ_STATETQTAB); break; default : IPFERROR(100020); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_getent */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softs(I) - pointer to state context structure */ /* data(I) - pointer to state structure to retrieve from table*/ /* */ /* Copy out state information from the kernel to a user space process. If */ /* there is a filter rule associated with the state entry, copy that out */ /* as well. The entry to copy out is taken from the value of "ips_next" in */ /* the struct passed in and if not null and not found in the list of current*/ /* state entries, the retrieval fails. */ /* ------------------------------------------------------------------------ */ static int ipf_state_getent(softc, softs, data) ipf_main_softc_t *softc; ipf_state_softc_t *softs; caddr_t data; { ipstate_t *is, *isn; ipstate_save_t ips; int error; error = ipf_inobj(softc, data, NULL, &ips, IPFOBJ_STATESAVE); if (error) return EFAULT; READ_ENTER(&softc->ipf_state); isn = ips.ips_next; if (isn == NULL) { isn = softs->ipf_state_list; if (isn == NULL) { if (ips.ips_next == NULL) { RWLOCK_EXIT(&softc->ipf_state); IPFERROR(100021); return ENOENT; } return 0; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (is = softs->ipf_state_list; is; is = is->is_next) if (is == isn) break; if (!is) { RWLOCK_EXIT(&softc->ipf_state); IPFERROR(100022); return ESRCH; } } ips.ips_next = isn->is_next; bcopy((char *)isn, (char *)&ips.ips_is, sizeof(ips.ips_is)); ips.ips_rule = isn->is_rule; if (isn->is_rule != NULL) bcopy((char *)isn->is_rule, (char *)&ips.ips_fr, sizeof(ips.ips_fr)); RWLOCK_EXIT(&softc->ipf_state); error = ipf_outobj(softc, data, &ips, IPFOBJ_STATESAVE); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_putent */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softs(I) - pointer to state context structure */ /* data(I) - pointer to state information struct */ /* */ /* This function implements the SIOCSTPUT ioctl: insert a state entry into */ /* the state table. If the state info. includes a pointer to a filter rule */ /* then also add in an orphaned rule (will not show up in any "ipfstat -io" */ /* output. */ /* ------------------------------------------------------------------------ */ int ipf_state_putent(softc, softs, data) ipf_main_softc_t *softc; ipf_state_softc_t *softs; caddr_t data; { ipstate_t *is, *isn; ipstate_save_t ips; int error, out, i; frentry_t *fr; char *name; error = ipf_inobj(softc, data, NULL, &ips, IPFOBJ_STATESAVE); if (error != 0) return error; KMALLOC(isn, ipstate_t *); if (isn == NULL) { IPFERROR(100023); return ENOMEM; } bcopy((char *)&ips.ips_is, (char *)isn, sizeof(*isn)); bzero((char *)isn, offsetof(struct ipstate, is_pkts)); isn->is_sti.tqe_pnext = NULL; isn->is_sti.tqe_next = NULL; isn->is_sti.tqe_ifq = NULL; isn->is_sti.tqe_parent = isn; isn->is_ifp[0] = NULL; isn->is_ifp[1] = NULL; isn->is_ifp[2] = NULL; isn->is_ifp[3] = NULL; isn->is_sync = NULL; fr = ips.ips_rule; if (fr == NULL) { int inserr; READ_ENTER(&softc->ipf_state); inserr = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); RWLOCK_EXIT(&softc->ipf_state); return inserr; } if (isn->is_flags & SI_NEWFR) { KMALLOC(fr, frentry_t *); if (fr == NULL) { KFREE(isn); IPFERROR(100024); return ENOMEM; } bcopy((char *)&ips.ips_fr, (char *)fr, sizeof(*fr)); out = fr->fr_flags & FR_OUTQUE ? 1 : 0; isn->is_rule = fr; ips.ips_is.is_rule = fr; MUTEX_NUKE(&fr->fr_lock); MUTEX_INIT(&fr->fr_lock, "state filter rule lock"); /* * Look up all the interface names in the rule. */ for (i = 0; i < FR_NUM(fr->fr_ifnames); i++) { if (fr->fr_ifnames[i] == -1) { fr->fr_ifas[i] = NULL; continue; } name = FR_NAME(fr, fr_ifnames[i]); fr->fr_ifas[i] = ipf_resolvenic(softc, name, fr->fr_family); } for (i = 0; i < FR_NUM(isn->is_ifname); i++) { name = isn->is_ifname[i]; isn->is_ifp[i] = ipf_resolvenic(softc, name, isn->is_v); } fr->fr_ref = 0; fr->fr_dsize = 0; fr->fr_data = NULL; fr->fr_type = FR_T_NONE; (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_tifs[0], fr->fr_family); (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_tifs[1], fr->fr_family); (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_dif, fr->fr_family); /* * send a copy back to userland of what we ended up * to allow for verification. */ error = ipf_outobj(softc, data, &ips, IPFOBJ_STATESAVE); if (error != 0) { KFREE(isn); MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); IPFERROR(100025); return EFAULT; } READ_ENTER(&softc->ipf_state); error = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); RWLOCK_EXIT(&softc->ipf_state); } else { READ_ENTER(&softc->ipf_state); for (is = softs->ipf_state_list; is; is = is->is_next) if (is->is_rule == fr) { error = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); break; } if (is == NULL) { KFREE(isn); isn = NULL; } RWLOCK_EXIT(&softc->ipf_state); if (isn == NULL) { IPFERROR(100033); error = ESRCH; } } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_insert */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: is(I) - pointer to state structure */ /* rev(I) - flag indicating direction of packet */ /* */ /* Inserts a state structure into the hash table (for lookups) and the list */ /* of state entries (for enumeration). Resolves all of the interface names */ /* to pointers and adjusts running stats for the hash table as appropriate. */ /* */ /* This function can fail if the filter rule has had a population policy of */ /* IP addresses used with stateful filtering assigned to it. */ /* */ /* Locking: it is assumed that some kind of lock on ipf_state is held. */ /* Exits with is_lock initialised and held - *EVEN IF ERROR*. */ /* ------------------------------------------------------------------------ */ int ipf_state_insert(softc, is, rev) ipf_main_softc_t *softc; ipstate_t *is; int rev; { ipf_state_softc_t *softs = softc->ipf_state_soft; frentry_t *fr; u_int hv; int i; /* * Look up all the interface names in the state entry. */ for (i = 0; i < FR_NUM(is->is_ifp); i++) { if (is->is_ifp[i] != NULL) continue; is->is_ifp[i] = ipf_resolvenic(softc, is->is_ifname[i], is->is_v); } /* * If we could trust is_hv, then the modulus would not be needed, * but when running with IPFILTER_SYNC, this stops bad values. */ hv = is->is_hv % softs->ipf_state_size; /* TRACE is, hv */ is->is_hv = hv; /* * We need to get both of these locks...the first because it is * possible that once the insert is complete another packet might * come along, match the entry and want to update it. */ MUTEX_INIT(&is->is_lock, "ipf state entry"); MUTEX_ENTER(&is->is_lock); MUTEX_ENTER(&softs->ipf_stinsert); fr = is->is_rule; if (fr != NULL) { if ((fr->fr_srctrack.ht_max_nodes != 0) && (ipf_ht_node_add(softc, &fr->fr_srctrack, is->is_family, &is->is_src) == -1)) { SBUMPD(ipf_state_stats, iss_max_track); MUTEX_EXIT(&softs->ipf_stinsert); return -1; } MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); fr->fr_statecnt++; } if (is->is_flags & (SI_WILDP|SI_WILDA)) { DT(iss_wild_plus_one); SINCL(ipf_state_stats.iss_wild); } SBUMP(ipf_state_stats.iss_proto[is->is_p]); SBUMP(ipf_state_stats.iss_active_proto[is->is_p]); /* * add into list table. */ if (softs->ipf_state_list != NULL) softs->ipf_state_list->is_pnext = &is->is_next; is->is_pnext = &softs->ipf_state_list; is->is_next = softs->ipf_state_list; softs->ipf_state_list = is; if (softs->ipf_state_table[hv] != NULL) softs->ipf_state_table[hv]->is_phnext = &is->is_hnext; else softs->ipf_state_stats.iss_inuse++; is->is_phnext = softs->ipf_state_table + hv; is->is_hnext = softs->ipf_state_table[hv]; softs->ipf_state_table[hv] = is; softs->ipf_state_stats.iss_bucketlen[hv]++; softs->ipf_state_stats.iss_active++; MUTEX_EXIT(&softs->ipf_stinsert); ipf_state_setqueue(softc, is, rev); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matchipv4addrs */ /* Returns: int - 2 addresses match (strong match), 1 reverse match, */ /* 0 no match */ /* Parameters: is1, is2 pointers to states we are checking */ /* */ /* Function matches IPv4 addresses it returns strong match for ICMP proto */ /* even there is only reverse match */ /* ------------------------------------------------------------------------ */ static int ipf_state_matchipv4addrs(is1, is2) ipstate_t *is1, *is2; { int rv; if (is1->is_saddr == is2->is_saddr && is1->is_daddr == is2->is_daddr) rv = 2; else if (is1->is_saddr == is2->is_daddr && is1->is_daddr == is2->is_saddr) { /* force strong match for ICMP protocol */ rv = (is1->is_p == IPPROTO_ICMP) ? 2 : 1; } else rv = 0; return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matchipv6addrs */ /* Returns: int - 2 addresses match (strong match), 1 reverse match, */ /* 0 no match */ /* Parameters: is1, is2 pointers to states we are checking */ /* */ /* Function matches IPv6 addresses it returns strong match for ICMP proto */ /* even there is only reverse match */ /* ------------------------------------------------------------------------ */ static int ipf_state_matchipv6addrs(is1, is2) ipstate_t *is1, *is2; { int rv; if (IP6_EQ(&is1->is_src, &is2->is_src) && IP6_EQ(&is1->is_dst, &is2->is_dst)) rv = 2; else if (IP6_EQ(&is1->is_src, &is2->is_dst) && IP6_EQ(&is1->is_dst, &is2->is_src)) { /* force strong match for ICMPv6 protocol */ rv = (is1->is_p == IPPROTO_ICMPV6) ? 2 : 1; } else rv = 0; return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matchaddresses */ /* Returns: int - 2 addresses match, 1 reverse match, zero no match */ /* Parameters: is1, is2 pointers to states we are checking */ /* */ /* function retruns true if two pairs of addresses belong to single */ /* connection. suppose there are two endpoints: */ /* endpoint1 1.1.1.1 */ /* endpoint2 1.1.1.2 */ /* */ /* the state is established by packet flying from .1 to .2 so we see: */ /* is1->src = 1.1.1.1 */ /* is1->dst = 1.1.1.2 */ /* now endpoint 1.1.1.2 sends answer */ /* retreives is1 record created by first packat and compares it with is2 */ /* temporal record, is2 is initialized as follows: */ /* is2->src = 1.1.1.2 */ /* is2->dst = 1.1.1.1 */ /* in this case 1 will be returned */ /* */ /* the ipf_matchaddresses() assumes those two records to be same. of course */ /* the ipf_matchaddresses() also assume records are same in case you pass */ /* identical arguments (i.e. ipf_matchaddress(is1, is1) would return 2 */ /* ------------------------------------------------------------------------ */ static int ipf_state_matchaddresses(is1, is2) ipstate_t *is1, *is2; { int rv; if (is1->is_v == 4) { rv = ipf_state_matchipv4addrs(is1, is2); } else { rv = ipf_state_matchipv6addrs(is1, is2); } return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_matchports */ /* Returns: int - 2 match, 1 rverse match, 0 no match */ /* Parameters: ppairs1, ppairs - src, dst ports we want to match */ /* */ /* performs the same match for isps members as for addresses */ /* ------------------------------------------------------------------------ */ static int ipf_state_matchports(ppairs1, ppairs2) udpinfo_t *ppairs1, *ppairs2; { int rv; if (ppairs1->us_sport == ppairs2->us_sport && ppairs1->us_dport == ppairs2->us_dport) rv = 2; else if (ppairs1->us_sport == ppairs2->us_dport && ppairs1->us_dport == ppairs2->us_sport) rv = 1; else rv = 0; return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_matchisps */ /* Returns: int - nonzero if isps members match, 0 nomatch */ /* Parameters: is1, is2 - states we want to match */ /* */ /* performs the same match for isps members as for addresses */ /* ------------------------------------------------------------------------ */ static int ipf_state_matchisps(is1, is2) ipstate_t *is1, *is2; { int rv; if (is1->is_p == is2->is_p) { switch (is1->is_p) { case IPPROTO_TCP : case IPPROTO_UDP : case IPPROTO_GRE : /* greinfo_t can be also interprted as port pair */ rv = ipf_state_matchports(&is1->is_ps.is_us, &is2->is_ps.is_us); break; case IPPROTO_ICMP : case IPPROTO_ICMPV6 : /* force strong match for ICMP datagram. */ if (bcmp(&is1->is_ps, &is2->is_ps, sizeof(icmpinfo_t)) == 0) { rv = 2; } else { rv = 0; } break; default: rv = 0; } } else { rv = 0; } return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_match */ /* Returns: int - nonzero match, zero no match */ /* Parameters: is1, is2 - states we want to match */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_state_match(is1, is2) ipstate_t *is1, *is2; { int rv; int amatch; int pomatch; if (bcmp(&is1->is_pass, &is2->is_pass, offsetof(struct ipstate, is_authmsk) - offsetof(struct ipstate, is_pass)) == 0) { pomatch = ipf_state_matchisps(is1, is2); amatch = ipf_state_matchaddresses(is1, is2); rv = (amatch != 0) && (amatch == pomatch); } else { rv = 0; } return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_add */ /* Returns: ipstate_t - 0 = success */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fin(I) - pointer to packet information */ /* stsave(O) - pointer to place to save pointer to created */ /* state structure. */ /* flags(I) - flags to use when creating the structure */ /* */ /* Creates a new IP state structure from the packet information collected. */ /* Inserts it into the state table and appends to the bottom of the active */ /* list. If the capacity of the table has reached the maximum allowed then */ /* the call will fail and a flush is scheduled for the next timeout call. */ /* */ /* NOTE: The use of stsave to point to nat_state will result in memory */ /* corruption. It should only be used to point to objects that will */ /* either outlive this (not expired) or will deref the ip_state_t */ /* when they are deleted. */ /* ------------------------------------------------------------------------ */ int ipf_state_add(softc, fin, stsave, flags) ipf_main_softc_t *softc; fr_info_t *fin; ipstate_t **stsave; u_int flags; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, ips; struct icmp *ic; u_int pass, hv; frentry_t *fr; tcphdr_t *tcp; frdest_t *fdp; int out; /* * If a locally created packet is trying to egress but it * does not match because of this lock, it is likely that * the policy will block it and return network unreachable further * up the stack. To mitigate this error, EAGAIN is returned instead, * telling the IP stack to try sending this packet again later. */ if (softs->ipf_state_lock) { SBUMPD(ipf_state_stats, iss_add_locked); fin->fin_error = EAGAIN; return -1; } if (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD)) { SBUMPD(ipf_state_stats, iss_add_bad); return -1; } if ((fin->fin_flx & FI_OOW) && !(fin->fin_tcpf & TH_SYN)) { SBUMPD(ipf_state_stats, iss_add_oow); return -1; } if ((softs->ipf_state_stats.iss_active * 100 / softs->ipf_state_max) > softs->ipf_state_wm_high) { softs->ipf_state_doflush = 1; } /* * If a "keep state" rule has reached the maximum number of references * to it, then schedule an automatic flush in case we can clear out * some "dead old wood". Note that because the lock isn't held on * fr it is possible that we could overflow. The cost of overflowing * is being ignored here as the number by which it can overflow is * a product of the number of simultaneous threads that could be * executing in here, so a limit of 100 won't result in 200, but could * result in 101 or 102. */ fr = fin->fin_fr; if (fr != NULL) { if ((softs->ipf_state_stats.iss_active >= softs->ipf_state_max) && (fr->fr_statemax == 0)) { SBUMPD(ipf_state_stats, iss_max); return 1; } if ((fr->fr_statemax != 0) && (fr->fr_statecnt >= fr->fr_statemax)) { SBUMPD(ipf_state_stats, iss_max_ref); return 2; } } is = &ips; if (fr == NULL) { pass = softc->ipf_flags; is->is_tag = FR_NOLOGTAG; } else { pass = fr->fr_flags; } ic = NULL; tcp = NULL; out = fin->fin_out; bzero((char *)is, sizeof(*is)); is->is_die = 1 + softc->ipf_ticks; /* * We want to check everything that is a property of this packet, * but we don't (automatically) care about its fragment status as * this may change. */ is->is_pass = pass; is->is_v = fin->fin_v; is->is_sec = fin->fin_secmsk; is->is_secmsk = 0xffff; is->is_auth = fin->fin_auth; is->is_authmsk = 0xffff; is->is_family = fin->fin_family; is->is_opt[0] = fin->fin_optmsk; is->is_optmsk[0] = 0xffffffff; if (is->is_v == 6) { is->is_opt[0] &= ~0x8; is->is_optmsk[0] &= ~0x8; } /* * Copy and calculate... */ hv = (is->is_p = fin->fin_fi.fi_p); is->is_src = fin->fin_fi.fi_src; hv += is->is_saddr; is->is_dst = fin->fin_fi.fi_dst; hv += is->is_daddr; #ifdef USE_INET6 if (fin->fin_v == 6) { /* * For ICMPv6, we check to see if the destination address is * a multicast address. If it is, do not include it in the * calculation of the hash because the correct reply will come * back from a real address, not a multicast address. */ if ((is->is_p == IPPROTO_ICMPV6) && IN6_IS_ADDR_MULTICAST(&is->is_dst.in6)) { /* * So you can do keep state with neighbour discovery. * * Here we could use the address from the neighbour * solicit message to put in the state structure and * we could use that without a wildcard flag too... */ flags |= SI_W_DADDR; hv -= is->is_daddr; } else { hv += is->is_dst.i6[1]; hv += is->is_dst.i6[2]; hv += is->is_dst.i6[3]; } hv += is->is_src.i6[1]; hv += is->is_src.i6[2]; hv += is->is_src.i6[3]; } #endif if ((fin->fin_v == 4) && (fin->fin_flx & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST))) { flags |= SI_W_DADDR; hv -= is->is_daddr; } switch (is->is_p) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : ic = fin->fin_dp; switch (ic->icmp_type) { case ICMP6_ECHO_REQUEST : hv += (is->is_icmp.ici_id = ic->icmp_id); /*FALLTHROUGH*/ case ICMP6_MEMBERSHIP_QUERY : case ND_ROUTER_SOLICIT : case ND_NEIGHBOR_SOLICIT : case ICMP6_NI_QUERY : is->is_icmp.ici_type = ic->icmp_type; break; default : SBUMPD(ipf_state_stats, iss_icmp6_notquery); return -2; } break; #endif case IPPROTO_ICMP : ic = fin->fin_dp; switch (ic->icmp_type) { case ICMP_ECHO : case ICMP_TSTAMP : case ICMP_IREQ : case ICMP_MASKREQ : is->is_icmp.ici_type = ic->icmp_type; hv += (is->is_icmp.ici_id = ic->icmp_id); break; default : SBUMPD(ipf_state_stats, iss_icmp_notquery); return -3; } break; #if 0 case IPPROTO_GRE : gre = fin->fin_dp; is->is_gre.gs_flags = gre->gr_flags; is->is_gre.gs_ptype = gre->gr_ptype; if (GRE_REV(is->is_gre.gs_flags) == 1) { is->is_call[0] = fin->fin_data[0]; is->is_call[1] = fin->fin_data[1]; } break; #endif case IPPROTO_TCP : tcp = fin->fin_dp; if (tcp->th_flags & TH_RST) { SBUMPD(ipf_state_stats, iss_tcp_rstadd); return -4; } /* TRACE is, flags, hv */ /* * The endian of the ports doesn't matter, but the ack and * sequence numbers do as we do mathematics on them later. */ is->is_sport = htons(fin->fin_data[0]); is->is_dport = htons(fin->fin_data[1]); if ((flags & (SI_W_DPORT|SI_W_SPORT)) == 0) { hv += is->is_sport; hv += is->is_dport; } /* TRACE is, flags, hv */ /* * If this is a real packet then initialise fields in the * state information structure from the TCP header information. */ is->is_maxdwin = 1; is->is_maxswin = ntohs(tcp->th_win); if (is->is_maxswin == 0) is->is_maxswin = 1; if ((fin->fin_flx & FI_IGNORE) == 0) { is->is_send = ntohl(tcp->th_seq) + fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); is->is_maxsend = is->is_send; /* * Window scale option is only present in * SYN/SYN-ACK packet. */ if ((tcp->th_flags & ~(TH_FIN|TH_ACK|TH_ECNALL)) == TH_SYN && (TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { if (ipf_tcpoptions(softs, fin, tcp, &is->is_tcp.ts_data[0]) == -1) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_tcpoptions_th_fin_ack_ecnall, fr_info_t *, fin); } } if ((fin->fin_out != 0) && (pass & FR_NEWISN) != 0) { ipf_checknewisn(fin, is); ipf_fixoutisn(fin, is); } if ((tcp->th_flags & TH_OPENING) == TH_SYN) flags |= IS_TCPFSM; else { is->is_maxdwin = is->is_maxswin * 2; is->is_dend = ntohl(tcp->th_ack); is->is_maxdend = ntohl(tcp->th_ack); is->is_maxdwin *= 2; } } /* * If we're creating state for a starting connection, start * the timer on it as we'll never see an error if it fails * to connect. */ break; case IPPROTO_UDP : tcp = fin->fin_dp; is->is_sport = htons(fin->fin_data[0]); is->is_dport = htons(fin->fin_data[1]); if ((flags & (SI_W_DPORT|SI_W_SPORT)) == 0) { hv += tcp->th_dport; hv += tcp->th_sport; } break; default : break; } hv = DOUBLE_HASH(hv); is->is_hv = hv; /* * Look for identical state. */ for (is = softs->ipf_state_table[hv % softs->ipf_state_size]; is != NULL; is = is->is_hnext) { if (ipf_state_match(&ips, is) == 1) break; } if (is != NULL) { SBUMPD(ipf_state_stats, iss_add_dup); return 3; } if (softs->ipf_state_stats.iss_bucketlen[hv] >= softs->ipf_state_maxbucket) { SBUMPD(ipf_state_stats, iss_bucket_full); return 4; } /* * No existing state; create new */ KMALLOC(is, ipstate_t *); if (is == NULL) { SBUMPD(ipf_state_stats, iss_nomem); return 5; } bcopy((char *)&ips, (char *)is, sizeof(*is)); is->is_flags = flags & IS_INHERITED; is->is_rulen = fin->fin_rule; is->is_rule = fr; /* * Do not do the modulus here, it is done in ipf_state_insert(). */ if (fr != NULL) { ipftq_t *tq; (void) strncpy(is->is_group, FR_NAME(fr, fr_group), FR_GROUPLEN); if (fr->fr_age[0] != 0) { tq = ipf_addtimeoutqueue(softc, &softs->ipf_state_usertq, fr->fr_age[0]); is->is_tqehead[0] = tq; is->is_sti.tqe_flags |= TQE_RULEBASED; } if (fr->fr_age[1] != 0) { tq = ipf_addtimeoutqueue(softc, &softs->ipf_state_usertq, fr->fr_age[1]); is->is_tqehead[1] = tq; is->is_sti.tqe_flags |= TQE_RULEBASED; } is->is_tag = fr->fr_logtag; } /* * It may seem strange to set is_ref to 2, but if stsave is not NULL * then a copy of the pointer is being stored somewhere else and in * the end, it will expect to be able to do something with it. */ is->is_me = stsave; if (stsave != NULL) { *stsave = is; is->is_ref = 2; } else { is->is_ref = 1; } is->is_pkts[0] = 0, is->is_bytes[0] = 0; is->is_pkts[1] = 0, is->is_bytes[1] = 0; is->is_pkts[2] = 0, is->is_bytes[2] = 0; is->is_pkts[3] = 0, is->is_bytes[3] = 0; if ((fin->fin_flx & FI_IGNORE) == 0) { is->is_pkts[out] = 1; fin->fin_pktnum = 1; is->is_bytes[out] = fin->fin_plen; is->is_flx[out][0] = fin->fin_flx & FI_CMP; is->is_flx[out][0] &= ~FI_OOW; } if (pass & FR_STLOOSE) is->is_flags |= IS_LOOSE; if (pass & FR_STSTRICT) is->is_flags |= IS_STRICT; if (pass & FR_STATESYNC) is->is_flags |= IS_STATESYNC; if (pass & FR_LOGFIRST) is->is_pass &= ~(FR_LOGFIRST|FR_LOG); READ_ENTER(&softc->ipf_state); if (ipf_state_insert(softc, is, fin->fin_rev) == -1) { RWLOCK_EXIT(&softc->ipf_state); /* * This is a bit more manual than it should be but * ipf_state_del cannot be called. */ MUTEX_EXIT(&is->is_lock); MUTEX_DESTROY(&is->is_lock); if (is->is_tqehead[0] != NULL) { if (ipf_deletetimeoutqueue(is->is_tqehead[0]) == 0) ipf_freetimeoutqueue(softc, is->is_tqehead[0]); is->is_tqehead[0] = NULL; } if (is->is_tqehead[1] != NULL) { if (ipf_deletetimeoutqueue(is->is_tqehead[1]) == 0) ipf_freetimeoutqueue(softc, is->is_tqehead[1]); is->is_tqehead[1] = NULL; } KFREE(is); return -1; } /* * Filling in the interface name is after the insert so that an * event (such as add/delete) of an interface that is referenced * by this rule will see this state entry. */ if (fr != NULL) { /* * The name '-' is special for network interfaces and causes * a NULL name to be present, always, allowing packets to * match it, regardless of their interface. */ if ((fin->fin_ifp == NULL) || (fr->fr_ifnames[out << 1] != -1 && fr->fr_names[fr->fr_ifnames[out << 1] + 0] == '-' && fr->fr_names[fr->fr_ifnames[out << 1] + 1] == '\0')) { is->is_ifp[out << 1] = fr->fr_ifas[0]; strncpy(is->is_ifname[out << 1], FR_NAME(fr, fr_ifnames[0]), sizeof(fr->fr_ifnames[0])); } else { is->is_ifp[out << 1] = fin->fin_ifp; COPYIFNAME(fin->fin_v, fin->fin_ifp, is->is_ifname[out << 1]); } is->is_ifp[(out << 1) + 1] = fr->fr_ifas[1]; if (fr->fr_ifnames[1] != -1) { strncpy(is->is_ifname[(out << 1) + 1], FR_NAME(fr, fr_ifnames[1]), sizeof(fr->fr_ifnames[1])); } is->is_ifp[(1 - out) << 1] = fr->fr_ifas[2]; if (fr->fr_ifnames[2] != -1) { strncpy(is->is_ifname[((1 - out) << 1)], FR_NAME(fr, fr_ifnames[2]), sizeof(fr->fr_ifnames[2])); } is->is_ifp[((1 - out) << 1) + 1] = fr->fr_ifas[3]; if (fr->fr_ifnames[3] != -1) { strncpy(is->is_ifname[((1 - out) << 1) + 1], FR_NAME(fr, fr_ifnames[3]), sizeof(fr->fr_ifnames[3])); } } else { if (fin->fin_ifp != NULL) { is->is_ifp[out << 1] = fin->fin_ifp; COPYIFNAME(fin->fin_v, fin->fin_ifp, is->is_ifname[out << 1]); } } if (fin->fin_p == IPPROTO_TCP) { /* * If we're creating state for a starting connection, start the * timer on it as we'll never see an error if it fails to * connect. */ (void) ipf_tcp_age(&is->is_sti, fin, softs->ipf_state_tcptq, is->is_flags, 2); } MUTEX_EXIT(&is->is_lock); if ((is->is_flags & IS_STATESYNC) && ((is->is_flags & SI_CLONE) == 0)) is->is_sync = ipf_sync_new(softc, SMC_STATE, fin, is); if (softs->ipf_state_logging) ipf_state_log(softc, is, ISL_NEW); RWLOCK_EXIT(&softc->ipf_state); fin->fin_flx |= FI_STATE; if (fin->fin_flx & FI_FRAG) (void) ipf_frag_new(softc, fin, pass); fdp = &fr->fr_tifs[0]; if (fdp->fd_type == FRD_DSTLIST) { ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &is->is_tifs[0]); } else { bcopy(fdp, &is->is_tifs[0], sizeof(*fdp)); } fdp = &fr->fr_tifs[1]; if (fdp->fd_type == FRD_DSTLIST) { ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &is->is_tifs[1]); } else { bcopy(fdp, &is->is_tifs[1], sizeof(*fdp)); } fin->fin_tif = &is->is_tifs[fin->fin_rev]; fdp = &fr->fr_dif; if (fdp->fd_type == FRD_DSTLIST) { ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &is->is_dif); } else { bcopy(fdp, &is->is_dif, sizeof(*fdp)); } fin->fin_dif = &is->is_dif; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tcpoptions */ /* Returns: int - 1 == packet matches state entry, 0 == it does not, */ /* -1 == packet has bad TCP options data */ /* Parameters: softs(I) - pointer to state context structure */ /* fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP packet header */ /* td(I) - pointer to TCP data held as part of the state */ /* */ /* Look after the TCP header for any options and deal with those that are */ /* present. Record details about those that we recogise. */ /* ------------------------------------------------------------------------ */ static int ipf_tcpoptions(softs, fin, tcp, td) ipf_state_softc_t *softs; fr_info_t *fin; tcphdr_t *tcp; tcpdata_t *td; { int off, mlen, ol, i, len, retval; char buf[64], *s, opt; mb_t *m = NULL; len = (TCP_OFF(tcp) << 2); if (fin->fin_dlen < len) { SBUMPD(ipf_state_stats, iss_tcp_toosmall); return 0; } len -= sizeof(*tcp); off = fin->fin_plen - fin->fin_dlen + sizeof(*tcp) + fin->fin_ipoff; m = fin->fin_m; mlen = MSGDSIZE(m) - off; if (len > mlen) { len = mlen; retval = 0; } else { retval = 1; } COPYDATA(m, off, len, buf); for (s = buf; len > 0; ) { opt = *s; if (opt == TCPOPT_EOL) break; else if (opt == TCPOPT_NOP) ol = 1; else { if (len < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > len) break; /* * Extract the TCP options we are interested in out of * the header and store them in the the tcpdata struct. */ switch (opt) { case TCPOPT_WINDOW : if (ol == TCPOLEN_WINDOW) { i = (int)*(s + 2); if (i > TCP_WSCALE_MAX) i = TCP_WSCALE_MAX; else if (i < 0) i = 0; td->td_winscale = i; td->td_winflags |= TCP_WSCALE_SEEN| TCP_WSCALE_FIRST; } else retval = -1; break; case TCPOPT_MAXSEG : /* * So, if we wanted to set the TCP MAXSEG, * it should be done here... */ if (ol == TCPOLEN_MAXSEG) { i = (int)*(s + 2); i <<= 8; i += (int)*(s + 3); td->td_maxseg = i; } else retval = -1; break; case TCPOPT_SACK_PERMITTED : if (ol == TCPOLEN_SACK_PERMITTED) td->td_winflags |= TCP_SACK_PERMIT; else retval = -1; break; } } len -= ol; s += ol; } if (retval == -1) { SBUMPD(ipf_state_stats, iss_tcp_badopt); } return retval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_tcp */ /* Returns: int - 1 == packet matches state entry, 0 == it does not */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softs(I) - pointer to state context structure */ /* fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP packet header */ /* is(I) - pointer to master state structure */ /* */ /* Check to see if a packet with TCP headers fits within the TCP window. */ /* Change timeout depending on whether new packet is a SYN-ACK returning */ /* for a SYN or a RST or FIN which indicate time to close up shop. */ /* ------------------------------------------------------------------------ */ static int ipf_state_tcp(softc, softs, fin, tcp, is) ipf_main_softc_t *softc; ipf_state_softc_t *softs; fr_info_t *fin; tcphdr_t *tcp; ipstate_t *is; { tcpdata_t *fdata, *tdata; int source, ret, flags; source = !fin->fin_rev; if (((is->is_flags & IS_TCPFSM) != 0) && (source == 1) && (ntohs(is->is_sport) != fin->fin_data[0])) source = 0; fdata = &is->is_tcp.ts_data[!source]; tdata = &is->is_tcp.ts_data[source]; MUTEX_ENTER(&is->is_lock); /* * If a SYN packet is received for a connection that is on the way out * but hasn't yet departed then advance this session along the way. */ if ((tcp->th_flags & TH_OPENING) == TH_SYN) { if ((is->is_state[0] > IPF_TCPS_ESTABLISHED) && (is->is_state[1] > IPF_TCPS_ESTABLISHED)) { is->is_state[!source] = IPF_TCPS_CLOSED; ipf_movequeue(softc->ipf_ticks, &is->is_sti, is->is_sti.tqe_ifq, &softs->ipf_state_deletetq); MUTEX_EXIT(&is->is_lock); DT1(iss_tcp_closing, ipstate_t *, is); SBUMP(ipf_state_stats.iss_tcp_closing); return 0; } } if (is->is_flags & IS_LOOSE) ret = 1; else ret = ipf_state_tcpinwindow(fin, fdata, tdata, tcp, is->is_flags); if (ret > 0) { /* * Nearing end of connection, start timeout. */ ret = ipf_tcp_age(&is->is_sti, fin, softs->ipf_state_tcptq, is->is_flags, ret); if (ret == 0) { MUTEX_EXIT(&is->is_lock); DT2(iss_tcp_fsm, fr_info_t *, fin, ipstate_t *, is); SBUMP(ipf_state_stats.iss_tcp_fsm); return 0; } if (softs->ipf_state_logging > 4) ipf_state_log(softc, is, ISL_STATECHANGE); /* * set s0's as appropriate. Use syn-ack packet as it * contains both pieces of required information. */ /* * Window scale option is only present in SYN/SYN-ACK packet. * Compare with ~TH_FIN to mask out T/TCP setups. */ flags = tcp->th_flags & ~(TH_FIN|TH_ECNALL); if (flags == (TH_SYN|TH_ACK)) { is->is_s0[source] = ntohl(tcp->th_ack); is->is_s0[!source] = ntohl(tcp->th_seq) + 1; if ((TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { if (ipf_tcpoptions(softs, fin, tcp, fdata) == -1) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_winscale_syn_ack, fr_info_t *, fin); } } if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) ipf_checknewisn(fin, is); } else if (flags == TH_SYN) { is->is_s0[source] = ntohl(tcp->th_seq) + 1; if ((TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { if (ipf_tcpoptions(softs, fin, tcp, fdata) == -1) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_winscale_syn, fr_info_t *, fin); } } if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) ipf_checknewisn(fin, is); } ret = 1; } else { DT2(iss_tcp_oow, fr_info_t *, fin, ipstate_t *, is); SBUMP(ipf_state_stats.iss_tcp_oow); ret = 0; } MUTEX_EXIT(&is->is_lock); return ret; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checknewisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* */ /* Check to see if this TCP connection is expecting and needs a new */ /* sequence number for a particular direction of the connection. */ /* */ /* NOTE: This does not actually change the sequence numbers, only gets new */ /* one ready. */ /* ------------------------------------------------------------------------ */ static void ipf_checknewisn(fin, is) fr_info_t *fin; ipstate_t *is; { u_32_t sumd, old, new; tcphdr_t *tcp; int i; i = fin->fin_rev; tcp = fin->fin_dp; if (((i == 0) && !(is->is_flags & IS_ISNSYN)) || ((i == 1) && !(is->is_flags & IS_ISNACK))) { old = ntohl(tcp->th_seq); new = ipf_newisn(fin); is->is_isninc[i] = new - old; CALC_SUMD(old, new, sumd); is->is_sumd[i] = (sumd & 0xffff) + (sumd >> 16); is->is_flags |= ((i == 0) ? IS_ISNSYN : IS_ISNACK); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_tcpinwindow */ /* Returns: int - 1 == packet inside TCP "window", 0 == not inside. */ /* Parameters: fin(I) - pointer to packet information */ /* fdata(I) - pointer to tcp state informatio (forward) */ /* tdata(I) - pointer to tcp state informatio (reverse) */ /* tcp(I) - pointer to TCP packet header */ /* */ /* Given a packet has matched addresses and ports, check to see if it is */ /* within the TCP data window. In a show of generosity, allow packets that */ /* are within the window space behind the current sequence # as well. */ /* ------------------------------------------------------------------------ */ static int ipf_state_tcpinwindow(fin, fdata, tdata, tcp, flags) fr_info_t *fin; tcpdata_t *fdata, *tdata; tcphdr_t *tcp; int flags; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; tcp_seq seq, ack, end; int ackskew, tcpflags; u_32_t win, maxwin; int dsize, inseq; /* * Find difference between last checked packet and this packet. */ tcpflags = tcp->th_flags; seq = ntohl(tcp->th_seq); ack = ntohl(tcp->th_ack); if (tcpflags & TH_SYN) win = ntohs(tcp->th_win); else win = ntohs(tcp->th_win) << fdata->td_winscale; /* * A window of 0 produces undesirable behaviour from this function. */ if (win == 0) win = 1; dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcpflags & TH_SYN) ? 1 : 0) + ((tcpflags & TH_FIN) ? 1 : 0); /* * if window scaling is present, the scaling is only allowed * for windows not in the first SYN packet. In that packet the * window is 65535 to specify the largest window possible * for receivers not implementing the window scale option. * Currently, we do not assume TTCP here. That means that * if we see a second packet from a host (after the initial * SYN), we can assume that the receiver of the SYN did * already send back the SYN/ACK (and thus that we know if * the receiver also does window scaling) */ if (!(tcpflags & TH_SYN) && (fdata->td_winflags & TCP_WSCALE_FIRST)) { fdata->td_winflags &= ~TCP_WSCALE_FIRST; fdata->td_maxwin = win; } end = seq + dsize; if ((fdata->td_end == 0) && (!(flags & IS_TCPFSM) || ((tcpflags & TH_OPENING) == TH_OPENING))) { /* * Must be a (outgoing) SYN-ACK in reply to a SYN. */ fdata->td_end = end - 1; fdata->td_maxwin = 1; fdata->td_maxend = end + win; } if (!(tcpflags & TH_ACK)) { /* Pretend an ack was sent */ ack = tdata->td_end; } else if (((tcpflags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) && (ack == 0)) { /* gross hack to get around certain broken tcp stacks */ ack = tdata->td_end; } maxwin = tdata->td_maxwin; ackskew = tdata->td_end - ack; /* * Strict sequencing only allows in-order delivery. */ if ((flags & IS_STRICT) != 0) { if (seq != fdata->td_end) { DT2(iss_tcp_struct, tcpdata_t *, fdata, int, seq); SBUMP(ipf_state_stats.iss_tcp_strict); fin->fin_flx |= FI_OOW; return 0; } } #define SEQ_GE(a,b) ((int)((a) - (b)) >= 0) #define SEQ_GT(a,b) ((int)((a) - (b)) > 0) inseq = 0; if ((SEQ_GE(fdata->td_maxend, end)) && (SEQ_GE(seq, fdata->td_end - maxwin)) && /* XXX what about big packets */ #define MAXACKWINDOW 66000 (-ackskew <= (MAXACKWINDOW)) && ( ackskew <= (MAXACKWINDOW << fdata->td_winscale))) { inseq = 1; /* * Microsoft Windows will send the next packet to the right of the * window if SACK is in use. */ } else if ((seq == fdata->td_maxend) && (ackskew == 0) && (fdata->td_winflags & TCP_SACK_PERMIT) && (tdata->td_winflags & TCP_SACK_PERMIT)) { DT2(iss_sinsack, tcpdata_t *, fdata, int, seq); SBUMP(ipf_state_stats.iss_winsack); inseq = 1; /* * Sometimes a TCP RST will be generated with only the ACK field * set to non-zero. */ } else if ((seq == 0) && (tcpflags == (TH_RST|TH_ACK)) && (ackskew >= -1) && (ackskew <= 1)) { inseq = 1; } else if (!(flags & IS_TCPFSM)) { int i; i = (fin->fin_rev << 1) + fin->fin_out; #if 0 if (is_pkts[i]0 == 0) { /* * Picking up a connection in the middle, the "next" * packet seen from a direction that is new should be * accepted, even if it appears out of sequence. */ inseq = 1; } else #endif if (!(fdata->td_winflags & (TCP_WSCALE_SEEN|TCP_WSCALE_FIRST))) { /* * No TCPFSM and no window scaling, so make some * extra guesses. */ if ((seq == fdata->td_maxend) && (ackskew == 0)) inseq = 1; else if (SEQ_GE(seq + maxwin, fdata->td_end - maxwin)) inseq = 1; } } /* TRACE(inseq, fdata, tdata, seq, end, ack, ackskew, win, maxwin) */ if (inseq) { /* if ackskew < 0 then this should be due to fragmented * packets. There is no way to know the length of the * total packet in advance. * We do know the total length from the fragment cache though. * Note however that there might be more sessions with * exactly the same source and destination parameters in the * state cache (and source and destination is the only stuff * that is saved in the fragment cache). Note further that * some TCP connections in the state cache are hashed with * sport and dport as well which makes it not worthwhile to * look for them. * Thus, when ackskew is negative but still seems to belong * to this session, we bump up the destinations end value. */ if (ackskew < 0) tdata->td_end = ack; /* update max window seen */ if (fdata->td_maxwin < win) fdata->td_maxwin = win; if (SEQ_GT(end, fdata->td_end)) fdata->td_end = end; if (SEQ_GE(ack + win, tdata->td_maxend)) tdata->td_maxend = ack + win; return 1; } SBUMP(ipf_state_stats.iss_oow); fin->fin_flx |= FI_OOW; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP/UDP header */ /* is(I) - pointer to master state structure */ /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ static ipstate_t * ipf_state_clone(fin, tcp, is) fr_info_t *fin; tcphdr_t *tcp; ipstate_t *is; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *clone; u_32_t send; if (softs->ipf_state_stats.iss_active == softs->ipf_state_max) { SBUMPD(ipf_state_stats, iss_max); softs->ipf_state_doflush = 1; return NULL; } KMALLOC(clone, ipstate_t *); if (clone == NULL) { SBUMPD(ipf_state_stats, iss_clone_nomem); return NULL; } bcopy((char *)is, (char *)clone, sizeof(*clone)); MUTEX_NUKE(&clone->is_lock); /* * It has not yet been placed on any timeout queue, so make sure * all of that data is zero'd out. */ clone->is_sti.tqe_pnext = NULL; clone->is_sti.tqe_next = NULL; clone->is_sti.tqe_ifq = NULL; clone->is_sti.tqe_parent = clone; clone->is_die = ONE_DAY + softc->ipf_ticks; clone->is_state[0] = 0; clone->is_state[1] = 0; send = ntohl(tcp->th_seq) + fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); if (fin->fin_rev == 1) { clone->is_dend = send; clone->is_maxdend = send; clone->is_send = 0; clone->is_maxswin = 1; clone->is_maxdwin = ntohs(tcp->th_win); if (clone->is_maxdwin == 0) clone->is_maxdwin = 1; } else { clone->is_send = send; clone->is_maxsend = send; clone->is_dend = 0; clone->is_maxdwin = 1; clone->is_maxswin = ntohs(tcp->th_win); if (clone->is_maxswin == 0) clone->is_maxswin = 1; } clone->is_flags &= ~SI_CLONE; clone->is_flags |= SI_CLONED; if (ipf_state_insert(softc, clone, fin->fin_rev) == -1) { KFREE(clone); return NULL; } clone->is_ref = 1; if (clone->is_p == IPPROTO_TCP) { (void) ipf_tcp_age(&clone->is_sti, fin, softs->ipf_state_tcptq, clone->is_flags, 2); } MUTEX_EXIT(&clone->is_lock); if (is->is_flags & IS_STATESYNC) clone->is_sync = ipf_sync_new(softc, SMC_STATE, fin, clone); DT2(iss_clone, ipstate_t *, is, ipstate_t *, clone); SBUMP(ipf_state_stats.iss_cloned); return clone; } /* ------------------------------------------------------------------------ */ /* Function: ipf_matchsrcdst */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to state structure */ /* src(I) - pointer to source address */ /* dst(I) - pointer to destination address */ /* tcp(I) - pointer to TCP/UDP header */ /* cmask(I) - mask of FI_* bits to check */ /* */ /* Match a state table entry against an IP packet. The logic below is that */ /* ret gets set to one if the match succeeds, else remains 0. If it is */ /* still 0 after the test. no match. */ /* ------------------------------------------------------------------------ */ static ipstate_t * ipf_matchsrcdst(fin, is, src, dst, tcp, cmask) fr_info_t *fin; ipstate_t *is; i6addr_t *src, *dst; tcphdr_t *tcp; u_32_t cmask; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; int ret = 0, rev, out, flags, flx = 0, idx; u_short sp, dp; u_32_t cflx; void *ifp; /* * If a connection is about to be deleted, no packets * are allowed to match it. */ if (is->is_sti.tqe_ifq == &softs->ipf_state_deletetq) return NULL; rev = IP6_NEQ(&is->is_dst, dst); ifp = fin->fin_ifp; out = fin->fin_out; flags = is->is_flags; sp = 0; dp = 0; if (tcp != NULL) { sp = htons(fin->fin_sport); dp = ntohs(fin->fin_dport); } if (!rev) { if (tcp != NULL) { if (!(flags & SI_W_SPORT) && (sp != is->is_sport)) rev = 1; else if (!(flags & SI_W_DPORT) && (dp != is->is_dport)) rev = 1; } } idx = (out << 1) + rev; /* * If the interface for this 'direction' is set, make sure it matches. * An interface name that is not set matches any, as does a name of *. */ if ((is->is_ifp[idx] == ifp) || (is->is_ifp[idx] == NULL && (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '-' || *is->is_ifname[idx] == '*'))) ret = 1; if (ret == 0) { DT2(iss_lookup_badifp, fr_info_t *, fin, ipstate_t *, is); SBUMP(ipf_state_stats.iss_lookup_badifp); /* TRACE is, out, rev, idx */ return NULL; } ret = 0; /* * Match addresses and ports. */ if (rev == 0) { if ((IP6_EQ(&is->is_dst, dst) || (flags & SI_W_DADDR)) && (IP6_EQ(&is->is_src, src) || (flags & SI_W_SADDR))) { if (tcp) { if ((sp == is->is_sport || flags & SI_W_SPORT) && (dp == is->is_dport || flags & SI_W_DPORT)) ret = 1; } else { ret = 1; } } } else { if ((IP6_EQ(&is->is_dst, src) || (flags & SI_W_DADDR)) && (IP6_EQ(&is->is_src, dst) || (flags & SI_W_SADDR))) { if (tcp) { if ((dp == is->is_sport || flags & SI_W_SPORT) && (sp == is->is_dport || flags & SI_W_DPORT)) ret = 1; } else { ret = 1; } } } if (ret == 0) { SBUMP(ipf_state_stats.iss_lookup_badport); DT2(iss_lookup_badport, fr_info_t *, fin, ipstate_t *, is); /* TRACE rev, is, sp, dp, src, dst */ return NULL; } /* * Whether or not this should be here, is questionable, but the aim * is to get this out of the main line. */ if (tcp == NULL) flags = is->is_flags & ~(SI_WILDP|SI_NEWFR|SI_CLONE|SI_CLONED); /* * Only one of the source or destination address can be flaged as a * wildcard. Fill in the missing address, if set. * For IPv6, if the address being copied in is multicast, then * don't reset the wild flag - multicast causes it to be set in the * first place! */ if ((flags & (SI_W_SADDR|SI_W_DADDR))) { fr_ip_t *fi = &fin->fin_fi; if ((flags & SI_W_SADDR) != 0) { if (rev == 0) { is->is_src = fi->fi_src; is->is_flags &= ~SI_W_SADDR; } else { if (!(fin->fin_flx & (FI_MULTICAST|FI_MBCAST))){ is->is_src = fi->fi_dst; is->is_flags &= ~SI_W_SADDR; } } } else if ((flags & SI_W_DADDR) != 0) { if (rev == 0) { if (!(fin->fin_flx & (FI_MULTICAST|FI_MBCAST))){ is->is_dst = fi->fi_dst; is->is_flags &= ~SI_W_DADDR; } } else { is->is_dst = fi->fi_src; is->is_flags &= ~SI_W_DADDR; } } if ((is->is_flags & (SI_WILDA|SI_WILDP)) == 0) { ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } } flx = fin->fin_flx & cmask; cflx = is->is_flx[out][rev]; /* * Match up any flags set from IP options. */ if ((cflx && (flx != (cflx & cmask))) || ((fin->fin_optmsk & is->is_optmsk[rev]) != is->is_opt[rev]) || ((fin->fin_secmsk & is->is_secmsk) != is->is_sec) || ((fin->fin_auth & is->is_authmsk) != is->is_auth)) { SBUMPD(ipf_state_stats, iss_miss_mask); return NULL; } if ((fin->fin_flx & FI_IGNORE) != 0) { fin->fin_rev = rev; return is; } /* * Only one of the source or destination port can be flagged as a * wildcard. When filling it in, fill in a copy of the matched entry * if it has the cloning flag set. */ if ((flags & (SI_W_SPORT|SI_W_DPORT))) { if ((flags & SI_CLONE) != 0) { ipstate_t *clone; clone = ipf_state_clone(fin, tcp, is); if (clone == NULL) return NULL; is = clone; } else { ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } if ((flags & SI_W_SPORT) != 0) { if (rev == 0) { is->is_sport = sp; is->is_send = ntohl(tcp->th_seq); } else { is->is_sport = dp; is->is_send = ntohl(tcp->th_ack); } is->is_maxsend = is->is_send + 1; } else if ((flags & SI_W_DPORT) != 0) { if (rev == 0) { is->is_dport = dp; is->is_dend = ntohl(tcp->th_ack); } else { is->is_dport = sp; is->is_dend = ntohl(tcp->th_seq); } is->is_maxdend = is->is_dend + 1; } is->is_flags &= ~(SI_W_SPORT|SI_W_DPORT); if ((flags & SI_CLONED) && softs->ipf_state_logging) ipf_state_log(softc, is, ISL_CLONE); } ret = -1; if (is->is_flx[out][rev] == 0) { is->is_flx[out][rev] = flx; if (rev == 1 && is->is_optmsk[1] == 0) { is->is_opt[1] = fin->fin_optmsk; is->is_optmsk[1] = 0xffffffff; if (is->is_v == 6) { is->is_opt[1] &= ~0x8; is->is_optmsk[1] &= ~0x8; } } } /* * Check if the interface name for this "direction" is set and if not, * fill it in. */ if (is->is_ifp[idx] == NULL && (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '*')) { is->is_ifp[idx] = ifp; COPYIFNAME(fin->fin_v, ifp, is->is_ifname[idx]); } fin->fin_rev = rev; return is; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkicmpmatchingstate */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* If we've got an ICMP error message, using the information stored in the */ /* ICMP packet, look for a matching state table entry. */ /* */ /* If we return NULL then no lock on ipf_state is held. */ /* If we return non-null then a read-lock on ipf_state is held. */ /* ------------------------------------------------------------------------ */ static ipstate_t * ipf_checkicmpmatchingstate(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, **isp; i6addr_t dst, src; struct icmp *ic; u_short savelen; icmphdr_t *icmp; fr_info_t ofin; tcphdr_t *tcp; int type, len; u_char pr; ip_t *oip; u_int hv; /* * Does it at least have the return (basic) IP header ? * Is it an actual recognised ICMP error type? * Only a basic IP header (no options) should be with * an ICMP error header. */ if ((fin->fin_v != 4) || (fin->fin_hlen != sizeof(ip_t)) || (fin->fin_plen < ICMPERR_MINPKTLEN) || !(fin->fin_flx & FI_ICMPERR)) { SBUMPD(ipf_state_stats, iss_icmp_bad); return NULL; } ic = fin->fin_dp; type = ic->icmp_type; oip = (ip_t *)((char *)ic + ICMPERR_ICMPHLEN); /* * Check if the at least the old IP header (with options) and * 8 bytes of payload is present. */ if (fin->fin_plen < ICMPERR_MAXPKTLEN + ((IP_HL(oip) - 5) << 2)) { SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_1); return NULL; } /* * Sanity Checks. */ len = fin->fin_dlen - ICMPERR_ICMPHLEN; if ((len <= 0) || ((IP_HL(oip) << 2) > len)) { DT2(iss_icmp_len, fr_info_t *, fin, struct ip*, oip); SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_1); return NULL; } /* * Is the buffer big enough for all of it ? It's the size of the IP * header claimed in the encapsulated part which is of concern. It * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we * do the pullup early in ipf_check() and thus can't guarantee it is * all here now. */ #ifdef _KERNEL { mb_t *m; m = fin->fin_m; # if defined(MENTAT) if ((char *)oip + len > (char *)m->b_wptr) { SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_2); return NULL; } # else if ((char *)oip + len > (char *)fin->fin_ip + m->m_len) { SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_3); return NULL; } # endif } #endif bcopy((char *)fin, (char *)&ofin, sizeof(*fin)); /* * in the IPv4 case we must zero the i6addr union otherwise * the IP6_EQ and IP6_NEQ macros produce the wrong results because * of the 'junk' in the unused part of the union */ bzero((char *)&src, sizeof(src)); bzero((char *)&dst, sizeof(dst)); /* * we make an fin entry to be able to feed it to * matchsrcdst note that not all fields are encessary * but this is the cleanest way. Note further we fill * in fin_mp such that if someone uses it we'll get * a kernel panic. ipf_matchsrcdst does not use this. * * watch out here, as ip is in host order and oip in network * order. Any change we make must be undone afterwards, like * oip->ip_len. */ savelen = oip->ip_len; oip->ip_len = htons(len); ofin.fin_flx = FI_NOCKSUM; ofin.fin_v = 4; ofin.fin_ip = oip; ofin.fin_m = NULL; /* if dereferenced, panic XXX */ ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ (void) ipf_makefrip(IP_HL(oip) << 2, oip, &ofin); ofin.fin_ifp = fin->fin_ifp; ofin.fin_out = !fin->fin_out; hv = (pr = oip->ip_p); src.in4 = oip->ip_src; hv += src.in4.s_addr; dst.in4 = oip->ip_dst; hv += dst.in4.s_addr; /* * Reset the short and bad flag here because in ipf_matchsrcdst() * the flags for the current packet (fin_flx) are compared against * those for the existing session. */ ofin.fin_flx &= ~(FI_BAD|FI_SHORT); /* * Put old values of ip_len back as we don't know * if we have to forward the packet or process it again. */ oip->ip_len = savelen; switch (oip->ip_p) { case IPPROTO_ICMP : /* * an ICMP error can only be generated as a result of an * ICMP query, not as the response on an ICMP error * * XXX theoretically ICMP_ECHOREP and the other reply's are * ICMP query's as well, but adding them here seems strange XXX */ if ((ofin.fin_flx & FI_ICMPERR) != 0) { DT1(iss_icmp_icmperr, fr_info_t *, &ofin); SBUMP(ipf_state_stats.iss_icmp_icmperr); return NULL; } /* * perform a lookup of the ICMP packet in the state table */ icmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); hv += icmp->icmp_id; hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != 4)) continue; if (is->is_pass & FR_NOICMPERR) continue; is = ipf_matchsrcdst(&ofin, is, &src, &dst, NULL, FI_ICMPCMP); if ((is != NULL) && !ipf_allowstateicmp(fin, is, &src)) return is; } RWLOCK_EXIT(&softc->ipf_state); SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_1); return NULL; case IPPROTO_TCP : case IPPROTO_UDP : break; default : SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_2); return NULL; } tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); hv += tcp->th_dport;; hv += tcp->th_sport;; hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; /* * Only allow this icmp though if the * encapsulated packet was allowed through the * other way around. Note that the minimal amount * of info present does not allow for checking against * tcp internals such as seq and ack numbers. Only the * ports are known to be present and can be even if the * short flag is set. */ if ((is->is_p == pr) && (is->is_v == 4) && (is = ipf_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP))) { if (ipf_allowstateicmp(fin, is, &src) == 0) return is; } } RWLOCK_EXIT(&softc->ipf_state); SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_3); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_allowstateicmp */ /* Returns: int - 1 = packet denied, 0 = packet allowed */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to state table entry */ /* src(I) - source address to check permission for */ /* */ /* For an ICMP packet that has so far matched a state table entry, check if */ /* there are any further refinements that might mean we want to block this */ /* packet. This code isn't specific to either IPv4 or IPv6. */ /* ------------------------------------------------------------------------ */ static int ipf_allowstateicmp(fin, is, src) fr_info_t *fin; ipstate_t *is; i6addr_t *src; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; frentry_t *savefr; frentry_t *fr; u_32_t ipass; int backward; int oi; int i; fr = is->is_rule; if (fr != NULL && fr->fr_icmpgrp != NULL) { savefr = fin->fin_fr; fin->fin_fr = fr->fr_icmpgrp->fg_start; ipass = ipf_scanlist(fin, softc->ipf_pass); fin->fin_fr = savefr; if (FR_ISBLOCK(ipass)) { SBUMPD(ipf_state_stats, iss_icmp_headblock); return 1; } } /* * i : the index of this packet (the icmp unreachable) * oi : the index of the original packet found in the * icmp header (i.e. the packet causing this icmp) * backward : original packet was backward compared to * the state */ backward = IP6_NEQ(&is->is_src, src); fin->fin_rev = !backward; i = (!backward << 1) + fin->fin_out; oi = (backward << 1) + !fin->fin_out; if (is->is_pass & FR_NOICMPERR) { SBUMPD(ipf_state_stats, iss_icmp_banned); return 1; } if (is->is_icmppkts[i] > is->is_pkts[oi]) { SBUMPD(ipf_state_stats, iss_icmp_toomany); return 1; } DT2(iss_icmp_hits, fr_info_t *, fin, ipstate_t *, is); SBUMP(ipf_state_stats.iss_icmp_hits); is->is_icmppkts[i]++; /* * we deliberately do not touch the timeouts * for the accompanying state table entry. * It remains to be seen if that is correct. XXX */ return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ipsmove */ /* Returns: Nil */ /* Parameters: is(I) - pointer to state table entry */ /* hv(I) - new hash value for state table entry */ /* Write Locks: ipf_state */ /* */ /* Move a state entry from one position in the hash table to another. */ /* ------------------------------------------------------------------------ */ static void ipf_ipsmove(softs, is, hv) ipf_state_softc_t *softs; ipstate_t *is; u_int hv; { ipstate_t **isp; u_int hvm; hvm = is->is_hv; /* TRACE is, is_hv, hvm */ /* * Remove the hash from the old location... */ isp = is->is_phnext; if (is->is_hnext) is->is_hnext->is_phnext = isp; *isp = is->is_hnext; if (softs->ipf_state_table[hvm] == NULL) softs->ipf_state_stats.iss_inuse--; softs->ipf_state_stats.iss_bucketlen[hvm]--; /* * ...and put the hash in the new one. */ hvm = DOUBLE_HASH(hv); is->is_hv = hvm; /* TRACE is, hv, is_hv, hvm */ isp = &softs->ipf_state_table[hvm]; if (*isp) (*isp)->is_phnext = &is->is_hnext; else softs->ipf_state_stats.iss_inuse++; softs->ipf_state_stats.iss_bucketlen[hvm]++; is->is_phnext = isp; is->is_hnext = *isp; *isp = is; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_lookup */ /* Returns: ipstate_t* - NULL == no matching state found, */ /* else pointer to state information is returned */ /* Parameters: fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP/UDP header. */ /* ifqp(O) - pointer for storing tailq timeout */ /* */ /* Search the state table for a matching entry to the packet described by */ /* the contents of *fin. For certain protocols, when a match is found the */ /* timeout queue is also selected and stored in ifpq if it is non-NULL. */ /* */ /* If we return NULL then no lock on ipf_state is held. */ /* If we return non-null then a read-lock on ipf_state is held. */ /* ------------------------------------------------------------------------ */ ipstate_t * ipf_state_lookup(fin, tcp, ifqp) fr_info_t *fin; tcphdr_t *tcp; ipftq_t **ifqp; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; u_int hv, hvm, pr, v, tryagain; ipstate_t *is, **isp; u_short dport, sport; i6addr_t src, dst; struct icmp *ic; ipftq_t *ifq; int oow; is = NULL; ifq = NULL; tcp = fin->fin_dp; ic = (struct icmp *)tcp; hv = (pr = fin->fin_fi.fi_p); src = fin->fin_fi.fi_src; dst = fin->fin_fi.fi_dst; hv += src.in4.s_addr; hv += dst.in4.s_addr; v = fin->fin_fi.fi_v; #ifdef USE_INET6 if (v == 6) { hv += fin->fin_fi.fi_src.i6[1]; hv += fin->fin_fi.fi_src.i6[2]; hv += fin->fin_fi.fi_src.i6[3]; if ((fin->fin_p == IPPROTO_ICMPV6) && IN6_IS_ADDR_MULTICAST(&fin->fin_fi.fi_dst.in6)) { hv -= dst.in4.s_addr; } else { hv += fin->fin_fi.fi_dst.i6[1]; hv += fin->fin_fi.fi_dst.i6[2]; hv += fin->fin_fi.fi_dst.i6[3]; } } #endif if ((v == 4) && (fin->fin_flx & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST))) { if (fin->fin_out == 0) { hv -= src.in4.s_addr; } else { hv -= dst.in4.s_addr; } } /* TRACE fin_saddr, fin_daddr, hv */ /* * Search the hash table for matching packet header info. */ switch (pr) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : tryagain = 0; if (v == 6) { if ((ic->icmp_type == ICMP6_ECHO_REQUEST) || (ic->icmp_type == ICMP6_ECHO_REPLY)) { hv += ic->icmp_id; } } READ_ENTER(&softc->ipf_state); icmp6again: hvm = DOUBLE_HASH(hv); for (isp = &softs->ipf_state_table[hvm]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if (is != NULL && ipf_matchicmpqueryreply(v, &is->is_icmp, ic, fin->fin_rev)) { if (fin->fin_rev) ifq = &softs->ipf_state_icmpacktq; else ifq = &softs->ipf_state_icmptq; break; } } if (is != NULL) { if ((tryagain != 0) && !(is->is_flags & SI_W_DADDR)) { hv += fin->fin_fi.fi_src.i6[0]; hv += fin->fin_fi.fi_src.i6[1]; hv += fin->fin_fi.fi_src.i6[2]; hv += fin->fin_fi.fi_src.i6[3]; ipf_ipsmove(softs, is, hv); MUTEX_DOWNGRADE(&softc->ipf_state); } break; } RWLOCK_EXIT(&softc->ipf_state); /* * No matching icmp state entry. Perhaps this is a * response to another state entry. * * XXX With some ICMP6 packets, the "other" address is already * in the packet, after the ICMP6 header, and this could be * used in place of the multicast address. However, taking * advantage of this requires some significant code changes * to handle the specific types where that is the case. */ if ((softs->ipf_state_stats.iss_wild != 0) && ((fin->fin_flx & FI_NOWILD) == 0) && (v == 6) && (tryagain == 0)) { hv -= fin->fin_fi.fi_src.i6[0]; hv -= fin->fin_fi.fi_src.i6[1]; hv -= fin->fin_fi.fi_src.i6[2]; hv -= fin->fin_fi.fi_src.i6[3]; tryagain = 1; WRITE_ENTER(&softc->ipf_state); goto icmp6again; } is = ipf_checkicmp6matchingstate(fin); if (is != NULL) return is; break; #endif case IPPROTO_ICMP : if (v == 4) { hv += ic->icmp_id; } hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if ((is != NULL) && (ic->icmp_id == is->is_icmp.ici_id) && ipf_matchicmpqueryreply(v, &is->is_icmp, ic, fin->fin_rev)) { if (fin->fin_rev) ifq = &softs->ipf_state_icmpacktq; else ifq = &softs->ipf_state_icmptq; break; } } if (is == NULL) { RWLOCK_EXIT(&softc->ipf_state); } break; case IPPROTO_TCP : case IPPROTO_UDP : ifqp = NULL; sport = htons(fin->fin_data[0]); hv += sport; dport = htons(fin->fin_data[1]); hv += dport; oow = 0; tryagain = 0; READ_ENTER(&softc->ipf_state); retry_tcpudp: hvm = DOUBLE_HASH(hv); /* TRACE hv, hvm */ for (isp = &softs->ipf_state_table[hvm]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; fin->fin_flx &= ~FI_OOW; is = ipf_matchsrcdst(fin, is, &src, &dst, tcp, FI_CMP); if (is != NULL) { if (pr == IPPROTO_TCP) { if (!ipf_state_tcp(softc, softs, fin, tcp, is)) { oow |= fin->fin_flx & FI_OOW; continue; } } break; } } if (is != NULL) { if (tryagain && !(is->is_flags & (SI_CLONE|SI_WILDP|SI_WILDA))) { hv += dport; hv += sport; ipf_ipsmove(softs, is, hv); MUTEX_DOWNGRADE(&softc->ipf_state); } break; } RWLOCK_EXIT(&softc->ipf_state); if ((softs->ipf_state_stats.iss_wild != 0) && ((fin->fin_flx & FI_NOWILD) == 0)) { if (tryagain == 0) { hv -= dport; hv -= sport; } else if (tryagain == 1) { hv = fin->fin_fi.fi_p; /* * If we try to pretend this is a reply to a * multicast/broadcast packet then we need to * exclude part of the address from the hash * calculation. */ if (fin->fin_out == 0) { hv += src.in4.s_addr; } else { hv += dst.in4.s_addr; } hv += dport; hv += sport; } tryagain++; if (tryagain <= 2) { WRITE_ENTER(&softc->ipf_state); goto retry_tcpudp; } } fin->fin_flx |= oow; break; #if 0 case IPPROTO_GRE : gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) { hv += gre->gr_call; } /* FALLTHROUGH */ #endif default : ifqp = NULL; hvm = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hvm]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if (is != NULL) { ifq = &softs->ipf_state_iptq; break; } } if (is == NULL) { RWLOCK_EXIT(&softc->ipf_state); } break; } if (is != NULL) { if (((is->is_sti.tqe_flags & TQE_RULEBASED) != 0) && (is->is_tqehead[fin->fin_rev] != NULL)) ifq = is->is_tqehead[fin->fin_rev]; if (ifq != NULL && ifqp != NULL) *ifqp = ifq; } else { SBUMP(ipf_state_stats.iss_lookup_miss); } return is; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_check */ /* Returns: frentry_t* - NULL == search failed, */ /* else pointer to rule for matching state */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check if a packet is associated with an entry in the state table. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_state_check(fin, passp) fr_info_t *fin; u_32_t *passp; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; ipftqent_t *tqe; ipstate_t *is; frentry_t *fr; tcphdr_t *tcp; ipftq_t *ifq; u_int pass; int inout; if (softs->ipf_state_lock || (softs->ipf_state_list == NULL)) return NULL; if (fin->fin_flx & (FI_SHORT|FI_FRAGBODY|FI_BAD)) { SBUMPD(ipf_state_stats, iss_check_bad); return NULL; } if ((fin->fin_flx & FI_TCPUDP) || (fin->fin_fi.fi_p == IPPROTO_ICMP) #ifdef USE_INET6 || (fin->fin_fi.fi_p == IPPROTO_ICMPV6) #endif ) tcp = fin->fin_dp; else tcp = NULL; ifq = NULL; /* * Search the hash table for matching packet header info. */ is = ipf_state_lookup(fin, tcp, &ifq); switch (fin->fin_p) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : if (is != NULL) break; if (fin->fin_v == 6) { is = ipf_checkicmp6matchingstate(fin); } break; #endif case IPPROTO_ICMP : if (is != NULL) break; /* * No matching icmp state entry. Perhaps this is a * response to another state entry. */ is = ipf_checkicmpmatchingstate(fin); break; case IPPROTO_TCP : if (is == NULL) break; if (is->is_pass & FR_NEWISN) { if (fin->fin_out == 0) ipf_fixinisn(fin, is); else if (fin->fin_out == 1) ipf_fixoutisn(fin, is); } break; default : if (fin->fin_rev) ifq = &softs->ipf_state_udpacktq; else ifq = &softs->ipf_state_udptq; break; } if (is == NULL) { SBUMP(ipf_state_stats.iss_check_miss); return NULL; } fr = is->is_rule; if (fr != NULL) { if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { if (fin->fin_nattag == NULL) { RWLOCK_EXIT(&softc->ipf_state); SBUMPD(ipf_state_stats, iss_check_notag); return NULL; } if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag)!=0) { RWLOCK_EXIT(&softc->ipf_state); SBUMPD(ipf_state_stats, iss_check_nattag); return NULL; } } (void) strncpy(fin->fin_group, FR_NAME(fr, fr_group), FR_GROUPLEN); fin->fin_icode = fr->fr_icode; } fin->fin_rule = is->is_rulen; fin->fin_fr = fr; /* * If this packet is a fragment and the rule says to track fragments, * then create a new fragment cache entry. */ if (fin->fin_flx & FI_FRAG && FR_ISPASS(is->is_pass) && is->is_pass & FR_KEEPFRAG) (void) ipf_frag_new(softc, fin, is->is_pass); /* * For TCP packets, ifq == NULL. For all others, check if this new * queue is different to the last one it was on and move it if so. */ tqe = &is->is_sti; if ((tqe->tqe_flags & TQE_RULEBASED) != 0) ifq = is->is_tqehead[fin->fin_rev]; MUTEX_ENTER(&is->is_lock); if (ifq != NULL) ipf_movequeue(softc->ipf_ticks, tqe, tqe->tqe_ifq, ifq); inout = (fin->fin_rev << 1) + fin->fin_out; is->is_pkts[inout]++; is->is_bytes[inout] += fin->fin_plen; fin->fin_pktnum = is->is_pkts[inout] + is->is_icmppkts[inout]; MUTEX_EXIT(&is->is_lock); pass = is->is_pass; if (is->is_flags & IS_STATESYNC) ipf_sync_update(softc, SMC_STATE, fin, is->is_sync); RWLOCK_EXIT(&softc->ipf_state); SBUMP(ipf_state_stats.iss_hits); fin->fin_dif = &is->is_dif; fin->fin_tif = &is->is_tifs[fin->fin_rev]; fin->fin_flx |= FI_STATE; if ((pass & FR_LOGFIRST) != 0) pass &= ~(FR_LOGFIRST|FR_LOG); *passp = pass; return fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_fixoutisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* */ /* Called only for outbound packets, adjusts the sequence number and the */ /* TCP checksum to match that change. */ /* ------------------------------------------------------------------------ */ static void ipf_fixoutisn(fin, is) fr_info_t *fin; ipstate_t *is; { tcphdr_t *tcp; int rev; u_32_t seq; tcp = fin->fin_dp; rev = fin->fin_rev; if ((is->is_flags & IS_ISNSYN) != 0) { if ((rev == 0) && (fin->fin_cksum < FI_CK_L4PART)) { seq = ntohl(tcp->th_seq); seq += is->is_isninc[0]; tcp->th_seq = htonl(seq); ipf_fix_outcksum(0, &tcp->th_sum, is->is_sumd[0], 0); } } if ((is->is_flags & IS_ISNACK) != 0) { if ((rev == 1) && (fin->fin_cksum < FI_CK_L4PART)) { seq = ntohl(tcp->th_seq); seq += is->is_isninc[1]; tcp->th_seq = htonl(seq); ipf_fix_outcksum(0, &tcp->th_sum, is->is_sumd[1], 0); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_fixinisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* */ /* Called only for inbound packets, adjusts the acknowledge number and the */ /* TCP checksum to match that change. */ /* ------------------------------------------------------------------------ */ static void ipf_fixinisn(fin, is) fr_info_t *fin; ipstate_t *is; { tcphdr_t *tcp; int rev; u_32_t ack; tcp = fin->fin_dp; rev = fin->fin_rev; if ((is->is_flags & IS_ISNSYN) != 0) { if ((rev == 1) && (fin->fin_cksum < FI_CK_L4PART)) { ack = ntohl(tcp->th_ack); ack -= is->is_isninc[0]; tcp->th_ack = htonl(ack); ipf_fix_incksum(0, &tcp->th_sum, is->is_sumd[0], 0); } } if ((is->is_flags & IS_ISNACK) != 0) { if ((rev == 0) && (fin->fin_cksum < FI_CK_L4PART)) { ack = ntohl(tcp->th_ack); ack -= is->is_isninc[1]; tcp->th_ack = htonl(ack); ipf_fix_incksum(0, &tcp->th_sum, is->is_sumd[1], 0); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_sync */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ifp(I) - pointer to interface */ /* */ /* Walk through all state entries and if an interface pointer match is */ /* found then look it up again, based on its name in case the pointer has */ /* changed since last time. */ /* */ /* If ifp is passed in as being non-null then we are only doing updates for */ /* existing, matching, uses of it. */ /* ------------------------------------------------------------------------ */ void ipf_state_sync(softc, ifp) ipf_main_softc_t *softc; void *ifp; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is; int i; if (softc->ipf_running <= 0) return; WRITE_ENTER(&softc->ipf_state); if (softc->ipf_running <= 0) { RWLOCK_EXIT(&softc->ipf_state); return; } for (is = softs->ipf_state_list; is; is = is->is_next) { /* * Look up all the interface names in the state entry. */ for (i = 0; i < FR_NUM(is->is_ifp); i++) { if (ifp == NULL || ifp == is->is_ifp[i]) is->is_ifp[i] = ipf_resolvenic(softc, is->is_ifname[i], is->is_v); } } RWLOCK_EXIT(&softc->ipf_state); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_del */ /* Returns: int - 0 = deleted, else refernce count on active struct */ /* Parameters: softc(I) - pointer to soft context main structure */ /* is(I) - pointer to state structure to delete */ /* why(I) - if not 0, log reason why it was deleted */ /* Write Locks: ipf_state */ /* */ /* Deletes a state entry from the enumerated list as well as the hash table */ /* and timeout queue lists. Make adjustments to hash table statistics and */ /* global counters as required. */ /* ------------------------------------------------------------------------ */ static int ipf_state_del(softc, is, why) ipf_main_softc_t *softc; ipstate_t *is; int why; { ipf_state_softc_t *softs = softc->ipf_state_soft; int orphan = 1; frentry_t *fr; /* * Since we want to delete this, remove it from the state table, * where it can be found & used, first. */ if (is->is_phnext != NULL) { *is->is_phnext = is->is_hnext; if (is->is_hnext != NULL) is->is_hnext->is_phnext = is->is_phnext; if (softs->ipf_state_table[is->is_hv] == NULL) softs->ipf_state_stats.iss_inuse--; softs->ipf_state_stats.iss_bucketlen[is->is_hv]--; is->is_phnext = NULL; is->is_hnext = NULL; orphan = 0; } /* * Because ipf_state_stats.iss_wild is a count of entries in the state * table that have wildcard flags set, only decerement it once * and do it here. */ if (is->is_flags & (SI_WILDP|SI_WILDA)) { if (!(is->is_flags & SI_CLONED)) { ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } is->is_flags &= ~(SI_WILDP|SI_WILDA); } /* * Next, remove it from the timeout queue it is in. */ if (is->is_sti.tqe_ifq != NULL) ipf_deletequeueentry(&is->is_sti); /* * If it is still in use by something else, do not go any further, * but note that at this point it is now an orphan. How can this * be? ipf_state_flush() calls ipf_delete() directly because it wants * to empty the table out and if something has a hold on a state * entry (such as ipfstat), it'll do the deref path that'll bring * us back here to do the real delete & free. */ MUTEX_ENTER(&is->is_lock); if (is->is_me != NULL) { *is->is_me = NULL; is->is_me = NULL; is->is_ref--; } is->is_ref--; if (is->is_ref > 0) { int refs; refs = is->is_ref; MUTEX_EXIT(&is->is_lock); if (!orphan) softs->ipf_state_stats.iss_orphan++; return refs; } fr = is->is_rule; is->is_rule = NULL; if (fr != NULL) { if (fr->fr_srctrack.ht_max_nodes != 0) { (void) ipf_ht_node_del(&fr->fr_srctrack, is->is_family, &is->is_src); } } ASSERT(is->is_ref == 0); MUTEX_EXIT(&is->is_lock); if (is->is_tqehead[0] != NULL) { if (ipf_deletetimeoutqueue(is->is_tqehead[0]) == 0) ipf_freetimeoutqueue(softc, is->is_tqehead[0]); } if (is->is_tqehead[1] != NULL) { if (ipf_deletetimeoutqueue(is->is_tqehead[1]) == 0) ipf_freetimeoutqueue(softc, is->is_tqehead[1]); } if (is->is_sync) ipf_sync_del_state(softc->ipf_sync_soft, is->is_sync); /* * Now remove it from the linked list of known states */ if (is->is_pnext != NULL) { *is->is_pnext = is->is_next; if (is->is_next != NULL) is->is_next->is_pnext = is->is_pnext; is->is_pnext = NULL; is->is_next = NULL; } if (softs->ipf_state_logging != 0 && why != 0) ipf_state_log(softc, is, why); if (is->is_p == IPPROTO_TCP) softs->ipf_state_stats.iss_fin++; else softs->ipf_state_stats.iss_expire++; if (orphan) softs->ipf_state_stats.iss_orphan--; if (fr != NULL) { fr->fr_statecnt--; (void) ipf_derefrule(softc, &fr); } softs->ipf_state_stats.iss_active_proto[is->is_p]--; MUTEX_DESTROY(&is->is_lock); KFREE(is); softs->ipf_state_stats.iss_active--; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Slowly expire held state for thingslike UDP and ICMP. The algorithm */ /* used here is to keep the queue sorted with the oldest things at the top */ /* and the youngest at the bottom. So if the top one doesn't need to be */ /* expired then neither will any under it. */ /* ------------------------------------------------------------------------ */ void ipf_state_expire(softc) ipf_main_softc_t *softc; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; ipstate_t *is; SPL_INT(s); SPL_NET(s); WRITE_ENTER(&softc->ipf_state); for (ifq = softs->ipf_state_tcptq; ifq != NULL; ifq = ifq->ifq_next) for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; is = tqe->tqe_parent; ipf_state_del(softc, is, ISL_EXPIRE); } for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; is = tqe->tqe_parent; ipf_state_del(softc, is, ISL_EXPIRE); } } for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (((ifq->ifq_flags & IFQF_DELETE) != 0) && (ifq->ifq_ref == 0)) { ipf_freetimeoutqueue(softc, ifq); } } if (softs->ipf_state_doflush) { (void) ipf_state_flush(softc, 2, 0); softs->ipf_state_doflush = 0; softs->ipf_state_wm_last = softc->ipf_ticks; } RWLOCK_EXIT(&softc->ipf_state); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_flush */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* which(I) - which flush action to perform */ /* proto(I) - which protocol to flush (0 == ALL) */ /* Write Locks: ipf_state */ /* */ /* Flush state tables. Three actions currently defined: */ /* which == 0 : flush all state table entries */ /* which == 1 : flush TCP connections which have started to close but are */ /* stuck for some reason. */ /* which == 2 : flush TCP connections which have been idle for a long time, */ /* starting at > 4 days idle and working back in successive half-*/ /* days to at most 12 hours old. If this fails to free enough */ /* slots then work backwards in half hour slots to 30 minutes. */ /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ int ipf_state_flush(softc, which, proto) ipf_main_softc_t *softc; int which, proto; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipftqent_t *tqe, *tqn; ipstate_t *is, **isp; ipftq_t *ifq; int removed; SPL_INT(s); removed = 0; SPL_NET(s); switch (which) { case 0 : SBUMP(ipf_state_stats.iss_flush_all); /* * Style 0 flush removes everything... */ for (isp = &softs->ipf_state_list; ((is = *isp) != NULL); ) { if ((proto != 0) && (is->is_v != proto)) { isp = &is->is_next; continue; } if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; else isp = &is->is_next; } break; case 1 : SBUMP(ipf_state_stats.iss_flush_closing); /* * Since we're only interested in things that are closing, * we can start with the appropriate timeout queue. */ for (ifq = softs->ipf_state_tcptq + IPF_TCPS_CLOSE_WAIT; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; is = tqe->tqe_parent; if (is->is_p != IPPROTO_TCP) break; if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } } /* * Also need to look through the user defined queues. */ for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; is = tqe->tqe_parent; if (is->is_p != IPPROTO_TCP) continue; if ((is->is_state[0] > IPF_TCPS_ESTABLISHED) && (is->is_state[1] > IPF_TCPS_ESTABLISHED)) { if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } } } break; case 2 : break; /* * Args 5-11 correspond to flushing those particular states * for TCP connections. */ case IPF_TCPS_CLOSE_WAIT : case IPF_TCPS_FIN_WAIT_1 : case IPF_TCPS_CLOSING : case IPF_TCPS_LAST_ACK : case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : SBUMP(ipf_state_stats.iss_flush_queue); tqn = softs->ipf_state_tcptq[which].ifq_head; while (tqn != NULL) { tqe = tqn; tqn = tqe->tqe_next; is = tqe->tqe_parent; if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } break; default : if (which < 30) break; SBUMP(ipf_state_stats.iss_flush_state); /* * Take a large arbitrary number to mean the number of seconds * for which which consider to be the maximum value we'll allow * the expiration to be. */ which = IPF_TTLVAL(which); for (isp = &softs->ipf_state_list; ((is = *isp) != NULL); ) { if ((proto == 0) || (is->is_v == proto)) { if (softc->ipf_ticks - is->is_touched > which) { if (ipf_state_del(softc, is, ISL_FLUSH) == 0) { removed++; continue; } } } isp = &is->is_next; } break; } if (which != 2) { SPL_X(s); return removed; } SBUMP(ipf_state_stats.iss_flush_timeout); /* * Asked to remove inactive entries because the table is full, try * again, 3 times, if first attempt failed with a different criteria * each time. The order tried in must be in decreasing age. * Another alternative is to implement random drop and drop N entries * at random until N have been freed up. */ if (softc->ipf_ticks - softs->ipf_state_wm_last > softs->ipf_state_wm_freq) { removed = ipf_queueflush(softc, ipf_state_flush_entry, softs->ipf_state_tcptq, softs->ipf_state_usertq, &softs->ipf_state_stats.iss_active, softs->ipf_state_size, softs->ipf_state_wm_low); softs->ipf_state_wm_last = softc->ipf_ticks; } SPL_X(s); return removed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_flush_entry */ /* Returns: int - 0 = entry deleted, else not deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* entry(I) - pointer to state structure to delete */ /* Write Locks: ipf_state */ /* */ /* This function is a stepping stone between ipf_queueflush() and */ /* ipf_state_del(). It is used so we can provide a uniform interface via */ /* the ipf_queueflush() function. */ /* ------------------------------------------------------------------------ */ static int ipf_state_flush_entry(softc, entry) ipf_main_softc_t *softc; void *entry; { return ipf_state_del(softc, entry, ISL_FLUSH); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tcp_age */ /* Returns: int - 1 == state transition made, 0 == no change (rejected) */ /* Parameters: tqe(I) - pointer to timeout queue information */ /* fin(I) - pointer to packet information */ /* tqtab(I) - TCP timeout queue table this is in */ /* flags(I) - flags from state/NAT entry */ /* ok(I) - can we advance state */ /* */ /* Rewritten by Arjan de Vet , 2000-07-29: */ /* */ /* - (try to) base state transitions on real evidence only, */ /* i.e. packets that are sent and have been received by ipfilter; */ /* diagram 18.12 of TCP/IP volume 1 by W. Richard Stevens was used. */ /* */ /* - deal with half-closed connections correctly; */ /* */ /* - store the state of the source in state[0] such that ipfstat */ /* displays the state as source/dest instead of dest/source; the calls */ /* to ipf_tcp_age have been changed accordingly. */ /* */ /* Internal Parameters: */ /* */ /* state[0] = state of source (host that initiated connection) */ /* state[1] = state of dest (host that accepted the connection) */ /* */ /* dir == 0 : a packet from source to dest */ /* dir == 1 : a packet from dest to source */ /* */ /* A typical procession for a connection is as follows: */ /* */ /* +--------------+-------------------+ */ /* | Side '0' | Side '1' | */ /* +--------------+-------------------+ */ /* | 0 -> 1 (SYN) | | */ /* | | 0 -> 2 (SYN-ACK) | */ /* | 1 -> 3 (ACK) | | */ /* | | 2 -> 4 (ACK-PUSH) | */ /* | 3 -> 4 (ACK) | | */ /* | ... | ... | */ /* | | 4 -> 6 (FIN-ACK) | */ /* | 4 -> 5 (ACK) | | */ /* | | 6 -> 6 (ACK-PUSH) | */ /* | 5 -> 5 (ACK) | | */ /* | 5 -> 8 (FIN) | | */ /* | | 6 -> 10 (ACK) | */ /* +--------------+-------------------+ */ /* */ /* Locking: it is assumed that the parent of the tqe structure is locked. */ /* ------------------------------------------------------------------------ */ int ipf_tcp_age(tqe, fin, tqtab, flags, ok) ipftqent_t *tqe; fr_info_t *fin; ipftq_t *tqtab; int flags, ok; { ipf_main_softc_t *softc = fin->fin_main_soft; int dlen, ostate, nstate, rval, dir; u_char tcpflags; tcphdr_t *tcp; tcp = fin->fin_dp; rval = 0; dir = fin->fin_rev; tcpflags = tcp->th_flags; dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); ostate = tqe->tqe_state[1 - dir]; nstate = tqe->tqe_state[dir]; if (tcpflags & TH_RST) { if (!(tcpflags & TH_PUSH) && !dlen) nstate = IPF_TCPS_CLOSED; else nstate = IPF_TCPS_CLOSE_WAIT; if (ostate <= IPF_TCPS_ESTABLISHED) { tqe->tqe_state[1 - dir] = IPF_TCPS_CLOSE_WAIT; } rval = 1; } else { switch (nstate) { case IPF_TCPS_LISTEN: /* 0 */ if ((tcpflags & TH_OPENING) == TH_OPENING) { /* * 'dir' received an S and sends SA in * response, LISTEN -> SYN_RECEIVED */ nstate = IPF_TCPS_SYN_RECEIVED; rval = 1; } else if ((tcpflags & TH_OPENING) == TH_SYN) { /* 'dir' sent S, LISTEN -> SYN_SENT */ nstate = IPF_TCPS_SYN_SENT; rval = 1; } /* * the next piece of code makes it possible to get * already established connections into the state table * after a restart or reload of the filter rules; this * does not work when a strict 'flags S keep state' is * used for tcp connections of course */ if (((flags & IS_TCPFSM) == 0) && ((tcpflags & TH_ACKMASK) == TH_ACK)) { /* * we saw an A, guess 'dir' is in ESTABLISHED * mode */ switch (ostate) { case IPF_TCPS_LISTEN : case IPF_TCPS_SYN_RECEIVED : nstate = IPF_TCPS_HALF_ESTAB; rval = 1; break; case IPF_TCPS_HALF_ESTAB : case IPF_TCPS_ESTABLISHED : nstate = IPF_TCPS_ESTABLISHED; rval = 1; break; default : break; } } /* * TODO: besides regular ACK packets we can have other * packets as well; it is yet to be determined how we * should initialize the states in those cases */ break; case IPF_TCPS_SYN_SENT: /* 1 */ if ((tcpflags & ~(TH_ECN|TH_CWR)) == TH_SYN) { /* * A retransmitted SYN packet. We do not reset * the timeout here to ipf_tcptimeout because a * connection connect timeout does not renew * after every packet that is sent. We need to * set rval so as to indicate the packet has * passed the check for its flags being valid * in the TCP FSM. Setting rval to 2 has the * result of not resetting the timeout. */ rval = 2; } else if ((tcpflags & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK) { /* * we see an A from 'dir' which is in SYN_SENT * state: 'dir' sent an A in response to an SA * which it received, SYN_SENT -> ESTABLISHED */ nstate = IPF_TCPS_ESTABLISHED; rval = 1; } else if (tcpflags & TH_FIN) { /* * we see an F from 'dir' which is in SYN_SENT * state and wants to close its side of the * connection; SYN_SENT -> FIN_WAIT_1 */ nstate = IPF_TCPS_FIN_WAIT_1; rval = 1; } else if ((tcpflags & TH_OPENING) == TH_OPENING) { /* * we see an SA from 'dir' which is already in * SYN_SENT state, this means we have a * simultaneous open; SYN_SENT -> SYN_RECEIVED */ nstate = IPF_TCPS_SYN_RECEIVED; rval = 1; } break; case IPF_TCPS_SYN_RECEIVED: /* 2 */ if ((tcpflags & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK) { /* * we see an A from 'dir' which was in * SYN_RECEIVED state so it must now be in * established state, SYN_RECEIVED -> * ESTABLISHED */ nstate = IPF_TCPS_ESTABLISHED; rval = 1; } else if ((tcpflags & ~(TH_ECN|TH_CWR)) == TH_OPENING) { /* * We see an SA from 'dir' which is already in * SYN_RECEIVED state. */ rval = 2; } else if (tcpflags & TH_FIN) { /* * we see an F from 'dir' which is in * SYN_RECEIVED state and wants to close its * side of the connection; SYN_RECEIVED -> * FIN_WAIT_1 */ nstate = IPF_TCPS_FIN_WAIT_1; rval = 1; } break; case IPF_TCPS_HALF_ESTAB: /* 3 */ if (tcpflags & TH_FIN) { nstate = IPF_TCPS_FIN_WAIT_1; rval = 1; } else if ((tcpflags & TH_ACKMASK) == TH_ACK) { /* * If we've picked up a connection in mid * flight, we could be looking at a follow on * packet from the same direction as the one * that created this state. Recognise it but * do not advance the entire connection's * state. */ switch (ostate) { case IPF_TCPS_LISTEN : case IPF_TCPS_SYN_SENT : case IPF_TCPS_SYN_RECEIVED : rval = 1; break; case IPF_TCPS_HALF_ESTAB : case IPF_TCPS_ESTABLISHED : nstate = IPF_TCPS_ESTABLISHED; rval = 1; break; default : break; } } break; case IPF_TCPS_ESTABLISHED: /* 4 */ rval = 1; if (tcpflags & TH_FIN) { /* * 'dir' closed its side of the connection; * this gives us a half-closed connection; * ESTABLISHED -> FIN_WAIT_1 */ if (ostate == IPF_TCPS_FIN_WAIT_1) { nstate = IPF_TCPS_CLOSING; } else { nstate = IPF_TCPS_FIN_WAIT_1; } } else if (tcpflags & TH_ACK) { /* * an ACK, should we exclude other flags here? */ if (ostate == IPF_TCPS_FIN_WAIT_1) { /* * We know the other side did an active * close, so we are ACKing the recvd * FIN packet (does the window matching * code guarantee this?) and go into * CLOSE_WAIT state; this gives us a * half-closed connection */ nstate = IPF_TCPS_CLOSE_WAIT; } else if (ostate < IPF_TCPS_CLOSE_WAIT) { /* * still a fully established * connection reset timeout */ nstate = IPF_TCPS_ESTABLISHED; } } break; case IPF_TCPS_CLOSE_WAIT: /* 5 */ rval = 1; if (tcpflags & TH_FIN) { /* * application closed and 'dir' sent a FIN, * we're now going into LAST_ACK state */ nstate = IPF_TCPS_LAST_ACK; } else { /* * we remain in CLOSE_WAIT because the other * side has closed already and we did not * close our side yet; reset timeout */ nstate = IPF_TCPS_CLOSE_WAIT; } break; case IPF_TCPS_FIN_WAIT_1: /* 6 */ rval = 1; if ((tcpflags & TH_ACK) && ostate > IPF_TCPS_CLOSE_WAIT) { /* * if the other side is not active anymore * it has sent us a FIN packet that we are * ack'ing now with an ACK; this means both * sides have now closed the connection and * we go into TIME_WAIT */ /* * XXX: how do we know we really are ACKing * the FIN packet here? does the window code * guarantee that? */ nstate = IPF_TCPS_LAST_ACK; } else { /* * we closed our side of the connection * already but the other side is still active * (ESTABLISHED/CLOSE_WAIT); continue with * this half-closed connection */ nstate = IPF_TCPS_FIN_WAIT_1; } break; case IPF_TCPS_CLOSING: /* 7 */ if ((tcpflags & (TH_FIN|TH_ACK)) == TH_ACK) { nstate = IPF_TCPS_TIME_WAIT; } rval = 1; break; case IPF_TCPS_LAST_ACK: /* 8 */ if (tcpflags & TH_ACK) { rval = 1; } /* * we cannot detect when we go out of LAST_ACK state * to CLOSED because that is based on the reception * of ACK packets; ipfilter can only detect that a * packet has been sent by a host */ break; case IPF_TCPS_FIN_WAIT_2: /* 9 */ /* NOT USED */ break; case IPF_TCPS_TIME_WAIT: /* 10 */ /* we're in 2MSL timeout now */ if (ostate == IPF_TCPS_LAST_ACK) { nstate = IPF_TCPS_CLOSED; rval = 1; } else { rval = 2; } break; case IPF_TCPS_CLOSED: /* 11 */ rval = 2; break; default : #if !defined(_KERNEL) abort(); #endif break; } } /* * If rval == 2 then do not update the queue position, but treat the * packet as being ok. */ if (rval == 2) rval = 1; else if (rval == 1) { if (ok) tqe->tqe_state[dir] = nstate; if ((tqe->tqe_flags & TQE_RULEBASED) == 0) ipf_movequeue(softc->ipf_ticks, tqe, tqe->tqe_ifq, tqtab + nstate); } return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_log */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* is(I) - pointer to state structure */ /* type(I) - type of log entry to create */ /* */ /* Creates a state table log entry using the state structure and type info. */ /* passed in. Log packet/byte counts, source/destination address and other */ /* protocol specific information. */ /* ------------------------------------------------------------------------ */ void ipf_state_log(softc, is, type) ipf_main_softc_t *softc; struct ipstate *is; u_int type; { #ifdef IPFILTER_LOG struct ipslog ipsl; size_t sizes[1]; void *items[1]; int types[1]; /* * Copy information out of the ipstate_t structure and into the * structure used for logging. */ ipsl.isl_type = type; ipsl.isl_pkts[0] = is->is_pkts[0] + is->is_icmppkts[0]; ipsl.isl_bytes[0] = is->is_bytes[0]; ipsl.isl_pkts[1] = is->is_pkts[1] + is->is_icmppkts[1]; ipsl.isl_bytes[1] = is->is_bytes[1]; ipsl.isl_pkts[2] = is->is_pkts[2] + is->is_icmppkts[2]; ipsl.isl_bytes[2] = is->is_bytes[2]; ipsl.isl_pkts[3] = is->is_pkts[3] + is->is_icmppkts[3]; ipsl.isl_bytes[3] = is->is_bytes[3]; ipsl.isl_src = is->is_src; ipsl.isl_dst = is->is_dst; ipsl.isl_p = is->is_p; ipsl.isl_v = is->is_v; ipsl.isl_flags = is->is_flags; ipsl.isl_tag = is->is_tag; ipsl.isl_rulen = is->is_rulen; (void) strncpy(ipsl.isl_group, is->is_group, FR_GROUPLEN); if (ipsl.isl_p == IPPROTO_TCP || ipsl.isl_p == IPPROTO_UDP) { ipsl.isl_sport = is->is_sport; ipsl.isl_dport = is->is_dport; if (ipsl.isl_p == IPPROTO_TCP) { ipsl.isl_state[0] = is->is_state[0]; ipsl.isl_state[1] = is->is_state[1]; } } else if (ipsl.isl_p == IPPROTO_ICMP) { ipsl.isl_itype = is->is_icmp.ici_type; } else if (ipsl.isl_p == IPPROTO_ICMPV6) { ipsl.isl_itype = is->is_icmp.ici_type; } else { ipsl.isl_ps.isl_filler[0] = 0; ipsl.isl_ps.isl_filler[1] = 0; } items[0] = &ipsl; sizes[0] = sizeof(ipsl); types[0] = 0; (void) ipf_log_items(softc, IPL_LOGSTATE, NULL, items, sizes, types, 1); #endif } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_checkicmp6matchingstate */ /* Returns: ipstate_t* - NULL == no match found, */ /* else pointer to matching state entry */ /* Parameters: fin(I) - pointer to packet information */ /* Locks: NULL == no locks, else Read Lock on ipf_state */ /* */ /* If we've got an ICMPv6 error message, using the information stored in */ /* the ICMPv6 packet, look for a matching state table entry. */ /* ------------------------------------------------------------------------ */ static ipstate_t * ipf_checkicmp6matchingstate(fin) fr_info_t *fin; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; struct icmp6_hdr *ic6, *oic; ipstate_t *is, **isp; u_short sport, dport; i6addr_t dst, src; u_short savelen; icmpinfo_t *ic; fr_info_t ofin; tcphdr_t *tcp; ip6_t *oip6; u_char pr; u_int hv; int type; /* * Does it at least have the return (basic) IP header ? * Is it an actual recognised ICMP error type? * Only a basic IP header (no options) should be with * an ICMP error header. */ if ((fin->fin_v != 6) || (fin->fin_plen < ICMP6ERR_MINPKTLEN) || !(fin->fin_flx & FI_ICMPERR)) { SBUMPD(ipf_state_stats, iss_icmp_bad); return NULL; } ic6 = fin->fin_dp; type = ic6->icmp6_type; oip6 = (ip6_t *)((char *)ic6 + ICMPERR_ICMPHLEN); if (fin->fin_plen < sizeof(*oip6)) { SBUMPD(ipf_state_stats, iss_icmp_short); return NULL; } bcopy((char *)fin, (char *)&ofin, sizeof(*fin)); ofin.fin_v = 6; ofin.fin_ifp = fin->fin_ifp; ofin.fin_out = !fin->fin_out; ofin.fin_m = NULL; /* if dereferenced, panic XXX */ ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ /* * We make a fin entry to be able to feed it to * matchsrcdst. Note that not all fields are necessary * but this is the cleanest way. Note further we fill * in fin_mp such that if someone uses it we'll get * a kernel panic. ipf_matchsrcdst does not use this. * * watch out here, as ip is in host order and oip6 in network * order. Any change we make must be undone afterwards. */ savelen = oip6->ip6_plen; oip6->ip6_plen = htons(fin->fin_dlen - ICMPERR_ICMPHLEN); ofin.fin_flx = FI_NOCKSUM; ofin.fin_ip = (ip_t *)oip6; (void) ipf_makefrip(sizeof(*oip6), (ip_t *)oip6, &ofin); ofin.fin_flx &= ~(FI_BAD|FI_SHORT); oip6->ip6_plen = savelen; pr = ofin.fin_p; /* * an ICMP error can never generate an ICMP error in response. */ if (ofin.fin_flx & FI_ICMPERR) { DT1(iss_icmp6_icmperr, fr_info_t *, &ofin); SBUMP(ipf_state_stats.iss_icmp6_icmperr); return NULL; } if (oip6->ip6_nxt == IPPROTO_ICMPV6) { oic = ofin.fin_dp; /* * an ICMP error can only be generated as a result of an * ICMP query, not as the response on an ICMP error * * XXX theoretically ICMP_ECHOREP and the other reply's are * ICMP query's as well, but adding them here seems strange XXX */ if (!(oic->icmp6_type & ICMP6_INFOMSG_MASK)) { DT1(iss_icmp6_notinfo, fr_info_t *, &ofin); SBUMP(ipf_state_stats.iss_icmp6_notinfo); return NULL; } /* * perform a lookup of the ICMP packet in the state table */ hv = (pr = oip6->ip6_nxt); src.in6 = oip6->ip6_src; hv += src.in4.s_addr; dst.in6 = oip6->ip6_dst; hv += dst.in4.s_addr; hv += oic->icmp6_id; hv += oic->icmp6_seq; hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { ic = &is->is_icmp; isp = &is->is_hnext; if ((is->is_p == pr) && !(is->is_pass & FR_NOICMPERR) && (oic->icmp6_id == ic->ici_id) && (oic->icmp6_seq == ic->ici_seq) && (is = ipf_matchsrcdst(&ofin, is, &src, &dst, NULL, FI_ICMPCMP))) { /* * in the state table ICMP query's are stored * with the type of the corresponding ICMP * response. Correct here */ if (((ic->ici_type == ICMP6_ECHO_REPLY) && (oic->icmp6_type == ICMP6_ECHO_REQUEST)) || (ic->ici_type - 1 == oic->icmp6_type )) { if (!ipf_allowstateicmp(fin, is, &src)) return is; } } } RWLOCK_EXIT(&softc->ipf_state); SBUMPD(ipf_state_stats, iss_icmp6_miss); return NULL; } hv = (pr = oip6->ip6_nxt); src.in6 = oip6->ip6_src; hv += src.i6[0]; hv += src.i6[1]; hv += src.i6[2]; hv += src.i6[3]; dst.in6 = oip6->ip6_dst; hv += dst.i6[0]; hv += dst.i6[1]; hv += dst.i6[2]; hv += dst.i6[3]; tcp = NULL; switch (oip6->ip6_nxt) { case IPPROTO_TCP : case IPPROTO_UDP : tcp = (tcphdr_t *)(oip6 + 1); dport = tcp->th_dport; sport = tcp->th_sport; hv += dport; hv += sport; break; case IPPROTO_ICMPV6 : oic = (struct icmp6_hdr *)(oip6 + 1); hv += oic->icmp6_id; hv += oic->icmp6_seq; break; default : break; } hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; /* * Only allow this icmp though if the * encapsulated packet was allowed through the * other way around. Note that the minimal amount * of info present does not allow for checking against * tcp internals such as seq and ack numbers. */ if ((is->is_p != pr) || (is->is_v != 6) || (is->is_pass & FR_NOICMPERR)) continue; is = ipf_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP); if ((is != NULL) && (ipf_allowstateicmp(fin, is, &src) == 0)) return is; } RWLOCK_EXIT(&softc->ipf_state); SBUMPD(ipf_state_stats, iss_icmp_miss); return NULL; } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_sttab_init */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tqp(I) - pointer to an array of timeout queues for TCP */ /* */ /* Initialise the array of timeout queues for TCP. */ /* ------------------------------------------------------------------------ */ void ipf_sttab_init(softc, tqp) ipf_main_softc_t *softc; ipftq_t *tqp; { int i; for (i = IPF_TCP_NSTATES - 1; i >= 0; i--) { IPFTQ_INIT(&tqp[i], 0, "ipftq tcp tab"); tqp[i].ifq_next = tqp + i + 1; } tqp[IPF_TCP_NSTATES - 1].ifq_next = NULL; tqp[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcpclosed; tqp[IPF_TCPS_LISTEN].ifq_ttl = softc->ipf_tcptimeout; tqp[IPF_TCPS_SYN_SENT].ifq_ttl = softc->ipf_tcpsynsent; tqp[IPF_TCPS_SYN_RECEIVED].ifq_ttl = softc->ipf_tcpsynrecv; tqp[IPF_TCPS_ESTABLISHED].ifq_ttl = softc->ipf_tcpidletimeout; tqp[IPF_TCPS_CLOSE_WAIT].ifq_ttl = softc->ipf_tcphalfclosed; tqp[IPF_TCPS_FIN_WAIT_1].ifq_ttl = softc->ipf_tcphalfclosed; tqp[IPF_TCPS_CLOSING].ifq_ttl = softc->ipf_tcptimeout; tqp[IPF_TCPS_LAST_ACK].ifq_ttl = softc->ipf_tcplastack; tqp[IPF_TCPS_FIN_WAIT_2].ifq_ttl = softc->ipf_tcpclosewait; tqp[IPF_TCPS_TIME_WAIT].ifq_ttl = softc->ipf_tcptimewait; tqp[IPF_TCPS_HALF_ESTAB].ifq_ttl = softc->ipf_tcptimeout; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sttab_destroy */ /* Returns: Nil */ /* Parameters: tqp(I) - pointer to an array of timeout queues for TCP */ /* */ /* Do whatever is necessary to "destroy" each of the entries in the array */ /* of timeout queues for TCP. */ /* ------------------------------------------------------------------------ */ void ipf_sttab_destroy(tqp) ipftq_t *tqp; { int i; for (i = IPF_TCP_NSTATES - 1; i >= 0; i--) MUTEX_DESTROY(&tqp[i].ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* isp(I) - pointer to pointer to state table entry */ /* */ /* Decrement the reference counter for this state table entry and free it */ /* if there are no more things using it. */ /* */ /* This function is only called when cleaning up after increasing is_ref by */ /* one earlier in the 'code path' so if is_ref is 1 when entering, we do */ /* have an orphan, otherwise not. However there is a possible race between */ /* the entry being deleted via flushing with an ioctl call (that calls the */ /* delete function directly) and the tail end of packet processing so we */ /* need to grab is_lock before doing the check to synchronise the two code */ /* paths. */ /* */ /* When operating in userland (ipftest), we have no timers to clear a state */ /* entry. Therefore, we make a few simple tests before deleting an entry */ /* outright. We compare states on each side looking for a combination of */ /* TIME_WAIT (should really be FIN_WAIT_2?) and LAST_ACK. Then we factor */ /* in packet direction with the interface list to make sure we don't */ /* prematurely delete an entry on a final inbound packet that's we're also */ /* supposed to route elsewhere. */ /* */ /* Internal parameters: */ /* state[0] = state of source (host that initiated connection) */ /* state[1] = state of dest (host that accepted the connection) */ /* */ /* dir == 0 : a packet from source to dest */ /* dir == 1 : a packet from dest to source */ /* ------------------------------------------------------------------------ */ void ipf_state_deref(softc, isp) ipf_main_softc_t *softc; ipstate_t **isp; { ipstate_t *is = *isp; is = *isp; *isp = NULL; MUTEX_ENTER(&is->is_lock); if (is->is_ref > 1) { is->is_ref--; MUTEX_EXIT(&is->is_lock); #ifndef _KERNEL if ((is->is_sti.tqe_state[0] > IPF_TCPS_ESTABLISHED) || (is->is_sti.tqe_state[1] > IPF_TCPS_ESTABLISHED)) { ipf_state_del(softc, is, ISL_EXPIRE); } #endif return; } MUTEX_EXIT(&is->is_lock); WRITE_ENTER(&softc->ipf_state); ipf_state_del(softc, is, ISL_ORPHAN); RWLOCK_EXIT(&softc->ipf_state); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_setqueue */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* is(I) - pointer to state structure */ /* rev(I) - forward(0) or reverse(1) direction */ /* Locks: ipf_state (read or write) */ /* */ /* Put the state entry on its default queue entry, using rev as a helped in */ /* determining which queue it should be placed on. */ /* ------------------------------------------------------------------------ */ void ipf_state_setqueue(softc, is, rev) ipf_main_softc_t *softc; ipstate_t *is; int rev; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipftq_t *oifq, *nifq; if ((is->is_sti.tqe_flags & TQE_RULEBASED) != 0) nifq = is->is_tqehead[rev]; else nifq = NULL; if (nifq == NULL) { switch (is->is_p) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : if (rev == 1) nifq = &softs->ipf_state_icmpacktq; else nifq = &softs->ipf_state_icmptq; break; #endif case IPPROTO_ICMP : if (rev == 1) nifq = &softs->ipf_state_icmpacktq; else nifq = &softs->ipf_state_icmptq; break; case IPPROTO_TCP : nifq = softs->ipf_state_tcptq + is->is_state[rev]; break; case IPPROTO_UDP : if (rev == 1) nifq = &softs->ipf_state_udpacktq; else nifq = &softs->ipf_state_udptq; break; default : nifq = &softs->ipf_state_iptq; break; } } oifq = is->is_sti.tqe_ifq; /* * If it's currently on a timeout queue, move it from one queue to * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &is->is_sti, oifq, nifq); else ipf_queueappend(softc->ipf_ticks, &is->is_sti, nifq, is); return; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_iter */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to main soft context */ /* token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter structure */ /* obj(I) - pointer to data description structure */ /* */ /* This function handles the SIOCGENITER ioctl for the state tables and */ /* walks through the list of entries in the state table list (softs->ipf_state_list.) */ /* ------------------------------------------------------------------------ */ static int ipf_state_iter(softc, token, itp, obj) ipf_main_softc_t *softc; ipftoken_t *token; ipfgeniter_t *itp; ipfobj_t *obj; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, *next, zero; int error; if (itp->igi_data == NULL) { IPFERROR(100026); return EFAULT; } if (itp->igi_nitems < 1) { IPFERROR(100027); return ENOSPC; } if (itp->igi_type != IPFGENITER_STATE) { IPFERROR(100028); return EINVAL; } is = token->ipt_data; if (is == (void *)-1) { IPFERROR(100029); return ESRCH; } error = 0; obj->ipfo_type = IPFOBJ_IPSTATE; obj->ipfo_size = sizeof(ipstate_t); READ_ENTER(&softc->ipf_state); is = token->ipt_data; if (is == NULL) { next = softs->ipf_state_list; } else { next = is->is_next; } /* * If we find a state entry to use, bump its reference count so that * it can be used for is_next when we come back. */ if (next != NULL) { MUTEX_ENTER(&next->is_lock); next->is_ref++; MUTEX_EXIT(&next->is_lock); token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; } if (next->is_next == NULL) ipf_token_mark_complete(token); RWLOCK_EXIT(&softc->ipf_state); obj->ipfo_ptr = itp->igi_data; error = ipf_outobjk(softc, obj, next); if (is != NULL) ipf_state_deref(softc, &is); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_gettable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to main soft context */ /* softs(I) - pointer to state context structure */ /* data(I) - pointer to ioctl data */ /* */ /* This function handles ioctl requests for tables of state information. */ /* At present the only table it deals with is the hash bucket statistics. */ /* ------------------------------------------------------------------------ */ static int ipf_state_gettable(softc, softs, data) ipf_main_softc_t *softc; ipf_state_softc_t *softs; char *data; { ipftable_t table; int error; error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return error; if (table.ita_type != IPFTABLE_BUCKETS) { IPFERROR(100031); return EINVAL; } error = COPYOUT(softs->ipf_state_stats.iss_bucketlen, table.ita_table, softs->ipf_state_size * sizeof(u_int)); if (error != 0) { IPFERROR(100032); error = EFAULT; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_setpending */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to main soft context */ /* is(I) - pointer to state structure */ /* Locks: ipf_state (read or write) */ /* */ /* Put the state entry on to the pending queue - this queue has a very */ /* short lifetime where items are put that can't be deleted straight away */ /* because of locking issues but we want to delete them ASAP, anyway. */ /* ------------------------------------------------------------------------ */ void ipf_state_setpending(softc, is) ipf_main_softc_t *softc; ipstate_t *is; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipftq_t *oifq; oifq = is->is_sti.tqe_ifq; if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &is->is_sti, oifq, &softs->ipf_state_pending); else ipf_queueappend(softc->ipf_ticks, &is->is_sti, &softs->ipf_state_pending, is); MUTEX_ENTER(&is->is_lock); if (is->is_me != NULL) { *is->is_me = NULL; is->is_me = NULL; is->is_ref--; } MUTEX_EXIT(&is->is_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matchflush */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to main soft context */ /* data(I) - pointer to state structure */ /* Locks: ipf_state (read or write) */ /* */ /* Flush all entries from the list of state entries that match the */ /* properties in the array loaded. */ /* ------------------------------------------------------------------------ */ int ipf_state_matchflush(softc, data) ipf_main_softc_t *softc; caddr_t data; { ipf_state_softc_t *softs = softc->ipf_state_soft; int *array, flushed, error; ipstate_t *state, *statenext; ipfobj_t obj; error = ipf_matcharray_load(softc, data, &obj, &array); if (error != 0) return error; flushed = 0; for (state = softs->ipf_state_list; state != NULL; state = statenext) { statenext = state->is_next; if (ipf_state_matcharray(state, array, softc->ipf_ticks) == 0) { ipf_state_del(softc, state, ISL_FLUSH); flushed++; } } obj.ipfo_retval = flushed; error = BCOPYOUT(&obj, data, sizeof(obj)); KFREES(array, array[0] * sizeof(*array)); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matcharray */ /* Returns: int - 0 = no match, 1 = match */ /* Parameters: state(I) - pointer to state structure */ /* array(I) - pointer to ipf matching expression */ /* ticks(I) - current value of ipfilter tick timer */ /* Locks: ipf_state (read or write) */ /* */ /* Compare a state entry with the match array passed in and return a value */ /* to indicate whether or not the matching was successful. */ /* ------------------------------------------------------------------------ */ static int ipf_state_matcharray(state, array, ticks) ipstate_t *state; int *array; u_long ticks; { 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; n -= e->ipfe_size; if (x[0] == IPF_EXP_END) break; /* * If we need to match the protocol and that doesn't match, * don't even both with the instruction array. */ p = e->ipfe_cmd >> 16; if ((p != 0) && (p != state->is_p)) break; switch (e->ipfe_cmd) { case IPF_EXP_IP_PR : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (state->is_p == e->ipfe_arg0[i]); } break; case IPF_EXP_IP_SRCADDR : if (state->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((state->is_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_DSTADDR : if (state->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((state->is_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_ADDR : if (state->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((state->is_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((state->is_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (state->is_v != 6) break; for (i = 0; !rv && i < x[3]; i++) { rv |= IP6_MASKEQ(&state->is_src.in6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_DSTADDR : if (state->is_v != 6) break; for (i = 0; !rv && i < x[3]; i++) { rv |= IP6_MASKEQ(&state->is_dst.in6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_ADDR : if (state->is_v != 6) break; for (i = 0; !rv && i < x[3]; i++) { rv |= IP6_MASKEQ(&state->is_src.in6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&state->is_dst.in6, &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 |= (state->is_sport == e->ipfe_arg0[i]) || (state->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 |= (state->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 |= (state->is_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_TCP_STATE : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (state->is_state[0] == e->ipfe_arg0[i]) || (state->is_state[1] == e->ipfe_arg0[i]); } break; case IPF_EXP_IDLE_GT : rv |= (ticks - state->is_touched > e->ipfe_arg0[0]); break; } /* * Factor in doing a negative match. */ rv ^= e->ipfe_not; if (rv == 0) break; } return rv; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_settimeout */ /* Returns: int 0 = success, else failure */ /* Parameters: softc(I) - pointer to main soft context */ /* t(I) - pointer to tuneable being changed */ /* p(I) - pointer to the new value */ /* */ /* Sets a timeout value for one of the many timeout queues. We find the */ /* correct queue using a somewhat manual process of comparing the timeout */ /* names for each specific value available and calling ipf_apply_timeout on */ /* that queue so that all of the items on it are updated accordingly. */ /* ------------------------------------------------------------------------ */ int ipf_state_settimeout(softc, t, p) struct ipf_main_softc_s *softc; ipftuneable_t *t; ipftuneval_t *p; { ipf_state_softc_t *softs = softc->ipf_state_soft; /* * In case there is nothing to do... */ if (*t->ipft_pint == p->ipftu_int) return 0; if (!strncmp(t->ipft_name, "tcp_", 4)) return ipf_settimeout_tcp(t, p, softs->ipf_state_tcptq); if (!strcmp(t->ipft_name, "udp_timeout")) { ipf_apply_timeout(&softs->ipf_state_udptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { ipf_apply_timeout(&softs->ipf_state_udpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_timeout")) { ipf_apply_timeout(&softs->ipf_state_icmptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { ipf_apply_timeout(&softs->ipf_state_icmpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "ip_timeout")) { ipf_apply_timeout(&softs->ipf_state_iptq, p->ipftu_int); } else { IPFERROR(100034); return ESRCH; } /* * Update the tuneable being set. */ *t->ipft_pint = p->ipftu_int; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_rehash */ /* Returns: int 0 = success, else failure */ /* Parameters: softc(I) - pointer to main soft context */ /* t(I) - pointer to tuneable being changed */ /* p(I) - pointer to the new value */ /* */ /* To change the size of the state hash table at runtime, a new table has */ /* to be allocated and then all of the existing entries put in it, bumping */ /* up the bucketlength for it as we go along. */ /* ------------------------------------------------------------------------ */ int ipf_state_rehash(softc, t, p) ipf_main_softc_t *softc; ipftuneable_t *t; ipftuneval_t *p; { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t **newtab, *is; u_long *newseed; u_int *bucketlens; u_int maxbucket; u_int newsize; u_int hv; int i; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == softs->ipf_state_size) return 0; KMALLOCS(newtab, ipstate_t **, newsize * sizeof(ipstate_t *)); if (newtab == NULL) { IPFERROR(100035); return ENOMEM; } KMALLOCS(bucketlens, u_int *, newsize * sizeof(u_int)); if (bucketlens == NULL) { KFREES(newtab, newsize * sizeof(*softs->ipf_state_table)); IPFERROR(100036); return ENOMEM; } newseed = ipf_state_seed_alloc(newsize, softs->ipf_state_max); if (newseed == NULL) { KFREES(bucketlens, newsize * sizeof(*bucketlens)); KFREES(newtab, newsize * sizeof(*newtab)); IPFERROR(100037); return ENOMEM; } for (maxbucket = 0, i = newsize; i > 0; i >>= 1) maxbucket++; maxbucket *= 2; bzero((char *)newtab, newsize * sizeof(ipstate_t *)); bzero((char *)bucketlens, newsize * sizeof(u_int)); WRITE_ENTER(&softc->ipf_state); if (softs->ipf_state_table != NULL) { KFREES(softs->ipf_state_table, softs->ipf_state_size * sizeof(*softs->ipf_state_table)); } softs->ipf_state_table = newtab; if (softs->ipf_state_seed != NULL) { KFREES(softs->ipf_state_seed, softs->ipf_state_size * sizeof(*softs->ipf_state_seed)); } softs->ipf_state_seed = newseed; if (softs->ipf_state_stats.iss_bucketlen != NULL) { KFREES(softs->ipf_state_stats.iss_bucketlen, softs->ipf_state_size * sizeof(u_int)); } softs->ipf_state_stats.iss_bucketlen = bucketlens; softs->ipf_state_maxbucket = maxbucket; softs->ipf_state_size = newsize; /* * Walk through the entire list of state table entries and put them * in the new state table, somewhere. Because we have a new table, * we need to restart the counter of how many chains are in use. */ softs->ipf_state_stats.iss_inuse = 0; for (is = softs->ipf_state_list; is != NULL; is = is->is_next) { is->is_hnext = NULL; is->is_phnext = NULL; hv = is->is_hv % softs->ipf_state_size; if (softs->ipf_state_table[hv] != NULL) softs->ipf_state_table[hv]->is_phnext = &is->is_hnext; else softs->ipf_state_stats.iss_inuse++; is->is_phnext = softs->ipf_state_table + hv; is->is_hnext = softs->ipf_state_table[hv]; softs->ipf_state_table[hv] = is; softs->ipf_state_stats.iss_bucketlen[hv]++; } RWLOCK_EXIT(&softc->ipf_state); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_add_tq */ /* Returns: ipftq_t * - NULL = failure, else pointer to new timeout */ /* queue */ /* Parameters: softc(I) - pointer to main soft context */ /* ttl(I) - pointer to the ttl for the new queue */ /* */ /* Request a pointer to a timeout queue that has a ttl as given by the */ /* value being passed in. The timeout queue is added tot the list of those */ /* used internally for stateful filtering. */ /* ------------------------------------------------------------------------ */ ipftq_t * ipf_state_add_tq(softc, ttl) ipf_main_softc_t *softc; int ttl; { ipf_state_softc_t *softs = softc->ipf_state_soft; return ipf_addtimeoutqueue(softc, &softs->ipf_state_usertq, ttl); } #ifndef _KERNEL /* * Display the built up state table rules and mapping entries. */ void ipf_state_dump(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_state_softc_t *softs = arg; ipstate_t *ips; printf("List of active state sessions:\n"); for (ips = softs->ipf_state_list; ips != NULL; ) ips = printstate(ips, opts & (OPT_DEBUG|OPT_VERBOSE), softc->ipf_ticks); } #endif diff --git a/sys/contrib/ipfilter/netinet/ip_state.h b/sys/contrib/ipfilter/netinet/ip_state.h index e541cff52183..26377bc7f5d2 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.h +++ b/sys/contrib/ipfilter/netinet/ip_state.h @@ -1,333 +1,333 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_state.h 1.3 1/12/96 (C) 1995 Darren Reed * $FreeBSD$ * Id: ip_state.h,v 2.68.2.10 2007/10/16 09:33:24 darrenr Exp $ */ #ifndef __IP_STATE_H__ #define __IP_STATE_H__ # define SIOCDELST _IOW('r', 61, struct ipfobj) struct ipscan; #ifndef IPSTATE_SIZE # define IPSTATE_SIZE 5737 #endif #ifndef IPSTATE_MAX # define IPSTATE_MAX 4013 /* Maximum number of states held */ #endif #define PAIRS(s1,d1,s2,d2) ((((s1) == (s2)) && ((d1) == (d2))) ||\ (((s1) == (d2)) && ((d1) == (s2)))) #define IPPAIR(s1,d1,s2,d2) PAIRS((s1).s_addr, (d1).s_addr, \ (s2).s_addr, (d2).s_addr) typedef struct ipstate { ipfmutex_t is_lock; struct ipstate *is_next; struct ipstate **is_pnext; struct ipstate *is_hnext; struct ipstate **is_phnext; struct ipstate **is_me; void *is_ifp[4]; void *is_sync; frentry_t *is_rule; struct ipftq *is_tqehead[2]; struct ipscan *is_isc; U_QUAD_T is_pkts[4]; U_QUAD_T is_bytes[4]; U_QUAD_T is_icmppkts[4]; struct ipftqent is_sti; u_int is_frage[2]; int is_ref; /* reference count */ int is_isninc[2]; u_short is_sumd[2]; i6addr_t is_src; i6addr_t is_dst; u_int is_pass; u_char is_p; /* Protocol */ u_char is_v; int is_family; u_32_t is_hv; u_32_t is_tag; u_32_t is_opt[2]; /* packet options set */ u_32_t is_optmsk[2]; /* " " mask */ u_short is_sec; /* security options set */ u_short is_secmsk; /* " " mask */ u_short is_auth; /* authentication options set */ u_short is_authmsk; /* " " mask */ union { icmpinfo_t is_ics; tcpinfo_t is_ts; udpinfo_t is_us; greinfo_t is_ug; } is_ps; u_32_t is_flags; int is_flx[2][2]; u_32_t is_rulen; /* rule number when created */ u_32_t is_s0[2]; u_short is_smsk[2]; frdest_t is_dif; frdest_t is_tifs[2]; char is_group[FR_GROUPLEN]; char is_sbuf[2][16]; char is_ifname[4][LIFNAMSIZ]; } ipstate_t; #define is_die is_sti.tqe_die #define is_state is_sti.tqe_state #define is_touched is_sti.tqe_touched #define is_saddr is_src.in4.s_addr #define is_daddr is_dst.in4.s_addr #define is_icmp is_ps.is_ics #define is_type is_icmp.ici_type #define is_tcp is_ps.is_ts #define is_udp is_ps.is_us #define is_send is_tcp.ts_data[0].td_end #define is_dend is_tcp.ts_data[1].td_end #define is_maxswin is_tcp.ts_data[0].td_maxwin #define is_maxdwin is_tcp.ts_data[1].td_maxwin #define is_maxsend is_tcp.ts_data[0].td_maxend #define is_maxdend is_tcp.ts_data[1].td_maxend #define is_swinscale is_tcp.ts_data[0].td_winscale #define is_dwinscale is_tcp.ts_data[1].td_winscale #define is_swinflags is_tcp.ts_data[0].td_winflags #define is_dwinflags is_tcp.ts_data[1].td_winflags #define is_sport is_tcp.ts_sport #define is_dport is_tcp.ts_dport #define is_ifpin is_ifp[0] #define is_ifpout is_ifp[2] #define is_gre is_ps.is_ug #define is_call is_gre.gs_call #define IS_WSPORT SI_W_SPORT /* 0x00100 */ #define IS_WDPORT SI_W_DPORT /* 0x00200 */ #define IS_WSADDR SI_W_SADDR /* 0x00400 */ #define IS_WDADDR SI_W_DADDR /* 0x00800 */ #define IS_NEWFR SI_NEWFR /* 0x01000 */ #define IS_CLONE SI_CLONE /* 0x02000 */ #define IS_CLONED SI_CLONED /* 0x04000 */ #define IS_TCPFSM 0x10000 #define IS_STRICT 0x20000 #define IS_ISNSYN 0x40000 #define IS_ISNACK 0x80000 #define IS_STATESYNC 0x100000 #define IS_LOOSE 0x200000 /* * IS_SC flags are for scan-operations that need to be recognised in state. */ #define IS_SC_CLIENT 0x10000000 #define IS_SC_SERVER 0x20000000 #define IS_SC_MATCHC 0x40000000 #define IS_SC_MATCHS 0x80000000 #define IS_SC_MATCHALL (IS_SC_MATCHC|IS_SC_MATCHC) #define IS_SC_ALL (IS_SC_MATCHC|IS_SC_MATCHC|IS_SC_CLIENT|IS_SC_SERVER) /* * Flags that can be passed into ipf_addstate */ #define IS_INHERITED 0x0fffff00 #define TH_OPENING (TH_SYN|TH_ACK) /* * is_flags: * Bits 0 - 3 are use as a mask with the current packet's bits to check for * whether it is short, tcp/udp, a fragment or the presence of IP options. * Bits 4 - 7 are set from the initial packet and contain what the packet * anded with bits 0-3 must match. * Bits 8,9 are used to indicate wildcard source/destination port matching. * Bits 10,11 are reserved for other wildcard flag compatibility. * Bits 12,13 are for scaning. */ typedef struct ipstate_save { void *ips_next; struct ipstate ips_is; struct frentry ips_fr; } ipstate_save_t; #define ips_rule ips_is.is_rule typedef struct ipslog { U_QUAD_T isl_pkts[4]; U_QUAD_T isl_bytes[4]; i6addr_t isl_src; i6addr_t isl_dst; u_32_t isl_tag; u_short isl_type; union { u_short isl_filler[2]; u_short isl_ports[2]; u_short isl_icmp; } isl_ps; u_char isl_v; u_char isl_p; u_char isl_flags; u_char isl_state[2]; u_32_t isl_rulen; char isl_group[FR_GROUPLEN]; } ipslog_t; #define isl_sport isl_ps.isl_ports[0] #define isl_dport isl_ps.isl_ports[1] #define isl_itype isl_ps.isl_icmp #define ISL_NEW 0 #define ISL_CLONE 1 #define ISL_STATECHANGE 2 #define ISL_EXPIRE 0xffff #define ISL_FLUSH 0xfffe #define ISL_REMOVE 0xfffd #define ISL_INTERMEDIATE 0xfffc #define ISL_KILLED 0xfffb #define ISL_ORPHAN 0xfffa #define ISL_UNLOAD 0xfff9 typedef struct ips_stat { u_int iss_active; u_int iss_active_proto[256]; u_long iss_add_bad; u_long iss_add_dup; u_long iss_add_locked; u_long iss_add_oow; u_long iss_bucket_full; u_long iss_check_bad; u_long iss_check_miss; u_long iss_check_nattag; u_long iss_check_notag; u_long iss_clone_nomem; u_long iss_cloned; u_long iss_expire; u_long iss_fin; u_long iss_flush_all; u_long iss_flush_closing; u_long iss_flush_queue; u_long iss_flush_state; u_long iss_flush_timeout; u_long iss_hits; u_long iss_icmp6_icmperr; u_long iss_icmp6_miss; u_long iss_icmp6_notinfo; u_long iss_icmp6_notquery; u_long iss_icmp_bad; u_long iss_icmp_banned; u_long iss_icmp_headblock; u_long iss_icmp_hits; u_long iss_icmp_icmperr; u_long iss_icmp_miss; u_long iss_icmp_notquery; u_long iss_icmp_short; u_long iss_icmp_toomany; u_int iss_inuse; ipstate_t *iss_list; u_long iss_log_fail; u_long iss_log_ok; u_long iss_lookup_badifp; u_long iss_lookup_badport; u_long iss_lookup_miss; u_long iss_max; u_long iss_max_ref; u_long iss_max_track; u_long iss_miss_mask; u_long iss_nomem; u_long iss_oow; u_long iss_orphan; u_long iss_proto[256]; u_long iss_scan_block; u_long iss_state_max; u_long iss_state_size; u_long iss_states[IPF_TCP_NSTATES]; ipstate_t **iss_table; u_long iss_tcp_closing; u_long iss_tcp_oow; u_long iss_tcp_rstadd; u_long iss_tcp_toosmall; u_long iss_tcp_badopt; u_long iss_tcp_fsm; u_long iss_tcp_strict; ipftq_t *iss_tcptab; u_int iss_ticks; u_long iss_wild; u_long iss_winsack; u_int *iss_bucketlen; } ips_stat_t; typedef struct ipf_state_softc_s { ipfmutex_t ipf_stinsert; int ipf_state_logging; int ipf_state_lock; int ipf_state_doflush; u_int ipf_state_inited; u_int ipf_state_max; u_int ipf_state_maxbucket; u_int ipf_state_size; u_int ipf_state_wm_freq; u_int ipf_state_wm_high; u_int ipf_state_wm_low; u_int ipf_state_wm_last; u_long *ipf_state_seed; ipstate_t *ipf_state_list; ipstate_t **ipf_state_table; ipftuneable_t *ipf_state_tune; ipftq_t *ipf_state_usertq; ipftq_t ipf_state_pending; ipftq_t ipf_state_deletetq; ipftq_t ipf_state_udptq; ipftq_t ipf_state_udpacktq; ipftq_t ipf_state_iptq; ipftq_t ipf_state_icmptq; ipftq_t ipf_state_icmpacktq; ipftq_t ipf_state_tcptq[IPF_TCP_NSTATES]; ips_stat_t ipf_state_stats; } ipf_state_softc_t; #ifndef _KERNEL -extern void ipf_state_dump __P((ipf_main_softc_t *, void *)); +extern void ipf_state_dump(ipf_main_softc_t *, void *); #endif -extern int ipf_tcp_age __P((struct ipftqent *, struct fr_info *, - struct ipftq *, int, int)); -extern int ipf_tcpinwindow __P((struct fr_info *, struct tcpdata *, - struct tcpdata *, tcphdr_t *, int)); +extern int ipf_tcp_age(struct ipftqent *, struct fr_info *, + struct ipftq *, int, int); +extern int ipf_tcpinwindow(struct fr_info *, struct tcpdata *, + struct tcpdata *, tcphdr_t *, int); -extern int ipf_state_add __P((ipf_main_softc_t *, fr_info_t *, - ipstate_t **, u_int)); -extern frentry_t *ipf_state_check __P((struct fr_info *, u_32_t *)); -extern void ipf_state_deref __P((ipf_main_softc_t *, ipstate_t **)); -extern void ipf_state_expire __P((ipf_main_softc_t *)); -extern int ipf_state_flush __P((ipf_main_softc_t *, int, int)); -extern ipstate_t *ipf_state_lookup __P((fr_info_t *, tcphdr_t *, ipftq_t **)); -extern int ipf_state_init __P((void)); -extern int ipf_state_insert __P((ipf_main_softc_t *, struct ipstate *, int)); -extern int ipf_state_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *)); -extern void ipf_state_log __P((ipf_main_softc_t *, struct ipstate *, u_int)); -extern int ipf_state_matchflush __P((ipf_main_softc_t *, caddr_t)); -extern int ipf_state_rehash __P((ipf_main_softc_t *, ipftuneable_t *, ipftuneval_t *)); -extern void ipf_state_setqueue __P((ipf_main_softc_t *, ipstate_t *, int)); -extern void ipf_state_setpending __P((ipf_main_softc_t *, ipstate_t *)); -extern int ipf_state_settimeout __P((struct ipf_main_softc_s *, ipftuneable_t *, ipftuneval_t *)); -extern void ipf_state_sync __P((ipf_main_softc_t *, void *)); -extern void ipf_state_update __P((fr_info_t *, ipstate_t *)); +extern int ipf_state_add(ipf_main_softc_t *, fr_info_t *, + ipstate_t **, u_int); +extern frentry_t *ipf_state_check(struct fr_info *, u_32_t *); +extern void ipf_state_deref(ipf_main_softc_t *, ipstate_t **); +extern void ipf_state_expire(ipf_main_softc_t *); +extern int ipf_state_flush(ipf_main_softc_t *, int, int); +extern ipstate_t *ipf_state_lookup(fr_info_t *, tcphdr_t *, ipftq_t **); +extern int ipf_state_init(void); +extern int ipf_state_insert(ipf_main_softc_t *, struct ipstate *, int); +extern int ipf_state_ioctl(ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *); +extern void ipf_state_log(ipf_main_softc_t *, struct ipstate *, u_int); +extern int ipf_state_matchflush(ipf_main_softc_t *, caddr_t); +extern int ipf_state_rehash(ipf_main_softc_t *, ipftuneable_t *, ipftuneval_t *); +extern void ipf_state_setqueue(ipf_main_softc_t *, ipstate_t *, int); +extern void ipf_state_setpending(ipf_main_softc_t *, ipstate_t *); +extern int ipf_state_settimeout(struct ipf_main_softc_s *, ipftuneable_t *, ipftuneval_t *); +extern void ipf_state_sync(ipf_main_softc_t *, void *); +extern void ipf_state_update(fr_info_t *, ipstate_t *); -extern void ipf_sttab_init __P((ipf_main_softc_t *, struct ipftq *)); -extern void ipf_sttab_destroy __P((struct ipftq *)); -extern void ipf_state_setlock __P((void *, int)); -extern int ipf_state_main_load __P((void)); -extern int ipf_state_main_unload __P((void)); -extern void *ipf_state_soft_create __P((ipf_main_softc_t *)); -extern void ipf_state_soft_destroy __P((ipf_main_softc_t *, void *)); -extern int ipf_state_soft_init __P((ipf_main_softc_t *, void *)); -extern int ipf_state_soft_fini __P((ipf_main_softc_t *, void *)); -extern ipftq_t *ipf_state_add_tq __P((ipf_main_softc_t *, int)); +extern void ipf_sttab_init(ipf_main_softc_t *, struct ipftq *); +extern void ipf_sttab_destroy(struct ipftq *); +extern void ipf_state_setlock(void *, int); +extern int ipf_state_main_load(void); +extern int ipf_state_main_unload(void); +extern void *ipf_state_soft_create(ipf_main_softc_t *); +extern void ipf_state_soft_destroy(ipf_main_softc_t *, void *); +extern int ipf_state_soft_init(ipf_main_softc_t *, void *); +extern int ipf_state_soft_fini(ipf_main_softc_t *, void *); +extern ipftq_t *ipf_state_add_tq(ipf_main_softc_t *, int); #endif /* __IP_STATE_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_sync.c b/sys/contrib/ipfilter/netinet/ip_sync.c index 95c383c08d8f..f95cd824544b 100644 --- a/sys/contrib/ipfilter/netinet/ip_sync.c +++ b/sys/contrib/ipfilter/netinet/ip_sync.c @@ -1,1461 +1,1461 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # define KERNEL # include # undef _KERNEL # undef KERNEL #else # include # if !defined(__SVR4) # include # endif # include # ifdef __FreeBSD_version # include # endif #endif #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include #endif #if defined(_KERNEL) && defined(__FreeBSD_version) # include # include #else # include #endif #include # include #include #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #include #ifdef sun # include #endif #include #include #include #include # include # include #include #include #include "netinet/ip_compat.h" #include #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_sync.h" #ifdef USE_INET6 #include #endif #if defined(__FreeBSD_version) # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include # include # endif #endif /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif #define SYNC_STATETABSZ 256 #define SYNC_NATTABSZ 256 typedef struct ipf_sync_softc_s { ipfmutex_t ipf_syncadd; ipfmutex_t ipsl_mutex; ipfrwlock_t ipf_syncstate; ipfrwlock_t ipf_syncnat; #if SOLARIS && defined(_KERNEL) kcondvar_t ipslwait; #endif synclist_t **syncstatetab; synclist_t **syncnattab; synclogent_t *synclog; syncupdent_t *syncupd; u_int ipf_sync_num; u_int ipf_sync_wrap; u_int sl_idx; /* next available sync log entry */ u_int su_idx; /* next available sync update entry */ u_int sl_tail; /* next sync log entry to read */ u_int su_tail; /* next sync update entry to read */ int ipf_sync_log_sz; int ipf_sync_nat_tab_sz; int ipf_sync_state_tab_sz; int ipf_sync_debug; int ipf_sync_events; u_32_t ipf_sync_lastwakeup; int ipf_sync_wake_interval; int ipf_sync_event_high_wm; int ipf_sync_queue_high_wm; int ipf_sync_inited; } ipf_sync_softc_t; -static int ipf_sync_flush_table __P((ipf_sync_softc_t *, int, synclist_t **)); -static void ipf_sync_wakeup __P((ipf_main_softc_t *)); -static void ipf_sync_del __P((ipf_sync_softc_t *, synclist_t *)); -static void ipf_sync_poll_wakeup __P((ipf_main_softc_t *)); -static int ipf_sync_nat __P((ipf_main_softc_t *, synchdr_t *, void *)); -static int ipf_sync_state __P((ipf_main_softc_t *, synchdr_t *, void *)); +static int ipf_sync_flush_table(ipf_sync_softc_t *, int, synclist_t **); +static void ipf_sync_wakeup(ipf_main_softc_t *); +static void ipf_sync_del(ipf_sync_softc_t *, synclist_t *); +static void ipf_sync_poll_wakeup(ipf_main_softc_t *); +static int ipf_sync_nat(ipf_main_softc_t *, synchdr_t *, void *); +static int ipf_sync_state(ipf_main_softc_t *, synchdr_t *, void *); # if !defined(sparc) && !defined(__hppa) -void ipf_sync_tcporder __P((int, struct tcpdata *)); -void ipf_sync_natorder __P((int, struct nat *)); -void ipf_sync_storder __P((int, struct ipstate *)); +void ipf_sync_tcporder(int, struct tcpdata *); +void ipf_sync_natorder(int, struct nat *); +void ipf_sync_storder(int, struct ipstate *); # endif void * ipf_sync_soft_create(softc) ipf_main_softc_t *softc; { ipf_sync_softc_t *softs; KMALLOC(softs, ipf_sync_softc_t *); if (softs == NULL) { IPFERROR(110024); return NULL; } bzero((char *)softs, sizeof(*softs)); softs->ipf_sync_log_sz = SYNCLOG_SZ; softs->ipf_sync_nat_tab_sz = SYNC_STATETABSZ; softs->ipf_sync_state_tab_sz = SYNC_STATETABSZ; softs->ipf_sync_event_high_wm = SYNCLOG_SZ * 100 / 90; /* 90% */ softs->ipf_sync_queue_high_wm = SYNCLOG_SZ * 100 / 90; /* 90% */ return softs; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_init */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* Initialise all of the locks required for the sync code and initialise */ /* any data structures, as required. */ /* ------------------------------------------------------------------------ */ int ipf_sync_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_sync_softc_t *softs = arg; KMALLOCS(softs->synclog, synclogent_t *, softs->ipf_sync_log_sz * sizeof(*softs->synclog)); if (softs->synclog == NULL) return -1; bzero((char *)softs->synclog, softs->ipf_sync_log_sz * sizeof(*softs->synclog)); KMALLOCS(softs->syncupd, syncupdent_t *, softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); if (softs->syncupd == NULL) return -2; bzero((char *)softs->syncupd, softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); KMALLOCS(softs->syncstatetab, synclist_t **, softs->ipf_sync_state_tab_sz * sizeof(*softs->syncstatetab)); if (softs->syncstatetab == NULL) return -3; bzero((char *)softs->syncstatetab, softs->ipf_sync_state_tab_sz * sizeof(*softs->syncstatetab)); KMALLOCS(softs->syncnattab, synclist_t **, softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); if (softs->syncnattab == NULL) return -3; bzero((char *)softs->syncnattab, softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); softs->ipf_sync_num = 1; softs->ipf_sync_wrap = 0; softs->sl_idx = 0; softs->su_idx = 0; softs->sl_tail = 0; softs->su_tail = 0; softs->ipf_sync_events = 0; softs->ipf_sync_lastwakeup = 0; # if SOLARIS && defined(_KERNEL) cv_init(&softs->ipslwait, "ipsl condvar", CV_DRIVER, NULL); # endif RWLOCK_INIT(&softs->ipf_syncstate, "add things to state sync table"); RWLOCK_INIT(&softs->ipf_syncnat, "add things to nat sync table"); MUTEX_INIT(&softs->ipf_syncadd, "add things to sync table"); MUTEX_INIT(&softs->ipsl_mutex, "read ring lock"); softs->ipf_sync_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_unload */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* Destroy the locks created when initialising and free any memory in use */ /* with the synchronisation tables. */ /* ------------------------------------------------------------------------ */ int ipf_sync_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_sync_softc_t *softs = arg; if (softs->syncnattab != NULL) { ipf_sync_flush_table(softs, softs->ipf_sync_nat_tab_sz, softs->syncnattab); KFREES(softs->syncnattab, softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); softs->syncnattab = NULL; } if (softs->syncstatetab != NULL) { ipf_sync_flush_table(softs, softs->ipf_sync_state_tab_sz, softs->syncstatetab); KFREES(softs->syncstatetab, softs->ipf_sync_state_tab_sz * sizeof(*softs->syncstatetab)); softs->syncstatetab = NULL; } if (softs->syncupd != NULL) { KFREES(softs->syncupd, softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); softs->syncupd = NULL; } if (softs->synclog != NULL) { KFREES(softs->synclog, softs->ipf_sync_log_sz * sizeof(*softs->synclog)); softs->synclog = NULL; } if (softs->ipf_sync_inited == 1) { MUTEX_DESTROY(&softs->ipsl_mutex); MUTEX_DESTROY(&softs->ipf_syncadd); RW_DESTROY(&softs->ipf_syncnat); RW_DESTROY(&softs->ipf_syncstate); softs->ipf_sync_inited = 0; } return 0; } void ipf_sync_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_sync_softc_t *softs = arg; KFREE(softs); } # if !defined(sparc) /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_tcporder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* td(IO) - pointer to data to be converted. */ /* */ /* Do byte swapping on values in the TCP state information structure that */ /* need to be used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ void ipf_sync_tcporder(way, td) int way; tcpdata_t *td; { if (way) { td->td_maxwin = htons(td->td_maxwin); td->td_end = htonl(td->td_end); td->td_maxend = htonl(td->td_maxend); } else { td->td_maxwin = ntohs(td->td_maxwin); td->td_end = ntohl(td->td_end); td->td_maxend = ntohl(td->td_maxend); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_natorder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* nat(IO) - pointer to data to be converted. */ /* */ /* Do byte swapping on values in the NAT data structure that need to be */ /* used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ void ipf_sync_natorder(way, n) int way; nat_t *n; { if (way) { n->nat_age = htonl(n->nat_age); n->nat_flags = htonl(n->nat_flags); n->nat_ipsumd = htonl(n->nat_ipsumd); n->nat_use = htonl(n->nat_use); n->nat_dir = htonl(n->nat_dir); } else { n->nat_age = ntohl(n->nat_age); n->nat_flags = ntohl(n->nat_flags); n->nat_ipsumd = ntohl(n->nat_ipsumd); n->nat_use = ntohl(n->nat_use); n->nat_dir = ntohl(n->nat_dir); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_storder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* ips(IO) - pointer to data to be converted. */ /* */ /* Do byte swapping on values in the IP state data structure that need to */ /* be used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ void ipf_sync_storder(way, ips) int way; ipstate_t *ips; { ipf_sync_tcporder(way, &ips->is_tcp.ts_data[0]); ipf_sync_tcporder(way, &ips->is_tcp.ts_data[1]); if (way) { ips->is_hv = htonl(ips->is_hv); ips->is_die = htonl(ips->is_die); ips->is_pass = htonl(ips->is_pass); ips->is_flags = htonl(ips->is_flags); ips->is_opt[0] = htonl(ips->is_opt[0]); ips->is_opt[1] = htonl(ips->is_opt[1]); ips->is_optmsk[0] = htonl(ips->is_optmsk[0]); ips->is_optmsk[1] = htonl(ips->is_optmsk[1]); ips->is_sec = htons(ips->is_sec); ips->is_secmsk = htons(ips->is_secmsk); ips->is_auth = htons(ips->is_auth); ips->is_authmsk = htons(ips->is_authmsk); ips->is_s0[0] = htonl(ips->is_s0[0]); ips->is_s0[1] = htonl(ips->is_s0[1]); ips->is_smsk[0] = htons(ips->is_smsk[0]); ips->is_smsk[1] = htons(ips->is_smsk[1]); } else { ips->is_hv = ntohl(ips->is_hv); ips->is_die = ntohl(ips->is_die); ips->is_pass = ntohl(ips->is_pass); ips->is_flags = ntohl(ips->is_flags); ips->is_opt[0] = ntohl(ips->is_opt[0]); ips->is_opt[1] = ntohl(ips->is_opt[1]); ips->is_optmsk[0] = ntohl(ips->is_optmsk[0]); ips->is_optmsk[1] = ntohl(ips->is_optmsk[1]); ips->is_sec = ntohs(ips->is_sec); ips->is_secmsk = ntohs(ips->is_secmsk); ips->is_auth = ntohs(ips->is_auth); ips->is_authmsk = ntohs(ips->is_authmsk); ips->is_s0[0] = ntohl(ips->is_s0[0]); ips->is_s0[1] = ntohl(ips->is_s0[1]); ips->is_smsk[0] = ntohl(ips->is_smsk[0]); ips->is_smsk[1] = ntohl(ips->is_smsk[1]); } } # else /* !defined(sparc) */ # define ipf_sync_tcporder(x,y) # define ipf_sync_natorder(x,y) # define ipf_sync_storder(x,y) # endif /* !defined(sparc) */ /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_write */ /* Returns: int - 0 == success, else error value. */ /* Parameters: uio(I) - pointer to information about data to write */ /* */ /* Moves data from user space into the kernel and uses it for updating data */ /* structures in the state/NAT tables. */ /* ------------------------------------------------------------------------ */ int ipf_sync_write(softc, uio) ipf_main_softc_t *softc; struct uio *uio; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; synchdr_t sh; /* * THIS MUST BE SUFFICIENT LARGE TO STORE * ANY POSSIBLE DATA TYPE */ char data[2048]; int err = 0; # if defined(__NetBSD__) || defined(__FreeBSD__) uio->uio_rw = UIO_WRITE; # endif /* Try to get bytes */ while (uio->uio_resid > 0) { if (uio->uio_resid >= sizeof(sh)) { err = UIOMOVE(&sh, sizeof(sh), UIO_WRITE, uio); if (err) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) failed: %d\n", err); return err; } /* convert to host order */ sh.sm_magic = ntohl(sh.sm_magic); sh.sm_len = ntohl(sh.sm_len); sh.sm_num = ntohl(sh.sm_num); if (softs->ipf_sync_debug > 8) printf("[%d] Read v:%d p:%d cmd:%d table:%d rev:%d len:%d magic:%x\n", sh.sm_num, sh.sm_v, sh.sm_p, sh.sm_cmd, sh.sm_table, sh.sm_rev, sh.sm_len, sh.sm_magic); if (sh.sm_magic != SYNHDRMAGIC) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "magic"); IPFERROR(110001); return EINVAL; } if (sh.sm_v != 4 && sh.sm_v != 6) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "protocol"); IPFERROR(110002); return EINVAL; } if (sh.sm_cmd > SMC_MAXCMD) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "command"); IPFERROR(110003); return EINVAL; } if (sh.sm_table > SMC_MAXTBL) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "table"); IPFERROR(110004); return EINVAL; } } else { /* unsufficient data, wait until next call */ if (softs->ipf_sync_debug > 2) printf("uiomove(header) insufficient data"); IPFERROR(110005); return EAGAIN; } /* * We have a header, so try to read the amount of data * needed for the request */ /* not supported */ if (sh.sm_len == 0) { if (softs->ipf_sync_debug > 2) printf("uiomove(data zero length %s\n", "not supported"); IPFERROR(110006); return EINVAL; } if (uio->uio_resid >= sh.sm_len) { err = UIOMOVE(data, sh.sm_len, UIO_WRITE, uio); if (err) { if (softs->ipf_sync_debug > 2) printf("uiomove(data) failed: %d\n", err); return err; } if (softs->ipf_sync_debug > 7) printf("uiomove(data) %d bytes read\n", sh.sm_len); if (sh.sm_table == SMC_STATE) err = ipf_sync_state(softc, &sh, data); else if (sh.sm_table == SMC_NAT) err = ipf_sync_nat(softc, &sh, data); if (softs->ipf_sync_debug > 7) printf("[%d] Finished with error %d\n", sh.sm_num, err); } else { /* insufficient data, wait until next call */ if (softs->ipf_sync_debug > 2) printf("uiomove(data) %s %d bytes, got %d\n", "insufficient data, need", sh.sm_len, (int)uio->uio_resid); IPFERROR(110007); return EAGAIN; } } /* no more data */ return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_read */ /* Returns: int - 0 == success, else error value. */ /* Parameters: uio(O) - pointer to information about where to store data */ /* */ /* This function is called when a user program wants to read some data */ /* for pending state/NAT updates. If no data is available, the caller is */ /* put to sleep, pending a wakeup from the "lower half" of this code. */ /* ------------------------------------------------------------------------ */ int ipf_sync_read(softc, uio) ipf_main_softc_t *softc; struct uio *uio; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; syncupdent_t *su; synclogent_t *sl; int err = 0; if ((uio->uio_resid & 3) || (uio->uio_resid < 8)) { IPFERROR(110008); return EINVAL; } # if defined(__NetBSD__) || defined(__FreeBSD__) uio->uio_rw = UIO_READ; # endif MUTEX_ENTER(&softs->ipsl_mutex); while ((softs->sl_tail == softs->sl_idx) && (softs->su_tail == softs->su_idx)) { # if defined(_KERNEL) # if SOLARIS if (!cv_wait_sig(&softs->ipslwait, &softs->ipsl_mutex.ipf_lk)) { MUTEX_EXIT(&softs->ipsl_mutex); IPFERROR(110009); return EINTR; } # else MUTEX_EXIT(&softs->ipsl_mutex); err = SLEEP(&softs->sl_tail, "ipl sleep"); if (err) { IPFERROR(110012); return EINTR; } MUTEX_ENTER(&softs->ipsl_mutex); # endif /* SOLARIS */ # endif /* _KERNEL */ } while ((softs->sl_tail < softs->sl_idx) && (uio->uio_resid > sizeof(*sl))) { sl = softs->synclog + softs->sl_tail++; MUTEX_EXIT(&softs->ipsl_mutex); err = UIOMOVE(sl, sizeof(*sl), UIO_READ, uio); if (err != 0) goto goterror; MUTEX_ENTER(&softs->ipsl_mutex); } while ((softs->su_tail < softs->su_idx) && (uio->uio_resid > sizeof(*su))) { su = softs->syncupd + softs->su_tail; softs->su_tail++; MUTEX_EXIT(&softs->ipsl_mutex); err = UIOMOVE(su, sizeof(*su), UIO_READ, uio); if (err != 0) goto goterror; MUTEX_ENTER(&softs->ipsl_mutex); if (su->sup_hdr.sm_sl != NULL) su->sup_hdr.sm_sl->sl_idx = -1; } if (softs->sl_tail == softs->sl_idx) softs->sl_tail = softs->sl_idx = 0; if (softs->su_tail == softs->su_idx) softs->su_tail = softs->su_idx = 0; MUTEX_EXIT(&softs->ipsl_mutex); goterror: return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_state */ /* Returns: int - 0 == success, else error value. */ /* Parameters: sp(I) - pointer to sync packet data header */ /* uio(I) - pointer to user data for further information */ /* */ /* Updates the state table according to information passed in the sync */ /* header. As required, more data is fetched from the uio structure but */ /* varies depending on the contents of the sync header. This function can */ /* create a new state entry or update one. Deletion is left to the state */ /* structures being timed out correctly. */ /* ------------------------------------------------------------------------ */ static int ipf_sync_state(softc, sp, data) ipf_main_softc_t *softc; synchdr_t *sp; void *data; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; synctcp_update_t su; ipstate_t *is, sn; synclist_t *sl; frentry_t *fr; u_int hv; int err = 0; hv = sp->sm_num & (softs->ipf_sync_state_tab_sz - 1); switch (sp->sm_cmd) { case SMC_CREATE : bcopy(data, &sn, sizeof(sn)); KMALLOC(is, ipstate_t *); if (is == NULL) { IPFERROR(110013); err = ENOMEM; break; } KMALLOC(sl, synclist_t *); if (sl == NULL) { IPFERROR(110014); err = ENOMEM; KFREE(is); break; } bzero((char *)is, offsetof(ipstate_t, is_die)); bcopy((char *)&sn.is_die, (char *)&is->is_die, sizeof(*is) - offsetof(ipstate_t, is_die)); ipf_sync_storder(0, is); /* * We need to find the same rule on the slave as was used on * the master to create this state entry. */ READ_ENTER(&softc->ipf_mutex); fr = ipf_getrulen(softc, IPL_LOGIPF, sn.is_group, sn.is_rulen); if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; fr->fr_statecnt++; MUTEX_EXIT(&fr->fr_lock); } RWLOCK_EXIT(&softc->ipf_mutex); if (softs->ipf_sync_debug > 4) printf("[%d] Filter rules = %p\n", sp->sm_num, fr); is->is_rule = fr; is->is_sync = sl; sl->sl_idx = -1; sl->sl_ips = is; bcopy(sp, &sl->sl_hdr, sizeof(struct synchdr)); WRITE_ENTER(&softs->ipf_syncstate); WRITE_ENTER(&softc->ipf_state); sl->sl_pnext = softs->syncstatetab + hv; sl->sl_next = softs->syncstatetab[hv]; if (softs->syncstatetab[hv] != NULL) softs->syncstatetab[hv]->sl_pnext = &sl->sl_next; softs->syncstatetab[hv] = sl; MUTEX_DOWNGRADE(&softs->ipf_syncstate); ipf_state_insert(softc, is, sp->sm_rev); /* * Do not initialise the interface pointers for the state * entry as the full complement of interface names may not * be present. * * Put this state entry on its timeout queue. */ /*fr_setstatequeue(is, sp->sm_rev);*/ break; case SMC_UPDATE : bcopy(data, &su, sizeof(su)); if (softs->ipf_sync_debug > 4) printf("[%d] Update age %lu state %d/%d \n", sp->sm_num, su.stu_age, su.stu_state[0], su.stu_state[1]); READ_ENTER(&softs->ipf_syncstate); for (sl = softs->syncstatetab[hv]; (sl != NULL); sl = sl->sl_next) if (sl->sl_hdr.sm_num == sp->sm_num) break; if (sl == NULL) { if (softs->ipf_sync_debug > 1) printf("[%d] State not found - can't update\n", sp->sm_num); RWLOCK_EXIT(&softs->ipf_syncstate); IPFERROR(110015); err = ENOENT; break; } READ_ENTER(&softc->ipf_state); if (softs->ipf_sync_debug > 6) printf("[%d] Data from state v:%d p:%d cmd:%d table:%d rev:%d\n", sp->sm_num, sl->sl_hdr.sm_v, sl->sl_hdr.sm_p, sl->sl_hdr.sm_cmd, sl->sl_hdr.sm_table, sl->sl_hdr.sm_rev); is = sl->sl_ips; MUTEX_ENTER(&is->is_lock); switch (sp->sm_p) { case IPPROTO_TCP : /* XXX FV --- shouldn't we do ntohl/htonl???? XXX */ is->is_send = su.stu_data[0].td_end; is->is_maxsend = su.stu_data[0].td_maxend; is->is_maxswin = su.stu_data[0].td_maxwin; is->is_state[0] = su.stu_state[0]; is->is_dend = su.stu_data[1].td_end; is->is_maxdend = su.stu_data[1].td_maxend; is->is_maxdwin = su.stu_data[1].td_maxwin; is->is_state[1] = su.stu_state[1]; break; default : break; } if (softs->ipf_sync_debug > 6) printf("[%d] Setting timers for state\n", sp->sm_num); ipf_state_setqueue(softc, is, sp->sm_rev); MUTEX_EXIT(&is->is_lock); break; default : IPFERROR(110016); err = EINVAL; break; } if (err == 0) { RWLOCK_EXIT(&softc->ipf_state); RWLOCK_EXIT(&softs->ipf_syncstate); } if (softs->ipf_sync_debug > 6) printf("[%d] Update completed with error %d\n", sp->sm_num, err); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_del */ /* Returns: Nil */ /* Parameters: sl(I) - pointer to synclist object to delete */ /* */ /* Deletes an object from the synclist. */ /* ------------------------------------------------------------------------ */ static void ipf_sync_del(softs, sl) ipf_sync_softc_t *softs; synclist_t *sl; { *sl->sl_pnext = sl->sl_next; if (sl->sl_next != NULL) sl->sl_next->sl_pnext = sl->sl_pnext; if (sl->sl_idx != -1) softs->syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_del_state */ /* Returns: Nil */ /* Parameters: sl(I) - pointer to synclist object to delete */ /* */ /* Deletes an object from the synclist state table and free's its memory. */ /* ------------------------------------------------------------------------ */ void ipf_sync_del_state(arg, sl) void *arg; synclist_t *sl; { ipf_sync_softc_t *softs = arg; WRITE_ENTER(&softs->ipf_syncstate); ipf_sync_del(softs, sl); RWLOCK_EXIT(&softs->ipf_syncstate); KFREE(sl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_del_nat */ /* Returns: Nil */ /* Parameters: sl(I) - pointer to synclist object to delete */ /* */ /* Deletes an object from the synclist nat table and free's its memory. */ /* ------------------------------------------------------------------------ */ void ipf_sync_del_nat(arg, sl) void *arg; synclist_t *sl; { ipf_sync_softc_t *softs = arg; WRITE_ENTER(&softs->ipf_syncnat); ipf_sync_del(softs, sl); RWLOCK_EXIT(&softs->ipf_syncnat); KFREE(sl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_nat */ /* Returns: int - 0 == success, else error value. */ /* Parameters: sp(I) - pointer to sync packet data header */ /* uio(I) - pointer to user data for further information */ /* */ /* Updates the NAT table according to information passed in the sync */ /* header. As required, more data is fetched from the uio structure but */ /* varies depending on the contents of the sync header. This function can */ /* create a new NAT entry or update one. Deletion is left to the NAT */ /* structures being timed out correctly. */ /* ------------------------------------------------------------------------ */ static int ipf_sync_nat(softc, sp, data) ipf_main_softc_t *softc; synchdr_t *sp; void *data; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; syncupdent_t su; nat_t *n, *nat; synclist_t *sl; u_int hv = 0; int err = 0; READ_ENTER(&softs->ipf_syncnat); switch (sp->sm_cmd) { case SMC_CREATE : KMALLOC(n, nat_t *); if (n == NULL) { IPFERROR(110017); err = ENOMEM; break; } KMALLOC(sl, synclist_t *); if (sl == NULL) { IPFERROR(110018); err = ENOMEM; KFREE(n); break; } nat = (nat_t *)data; bzero((char *)n, offsetof(nat_t, nat_age)); bcopy((char *)&nat->nat_age, (char *)&n->nat_age, sizeof(*n) - offsetof(nat_t, nat_age)); ipf_sync_natorder(0, n); n->nat_sync = sl; n->nat_rev = sl->sl_rev; sl->sl_idx = -1; sl->sl_ipn = n; sl->sl_num = ntohl(sp->sm_num); WRITE_ENTER(&softc->ipf_nat); sl->sl_pnext = softs->syncnattab + hv; sl->sl_next = softs->syncnattab[hv]; if (softs->syncnattab[hv] != NULL) softs->syncnattab[hv]->sl_pnext = &sl->sl_next; softs->syncnattab[hv] = sl; (void) ipf_nat_insert(softc, softc->ipf_nat_soft, n); RWLOCK_EXIT(&softc->ipf_nat); break; case SMC_UPDATE : bcopy(data, &su, sizeof(su)); for (sl = softs->syncnattab[hv]; (sl != NULL); sl = sl->sl_next) if (sl->sl_hdr.sm_num == sp->sm_num) break; if (sl == NULL) { IPFERROR(110019); err = ENOENT; break; } READ_ENTER(&softc->ipf_nat); nat = sl->sl_ipn; nat->nat_rev = sl->sl_rev; MUTEX_ENTER(&nat->nat_lock); ipf_nat_setqueue(softc, softc->ipf_nat_soft, nat); MUTEX_EXIT(&nat->nat_lock); RWLOCK_EXIT(&softc->ipf_nat); break; default : IPFERROR(110020); err = EINVAL; break; } RWLOCK_EXIT(&softs->ipf_syncnat); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_new */ /* Returns: synclist_t* - NULL == failure, else pointer to new synclist */ /* data structure. */ /* Parameters: tab(I) - type of synclist_t to create */ /* fin(I) - pointer to packet information */ /* ptr(I) - pointer to owning object */ /* */ /* Creates a new sync table entry and notifies any sleepers that it's there */ /* waiting to be processed. */ /* ------------------------------------------------------------------------ */ synclist_t * ipf_sync_new(softc, tab, fin, ptr) ipf_main_softc_t *softc; int tab; fr_info_t *fin; void *ptr; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; synclist_t *sl, *ss; synclogent_t *sle; u_int hv, sz; if (softs->sl_idx == softs->ipf_sync_log_sz) return NULL; KMALLOC(sl, synclist_t *); if (sl == NULL) return NULL; MUTEX_ENTER(&softs->ipf_syncadd); /* * Get a unique number for this synclist_t. The number is only meant * to be unique for the lifetime of the structure and may be reused * later. */ softs->ipf_sync_num++; if (softs->ipf_sync_num == 0) { softs->ipf_sync_num = 1; softs->ipf_sync_wrap++; } /* * Use the synch number of the object as the hash key. Should end up * with relatively even distribution over time. * XXX - an attacker could lunch an DoS attack, of sorts, if they are * the only one causing new table entries by only keeping open every * nth connection they make, where n is a value in the interval * [0, SYNC_STATETABSZ-1]. */ switch (tab) { case SMC_STATE : hv = softs->ipf_sync_num & (softs->ipf_sync_state_tab_sz - 1); while (softs->ipf_sync_wrap != 0) { for (ss = softs->syncstatetab[hv]; ss; ss = ss->sl_next) if (ss->sl_hdr.sm_num == softs->ipf_sync_num) break; if (ss == NULL) break; softs->ipf_sync_num++; hv = softs->ipf_sync_num & (softs->ipf_sync_state_tab_sz - 1); } sl->sl_pnext = softs->syncstatetab + hv; sl->sl_next = softs->syncstatetab[hv]; softs->syncstatetab[hv] = sl; break; case SMC_NAT : hv = softs->ipf_sync_num & (softs->ipf_sync_nat_tab_sz - 1); while (softs->ipf_sync_wrap != 0) { for (ss = softs->syncnattab[hv]; ss; ss = ss->sl_next) if (ss->sl_hdr.sm_num == softs->ipf_sync_num) break; if (ss == NULL) break; softs->ipf_sync_num++; hv = softs->ipf_sync_num & (softs->ipf_sync_nat_tab_sz - 1); } sl->sl_pnext = softs->syncnattab + hv; sl->sl_next = softs->syncnattab[hv]; softs->syncnattab[hv] = sl; break; default : break; } sl->sl_num = softs->ipf_sync_num; MUTEX_EXIT(&softs->ipf_syncadd); sl->sl_magic = htonl(SYNHDRMAGIC); sl->sl_v = fin->fin_v; sl->sl_p = fin->fin_p; sl->sl_cmd = SMC_CREATE; sl->sl_idx = -1; sl->sl_table = tab; sl->sl_rev = fin->fin_rev; if (tab == SMC_STATE) { sl->sl_ips = ptr; sz = sizeof(*sl->sl_ips); } else if (tab == SMC_NAT) { sl->sl_ipn = ptr; sz = sizeof(*sl->sl_ipn); } else { ptr = NULL; sz = 0; } sl->sl_len = sz; /* * Create the log entry to be read by a user daemon. When it has been * finished and put on the queue, send a signal to wakeup any waiters. */ MUTEX_ENTER(&softs->ipf_syncadd); sle = softs->synclog + softs->sl_idx++; bcopy((char *)&sl->sl_hdr, (char *)&sle->sle_hdr, sizeof(sle->sle_hdr)); sle->sle_hdr.sm_num = htonl(sle->sle_hdr.sm_num); sle->sle_hdr.sm_len = htonl(sle->sle_hdr.sm_len); if (ptr != NULL) { bcopy((char *)ptr, (char *)&sle->sle_un, sz); if (tab == SMC_STATE) { ipf_sync_storder(1, &sle->sle_un.sleu_ips); } else if (tab == SMC_NAT) { ipf_sync_natorder(1, &sle->sle_un.sleu_ipn); } } MUTEX_EXIT(&softs->ipf_syncadd); ipf_sync_wakeup(softc); return sl; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_update */ /* Returns: Nil */ /* Parameters: tab(I) - type of synclist_t to create */ /* fin(I) - pointer to packet information */ /* sl(I) - pointer to synchronisation object */ /* */ /* For outbound packets, only, create an sync update record for the user */ /* process to read. */ /* ------------------------------------------------------------------------ */ void ipf_sync_update(softc, tab, fin, sl) ipf_main_softc_t *softc; int tab; fr_info_t *fin; synclist_t *sl; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; synctcp_update_t *st; syncupdent_t *slu; ipstate_t *ips; nat_t *nat; ipfrwlock_t *lock; if (fin->fin_out == 0 || sl == NULL) return; if (tab == SMC_STATE) { lock = &softs->ipf_syncstate; } else { lock = &softs->ipf_syncnat; } READ_ENTER(lock); if (sl->sl_idx == -1) { MUTEX_ENTER(&softs->ipf_syncadd); slu = softs->syncupd + softs->su_idx; sl->sl_idx = softs->su_idx++; MUTEX_EXIT(&softs->ipf_syncadd); bcopy((char *)&sl->sl_hdr, (char *)&slu->sup_hdr, sizeof(slu->sup_hdr)); slu->sup_hdr.sm_magic = htonl(SYNHDRMAGIC); slu->sup_hdr.sm_sl = sl; slu->sup_hdr.sm_cmd = SMC_UPDATE; slu->sup_hdr.sm_table = tab; slu->sup_hdr.sm_num = htonl(sl->sl_num); slu->sup_hdr.sm_len = htonl(sizeof(struct synctcp_update)); slu->sup_hdr.sm_rev = fin->fin_rev; # if 0 if (fin->fin_p == IPPROTO_TCP) { st->stu_len[0] = 0; st->stu_len[1] = 0; } # endif } else slu = softs->syncupd + sl->sl_idx; /* * Only TCP has complex timeouts, others just use default timeouts. * For TCP, we only need to track the connection state and window. */ if (fin->fin_p == IPPROTO_TCP) { st = &slu->sup_tcp; if (tab == SMC_STATE) { ips = sl->sl_ips; st->stu_age = htonl(ips->is_die); st->stu_data[0].td_end = ips->is_send; st->stu_data[0].td_maxend = ips->is_maxsend; st->stu_data[0].td_maxwin = ips->is_maxswin; st->stu_state[0] = ips->is_state[0]; st->stu_data[1].td_end = ips->is_dend; st->stu_data[1].td_maxend = ips->is_maxdend; st->stu_data[1].td_maxwin = ips->is_maxdwin; st->stu_state[1] = ips->is_state[1]; } else if (tab == SMC_NAT) { nat = sl->sl_ipn; st->stu_age = htonl(nat->nat_age); } } RWLOCK_EXIT(lock); ipf_sync_wakeup(softc); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_flush_table */ /* Returns: int - number of entries freed by flushing table */ /* Parameters: tabsize(I) - size of the array pointed to by table */ /* table(I) - pointer to sync table to empty */ /* */ /* Walk through a table of sync entries and free each one. It is assumed */ /* that some lock is held so that nobody else tries to access the table */ /* during this cleanup. */ /* ------------------------------------------------------------------------ */ static int ipf_sync_flush_table(softs, tabsize, table) ipf_sync_softc_t *softs; int tabsize; synclist_t **table; { synclist_t *sl; int i, items; items = 0; for (i = 0; i < tabsize; i++) { while ((sl = table[i]) != NULL) { switch (sl->sl_table) { case SMC_STATE : if (sl->sl_ips != NULL) sl->sl_ips->is_sync = NULL; break; case SMC_NAT : if (sl->sl_ipn != NULL) sl->sl_ipn->nat_sync = NULL; break; } if (sl->sl_next != NULL) sl->sl_next->sl_pnext = sl->sl_pnext; table[i] = sl->sl_next; if (sl->sl_idx != -1) softs->syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; KFREE(sl); items++; } } return items; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command integer */ /* mode(I) - file mode bits used with open */ /* */ /* This function currently does not handle any ioctls and so just returns */ /* EINVAL on all occasions. */ /* ------------------------------------------------------------------------ */ int ipf_sync_ioctl(softc, data, cmd, mode, uid, ctx) ipf_main_softc_t *softc; caddr_t data; ioctlcmd_t cmd; int mode, uid; void *ctx; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; int error, i; SPL_INT(s); switch (cmd) { case SIOCIPFFL: error = BCOPYIN(data, &i, sizeof(i)); if (error != 0) { IPFERROR(110023); error = EFAULT; break; } switch (i) { case SMC_RLOG : SPL_NET(s); MUTEX_ENTER(&softs->ipsl_mutex); i = (softs->sl_tail - softs->sl_idx) + (softs->su_tail - softs->su_idx); softs->sl_idx = 0; softs->su_idx = 0; softs->sl_tail = 0; softs->su_tail = 0; MUTEX_EXIT(&softs->ipsl_mutex); SPL_X(s); break; case SMC_NAT : SPL_NET(s); WRITE_ENTER(&softs->ipf_syncnat); i = ipf_sync_flush_table(softs, SYNC_NATTABSZ, softs->syncnattab); RWLOCK_EXIT(&softs->ipf_syncnat); SPL_X(s); break; case SMC_STATE : SPL_NET(s); WRITE_ENTER(&softs->ipf_syncstate); i = ipf_sync_flush_table(softs, SYNC_STATETABSZ, softs->syncstatetab); RWLOCK_EXIT(&softs->ipf_syncstate); SPL_X(s); break; } error = BCOPYOUT(&i, data, sizeof(i)); if (error != 0) { IPFERROR(110022); error = EFAULT; } break; default : IPFERROR(110021); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_canread */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: Nil */ /* */ /* This function provides input to the poll handler about whether or not */ /* there is data waiting to be read from the /dev/ipsync device. */ /* ------------------------------------------------------------------------ */ int ipf_sync_canread(arg) void *arg; { ipf_sync_softc_t *softs = arg; return !((softs->sl_tail == softs->sl_idx) && (softs->su_tail == softs->su_idx)); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_canwrite */ /* Returns: int - 1 == can always write */ /* Parameters: Nil */ /* */ /* This function lets the poll handler know that it is always ready willing */ /* to accept write events. */ /* XXX Maybe this should return false if the sync table is full? */ /* ------------------------------------------------------------------------ */ int ipf_sync_canwrite(arg) void *arg; { return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_wakeup */ /* Parameters: Nil */ /* Returns: Nil */ /* */ /* This function implements the heuristics that decide how often to */ /* generate a poll wakeup for programs that are waiting for information */ /* about when they can do a read on /dev/ipsync. */ /* */ /* There are three different considerations here: */ /* - do not keep a program waiting too long: ipf_sync_wake_interval is the */ /* maximum number of ipf ticks to let pass by; */ /* - do not let the queue of ouststanding things to generate notifies for */ /* get too full (ipf_sync_queue_high_wm is the high water mark); */ /* - do not let too many events get collapsed in before deciding that the */ /* other host(s) need an update (ipf_sync_event_high_wm is the high water */ /* mark for this counter.) */ /* ------------------------------------------------------------------------ */ static void ipf_sync_wakeup(softc) ipf_main_softc_t *softc; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; softs->ipf_sync_events++; if ((softc->ipf_ticks > softs->ipf_sync_lastwakeup + softs->ipf_sync_wake_interval) || (softs->ipf_sync_events > softs->ipf_sync_event_high_wm) || ((softs->sl_tail - softs->sl_idx) > softs->ipf_sync_queue_high_wm) || ((softs->su_tail - softs->su_idx) > softs->ipf_sync_queue_high_wm)) { ipf_sync_poll_wakeup(softc); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_poll_wakeup */ /* Parameters: Nil */ /* Returns: Nil */ /* */ /* Deliver a poll wakeup and reset counters for two of the three heuristics */ /* ------------------------------------------------------------------------ */ static void ipf_sync_poll_wakeup(softc) ipf_main_softc_t *softc; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; softs->ipf_sync_events = 0; softs->ipf_sync_lastwakeup = softc->ipf_ticks; # ifdef _KERNEL # if SOLARIS MUTEX_ENTER(&softs->ipsl_mutex); cv_signal(&softs->ipslwait); MUTEX_EXIT(&softs->ipsl_mutex); pollwakeup(&softc->ipf_poll_head[IPL_LOGSYNC], POLLIN|POLLRDNORM); # else WAKEUP(&softs->sl_tail, 0); POLLWAKEUP(IPL_LOGSYNC); # endif # endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_expire */ /* Parameters: Nil */ /* Returns: Nil */ /* */ /* This is the function called even ipf_tick. It implements one of the */ /* three heuristics above *IF* there are events waiting. */ /* ------------------------------------------------------------------------ */ void ipf_sync_expire(softc) ipf_main_softc_t *softc; { ipf_sync_softc_t *softs = softc->ipf_sync_soft; if ((softs->ipf_sync_events > 0) && (softc->ipf_ticks > softs->ipf_sync_lastwakeup + softs->ipf_sync_wake_interval)) { ipf_sync_poll_wakeup(softc); } } diff --git a/sys/contrib/ipfilter/netinet/ip_sync.h b/sys/contrib/ipfilter/netinet/ip_sync.h index d9d6d41046e8..ad9c8627e8dd 100644 --- a/sys/contrib/ipfilter/netinet/ip_sync.h +++ b/sys/contrib/ipfilter/netinet/ip_sync.h @@ -1,121 +1,121 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_fil.h 1.35 6/5/96 * $Id$ */ #ifndef __IP_SYNC_H__ #define __IP_SYNC_H__ typedef struct synchdr { u_32_t sm_magic; /* magic */ u_char sm_v; /* version: 4,6 */ u_char sm_p; /* protocol */ u_char sm_cmd; /* command */ u_char sm_table; /* NAT, STATE, etc */ u_int sm_num; /* table entry number */ int sm_rev; /* forward/reverse */ int sm_len; /* length of the data section */ struct synclist *sm_sl; /* back pointer to parent */ } synchdr_t; #define SYNHDRMAGIC 0x0FF51DE5 /* * Commands * No delete required as expirey will take care of that! */ #define SMC_CREATE 0 /* pass ipstate_t after synchdr_t */ #define SMC_UPDATE 1 #define SMC_MAXCMD 1 /* * Tables */ #define SMC_RLOG -2 /* Only used with SIOCIPFFL */ #define SMC_NAT 0 #define SMC_STATE 1 #define SMC_MAXTBL 1 /* * Only TCP requires "more" information than just a reference to the entry * for which an update is being made. */ typedef struct synctcp_update { u_long stu_age; tcpdata_t stu_data[2]; int stu_state[2]; } synctcp_update_t; typedef struct synclist { struct synclist *sl_next; struct synclist **sl_pnext; int sl_idx; /* update index */ struct synchdr sl_hdr; union { struct ipstate *slu_ips; struct nat *slu_ipn; void *slu_ptr; } sl_un; } synclist_t; #define sl_ptr sl_un.slu_ptr #define sl_ips sl_un.slu_ips #define sl_ipn sl_un.slu_ipn #define sl_magic sl_hdr.sm_magic #define sl_v sl_hdr.sm_v #define sl_p sl_hdr.sm_p #define sl_cmd sl_hdr.sm_cmd #define sl_rev sl_hdr.sm_rev #define sl_table sl_hdr.sm_table #define sl_num sl_hdr.sm_num #define sl_len sl_hdr.sm_len /* * NOTE: SYNCLOG_SZ is defined *low*. It should be the next power of two * up for whatever number of packets per second you expect to see. Be * warned: this index's a table of large elements (upto 272 bytes in size * each), and thus a size of 8192, for example, results in a 2MB table. * The lesson here is not to use small machines for running fast firewalls * (100BaseT) in sync, where you might have upwards of 10k pps. */ #define SYNCLOG_SZ 256 typedef struct synclogent { struct synchdr sle_hdr; union { struct ipstate sleu_ips; struct nat sleu_ipn; } sle_un; } synclogent_t; typedef struct syncupdent { /* 28 or 32 bytes */ struct synchdr sup_hdr; struct synctcp_update sup_tcp; } syncupdent_t; -extern void *ipf_sync_create __P((ipf_main_softc_t *)); -extern int ipf_sync_soft_init __P((ipf_main_softc_t *, void *)); -extern int ipf_sync_soft_fini __P((ipf_main_softc_t *, void *)); -extern int ipf_sync_canread __P((void *)); -extern int ipf_sync_canwrite __P((void *)); -extern void ipf_sync_del_nat __P((void *, synclist_t *)); -extern void ipf_sync_del_state __P((void *, synclist_t *)); -extern int ipf_sync_init __P((void)); -extern int ipf_sync_ioctl __P((ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *)); -extern synclist_t *ipf_sync_new __P((ipf_main_softc_t *, int, fr_info_t *, void *)); -extern int ipf_sync_read __P((ipf_main_softc_t *, struct uio *uio)); -extern int ipf_sync_write __P((ipf_main_softc_t *, struct uio *uio)); -extern int ipf_sync_main_unload __P((void)); -extern void ipf_sync_update __P((ipf_main_softc_t *, int, fr_info_t *, synclist_t *)); -extern void ipf_sync_expire __P((ipf_main_softc_t *)); -extern void ipf_sync_soft_destroy __P((ipf_main_softc_t *, void *)); -extern void *ipf_sync_soft_create __P((ipf_main_softc_t *)); +extern void *ipf_sync_create(ipf_main_softc_t *); +extern int ipf_sync_soft_init(ipf_main_softc_t *, void *); +extern int ipf_sync_soft_fini(ipf_main_softc_t *, void *); +extern int ipf_sync_canread(void *); +extern int ipf_sync_canwrite(void *); +extern void ipf_sync_del_nat(void *, synclist_t *); +extern void ipf_sync_del_state(void *, synclist_t *); +extern int ipf_sync_init(void); +extern int ipf_sync_ioctl(ipf_main_softc_t *, caddr_t, ioctlcmd_t, int, int, void *); +extern synclist_t *ipf_sync_new(ipf_main_softc_t *, int, fr_info_t *, void *); +extern int ipf_sync_read(ipf_main_softc_t *, struct uio *uio); +extern int ipf_sync_write(ipf_main_softc_t *, struct uio *uio); +extern int ipf_sync_main_unload(void); +extern void ipf_sync_update(ipf_main_softc_t *, int, fr_info_t *, synclist_t *); +extern void ipf_sync_expire(ipf_main_softc_t *); +extern void ipf_sync_soft_destroy(ipf_main_softc_t *, void *); +extern void *ipf_sync_soft_create(ipf_main_softc_t *); #endif /* __IP_SYNC_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c b/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c index 9c0ef91ec9cf..6f6648899a44 100644 --- a/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c @@ -1,508 +1,508 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_tftp_pxy.c,v 1.1.2.9 2012/07/22 08:04:23 darren_r Exp $ */ #define IPF_TFTP_PROXY typedef struct ipf_tftp_softc_s { int ipf_p_tftp_readonly; ipftuneable_t *ipf_p_tftp_tune; } ipf_tftp_softc_t; -int ipf_p_tftp_backchannel __P((fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_tftp_client __P((ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, - nat_t *)); -int ipf_p_tftp_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -void ipf_p_tftp_main_load __P((void)); -void ipf_p_tftp_main_unload __P((void)); -int ipf_p_tftp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -void ipf_p_tftp_del __P((ipf_main_softc_t *, ap_session_t *)); -int ipf_p_tftp_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); -int ipf_p_tftp_server __P((ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, - nat_t *)); -void *ipf_p_tftp_soft_create __P((ipf_main_softc_t *)); -void ipf_p_tftp_soft_destroy __P((ipf_main_softc_t *, void *)); +int ipf_p_tftp_backchannel(fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_tftp_client(ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, + nat_t *); +int ipf_p_tftp_in(void *, fr_info_t *, ap_session_t *, nat_t *); +void ipf_p_tftp_main_load(void); +void ipf_p_tftp_main_unload(void); +int ipf_p_tftp_new(void *, fr_info_t *, ap_session_t *, nat_t *); +void ipf_p_tftp_del(ipf_main_softc_t *, ap_session_t *); +int ipf_p_tftp_out(void *, fr_info_t *, ap_session_t *, nat_t *); +int ipf_p_tftp_server(ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, + nat_t *); +void *ipf_p_tftp_soft_create(ipf_main_softc_t *); +void ipf_p_tftp_soft_destroy(ipf_main_softc_t *, void *); static frentry_t tftpfr; static int tftp_proxy_init = 0; typedef enum tftp_cmd_e { TFTP_CMD_READ = 1, TFTP_CMD_WRITE = 2, TFTP_CMD_DATA = 3, TFTP_CMD_ACK = 4, TFTP_CMD_ERROR = 5 } tftp_cmd_t; typedef struct tftpinfo { tftp_cmd_t ti_lastcmd; int ti_nextblk; int ti_lastblk; int ti_lasterror; char ti_filename[80]; ipnat_t *ti_rule; } tftpinfo_t; static ipftuneable_t ipf_tftp_tuneables[] = { { { (void *)offsetof(ipf_tftp_softc_t, ipf_p_tftp_readonly) }, "tftp_read_only", 0, 1, stsizeof(ipf_tftp_softc_t, ipf_p_tftp_readonly), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* * TFTP application proxy initialization. */ void ipf_p_tftp_main_load() { bzero((char *)&tftpfr, sizeof(tftpfr)); tftpfr.fr_ref = 1; tftpfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&tftpfr.fr_lock, "TFTP proxy rule lock"); tftp_proxy_init = 1; } void ipf_p_tftp_main_unload() { if (tftp_proxy_init == 1) { MUTEX_DESTROY(&tftpfr.fr_lock); tftp_proxy_init = 0; } } void * ipf_p_tftp_soft_create(softc) ipf_main_softc_t *softc; { ipf_tftp_softc_t *softt; KMALLOC(softt, ipf_tftp_softc_t *); if (softt == NULL) return NULL; bzero((char *)softt, sizeof(*softt)); softt->ipf_p_tftp_tune = ipf_tune_array_copy(softt, sizeof(ipf_tftp_tuneables), ipf_tftp_tuneables); if (softt->ipf_p_tftp_tune == NULL) { ipf_p_tftp_soft_destroy(softc, softt); return NULL; } if (ipf_tune_array_link(softc, softt->ipf_p_tftp_tune) == -1) { ipf_p_tftp_soft_destroy(softc, softt); return NULL; } softt->ipf_p_tftp_readonly = 1; return softt; } void ipf_p_tftp_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_tftp_softc_t *softt = arg; if (softt->ipf_p_tftp_tune != NULL) { ipf_tune_array_unlink(softc, softt->ipf_p_tftp_tune); KFREES(softt->ipf_p_tftp_tune, sizeof(ipf_tftp_tuneables)); softt->ipf_p_tftp_tune = NULL; } KFREE(softt); } int ipf_p_tftp_out(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipf_tftp_softc_t *softt = arg; fin->fin_flx |= FI_NOWILD; if (nat->nat_dir == NAT_OUTBOUND) return ipf_p_tftp_client(softt, fin, aps, nat); return ipf_p_tftp_server(softt, fin, aps, nat); } int ipf_p_tftp_in(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipf_tftp_softc_t *softt = arg; fin->fin_flx |= FI_NOWILD; if (nat->nat_dir == NAT_INBOUND) return ipf_p_tftp_client(softt, fin, aps, nat); return ipf_p_tftp_server(softt, fin, aps, nat); } int ipf_p_tftp_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { udphdr_t *udp; tftpinfo_t *ti; ipnat_t *ipn; ipnat_t *np; int size; fin = fin; /* LINT */ np = nat->nat_ptr; size = np->in_size; KMALLOC(ti, tftpinfo_t *); if (ti == NULL) return -1; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) { KFREE(ti); return -1; } aps->aps_data = ti; aps->aps_psiz = sizeof(*ti); bzero((char *)ti, sizeof(*ti)); bzero((char *)ipn, size); ti->ti_rule = ipn; udp = (udphdr_t *)fin->fin_dp; aps->aps_sport = udp->uh_sport; aps->aps_dport = udp->uh_dport; ipn->in_size = size; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_pr[0] = IPPROTO_UDP; ipn->in_pr[1] = IPPROTO_UDP; ipn->in_ifps[0] = nat->nat_ifps[0]; ipn->in_ifps[1] = nat->nat_ifps[1]; ipn->in_v[0] = nat->nat_ptr->in_v[1]; ipn->in_v[1] = nat->nat_ptr->in_v[0]; ipn->in_flags = IPN_UDP|IPN_FIXEDDPORT|IPN_PROXYRULE; ipn->in_nsrcip6 = nat->nat_odst6; ipn->in_osrcip6 = nat->nat_ndst6; if ((np->in_redir & NAT_REDIRECT) != 0) { ipn->in_redir = NAT_MAP; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_nsrcaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_nsrc6; #endif } ipn->in_ndstip6 = nat->nat_nsrc6; ipn->in_odstip6 = nat->nat_osrc6; } else { ipn->in_redir = NAT_REDIRECT; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_osrcaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_osrc6; #endif } ipn->in_ndstip6 = nat->nat_osrc6; ipn->in_odstip6 = nat->nat_nsrc6; } ipn->in_odport = htons(fin->fin_sport); ipn->in_ndport = htons(fin->fin_sport); IP6_SETONES(&ipn->in_osrcmsk6); IP6_SETONES(&ipn->in_nsrcmsk6); IP6_SETONES(&ipn->in_odstmsk6); IP6_SETONES(&ipn->in_ndstmsk6); MUTEX_INIT(&ipn->in_lock, "tftp proxy NAT rule"); ipn->in_namelen = np->in_namelen; bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); ipn->in_ifnames[0] = np->in_ifnames[0]; ipn->in_ifnames[1] = np->in_ifnames[1]; ti->ti_lastcmd = 0; return 0; } void ipf_p_tftp_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { tftpinfo_t *tftp; tftp = aps->aps_data; if (tftp != NULL) { tftp->ti_rule->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &tftp->ti_rule); } } /* * Setup for a new TFTP proxy. */ int ipf_p_tftp_backchannel(fin, aps, nat) fr_info_t *fin; ap_session_t *aps; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif #ifdef USE_INET6 i6addr_t swip6, sw2ip6; ip6_t *ip6; #endif struct in_addr swip, sw2ip; tftpinfo_t *ti; udphdr_t udp; fr_info_t fi; u_short slen = 0; /* silence gcc */ nat_t *nat2; int nflags; ip_t *ip; int dir; ti = aps->aps_data; /* * Add skeleton NAT entry for connection which will come back the * other way. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_flx |= FI_IGNORE; fi.fin_data[1] = 0; bzero((char *)&udp, sizeof(udp)); udp.uh_sport = 0; /* XXX - don't specify remote port */ udp.uh_dport = ti->ti_rule->in_ndport; udp.uh_ulen = htons(sizeof(udp)); udp.uh_sum = 0; fi.fin_fr = &tftpfr; fi.fin_dp = (char *)&udp; fi.fin_sport = 0; fi.fin_dport = ntohs(ti->ti_rule->in_ndport); fi.fin_dlen = sizeof(udp); fi.fin_plen = fi.fin_hlen + sizeof(udp); fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; nflags = NAT_SLAVE|IPN_UDP|SI_W_SPORT; #ifdef USE_INET6 ip6 = (ip6_t *)fin->fin_ip; #endif ip = fin->fin_ip; sw2ip.s_addr = 0; swip.s_addr = 0; fi.fin_src6 = nat->nat_ndst6; fi.fin_dst6 = nat->nat_nsrc6; if (nat->nat_v[0] == 4) { slen = ip->ip_len; ip->ip_len = htons(fin->fin_hlen + sizeof(udp)); swip = ip->ip_src; sw2ip = ip->ip_dst; ip->ip_src = nat->nat_ndstip; ip->ip_dst = nat->nat_nsrcip; } else { #ifdef USE_INET6 slen = ip6->ip6_plen; ip6->ip6_plen = htons(sizeof(udp)); swip6.in6 = ip6->ip6_src; sw2ip6.in6 = ip6->ip6_dst; ip6->ip6_src = nat->nat_ndst6.in6; ip6->ip6_dst = nat->nat_nsrc6.in6; #endif } if (nat->nat_dir == NAT_INBOUND) { dir = NAT_OUTBOUND; fi.fin_out = 1; } else { dir = NAT_INBOUND; fi.fin_out = 0; } nflags |= NAT_NOTRULEPORT; MUTEX_ENTER(&softn->ipf_nat_new); #ifdef USE_INET6 if (nat->nat_v[0] == 6) nat2 = ipf_nat6_add(&fi, ti->ti_rule, NULL, nflags, dir); else #endif nat2 = ipf_nat_add(&fi, ti->ti_rule, NULL, nflags, dir); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, IPN_UDP); ipf_nat_update(&fi, nat2); fi.fin_ifp = NULL; if (ti->ti_rule->in_redir == NAT_MAP) { fi.fin_src6 = nat->nat_ndst6; fi.fin_dst6 = nat->nat_nsrc6; if (nat->nat_v[0] == 4) { ip->ip_src = nat->nat_ndstip; ip->ip_dst = nat->nat_nsrcip; } else { #ifdef USE_INET6 ip6->ip6_src = nat->nat_ndst6.in6; ip6->ip6_dst = nat->nat_nsrc6.in6; #endif } } else { fi.fin_src6 = nat->nat_odst6; fi.fin_dst6 = nat->nat_osrc6; if (fin->fin_v == 4) { ip->ip_src = nat->nat_odstip; ip->ip_dst = nat->nat_osrcip; } else { #ifdef USE_INET6 ip6->ip6_src = nat->nat_odst6.in6; ip6->ip6_dst = nat->nat_osrc6.in6; #endif } } if (ipf_state_add(softc, &fi, NULL, SI_W_SPORT) != 0) { ipf_nat_setpending(softc, nat2); } } if (nat->nat_v[0] == 4) { ip->ip_len = slen; ip->ip_src = swip; ip->ip_dst = sw2ip; } else { #ifdef USE_INET6 ip6->ip6_plen = slen; ip6->ip6_src = swip6.in6; ip6->ip6_dst = sw2ip6.in6; #endif } return 0; } int ipf_p_tftp_client(softt, fin, aps, nat) ipf_tftp_softc_t *softt; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { u_char *msg, *s, *t; tftpinfo_t *ti; u_short opcode; udphdr_t *udp; int len; if (fin->fin_dlen < 4) return 0; ti = aps->aps_data; msg = fin->fin_dp; msg += sizeof(udphdr_t); opcode = (msg[0] << 8) | msg[1]; DT3(tftp_cmd, fr_info_t *, fin, int, opcode, nat_t *, nat); switch (opcode) { case TFTP_CMD_WRITE : if (softt->ipf_p_tftp_readonly != 0) break; /* FALLTHROUGH */ case TFTP_CMD_READ : len = fin->fin_dlen - sizeof(*udp) - 2; if (len > sizeof(ti->ti_filename) - 1) len = sizeof(ti->ti_filename) - 1; s = msg + 2; for (t = (u_char *)ti->ti_filename; (len > 0); len--, s++) { *t++ = *s; if (*s == '\0') break; } ipf_p_tftp_backchannel(fin, aps, nat); break; default : return -1; } ti = aps->aps_data; ti->ti_lastcmd = opcode; return 0; } int ipf_p_tftp_server(softt, fin, aps, nat) ipf_tftp_softc_t *softt; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { tftpinfo_t *ti; u_short opcode; u_short arg; u_char *msg; if (fin->fin_dlen < 4) return 0; ti = aps->aps_data; msg = fin->fin_dp; msg += sizeof(udphdr_t); arg = (msg[2] << 8) | msg[3]; opcode = (msg[0] << 8) | msg[1]; switch (opcode) { case TFTP_CMD_ACK : ti->ti_lastblk = arg; break; case TFTP_CMD_ERROR : ti->ti_lasterror = arg; break; default : return -1; } ti->ti_lastcmd = opcode; return 0; } diff --git a/sys/contrib/ipfilter/netinet/mlfk_ipl.c b/sys/contrib/ipfilter/netinet/mlfk_ipl.c index aba5b55316ca..aa75fa3e686c 100644 --- a/sys/contrib/ipfilter/netinet/mlfk_ipl.c +++ b/sys/contrib/ipfilter/netinet/mlfk_ipl.c @@ -1,673 +1,673 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * $FreeBSD$ * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #include #include #include #ifdef __FreeBSD_version # include # include # ifdef _KERNEL # include # else # define CURVNET_SET(arg) # define CURVNET_RESTORE() # define VNET_DEFINE(_t, _v) _t _v # define VNET_DECLARE(_t, _v) extern _t _v # define VNET(arg) arg # endif #endif #include #include #include #include "netinet/ipl.h" #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_state.h" #include "netinet/ip_nat.h" #include "netinet/ip_auth.h" #include "netinet/ip_frag.h" #include "netinet/ip_sync.h" VNET_DECLARE(ipf_main_softc_t, ipfmain); #define V_ipfmain VNET(ipfmain) #ifdef __FreeBSD_version static struct cdev *ipf_devs[IPL_LOGSIZE]; #else static dev_t ipf_devs[IPL_LOGSIZE]; #endif static int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ); static int sysctl_ipf_int_nat ( SYSCTL_HANDLER_ARGS ); static int sysctl_ipf_int_state ( SYSCTL_HANDLER_ARGS ); static int sysctl_ipf_int_auth ( SYSCTL_HANDLER_ARGS ); static int sysctl_ipf_int_frag ( SYSCTL_HANDLER_ARGS ); static int ipf_modload(void); static int ipf_modunload(void); static int ipf_fbsd_sysctl_create(void); static int ipf_fbsd_sysctl_destroy(void); #ifdef __FreeBSD_version -static int ipfopen __P((struct cdev*, int, int, struct thread *)); -static int ipfclose __P((struct cdev*, int, int, struct thread *)); -static int ipfread __P((struct cdev*, struct uio *, int)); -static int ipfwrite __P((struct cdev*, struct uio *, int)); +static int ipfopen(struct cdev*, int, int, struct thread *); +static int ipfclose(struct cdev*, int, int, struct thread *); +static int ipfread(struct cdev*, struct uio *, int); +static int ipfwrite(struct cdev*, struct uio *, int); #else -static int ipfopen __P((dev_t, int, int, struct proc *)); -static int ipfclose __P((dev_t, int, int, struct proc *)); -static int ipfread __P((dev_t, struct uio *, int)); -static int ipfwrite __P((dev_t, struct uio *, int)); +static int ipfopen(dev_t, int, int, struct proc *); +static int ipfclose(dev_t, int, int, struct proc *); +static int ipfread(dev_t, struct uio *, int); +static int ipfwrite(dev_t, struct uio *, int); #endif SYSCTL_DECL(_net_inet); #define SYSCTL_IPF(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_INT|CTLFLAG_VNET|access, \ ptr, val, sysctl_ipf_int, "I", descr) #define SYSCTL_DYN_IPF_NAT(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_nat, "I", descr) #define SYSCTL_DYN_IPF_STATE(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_state, "I", descr) #define SYSCTL_DYN_IPF_FRAG(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_frag, "I", descr) #define SYSCTL_DYN_IPF_AUTH(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_auth, "I", descr) static struct sysctl_ctx_list ipf_clist; #define CTLFLAG_OFF 0x00800000 /* IPFilter must be disabled */ #define CTLFLAG_RWO (CTLFLAG_RW|CTLFLAG_OFF) SYSCTL_NODE(_net_inet, OID_AUTO, ipf, CTLFLAG_RW, 0, "IPF"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_flags, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_flags), 0, "IPF flags"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_pass, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_pass), 0, "default pass/block"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &VNET_NAME(ipfmain.ipf_active), 0, "IPF is active"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpidletimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpidletimeout), 0, "TCP idle timeout in seconds"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcphalfclosed, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcphalfclosed), 0, "timeout for half closed TCP sessions"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosewait, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpclosewait), 0, "timeout for TCP sessions in closewait status"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcplastack, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcplastack), 0, "timeout for TCP sessions in last ack status"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcptimeout), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosed, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpclosed), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_udptimeout), 0, "UDP timeout"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udpacktimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_udpacktimeout), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_icmptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_icmptimeout), 0, "ICMP timeout"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_running, CTLFLAG_RD, &VNET_NAME(ipfmain.ipf_running), 0, "IPF is running"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_chksrc), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_minttl), 0, ""); #define CDEV_MAJOR 79 #include #ifdef __FreeBSD_version # include static int ipfpoll(struct cdev *dev, int events, struct thread *td); static struct cdevsw ipf_cdevsw = { .d_version = D_VERSION, .d_flags = 0, /* D_NEEDGIANT - Should be SMP safe */ .d_open = ipfopen, .d_close = ipfclose, .d_read = ipfread, .d_write = ipfwrite, .d_ioctl = ipfioctl, .d_poll = ipfpoll, .d_name = "ipf", }; #else static int ipfpoll(dev_t dev, int events, struct proc *td); static struct cdevsw ipf_cdevsw = { /* open */ ipfopen, /* close */ ipfclose, /* read */ ipfread, /* write */ ipfwrite, /* ioctl */ ipfioctl, /* poll */ ipfpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ipf", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, }; #endif static char *ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME, IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME, IPLOOKUP_NAME, NULL }; static int ipfilter_modevent(module_t mod, int type, void *unused) { int error = 0; switch (type) { case MOD_LOAD : error = ipf_modload(); break; case MOD_UNLOAD : error = ipf_modunload(); break; default: error = EINVAL; break; } return error; } static void vnet_ipf_init(void) { char *defpass; int error; if (ipf_create_all(&V_ipfmain) == NULL) return; error = ipfattach(&V_ipfmain); if (error) { ipf_destroy_all(&V_ipfmain); return; } if (FR_ISPASS(V_ipfmain.ipf_pass)) defpass = "pass"; else if (FR_ISBLOCK(V_ipfmain.ipf_pass)) defpass = "block"; else defpass = "no-match -> block"; if (IS_DEFAULT_VNET(curvnet)) { printf("%s initialized. Default = %s all, Logging = %s%s\n", ipfilter_version, defpass, #ifdef IPFILTER_LOG "enabled", #else "disabled", #endif #ifdef IPFILTER_COMPILED " (COMPILED)" #else "" #endif ); } else { (void)ipf_pfil_hook(); ipf_event_reg(); } } VNET_SYSINIT(vnet_ipf_init, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD, vnet_ipf_init, NULL); static int ipf_modload() { char *c, *str; int i, j, error; if (ipf_load_all() != 0) return EIO; if (ipf_fbsd_sysctl_create() != 0) { return EIO; } for (i = 0; i < IPL_LOGSIZE; i++) ipf_devs[i] = NULL; for (i = 0; (str = ipf_devfiles[i]); i++) { c = NULL; for(j = strlen(str); j > 0; j--) if (str[j] == '/') { c = str + j + 1; break; } if (!c) c = str; ipf_devs[i] = make_dev(&ipf_cdevsw, i, 0, 0, 0600, "%s", c); } error = ipf_pfil_hook(); if (error != 0) return error; ipf_event_reg(); return 0; } static void vnet_ipf_uninit(void) { if (V_ipfmain.ipf_refcnt) return; if (V_ipfmain.ipf_running >= 0) { if (ipfdetach(&V_ipfmain) != 0) return; V_ipfmain.ipf_running = -2; ipf_destroy_all(&V_ipfmain); if (!IS_DEFAULT_VNET(curvnet)) { ipf_event_dereg(); (void)ipf_pfil_unhook(); } } } VNET_SYSUNINIT(vnet_ipf_uninit, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD, vnet_ipf_uninit, NULL); static int ipf_modunload() { int error, i; ipf_event_dereg(); ipf_fbsd_sysctl_destroy(); error = ipf_pfil_unhook(); if (error != 0) return error; for (i = 0; ipf_devfiles[i]; i++) { if (ipf_devs[i] != NULL) destroy_dev(ipf_devs[i]); } ipf_unload_all(); printf("%s unloaded\n", ipfilter_version); return (0); } static moduledata_t ipfiltermod = { "ipfilter", ipfilter_modevent, 0 }; DECLARE_MODULE(ipfilter, ipfiltermod, SI_SUB_PROTO_FIREWALL, SI_ORDER_SECOND); #ifdef MODULE_VERSION MODULE_VERSION(ipfilter, 1); #endif #ifdef SYSCTL_IPF int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) { int error = 0; if (arg1) error = SYSCTL_OUT(req, arg1, sizeof(int)); else error = SYSCTL_OUT(req, &arg2, sizeof(int)); if (error || !req->newptr) return (error); if (!arg1) error = EPERM; else { if ((oidp->oid_kind & CTLFLAG_OFF) && (V_ipfmain.ipf_running > 0)) error = EBUSY; else error = SYSCTL_IN(req, arg1, sizeof(int)); } return (error); } /* * In the VIMAGE case kern_sysctl.c already adds the vnet base address given * we set CTLFLAG_VNET to get proper access checks. Have to undo this. * Then we add the given offset to the specific malloced struct hanging off * virtualized ipmain struct. */ static int sysctl_ipf_int_nat ( SYSCTL_HANDLER_ARGS ) { if (arg1) { ipf_nat_softc_t *nat_softc; nat_softc = V_ipfmain.ipf_nat_soft; #ifdef VIMAGE arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif arg1 = (void *)((uintptr_t)nat_softc + (uintptr_t)arg1); } return (sysctl_ipf_int(oidp, arg1, arg2, req)); } static int sysctl_ipf_int_state ( SYSCTL_HANDLER_ARGS ) { if (arg1) { ipf_state_softc_t *state_softc; state_softc = V_ipfmain.ipf_state_soft; #ifdef VIMAGE arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif arg1 = (void *)((uintptr_t)state_softc + (uintptr_t)arg1); } return (sysctl_ipf_int(oidp, arg1, arg2, req)); } static int sysctl_ipf_int_auth ( SYSCTL_HANDLER_ARGS ) { if (arg1) { ipf_auth_softc_t *auth_softc; auth_softc = V_ipfmain.ipf_auth_soft; #ifdef VIMAGE arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif arg1 = (void *)((uintptr_t)auth_softc + (uintptr_t)arg1); } return (sysctl_ipf_int(oidp, arg1, arg2, req)); } static int sysctl_ipf_int_frag ( SYSCTL_HANDLER_ARGS ) { if (arg1) { ipf_frag_softc_t *frag_softc; frag_softc = V_ipfmain.ipf_frag_soft; #ifdef VIMAGE arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif arg1 = (void *)((uintptr_t)frag_softc + (uintptr_t)arg1); } return (sysctl_ipf_int(oidp, arg1, arg2, req)); } #endif static int #ifdef __FreeBSD_version ipfpoll(struct cdev *dev, int events, struct thread *td) #else ipfpoll(dev_t dev, int events, struct proc *td) #endif { int unit = GET_MINOR(dev); int revents; if (unit < 0 || unit > IPL_LOGMAX) return 0; revents = 0; CURVNET_SET(TD_TO_VNET(td)); switch (unit) { case IPL_LOGIPF : case IPL_LOGNAT : case IPL_LOGSTATE : #ifdef IPFILTER_LOG if ((events & (POLLIN | POLLRDNORM)) && ipf_log_canread(&V_ipfmain, unit)) revents |= events & (POLLIN | POLLRDNORM); #endif break; case IPL_LOGAUTH : if ((events & (POLLIN | POLLRDNORM)) && ipf_auth_waiting(&V_ipfmain)) revents |= events & (POLLIN | POLLRDNORM); break; case IPL_LOGSYNC : if ((events & (POLLIN | POLLRDNORM)) && ipf_sync_canread(&V_ipfmain)) revents |= events & (POLLIN | POLLRDNORM); if ((events & (POLLOUT | POLLWRNORM)) && ipf_sync_canwrite(&V_ipfmain)) revents |= events & (POLLOUT | POLLWRNORM); break; case IPL_LOGSCAN : case IPL_LOGLOOKUP : default : break; } if ((revents == 0) && ((events & (POLLIN|POLLRDNORM)) != 0)) selrecord(td, &V_ipfmain.ipf_selwait[unit]); CURVNET_RESTORE(); return revents; } /* * routines below for saving IP headers to buffer */ static int ipfopen(dev, flags #ifdef __FreeBSD_version , devtype, p) int devtype; struct thread *p; struct cdev *dev; #else ) dev_t dev; #endif int flags; { int unit = GET_MINOR(dev); int error; if (IPL_LOGMAX < unit) error = ENXIO; else { switch (unit) { case IPL_LOGIPF : case IPL_LOGNAT : case IPL_LOGSTATE : case IPL_LOGAUTH : case IPL_LOGLOOKUP : case IPL_LOGSYNC : #ifdef IPFILTER_SCAN case IPL_LOGSCAN : #endif error = 0; break; default : error = ENXIO; break; } } return error; } static int ipfclose(dev, flags #ifdef __FreeBSD_version , devtype, p) int devtype; struct thread *p; struct cdev *dev; #else ) dev_t dev; #endif int flags; { int unit = GET_MINOR(dev); if (IPL_LOGMAX < unit) unit = ENXIO; else unit = 0; return unit; } /* * ipfread/ipflog * both of these must operate with at least splnet() lest they be * called during packet processing and cause an inconsistancy to appear in * the filter lists. */ #if (BSD >= 199306) static int ipfread(dev, uio, ioflag) int ioflag; #else static int ipfread(dev, uio) #endif #ifdef __FreeBSD_version struct cdev *dev; #else dev_t dev; #endif struct uio *uio; { int error; int unit = GET_MINOR(dev); if (unit < 0) return ENXIO; CURVNET_SET(TD_TO_VNET(curthread)); if (V_ipfmain.ipf_running < 1) { CURVNET_RESTORE(); return EIO; } if (unit == IPL_LOGSYNC) { error = ipf_sync_read(&V_ipfmain, uio); CURVNET_RESTORE(); return error; } #ifdef IPFILTER_LOG error = ipf_log_read(&V_ipfmain, unit, uio); #else error = ENXIO; #endif CURVNET_RESTORE(); return error; } /* * ipfwrite * both of these must operate with at least splnet() lest they be * called during packet processing and cause an inconsistancy to appear in * the filter lists. */ #if (BSD >= 199306) static int ipfwrite(dev, uio, ioflag) int ioflag; #else static int ipfwrite(dev, uio) #endif #ifdef __FreeBSD_version struct cdev *dev; #else dev_t dev; #endif struct uio *uio; { int error; CURVNET_SET(TD_TO_VNET(curthread)); if (V_ipfmain.ipf_running < 1) { CURVNET_RESTORE(); return EIO; } if (GET_MINOR(dev) == IPL_LOGSYNC) { error = ipf_sync_write(&V_ipfmain, uio); CURVNET_RESTORE(); return error; } return ENXIO; } static int ipf_fbsd_sysctl_create(void) { sysctl_ctx_init(&ipf_clist); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "fr_defnatage", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_defage), 0, ""); SYSCTL_DYN_IPF_STATE(_net_inet_ipf, OID_AUTO, "fr_statesize", CTLFLAG_RWO, (void *)offsetof(ipf_state_softc_t, ipf_state_size), 0, ""); SYSCTL_DYN_IPF_STATE(_net_inet_ipf, OID_AUTO, "fr_statemax", CTLFLAG_RWO, (void *)offsetof(ipf_state_softc_t, ipf_state_max), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_nattable_max", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_nattable_sz", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_natrules_sz", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_rdrrules_sz", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_hostmap_sz", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 0, ""); SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_authsize", CTLFLAG_RWO, (void *)offsetof(ipf_auth_softc_t, ipf_auth_size), 0, ""); SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_authused", CTLFLAG_RD, (void *)offsetof(ipf_auth_softc_t, ipf_auth_used), 0, ""); SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_defaultauthage", CTLFLAG_RW, (void *)offsetof(ipf_auth_softc_t, ipf_auth_defaultage), 0, ""); SYSCTL_DYN_IPF_FRAG(_net_inet_ipf, OID_AUTO, "fr_ipfrttl", CTLFLAG_RW, (void *)offsetof(ipf_frag_softc_t, ipfr_ttl), 0, ""); return 0; } static int ipf_fbsd_sysctl_destroy(void) { if (sysctl_ctx_free(&ipf_clist)) { printf("sysctl_ctx_free failed"); return(ENOTEMPTY); } return 0; } diff --git a/sys/contrib/ipfilter/netinet/radix_ipf.c b/sys/contrib/ipfilter/netinet/radix_ipf.c index ac7c88ce2847..df0932de4181 100644 --- a/sys/contrib/ipfilter/netinet/radix_ipf.c +++ b/sys/contrib/ipfilter/netinet/radix_ipf.c @@ -1,1532 +1,1532 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include #include #include #include #include #include #ifdef _KERNEL #include #else # include # include # include # include #endif /* !_KERNEL */ #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #ifdef RDX_DEBUG # include # include # include #endif #include "netinet/radix_ipf.h" #define ADF_OFF offsetof(addrfamily_t, adf_addr) #define ADF_OFF_BITS (ADF_OFF << 3) -static ipf_rdx_node_t *ipf_rx_insert __P((ipf_rdx_head_t *, - ipf_rdx_node_t nodes[2], int *)); -static void ipf_rx_attach_mask __P((ipf_rdx_node_t *, ipf_rdx_mask_t *)); -static int count_mask_bits __P((addrfamily_t *, u_32_t **)); -static void buildnodes __P((addrfamily_t *, addrfamily_t *, - ipf_rdx_node_t n[2])); -static ipf_rdx_node_t *ipf_rx_find_addr __P((ipf_rdx_node_t *, u_32_t *)); -static ipf_rdx_node_t *ipf_rx_lookup __P((ipf_rdx_head_t *, addrfamily_t *, - addrfamily_t *)); -static ipf_rdx_node_t *ipf_rx_match __P((ipf_rdx_head_t *, addrfamily_t *)); +static ipf_rdx_node_t *ipf_rx_insert(ipf_rdx_head_t *, + ipf_rdx_node_t nodes[2], int *); +static void ipf_rx_attach_mask(ipf_rdx_node_t *, ipf_rdx_mask_t *); +static int count_mask_bits(addrfamily_t *, u_32_t **); +static void buildnodes(addrfamily_t *, addrfamily_t *, + ipf_rdx_node_t n[2]); +static ipf_rdx_node_t *ipf_rx_find_addr(ipf_rdx_node_t *, u_32_t *); +static ipf_rdx_node_t *ipf_rx_lookup(ipf_rdx_head_t *, addrfamily_t *, + addrfamily_t *); +static ipf_rdx_node_t *ipf_rx_match(ipf_rdx_head_t *, addrfamily_t *); /* * Foreword. * --------- * The code in this file has been written to target using the addrfamily_t * data structure to house the address information and no other. Thus there * are certain aspects of thise code (such as offsets to the address itself) * that are hard coded here whilst they might be more variable elsewhere. * Similarly, this code enforces no maximum key length as that's implied by * all keys needing to be stored in addrfamily_t. */ /* ------------------------------------------------------------------------ */ /* Function: count_mask_bits */ /* Returns: number of consecutive bits starting at "mask". */ /* */ /* Count the number of bits set in the address section of addrfamily_t and */ /* return both that number and a pointer to the last word with a bit set if */ /* lastp is not NULL. The bit count is performed using network byte order */ /* as the guide for which bit is the most significant bit. */ /* ------------------------------------------------------------------------ */ static int count_mask_bits(mask, lastp) addrfamily_t *mask; u_32_t **lastp; { u_32_t *mp = (u_32_t *)&mask->adf_addr; u_32_t m; int count = 0; int mlen; mlen = mask->adf_len - offsetof(addrfamily_t, adf_addr); for (; mlen > 0; mlen -= 4, mp++) { if ((m = ntohl(*mp)) == 0) break; if (lastp != NULL) *lastp = mp; for (; m & 0x80000000; m <<= 1) count++; } return count; } /* ------------------------------------------------------------------------ */ /* Function: buildnodes */ /* Returns: Nil */ /* Parameters: addr(I) - network address for this radix node */ /* mask(I) - netmask associated with the above address */ /* nodes(O) - pair of ipf_rdx_node_t's to initialise with data */ /* associated with addr and mask. */ /* */ /* Initialise the fields in a pair of radix tree nodes according to the */ /* data supplied in the paramters "addr" and "mask". It is expected that */ /* "mask" will contain a consecutive string of bits set. Masks with gaps in */ /* the middle are not handled by this implementation. */ /* ------------------------------------------------------------------------ */ static void buildnodes(addr, mask, nodes) addrfamily_t *addr, *mask; ipf_rdx_node_t nodes[2]; { u_32_t maskbits; u_32_t lastmask; u_32_t *last; int masklen; last = NULL; maskbits = count_mask_bits(mask, &last); if (last == NULL) { masklen = 0; lastmask = 0; } else { masklen = last - (u_32_t *)mask; lastmask = *last; } bzero(&nodes[0], sizeof(ipf_rdx_node_t) * 2); nodes[0].maskbitcount = maskbits; nodes[0].index = -1 - (ADF_OFF_BITS + maskbits); nodes[0].addrkey = (u_32_t *)addr; nodes[0].maskkey = (u_32_t *)mask; nodes[0].addroff = nodes[0].addrkey + masklen; nodes[0].maskoff = nodes[0].maskkey + masklen; nodes[0].parent = &nodes[1]; nodes[0].offset = masklen; nodes[0].lastmask = lastmask; nodes[1].offset = masklen; nodes[1].left = &nodes[0]; nodes[1].maskbitcount = maskbits; #ifdef RDX_DEBUG (void) strcpy(nodes[0].name, "_BUILD.0"); (void) strcpy(nodes[1].name, "_BUILD.1"); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_find_addr */ /* Returns: ipf_rdx_node_t * - pointer to a node in the radix tree. */ /* Parameters: tree(I) - pointer to first right node in tree to search */ /* addr(I) - pointer to address to match */ /* */ /* Walk the radix tree given by "tree", looking for a leaf node that is a */ /* match for the address given by "addr". */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * ipf_rx_find_addr(tree, addr) ipf_rdx_node_t *tree; u_32_t *addr; { ipf_rdx_node_t *cur; for (cur = tree; cur->index >= 0;) { if (cur->bitmask & addr[cur->offset]) { cur = cur->right; } else { cur = cur->left; } } return (cur); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_match */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - pointer to address to find */ /* */ /* Search the radix tree for the best match to the address pointed to by */ /* "addr" and return a pointer to that node. This search will not match the */ /* address information stored in either of the root leaves as neither of */ /* them are considered to be part of the tree of data being stored. */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * ipf_rx_match(head, addr) ipf_rdx_head_t *head; addrfamily_t *addr; { ipf_rdx_mask_t *masknode; ipf_rdx_node_t *prev; ipf_rdx_node_t *node; ipf_rdx_node_t *cur; u_32_t *data; u_32_t *mask; u_32_t *key; u_32_t *end; int len; int i; len = addr->adf_len; end = (u_32_t *)((u_char *)addr + len); node = ipf_rx_find_addr(head->root, (u_32_t *)addr); /* * Search the dupkey list for a potential match. */ for (cur = node; (cur != NULL) && (cur->root == 0); cur = cur->dupkey) { i = cur[0].addroff - cur[0].addrkey; data = cur[0].addrkey + i; mask = cur[0].maskkey + i; key = (u_32_t *)addr + i; for (; key < end; data++, key++, mask++) if ((*key & *mask) != *data) break; if ((end == key) && (cur->root == 0)) return (cur); /* Equal keys */ } prev = node->parent; key = (u_32_t *)addr; for (node = prev; node->root == 0; node = node->parent) { /* * We know that the node hasn't matched so therefore only * the entries in the mask list are searched, not the top * node nor the dupkey list. */ masknode = node->masks; for (; masknode != NULL; masknode = masknode->next) { if (masknode->maskbitcount > node->maskbitcount) continue; cur = masknode->node; for (i = ADF_OFF >> 2; i <= node->offset; i++) { if ((key[i] & masknode->mask[i]) == cur->addrkey[i]) return (cur); } } } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_lookup */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - address part of the key to match */ /* mask(I) - netmask part of the key to match */ /* */ /* ipf_rx_lookup searches for an exact match on (addr,mask). The intention */ /* is to see if a given key is in the tree, not to see if a route exists. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * ipf_rx_lookup(head, addr, mask) ipf_rdx_head_t *head; addrfamily_t *addr, *mask; { ipf_rdx_node_t *found; ipf_rdx_node_t *node; u_32_t *akey; int count; found = ipf_rx_find_addr(head->root, (u_32_t *)addr); if (found->root == 1) return NULL; /* * It is possible to find a matching address in the tree but for the * netmask to not match. If the netmask does not match and there is * no list of alternatives present at dupkey, return a failure. */ count = count_mask_bits(mask, NULL); if (count != found->maskbitcount && found->dupkey == NULL) return (NULL); akey = (u_32_t *)addr; if ((found->addrkey[found->offset] & found->maskkey[found->offset]) != akey[found->offset]) return NULL; if (found->dupkey != NULL) { node = found; while (node != NULL && node->maskbitcount != count) node = node->dupkey; if (node == NULL) return (NULL); found = node; } return found; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_attach_mask */ /* Returns: Nil */ /* Parameters: node(I) - pointer to a radix tree node */ /* mask(I) - pointer to mask structure to add */ /* */ /* Add the netmask to the given node in an ordering where the most specific */ /* netmask is at the top of the list. */ /* ------------------------------------------------------------------------ */ static void ipf_rx_attach_mask(node, mask) ipf_rdx_node_t *node; ipf_rdx_mask_t *mask; { ipf_rdx_mask_t **pm; ipf_rdx_mask_t *m; for (pm = &node->masks; (m = *pm) != NULL; pm = &m->next) if (m->maskbitcount < mask->maskbitcount) break; mask->next = *pm; *pm = mask; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_insert */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to add nodes to */ /* nodes(I) - pointer to radix nodes to be added */ /* dup(O) - set to 1 if node is a duplicate, else 0. */ /* */ /* Add the new radix tree entry that owns nodes[] to the tree given by head.*/ /* If there is already a matching key in the table, "dup" will be set to 1 */ /* and the existing node pointer returned if there is a complete key match. */ /* A complete key match is a matching of all key data that is presented by */ /* by the netmask. */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * ipf_rx_insert(head, nodes, dup) ipf_rdx_head_t *head; ipf_rdx_node_t nodes[2]; int *dup; { ipf_rdx_mask_t **pmask; ipf_rdx_node_t *node; ipf_rdx_node_t *prev; ipf_rdx_mask_t *mask; ipf_rdx_node_t *cur; u_32_t nodemask; u_32_t *addr; u_32_t *data; int nodebits; u_32_t *key; u_32_t *end; u_32_t bits; int nodekey; int nodeoff; int nlen; int len; addr = nodes[0].addrkey; node = ipf_rx_find_addr(head->root, addr); len = ((addrfamily_t *)addr)->adf_len; key = (u_32_t *)&((addrfamily_t *)addr)->adf_addr; data= (u_32_t *)&((addrfamily_t *)node->addrkey)->adf_addr; end = (u_32_t *)((u_char *)addr + len); for (nlen = 0; key < end; data++, key++, nlen += 32) if (*key != *data) break; if (end == data) { *dup = 1; return (node); /* Equal keys */ } *dup = 0; bits = (ntohl(*data) ^ ntohl(*key)); for (; bits != 0; nlen++) { if ((bits & 0x80000000) != 0) break; bits <<= 1; } nlen += ADF_OFF_BITS; nodes[1].index = nlen; nodes[1].bitmask = htonl(0x80000000 >> (nlen & 0x1f)); nodes[0].offset = nlen / 32; nodes[1].offset = nlen / 32; /* * Walk through the tree and look for the correct place to attach * this node. ipf_rx_fin_addr is not used here because the place * to attach this node may be an internal node (same key, different * netmask.) Additionally, the depth of the search is forcibly limited * here to not exceed the netmask, so that a short netmask will be * added higher up the tree even if there are lower branches. */ cur = head->root; key = nodes[0].addrkey; do { prev = cur; if (key[cur->offset] & cur->bitmask) { cur = cur->right; } else { cur = cur->left; } } while (nlen > (unsigned)cur->index); if ((key[prev->offset] & prev->bitmask) == 0) { prev->left = &nodes[1]; } else { prev->right = &nodes[1]; } cur->parent = &nodes[1]; nodes[1].parent = prev; if ((key[nodes[1].offset] & nodes[1].bitmask) == 0) { nodes[1].right = cur; } else { nodes[1].right = &nodes[0]; nodes[1].left = cur; } nodeoff = nodes[0].offset; nodekey = nodes[0].addrkey[nodeoff]; nodemask = nodes[0].lastmask; nodebits = nodes[0].maskbitcount; prev = NULL; /* * Find the node up the tree with the largest pattern that still * matches the node being inserted to see if this mask can be * moved there. */ for (cur = nodes[1].parent; cur->root == 0; cur = cur->parent) { if (cur->maskbitcount <= nodebits) break; if (((cur - 1)->addrkey[nodeoff] & nodemask) != nodekey) break; prev = cur; } KMALLOC(mask, ipf_rdx_mask_t *); if (mask == NULL) return NULL; bzero(mask, sizeof(*mask)); mask->next = NULL; mask->node = &nodes[0]; mask->maskbitcount = nodebits; mask->mask = nodes[0].maskkey; nodes[0].mymask = mask; if (prev != NULL) { ipf_rdx_mask_t *m; for (pmask = &prev->masks; (m = *pmask) != NULL; pmask = &m->next) { if (m->maskbitcount < nodebits) break; } } else { /* * No higher up nodes qualify, so attach mask locally. */ pmask = &nodes[0].masks; } mask->next = *pmask; *pmask = mask; /* * Search the mask list on each child to see if there are any masks * there that can be moved up to this newly inserted node. */ cur = nodes[1].right; if (cur->root == 0) { for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { if (mask->maskbitcount < nodebits) { *pmask = mask->next; ipf_rx_attach_mask(&nodes[0], mask); } else { pmask = &mask->next; } } } cur = nodes[1].left; if (cur->root == 0 && cur != &nodes[0]) { for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { if (mask->maskbitcount < nodebits) { *pmask = mask->next; ipf_rx_attach_mask(&nodes[0], mask); } else { pmask = &mask->next; } } } return (&nodes[0]); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_addroute */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - address portion of "route" to add */ /* mask(I) - netmask portion of "route" to add */ /* nodes(I) - radix tree data nodes inside allocate structure */ /* */ /* Attempt to add a node to the radix tree. The key for the node is the */ /* (addr,mask). No memory allocation for the radix nodes themselves is */ /* performed here, the data structure that this radix node is being used to */ /* find is expected to house the node data itself however the call to */ /* ipf_rx_insert() will attempt to allocate memory in order for netmask to */ /* be promoted further up the tree. */ /* In this case, the ip_pool_node_t structure from ip_pool.h contains both */ /* the key material (addr,mask) and the radix tree nodes[]. */ /* */ /* The mechanics of inserting the node into the tree is handled by the */ /* function ipf_rx_insert() above. Here, the code deals with the case */ /* where the data to be inserted is a duplicate. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * ipf_rx_addroute(head, addr, mask, nodes) ipf_rdx_head_t *head; addrfamily_t *addr, *mask; ipf_rdx_node_t *nodes; { ipf_rdx_node_t *node; ipf_rdx_node_t *prev; ipf_rdx_node_t *x; int dup; buildnodes(addr, mask, nodes); x = ipf_rx_insert(head, nodes, &dup); if (x == NULL) return NULL; if (dup == 1) { node = &nodes[0]; prev = NULL; /* * The duplicate list is kept sorted with the longest * mask at the top, meaning that the most specific entry * in the listis found first. This list thus allows for * duplicates such as 128.128.0.0/32 and 128.128.0.0/16. */ while ((x != NULL) && (x->maskbitcount > node->maskbitcount)) { prev = x; x = x->dupkey; } /* * Is it a complete duplicate? If so, return NULL and * fail the insert. Otherwise, insert it into the list * of netmasks active for this key. */ if ((x != NULL) && (x->maskbitcount == node->maskbitcount)) return (NULL); if (prev != NULL) { nodes[0].dupkey = x; prev->dupkey = &nodes[0]; nodes[0].parent = prev; if (x != NULL) x->parent = &nodes[0]; } else { nodes[0].dupkey = x->dupkey; prev = x->parent; nodes[0].parent = prev; x->parent = &nodes[0]; if (prev->left == x) prev->left = &nodes[0]; else prev->right = &nodes[0]; } } return &nodes[0]; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_delete */ /* Returns: ipf_rdx_node_t * - NULL on error, else node removed from */ /* the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - pointer to the address part of the key */ /* mask(I) - pointer to the netmask part of the key */ /* */ /* Search for an entry in the radix tree that is an exact match for (addr, */ /* mask) and remove it if it exists. In the case where (addr,mask) is a not */ /* a unique key, the tree structure itself is not changed - only the list */ /* of duplicate keys. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * ipf_rx_delete(head, addr, mask) ipf_rdx_head_t *head; addrfamily_t *addr, *mask; { ipf_rdx_mask_t **pmask; ipf_rdx_node_t *parent; ipf_rdx_node_t *found; ipf_rdx_node_t *prev; ipf_rdx_node_t *node; ipf_rdx_node_t *cur; ipf_rdx_mask_t *m; int count; found = ipf_rx_find_addr(head->root, (u_32_t *)addr); if (found == NULL) return NULL; if (found->root == 1) return NULL; count = count_mask_bits(mask, NULL); parent = found->parent; if (found->dupkey != NULL) { node = found; while (node != NULL && node->maskbitcount != count) node = node->dupkey; if (node == NULL) return (NULL); if (node != found) { /* * Remove from the dupkey list. Here, "parent" is * the previous node on the list (rather than tree) * and "dupkey" is the next node on the list. */ parent = node->parent; parent->dupkey = node->dupkey; node->dupkey->parent = parent; } else { /* * * When removing the top node of the dupkey list, * the pointers at the top of the list that point * to other tree nodes need to be preserved and * any children must have their parent updated. */ node = node->dupkey; node->parent = found->parent; node->right = found->right; node->left = found->left; found->right->parent = node; found->left->parent = node; if (parent->left == found) parent->left = node; else parent->right= node; } } else { if (count != found->maskbitcount) return (NULL); /* * Remove the node from the tree and reconnect the subtree * below. */ /* * If there is a tree to the left, look for something to * attach in place of "found". */ prev = found + 1; cur = parent->parent; if (parent != found + 1) { if ((found + 1)->parent->right == found + 1) (found + 1)->parent->right = parent; else (found + 1)->parent->left = parent; if (cur->right == parent) { if (parent->left == found) { cur->right = parent->right; } else if (parent->left != parent - 1) { cur->right = parent->left; } else { cur->right = parent - 1; } cur->right->parent = cur; } else { if (parent->right == found) { cur->left = parent->left; } else if (parent->right != parent - 1) { cur->left = parent->right; } else { cur->left = parent - 1; } cur->left->parent = cur; } parent->left = (found + 1)->left; if ((found + 1)->right != parent) parent->right = (found + 1)->right; parent->left->parent = parent; parent->right->parent = parent; parent->parent = (found + 1)->parent; parent->bitmask = prev->bitmask; parent->offset = prev->offset; parent->index = prev->index; } else { /* * We found an edge node. */ cur = parent->parent; if (cur->left == parent) { if (parent->left == found) { cur->left = parent->right; parent->right->parent = cur; } else { cur->left = parent->left; parent->left->parent = cur; } } else { if (parent->right != found) { cur->right = parent->right; parent->right->parent = cur; } else { cur->right = parent->left; prev->left->parent = cur; } } } } /* * Remove mask associated with this node. */ for (cur = parent; cur->root == 0; cur = cur->parent) { ipf_rdx_mask_t **pm; if (cur->maskbitcount <= found->maskbitcount) break; if (((cur - 1)->addrkey[found->offset] & found->bitmask) != found->addrkey[found->offset]) break; for (pm = &cur->masks; (m = *pm) != NULL; ) if (m->node == cur) { *pm = m->next; break; } else { pm = &m->next; } } KFREE(found->mymask); /* * Masks that have been brought up to this node from below need to * be sent back down. */ for (pmask = &parent->masks; (m = *pmask) != NULL; ) { *pmask = m->next; cur = m->node; if (cur == found) continue; if (found->addrkey[cur->offset] & cur->lastmask) { ipf_rx_attach_mask(parent->right, m); } else if (parent->left != found) { ipf_rx_attach_mask(parent->left, m); } } return (found); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_walktree */ /* Returns: Nil */ /* Paramters: head(I) - pointer to tree head to search */ /* walker(I) - function to call for each node in the tree */ /* arg(I) - parameter to pass to walker, in addition to the */ /* node pointer */ /* */ /* A standard tree walking function except that it is iterative, rather */ /* than recursive and tracks the next node in case the "walker" function */ /* should happen to delete and free the current node. It thus goes without */ /* saying that the "walker" function is not permitted to cause any change */ /* in the validity of the data found at either the left or right child. */ /* ------------------------------------------------------------------------ */ void ipf_rx_walktree(head, walker, arg) ipf_rdx_head_t *head; radix_walk_func_t walker; void *arg; { ipf_rdx_node_t *next; ipf_rdx_node_t *node = head->root; ipf_rdx_node_t *base; while (node->index >= 0) node = node->left; for (;;) { base = node; while ((node->parent->right == node) && (node->root == 0)) node = node->parent; for (node = node->parent->right; node->index >= 0; ) node = node->left; next = node; for (node = base; node != NULL; node = base) { base = node->dupkey; if (node->root == 0) walker(node, arg); } node = next; if (node->root) return; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_inithead */ /* Returns: int - 0 = success, else failure */ /* Paramters: softr(I) - pointer to radix context */ /* headp(O) - location for where to store allocated tree head */ /* */ /* This function allocates and initialises a radix tree head structure. */ /* As a traditional radix tree, node 0 is used as the "0" sentinel and node */ /* "2" is used as the all ones sentinel, leaving node "1" as the root from */ /* which the tree is hung with node "0" on its left and node "2" to the */ /* right. The context, "softr", is used here to provide a common source of */ /* the zeroes and ones data rather than have one per head. */ /* ------------------------------------------------------------------------ */ int ipf_rx_inithead(softr, headp) radix_softc_t *softr; ipf_rdx_head_t **headp; { ipf_rdx_head_t *ptr; ipf_rdx_node_t *node; KMALLOC(ptr, ipf_rdx_head_t *); *headp = ptr; if (ptr == NULL) return -1; bzero(ptr, sizeof(*ptr)); node = ptr->nodes; ptr->root = node + 1; node[0].index = ADF_OFF_BITS; node[0].index = -1 - node[0].index; node[1].index = ADF_OFF_BITS; node[2].index = node[0].index; node[0].parent = node + 1; node[1].parent = node + 1; node[2].parent = node + 1; node[1].bitmask = htonl(0x80000000); node[0].root = 1; node[1].root = 1; node[2].root = 1; node[0].offset = ADF_OFF_BITS >> 5; node[1].offset = ADF_OFF_BITS >> 5; node[2].offset = ADF_OFF_BITS >> 5; node[1].left = &node[0]; node[1].right = &node[2]; node[0].addrkey = (u_32_t *)softr->zeros; node[2].addrkey = (u_32_t *)softr->ones; #ifdef RDX_DEBUG (void) strcpy(node[0].name, "0_ROOT"); (void) strcpy(node[1].name, "1_ROOT"); (void) strcpy(node[2].name, "2_ROOT"); #endif ptr->addaddr = ipf_rx_addroute; ptr->deladdr = ipf_rx_delete; ptr->lookup = ipf_rx_lookup; ptr->matchaddr = ipf_rx_match; ptr->walktree = ipf_rx_walktree; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_freehead */ /* Returns: Nil */ /* Paramters: head(I) - pointer to tree head to free */ /* */ /* This function simply free's up the radix tree head. Prior to calling */ /* this function, it is expected that the tree will have been emptied. */ /* ------------------------------------------------------------------------ */ void ipf_rx_freehead(head) ipf_rdx_head_t *head; { KFREE(head); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_create */ /* Parameters: Nil */ /* */ /* ------------------------------------------------------------------------ */ void * ipf_rx_create() { radix_softc_t *softr; KMALLOC(softr, radix_softc_t *); if (softr == NULL) return NULL; bzero((char *)softr, sizeof(*softr)); KMALLOCS(softr->zeros, u_char *, 3 * sizeof(addrfamily_t)); if (softr->zeros == NULL) { KFREE(softr); return (NULL); } softr->ones = softr->zeros + sizeof(addrfamily_t); return softr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_init */ /* Returns: int - 0 = success (always) */ /* */ /* ------------------------------------------------------------------------ */ int ipf_rx_init(ctx) void *ctx; { radix_softc_t *softr = ctx; memset(softr->zeros, 0, 3 * sizeof(addrfamily_t)); memset(softr->ones, 0xff, sizeof(addrfamily_t)); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_destroy */ /* Returns: Nil */ /* */ /* ------------------------------------------------------------------------ */ void ipf_rx_destroy(ctx) void *ctx; { radix_softc_t *softr = ctx; if (softr->zeros != NULL) KFREES(softr->zeros, 3 * sizeof(addrfamily_t)); KFREE(softr); } /* ====================================================================== */ #ifdef RDX_DEBUG /* * To compile this file as a standalone test unit, use -DRDX_DEBUG=1 */ #define NAME(x) ((x)->index < 0 ? (x)->name : (x)->name) #define GNAME(y) ((y) == NULL ? "NULL" : NAME(y)) typedef struct myst { struct ipf_rdx_node nodes[2]; addrfamily_t dst; addrfamily_t mask; struct myst *next; int printed; } myst_t; typedef struct tabe_s { char *host; char *mask; char *what; } tabe_t; tabe_t builtin[] = { #if 1 { "192:168:100::0", "48", "d" }, { "192:168:100::2", "128", "d" }, #else { "127.192.0.0", "255.255.255.0", "d" }, { "127.128.0.0", "255.255.255.0", "d" }, { "127.96.0.0", "255.255.255.0", "d" }, { "127.80.0.0", "255.255.255.0", "d" }, { "127.72.0.0", "255.255.255.0", "d" }, { "127.64.0.0", "255.255.255.0", "d" }, { "127.56.0.0", "255.255.255.0", "d" }, { "127.48.0.0", "255.255.255.0", "d" }, { "127.40.0.0", "255.255.255.0", "d" }, { "127.32.0.0", "255.255.255.0", "d" }, { "127.24.0.0", "255.255.255.0", "d" }, { "127.16.0.0", "255.255.255.0", "d" }, { "127.8.0.0", "255.255.255.0", "d" }, { "124.0.0.0", "255.0.0.0", "d" }, { "125.0.0.0", "255.0.0.0", "d" }, { "126.0.0.0", "255.0.0.0", "d" }, { "127.0.0.0", "255.0.0.0", "d" }, { "10.0.0.0", "255.0.0.0", "d" }, { "128.250.0.0", "255.255.0.0", "d" }, { "192.168.0.0", "255.255.0.0", "d" }, { "192.168.1.0", "255.255.255.0", "d" }, #endif { NULL, NULL, NULL } }; char *mtable[][1] = { #if 1 { "192:168:100::2" }, { "192:168:101::2" }, #else { "9.0.0.0" }, { "9.0.0.1" }, { "11.0.0.0" }, { "11.0.0.1" }, { "127.0.0.1" }, { "127.0.1.0" }, { "255.255.255.0" }, { "126.0.0.1" }, { "128.251.0.0" }, { "128.251.0.1" }, { "128.251.255.255" }, { "129.250.0.0" }, { "129.250.0.1" }, { "192.168.255.255" }, #endif { NULL } }; int forder[22] = { 14, 13, 12, 5, 10, 3, 19, 7, 4, 20, 8, 2, 17, 9, 16, 11, 15, 1, 6, 18, 0, 21 }; static int nodecount = 0; myst_t *myst_top = NULL; tabe_t *ttable = NULL; void add_addr(ipf_rdx_head_t *, int , int); void checktree(ipf_rdx_head_t *); void delete_addr(ipf_rdx_head_t *rnh, int item); void dumptree(ipf_rdx_head_t *rnh); void nodeprinter(ipf_rdx_node_t *, void *); void printroots(ipf_rdx_head_t *); void random_add(ipf_rdx_head_t *); void random_delete(ipf_rdx_head_t *); void test_addr(ipf_rdx_head_t *rnh, int pref, addrfamily_t *, int); static void ipf_rx_freenode(node, arg) ipf_rdx_node_t *node; void *arg; { ipf_rdx_head_t *head = arg; ipf_rdx_node_t *rv; myst_t *stp; stp = (myst_t *)node; rv = ipf_rx_delete(head, &stp->dst, &stp->mask); if (rv != NULL) { free(rv); } } const char * addrname(ap) addrfamily_t *ap; { static char name[80]; const char *txt; bzero((char *)name, sizeof(name)); txt = inet_ntop(ap->adf_family, &ap->adf_addr, name, sizeof(name)); return txt; } 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)); } } void setaddr(afp, str) addrfamily_t *afp; char *str; { bzero((char *)afp, sizeof(*afp)); if (strchr(str, ':') == NULL) { afp->adf_family = AF_INET; afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else { afp->adf_family = AF_INET6; afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; } inet_pton(afp->adf_family, str, &afp->adf_addr); } void setmask(afp, str) addrfamily_t *afp; char *str; { if (strchr(str, '.') != NULL) { afp->adf_addr.in4.s_addr = inet_addr(str); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else if (afp->adf_family == AF_INET) { afp->adf_addr.i6[0] = htonl(0xffffffff << (32 - atoi(str))); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else if (afp->adf_family == AF_INET6) { fill6bits(atoi(str), afp->adf_addr.i6); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; } } void nodeprinter(node, arg) ipf_rdx_node_t *node; void *arg; { myst_t *stp = (myst_t *)node; printf("Node %-9.9s L %-9.9s R %-9.9s P %9.9s/%-9.9s %s/%d\n", node[0].name, GNAME(node[1].left), GNAME(node[1].right), GNAME(node[0].parent), GNAME(node[1].parent), addrname(&stp->dst), node[0].maskbitcount); if (stp->printed == -1) printf("!!! %d\n", stp->printed); else stp->printed = 1; } void printnode(stp) myst_t *stp; { ipf_rdx_node_t *node = &stp->nodes[0]; if (stp->nodes[0].index > 0) stp = (myst_t *)&stp->nodes[-1]; printf("Node %-9.9s ", node[0].name); printf("L %-9.9s ", GNAME(node[1].left)); printf("R %-9.9s ", GNAME(node[1].right)); printf("P %9.9s", GNAME(node[0].parent)); printf("/%-9.9s ", GNAME(node[1].parent)); printf("%s P%d\n", addrname(&stp->dst), stp->printed); } void buildtab(void) { char line[80], *s; tabe_t *tab; int lines; FILE *fp; lines = 0; fp = fopen("hosts", "r"); while (fgets(line, sizeof(line), fp) != NULL) { s = strchr(line, '\n'); if (s != NULL) *s = '\0'; lines++; if (lines == 1) tab = malloc(sizeof(*tab) * 2); else tab = realloc(tab, (lines + 1) * sizeof(*tab)); tab[lines - 1].host = strdup(line); s = strchr(tab[lines - 1].host, '/'); *s++ = '\0'; tab[lines - 1].mask = s; tab[lines - 1].what = "d"; } fclose(fp); tab[lines].host = NULL; tab[lines].mask = NULL; tab[lines].what = NULL; ttable = tab; } void printroots(rnh) ipf_rdx_head_t *rnh; { printf("Root.0.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[0]), rnh->nodes[0].index, GNAME(rnh->nodes[0].parent), GNAME(rnh->nodes[0].left), GNAME(rnh->nodes[0].right)); printf("Root.1.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[1]), rnh->nodes[1].index, GNAME(rnh->nodes[1].parent), GNAME(rnh->nodes[1].left), GNAME(rnh->nodes[1].right)); printf("Root.2.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[2]), rnh->nodes[2].index, GNAME(rnh->nodes[2].parent), GNAME(rnh->nodes[2].left), GNAME(rnh->nodes[2].right)); } int main(int argc, char *argv[]) { addrfamily_t af; ipf_rdx_head_t *rnh; radix_softc_t *ctx; int j; int i; rnh = NULL; buildtab(); ctx = ipf_rx_create(); ipf_rx_init(ctx); ipf_rx_inithead(ctx, &rnh); printf("=== ADD-0 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { add_addr(rnh, i, i); checktree(rnh); } printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== DELETE-0 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { delete_addr(rnh, i); printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); } printf("=== ADD-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { setaddr(&af, ttable[i].host); add_addr(rnh, i, i); /*forder[i]); */ checktree(rnh); } dumptree(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== TEST-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { setaddr(&af, ttable[i].host); test_addr(rnh, i, &af, -1); } printf("=== TEST-2 ===\n"); for (i = 0; mtable[i][0] != NULL; i++) { setaddr(&af, mtable[i][0]); test_addr(rnh, i, &af, -1); } printf("=== DELETE-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { if (ttable[i].what[0] != 'd') continue; delete_addr(rnh, i); for (j = 0; ttable[j].host != NULL; j++) { setaddr(&af, ttable[j].host); test_addr(rnh, i, &af, 3); } printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); } dumptree(rnh); printf("=== ADD-2 ===\n"); random_add(rnh); checktree(rnh); dumptree(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== DELETE-2 ===\n"); random_delete(rnh); checktree(rnh); dumptree(rnh); ipf_rx_walktree(rnh, ipf_rx_freenode, rnh); return 0; } void dumptree(rnh) ipf_rdx_head_t *rnh; { myst_t *stp; printf("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n"); printroots(rnh); for (stp = myst_top; stp; stp = stp->next) printnode(stp); printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); } void test_addr(rnh, pref, addr, limit) ipf_rdx_head_t *rnh; int pref, limit; addrfamily_t *addr; { static int extras[14] = { 0, -1, 1, 3, 5, 8, 9, 15, 16, 19, 255, 256, 65535, 65536 }; ipf_rdx_node_t *rn; addrfamily_t af; char name[80]; myst_t *stp; int i; memset(&af, 0, sizeof(af)); #if 0 if (limit < 0 || limit > 14) limit = 14; for (i = 0; i < limit; i++) { if (ttable[i].host == NULL) break; setaddr(&af, ttable[i].host); printf("%d.%d.LOOKUP(%s)", pref, i, addrname(&af)); rn = ipf_rx_match(rnh, &af); stp = (myst_t *)rn; printf(" = %s (%s/%d)\n", GNAME(rn), rn ? addrname(&stp->dst) : "NULL", rn ? rn->maskbitcount : 0); } #else printf("%d.%d.LOOKUP(%s)", pref, -1, addrname(addr)); rn = ipf_rx_match(rnh, addr); stp = (myst_t *)rn; printf(" = %s (%s/%d)\n", GNAME(rn), rn ? addrname(&stp->dst) : "NULL", rn ? rn->maskbitcount : 0); #endif } void delete_addr(rnh, item) ipf_rdx_head_t *rnh; int item; { ipf_rdx_node_t *rn; addrfamily_t mask; addrfamily_t af; myst_t **pstp; myst_t *stp; memset(&af, 0, sizeof(af)); memset(&mask, 0, sizeof(mask)); setaddr(&af, ttable[item].host); mask.adf_family = af.adf_family; setmask(&mask, ttable[item].mask); printf("DELETE(%s)\n", addrname(&af)); rn = ipf_rx_delete(rnh, &af, &mask); if (rn == NULL) { printf("FAIL LOOKUP DELETE\n"); checktree(rnh); for (stp = myst_top; stp != NULL; stp = stp->next) if (stp->printed != -1) stp->printed = -2; ipf_rx_walktree(rnh, nodeprinter, NULL); dumptree(rnh); abort(); } printf("%d.delete(%s) = %s\n", item, addrname(&af), GNAME(rn)); for (pstp = &myst_top; (stp = *pstp) != NULL; pstp = &stp->next) if (stp == (myst_t *)rn) break; stp->printed = -1; stp->nodes[0].parent = &stp->nodes[0]; stp->nodes[1].parent = &stp->nodes[1]; *pstp = stp->next; free(stp); nodecount--; checktree(rnh); } void add_addr(rnh, n, item) ipf_rdx_head_t *rnh; int n, item; { ipf_rdx_node_t *rn; myst_t *stp; stp = calloc(1, sizeof(*stp)); rn = (ipf_rdx_node_t *)stp; setaddr(&stp->dst, ttable[item].host); stp->mask.adf_family = stp->dst.adf_family; setmask(&stp->mask, ttable[item].mask); stp->next = myst_top; myst_top = stp; (void) sprintf(rn[0].name, "_BORN.0"); (void) sprintf(rn[1].name, "_BORN.1"); rn = ipf_rx_addroute(rnh, &stp->dst, &stp->mask, stp->nodes); (void) sprintf(rn[0].name, "%d_NODE.0", item); (void) sprintf(rn[1].name, "%d_NODE.1", item); printf("ADD %d/%d %s/%s\n", n, item, rn[0].name, rn[1].name); nodecount++; checktree(rnh); } void checktree(ipf_rdx_head_t *head) { myst_t *s1; ipf_rdx_node_t *rn; if (nodecount <= 1) return; for (s1 = myst_top; s1 != NULL; s1 = s1->next) { int fault = 0; if (s1->printed == -1) continue; rn = &s1->nodes[1]; if (rn->right->parent != rn) fault |= 1; if (rn->left->parent != rn) fault |= 2; if (rn->parent->left != rn && rn->parent->right != rn) fault |= 4; if (fault != 0) { printf("FAULT %#x %s\n", fault, rn->name); dumptree(head); ipf_rx_walktree(head, nodeprinter, NULL); fflush(stdout); fflush(stderr); printf("--\n"); abort(); } } } int * randomize(int *pnitems) { int *order; int nitems; int choice; int j; int i; nitems = sizeof(ttable) / sizeof(ttable[0]); *pnitems = nitems; order = calloc(nitems, sizeof(*order)); srandom(getpid() * time(NULL)); memset(order, 0xff, nitems * sizeof(*order)); order[21] = 21; for (i = 0; i < nitems - 1; i++) { do { choice = rand() % (nitems - 1); for (j = 0; j < nitems; j++) if (order[j] == choice) break; } while (j != nitems); order[i] = choice; } return order; } void random_add(rnh) ipf_rdx_head_t *rnh; { int *order; int nitems; int i; order = randomize(&nitems); for (i = 0; i < nitems - 1; i++) { add_addr(rnh, i, order[i]); checktree(rnh); } free(order); } void random_delete(rnh) ipf_rdx_head_t *rnh; { int *order; int nitems; int i; order = randomize(&nitems); for (i = 0; i < nitems - 1; i++) { delete_addr(rnh, i); checktree(rnh); } free(order); } #endif /* RDX_DEBUG */ diff --git a/sys/contrib/ipfilter/netinet/radix_ipf.h b/sys/contrib/ipfilter/netinet/radix_ipf.h index 73e66b0aa169..6d6d346d8c77 100644 --- a/sys/contrib/ipfilter/netinet/radix_ipf.h +++ b/sys/contrib/ipfilter/netinet/radix_ipf.h @@ -1,95 +1,95 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #ifndef __RADIX_IPF_H__ #define __RADIX_IPF_H__ #ifndef U_32_T typedef unsigned int u_32_t; # define U_32_T 1 #endif typedef struct ipf_rdx_mask { struct ipf_rdx_mask *next; struct ipf_rdx_node *node; u_32_t *mask; int maskbitcount; } ipf_rdx_mask_t; typedef struct ipf_rdx_node { struct ipf_rdx_node *left; struct ipf_rdx_node *right; struct ipf_rdx_node *parent; struct ipf_rdx_node *dupkey; struct ipf_rdx_mask *masks; struct ipf_rdx_mask *mymask; u_32_t *addrkey; u_32_t *maskkey; u_32_t *addroff; u_32_t *maskoff; u_32_t lastmask; u_32_t bitmask; int offset; int index; int maskbitcount; int root; #ifdef RDX_DEBUG char name[40]; #endif } ipf_rdx_node_t; struct ipf_rdx_head; typedef void (* radix_walk_func_t)(ipf_rdx_node_t *, void *); typedef ipf_rdx_node_t *(* idx_hamn_func_t)(struct ipf_rdx_head *, addrfamily_t *, addrfamily_t *, ipf_rdx_node_t *); typedef ipf_rdx_node_t *(* idx_ham_func_t)(struct ipf_rdx_head *, addrfamily_t *, addrfamily_t *); typedef ipf_rdx_node_t *(* idx_ha_func_t)(struct ipf_rdx_head *, addrfamily_t *); typedef void (* idx_walk_func_t)(struct ipf_rdx_head *, radix_walk_func_t, void *); typedef struct ipf_rdx_head { ipf_rdx_node_t *root; ipf_rdx_node_t nodes[3]; ipfmutex_t lock; idx_hamn_func_t addaddr; /* add addr/mask to tree */ idx_ham_func_t deladdr; /* delete addr/mask from tree */ idx_ham_func_t lookup; /* look for specific addr/mask */ idx_ha_func_t matchaddr; /* search tree for address match */ idx_walk_func_t walktree; /* walk entire tree */ } ipf_rdx_head_t; typedef struct radix_softc { u_char *zeros; u_char *ones; } radix_softc_t; #undef RADIX_NODE_HEAD_LOCK #undef RADIX_NODE_HEAD_UNLOCK #ifdef _KERNEL # define RADIX_NODE_HEAD_LOCK(x) MUTEX_ENTER(&(x)->lock) # define RADIX_NODE_HEAD_UNLOCK(x) MUTEX_UNLOCK(&(x)->lock) #else # define RADIX_NODE_HEAD_LOCK(x) # define RADIX_NODE_HEAD_UNLOCK(x) #endif -extern void *ipf_rx_create __P((void)); -extern int ipf_rx_init __P((void *)); -extern void ipf_rx_destroy __P((void *)); -extern int ipf_rx_inithead __P((radix_softc_t *, ipf_rdx_head_t **)); -extern void ipf_rx_freehead __P((ipf_rdx_head_t *)); -extern ipf_rdx_node_t *ipf_rx_addroute __P((ipf_rdx_head_t *, +extern void *ipf_rx_create(void); +extern int ipf_rx_init(void *); +extern void ipf_rx_destroy(void *); +extern int ipf_rx_inithead(radix_softc_t *, ipf_rdx_head_t **); +extern void ipf_rx_freehead(ipf_rdx_head_t *); +extern ipf_rdx_node_t *ipf_rx_addroute(ipf_rdx_head_t *, addrfamily_t *, addrfamily_t *, - ipf_rdx_node_t *)); -extern ipf_rdx_node_t *ipf_rx_delete __P((ipf_rdx_head_t *, addrfamily_t *, - addrfamily_t *)); -extern void ipf_rx_walktree __P((ipf_rdx_head_t *, radix_walk_func_t, - void *)); + ipf_rdx_node_t *); +extern ipf_rdx_node_t *ipf_rx_delete(ipf_rdx_head_t *, addrfamily_t *, + addrfamily_t *); +extern void ipf_rx_walktree(ipf_rdx_head_t *, radix_walk_func_t, + void *); #endif /* __RADIX_IPF_H__ */