diff options
author | markm <markm@FreeBSD.org> | 2013-09-07 07:58:29 +0000 |
---|---|---|
committer | markm <markm@FreeBSD.org> | 2013-09-07 07:58:29 +0000 |
commit | b41e1125b0d80cc6d7243ca4cb413371daa39c4e (patch) | |
tree | 105a603684bdadf523dd0602d74ac9bd3c413033 /sys | |
parent | d3a8919edc9a8e3635968a76db07078a44c2ead2 (diff) | |
parent | 24e0bcea209256960f8d7476c2e6fb93569913c4 (diff) | |
download | FreeBSD-src-b41e1125b0d80cc6d7243ca4cb413371daa39c4e.zip FreeBSD-src-b41e1125b0d80cc6d7243ca4cb413371daa39c4e.tar.gz |
MFC
Diffstat (limited to 'sys')
83 files changed, 33402 insertions, 13178 deletions
diff --git a/sys/amd64/amd64/mp_machdep.c b/sys/amd64/amd64/mp_machdep.c index 3addd43..0fdb668 100644 --- a/sys/amd64/amd64/mp_machdep.c +++ b/sys/amd64/amd64/mp_machdep.c @@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$"); #include <machine/smp.h> #include <machine/specialreg.h> #include <machine/tss.h> +#include <machine/cpu.h> #ifdef XENHVM #include <xen/hvm.h> @@ -125,6 +126,11 @@ u_long *ipi_rendezvous_counts[MAXCPU]; static u_long *ipi_hardclock_counts[MAXCPU]; #endif +/* Default cpu_ops implementation. */ +struct cpu_ops cpu_ops = { + .ipi_vectored = lapic_ipi_vectored +}; + extern inthand_t IDTVEC(fast_syscall), IDTVEC(fast_syscall32); extern int pmap_pcid_enabled; @@ -1125,7 +1131,7 @@ ipi_send_cpu(int cpu, u_int ipi) if (old_pending) return; } - lapic_ipi_vectored(ipi, cpu_apic_ids[cpu]); + cpu_ops.ipi_vectored(ipi, cpu_apic_ids[cpu]); } /* @@ -1395,7 +1401,7 @@ ipi_all_but_self(u_int ipi) CPU_OR_ATOMIC(&ipi_nmi_pending, &other_cpus); CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); - lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS); + cpu_ops.ipi_vectored(ipi, APIC_IPI_DEST_OTHERS); } int diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c index 8940d4a..10d4184 100644 --- a/sys/amd64/amd64/pmap.c +++ b/sys/amd64/amd64/pmap.c @@ -254,30 +254,6 @@ SYSCTL_INT(_vm_pmap, OID_AUTO, pcid_enabled, CTLFLAG_RDTUN, &pmap_pcid_enabled, 0, "Is TLB Context ID enabled ?"); int invpcid_works = 0; -/* - * Perform the guaranteed invalidation of all TLB entries. This - * includes the global entries, and entries in all PCIDs, not only the - * current context. The function works both on non-PCID CPUs and CPUs - * with the PCID turned off or on. See IA-32 SDM Vol. 3a 4.10.4.1 - * Operations that Invalidate TLBs and Paging-Structure Caches. - */ -static __inline void -invltlb_globpcid(void) -{ - uint64_t cr4; - - cr4 = rcr4(); - load_cr4(cr4 & ~CR4_PGE); - /* - * Although preemption at this point could be detrimental to - * performance, it would not lead to an error. PG_G is simply - * ignored if CR4.PGE is clear. Moreover, in case this block - * is re-entered, the load_cr4() either above or below will - * modify CR4.PGE flushing the TLB. - */ - load_cr4(cr4 | CR4_PGE); -} - static int pmap_pcid_save_cnt_proc(SYSCTL_HANDLER_ARGS) { diff --git a/sys/amd64/conf/NOTES b/sys/amd64/conf/NOTES index 95d4ce0..57a9331 100644 --- a/sys/amd64/conf/NOTES +++ b/sys/amd64/conf/NOTES @@ -309,6 +309,7 @@ options DRM_DEBUG # Include debug printfs (slow) # nfe: nVidia nForce MCP on-board Ethernet Networking (BSD open source) # nve: nVidia nForce MCP on-board Ethernet Networking # sfxge: Solarflare SFC9000 family 10Gb Ethernet adapters +# vmx: VMware VMXNET3 Ethernet (BSD open source) # wpi: Intel 3945ABG Wireless LAN controller # Requires the wpi firmware module @@ -325,6 +326,7 @@ device mthca # Mellanox HCA InfiniBand device nfe # nVidia nForce MCP on-board Ethernet device nve # nVidia nForce MCP on-board Ethernet Networking device sfxge # Solarflare SFC9000 10Gb Ethernet +device vmx # VMware VMXNET3 Ethernet device wpi # Intel 3945ABG wireless NICs. # IEEE 802.11 adapter firmware modules diff --git a/sys/amd64/include/cpu.h b/sys/amd64/include/cpu.h index 1c2871f..5b994e1 100644 --- a/sys/amd64/include/cpu.h +++ b/sys/amd64/include/cpu.h @@ -54,6 +54,17 @@ #define TRAPF_PC(framep) ((framep)->tf_rip) #ifdef _KERNEL +/* + * Struct containing pointers to CPU management functions whose + * implementation is run time selectable. Selection can be made, + * for example, based on detection of a particular CPU variant or + * hypervisor environment. + */ +struct cpu_ops { + void (*ipi_vectored)(u_int, int); +}; + +extern struct cpu_ops cpu_ops; extern char btext[]; extern char etext[]; diff --git a/sys/amd64/include/cpufunc.h b/sys/amd64/include/cpufunc.h index 3d381c6..5f8197b 100644 --- a/sys/amd64/include/cpufunc.h +++ b/sys/amd64/include/cpufunc.h @@ -461,6 +461,34 @@ invltlb(void) load_cr3(rcr3()); } +#ifndef CR4_PGE +#define CR4_PGE 0x00000080 /* Page global enable */ +#endif + +/* + * Perform the guaranteed invalidation of all TLB entries. This + * includes the global entries, and entries in all PCIDs, not only the + * current context. The function works both on non-PCID CPUs and CPUs + * with the PCID turned off or on. See IA-32 SDM Vol. 3a 4.10.4.1 + * Operations that Invalidate TLBs and Paging-Structure Caches. + */ +static __inline void +invltlb_globpcid(void) +{ + uint64_t cr4; + + cr4 = rcr4(); + load_cr4(cr4 & ~CR4_PGE); + /* + * Although preemption at this point could be detrimental to + * performance, it would not lead to an error. PG_G is simply + * ignored if CR4.PGE is clear. Moreover, in case this block + * is re-entered, the load_cr4() either above or below will + * modify CR4.PGE flushing the TLB. + */ + load_cr4(cr4 | CR4_PGE); +} + /* * TLB flush for an individual page (even if it has PG_G). * Only works on 486+ CPUs (i386 does not have PG_G). diff --git a/sys/amd64/include/pcpu.h b/sys/amd64/include/pcpu.h index 387df1a..3d51512 100644 --- a/sys/amd64/include/pcpu.h +++ b/sys/amd64/include/pcpu.h @@ -33,15 +33,6 @@ #error "sys/cdefs.h is a prerequisite for this file" #endif -#if defined(XEN) || defined(XENHVM) -#ifndef NR_VIRQS -#define NR_VIRQS 24 -#endif -#ifndef NR_IPIS -#define NR_IPIS 2 -#endif -#endif - /* * The SMP parts are setup in pmap.c and locore.s for the BSP, and * mp_machdep.c sets up the data for the AP's to "see" when they awake. diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c index 419101f..c365111 100644 --- a/sys/amd64/vmm/intel/vmx.c +++ b/sys/amd64/vmm/intel/vmx.c @@ -138,8 +138,6 @@ SYSCTL_ULONG(_hw_vmm_vmx, OID_AUTO, cr4_ones_mask, CTLFLAG_RD, SYSCTL_ULONG(_hw_vmm_vmx, OID_AUTO, cr4_zeros_mask, CTLFLAG_RD, &cr4_zeros_mask, 0, NULL); -static volatile u_int nextvpid; - static int vmx_no_patmsr; static int vmx_initialized; @@ -172,6 +170,11 @@ static int cap_monitor_trap; /* statistics */ static VMM_STAT_INTEL(VMEXIT_HLT_IGNORED, "number of times hlt was ignored"); +static struct unrhdr *vpid_unr; +static u_int vpid_alloc_failed; +SYSCTL_UINT(_hw_vmm_vmx, OID_AUTO, vpid_alloc_failed, CTLFLAG_RD, + &vpid_alloc_failed, 0, NULL); + #ifdef KTR static const char * exit_reason_to_str(int reason) @@ -382,6 +385,88 @@ vmx_fix_cr4(u_long cr4) } static void +vpid_free(int vpid) +{ + if (vpid < 0 || vpid > 0xffff) + panic("vpid_free: invalid vpid %d", vpid); + + /* + * VPIDs [0,VM_MAXCPU] are special and are not allocated from + * the unit number allocator. + */ + + if (vpid > VM_MAXCPU) + free_unr(vpid_unr, vpid); +} + +static void +vpid_alloc(uint16_t *vpid, int num) +{ + int i, x; + + if (num <= 0 || num > VM_MAXCPU) + panic("invalid number of vpids requested: %d", num); + + /* + * If the "enable vpid" execution control is not enabled then the + * VPID is required to be 0 for all vcpus. + */ + if ((procbased_ctls2 & PROCBASED2_ENABLE_VPID) == 0) { + for (i = 0; i < num; i++) + vpid[i] = 0; + return; + } + + /* + * Allocate a unique VPID for each vcpu from the unit number allocator. + */ + for (i = 0; i < num; i++) { + x = alloc_unr(vpid_unr); + if (x == -1) + break; + else + vpid[i] = x; + } + + if (i < num) { + atomic_add_int(&vpid_alloc_failed, 1); + + /* + * If the unit number allocator does not have enough unique + * VPIDs then we need to allocate from the [1,VM_MAXCPU] range. + * + * These VPIDs are not be unique across VMs but this does not + * affect correctness because the combined mappings are also + * tagged with the EP4TA which is unique for each VM. + * + * It is still sub-optimal because the invvpid will invalidate + * combined mappings for a particular VPID across all EP4TAs. + */ + while (i-- > 0) + vpid_free(vpid[i]); + + for (i = 0; i < num; i++) + vpid[i] = i + 1; + } +} + +static void +vpid_init(void) +{ + /* + * VPID 0 is required when the "enable VPID" execution control is + * disabled. + * + * VPIDs [1,VM_MAXCPU] are used as the "overflow namespace" when the + * unit number allocator does not have sufficient unique VPIDs to + * satisfy the allocation. + * + * The remaining VPIDs are managed by the unit number allocator. + */ + vpid_unr = new_unrhdr(VM_MAXCPU + 1, 0xffff, NULL); +} + +static void msr_save_area_init(struct msr_entry *g_area, int *g_count) { int cnt; @@ -422,6 +507,11 @@ static int vmx_cleanup(void) { + if (vpid_unr != NULL) { + delete_unrhdr(vpid_unr); + vpid_unr = NULL; + } + smp_rendezvous(NULL, vmx_disable, NULL, NULL); return (0); @@ -607,6 +697,8 @@ vmx_init(void) cr4_ones_mask = fixed0 & fixed1; cr4_zeros_mask = ~fixed0 & ~fixed1; + vpid_init(); + /* enable VMX operation */ smp_rendezvous(NULL, vmx_enable, NULL, NULL); @@ -615,37 +707,6 @@ vmx_init(void) return (0); } -/* - * If this processor does not support VPIDs then simply return 0. - * - * Otherwise generate the next value of VPID to use. Any value is alright - * as long as it is non-zero. - * - * We always execute in VMX non-root context with EPT enabled. Thus all - * combined mappings are tagged with the (EP4TA, VPID, PCID) tuple. This - * in turn means that multiple VMs can share the same VPID as long as - * they have distinct EPT page tables. - * - * XXX - * We should optimize this so that it returns VPIDs that are not in - * use. Then we will not unnecessarily invalidate mappings in - * vmx_set_pcpu_defaults() just because two or more vcpus happen to - * use the same 'vpid'. - */ -static uint16_t -vmx_vpid(void) -{ - uint16_t vpid = 0; - - if ((procbased_ctls2 & PROCBASED2_ENABLE_VPID) != 0) { - do { - vpid = atomic_fetchadd_int(&nextvpid, 1); - } while (vpid == 0); - } - - return (vpid); -} - static int vmx_setup_cr_shadow(int which, struct vmcs *vmcs, uint32_t initial) { @@ -681,7 +742,7 @@ vmx_setup_cr_shadow(int which, struct vmcs *vmcs, uint32_t initial) static void * vmx_vminit(struct vm *vm) { - uint16_t vpid; + uint16_t vpid[VM_MAXCPU]; int i, error, guest_msr_count; struct vmx *vmx; @@ -744,6 +805,8 @@ vmx_vminit(struct vm *vm) if (!vmx_no_patmsr && guest_msr_rw(vmx, MSR_PAT)) panic("vmx_vminit: error setting guest pat msr access"); + vpid_alloc(vpid, VM_MAXCPU); + for (i = 0; i < VM_MAXCPU; i++) { vmx->vmcs[i].identifier = vmx_revision(); error = vmclear(&vmx->vmcs[i]); @@ -752,8 +815,6 @@ vmx_vminit(struct vm *vm) error, i); } - vpid = vmx_vpid(); - error = vmcs_set_defaults(&vmx->vmcs[i], (u_long)vmx_longjmp, (u_long)&vmx->ctx[i], @@ -763,7 +824,7 @@ vmx_vminit(struct vm *vm) procbased_ctls2, exit_ctls, entry_ctls, vtophys(vmx->msr_bitmap), - vpid); + vpid[i]); if (error != 0) panic("vmx_vminit: vmcs_set_defaults error %d", error); @@ -772,7 +833,7 @@ vmx_vminit(struct vm *vm) vmx->cap[i].proc_ctls = procbased_ctls; vmx->state[i].lastcpu = -1; - vmx->state[i].vpid = vpid; + vmx->state[i].vpid = vpid[i]; msr_save_area_init(vmx->guest_msrs[i], &guest_msr_count); @@ -1580,9 +1641,12 @@ err_exit: static void vmx_vmcleanup(void *arg) { - int error; + int i, error; struct vmx *vmx = arg; + for (i = 0; i < VM_MAXCPU; i++) + vpid_free(vmx->state[i].vpid); + /* * XXXSMP we also need to clear the VMCS active on the other vcpus. */ diff --git a/sys/amd64/vmm/io/ppt.c b/sys/amd64/vmm/io/ppt.c index 5aedaf2..878bf8a 100644 --- a/sys/amd64/vmm/io/ppt.c +++ b/sys/amd64/vmm/io/ppt.c @@ -568,7 +568,7 @@ ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, return (ENXIO); ppt->msix.arg[idx].pptdev = ppt; - ppt->msix.arg[idx].vec = msg; + ppt->msix.arg[idx].vec = msg & 0xFF; ppt->msix.arg[idx].vcpu = (addr >> 12) & 0xFF; /* Setup the MSI-X interrupt */ diff --git a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c index 9ddb44b..267e3f9 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c @@ -729,7 +729,7 @@ bcm_gpio_attach(device_t dev) goto fail; /* Initialize the software controlled pins. */ - for (i = 0, j = 0; j < BCM_GPIO_PINS - 1; j++) { + for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { if (bcm_gpio_pin_is_ro(sc, j)) continue; snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, diff --git a/sys/arm/include/sf_buf.h b/sys/arm/include/sf_buf.h index 2225d58..6508744 100644 --- a/sys/arm/include/sf_buf.h +++ b/sys/arm/include/sf_buf.h @@ -40,6 +40,8 @@ struct vm_page; struct sf_buf; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); static __inline vm_offset_t sf_buf_kva(struct sf_buf *sf) diff --git a/sys/conf/files b/sys/conf/files index 2920581..f790acb 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -483,6 +483,16 @@ contrib/ipfilter/netinet/ip_sync.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/mlfk_ipl.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" +contrib/ipfilter/netinet/ip_nat6.c optional ipfilter inet \ + compile-with "${NORMAL_C} -I$S/contrib/ipfilter" +contrib/ipfilter/netinet/ip_rules.c optional ipfilter inet \ + compile-with "${NORMAL_C} -I$S/contrib/ipfilter" +contrib/ipfilter/netinet/ip_scan.c optional ipfilter inet \ + compile-with "${NORMAL_C} -I$S/contrib/ipfilter" +contrib/ipfilter/netinet/ip_dstlist.c optional ipfilter inet \ + compile-with "${NORMAL_C} -I$S/contrib/ipfilter" +contrib/ipfilter/netinet/radix_ipf.c optional ipfilter inet \ + compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/libfdt/fdt.c optional fdt contrib/libfdt/fdt_ro.c optional fdt contrib/libfdt/fdt_rw.c optional fdt diff --git a/sys/contrib/ipfilter/netinet/QNX_OCL.txt b/sys/contrib/ipfilter/netinet/QNX_OCL.txt deleted file mode 100644 index b623776..0000000 --- a/sys/contrib/ipfilter/netinet/QNX_OCL.txt +++ /dev/null @@ -1,277 +0,0 @@ -$FreeBSD$ - - End User License Certificate (EULA) End User License Certificate - (EULA) - Support Support - QNX Source Licenses QNX Source Licenses - License of the month - Confidential Source License - Version 1.0 - -QNX Open Community License Version 1.0 - - THIS QNX OPEN COMMUNITY LICENSE ( "THE OCL", OR "THIS AGREEMENT") - APPLIES TO PROGRAMS THAT QNX SOFTWARE SYSTEMS LTD. ("QSS") EXPRESSLY - ELECTS TO LICENSE UNDER THE OCL TERMS. IT ALSO APPLIES TO DERIVATIVE - WORKS CREATED UNDER THIS AGREEMENT THAT CREATORS ELECT TO LICENSE TO - OTHERS IN SOURCE CODE FORM. ANY USE, REPRODUCTION, MODIFICATION OR - DISTRIBUTION OF SUCH PROGRAMS CONSTITUTES RECIPIENT'S ACCEPTANCE OF - THE OCL. THE LICENSE RIGHTS GRANTED BELOW ARE CONDITIONAL UPON - RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT AND THE FORMATION OF A - BINDING CONTRACT. NOTHING ELSE GRANTS PERMISSION TO USE, REPRODUCE, - MODIFY OR DISTRIBUTE SUCH PROGRAMS OR THEIR DERIVATIVE WORKS. THESE - ACTIONS ARE OTHERWISE PROHIBITED. CONTACT QSS IF OTHER STEPS ARE - REQUIRED LOCALLY TO CREATE A BINDING CONTRACT. - - The OCL is intended to promote the development, use and distribution - of derivative works created from QSS source code. This includes - commercial distribution of object code versions under the terms of - Recipient's own license agreement and, at Recipient's option, sharing - of source code modifications within the QNX developer's community. The - license granted under the OCL is royalty free. Recipient is entitled - to charge royalties for object code versions of derivative works that - originate with Recipient. If Recipient elects to license source code - for its derivative works to others, then it must be licensed under the - OCL. The terms of the OCL are as follows: - -1. DEFINITIONS - - "Contribution" means: - - a. in the case of QSS: (i) the Original Program, where the Original - Program originates from QSS, (ii) changes and/or additions to - Unrestricted Open Source, where the Original Program originates - from Unrestricted Open Source and where such changes and/or - additions originate from QSS, and (iii) changes and/or additions - to the Program where such changes and/or additions originate from - QSS. - b. in the case of each Contributor, changes and/or additions to the - Program, where such changes and/or additions originate from and - are distributed by that particular Contributor. - - A Contribution 'originates' from a Contributor if it was added to the - Program by such Contributor itself or anyone acting on such - Contributor's behalf. Contributions do not include additions to the - Program which: (i) are separate modules of software distributed in - conjunction with the Program under their own license agreement, and - (ii) are not derivative works of the Program. - - "Contributor" means QSS and any other entity that distributes the - Program. - - "Licensed Patents " mean patent claims licensable by Contributor to - others, which are necessarily infringed by the use or sale of its - Contribution alone or when combined with the Program. - - "Unrestricted Open Source" means published source code that is - licensed for free use and distribution under an unrestricted licensing - and distribution model, such as the Berkley Software Design ("BSD") - and "BSD-like" licenses. It specifically excludes any source code - licensed under any version of the GNU General Public License (GPL) or - the GNU Lesser/Library GPL. All "Unrestricted Open Source" license - terms appear or are clearly identified in the header of any affected - source code for the Original Program. - - "Original Program" means the original version of the software - accompanying this Agreement as released by QSS, including source code, - object code and documentation, if any. - - "Program" means the Original Program and Contributions. - - "Recipient" means anyone who receives the Program under this - Agreement, including all Contributors. - -2. GRANT OF RIGHTS - - a. Subject to the terms of this Agreement, each Contributor hereby - grants Recipient a non-exclusive, worldwide, royalty-free - copyright license to reproduce, prepare derivative works of, - publicly display, publicly perform, and directly and indirectly - sublicense and distribute the Contribution of such Contributor, if - any, and such derivative works, in source code and object code - form. - b. Subject to the terms of this Agreement, each Contributor hereby - grants Recipient a non-exclusive, worldwide, royalty-free patent - license under Licensed Patents to make, use, sell, offer to sell, - import and otherwise transfer the Contribution of such - Contributor, if any, in source code and object code form. This - patent license shall apply to the combination of the Contribution - and the Program if, at the time the Contribution is added by the - Contributor, such addition of the Contribution causes such - combination to be covered by the Licensed Patents. The patent - license shall not apply to any other combinations which include - the Contribution. - c. Recipient understands that although each Contributor grants the - licenses to its Contributions set forth herein, no assurances are - provided by any Contributor that the Program does not infringe the - patent or other intellectual property rights of any other entity. - Each Contributor disclaims any liability to Recipient for claims - brought by any other entity based on infringement of intellectual - property rights or otherwise. As a condition to exercising the - rights and licenses granted hereunder, each Recipient hereby - assumes sole responsibility to secure any other intellectual - property rights needed, if any. For example, if a third party - patent license is required to allow Recipient to distribute the - Program, it is Recipient's responsibility to acquire that license - before distributing the Program. - d. Each Contributor represents that to its knowledge it has - sufficient copyright rights in its Contribution, if any, to grant - the copyright license set forth in this Agreement. - - 3. REQUIREMENTS - - A Contributor may choose to distribute the Program in object code form - under its own license agreement, provided that: - - a. it complies with the terms and conditions of this Agreement; and - b. its license agreement: - i. effectively disclaims on behalf of all Contributors all - warranties and conditions, express and implied, including - warranties or conditions of title and non-infringement, and - implied warranties or conditions of merchantability and - fitness for a particular purpose; - ii. effectively excludes on behalf of all Contributors all - liability for damages, including direct, indirect, special, - incidental and consequential damages, such as lost profits; - and - iii. states that any provisions which differ from this Agreement - are offered by that Contributor alone and not by any other - party. - - If the Program is made available in source code form: - - a. it must be made available under this Agreement; and - b. a copy of this Agreement must be included with each copy of the - Program. Each Contributor must include the following in a - conspicuous location in the Program along with any other copyright - or attribution statements required by the terms of any applicable - Unrestricted Open Source license: - Copyright {date here}, QNX Software Systems Ltd. and others. All - Rights Reserved. - - In addition, each Contributor must identify itself as the originator - of its Contribution, if any, in a manner that reasonably allows - subsequent Recipients to identify the originator of the Contribution. - - 4. COMMERCIAL DISTRIBUTION - - Commercial distributors of software may accept certain - responsibilities with respect to end users, business partners and the - like. While this license is intended to facilitate the commercial use - of the Program, the Contributor who includes the Program in a - commercial product offering should do so in a manner which does not - create potential liability for other Contributors. Therefore, if a - Contributor includes the Program in a commercial product offering, - such Contributor ("Commercial Contributor") hereby agrees to defend - and indemnify every other Contributor ("Indemnified Contributor") - against any losses, damages and costs (collectively "Losses") arising - from claims, lawsuits and other legal actions brought by a third party - against the Indemnified Contributor to the extent caused by the acts - or omissions of such Commercial Contributor in connection with its - distribution of the Program in a commercial product offering. The - obligations in this section do not apply to any claims or Losses - relating to any actual or alleged intellectual property infringement. - In order to qualify, an Indemnified Contributor must: a) promptly - notify the Commercial Contributor in writing of such claim, and b) - allow the Commercial Contributor to control, and cooperate with the - Commercial Contributor in, the defense and any related settlement - negotiations. The Indemnified Contributor may participate in any such - claim at its own expense. - - For example, a Contributor might include the Program in a commercial - product offering, Product X. That Contributor is then a Commercial - Contributor. If that Commercial Contributor then makes performance - claims, or offers warranties related to Product X, those performance - claims and warranties are such Commercial Contributor's responsibility - alone. Under this section, the Commercial Contributor would have to - defend claims against the other Contributors related to those - performance claims and warranties, and if a court requires any other - Contributor to pay any damages as a result, the Commercial Contributor - must pay those damages. - - 5. NO WARRANTY - - Recipient acknowledges that there may be errors or bugs in the Program - and that it is imperative that Recipient conduct thorough testing to - identify and correct any problems prior to the productive use or - commercial release of any products that use the Program, and prior to - the release of any modifications, updates or enhancements thereto. - - EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS - PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY - WARRANTIES OR CONDITIONS OF TITLE, NON- INFRINGEMENT, MERCHANTABILITY - OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely - responsible for determining the appropriateness of using and - distributing the Program and assumes all risks associated with its - exercise of rights under this Agreement, including but not limited to - the risks and costs of program errors, compliance with applicable - laws, damage to or loss of data, programs or equipment, and - unavailability or interruption of operations. - - 6. DISCLAIMER OF LIABILITY - - EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR - ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING - WITHOUT LIMITATION LOST PROFITS), 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 OR - DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED - HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - 7. GENERAL - - If any provision of this Agreement is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this Agreement, and without further - action by the parties hereto, such provision shall be reformed to the - minimum extent necessary to make such provision valid and enforceable. - - If Recipient institutes patent litigation against a Contributor with - respect to a patent applicable to software (including a cross-claim or - counterclaim in a lawsuit), then any patent licenses granted by that - Contributor to such recipient under this Agreement shall terminate as - of the date such litigation is filed. In addition, If Recipient - institutes patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Program - itself (excluding combinations of the Program with other software or - hardware) infringes such Recipient's patent(s), then such Recipient's - rights granted under Section 2(b) shall terminate as of the date such - litigation is filed. - - All Recipient's rights under this Agreement shall terminate if it - fails to comply with any of the material terms or conditions of this - Agreement and does not cure such failure in a reasonable period of - time after becoming aware of such noncompliance. If all Recipient's - rights under this Agreement terminate, Recipient agrees to cease use - and distribution of the Program as soon as reasonably practicable. - However, Recipient's obligations under this Agreement and any licenses - granted by Recipient relating to the Program shall continue and - survive. - - QSS may publish new versions (including revisions) of this Agreement - from time to time. Each new version of the Agreement will be given a - distinguishing version number. The Program (including Contributions) - may always be distributed subject to the version of the Agreement - under which it was received. In addition, after a new version of the - Agreement is published, Contributor may elect to distribute the - Program (including its Contributions) under the new version. No one - other than QSS has the right to modify this Agreement. Except as - expressly stated in Sections 2(a) and 2(b) above, Recipient receives - no rights or licenses to the intellectual property of any Contributor - under this Agreement, whether expressly, by implication, estoppel or - otherwise. All rights in the Program not expressly granted under this - Agreement are reserved. - - This Agreement is governed by the laws in force in the Province of - Ontario, Canada without regard to the conflict of law provisions - therein. The parties expressly disclaim the provisions of the United - Nations Convention on Contracts for the International Sale of Goods. - No party to this Agreement will bring a legal action under this - Agreement more than one year after the cause of action arose. Each - party waives its rights to a jury trial in any resulting litigation. - - * QNX is a registered trademark of QNX Software Systems Ltd. - - Document Version: ocl1_00 diff --git a/sys/contrib/ipfilter/netinet/fil.c b/sys/contrib/ipfilter/netinet/fil.c index e8e543a..b392397 100644 --- a/sys/contrib/ipfilter/netinet/fil.c +++ b/sys/contrib/ipfilter/netinet/fil.c @@ -1,9 +1,14 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2003 by Darren Reed. + * 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 @@ -15,15 +20,6 @@ #include <sys/types.h> #include <sys/param.h> #include <sys/time.h> -#if defined(__NetBSD__) -# if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL) -# if (__NetBSD_Version__ < 301000000) -# include "opt_ipfilter_log.h" -# else -# include "opt_ipfilter.h" -# endif -# endif -#endif #if defined(_KERNEL) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 220000) # if (__FreeBSD_version >= 400000) @@ -82,23 +78,9 @@ struct file; #ifdef sun # include <net/af.h> #endif -#if !defined(_KERNEL) && (defined(__FreeBSD__) || defined(SOLARIS2)) -# if (__FreeBSD_version >= 504000) -# undef _RADIX_H_ -# endif -# include "radix_ipf.h" -#endif -#ifdef __osf__ -# include "radix_ipf.h" -#else -# include <net/route.h> -#endif #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> -#if !defined(linux) -# include <netinet/ip_var.h> -#endif #if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ # include <sys/hashing.h> # include <netinet/in_var.h> @@ -121,7 +103,6 @@ struct file; # include <netinet6/in6_var.h> # endif #endif -#include <netinet/tcpip.h> #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" @@ -131,9 +112,8 @@ struct file; #ifdef IPFILTER_SCAN # include "netinet/ip_scan.h" #endif -#ifdef IPFILTER_SYNC -# include "netinet/ip_sync.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 @@ -144,14 +124,18 @@ struct file; #endif #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include <sys/malloc.h> -# if defined(_KERNEL) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #endif #include "netinet/ipl.h" -/* END OF INCLUDES */ -#include <machine/in_cksum.h> +#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) +# include <sys/callout.h> +extern struct callout ipf_slowtimer_ch; +#endif +#if defined(__OpenBSD__) +# include <sys/timeout.h> +extern struct timeout 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"; @@ -162,101 +146,80 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #ifndef _KERNEL # include "ipf.h" # include "ipt.h" -# include "bpf-ipf.h" extern int opts; +extern int blockreason; #endif /* _KERNEL */ +#define LBUMP(x) softc->x++ +#define LBUMPD(x, y) do { softc->x.y++; DT(y); } while (0) -fr_info_t frcache[2][8]; -struct filterstats frstats[2]; -struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } }, - *ipfilter6[2][2] = { { NULL, NULL }, { NULL, NULL } }, - *ipacct6[2][2] = { { NULL, NULL }, { NULL, NULL } }, - *ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } }, - *ipnatrules[2][2] = { { NULL, NULL }, { NULL, NULL } }; -struct frgroup *ipfgroups[IPL_LOGSIZE][2]; -char ipfilter_version[] = IPL_VERSION; -int fr_refcnt = 0; -/* - * For fr_running: - * 0 == loading, 1 = running, -1 = disabled, -2 = unloading - */ -int fr_running = 0; -int fr_flags = IPF_LOGGING; -int fr_active = 0; -int fr_control_forwarding = 0; -int fr_update_ipid = 0; -u_short fr_ip_id = 0; -int fr_chksrc = 0; /* causes a system crash if enabled */ -int fr_minttl = 4; -int fr_icmpminfragmtu = 68; -u_long fr_frouteok[2] = {0, 0}; -u_long fr_userifqs = 0; -u_long fr_badcoalesces[2] = {0, 0}; -u_char ipf_iss_secret[32]; -#if defined(IPFILTER_DEFAULT_BLOCK) -int fr_pass = FR_BLOCK|FR_NOMATCH; -#else -int fr_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH; -#endif -int fr_features = 0 -#ifdef IPFILTER_LKM - | IPF_FEAT_LKM -#endif -#ifdef IPFILTER_LOG - | IPF_FEAT_LOG -#endif -#ifdef IPFILTER_LOOKUP - | IPF_FEAT_LOOKUP -#endif -#ifdef IPFILTER_BPF - | IPF_FEAT_BPF -#endif -#ifdef IPFILTER_COMPILED - | IPF_FEAT_COMPILED -#endif -#ifdef IPFILTER_CKSUM - | IPF_FEAT_CKSUM -#endif -#ifdef IPFILTER_SYNC - | IPF_FEAT_SYNC -#endif -#ifdef IPFILTER_SCAN - | IPF_FEAT_SCAN -#endif -#ifdef USE_INET6 - | IPF_FEAT_IPV6 +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)); +#ifdef IPFILTER_LOG +static frentry_t *ipf_dolog __P((fr_info_t *, u_32_t *)); #endif - ; - -static INLINE int fr_ipfcheck __P((fr_info_t *, frentry_t *, int)); -static int fr_portcheck __P((frpcmp_t *, u_short *)); -static int frflushlist __P((int, minor_t, int *, frentry_t **)); -static ipfunc_t fr_findfunc __P((ipfunc_t)); -static frentry_t *fr_firewall __P((fr_info_t *, u_32_t *)); -static int fr_funcinit __P((frentry_t *fr)); -static INLINE void frpr_ah __P((fr_info_t *)); -static INLINE void frpr_esp __P((fr_info_t *)); -static INLINE void frpr_gre __P((fr_info_t *)); -static INLINE void frpr_udp __P((fr_info_t *)); -static INLINE void frpr_tcp __P((fr_info_t *)); -static INLINE void frpr_icmp __P((fr_info_t *)); -static INLINE void frpr_ipv4hdr __P((fr_info_t *)); -static INLINE int frpr_pullup __P((fr_info_t *, int)); -static INLINE void frpr_short __P((fr_info_t *, int)); -static INLINE int frpr_tcpcommon __P((fr_info_t *)); -static INLINE int frpr_udpcommon __P((fr_info_t *)); -static int fr_updateipid __P((fr_info_t *)); -#ifdef IPFILTER_LOOKUP -static int fr_grpmapinit __P((frentry_t *fr)); -static INLINE void *fr_resolvelookup __P((u_int, u_int, i6addr_t *, lookupfunc_t *)); +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 *, + struct ipftuneable *, + ipftuneval_t *)); +#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && \ + !defined(__FreeBSD__)) || \ + FREEBSD_LT_REV(501000) || NETBSD_LT_REV(105000000) || \ + OPENBSD_LT_REV(200006) +static int ppsratecheck(struct timeval *, int *, int); #endif -static void frsynclist __P((frentry_t *, void *)); -static ipftuneable_t *fr_findtunebyname __P((const char *)); -static ipftuneable_t *fr_findtunebycookie __P((void *, void **)); -static int ipf_geniter __P((ipftoken_t *, ipfgeniter_t *)); -static int ipf_frruleiter __P((void *, int, void *)); -static void ipf_unlinktoken __P((ipftoken_t *)); /* @@ -265,7 +228,7 @@ static void ipf_unlinktoken __P((ipftoken_t *)); * 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. */ -const struct optlist ipopts[20] = { +static const struct optlist ipopts[20] = { { IPOPT_NOP, 0x000001 }, { IPOPT_RR, 0x000002 }, { IPOPT_ZSU, 0x000004 }, @@ -289,7 +252,7 @@ const struct optlist ipopts[20] = { }; #ifdef USE_INET6 -struct optlist ip6exthdr[] = { +static struct optlist ip6exthdr[] = { { IPPROTO_HOPOPTS, 0x000001 }, { IPPROTO_IPV6, 0x000002 }, { IPPROTO_ROUTING, 0x000004 }, @@ -303,20 +266,10 @@ struct optlist ip6exthdr[] = { }; #endif -struct optlist tcpopts[] = { - { TCPOPT_NOP, 0x000001 }, - { TCPOPT_MAXSEG, 0x000002 }, - { TCPOPT_WINDOW, 0x000004 }, - { TCPOPT_SACK_PERMITTED, 0x000008 }, - { TCPOPT_SACK, 0x000010 }, - { TCPOPT_TIMESTAMP, 0x000020 }, - { 0, 0x000000 } -}; - /* * bit values for identifying presence of individual IP security options */ -const struct optlist secopt[8] = { +static const struct optlist secopt[8] = { { IPSO_CLASS_RES4, 0x01 }, { IPSO_CLASS_TOPS, 0x02 }, { IPSO_CLASS_SECR, 0x04 }, @@ -327,16 +280,143 @@ const struct optlist secopt[8] = { { 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 fr_availfuncs[] = { -#ifdef IPFILTER_LOOKUP - { "fr_srcgrpmap", fr_srcgrpmap, fr_grpmapinit }, - { "fr_dstgrpmap", fr_dstgrpmap, fr_grpmapinit }, +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 - { "", NULL, NULL } + { { 0 }, + NULL, 0, 0, + 0, + 0, NULL, NULL } }; @@ -346,39 +426,41 @@ static ipfunc_resolve_t fr_availfuncs[] = { * 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 frpr_ipinit*() function rather than by + * 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 frpr_ah6 __P((fr_info_t *)); -static INLINE void frpr_esp6 __P((fr_info_t *)); -static INLINE void frpr_gre6 __P((fr_info_t *)); -static INLINE void frpr_udp6 __P((fr_info_t *)); -static INLINE void frpr_tcp6 __P((fr_info_t *)); -static INLINE void frpr_icmp6 __P((fr_info_t *)); -static INLINE int frpr_ipv6hdr __P((fr_info_t *)); -static INLINE void frpr_short6 __P((fr_info_t *, int)); -static INLINE int frpr_hopopts6 __P((fr_info_t *)); -static INLINE int frpr_mobility6 __P((fr_info_t *)); -static INLINE int frpr_routing6 __P((fr_info_t *)); -static INLINE int frpr_dstopts6 __P((fr_info_t *)); -static INLINE int frpr_fragment6 __P((fr_info_t *)); -static INLINE int frpr_ipv6exthdr __P((fr_info_t *, int, int)); - - -/* ------------------------------------------------------------------------ */ -/* Function: frpr_short6 */ +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)); + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_pr_short6 */ /* Returns: void */ -/* Parameters: fin(I) - pointer to packet information */ +/* 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 frpr_short() for more details. */ +/* for ipf_pr_short() for more details. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_short6(fin, xmin) -fr_info_t *fin; -int xmin; +static INLINE void +ipf_pr_short6(fin, xmin) + fr_info_t *fin; + int xmin; { if (fin->fin_dlen < xmin) @@ -387,8 +469,8 @@ int xmin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_ipv6hdr */ -/* Returns: int - 0 = IPv6 packet intact, -1 = packet lost */ +/* Function: ipf_pr_ipv6hdr */ +/* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ @@ -397,8 +479,9 @@ int xmin; /* analyzer may pullup or free the packet itself so we need to be vigiliant */ /* of that possibility arising. */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_ipv6hdr(fin) -fr_info_t *fin; +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; @@ -412,57 +495,68 @@ fr_info_t *fin; 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_id = (u_short)(ip6->ip6_flow & 0xffff); + 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_BAD|FI_SHORT))) { + while (go && !(fin->fin_flx & FI_SHORT)) { switch (p) { case IPPROTO_UDP : - frpr_udp6(fin); + ipf_pr_udp6(fin); go = 0; break; case IPPROTO_TCP : - frpr_tcp6(fin); + ipf_pr_tcp6(fin); go = 0; break; case IPPROTO_ICMPV6 : - frpr_icmp6(fin); + ipf_pr_icmp6(fin); go = 0; break; case IPPROTO_GRE : - frpr_gre6(fin); + ipf_pr_gre6(fin); go = 0; break; case IPPROTO_HOPOPTS : - p = frpr_hopopts6(fin); + p = ipf_pr_hopopts6(fin); break; case IPPROTO_MOBILITY : - p = frpr_mobility6(fin); + p = ipf_pr_mobility6(fin); break; case IPPROTO_DSTOPTS : - p = frpr_dstopts6(fin); + p = ipf_pr_dstopts6(fin); break; case IPPROTO_ROUTING : - p = frpr_routing6(fin); + p = ipf_pr_routing6(fin); break; case IPPROTO_AH : - p = frpr_ah6(fin); + p = ipf_pr_ah6(fin); break; case IPPROTO_ESP : - frpr_esp6(fin); + ipf_pr_esp6(fin); go = 0; break; @@ -480,7 +574,13 @@ fr_info_t *fin; break; case IPPROTO_FRAGMENT : - p = frpr_fragment6(fin); + 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; @@ -501,39 +601,61 @@ fr_info_t *fin; * header. */ if ((go != 0) && (p != IPPROTO_NONE) && - (frpr_pullup(fin, 0) == -1)) { + (ipf_pr_pullup(fin, 0) == -1)) { p = IPPROTO_NONE; - go = 0; + break; } } - fi->fi_p = p; /* - * Some of the above functions, like frpr_esp6(), can call fr_pullup + * 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 -1 if there is a problem with fr_pullup. + * expects us to return if there is a problem with ipf_pullup. */ - if (fin->fin_m == NULL) - return -1; + if (fin->fin_m == NULL) { + ipf_main_softc_t *softc = fin->fin_main_soft; - return 0; + 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; + LBUMPD(ipf_stats[fin->fin_out], fr_v6_badfrag); + LBUMP(ipf_stats[fin->fin_out].fr_v6_bad); + } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_ipv6exthdr */ -/* Returns: int - value of the next header or IPPROTO_NONE if error */ +/* 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 */ -/* ------------------------------------------------------------------------ */ -static INLINE int frpr_ipv6exthdr(fin, multiple, proto) -fr_info_t *fin; -int multiple, proto; +/* 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; @@ -543,11 +665,14 @@ int multiple, proto; /* 8 is default length of extension hdr */ if ((fin->fin_dlen - 8) < 0) { fin->fin_flx |= FI_SHORT; - return IPPROTO_NONE; + LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_short); + return NULL; } - if (frpr_pullup(fin, 8) == -1) - return IPPROTO_NONE; + 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) @@ -562,9 +687,21 @@ int multiple, proto; if (shift > fin->fin_dlen) { /* Nasty extension header length? */ fin->fin_flx |= FI_BAD; - return IPPROTO_NONE; + 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) { /* @@ -578,149 +715,196 @@ int multiple, proto; break; } - fin->fin_exthdr = fin->fin_dp; - fin->fin_dp = (char *)fin->fin_dp + shift; - fin->fin_dlen -= shift; - - return hdr->ip6e_nxt; + return hdr; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_hopopts6 */ +/* 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 frpr_hopopts6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_hopopts6(fin) + fr_info_t *fin; { - return frpr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); + struct ip6_ext *hdr; + + hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); + if (hdr == NULL) + return IPPROTO_NONE; + return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_mobility6 */ +/* 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 frpr_mobility6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_mobility6(fin) + fr_info_t *fin; { - return frpr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); + struct ip6_ext *hdr; + + hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); + if (hdr == NULL) + return IPPROTO_NONE; + return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_routing6 */ +/* 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 frpr_routing6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_routing6(fin) + fr_info_t *fin; { - struct ip6_ext *hdr; + struct ip6_routing *hdr; - if (frpr_ipv6exthdr(fin, 0, IPPROTO_ROUTING) == IPPROTO_NONE) + hdr = (struct ip6_routing *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_ROUTING); + if (hdr == NULL) return IPPROTO_NONE; - hdr = fin->fin_exthdr; - if ((hdr->ip6e_len & 1) != 0) { - /* - * The routing header data is made up of 128 bit IPv6 addresses - * which means it must be a multiple of 2 lots of 8 in length. - */ - fin->fin_flx |= FI_BAD; + switch (hdr->ip6r_type) + { + case 0 : /* - * Compensate for the changes made in frpr_ipv6exthdr() + * Nasty extension header length? */ - fin->fin_dlen += 8 + (hdr->ip6e_len << 3); - fin->fin_dp = hdr; - return IPPROTO_NONE; + 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; + LBUMPD(ipf_stats[fin->fin_out], fr_v6_rh_bad); + return IPPROTO_NONE; + } + break; + + default : + break; } - return hdr->ip6e_nxt; + return hdr->ip6r_nxt; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_fragment6 */ +/* 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.*/ /* */ -/* We don't know where the transport layer header (or whatever is next is), */ -/* as it could be behind destination options (amongst others). Because */ -/* there is no fragment cache, there is no knowledge about whether or not an*/ -/* upper layer header has been seen (or where it ends) and thus we are not */ -/* able to continue processing beyond this header with any confidence. */ -/* ------------------------------------------------------------------------ */ -static INLINE int frpr_fragment6(fin) -fr_info_t *fin; +/* 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; - int extoff; fin->fin_flx |= FI_FRAG; - if (frpr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT) == IPPROTO_NONE) - return IPPROTO_NONE; - - extoff = (char *)fin->fin_exthdr - (char *)fin->fin_dp; - - if (frpr_pullup(fin, sizeof(*frag)) == -1) + 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; + } - fin->fin_exthdr = (char *)fin->fin_dp + extoff; - frag = fin->fin_exthdr; - /* - * Fragment but no fragmentation info set? Bad packet... - */ - if (frag->ip6f_offlg == 0) { - fin->fin_flx |= FI_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; } + fin->fin_fraghdr = frag; + fin->fin_id = frag->ip6f_ident; fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK); - fin->fin_off <<= 3; if (fin->fin_off != 0) fin->fin_flx |= FI_FRAGBODY; - fin->fin_dp = (char *)fin->fin_dp + sizeof(*frag); - fin->fin_dlen -= sizeof(*frag); + /* + * Jumbograms aren't handled, so the max. length is 64k + */ + if ((fin->fin_off << 3) + fin->fin_dlen > 65535) + fin->fin_flx |= FI_BAD; + /* + * 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: frpr_dstopts6 */ +/* Function: ipf_pr_dstopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ -/* nextheader(I) - stores next header value */ /* */ /* IPv6 Only */ /* This is function checks pending destination options extension header */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_dstopts6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_dstopts6(fin) + fr_info_t *fin; { - return frpr_ipv6exthdr(fin, 1, IPPROTO_DSTOPTS); + 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: frpr_icmp6 */ +/* Function: ipf_pr_icmp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -728,14 +912,19 @@ fr_info_t *fin; /* This routine is mainly concerned with determining the minimum valid size */ /* for an ICMPv6 packet. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_icmp6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_icmp6(fin) + fr_info_t *fin; { int minicmpsz = sizeof(struct icmp6_hdr); struct icmp6_hdr *icmp6; - if (frpr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) + 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; @@ -744,12 +933,18 @@ fr_info_t *fin; 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 : @@ -760,10 +955,13 @@ fr_info_t *fin; break; if (M_LEN(fin->fin_m) < fin->fin_plen) { - if (fr_coalesce(fin) != 1) + 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 @@ -774,19 +972,25 @@ fr_info_t *fin; if (IP6_NEQ(&fin->fin_fi.fi_dst, (i6addr_t *)&ip6->ip6_src)) fin->fin_flx |= FI_BAD; - break; default : break; } } - frpr_short6(fin, minicmpsz); + 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: frpr_udp6 */ +/* Function: ipf_pr_udp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -794,24 +998,23 @@ fr_info_t *fin; /* Analyse the packet for IPv6/UDP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_udp6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_udp6(fin) + fr_info_t *fin; { - frpr_short6(fin, sizeof(struct udphdr)); - - if (frpr_udpcommon(fin) == 0) { + if (ipf_pr_udpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_UDP; - fr_checkv6sum(fin); + ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_tcp6 */ +/* Function: ipf_pr_tcp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -819,24 +1022,23 @@ fr_info_t *fin; /* Analyse the packet for IPv6/TCP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_tcp6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_tcp6(fin) + fr_info_t *fin; { - frpr_short6(fin, sizeof(struct tcphdr)); - - if (frpr_tcpcommon(fin) == 0) { + if (ipf_pr_tcpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_TCP; - fr_checkv6sum(fin); + ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_esp6 */ +/* Function: ipf_pr_esp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -847,19 +1049,23 @@ fr_info_t *fin; /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_esp6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_esp6(fin) + fr_info_t *fin; { - frpr_short6(fin, sizeof(grehdr_t)); + if ((fin->fin_off == 0) && (ipf_pr_pullup(fin, 8) == -1)) { + ipf_main_softc_t *softc = fin->fin_main_soft; - (void) frpr_pullup(fin, 8); + LBUMPD(ipf_stats[fin->fin_out], fr_v6_esp_pullup); + return; + } } /* ------------------------------------------------------------------------ */ -/* Function: frpr_ah6 */ -/* Returns: void */ +/* 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 */ @@ -867,37 +1073,51 @@ fr_info_t *fin; /* 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 frpr_ah6(fin) -fr_info_t *fin; +static INLINE int +ipf_pr_ah6(fin) + fr_info_t *fin; { authhdr_t *ah; - frpr_short6(fin, 12); + 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; - if (frpr_pullup(fin, sizeof(*ah)) == -1) + LBUMPD(ipf_stats[fin->fin_out], fr_v6_ah_bad); return IPPROTO_NONE; + } - ah = (authhdr_t *)fin->fin_dp; + 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: frpr_gre6 */ +/* Function: ipf_pr_gre6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_gre6(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_gre6(fin) + fr_info_t *fin; { grehdr_t *gre; - frpr_short6(fin, sizeof(grehdr_t)); + if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { + ipf_main_softc_t *softc = fin->fin_main_soft; - if (frpr_pullup(fin, sizeof(grehdr_t)) == -1) + LBUMPD(ipf_stats[fin->fin_out], fr_v6_gre_pullup); return; + } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) @@ -907,13 +1127,13 @@ fr_info_t *fin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_pullup */ +/* 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 fr_pullup to ensure there is the required amount of data, */ +/* 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 */ @@ -924,23 +1144,32 @@ fr_info_t *fin; /* is necessary to add those we can already assume to be pulled up (fin_dp */ /* - fin_ip) to what is passed through. */ /* ------------------------------------------------------------------------ */ -static INLINE int frpr_pullup(fin, plen) -fr_info_t *fin; -int plen; +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) { + if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) { #if defined(_KERNEL) - if (fr_pullup(fin->fin_m, fin, plen) == NULL) + 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 fr_pullup failing + * Fake ipf_pullup failing */ + fin->fin_reason = FRB_PULLUP; *fin->fin_mp = NULL; fin->fin_m = NULL; fin->fin_ip = NULL; @@ -953,7 +1182,7 @@ int plen; /* ------------------------------------------------------------------------ */ -/* Function: frpr_short */ +/* Function: ipf_pr_short */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ @@ -964,9 +1193,10 @@ int plen; /* 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 frpr_short(fin, xmin) -fr_info_t *fin; -int xmin; +static INLINE void +ipf_pr_short(fin, xmin) + fr_info_t *fin; + int xmin; { if (fin->fin_off == 0) { @@ -979,7 +1209,7 @@ int xmin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_icmp */ +/* Function: ipf_pr_icmp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -991,110 +1221,111 @@ int xmin; /* */ /* XXX - other ICMP sanity checks? */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_icmp(fin) -fr_info_t *fin; +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) { - frpr_short(fin, ICMPERR_ICMPHLEN); + LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_frag); return; } - if (frpr_pullup(fin, ICMPERR_ICMPHLEN) == -1) + if (ipf_pr_pullup(fin, ICMPERR_ICMPHLEN) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_pullup); return; + } - if (fin->fin_dlen > 1) { - icmp = fin->fin_dp; + icmp = fin->fin_dp; - fin->fin_data[0] = *(u_short *)icmp; + fin->fin_data[0] = *(u_short *)icmp; + fin->fin_data[1] = icmp->icmp_id; - if (fin->fin_dlen >= 6) /* ID field */ - 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; + } +#endif + 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; + } - switch (icmp->icmp_type) - { - case ICMP_ECHOREPLY : - case ICMP_ECHO : - /* Router discovery messaes - RFC 1256 */ - case ICMP_ROUTERADVERT : - case ICMP_ROUTERSOLICIT : - minicmpsz = ICMP_MINLEN; - break; /* - * type(1) + code(1) + cksum(2) + id(2) seq(2) + - * 3 * timestamp(3 * 4) + * ICMP error packets should not be generated for IP + * packets that are a fragment that isn't the first + * fragment. */ - case ICMP_TSTAMP : - case ICMP_TSTAMPREPLY : - minicmpsz = 20; - break; - /* - * type(1) + code(1) + cksum(2) + id(2) seq(2) + - * mask(4) - */ - case ICMP_MASKREQ : - case ICMP_MASKREPLY : - minicmpsz = 12; - break; + oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); + if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) + fin->fin_flx |= FI_BAD; + /* - * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) + * If the destination of this packet doesn't match the + * source of the original packet then this packet is + * not correct. */ - case ICMP_UNREACH : -#ifdef icmp_nextmtu - if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { - if (icmp->icmp_nextmtu < fr_icmpminfragmtu) - fin->fin_flx |= FI_BAD; - } -#endif - case ICMP_SOURCEQUENCH : - case ICMP_REDIRECT : - case ICMP_TIMXCEED : - case ICMP_PARAMPROB : - fin->fin_flx |= FI_ICMPERR; - if (fr_coalesce(fin) != 1) - 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; - - /* - * 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; - - /* - * 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; - break; - default : - break; - } + if (oip->ip_src.s_addr != fin->fin_daddr) + fin->fin_flx |= FI_BAD; + break; + default : + break; } - frpr_short(fin, minicmpsz); + ipf_pr_short(fin, minicmpsz); - if ((fin->fin_flx & FI_FRAG) == 0) - fr_checkv4sum(fin); + ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ -/* Function: frpr_tcpcommon */ +/* Function: ipf_pr_tcpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -1103,27 +1334,35 @@ fr_info_t *fin; /* 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 frpr_tcpcommon(fin) -fr_info_t *fin; +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) + if (fin->fin_off != 0) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_frag); return 0; + } - if (frpr_pullup(fin, sizeof(*tcp)) == -1) + if (ipf_pr_pullup(fin, sizeof(*tcp)) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return -1; - tcp = fin->fin_dp; + } + 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) + 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 @@ -1131,6 +1370,7 @@ fr_info_t *fin; */ tlen = TCP_OFF(tcp) << 2; if (tlen < sizeof(tcphdr_t)) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_small); fin->fin_flx |= FI_BAD; return 1; } @@ -1189,6 +1429,10 @@ fr_info_t *fin; fin->fin_flx |= FI_BAD; } } + 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 @@ -1198,11 +1442,14 @@ fr_info_t *fin; * 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)) + if (tlen == sizeof(tcphdr_t)) { return 0; + } - if (frpr_pullup(fin, tlen) == -1) + if (ipf_pr_pullup(fin, tlen) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return -1; + } #if 0 tcp = fin->fin_dp; @@ -1249,23 +1496,27 @@ fr_info_t *fin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_udpcommon */ +/* 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 frpr_udpcommon(fin) -fr_info_t *fin; +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 (frpr_pullup(fin, sizeof(*udp)) == -1) { + 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; } @@ -1280,49 +1531,47 @@ fr_info_t *fin; /* ------------------------------------------------------------------------ */ -/* Function: frpr_tcp */ +/* 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 frpr_tcp(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_tcp(fin) + fr_info_t *fin; { - frpr_short(fin, sizeof(tcphdr_t)); + ipf_pr_short(fin, sizeof(tcphdr_t)); - if (frpr_tcpcommon(fin) == 0) { - if ((fin->fin_flx & FI_FRAG) == 0) - fr_checkv4sum(fin); - } + if (ipf_pr_tcpcommon(fin) == 0) + ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ -/* Function: frpr_udp */ +/* 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 frpr_udp(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_udp(fin) + fr_info_t *fin; { - frpr_short(fin, sizeof(udphdr_t)); + ipf_pr_short(fin, sizeof(udphdr_t)); - if (frpr_udpcommon(fin) == 0) { - if ((fin->fin_flx & FI_FRAG) == 0) - fr_checkv4sum(fin); - } + if (ipf_pr_udpcommon(fin) == 0) + ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ -/* Function: frpr_esp */ +/* Function: ipf_pr_esp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -1332,78 +1581,107 @@ fr_info_t *fin; /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_esp(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_esp(fin) + fr_info_t *fin; { if (fin->fin_off == 0) { - frpr_short(fin, 8); - (void) frpr_pullup(fin, 8); - } + 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: frpr_ah */ -/* Returns: void */ +/* 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 void frpr_ah(fin) -fr_info_t *fin; +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; - frpr_short(fin, sizeof(*ah)); + fin->fin_flx |= FI_AH; + ipf_pr_short(fin, sizeof(*ah)); - if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) - return; + 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 (frpr_pullup(fin, sizeof(*ah)) == -1) - return; + 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; - frpr_short(fin, len); + 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: frpr_gre */ +/* Function: ipf_pr_gre */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ -static INLINE void frpr_gre(fin) -fr_info_t *fin; +static INLINE void +ipf_pr_gre(fin) + fr_info_t *fin; { + ipf_main_softc_t *softc = fin->fin_main_soft; grehdr_t *gre; - frpr_short(fin, sizeof(*gre)); + ipf_pr_short(fin, sizeof(grehdr_t)); - if (fin->fin_off != 0) + if (fin->fin_off != 0) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_frag); return; + } - if (frpr_pullup(fin, sizeof(*gre)) == -1) + if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { + LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_pullup); return; - - if (fin->fin_off == 0) { - gre = fin->fin_dp; - if (GRE_REV(gre->gr_flags) == 1) - fin->fin_data[0] = gre->gr_call; } + + gre = fin->fin_dp; + if (GRE_REV(gre->gr_flags) == 1) + fin->fin_data[0] = gre->gr_call; } /* ------------------------------------------------------------------------ */ -/* Function: frpr_ipv4hdr */ +/* Function: ipf_pr_ipv4hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -1411,8 +1689,9 @@ fr_info_t *fin; /* 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 frpr_ipv4hdr(fin) -fr_info_t *fin; +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; @@ -1428,16 +1707,14 @@ fr_info_t *fin; 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 = ip->ip_id; - off = ip->ip_off; + off = ntohs(ip->ip_off); /* Get both TTL and protocol */ fi->fi_p = ip->ip_p; fi->fi_ttl = ip->ip_ttl; -#if 0 - (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4)); -#endif /* Zero out bits not used in IPv6 address */ fi->fi_src.i6[1] = 0; @@ -1448,7 +1725,11 @@ fr_info_t *fin; 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 @@ -1463,12 +1744,12 @@ fr_info_t *fin; if (off != 0) { fin->fin_flx |= FI_FRAGBODY; off <<= 3; - if ((off + fin->fin_dlen > 65535) || + 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 + * offset cannot exceed 65535 (0xffff) as the * length of an IP packet is only 16 bits. * * Any fragment that isn't the last fragment @@ -1484,25 +1765,30 @@ fr_info_t *fin; /* * 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 : - frpr_udp(fin); + ipf_pr_udp(fin); break; case IPPROTO_TCP : - frpr_tcp(fin); + ipf_pr_tcp(fin); break; case IPPROTO_ICMP : - frpr_icmp(fin); - break; - case IPPROTO_AH : - frpr_ah(fin); + ipf_pr_icmp(fin); break; case IPPROTO_ESP : - frpr_esp(fin); + ipf_pr_esp(fin); break; case IPPROTO_GRE : - frpr_gre(fin); + ipf_pr_gre(fin); break; } @@ -1545,32 +1831,37 @@ fr_info_t *fin; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; + if ((opt == (u_char)op->ol_val) && (ol > 4)) { - optmsk |= op->ol_bit; - if (opt == IPOPT_SECURITY) { - const struct optlist *sp; - 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--; + u_32_t doi; + + switch (opt) + { + case IPOPT_SECURITY : + if (optmsk & op->ol_bit) { + fin->fin_flx |= FI_BAD; + } 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; + } else { + doi = ipf_checkcipso(fin, + s, ol); + secmsk = doi >> 16; + auth = doi & 0xffff; + } + break; } - break; + optmsk |= op->ol_bit; } + if (opt < op->ol_val) i -= mv; else @@ -1593,8 +1884,142 @@ fr_info_t *fin; /* ------------------------------------------------------------------------ */ -/* Function: fr_makefrip */ +/* 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; + 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; + 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; + continue; + } else if (tag == 1) { + if (*(t + 2) != 0) { + fin->fin_flx |= FI_BAD; + continue; + } + sensitivity = *(t + 3); + /* Category bitmap for categories 0-239 */ + + } else if (tag == 4) { + if (*(t + 2) != 0) { + fin->fin_flx |= FI_BAD; + continue; + } + sensitivity = *(t + 3); + /* Enumerated categories, 16bits each, upto 15 */ + + } else if (tag == 5) { + if (*(t + 2) != 0) { + fin->fin_flx |= FI_BAD; + 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; + 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 */ @@ -1604,15 +2029,15 @@ fr_info_t *fin; /* 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 fr_makefrip(hlen, ip, fin) -int hlen; -ip_t *ip; -fr_info_t *fin; +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_nat = NULL; - fin->fin_state = NULL; fin->fin_depth = 0; fin->fin_hlen = (u_short)hlen; fin->fin_ip = ip; @@ -1623,43 +2048,43 @@ fr_info_t *fin; v = fin->fin_v; if (v == 4) { - fin->fin_plen = ip->ip_len; + fin->fin_plen = ntohs(ip->ip_len); fin->fin_dlen = fin->fin_plen - hlen; - - frpr_ipv4hdr(fin); + 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; - if (frpr_ipv6hdr(fin) == -1) - return -1; + ipf_pr_ipv6hdr(fin); #endif } - if (fin->fin_ip == NULL) + if (fin->fin_ip == NULL) { + LBUMP(ipf_stats[fin->fin_out].fr_ip_freed); return -1; + } return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_portcheck */ +/* Function: ipf_portcheck */ /* Returns: int - 1 == port matched, 0 == port match failed */ /* Parameters: frp(I) - pointer to port check `expression' */ -/* pop(I) - pointer to port number to evaluate */ +/* 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 fr_portcheck(frp, pop) -frpcmp_t *frp; -u_short *pop; +static INLINE int +ipf_portcheck(frp, pop) + frpcmp_t *frp; + u_32_t pop; { - u_short tup, po; int err = 1; + u_32_t po; - tup = *pop; po = frp->frp_port; /* @@ -1668,39 +2093,39 @@ u_short *pop; switch (frp->frp_cmp) { case FR_EQUAL : - if (tup != po) /* EQUAL */ + if (pop != po) /* EQUAL */ err = 0; break; case FR_NEQUAL : - if (tup == po) /* NOTEQUAL */ + if (pop == po) /* NOTEQUAL */ err = 0; break; case FR_LESST : - if (tup >= po) /* LESSTHAN */ + if (pop >= po) /* LESSTHAN */ err = 0; break; case FR_GREATERT : - if (tup <= po) /* GREATERTHAN */ + if (pop <= po) /* GREATERTHAN */ err = 0; break; case FR_LESSTE : - if (tup > po) /* LT or EQ */ + if (pop > po) /* LT or EQ */ err = 0; break; case FR_GREATERTE : - if (tup < po) /* GT or EQ */ + if (pop < po) /* GT or EQ */ err = 0; break; case FR_OUTRANGE : - if (tup >= po && tup <= frp->frp_top) /* Out of range */ + if (pop >= po && pop <= frp->frp_top) /* Out of range */ err = 0; break; case FR_INRANGE : - if (tup <= po || tup >= frp->frp_top) /* In range */ + if (pop <= po || pop >= frp->frp_top) /* In range */ err = 0; break; case FR_INCRANGE : - if (tup < po || tup > frp->frp_top) /* Inclusive range */ + if (pop < po || pop > frp->frp_top) /* Inclusive range */ err = 0; break; default : @@ -1711,17 +2136,18 @@ u_short *pop; /* ------------------------------------------------------------------------ */ -/* Function: fr_tcpudpchk */ +/* Function: ipf_tcpudpchk */ /* Returns: int - 1 == protocol matched, 0 == check failed */ -/* Parameters: fin(I) - pointer to packet information */ +/* 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 fr_tcpudpchk(fin, ft) -fr_info_t *fin; -frtuc_t *ft; +int +ipf_tcpudpchk(fi, ft) + fr_ip_t *fi; + frtuc_t *ft; { int err = 1; @@ -1732,13 +2158,13 @@ frtuc_t *ft; * compare destination ports */ if (ft->ftu_dcmp) - err = fr_portcheck(&ft->ftu_dst, &fin->fin_dport); + err = ipf_portcheck(&ft->ftu_dst, fi->fi_ports[1]); /* * compare source ports */ if (err && ft->ftu_scmp) - err = fr_portcheck(&ft->ftu_src, &fin->fin_sport); + err = ipf_portcheck(&ft->ftu_src, fi->fi_ports[0]); /* * If we don't have all the TCP/UDP header, then how can we @@ -1746,15 +2172,15 @@ frtuc_t *ft; * TCP flags, then NO match. If not, then match (which should * satisfy the "short" class too). */ - if (err && (fin->fin_p == IPPROTO_TCP)) { - if (fin->fin_flx & FI_SHORT) + 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 != (fin->fin_tcpf & ft->ftu_tcpfm)) { - FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf, + 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; } @@ -1763,10 +2189,9 @@ frtuc_t *ft; } - /* ------------------------------------------------------------------------ */ -/* Function: fr_ipfcheck */ -/* Returns: int - 0 == match, 1 == no match */ +/* 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 */ @@ -1776,10 +2201,11 @@ frtuc_t *ft; /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */ /* this function. */ /* ------------------------------------------------------------------------ */ -static INLINE int fr_ipfcheck(fin, fr, portcmp) -fr_info_t *fin; -frentry_t *fr; -int portcmp; +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; @@ -1807,10 +2233,10 @@ int portcmp; * are present (if any) in this packet. */ lip++, lm++, ld++; - i |= ((*lip & *lm) != *ld); + i = ((*lip & *lm) != *ld); FR_DEBUG(("1. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); - if (i) + if (i != 0) return 1; lip++, lm++, ld++; @@ -1820,16 +2246,15 @@ int portcmp; /* * Check the source address. */ -#ifdef IPFILTER_LOOKUP if (fr->fr_satype == FRI_LOOKUP) { - i = (*fr->fr_srcfunc)(fr->fr_srcptr, fi->fi_v, lip); + 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 { -#endif i = ((*lip & *lm) != *ld); FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); @@ -1851,27 +2276,24 @@ int portcmp; lm += 3; ld += 3; } -#ifdef IPFILTER_LOOKUP } -#endif i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6; - if (i) + if (i != 0) return 1; /* * Check the destination address. */ lip++, lm++, ld++; -#ifdef IPFILTER_LOOKUP if (fr->fr_datype == FRI_LOOKUP) { - i = (*fr->fr_dstfunc)(fr->fr_dstptr, fi->fi_v, lip); + 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 { -#endif i = ((*lip & *lm) != *ld); FR_DEBUG(("3a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); @@ -1893,28 +2315,24 @@ int portcmp; lm += 3; ld += 3; } -#ifdef IPFILTER_LOOKUP } -#endif i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7; - if (i) + if (i != 0) return 1; /* * IP addresses matched. The next 32bits contains: * mast of old IP header security & authentication bits. */ lip++, lm++, ld++; - i |= ((*lip & *lm) != *ld); - FR_DEBUG(("4. %#08x & %#08x != %#08x\n", - *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 |= ((*lip & *lm) != *ld); - FR_DEBUG(("5. %#08x & %#08x != %#08x\n", - *lip, *lm, *ld)); + i |= (*ld - (*lip & *lm)); + FR_DEBUG(("5. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i == 0) { /* @@ -1922,7 +2340,7 @@ int portcmp; * looking for here... */ if (portcmp) { - if (!fr_tcpudpchk(fin, &fr->fr_tuc)) + if (!ipf_tcpudpchk(&fin->fin_fi, &fr->fr_tuc)) i = 1; } else { if (fr->fr_dcmp || fr->fr_scmp || @@ -1948,7 +2366,7 @@ int portcmp; /* ------------------------------------------------------------------------ */ -/* Function: fr_scanlist */ +/* 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 */ @@ -1963,10 +2381,12 @@ int portcmp; /* 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 fr_scanlist(fin, pass) -fr_info_t *fin; -u_32_t pass; +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; @@ -1997,7 +2417,7 @@ u_32_t pass; for (rulen = 0; fr; fr = fnext, rulen++) { fnext = fr->fr_next; if (skip != 0) { - FR_VERBOSE(("%d (%#x)\n", skip, fr->fr_flags)); + FR_VERBOSE(("SKIP %d (%#x)\n", skip, fr->fr_flags)); skip--; continue; } @@ -2027,27 +2447,29 @@ u_32_t pass; switch (fr->fr_type) { case FR_T_IPF : - case FR_T_IPF|FR_T_BUILTIN : - if (fr_ipfcheck(fin, fr, portcmp)) + 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|FR_T_BUILTIN : + case FR_T_BPFOPC_BUILTIN : { u_char *mc; + int wlen; if (*fin->fin_mp == NULL) continue; - if (fin->fin_v != fr->fr_v) + if (fin->fin_family != fr->fr_family) continue; mc = (u_char *)fin->fin_m; - if (!bpf_filter(fr->fr_data, mc, fin->fin_plen, 0)) + wlen = fin->fin_dlen + fin->fin_hlen; + if (!bpf_filter(fr->fr_data, mc, wlen, 0)) continue; break; } #endif - case FR_T_CALLFUNC|FR_T_BUILTIN : + case FR_T_CALLFUNC_BUILTIN : { frentry_t *f; @@ -2058,6 +2480,15 @@ u_32_t pass; 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; } @@ -2065,23 +2496,14 @@ u_32_t pass; if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { if (fin->fin_nattag == NULL) continue; - if (fr_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) + if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) continue; } - FR_VERBOSE(("=%s.%d *", fr->fr_group, rulen)); + FR_VERBOSE(("=%d/%d.%d *", fr->fr_grhead, fr->fr_group, rulen)); passt = fr->fr_flags; /* - * Allowing a rule with the "keep state" flag set to match - * packets that have been tagged "out of window" by the TCP - * state tracking is foolish as the attempt to add a new - * state entry to the table will fail. - */ - if ((passt & FR_KEEPSTATE) && (fin->fin_flx & FI_OOW)) - continue; - - /* * 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 @@ -2091,7 +2513,7 @@ u_32_t pass; frentry_t *frs; ATOMIC_INC64(fr->fr_hits); - if ((fr->fr_func != NULL) && + if ((fr->fr_func == NULL) || (fr->fr_func == (ipfunc_t)-1)) continue; @@ -2111,43 +2533,69 @@ u_32_t pass; * Just log this packet... */ if ((passt & FR_LOGMASK) == FR_LOG) { - if (ipflog(fin, passt) == -1) { + 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; } - ATOMIC_INCL(frstats[fin->fin_out].fr_skip); } - ATOMIC_INCL(frstats[fin->fin_out].fr_pkl); - fin->fin_flx |= FI_DONTCACHE; } #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)) + if (FR_ISSKIP(passt)) { skip = fr->fr_arg; - else if ((passt & FR_LOGMASK) != FR_LOG) + 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; - FR_DEBUG(("pass %#x\n", pass)); - ATOMIC_INC64(fr->fr_hits); - fin->fin_rule = rulen; - (void) strncpy(fin->fin_group, fr->fr_group, FR_GROUPLEN); - if (fr->fr_grp != NULL) { - fin->fin_fr = *fr->fr_grp; - passt = fr_scanlist(fin, pass); + + 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; - (void) strncpy(fin->fin_group, fr->fr_group, - FR_GROUPLEN); + 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 (passt & FR_QUICK) { + if (pass & FR_QUICK) { /* * Finally, if we've asked to track state for this * packet, set it up. Add state for "quick" rules @@ -2155,15 +2603,15 @@ u_32_t pass; * the rule to "not match" and keep on processing * filter rules. */ - if ((pass & FR_KEEPSTATE) && + if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) && !(fin->fin_flx & FI_STATE)) { int out = fin->fin_out; fin->fin_fr = fr; - if (fr_addstate(fin, NULL, 0) != NULL) { - ATOMIC_INCL(frstats[out].fr_ads); + if (ipf_state_add(softc, fin, NULL, 0) == 0) { + LBUMPD(ipf_stats[out], fr_ads); } else { - ATOMIC_INCL(frstats[out].fr_bads); + LBUMPD(ipf_stats[out], fr_bads); pass = passo; continue; } @@ -2177,7 +2625,7 @@ u_32_t pass; /* ------------------------------------------------------------------------ */ -/* Function: fr_acctpkt */ +/* 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) */ @@ -2186,32 +2634,29 @@ u_32_t pass; /* IP protocol version. */ /* */ /* N.B.: this function returns NULL to match the prototype used by other */ -/* functions called from the IPFilter "mainline" in fr_check(). */ +/* functions called from the IPFilter "mainline" in ipf_check(). */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_acctpkt(fin, passp) -fr_info_t *fin; -u_32_t *passp; +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; -#ifdef USE_INET6 - if (fin->fin_v == 6) - fr = ipacct6[fin->fin_out][fr_active]; - else -#endif - fr = ipacct[fin->fin_out][fr_active]; + 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 = fr_scanlist(fin, FR_NOMATCH); + pass = ipf_scanlist(fin, FR_NOMATCH); if (FR_ISACCOUNT(pass)) { - ATOMIC_INCL(frstats[0].fr_acct); + LBUMPD(ipf_stats[0], fr_acct); } fin->fin_fr = frsave; bcopy(group, fin->fin_group, FR_GROUPLEN); @@ -2222,7 +2667,7 @@ u_32_t *passp; /* ------------------------------------------------------------------------ */ -/* Function: fr_firewall */ +/* 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 */ @@ -2234,12 +2679,13 @@ u_32_t *passp; /* matching rule is found, take any appropriate actions as defined by the */ /* rule - except logging. */ /* ------------------------------------------------------------------------ */ -static frentry_t *fr_firewall(fin, passp) -fr_info_t *fin; -u_32_t *passp; +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; - fr_info_t *fc; u_32_t pass; int out; @@ -2247,56 +2693,28 @@ u_32_t *passp; pass = *passp; /* - * 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. + * This rule cache will only affect packets that are not being + * statefully filtered. */ - fc = &frcache[out][CACHE_HASH(fin)]; - READ_ENTER(&ipf_frcache); - if (!bcmp((char *)fin, (char *)fc, FI_CSIZE)) { - /* - * copy cached data so we can unlock the mutexes earlier. - */ - bcopy((char *)fc, (char *)fin, FI_COPYSIZE); - RWLOCK_EXIT(&ipf_frcache); - ATOMIC_INCL(frstats[out].fr_chit); - - if ((fr = fin->fin_fr) != NULL) { - ATOMIC_INC64(fr->fr_hits); - pass = fr->fr_flags; - } - } else { - RWLOCK_EXIT(&ipf_frcache); + fin->fin_fr = softc->ipf_rules[out][softc->ipf_active]; + if (fin->fin_fr != NULL) + pass = ipf_scanlist(fin, softc->ipf_pass); -#ifdef USE_INET6 - if (fin->fin_v == 6) - fin->fin_fr = ipfilter6[out][fr_active]; - else -#endif - fin->fin_fr = ipfilter[out][fr_active]; - if (fin->fin_fr != NULL) - pass = fr_scanlist(fin, fr_pass); - - if (((pass & FR_KEEPSTATE) == 0) && - ((fin->fin_flx & FI_DONTCACHE) == 0)) { - WRITE_ENTER(&ipf_frcache); - bcopy((char *)fin, (char *)fc, FI_COPYSIZE); - RWLOCK_EXIT(&ipf_frcache); - } - if ((pass & FR_NOMATCH)) { - ATOMIC_INCL(frstats[out].fr_nom); - } - fr = fin->fin_fr; + 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)) { - pass &= ~(FR_CMDMASK|FR_DUP|FR_RETICMP|FR_RETRST); + DT2(frb_ppsrate, fr_info_t *, fin, frentry_t *, fr); + pass &= ~(FR_CMDMASK|FR_RETICMP|FR_RETRST); pass |= FR_BLOCK; - ATOMIC_INCL(frstats[out].fr_ppshit); + LBUMPD(ipf_stats[out], fr_ppshit); + fin->fin_reason = FRB_PPSRATE; } /* @@ -2305,15 +2723,15 @@ u_32_t *passp; * we've dropped it already. */ if (FR_ISAUTH(pass)) { - if (fr_newauth(fin->fin_m, fin) != 0) { -#ifdef _KERNEL + if (ipf_auth_new(fin->fin_m, fin) != 0) { + DT1(frb_authnew, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; -#else - ; -#endif + fin->fin_reason = FRB_AUTHNEW; fin->fin_error = 0; - } else + } else { + IPFERROR(1); fin->fin_error = ENOSPC; + } } if ((fr != NULL) && (fr->fr_func != NULL) && @@ -2327,8 +2745,7 @@ u_32_t *passp; * is treated as "not a pass", hence the packet is blocked. */ if (FR_ISPREAUTH(pass)) { - if ((fin->fin_fr = ipauth) != NULL) - pass = fr_scanlist(fin, fr_pass); + pass = ipf_auth_pre_scanlist(softc, fin, pass); } /* @@ -2337,27 +2754,25 @@ u_32_t *passp; */ if ((pass & (FR_KEEPFRAG|FR_KEEPSTATE)) == FR_KEEPFRAG) { if (fin->fin_flx & FI_FRAG) { - if (fr_newfrag(fin, pass) == -1) { - ATOMIC_INCL(frstats[out].fr_bnfr); + if (ipf_frag_new(softc, fin, pass) == -1) { + LBUMP(ipf_stats[out].fr_bnfr); } else { - ATOMIC_INCL(frstats[out].fr_nfr); + LBUMP(ipf_stats[out].fr_nfr); } } else { - ATOMIC_INCL(frstats[out].fr_cfr); + LBUMP(ipf_stats[out].fr_cfr); } } fr = fin->fin_fr; - - if (passp != NULL) - *passp = pass; + *passp = pass; return fr; } /* ------------------------------------------------------------------------ */ -/* Function: fr_check */ +/* Function: ipf_check */ /* Returns: int - 0 == packet allowed through, */ /* User space: */ /* -1 == packet blocked */ @@ -2375,7 +2790,7 @@ u_32_t *passp; /* qpi(I) - pointer to STREAMS queue information for this */ /* interface & direction. */ /* */ -/* fr_check() is the master function for all IPFilter packet processing. */ +/* 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 */ @@ -2386,31 +2801,34 @@ u_32_t *passp; /* freed. Packets passed may be returned with the pointer pointed to by */ /* by "mp" changed to a new buffer. */ /* ------------------------------------------------------------------------ */ -int fr_check(ip, hlen, ifp, out +int +ipf_check(ctx, ip, hlen, ifp, out #if defined(_KERNEL) && defined(MENTAT) -, qif, mp) -void *qif; + , qif, mp) + void *qif; #else -, mp) + , mp) #endif -mb_t **mp; -ip_t *ip; -int hlen; -void *ifp; -int out; + mb_t **mp; + ip_t *ip; + int hlen; + void *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 = fr_pass; + 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 fr_check() deals with making sure that what goes + * 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 @@ -2420,7 +2838,7 @@ int out; # ifdef MENTAT qpktinfo_t *qpi = qif; -# if !defined(_INET_IP_STACK_H) +# ifdef __sparc if ((u_int)ip & 0x3) return 2; # endif @@ -2428,18 +2846,17 @@ int out; SPL_INT(s); # endif - READ_ENTER(&ipf_global); - - if (fr_running <= 0) { - RWLOCK_EXIT(&ipf_global); + if (softc->ipf_running <= 0) { return 0; } bzero((char *)fin, sizeof(*fin)); # ifdef MENTAT - if (qpi->qpi_flags & QF_GROUP) - fin->fin_flx |= FI_MBCAST; + 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; @@ -2467,7 +2884,8 @@ int out; */ m->m_flags &= ~M_CANFASTFWD; # endif /* M_CANFASTFWD */ -# ifdef CSUM_DELAY_DATA +# if defined(CSUM_DELAY_DATA) && (!defined(__FreeBSD_version) || \ + (__FreeBSD_version < 501108)) /* * disable delayed checksums. */ @@ -2478,10 +2896,20 @@ int out; # endif /* CSUM_DELAY_DATA */ # endif /* MENTAT */ #else - READ_ENTER(&ipf_global); - 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; @@ -2493,6 +2921,7 @@ int out; 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 *); @@ -2500,27 +2929,29 @@ int out; #ifdef USE_INET6 if (v == 6) { - ATOMIC_INCL(frstats[out].fr_ipv6); + 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 { -#if ((defined(OpenBSD) && (OpenBSD >= 200311)) || (__FreeBSD_version >= 1000019)) && defined(_KERNEL) - ip->ip_len = ntohs(ip->ip_len); - ip->ip_off = ntohs(ip->ip_off); -#endif + fin->fin_family = AF_INET; } - if (fr_makefrip(hlen, ip, fin) == -1) { + 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; } @@ -2533,21 +2964,19 @@ int out; if (!out) { if (v == 4) { -#ifdef _KERNEL - if (fr_chksrc && !fr_verifysrc(fin)) { - ATOMIC_INCL(frstats[0].fr_badsrc); + if (softc->ipf_chksrc && !ipf_verifysrc(fin)) { + LBUMPD(ipf_stats[0], fr_v4_badsrc); fin->fin_flx |= FI_BADSRC; } -#endif - if (fin->fin_ip->ip_ttl < fr_minttl) { - ATOMIC_INCL(frstats[0].fr_badttl); + 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 < fr_minttl) { - ATOMIC_INCL(frstats[0].fr_badttl); + if (((ip6_t *)ip)->ip6_hlim < softc->ipf_minttl) { + LBUMPD(ipf_stats[0], fr_v6_badttl); fin->fin_flx |= FI_LOWTTL; } } @@ -2555,120 +2984,135 @@ int out; } if (fin->fin_flx & FI_SHORT) { - ATOMIC_INCL(frstats[out].fr_short); + LBUMPD(ipf_stats[out], fr_short); } - READ_ENTER(&ipf_mutex); + READ_ENTER(&softc->ipf_mutex); - /* - * Check auth now. This, combined with the check below to see if apass - * is 0 is to ensure that we don't count the packet twice, which can - * otherwise occur when we reprocess it. As it is, we only count it - * after it has no auth. table matchup. This also stops NAT from - * occuring until after the packet has been auth'd. - */ - fr = fr_checkauth(fin, &pass); if (!out) { - if (fr_checknatin(fin, &pass) == -1) { - goto filterdone; + 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; } } - if (!out) - (void) fr_acctpkt(fin, NULL); + /* + * 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|FI_BAD)) == FI_FRAG) { - fr = fr_knownfrag(fin, &pass); - /* - * Reset the keep state flag here so that we don't - * try and add a new state entry because of it, leading - * to a blocked packet because the add will fail. - */ - if (fr != NULL) - pass &= ~FR_KEEPSTATE; - } + if ((fin->fin_flx & FI_FRAG) != 0) + fr = ipf_frag_known(fin, &pass); + if (fr == NULL) - fr = fr_checkstate(fin, &pass); + fr = ipf_state_check(fin, &pass); } if ((pass & FR_NOMATCH) || (fr == NULL)) - fr = fr_firewall(fin, &pass); + fr = ipf_firewall(fin, &pass); /* * If we've asked to track state for this packet, set it up. - * Here rather than fr_firewall because fr_checkauth may decide + * 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 (fr_addstate(fin, NULL, 0) != NULL) { - ATOMIC_INCL(frstats[out].fr_ads); + if (ipf_state_add(softc, fin, NULL, 0) == 0) { + LBUMP(ipf_stats[out].fr_ads); } else { - ATOMIC_INCL(frstats[out].fr_bads); + 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) fr_acctpkt(fin, NULL); + (void) ipf_acctpkt(fin, NULL); - if (fr_checknatout(fin, &pass) == -1) { - ; - } else if ((fr_update_ipid != 0) && (v == 4)) { - if (fr_updateipid(fin) == -1) { - ATOMIC_INCL(frstats[1].fr_ipud); - pass &= ~FR_CMDMASK; - pass |= FR_BLOCK; - } else { - ATOMIC_INCL(frstats[0].fr_ipud); + 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 ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { - (void) fr_dolog(fin, &pass); + 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 fr_checkstate + * 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. */ - if (fin->fin_state != NULL) { - fr_statederef((ipstate_t **)&fin->fin_state); - fin->fin_flx ^= FI_STATE; - } - - if (fin->fin_nat != NULL) { - if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) { - WRITE_ENTER(&ipf_nat); - nat_delete((nat_t *)fin->fin_nat, NL_DESTROY); - RWLOCK_EXIT(&ipf_nat); - fin->fin_nat = NULL; - } else { - fr_natderef((nat_t **)&fin->fin_nat); - } - } + fin->fin_flx &= ~FI_STATE; +#if defined(FASTROUTE_RECURSION) /* - * Up the reference on fr_lock and exit ipf_mutex. fr_fastroute - * only frees up the lock on ipf_global and the generation of a - * packet below could cause a recursive call into IPFilter. - * Hang onto the filter rule just in case someone decides to remove - * or flush it in the meantime. + * 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); @@ -2676,16 +3120,17 @@ filterdone: MUTEX_EXIT(&fr->fr_lock); } - RWLOCK_EXIT(&ipf_mutex); + 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. + * 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) { @@ -2695,13 +3140,14 @@ filterdone: dst = 1; else dst = 0; - (void) fr_send_icmp_err(ICMP_UNREACH, fin, dst); - ATOMIC_INCL(frstats[0].fr_ret); + (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) || - (fr_send_reset(fin) == 0)) { - ATOMIC_INCL(frstats[1].fr_ret); + (ipf_send_reset(fin) == 0)) { + LBUMP(ipf_stats[1].fr_ret); } } @@ -2710,61 +3156,81 @@ filterdone: * 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) + 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 (* BSD ONLY *). + * we are dropping it. */ if (fr != NULL) { frdest_t *fdp; - fdp = &fr->fr_tifs[fin->fin_rev]; + /* + * 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 destioation interface defined + * For fastroute rule, no destination interface defined * so pass NULL as the frdest_t parameter */ - (void) fr_fastroute(fin->fin_m, mp, fin, NULL); + (void) ipf_fastroute(fin->fin_m, mp, fin, NULL); m = *mp = NULL; - } else if ((fdp->fd_ifp != NULL) && - (fdp->fd_ifp != (struct ifnet *)-1)) { + } else if ((fdp != NULL) && (fdp->fd_ptr != NULL) && + (fdp->fd_ptr != (struct ifnet *)-1)) { /* this is for to rules: */ - (void) fr_fastroute(fin->fin_m, mp, fin, fdp); + ipf_fastroute(fin->fin_m, mp, fin, fdp); m = *mp = NULL; } - /* - * Generate a duplicated packet. - */ - if ((pass & FR_DUP) != 0) { - mc = M_DUPLICATE(fin->fin_m); - if (mc != NULL) - (void) fr_fastroute(mc, &mc, fin, &fr->fr_dif); - } - - (void) fr_derefrule(&fr); +#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)) { - ATOMIC_INCL(frstats[out].fr_block); + LBUMP(ipf_stats[out].fr_block); if (*mp != NULL) { +#ifdef _KERNEL FREE_MB_T(*mp); +#endif m = *mp = NULL; } } else { - ATOMIC_INCL(frstats[out].fr_pass); + LBUMP(ipf_stats[out].fr_pass); #if defined(_KERNEL) && defined(__sgi) if ((fin->fin_hbuf != NULL) && (mtod(fin->fin_m, struct ip *) != fin->fin_ip)) { @@ -2774,21 +3240,20 @@ finished: } SPL_X(s); - RWLOCK_EXIT(&ipf_global); #ifdef _KERNEL -# if (defined(OpenBSD) && (OpenBSD >= 200311)) || (__FreeBSD_version >= 1000019) - if (FR_ISPASS(pass) && (v == 4)) { - ip = fin->fin_ip; - ip->ip_len = ntohs(ip->ip_len); - ip->ip_off = ntohs(ip->ip_off); - } -# endif - return (FR_ISPASS(pass)) ? 0 : fin->fin_error; + 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_NOMATCH) != 0) - return 1; + /*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) @@ -2821,7 +3286,7 @@ finished: #ifdef IPFILTER_LOG /* ------------------------------------------------------------------------ */ -/* Function: fr_dolog */ +/* 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) */ @@ -2829,43 +3294,47 @@ finished: /* 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 *fr_dolog(fin, passp) -fr_info_t *fin; -u_32_t *passp; +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 ((fr_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { + if ((softc->ipf_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { pass |= FF_LOGNOMATCH; - ATOMIC_INCL(frstats[out].fr_npkl); + LBUMPD(ipf_stats[out], fr_npkl); goto logit; + } else if (((pass & FR_LOGMASK) == FR_LOGP) || - (FR_ISPASS(pass) && (fr_flags & FF_LOGPASS))) { + (FR_ISPASS(pass) && (softc->ipf_flags & FF_LOGPASS))) { if ((pass & FR_LOGMASK) != FR_LOGP) pass |= FF_LOGPASS; - ATOMIC_INCL(frstats[out].fr_ppkl); + LBUMPD(ipf_stats[out], fr_ppkl); goto logit; + } else if (((pass & FR_LOGMASK) == FR_LOGB) || - (FR_ISBLOCK(pass) && (fr_flags & FF_LOGBLOCK))) { + (FR_ISBLOCK(pass) && (softc->ipf_flags & FF_LOGBLOCK))) { if ((pass & FR_LOGMASK) != FR_LOGB) pass |= FF_LOGBLOCK; - ATOMIC_INCL(frstats[out].fr_bpkl); -logit: - if (ipflog(fin, pass) == -1) { - ATOMIC_INCL(frstats[out].fr_skip); + 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)) { + 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; @@ -2886,9 +3355,10 @@ logit: /* */ /* N.B.: addr should be 16bit aligned. */ /* ------------------------------------------------------------------------ */ -u_short ipf_cksum(addr, len) -u_short *addr; -int len; +u_short +ipf_cksum(addr, len) + u_short *addr; + int len; { u_32_t sum = 0; @@ -2911,11 +3381,10 @@ int len; /* ------------------------------------------------------------------------ */ /* Function: fr_cksum */ /* Returns: u_short - layer 4 checksum */ -/* Parameters: m(I ) - pointer to buffer holding packet */ +/* 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 */ -/* l3len(I) - length of layer 4 data plus layer 3 header */ /* */ /* Calculates the TCP checksum for the packet held in "m", using the data */ /* in the IP header "ip" to seed it. */ @@ -2924,31 +3393,31 @@ int len; /* and the TCP header. We also assume that data blocks aren't allocated in */ /* odd sizes. */ /* */ -/* For IPv6, l3len excludes extension header size. */ -/* */ -/* Expects ip_len to be in host byte order when called. */ +/* Expects ip_len and ip_off to be in network byte order when called. */ /* ------------------------------------------------------------------------ */ -u_short fr_cksum(m, ip, l4proto, l4hdr, l3len) -mb_t *m; -ip_t *ip; -int l4proto, l3len; -void *l4hdr; +u_short +fr_cksum(fin, ip, l4proto, l4hdr) + fr_info_t *fin; + ip_t *ip; + int l4proto; + void *l4hdr; { - u_short *sp, slen, sumsave, l4hlen, *csump; + 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; - l4hlen = 0; sp = NULL; slen = 0; hlen = 0; sum = 0; + sum = htons((u_short)l4proto); /* * Add up IP Header portion */ @@ -2956,9 +3425,7 @@ void *l4hdr; if (IP_V(ip) == 4) { #endif hlen = IP_HL(ip) << 2; - slen = l3len - hlen; - sum = htons((u_short)l4proto); - sum += htons(slen); + off = hlen; sp = (u_short *)&ip->ip_src; sum += *sp++; /* ip_src */ sum += *sp++; @@ -2968,9 +3435,7 @@ void *l4hdr; } else if (IP_V(ip) == 6) { ip6 = (ip6_t *)ip; hlen = sizeof(*ip6); - slen = l3len - hlen; - sum = htons((u_short)l4proto); - sum += htons(slen); + off = ((char *)fin->fin_dp - (char *)fin->fin_ip); sp = (u_short *)&ip6->ip6_src; sum += *sp++; /* ip6_src */ sum += *sp++; @@ -2980,6 +3445,7 @@ void *l4hdr; sum += *sp++; sum += *sp++; sum += *sp++; + /* This needs to be routing header aware. */ sum += *sp++; /* ip6_dst */ sum += *sp++; sum += *sp++; @@ -2988,25 +3454,31 @@ void *l4hdr; sum += *sp++; sum += *sp++; sum += *sp++; + } else { + return 0xffff; } #endif + slen = fin->fin_plen - off; + sum += htons(slen); switch (l4proto) { case IPPROTO_UDP : csump = &((udphdr_t *)l4hdr)->uh_sum; - l4hlen = sizeof(udphdr_t); break; case IPPROTO_TCP : csump = &((tcphdr_t *)l4hdr)->th_sum; - l4hlen = sizeof(tcphdr_t); break; case IPPROTO_ICMP : csump = &((icmphdr_t *)l4hdr)->icmp_cksum; - l4hlen = 4; - sum = 0; + 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; } @@ -3016,298 +3488,18 @@ void *l4hdr; *csump = 0; } - l4hlen = l4hlen; /* LINT */ - -#ifdef _KERNEL -# ifdef MENTAT - { - void *rp = m->b_rptr; - - if ((unsigned char *)ip > m->b_rptr && (unsigned char *)ip < m->b_wptr) - m->b_rptr = (u_char *)ip; - sum2 = ip_cksum(m, hlen, sum); /* hlen == offset */ - m->b_rptr = rp; - sum2 = (u_short)(~sum2 & 0xffff); - } -# else /* MENTAT */ -# if defined(BSD) || defined(sun) -# if BSD >= 199103 - m->m_data += hlen; -# else - m->m_off += hlen; -# endif - m->m_len -= hlen; - sum2 = in_cksum(m, slen); - m->m_len += hlen; -# if BSD >= 199103 - m->m_data -= hlen; -# else - m->m_off -= hlen; -# endif - /* - * 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; -# else /* defined(BSD) || defined(sun) */ -{ - union { - u_char c[2]; - u_short s; - } bytes; - u_short len = ip->ip_len; -# if defined(__sgi) - int add; -# endif - - /* - * Add up IP Header portion - */ - if (sp != (u_short *)l4hdr) - sp = (u_short *)l4hdr; - - switch (l4proto) - { - case IPPROTO_UDP : - sum += *sp++; /* sport */ - sum += *sp++; /* dport */ - sum += *sp++; /* udp length */ - sum += *sp++; /* checksum */ - break; - - case IPPROTO_TCP : - sum += *sp++; /* sport */ - sum += *sp++; /* dport */ - sum += *sp++; /* seq */ - sum += *sp++; - sum += *sp++; /* ack */ - sum += *sp++; - sum += *sp++; /* off */ - sum += *sp++; /* win */ - sum += *sp++; /* checksum */ - sum += *sp++; /* urp */ - break; - case IPPROTO_ICMP : - sum = *sp++; /* type/code */ - sum += *sp++; /* checksum */ - break; - } - -# ifdef __sgi - /* - * In case we had to copy the IP & TCP header out of mbufs, - * skip over the mbuf bits which are the header - */ - if ((char *)ip != mtod(m, char *)) { - hlen = (char *)sp - (char *)ip; - while (hlen) { - add = MIN(hlen, m->m_len); - sp = (u_short *)(mtod(m, caddr_t) + add); - hlen -= add; - if (add == m->m_len) { - m = m->m_next; - if (!hlen) { - if (!m) - break; - sp = mtod(m, u_short *); - } - PANIC((!m),("fr_cksum(1): not enough data")); - } - } - } -# endif - - len -= (l4hlen + hlen); - if (len <= 0) - goto nodata; - - while (len > 1) { - if (((char *)sp - mtod(m, char *)) >= m->m_len) { - m = m->m_next; - PANIC((!m),("fr_cksum(2): not enough data")); - sp = mtod(m, u_short *); - } - if (((char *)(sp + 1) - mtod(m, char *)) > m->m_len) { - bytes.c[0] = *(u_char *)sp; - m = m->m_next; - PANIC((!m),("fr_cksum(3): not enough data")); - sp = mtod(m, u_short *); - bytes.c[1] = *(u_char *)sp; - sum += bytes.s; - sp = (u_short *)((u_char *)sp + 1); - } - if ((u_long)sp & 1) { - bcopy((char *)sp++, (char *)&bytes.s, sizeof(bytes.s)); - sum += bytes.s; - } else - sum += *sp++; - len -= 2; - } - - if (len != 0) - sum += ntohs(*(u_char *)sp << 8); -nodata: - while (sum > 0xffff) - sum = (sum & 0xffff) + (sum >> 16); - sum2 = (u_short)(~sum & 0xffff); -} -# endif /* defined(BSD) || defined(sun) */ -# endif /* MENTAT */ -#else /* _KERNEL */ - /* - * Add up IP Header portion - */ - if (sp != (u_short *)l4hdr) - sp = (u_short *)l4hdr; - - 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); -#endif /* _KERNEL */ + sum2 = ipf_pcksum(fin, off, sum); if (csump != NULL) *csump = sumsave; return sum2; } -#if defined(_KERNEL) && ( ((BSD < 199103) && !defined(MENTAT)) || \ - defined(__sgi) ) && !defined(linux) && !defined(_AIX51) -/* - * Copyright (c) 1982, 1986, 1988, 1991, 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. 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. - * - * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 - * $Id: fil.c,v 2.243.2.125 2007/10/10 09:27:20 darrenr Exp $ - */ -/* - * Copy data from an mbuf chain starting "off" bytes from the beginning, - * continuing for "len" bytes, into the indicated buffer. - */ -void -m_copydata(m, off, len, cp) - mb_t *m; - int off; - int len; - caddr_t cp; -{ - unsigned count; - - if (off < 0 || len < 0) - panic("m_copydata"); - while (off > 0) { - if (m == 0) - panic("m_copydata"); - if (off < m->m_len) - break; - off -= m->m_len; - m = m->m_next; - } - while (len > 0) { - if (m == 0) - panic("m_copydata"); - count = MIN(m->m_len - off, len); - bcopy(mtod(m, caddr_t) + off, cp, count); - len -= count; - cp += count; - off = 0; - m = m->m_next; - } -} - - -/* - * Copy data from a buffer back into the indicated mbuf chain, - * starting "off" bytes from the beginning, extending the mbuf - * chain if necessary. - */ -void -m_copyback(m0, off, len, cp) - struct mbuf *m0; - int off; - int len; - caddr_t cp; -{ - int mlen; - struct mbuf *m = m0, *n; - int totlen = 0; - - if (m0 == 0) - return; - while (off > (mlen = m->m_len)) { - off -= mlen; - totlen += mlen; - if (m->m_next == 0) { - n = m_getclr(M_DONTWAIT, m->m_type); - if (n == 0) - goto out; - n->m_len = min(MLEN, len + off); - m->m_next = n; - } - m = m->m_next; - } - while (len > 0) { - mlen = min(m->m_len - off, len); - bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen); - cp += mlen; - len -= mlen; - mlen += off; - off = 0; - totlen += mlen; - if (len == 0) - break; - if (m->m_next == 0) { - n = m_get(M_DONTWAIT, m->m_type); - if (n == 0) - break; - n->m_len = min(MLEN, len); - m->m_next = n; - } - m = m->m_next; - } -out: -#if 0 - if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) - m->m_pkthdr.len = totlen; -#endif - return; -} -#endif /* (_KERNEL) && ( ((BSD < 199103) && !MENTAT) || __sgi) */ - - /* ------------------------------------------------------------------------ */ -/* Function: fr_findgroup */ +/* Function: ipf_findgroup */ /* Returns: frgroup_t * - NULL = group not found, else pointer to group */ -/* Parameters: group(I) - group name to search for */ +/* 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 */ @@ -3316,11 +3508,13 @@ out: /* */ /* Search amongst the defined groups for a particular group number. */ /* ------------------------------------------------------------------------ */ -frgroup_t *fr_findgroup(group, unit, set, fgpp) -char *group; -minor_t unit; -int set; -frgroup_t ***fgpp; +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; @@ -3328,7 +3522,7 @@ frgroup_t ***fgpp; * Which list of groups to search in is dependent on which list of * rules are being operated on. */ - fgp = &ipfgroups[unit][set]; + fgp = &softc->ipf_groups[unit][set]; while ((fg = *fgp) != NULL) { if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0) @@ -3343,10 +3537,11 @@ frgroup_t ***fgpp; /* ------------------------------------------------------------------------ */ -/* Function: fr_addgroup */ +/* Function: ipf_group_add */ /* Returns: frgroup_t * - NULL == did not create group, */ /* != NULL == pointer to the group */ -/* Parameters: num(I) - group number to add */ +/* 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 */ @@ -3356,12 +3551,14 @@ frgroup_t ***fgpp; /* Add a new group head, or if it already exists, increase the reference */ /* count to it. */ /* ------------------------------------------------------------------------ */ -frgroup_t *fr_addgroup(group, head, flags, unit, set) -char *group; -void *head; -u_32_t flags; -minor_t unit; -int set; +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; @@ -3375,8 +3572,10 @@ int set; fgp = NULL; gflags = flags & FR_INOUT; - fg = fr_findgroup(group, unit, set, &fgp); + 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) @@ -3384,14 +3583,16 @@ int set; 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, FR_GROUPLEN); + 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; @@ -3399,38 +3600,82 @@ int set; /* ------------------------------------------------------------------------ */ -/* Function: fr_delgroup */ -/* Returns: Nil */ -/* Parameters: group(I) - group name to delete */ -/* unit(I) - device to which this group belongs */ -/* set(I) - which set of rules (inactive/inactive) this is */ +/* 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 */ /* */ -/* Attempt to delete a group head. */ -/* Only do this when its reference count reaches 0. */ +/* 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 fr_delgroup(group, unit, set) -char *group; -minor_t unit; -int set; +void +ipf_group_del(softc, group, fr) + ipf_main_softc_t *softc; + frgroup_t *group; + frentry_t *fr; { - frgroup_t *fg, **fgp; - fg = fr_findgroup(group, unit, set, &fgp); - if (fg == NULL) - return; + 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); +} + - fg->fg_ref--; - if (fg->fg_ref == 0) { - *fgp = fg->fg_next; - KFREE(fg); +/* ------------------------------------------------------------------------ */ +/* 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: fr_getrulen */ +/* 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 */ @@ -3439,18 +3684,20 @@ int set; /* 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 *fr_getrulen(unit, group, n) -int unit; -char *group; -u_32_t n; +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 = fr_findgroup(group, unit, fr_active, NULL); + fg = ipf_findgroup(softc, group, unit, softc->ipf_active, NULL); if (fg == NULL) return NULL; - for (fr = fg->fg_head; fr && n; fr = fr->fr_next, n--) + for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--) ; if (n != 0) return NULL; @@ -3459,41 +3706,9 @@ u_32_t n; /* ------------------------------------------------------------------------ */ -/* Function: fr_rulen */ -/* Returns: int - >= 0 - rule number, -1 == search failed */ -/* Parameters: unit(I) - device for which to count the rule's number */ -/* fr(I) - pointer to rule to match */ -/* */ -/* Return the number for a rule on a specific filtering device. */ -/* ------------------------------------------------------------------------ */ -int fr_rulen(unit, fr) -int unit; -frentry_t *fr; -{ - frentry_t *fh; - frgroup_t *fg; - u_32_t n = 0; - - if (fr == NULL) - return -1; - fg = fr_findgroup(fr->fr_group, unit, fr_active, NULL); - if (fg == NULL) - return -1; - for (fh = fg->fg_head; fh; n++, fh = fh->fr_next) - if (fh == fr) - break; - if (fh == NULL) - return -1; - return n; -} - - -/* ------------------------------------------------------------------------ */ -/* Function: frflushlist */ +/* Function: ipf_flushlist */ /* Returns: int - >= 0 - number of flushed rules */ -/* Parameters: set(I) - which set of rules (inactive/inactive) this is */ -/* unit(I) - device for which to flush rules */ -/* flags(I) - which set of rules to flush */ +/* 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 */ @@ -3507,11 +3722,11 @@ frentry_t *fr; /* */ /* NOTE: Rules not loaded from user space cannot be flushed. */ /* ------------------------------------------------------------------------ */ -static int frflushlist(set, unit, nfreedp, listp) -int set; -minor_t unit; -int *nfreedp; -frentry_t **listp; +static int +ipf_flushlist(softc, nfreedp, listp) + ipf_main_softc_t *softc; + int *nfreedp; + frentry_t **listp; { int freed = 0; frentry_t *fp; @@ -3523,18 +3738,27 @@ frentry_t **listp; continue; } *listp = fp->fr_next; - if (fp->fr_grp != NULL) { - (void) frflushlist(set, unit, nfreedp, fp->fr_grp); + 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_grhead != NULL) { - fr_delgroup(fp->fr_grhead, unit, set); - *fp->fr_grhead = '\0'; + if (fp->fr_icmpgrp != NULL) { + freed += ipf_group_flush(softc, fp->fr_icmpgrp); + fp->fr_names[fp->fr_icmphead] = '\0'; } - ASSERT(fp->fr_ref > 0); + if (fp->fr_srctrack.ht_max_nodes) + ipf_rb_ht_flush(&fp->fr_srctrack); + fp->fr_next = NULL; - if (fr_derefrule(&fp) == 0) + + ASSERT(fp->fr_ref > 0); + if (ipf_derefrule(softc, &fp) == 0) freed++; } *nfreedp += freed; @@ -3543,61 +3767,47 @@ frentry_t **listp; /* ------------------------------------------------------------------------ */ -/* Function: frflush */ +/* Function: ipf_flush */ /* Returns: int - >= 0 - number of flushed rules */ -/* Parameters: unit(I) - device for which to flush 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 frflush(unit, proto, flags) -minor_t unit; -int proto, flags; +int +ipf_flush(softc, unit, flags) + ipf_main_softc_t *softc; + minor_t unit; + int flags; { int flushed = 0, set; - WRITE_ENTER(&ipf_mutex); - bzero((char *)frcache, sizeof(frcache)); + WRITE_ENTER(&softc->ipf_mutex); - set = fr_active; + set = softc->ipf_active; if ((flags & FR_INACTIVE) == FR_INACTIVE) set = 1 - set; if (flags & FR_OUTQUE) { - if (proto == 0 || proto == 6) { - (void) frflushlist(set, unit, - &flushed, &ipfilter6[1][set]); - (void) frflushlist(set, unit, - &flushed, &ipacct6[1][set]); - } - if (proto == 0 || proto == 4) { - (void) frflushlist(set, unit, - &flushed, &ipfilter[1][set]); - (void) frflushlist(set, unit, - &flushed, &ipacct[1][set]); - } + ipf_flushlist(softc, &flushed, &softc->ipf_rules[1][set]); + ipf_flushlist(softc, &flushed, &softc->ipf_acct[1][set]); } if (flags & FR_INQUE) { - if (proto == 0 || proto == 6) { - (void) frflushlist(set, unit, - &flushed, &ipfilter6[0][set]); - (void) frflushlist(set, unit, - &flushed, &ipacct6[0][set]); - } - if (proto == 0 || proto == 4) { - (void) frflushlist(set, unit, - &flushed, &ipfilter[0][set]); - (void) frflushlist(set, unit, - &flushed, &ipacct[0][set]); - } + ipf_flushlist(softc, &flushed, &softc->ipf_rules[0][set]); + ipf_flushlist(softc, &flushed, &softc->ipf_acct[0][set]); } - RWLOCK_EXIT(&ipf_mutex); + + 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 = frflush(IPL_LOGCOUNT, proto, flags); + tmp = ipf_flush(softc, IPL_LOGCOUNT, flags); if (tmp >= 0) flushed += tmp; } @@ -3606,6 +3816,59 @@ int proto, flags; /* ------------------------------------------------------------------------ */ +/* 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 */ @@ -3616,10 +3879,11 @@ int proto, flags; /* 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 * +memstr(src, dst, slen, dlen) + const char *src; + char *dst; + size_t slen, dlen; { char *s = NULL; @@ -3634,7 +3898,7 @@ size_t slen, dlen; return s; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fixskip */ +/* 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. */ @@ -3645,9 +3909,10 @@ size_t slen, dlen; /* 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 fr_fixskip(listp, rp, addremove) -frentry_t **listp, *rp; -int addremove; +void +ipf_fixskip(listp, rp, addremove) + frentry_t **listp, *rp; + int addremove; { int rules, rn; frentry_t *fp; @@ -3676,8 +3941,9 @@ int addremove; /* consecutive 1's is different to that passed, return -1, else return # */ /* of bits. */ /* ------------------------------------------------------------------------ */ -int count4bits(ip) -u_32_t ip; +int +count4bits(ip) + u_32_t ip; { u_32_t ipn; int cnt = 0, i, j; @@ -3700,7 +3966,6 @@ u_32_t ip; } -# if 0 /* ------------------------------------------------------------------------ */ /* Function: count6bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ @@ -3709,8 +3974,10 @@ u_32_t ip; /* IPv6 ONLY */ /* count consecutive 1's in bit mask. */ /* ------------------------------------------------------------------------ */ -int count6bits(msk) -u_32_t *msk; +# ifdef USE_INET6 +int +count6bits(msk) + u_32_t *msk; { int i = 0, k; u_32_t j; @@ -3730,8 +3997,8 @@ u_32_t *msk; /* ------------------------------------------------------------------------ */ -/* Function: frsynclist */ -/* Returns: void */ +/* 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 */ @@ -3740,16 +4007,33 @@ u_32_t *msk; /* 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. */ -/* ------------------------------------------------------------------------ */ -static void frsynclist(fr, ifp) -frentry_t *fr; -void *ifp; +/* 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) { - v = fr->fr_v; + 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. @@ -3757,102 +4041,124 @@ void *ifp; for (i = 0; i < 4; i++) { if ((ifp != NULL) && (fr->fr_ifas[i] != ifp)) continue; - fr->fr_ifas[i] = fr_resolvenic(fr->fr_ifnames[i], v); + 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_IPF) { + if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if (fr->fr_satype != FRI_NORMAL && fr->fr_satype != FRI_LOOKUP) { - (void)fr_ifpaddr(v, fr->fr_satype, - fr->fr_ifas[fr->fr_sifpidx], - &fr->fr_src, &fr->fr_smsk); + 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) { - (void)fr_ifpaddr(v, fr->fr_datype, - fr->fr_ifas[fr->fr_difpidx], - &fr->fr_dst, &fr->fr_dmsk); + 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_ifp == ifp)) - fr_resolvedest(fdp, v); + 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_ifp == ifp)) - fr_resolvedest(fdp, v); + 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_ifp == ifp)) { - fr_resolvedest(fdp, v); - - fr->fr_flags &= ~FR_DUP; - if ((fdp->fd_ifp != (void *)-1) && - (fdp->fd_ifp != NULL)) - fr->fr_flags |= FR_DUP; - } - -#ifdef IPFILTER_LOOKUP - if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP && - fr->fr_srcptr == NULL) { - fr->fr_srcptr = fr_resolvelookup(fr->fr_srctype, - fr->fr_srcsubtype, - &fr->fr_slookup, - &fr->fr_srcfunc); - } - if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP && - fr->fr_dstptr == NULL) { - fr->fr_dstptr = fr_resolvelookup(fr->fr_dsttype, - fr->fr_dstsubtype, - &fr->fr_dlookup, - &fr->fr_dstfunc); + 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); } -#endif } + 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; } -#ifdef _KERNEL /* ------------------------------------------------------------------------ */ -/* Function: frsync */ +/* Function: ipf_sync */ /* Returns: void */ /* Parameters: Nil */ /* */ -/* frsync() is called when we suspect that the interface list or */ +/* 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. */ /* ------------------------------------------------------------------------ */ -void frsync(ifp) -void *ifp; +int +ipf_sync(softc, ifp) + ipf_main_softc_t *softc; + void *ifp; { int i; # if !SOLARIS - fr_natsync(ifp); - fr_statesync(ifp); + ipf_nat_sync(softc, ifp); + ipf_state_sync(softc, ifp); + ipf_lookup_sync(softc, ifp); # endif - WRITE_ENTER(&ipf_mutex); - frsynclist(ipacct[0][fr_active], ifp); - frsynclist(ipacct[1][fr_active], ifp); - frsynclist(ipfilter[0][fr_active], ifp); - frsynclist(ipfilter[1][fr_active], ifp); - frsynclist(ipacct6[0][fr_active], ifp); - frsynclist(ipacct6[1][fr_active], ifp); - frsynclist(ipfilter6[0][fr_active], ifp); - frsynclist(ipfilter6[1][fr_active], ifp); + 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 = ipfgroups[i][0]; g != NULL; g = g->fg_next) - frsynclist(g->fg_start, ifp); - for (g = ipfgroups[i][1]; g != NULL; g = g->fg_next) - frsynclist(g->fg_start, ifp); + 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(&ipf_mutex); + RWLOCK_EXIT(&softc->ipf_mutex); + + return 0; } @@ -3872,9 +4178,11 @@ void *ifp; /* 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(src, dst, size) -void *src, *dst; -size_t size; +int +copyinptr(softc, src, dst, size) + ipf_main_softc_t *softc; + void *src, *dst; + size_t size; { caddr_t ca; int error; @@ -3887,8 +4195,10 @@ size_t size; bcopy(src, (caddr_t)&ca, sizeof(ca)); # endif error = COPYIN(ca, dst, size); - if (error != 0) + if (error != 0) { + IPFERROR(3); error = EFAULT; + } return error; } @@ -3904,24 +4214,29 @@ size_t size; /* 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(src, dst, size) -void *src, *dst; -size_t size; +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) + if (error != 0) { + IPFERROR(4); error = EFAULT; + } return error; } +#ifdef _KERNEL #endif /* ------------------------------------------------------------------------ */ -/* Function: fr_lock */ +/* 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 */ @@ -3929,9 +4244,10 @@ size_t size; /* Get the new value for the lock integer, set it and return the old value */ /* in *lockp. */ /* ------------------------------------------------------------------------ */ -int fr_lock(data, lockp) -caddr_t data; -int *lockp; +int +ipf_lock(data, lockp) + caddr_t data; + int *lockp; { int arg, err; @@ -3947,51 +4263,78 @@ int *lockp; /* ------------------------------------------------------------------------ */ -/* Function: fr_getstat */ +/* Function: ipf_getstat */ /* Returns: Nil */ -/* Parameters: fiop(I) - pointer to ipfilter stats structure */ +/* 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. */ -/* ------------------------------------------------------------------------ */ -void fr_getstat(fiop) -friostat_t *fiop; +/* 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, j; - - bcopy((char *)frstats, (char *)fiop->f_st, sizeof(filterstats_t) * 2); - fiop->f_locks[IPL_LOGSTATE] = fr_state_lock; - fiop->f_locks[IPL_LOGNAT] = fr_nat_lock; - fiop->f_locks[IPL_LOGIPF] = fr_frag_lock; - fiop->f_locks[IPL_LOGAUTH] = fr_auth_lock; - - for (i = 0; i < 2; i++) - for (j = 0; j < 2; j++) { - fiop->f_ipf[i][j] = ipfilter[i][j]; - fiop->f_acct[i][j] = ipacct[i][j]; - fiop->f_ipf6[i][j] = ipfilter6[i][j]; - fiop->f_acct6[i][j] = ipacct6[i][j]; - } - - fiop->f_ticks = fr_ticks; - fiop->f_active = fr_active; - fiop->f_froute[0] = fr_frouteok[0]; - fiop->f_froute[1] = fr_frouteok[1]; + int i; - fiop->f_running = fr_running; + 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] = ipfgroups[i][0]; - fiop->f_groups[i][1] = ipfgroups[i][1]; + 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 = fr_pass; - fiop->f_features = fr_features; + 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 } @@ -4042,7 +4385,7 @@ int icmpreplytype4[ICMP_MAXTYPE + 1]; /* ------------------------------------------------------------------------ */ -/* Function: fr_matchicmpqueryreply */ +/* 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 */ @@ -4053,11 +4396,12 @@ int icmpreplytype4[ICMP_MAXTYPE + 1]; /* reply to one as described by what's in ic. If it is a match, return 1, */ /* else return 0 for no match. */ /* ------------------------------------------------------------------------ */ -int fr_matchicmpqueryreply(v, ic, icmp, rev) -int v; -icmpinfo_t *ic; -icmphdr_t *icmp; -int rev; +int +ipf_matchicmpqueryreply(v, ic, icmp, rev) + int v; + icmpinfo_t *ic; + icmphdr_t *icmp; + int rev; { int ictype; @@ -4091,88 +4435,6 @@ int rev; } -#ifdef IPFILTER_LOOKUP -/* ------------------------------------------------------------------------ */ -/* Function: fr_resolvelookup */ -/* Returns: void * - NULL = failure, else success. */ -/* Parameters: type(I) - type of lookup these parameters are for. */ -/* subtype(I) - whether the info below contains number/name */ -/* info(I) - pointer to name/number of the lookup data */ -/* 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. */ -/* ------------------------------------------------------------------------ */ -static void *fr_resolvelookup(type, subtype, info, funcptr) -u_int type, subtype; -i6addr_t *info; -lookupfunc_t *funcptr; -{ - char label[FR_GROUPLEN], *name; - iphtable_t *iph; - ip_pool_t *ipo; - void *ptr; - - if (subtype == 0) { -#if defined(SNPRINTF) && defined(_KERNEL) - SNPRINTF(label, sizeof(label), "%u", info->iplookupnum); -#else - (void) sprintf(label, "%u", info->iplookupnum); -#endif - name = label; - } else if (subtype == 1) { - /* - * Because iplookupname is currently only a 12 character - * string and FR_GROUPLEN is 16, copy all of it into the - * label buffer and add on a NULL at the end. - */ - strncpy(label, info->iplookupname, sizeof(info->iplookupname)); - label[sizeof(info->iplookupname)] = '\0'; - name = label; - } else { - return NULL; - } - - READ_ENTER(&ip_poolrw); - - switch (type) - { - case IPLT_POOL : -# if (defined(__osf__) && defined(_KERNEL)) - ptr = NULL; - *funcptr = NULL; -# else - ipo = ip_pool_find(IPL_LOGIPF, name); - ptr = ipo; - if (ipo != NULL) { - ATOMIC_INC32(ipo->ipo_ref); - } - *funcptr = ip_pool_search; -# endif - break; - case IPLT_HASH : - iph = fr_findhtable(IPL_LOGIPF, name); - ptr = iph; - if (iph != NULL) { - ATOMIC_INC32(iph->iph_ref); - } - *funcptr = fr_iphmfindip; - break; - default: - ptr = NULL; - *funcptr = NULL; - break; - } - RWLOCK_EXIT(&ip_poolrw); - - return ptr; -} -#endif - - /* ------------------------------------------------------------------------ */ /* Function: frrequest */ /* Returns: int - 0 == success, > 0 == errno value */ @@ -4190,54 +4452,98 @@ lookupfunc_t *funcptr; /* 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(unit, req, data, set, makecopy) -int unit; -ioctlcmd_t req; -int set, makecopy; -caddr_t data; +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, addrem, need_free = 0; frentry_t frd, *fp, *f, **fprev, **ftail; - int error = 0, in, v; - void *ptr, *uptr; + void *ptr, *uptr, *cptr; u_int *p, *pp; frgroup_t *fg; char *group; + ptr = NULL; + cptr = NULL; fg = NULL; fp = &frd; if (makecopy != 0) { - error = fr_inobj(data, fp, IPFOBJ_FRENTRY); - if (error) - return EFAULT; - if ((fp->fr_flags & FR_T_BUILTIN) != 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_dnext = NULL; fp->fr_ref = 0; fp->fr_flags |= FR_COPIED; } else { fp = (frentry_t *)data; - if ((fp->fr_type & FR_T_BUILTIN) == 0) + 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))) - return EINVAL; + ((fp->fr_dsize != 0) && (fp->fr_data == NULL))) { + IPFERROR(8); + error = EINVAL; + goto donenolock; + } - v = fp->fr_v; + 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 = 0; + else if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) + addrem = 1; + else if (req == (ioctlcmd_t)SIOCZRLST) + addrem = 2; + else { + IPFERROR(9); + error = EINVAL; + goto donenolock; + } + /* * Only filter rules for IPv4 or IPv6 are accepted. */ - if (v == 4) + if (family == AF_INET) { /*EMPTY*/; #ifdef USE_INET6 - else if (v == 6) + } else if (family == AF_INET6) { /*EMPTY*/; #endif - else { - return EINVAL; + } else if (family != 0) { + IPFERROR(10); + error = EINVAL; + goto donenolock; } /* @@ -4246,37 +4552,110 @@ caddr_t data; * rule. */ if ((makecopy == 1) && (fp->fr_func != NULL)) { - if (fr_findfunc(fp->fr_func) == NULL) - return ESRCH; - error = fr_funcinit(fp); - if (error != 0) - return error; + if (ipf_findfunc(fp->fr_func) == NULL) { + IPFERROR(11); + error = ESRCH; + goto donenolock; + } + + if (addrem == 0) { + 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; - /* - * Check that the group number does exist and that its use (in/out) - * matches what the rule is. - */ - if (!strncmp(fp->fr_grhead, "0", FR_GROUPLEN)) - *fp->fr_grhead = '\0'; - group = fp->fr_group; - if (!strncmp(group, "0", FR_GROUPLEN)) - *group = '\0'; + cptr = NULL; if (FR_ISACCOUNT(fp->fr_flags)) unit = IPL_LOGCOUNT; - if ((req != (int)SIOCZRLST) && (*group != '\0')) { - fg = fr_findgroup(group, unit, set, NULL); - if (fg == NULL) - return ESRCH; - if (fg->fg_flags == 0) - fg->fg_flags = fp->fr_flags & FR_INOUT; - else if (fg->fg_flags != (fp->fr_flags & FR_INOUT)) - return ESRCH; + /* + * 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 == 0) { + 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; /* @@ -4284,27 +4663,30 @@ caddr_t data; */ ftail = NULL; fprev = NULL; - if (unit == IPL_LOGAUTH) - fprev = &ipauth; - else if (v == 4) { - if (FR_ISACCOUNT(fp->fr_flags)) - fprev = &ipacct[in][set]; - else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) - fprev = &ipfilter[in][set]; - } else if (v == 6) { + 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 = &ipacct6[in][set]; + fprev = &softc->ipf_acct[in][set]; else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) - fprev = &ipfilter6[in][set]; + fprev = &softc->ipf_rules[in][set]; + } + if (fprev == NULL) { + IPFERROR(15); + error = ESRCH; + goto donenolock; } - if (fprev == NULL) - return ESRCH; - if (*group != '\0') { - if (!fg && !(fg = fr_findgroup(group, unit, set, NULL))) - return ESRCH; + if (fg != NULL) fprev = &fg->fg_start; - } /* * Copy in extra data for the rule. @@ -4312,51 +4694,82 @@ caddr_t data; if (fp->fr_dsize != 0) { if (makecopy != 0) { KMALLOCS(ptr, void *, fp->fr_dsize); - if (!ptr) - return ENOMEM; - error = COPYIN(uptr, ptr, fp->fr_dsize); - if (error != 0) - error = EFAULT; + 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; - error = 0; - } - if (error != 0) { - KFREES(ptr, fp->fr_dsize); - return ENOMEM; } fp->fr_data = ptr; - } else + } 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) - return EINVAL; + if (fp->fr_dsize == 0) { + IPFERROR(19); + error = EINVAL; + break; + } if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) { - if (makecopy && fp->fr_data != NULL) { - KFREES(fp->fr_data, fp->fr_dsize); - } - return EINVAL; + IPFERROR(20); + error = EINVAL; + break; } break; #endif case FR_T_IPF : - if (fp->fr_dsize != sizeof(fripf_t)) - return EINVAL; + /* + * 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)) - return EINVAL; + if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW)) { + IPFERROR(22); + error = EINVAL; + break; + } switch (fp->fr_satype) { @@ -4365,26 +4778,30 @@ caddr_t data; case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : - if (fp->fr_sifpidx < 0 || fp->fr_sifpidx > 3) { - if (makecopy && fp->fr_data != NULL) { - KFREES(fp->fr_data, fp->fr_dsize); - } - return EINVAL; + if (fp->fr_sifpidx < 0) { + IPFERROR(23); + error = EINVAL; } break; -#ifdef IPFILTER_LOOKUP case FRI_LOOKUP : - fp->fr_srcptr = fr_resolvelookup(fp->fr_srctype, - fp->fr_srcsubtype, - &fp->fr_slookup, - &fp->fr_srcfunc); - if (fp->fr_srcptr == NULL) - return ESRCH; + 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; -#endif default : + IPFERROR(133); + error = EINVAL; break; } + if (error != 0) + break; switch (fp->fr_datype) { @@ -4393,45 +4810,84 @@ caddr_t data; case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : - if (fp->fr_difpidx < 0 || fp->fr_difpidx > 3) { - if (makecopy && fp->fr_data != NULL) { - KFREES(fp->fr_data, fp->fr_dsize); - } - return EINVAL; + if (fp->fr_difpidx < 0) { + IPFERROR(24); + error = EINVAL; } break; -#ifdef IPFILTER_LOOKUP case FRI_LOOKUP : - fp->fr_dstptr = fr_resolvelookup(fp->fr_dsttype, - fp->fr_dstsubtype, - &fp->fr_dlookup, - &fp->fr_dstfunc); - if (fp->fr_dstptr == NULL) - return ESRCH; + fp->fr_dstptr = ipf_findlookup(softc, unit, fp, + &fp->fr_dst6, + &fp->fr_dmsk6); + if (fp->fr_dstfunc == NULL) { + IPFERROR(134); + error = ESRCH; + } break; -#endif - default : + case FRI_NORMAL : break; + default : + IPFERROR(135); + error = EINVAL; } break; + case FR_T_NONE : - break; case FR_T_CALLFUNC : - break; 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 : - if (makecopy && fp->fr_data != NULL) { - KFREES(fp->fr_data, fp->fr_dsize); + 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; } - return EINVAL; } /* * Lookup all the interface names that are part of the rule. */ - frsynclist(fp, NULL); + 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 @@ -4447,7 +4903,7 @@ caddr_t data; for (p = (u_int *)fp->fr_data; p < pp; p++) fp->fr_cksum += *p; - WRITE_ENTER(&ipf_mutex); + WRITE_ENTER(&softc->ipf_mutex); /* * Now that the filter rule lists are locked, we can walk the @@ -4462,13 +4918,15 @@ caddr_t data; } fprev = ftail; } - bzero((char *)frcache, sizeof(frcache)); for (; (f = *ftail) != NULL; ftail = &f->fr_next) { + DT2(rule_cmp, frentry_t *, fp, frentry_t *, f); if ((fp->fr_cksum != f->fr_cksum) || + (fp->fr_size != f->fr_size) || (f->fr_dsize != fp->fr_dsize)) continue; - if (bcmp((char *)&f->fr_func, (char *)&fp->fr_func, FR_CMPSIZ)) + if (bcmp((char *)&f->fr_func, (char *)&fp->fr_func, + fp->fr_size - offsetof(struct frentry, fr_func)) != 0) continue; if ((!ptr && !f->fr_data) || (ptr && f->fr_data && @@ -4479,10 +4937,11 @@ caddr_t data; /* * If zero'ing statistics, copy current to caller and zero. */ - if (req == (ioctlcmd_t)SIOCZRLST) { - if (f == NULL) + if (addrem == 2) { + if (f == NULL) { + IPFERROR(27); error = ESRCH; - else { + } else { /* * Copy and reduce lock because of impending copyout. * Well we should, but if we do then the atomicity of @@ -4491,22 +4950,24 @@ caddr_t data; * only resets them to 0 if they are successfully * copied out into user space. */ - bcopy((char *)f, (char *)fp, sizeof(*f)); - /* MUTEX_DOWNGRADE(&ipf_mutex); */ + 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 = fr_outobj(data, fp, IPFOBJ_FRENTRY); + 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) + if (error != 0) { + IPFERROR(28); error = EFAULT; + } if (error == 0) { f->fr_hits = 0; f->fr_bytes = 0; @@ -4514,14 +4975,17 @@ caddr_t data; } } - if ((ptr != NULL) && (makecopy != 0)) { - KFREES(ptr, fp->fr_dsize); + if (makecopy != 0) { + if (ptr != NULL) { + KFREES(ptr, fp->fr_dsize); + } + KFREES(fp, fp->fr_size); } - RWLOCK_EXIT(&ipf_mutex); + RWLOCK_EXIT(&softc->ipf_mutex); return error; } - if (!f) { + if (!f) { /* * At the end of this, ftail must point to the place where the * new rule is to be saved/inserted/added. @@ -4539,7 +5003,6 @@ caddr_t data; } f = NULL; ptr = NULL; - error = 0; } else if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR) { while ((f = *fprev) != NULL) { @@ -4547,34 +5010,35 @@ caddr_t data; break; fprev = &f->fr_next; } - ftail = fprev; - if (fp->fr_hits != 0) { + 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; + ftail = &f->fr_next; fp->fr_hits--; } - } - f = NULL; - ptr = NULL; - error = 0; + } + f = NULL; + ptr = NULL; } } /* * Request to remove a rule. */ - if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) { - if (!f) + if (addrem == 1) { + if (!f) { + IPFERROR(29); error = ESRCH; - else { + } 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; } @@ -4584,101 +5048,265 @@ caddr_t data; * something else (eg state information.) */ if (f->fr_ref > 1) { + IPFERROR(31); error = EBUSY; goto done; } #ifdef IPFILTER_SCAN - if (f->fr_isctag[0] != '\0' && + if (f->fr_isctag != -1 && (f->fr_isc != (struct ipscan *)-1)) - ipsc_detachfr(f); + ipf_scan_detachfr(f); #endif + if (unit == IPL_LOGAUTH) { - error = fr_preauthcmd(req, f, ftail); + error = ipf_auth_precmd(softc, req, f, ftail); goto done; } - if (*f->fr_grhead != '\0') - fr_delgroup(f->fr_grhead, unit, set); - fr_fixskip(ftail, f, -1); - *ftail = f->fr_next; - f->fr_next = NULL; - (void) fr_derefrule(&f); + + ipf_rule_delete(softc, f, unit, set); + + need_free = makecopy; } } else { /* * Not removing, so we must be adding/inserting a rule. */ - if (f) + if (f != NULL) { + IPFERROR(32); error = EEXIST; - else { - if (unit == IPL_LOGAUTH) { - error = fr_preauthcmd(req, fp, ftail); - goto done; - } - if (makecopy) { - KMALLOC(f, frentry_t *); - } else - f = fp; - if (f != NULL) { - if (fp != f) - bcopy((char *)fp, (char *)f, - sizeof(*f)); - MUTEX_NUKE(&f->fr_lock); - MUTEX_INIT(&f->fr_lock, "filter rule lock"); -#ifdef IPFILTER_SCAN - if (f->fr_isctag[0] != '\0' && - ipsc_attachfr(f)) - f->fr_isc = (struct ipscan *)-1; -#endif - f->fr_hits = 0; - if (makecopy != 0) - f->fr_ref = 1; - f->fr_next = *ftail; - *ftail = f; - if (req == (ioctlcmd_t)SIOCINIFR || - req == (ioctlcmd_t)SIOCINAFR) - fr_fixskip(ftail, f, 1); - f->fr_grp = NULL; - group = f->fr_grhead; - if (*group != '\0') { - fg = fr_addgroup(group, f, f->fr_flags, - unit, set); - if (fg != NULL) - f->fr_grp = &fg->fg_start; - } - } else - error = ENOMEM; + 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; + *ftail = fp; + if (addrem == 0) + 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(&ipf_mutex); - if ((ptr != NULL) && (error != 0) && (makecopy != 0)) { - KFREES(ptr, fp->fr_dsize); + 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: fr_funcinit */ +/* 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: fr(I) - pointer to filter rule */ +/* 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 fr_funcinit(fr) -frentry_t *fr; +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 = fr_availfuncs; ft->ipfu_addr != NULL; ft++) + 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)(fr); + err = (*ft->ipfu_init)(softc, fr); break; } return err; @@ -4686,18 +5314,45 @@ frentry_t *fr; /* ------------------------------------------------------------------------ */ -/* Function: fr_findfunc */ +/* 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 fr_findfunc(funcptr) -ipfunc_t funcptr; +static ipfunc_t +ipf_findfunc(funcptr) + ipfunc_t funcptr; { ipfunc_resolve_t *ft; - for (ft = fr_availfuncs; ft->ipfu_addr != NULL; ft++) + for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == funcptr) return funcptr; return NULL; @@ -4705,7 +5360,7 @@ ipfunc_t funcptr; /* ------------------------------------------------------------------------ */ -/* Function: fr_resolvefunc */ +/* Function: ipf_resolvefunc */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - ioctl data pointer to ipfunc_resolve_t struct */ /* */ @@ -4714,46 +5369,55 @@ ipfunc_t funcptr; /* 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 fr_resolvefunc(data) -void *data; +int +ipf_resolvefunc(softc, data) + ipf_main_softc_t *softc; + void *data; { ipfunc_resolve_t res, *ft; - int err; + int error; - err = BCOPYIN(data, &res, sizeof(res)); - if (err != 0) + 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 = fr_availfuncs; ft->ipfu_addr != NULL; ft++) + 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) + 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 = fr_availfuncs; ft->ipfu_addr != NULL; ft++) + 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) + if (COPYOUT(&res, data, sizeof(res)) != 0) { + IPFERROR(36); return EFAULT; + } return 0; } } + IPFERROR(37); return ESRCH; } -#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)) || \ - (defined(__FreeBSD__) && (__FreeBSD_version < 501000)) || \ - (defined(__NetBSD__) && (__NetBSD_Version__ < 105000000)) || \ - (defined(__OpenBSD__) && (OpenBSD < 200006)) +#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && \ + !defined(__FreeBSD__)) || \ + FREEBSD_LT_REV(501000) || NETBSD_LT_REV(105000000) || \ + OPENBSD_LT_REV(200006) /* * From: NetBSD * ppsratecheck(): packets (or events) per second limitation. @@ -4803,17 +5467,20 @@ ppsratecheck(lasttime, curpps, maxpps) /* ------------------------------------------------------------------------ */ -/* Function: fr_derefrule */ +/* 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 fr_derefrule(frp) -frentry_t **frp; +int +ipf_derefrule(softc, frp) + ipf_main_softc_t *softc; + frentry_t **frp; { frentry_t *fr; + frdest_t *fdp; fr = *frp; *frp = NULL; @@ -4824,18 +5491,41 @@ frentry_t **frp; MUTEX_EXIT(&fr->fr_lock); MUTEX_DESTROY(&fr->fr_lock); -#ifdef IPFILTER_LOOKUP - if (fr->fr_type == FR_T_IPF && fr->fr_satype == FRI_LOOKUP) - ip_lookup_deref(fr->fr_srctype, fr->fr_srcptr); - if (fr->fr_type == FR_T_IPF && fr->fr_datype == FRI_LOOKUP) - ip_lookup_deref(fr->fr_dsttype, fr->fr_dstptr); -#endif + 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_dsize) { - KFREES(fr->fr_data, fr->fr_dsize); - } if ((fr->fr_flags & FR_COPIED) != 0) { - KFREE(fr); + if (fr->fr_dsize) { + KFREES(fr->fr_data, fr->fr_dsize); + } + KFREES(fr, fr->fr_size); return 0; } return 1; @@ -4846,17 +5536,18 @@ frentry_t **frp; } -#ifdef IPFILTER_LOOKUP /* ------------------------------------------------------------------------ */ -/* Function: fr_grpmapinit */ +/* 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 fr_srcgrpmap and fr_dstgrpmap. */ +/* fr_ptr is later used by ipf_srcgrpmap and ipf_dstgrpmap. */ /* ------------------------------------------------------------------------ */ -static int fr_grpmapinit(fr) -frentry_t *fr; +static int +ipf_grpmapinit(softc, fr) + ipf_main_softc_t *softc; + frentry_t *fr; { char name[FR_GROUPLEN]; iphtable_t *iph; @@ -4866,18 +5557,45 @@ frentry_t *fr; #else (void) sprintf(name, "%d", fr->fr_arg); #endif - iph = fr_findhtable(IPL_LOGIPF, name); - if (iph == NULL) + 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)) + } + 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: fr_srcgrpmap */ +/* 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) */ @@ -4886,26 +5604,28 @@ frentry_t *fr; /* the key, and descend into that group and continue matching rules against */ /* the packet. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_srcgrpmap(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_srcgrpmap(fin, passp) + fr_info_t *fin; + u_32_t *passp; { frgroup_t *fg; void *rval; - rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, &fin->fin_src); + 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) fr_scanlist(fin, *passp); + (void) ipf_scanlist(fin, *passp); return fin->fin_fr; } /* ------------------------------------------------------------------------ */ -/* Function: fr_dstgrpmap */ +/* 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) */ @@ -4914,37 +5634,39 @@ u_32_t *passp; /* address as the key, and descend into that group and continue matching */ /* rules against the packet. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_dstgrpmap(fin, passp) -fr_info_t *fin; -u_32_t *passp; +frentry_t * +ipf_dstgrpmap(fin, passp) + fr_info_t *fin; + u_32_t *passp; { frgroup_t *fg; void *rval; - rval = fr_iphmfindgroup(fin->fin_fr->fr_ptr, &fin->fin_dst); + 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) fr_scanlist(fin, *passp); + (void) ipf_scanlist(fin, *passp); return fin->fin_fr; } -#endif /* IPFILTER_LOOKUP */ /* * 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 - * fr_freetimeoutqueue() from being called at the same time as any other. + * 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: fr_addtimeoutqueue */ +/* 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 */ @@ -4960,16 +5682,18 @@ u_32_t *passp; /* It is assumed that the caller of this function has an appropriate lock */ /* held (exclusively) in the domain that encompases 'parent'. */ /* ------------------------------------------------------------------------ */ -ipftq_t *fr_addtimeoutqueue(parent, seconds) -ipftq_t **parent; -u_int seconds; +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(&ipf_timeoutlock); + MUTEX_ENTER(&softc->ipf_timeoutlock); for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) { if (ifq->ifq_ttl == period) { /* @@ -4980,7 +5704,7 @@ u_int seconds; ifq->ifq_flags &= ~IFQF_DELETE; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); - MUTEX_EXIT(&ipf_timeoutlock); + MUTEX_EXIT(&softc->ipf_timeoutlock); return ifq; } @@ -4988,25 +5712,22 @@ u_int seconds; KMALLOC(ifq, ipftq_t *); if (ifq != NULL) { - ifq->ifq_ttl = period; - ifq->ifq_head = NULL; - ifq->ifq_tail = &ifq->ifq_head; + MUTEX_NUKE(&ifq->ifq_lock); + IPFTQ_INIT(ifq, period, "ipftq mutex"); ifq->ifq_next = *parent; ifq->ifq_pnext = parent; - ifq->ifq_ref = 1; ifq->ifq_flags = IFQF_USER; + ifq->ifq_ref++; *parent = ifq; - fr_userifqs++; - MUTEX_NUKE(&ifq->ifq_lock); - MUTEX_INIT(&ifq->ifq_lock, "ipftq mutex"); + softc->ipf_userifqs++; } - MUTEX_EXIT(&ipf_timeoutlock); + MUTEX_EXIT(&softc->ipf_timeoutlock); return ifq; } /* ------------------------------------------------------------------------ */ -/* Function: fr_deletetimeoutqueue */ +/* 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 */ @@ -5020,8 +5741,9 @@ u_int seconds; /* way because the locking may not be sufficient to safely do a free when */ /* this function is called. */ /* ------------------------------------------------------------------------ */ -int fr_deletetimeoutqueue(ifq) -ipftq_t *ifq; +int +ipf_deletetimeoutqueue(ifq) + ipftq_t *ifq; { ifq->ifq_ref--; @@ -5034,7 +5756,7 @@ ipftq_t *ifq; /* ------------------------------------------------------------------------ */ -/* Function: fr_freetimeoutqueue */ +/* Function: ipf_freetimeoutqueue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Returns: Nil */ /* */ @@ -5043,17 +5765,18 @@ ipftq_t *ifq; /* held (exclusively) in the domain that encompases the callers "domain". */ /* The ifq_lock for this structure should not be held. */ /* */ -/* Remove a user definde timeout queue from the list of queues it is in and */ +/* Remove a user defined timeout queue from the list of queues it is in and */ /* tidy up after this is done. */ /* ------------------------------------------------------------------------ */ -void fr_freetimeoutqueue(ifq) -ipftq_t *ifq; +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("fr_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n", + 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; @@ -5065,26 +5788,28 @@ ipftq_t *ifq; *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(fr_userifqs); + ATOMIC_DEC(softc->ipf_userifqs); KFREE(ifq); } /* ------------------------------------------------------------------------ */ -/* Function: fr_deletequeueentry */ +/* Function: ipf_deletequeueentry */ /* Returns: Nil */ /* Parameters: tqe(I) - timeout queue entry to delete */ -/* ifq(I) - timeout queue to remove entry from */ /* */ /* Remove a tail queue entry from its queue and make it an orphan. */ -/* fr_deletetimeoutqueue is called to make sure the reference count on the */ -/* queue is correct. We can't, however, call fr_freetimeoutqueue because */ +/* 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 fr_deletequeueentry(tqe) -ipftqent_t *tqe; +void +ipf_deletequeueentry(tqe) + ipftqent_t *tqe; { ipftq_t *ifq; @@ -5103,21 +5828,23 @@ ipftqent_t *tqe; tqe->tqe_ifq = NULL; } - (void) fr_deletetimeoutqueue(ifq); + (void) ipf_deletetimeoutqueue(ifq); + ASSERT(ifq->ifq_ref > 0); MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ -/* Function: fr_queuefront */ +/* 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 fr_queuefront(tqe) -ipftqent_t *tqe; +void +ipf_queuefront(tqe) + ipftqent_t *tqe; { ipftq_t *ifq; @@ -5143,21 +5870,27 @@ ipftqent_t *tqe; /* ------------------------------------------------------------------------ */ -/* Function: fr_queueback */ +/* Function: ipf_queueback */ /* Returns: Nil */ -/* Parameters: tqe(I) - pointer to timeout queue entry */ +/* 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 fr_queueback(tqe) -ipftqent_t *tqe; +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 = fr_ticks + ifq->ifq_ttl; + 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 ? */ @@ -5180,18 +5913,23 @@ ipftqent_t *tqe; /* ------------------------------------------------------------------------ */ -/* Function: fr_queueappend */ +/* Function: ipf_queueappend */ /* Returns: Nil */ -/* Parameters: tqe(I) - pointer to timeout queue entry */ +/* 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 fr_queueappend(tqe, ifq, parent) -ipftqent_t *tqe; -ipftq_t *ifq; -void *parent; +void +ipf_queueappend(ticks, tqe, ifq, parent) + u_long ticks; + ipftqent_t *tqe; + ipftq_t *ifq; + void *parent; { MUTEX_ENTER(&ifq->ifq_lock); @@ -5201,14 +5939,15 @@ void *parent; ifq->ifq_tail = &tqe->tqe_next; tqe->tqe_next = NULL; tqe->tqe_ifq = ifq; - tqe->tqe_die = fr_ticks + ifq->ifq_ttl; + tqe->tqe_die = ticks + ifq->ifq_ttl; + tqe->tqe_touched = ticks; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ -/* Function: fr_movequeue */ +/* Function: ipf_movequeue */ /* Returns: Nil */ /* Parameters: tq(I) - pointer to timeout queue information */ /* oifp(I) - old timeout queue entry was on */ @@ -5218,58 +5957,82 @@ void *parent; /* If it notices that the current entry is already last and does not need */ /* to move queue, the return. */ /* ------------------------------------------------------------------------ */ -void fr_movequeue(tqe, oifq, nifq) -ipftqent_t *tqe; -ipftq_t *oifq, *nifq; +void +ipf_movequeue(ticks, tqe, oifq, nifq) + u_long ticks; + ipftqent_t *tqe; + ipftq_t *oifq, *nifq; { + /* - * Is the operation here going to be a no-op ? + * 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); - if ((oifq != nifq) || (*oifq->ifq_tail != tqe)) { - /* - * 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; + 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; + } + } - (void) fr_deletetimeoutqueue(oifq); + /* + * 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; - MUTEX_EXIT(&oifq->ifq_lock); + /* + * 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; - MUTEX_ENTER(&nifq->ifq_lock); + (void) ipf_deletetimeoutqueue(oifq); - tqe->tqe_ifq = nifq; - nifq->ifq_ref++; - } + MUTEX_EXIT(&oifq->ifq_lock); - /* - * Add to the bottom of the new queue - */ - tqe->tqe_die = fr_ticks + nifq->ifq_ttl; - tqe->tqe_pnext = nifq->ifq_tail; - *nifq->ifq_tail = tqe; - nifq->ifq_tail = &tqe->tqe_next; + 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: fr_updateipid */ +/* Function: ipf_updateipid */ /* Returns: int - 0 == success, -1 == error (packet should be droppped) */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -5280,23 +6043,24 @@ ipftq_t *oifq, *nifq; /* the fragment cache for non-leading fragments. If a non-leading fragment */ /* has no match in the cache, return an error. */ /* ------------------------------------------------------------------------ */ -static int fr_updateipid(fin) -fr_info_t *fin; +static int +ipf_updateipid(fin) + fr_info_t *fin; { u_short id, ido, sums; u_32_t sumd, sum; ip_t *ip; if (fin->fin_off != 0) { - sum = fr_ipid_knownfrag(fin); + sum = ipf_frag_ipidknown(fin); if (sum == 0xffffffff) return -1; sum &= 0xffff; id = (u_short)sum; } else { - id = fr_nextipid(fin); + id = ipf_nextipid(fin); if (fin->fin_off == 0 && (fin->fin_flx & FI_FRAG) != 0) - (void) fr_ipid_newfrag(fin, (u_32_t)id); + (void) ipf_frag_ipidnew(fin, (u_32_t)id); } ip = fin->fin_ip; @@ -5317,7 +6081,7 @@ fr_info_t *fin; #ifdef NEED_FRGETIFNAME /* ------------------------------------------------------------------------ */ -/* Function: fr_getifname */ +/* 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 */ @@ -5326,9 +6090,10 @@ fr_info_t *fin; /* 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 *fr_getifname(ifp, buffer) -struct ifnet *ifp; -char *buffer; +char * +ipf_getifname(ifp, buffer) + struct ifnet *ifp; + char *buffer; { static char namebuf[LIFNAMSIZ]; # if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \ @@ -5350,7 +6115,7 @@ char *buffer; ; unit = ifp->if_unit; space = LIFNAMSIZ - (s - buffer); - if (space > 0) { + if ((space > 0) && (unit >= 0)) { # if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(temp, sizeof(temp), "%d", unit); # else @@ -5365,7 +6130,7 @@ char *buffer; /* ------------------------------------------------------------------------ */ -/* Function: fr_ioctlswitch */ +/* Function: ipf_ioctlswitch */ /* Returns: int - -1 continue processing, else ioctl return value */ /* Parameters: unit(I) - device unit opened */ /* data(I) - pointer to ioctl data */ @@ -5376,63 +6141,99 @@ char *buffer; /* */ /* 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. */ +/* 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 fr_ioctlswitch(unit, data, cmd, mode, uid, ctx) -int unit, mode, uid; -ioctlcmd_t cmd; -void *data, *ctx; +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 = fr_ipf_ioctl(data, cmd, mode, uid, ctx); + error = ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx); break; case IPL_LOGNAT : - if (fr_running > 0) - error = fr_nat_ioctl(data, cmd, mode, uid, ctx); - else + 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 (fr_running > 0) - error = fr_state_ioctl(data, cmd, mode, uid, ctx); - else + 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 (fr_running > 0) - error = fr_auth_ioctl(data, cmd, mode, uid, ctx); - else + if (softc->ipf_running > 0) { + error = ipf_auth_ioctl(softc, data, cmd, mode, + uid, ctx); + } else { + IPFERROR(44); error = EIO; + } break; case IPL_LOGSYNC : -#ifdef IPFILTER_SYNC - if (fr_running > 0) - error = fr_sync_ioctl(data, cmd, mode, uid, ctx); - else -#endif + 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 (fr_running > 0) - error = fr_scan_ioctl(data, cmd, mode, uid, ctx); + 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 : -#ifdef IPFILTER_LOOKUP - if (fr_running > 0) - error = ip_lookup_ioctl(data, cmd, mode, uid, ctx); - else -#endif + 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; } @@ -5443,200 +6244,244 @@ void *data, *ctx; /* * This array defines the expected size of objects coming into the kernel - * for the various recognised object types. + * 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 int fr_objbytes[IPFOBJ_COUNT][2] = { - { 1, sizeof(struct frentry) }, /* frentry */ - { 0, sizeof(struct friostat) }, - { 0, sizeof(struct fr_info) }, - { 0, sizeof(struct fr_authstat) }, - { 0, sizeof(struct ipfrstat) }, - { 0, sizeof(struct ipnat) }, - { 0, sizeof(struct natstat) }, - { 0, sizeof(struct ipstate_save) }, - { 1, sizeof(struct nat_save) }, /* nat_save */ - { 0, sizeof(struct natlookup) }, - { 1, sizeof(struct ipstate) }, /* ipstate */ - { 0, sizeof(struct ips_stat) }, - { 0, sizeof(struct frauth) }, - { 0, sizeof(struct ipftune) }, - { 0, sizeof(struct nat) }, /* nat_t */ - { 0, sizeof(struct ipfruleiter) }, - { 0, sizeof(struct ipfgeniter) }, - { 0, sizeof(struct ipftable) }, - { 0, sizeof(struct ipflookupiter) }, +static 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: fr_inobj */ +/* Function: ipf_inobj */ /* 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 */ +/* 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 fr_inobj(data, ptr, type) -void *data; -void *ptr; -int type; +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 = 0; + int error; + int size; - if ((type < 0) || (type >= IPFOBJ_COUNT)) + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(49); return EINVAL; + } - error = BCOPYIN(data, &obj, sizeof(obj)); - if (error != 0) + if (objp == NULL) + objp = &obj; + error = BCOPYIN(data, objp, sizeof(*objp)); + if (error != 0) { + IPFERROR(124); return EFAULT; + } - if (obj.ipfo_type != type) + if (objp->ipfo_type != type) { + IPFERROR(50); return EINVAL; + } -#ifndef IPFILTER_COMPAT - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) + 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; - } else if (obj.ipfo_size != fr_objbytes[type][1]) { - 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 - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; + IPFERROR(54); + error = EINVAL; #endif - - if ((fr_objbytes[type][0] & 1) != 0) { - error = COPYIN(obj.ipfo_ptr, ptr, fr_objbytes[type][1]); - } else { - error = COPYIN(obj.ipfo_ptr, ptr, obj.ipfo_size); } - if (error != 0) - error = EFAULT; return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_inobjsz */ +/* Function: ipf_inobjsz */ /* 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 */ +/* 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 fr_inobj, except the size of the object to copy in is passed in */ +/* 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 fr_inobj(). */ +/* not possible nor required in ipf_inobj(). */ /* ------------------------------------------------------------------------ */ -int fr_inobjsz(data, ptr, type, sz) -void *data; -void *ptr; -int type, sz; +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)) - return EINVAL; - if (((fr_objbytes[type][0] & 1) == 0) || (sz < fr_objbytes[type][1])) + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(56); return EINVAL; + } error = BCOPYIN(data, &obj, sizeof(obj)); - if (error != 0) + if (error != 0) { + IPFERROR(125); return EFAULT; + } - if (obj.ipfo_type != type) + if (obj.ipfo_type != type) { + IPFERROR(58); return EINVAL; + } -#ifndef IPFILTER_COMPAT - if (obj.ipfo_size != sz) - 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 - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if (obj.ipfo_size != sz) - /* XXX compatibility hook here */ - return EINVAL; + IPFERROR(60); + error = EINVAL; #endif - - error = COPYIN(obj.ipfo_ptr, ptr, sz); - if (error != 0) - error = EFAULT; + } return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_outobjsz */ +/* 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 fr_outobj, except the size of the object to copy out is passed in */ +/* 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 fr_outobj(). */ +/* not possible nor required in ipf_outobj(). */ /* ------------------------------------------------------------------------ */ -int fr_outobjsz(data, ptr, type, sz) -void *data; -void *ptr; -int type, sz; +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) || - ((fr_objbytes[type][0] & 1) == 0) || - (sz < fr_objbytes[type][1])) + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(62); return EINVAL; + } error = BCOPYIN(data, &obj, sizeof(obj)); - if (error != 0) + if (error != 0) { + IPFERROR(127); return EFAULT; + } - if (obj.ipfo_type != type) + if (obj.ipfo_type != type) { + IPFERROR(63); return EINVAL; + } -#ifndef IPFILTER_COMPAT - if (obj.ipfo_size != sz) - 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 - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if (obj.ipfo_size != sz) - /* XXX compatibility hook here */ - return EINVAL; + IPFERROR(65); + error = EINVAL; #endif - - error = COPYOUT(ptr, obj.ipfo_ptr, sz); - if (error != 0) - error = EFAULT; + } return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_outobj */ +/* Function: ipf_outobj */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ @@ -5646,75 +6491,134 @@ int type, sz; /* future, we add things to check for version numbers, sizes, etc, to make */ /* it backward compatible at the ABI for user land. */ /* ------------------------------------------------------------------------ */ -int fr_outobj(data, ptr, type) -void *data; -void *ptr; -int type; +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)) + if ((type < 0) || (type >= IPFOBJ_COUNT)) { + IPFERROR(67); return EINVAL; + } error = BCOPYIN(data, &obj, sizeof(obj)); - if (error != 0) + if (error != 0) { + IPFERROR(126); return EFAULT; + } - if (obj.ipfo_type != type) + if (obj.ipfo_type != type) { + IPFERROR(68); return EINVAL; + } -#ifndef IPFILTER_COMPAT - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) + 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; - } else if (obj.ipfo_size != fr_objbytes[type][1]) - 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 - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; + IPFERROR(72); + error = EINVAL; #endif + } + return error; +} - error = COPYOUT(ptr, obj.ipfo_ptr, obj.ipfo_size); - if (error != 0) - error = EFAULT; + +/* ------------------------------------------------------------------------ */ +/* 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: fr_checkl4sum */ +/* 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. */ +/* way that is ditinguishable. This function should only be called by the */ +/* ipf_checkv6sum() for each platform. */ /* ------------------------------------------------------------------------ */ -int fr_checkl4sum(fin) -fr_info_t *fin; +INLINE int +ipf_checkl4sum(fin) + fr_info_t *fin; { u_short sum, hdrsum, *csump; udphdr_t *udp; int dosum; - if ((fin->fin_flx & FI_NOCKSUM) != 0) - return 0; - - if (fin->fin_cksum == 1) - return 0; - - if (fin->fin_cksum == -1) - return -1; - /* * If the TCP packet isn't a fragment, isn't too short and otherwise * isn't already considered "bad", then validate the checksum. If @@ -5728,48 +6632,44 @@ fr_info_t *fin; dosum = 0; sum = 0; -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID) - if (dohwcksum && ((*fin->fin_mp)->b_ick_flag == ICK_VALID)) { - hdrsum = 0; - sum = 0; - } else { -#endif - switch (fin->fin_p) - { - case IPPROTO_TCP : - csump = &((tcphdr_t *)fin->fin_dp)->th_sum; + 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; + } + 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; + case IPPROTO_ICMP : + csump = &((struct icmp *)fin->fin_dp)->icmp_cksum; + dosum = 1; + break; - default : - return 1; - /*NOTREACHED*/ - } + default : + return 1; + /*NOTREACHED*/ + } - if (csump != NULL) - hdrsum = *csump; + if (csump != NULL) + hdrsum = *csump; - if (dosum) { - sum = fr_cksum(fin->fin_m, fin->fin_ip, - fin->fin_p, fin->fin_dp, - fin->fin_dlen + fin->fin_hlen); - } -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID) + if (dosum) { + sum = fr_cksum(fin, fin->fin_ip, fin->fin_p, fin->fin_dp); } -#endif #if !defined(_KERNEL) if (sum == hdrsum) { FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum)); @@ -5777,17 +6677,18 @@ fr_info_t *fin; FR_DEBUG(("checkl4sum: %hx != %hx\n", sum, hdrsum)); } #endif + DT2(l4sums, u_short, hdrsum, u_short, sum); if (hdrsum == sum) { - fin->fin_cksum = 1; + fin->fin_cksum = FI_CK_SUMOK; return 0; } - fin->fin_cksum = -1; + fin->fin_cksum = FI_CK_BAD; return -1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ifpfillv4addr */ +/* 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 */ @@ -5802,10 +6703,11 @@ fr_info_t *fin; /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ -int fr_ifpfillv4addr(atype, sin, mask, inp, inpmask) -int atype; -struct sockaddr_in *sin, *mask; -struct in_addr *inp, *inpmask; +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; @@ -5826,7 +6728,7 @@ struct in_addr *inp, *inpmask; #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ -/* Function: fr_ifpfillv6addr */ +/* 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 */ @@ -5841,44 +6743,43 @@ struct in_addr *inp, *inpmask; /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ -int fr_ifpfillv6addr(atype, sin, mask, inp, inpmask) -int atype; -struct sockaddr_in6 *sin, *mask; -struct in_addr *inp, *inpmask; +int +ipf_ifpfillv6addr(atype, sin, mask, inp, inpmask) + int atype; + struct sockaddr_in6 *sin, *mask; + i6addr_t *inp, *inpmask; { - i6addr_t *src, *dst, *and, *dmask; + i6addr_t *src, *and; src = (i6addr_t *)&sin->sin6_addr; and = (i6addr_t *)&mask->sin6_addr; - dst = (i6addr_t *)inp; - dmask = (i6addr_t *)inpmask; if (inpmask != NULL && atype != FRI_NETMASKED) { - dmask->i6[0] = 0xffffffff; - dmask->i6[1] = 0xffffffff; - dmask->i6[2] = 0xffffffff; - dmask->i6[3] = 0xffffffff; + 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; - dmask->i6[0] = and->i6[0]; - dmask->i6[1] = and->i6[1]; - dmask->i6[2] = and->i6[2]; - dmask->i6[3] = and->i6[3]; + inpmask->i6[0] = and->i6[0]; + inpmask->i6[1] = and->i6[1]; + inpmask->i6[2] = and->i6[2]; + inpmask->i6[3] = and->i6[3]; } - dst->i6[0] = src->i6[0] & and->i6[0]; - dst->i6[1] = src->i6[1] & and->i6[1]; - dst->i6[2] = src->i6[2] & and->i6[2]; - dst->i6[3] = src->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 { - dst->i6[0] = src->i6[0]; - dst->i6[1] = src->i6[1]; - dst->i6[2] = src->i6[2]; - dst->i6[3] = src->i6[3]; + 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; } @@ -5886,7 +6787,7 @@ struct in_addr *inp, *inpmask; /* ------------------------------------------------------------------------ */ -/* Function: fr_matchtag */ +/* 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 */ @@ -5898,8 +6799,9 @@ struct in_addr *inp, *inpmask; /* comparison. This function should only be called with both tag1 and tag2 */ /* as non-NULL pointers. */ /* ------------------------------------------------------------------------ */ -int fr_matchtag(tag1, tag2) -ipftag_t *tag1, *tag2; +int +ipf_matchtag(tag1, tag2) + ipftag_t *tag1, *tag2; { if (tag1 == tag2) return 1; @@ -5917,16 +6819,18 @@ ipftag_t *tag1, *tag2; /* ------------------------------------------------------------------------ */ -/* Function: fr_coalesce */ +/* 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 fr_coalesce(fin) -fr_info_t *fin; +int +ipf_coalesce(fin) + fr_info_t *fin; { + if ((fin->fin_flx & FI_COALESCE) != 0) return 1; @@ -5938,11 +6842,15 @@ fr_info_t *fin; return 0; #if defined(_KERNEL) - if (fr_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) { - ATOMIC_INCL(fr_badcoalesces[fin->fin_out]); + 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; @@ -5967,114 +6875,10 @@ fr_info_t *fin; * The obvious implication is if neither of these are set then the value can be * changed at any time without harm. */ -ipftuneable_t ipf_tuneables[] = { - /* filtering */ - { { &fr_flags }, "fr_flags", 0, 0xffffffff, - sizeof(fr_flags), 0, NULL }, - { { &fr_active }, "fr_active", 0, 0, - sizeof(fr_active), IPFT_RDONLY, NULL }, - { { &fr_control_forwarding }, "fr_control_forwarding", 0, 1, - sizeof(fr_control_forwarding), 0, NULL }, - { { &fr_update_ipid }, "fr_update_ipid", 0, 1, - sizeof(fr_update_ipid), 0, NULL }, - { { &fr_chksrc }, "fr_chksrc", 0, 1, - sizeof(fr_chksrc), 0, NULL }, - { { &fr_minttl }, "fr_minttl", 0, 1, - sizeof(fr_minttl), 0, NULL }, - { { &fr_icmpminfragmtu }, "fr_icmpminfragmtu", 0, 1, - sizeof(fr_icmpminfragmtu), 0, NULL }, - { { &fr_pass }, "fr_pass", 0, 0xffffffff, - sizeof(fr_pass), 0, NULL }, - /* state */ - { { &fr_tcpidletimeout }, "fr_tcpidletimeout", 1, 0x7fffffff, - sizeof(fr_tcpidletimeout), IPFT_WRDISABLED, NULL }, - { { &fr_tcpclosewait }, "fr_tcpclosewait", 1, 0x7fffffff, - sizeof(fr_tcpclosewait), IPFT_WRDISABLED, NULL }, - { { &fr_tcplastack }, "fr_tcplastack", 1, 0x7fffffff, - sizeof(fr_tcplastack), IPFT_WRDISABLED, NULL }, - { { &fr_tcptimeout }, "fr_tcptimeout", 1, 0x7fffffff, - sizeof(fr_tcptimeout), IPFT_WRDISABLED, NULL }, - { { &fr_tcpclosed }, "fr_tcpclosed", 1, 0x7fffffff, - sizeof(fr_tcpclosed), IPFT_WRDISABLED, NULL }, - { { &fr_tcphalfclosed }, "fr_tcphalfclosed", 1, 0x7fffffff, - sizeof(fr_tcphalfclosed), IPFT_WRDISABLED, NULL }, - { { &fr_udptimeout }, "fr_udptimeout", 1, 0x7fffffff, - sizeof(fr_udptimeout), IPFT_WRDISABLED, NULL }, - { { &fr_udpacktimeout }, "fr_udpacktimeout", 1, 0x7fffffff, - sizeof(fr_udpacktimeout), IPFT_WRDISABLED, NULL }, - { { &fr_icmptimeout }, "fr_icmptimeout", 1, 0x7fffffff, - sizeof(fr_icmptimeout), IPFT_WRDISABLED, NULL }, - { { &fr_icmpacktimeout }, "fr_icmpacktimeout", 1, 0x7fffffff, - sizeof(fr_icmpacktimeout), IPFT_WRDISABLED, NULL }, - { { &fr_iptimeout }, "fr_iptimeout", 1, 0x7fffffff, - sizeof(fr_iptimeout), IPFT_WRDISABLED, NULL }, - { { &fr_statemax }, "fr_statemax", 1, 0x7fffffff, - sizeof(fr_statemax), 0, NULL }, - { { &fr_statesize }, "fr_statesize", 1, 0x7fffffff, - sizeof(fr_statesize), IPFT_WRDISABLED, NULL }, - { { &fr_state_lock }, "fr_state_lock", 0, 1, - sizeof(fr_state_lock), IPFT_RDONLY, NULL }, - { { &fr_state_maxbucket }, "fr_state_maxbucket", 1, 0x7fffffff, - sizeof(fr_state_maxbucket), IPFT_WRDISABLED, NULL }, - { { &fr_state_maxbucket_reset }, "fr_state_maxbucket_reset", 0, 1, - sizeof(fr_state_maxbucket_reset), IPFT_WRDISABLED, NULL }, - { { &ipstate_logging }, "ipstate_logging", 0, 1, - sizeof(ipstate_logging), 0, NULL }, - /* nat */ - { { &fr_nat_lock }, "fr_nat_lock", 0, 1, - sizeof(fr_nat_lock), IPFT_RDONLY, NULL }, - { { &ipf_nattable_sz }, "ipf_nattable_sz", 1, 0x7fffffff, - sizeof(ipf_nattable_sz), IPFT_WRDISABLED, NULL }, - { { &ipf_nattable_max }, "ipf_nattable_max", 1, 0x7fffffff, - sizeof(ipf_nattable_max), 0, NULL }, - { { &ipf_natrules_sz }, "ipf_natrules_sz", 1, 0x7fffffff, - sizeof(ipf_natrules_sz), IPFT_WRDISABLED, NULL }, - { { &ipf_rdrrules_sz }, "ipf_rdrrules_sz", 1, 0x7fffffff, - sizeof(ipf_rdrrules_sz), IPFT_WRDISABLED, NULL }, - { { &ipf_hostmap_sz }, "ipf_hostmap_sz", 1, 0x7fffffff, - sizeof(ipf_hostmap_sz), IPFT_WRDISABLED, NULL }, - { { &fr_nat_maxbucket }, "fr_nat_maxbucket", 1, 0x7fffffff, - sizeof(fr_nat_maxbucket), 0, NULL }, - { { &fr_nat_maxbucket_reset }, "fr_nat_maxbucket_reset", 0, 1, - sizeof(fr_nat_maxbucket_reset), IPFT_WRDISABLED, NULL }, - { { &nat_logging }, "nat_logging", 0, 1, - sizeof(nat_logging), 0, NULL }, - { { &fr_defnatage }, "fr_defnatage", 1, 0x7fffffff, - sizeof(fr_defnatage), IPFT_WRDISABLED, NULL }, - { { &fr_defnatipage }, "fr_defnatipage", 1, 0x7fffffff, - sizeof(fr_defnatipage), IPFT_WRDISABLED, NULL }, - { { &fr_defnaticmpage }, "fr_defnaticmpage", 1, 0x7fffffff, - sizeof(fr_defnaticmpage), IPFT_WRDISABLED, NULL }, - { { &fr_nat_doflush }, "fr_nat_doflush", 0, 1, - sizeof(fr_nat_doflush), 0, NULL }, - /* proxy */ - { { &ipf_proxy_debug }, "ipf_proxy_debug", 0, 10, - sizeof(ipf_proxy_debug), 0, 0 }, - /* frag */ - { { &ipfr_size }, "ipfr_size", 1, 0x7fffffff, - sizeof(ipfr_size), IPFT_WRDISABLED, NULL }, - { { &fr_ipfrttl }, "fr_ipfrttl", 1, 0x7fffffff, - sizeof(fr_ipfrttl), IPFT_WRDISABLED, NULL }, -#ifdef IPFILTER_LOG - /* log */ - { { &ipl_suppress }, "ipl_suppress", 0, 1, - sizeof(ipl_suppress), 0, NULL }, - { { &ipl_logmax }, "ipl_logmax", 0, 0x7fffffff, - sizeof(ipl_logmax), IPFT_WRDISABLED, NULL }, - { { &ipl_logall }, "ipl_logall", 0, 1, - sizeof(ipl_logall), 0, NULL }, - { { &ipl_logsize }, "ipl_logsize", 0, 0x80000, - sizeof(ipl_logsize), 0, NULL }, -#endif - { { NULL }, NULL, 0, 0, - 0, 0, NULL } -}; - -static ipftuneable_t *ipf_tunelist = NULL; /* ------------------------------------------------------------------------ */ -/* Function: fr_findtunebycookie */ +/* 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 */ @@ -6085,12 +6889,14 @@ static ipftuneable_t *ipf_tunelist = NULL; /* 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 *fr_findtunebycookie(cookie, next) -void *cookie, **next; +static ipftuneable_t * +ipf_tune_findbycookie(ptop, cookie, next) + ipftuneable_t **ptop; + void *cookie, **next; { ipftuneable_t *ta, **tap; - for (ta = ipf_tuneables; ta->ipft_name != NULL; ta++) + for (ta = *ptop; ta->ipft_name != NULL; ta++) if (ta == cookie) { if (next != NULL) { /* @@ -6104,12 +6910,12 @@ void *cookie, **next; if ((ta + 1)->ipft_name != NULL) *next = ta + 1; else - *next = &ipf_tunelist; + *next = ptop; } return ta; } - for (tap = &ipf_tunelist; (ta = *tap) != NULL; tap = &ta->ipft_next) + for (tap = ptop; (ta = *tap) != NULL; tap = &ta->ipft_next) if (tap == cookie) { if (next != NULL) *next = &ta->ipft_next; @@ -6123,7 +6929,7 @@ void *cookie, **next; /* ------------------------------------------------------------------------ */ -/* Function: fr_findtunebyname */ +/* Function: ipf_tune_findbyname */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: name(I) - name of the tuneable entry to find. */ /* */ @@ -6131,44 +6937,192 @@ void *cookie, **next; /* for an entry with a matching name. If we can find one, return a pointer */ /* to the matching structure. */ /* ------------------------------------------------------------------------ */ -static ipftuneable_t *fr_findtunebyname(name) -const char *name; +static ipftuneable_t * +ipf_tune_findbyname(top, name) + ipftuneable_t *top; + const char *name; { ipftuneable_t *ta; - for (ta = ipf_tuneables; ta->ipft_name != NULL; ta++) + for (ta = top; ta != NULL; ta = ta->ipft_next) if (!strcmp(ta->ipft_name, name)) { return ta; } - for (ta = ipf_tunelist; 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 NULL; + return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_addipftune */ +/* 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 struct to add to tuneables */ +/* Parameters: newtune - pointer to new tune entry to add to tuneables */ /* */ -/* Appends the tune structure pointer to by "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". */ +/* 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 fr_addipftune(newtune) -ipftuneable_t *newtune; +int +ipf_tune_add(softc, newtune) + ipf_main_softc_t *softc; + ipftuneable_t *newtune; { ipftuneable_t *ta, **tap; - ta = fr_findtunebyname(newtune->ipft_name); - if (ta != NULL) + ta = ipf_tune_findbyname(softc->ipf_tuners, newtune->ipft_name); + if (ta != NULL) { + IPFERROR(74); return EEXIST; + } - for (tap = &ipf_tunelist; *tap != NULL; tap = &(*tap)->ipft_next) + for (tap = &softc->ipf_tuners; *tap != NULL; tap = &(*tap)->ipft_next) ; newtune->ipft_next = NULL; @@ -6178,33 +7132,72 @@ ipftuneable_t *newtune; /* ------------------------------------------------------------------------ */ -/* Function: fr_delipftune */ +/* Function: ipf_tune_del */ /* Returns: int - 0 == success, else failure */ -/* Parameters: oldtune - pointer to tune struct to remove from the list of */ +/* 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 fr_delipftune(oldtune) -ipftuneable_t *oldtune; +int +ipf_tune_del(softc, oldtune) + ipf_main_softc_t *softc; + ipftuneable_t *oldtune; { ipftuneable_t *ta, **tap; + int error = 0; - for (tap = &ipf_tunelist; (ta = *tap) != NULL; tap = &ta->ipft_next) + for (tap = &softc->ipf_tuners; (ta = *tap) != NULL; + tap = &ta->ipft_next) { if (ta == oldtune) { *tap = oldtune->ipft_next; oldtune->ipft_next = NULL; - return 0; + break; } + } - return ESRCH; + if (ta == NULL) { + error = ESRCH; + IPFERROR(75); + } + return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipftune */ +/* 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 */ @@ -6216,16 +7209,18 @@ ipftuneable_t *oldtune; /* and 'destruction' routines of the various components of ipfilter are all */ /* each responsible for handling their own values being too big. */ /* ------------------------------------------------------------------------ */ -int fr_ipftune(cmd, data) -ioctlcmd_t cmd; -void *data; +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 = fr_inobj(data, &tu, IPFOBJ_TUNEABLE); + error = ipf_inobj(softc, data, NULL, &tu, IPFOBJ_TUNEABLE); if (error != 0) return error; @@ -6246,9 +7241,10 @@ void *data; * at the front of the list. */ if (cookie != NULL) { - ta = fr_findtunebycookie(cookie, &tu.ipft_cookie); + ta = ipf_tune_findbycookie(&softc->ipf_tuners, + cookie, &tu.ipft_cookie); } else { - ta = ipf_tuneables; + ta = softc->ipf_tuners; tu.ipft_cookie = ta + 1; } if (ta != NULL) { @@ -6256,8 +7252,10 @@ void *data; * 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)) + if (ta->ipft_sz > sizeof(tu.ipft_un)) { + IPFERROR(76); return EINVAL; + } tu.ipft_vlong = 0; if (ta->ipft_sz == sizeof(u_long)) @@ -6277,7 +7275,7 @@ void *data; MIN(sizeof(tu.ipft_name), strlen(ta->ipft_name) + 1)); } - error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE); + error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); break; case SIOCIPFGET : @@ -6286,13 +7284,16 @@ void *data; * Search by name or by cookie value for a particular entry * in the tuning paramter table. */ + IPFERROR(77); error = ESRCH; if (cookie != NULL) { - ta = fr_findtunebycookie(cookie, NULL); + ta = ipf_tune_findbycookie(&softc->ipf_tuners, + cookie, NULL); if (ta != NULL) error = 0; } else if (tu.ipft_name[0] != '\0') { - ta = fr_findtunebyname(tu.ipft_name); + ta = ipf_tune_findbyname(softc->ipf_tuners, + tu.ipft_name); if (ta != NULL) error = 0; } @@ -6317,7 +7318,7 @@ void *data; tu.ipft_min = ta->ipft_min; tu.ipft_max = ta->ipft_max; tu.ipft_flags = ta->ipft_flags; - error = fr_outobj(data, &tu, IPFOBJ_TUNEABLE); + error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } else if (cmd == (ioctlcmd_t)SIOCIPFSET) { /* @@ -6328,35 +7329,49 @@ void *data; u_long in; if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) && - (fr_running > 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_sz == sizeof(u_long)) { + 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 = fr_outobj(data, &tu, IPFOBJ_TUNEABLE); + error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } break; default : + IPFERROR(80); error = EINVAL; break; } @@ -6366,109 +7381,7 @@ void *data; /* ------------------------------------------------------------------------ */ -/* Function: fr_initialise */ -/* Returns: int - 0 == success, < 0 == failure */ -/* Parameters: None. */ -/* */ -/* Call of the initialise functions for all the various subsystems inside */ -/* of IPFilter. If any of them should fail, return immeadiately a failure */ -/* BUT do not try to recover from the error here. */ -/* ------------------------------------------------------------------------ */ -int fr_initialise() -{ - int i; - - bzero(&frstats, sizeof(frstats)); - -#ifdef IPFILTER_LOG - i = fr_loginit(); - if (i < 0) - return -10 + i; -#endif - i = fr_natinit(); - if (i < 0) - return -20 + i; - - i = fr_stateinit(); - if (i < 0) - return -30 + i; - - i = fr_authinit(); - if (i < 0) - return -40 + i; - - i = fr_fraginit(); - if (i < 0) - return -50 + i; - - i = appr_init(); - if (i < 0) - return -60 + i; - -#ifdef IPFILTER_SYNC - i = ipfsync_init(); - if (i < 0) - return -70 + i; -#endif -#ifdef IPFILTER_SCAN - i = ipsc_init(); - if (i < 0) - return -80 + i; -#endif -#ifdef IPFILTER_LOOKUP - i = ip_lookup_init(); - if (i < 0) - return -90 + i; -#endif -#ifdef IPFILTER_COMPILED - ipfrule_add(); -#endif - return 0; -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_deinitialise */ -/* Returns: None. */ -/* Parameters: None. */ -/* */ -/* Call all the various subsystem cleanup routines to deallocate memory or */ -/* destroy locks or whatever they've done that they need to now undo. */ -/* The order here IS important as there are some cross references of */ -/* internal data structures. */ -/* ------------------------------------------------------------------------ */ -void fr_deinitialise() -{ - fr_fragunload(); - fr_authunload(); - fr_natunload(); - fr_stateunload(); -#ifdef IPFILTER_SCAN - fr_scanunload(); -#endif - appr_unload(); - -#ifdef IPFILTER_COMPILED - ipfrule_remove(); -#endif - - (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); - (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE); - (void) frflush(IPL_LOGCOUNT, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); - (void) frflush(IPL_LOGCOUNT, 0, FR_INQUE|FR_OUTQUE); - -#ifdef IPFILTER_LOOKUP - ip_lookup_unload(); -#endif - -#ifdef IPFILTER_LOG - fr_logunload(); -#endif -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_zerostats */ +/* Function: ipf_zerostats */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(O) - pointer to pointer for copying data back to */ /* */ @@ -6476,30 +7389,38 @@ void fr_deinitialise() /* current ones in the kernel. The lock is only held across the bzero() as */ /* the copyout may result in paging (ie network activity.) */ /* ------------------------------------------------------------------------ */ -int fr_zerostats(data) -void *data; +int +ipf_zerostats(softc, data) + ipf_main_softc_t *softc; + caddr_t data; { friostat_t fio; + ipfobj_t obj; int error; - fr_getstat(&fio); - error = fr_outobj(data, &fio, IPFOBJ_IPFSTAT); - if (error) - return EFAULT; + 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(&ipf_mutex); - bzero(&frstats, sizeof(frstats)); - RWLOCK_EXIT(&ipf_mutex); + WRITE_ENTER(&softc->ipf_mutex); + bzero(&softc->ipf_stats, sizeof(softc->ipf_stats)); + RWLOCK_EXIT(&softc->ipf_mutex); return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_resolvedest */ +/* Function: ipf_resolvedest */ /* Returns: Nil */ -/* Parameters: fdp(IO) - pointer to destination information to resolve */ -/* v(I) - IP protocol version to match */ +/* 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 */ @@ -6507,52 +7428,66 @@ void *data; /* found, then set the interface pointer to be -1 as NULL is considered to */ /* indicate there is no information at all in the structure. */ /* ------------------------------------------------------------------------ */ -void fr_resolvedest(fdp, v) -frdest_t *fdp; -int v; +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; - v = v; /* LINT */ - if (*fdp->fd_ifname != '\0') { - ifp = GETIFP(fdp->fd_ifname, v); - if (ifp == NULL) - ifp = (void *)-1; + 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_ifp = ifp; + fdp->fd_ptr = ifp; + + if ((ifp != NULL) && (ifp != (void *)-1)) { + fdp->fd_local = ipf_deliverlocal(softc, v, ifp, &fdp->fd_ip6); + } + + return errval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_resolvenic */ +/* Function: ipf_resolvenic */ /* Returns: void* - NULL = wildcard name, -1 = failed to find NIC, else */ /* pointer to interface structure for NIC */ -/* Parameters: name(I) - complete interface name */ +/* 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. */ -/* */ -/* One might wonder why name gets terminated with a \0 byte in here. The */ -/* reason is an interface name could get into the kernel structures of ipf */ -/* in any number of ways and so long as they all use the same sized array */ -/* to put the name in, it makes sense to ensure it gets null terminated */ -/* before it is used for its intended purpose - finding its match in the */ -/* kernel's list of configured interfaces. */ -/* */ -/* NOTE: This SHOULD ONLY be used with IPFilter structures that have an */ -/* array for the name that is LIFNAMSIZ bytes (at least) in length. */ /* ------------------------------------------------------------------------ */ -void *fr_resolvenic(name, v) -char *name; -int v; +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; @@ -6560,8 +7495,6 @@ int v; return NULL; } - name[LIFNAMSIZ - 1] = '\0'; - nic = GETIFP(name, v); if (nic == NULL) nic = (void *)-1; @@ -6569,68 +7502,121 @@ int v; } -ipftoken_t *ipftokenhead = NULL, **ipftokentail = &ipftokenhead; - - /* ------------------------------------------------------------------------ */ -/* Function: ipf_expiretokens */ +/* Function: ipf_token_expire */ /* Returns: None. */ -/* Parameters: 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_expiretokens() +void +ipf_token_expire(softc) + ipf_main_softc_t *softc; { ipftoken_t *it; - WRITE_ENTER(&ipf_tokens); - while ((it = ipftokenhead) != NULL) { - if (it->ipt_die > fr_ticks) + WRITE_ENTER(&softc->ipf_tokens); + while ((it = softc->ipf_token_head) != NULL) { + if (it->ipt_die > softc->ipf_ticks) break; - ipf_freetoken(it); + 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(&ipf_tokens); + RWLOCK_EXIT(&softc->ipf_tokens); } /* ------------------------------------------------------------------------ */ -/* Function: ipf_deltoken */ +/* Function: ipf_token_del */ /* Returns: int - 0 = success, else error */ -/* Parameters: type(I) - the token type to match */ +/* 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_freetoken() to remove it from the list. */ +/* 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_deltoken(type, uid, ptr) -int type, uid; -void *ptr; +int +ipf_token_del(softc, type, uid, ptr) + ipf_main_softc_t *softc; + int type, uid; + void *ptr; { ipftoken_t *it; - int error = ESRCH; + int error; - WRITE_ENTER(&ipf_tokens); - for (it = ipftokenhead; it != NULL; it = it->ipt_next) + 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) { - ipf_freetoken(it); + it->ipt_complete = 2; + ipf_token_deref(softc, it); error = 0; break; + } } - RWLOCK_EXIT(&ipf_tokens); + RWLOCK_EXIT(&softc->ipf_tokens); return error; } /* ------------------------------------------------------------------------ */ -/* Function: ipf_findtoken */ +/* 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: type(I) - the token type to match */ +/* 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 */ /* */ @@ -6638,97 +7624,115 @@ void *ptr; /* 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. */ -/* */ -/* NOTE: It is by design that this function returns holding a read lock on */ -/* ipf_tokens. Callers must make sure they release it! */ /* ------------------------------------------------------------------------ */ -ipftoken_t *ipf_findtoken(type, uid, ptr) -int type, uid; -void *ptr; +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(&ipf_tokens); - for (it = ipftokenhead; it != NULL; it = it->ipt_next) { - if (it->ipt_alive == 0) - continue; - if (ptr == it->ipt_ctx && type == it->ipt_type && - uid == it->ipt_uid) + 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) + if (it == NULL) { + RWLOCK_EXIT(&softc->ipf_tokens); return NULL; - it->ipt_data = NULL; + } it->ipt_ctx = ptr; it->ipt_uid = uid; it->ipt_type = type; - it->ipt_next = NULL; - it->ipt_alive = 1; + it->ipt_ref = 1; } else { if (new != NULL) { KFREE(new); new = NULL; } - ipf_unlinktoken(it); + if (it->ipt_complete > 0) + it = NULL; + else + ipf_token_unlink(softc, it); } - it->ipt_pnext = ipftokentail; - *ipftokentail = it; - ipftokentail = &it->ipt_next; - it->ipt_next = NULL; - it->ipt_die = fr_ticks + 2; + 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++; - MUTEX_DOWNGRADE(&ipf_tokens); + it->ipt_die = softc->ipf_ticks + 20; + } + + RWLOCK_EXIT(&softc->ipf_tokens); return it; } /* ------------------------------------------------------------------------ */ -/* Function: ipf_unlinktoken */ +/* Function: ipf_token_unlink */ /* Returns: None. */ -/* Parameters: token(I) - pointer to token structure */ +/* 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_unlinktoken(token) -ipftoken_t *token; +static void +ipf_token_unlink(softc, token) + ipf_main_softc_t *softc; + ipftoken_t *token; { - if (ipftokentail == &token->ipt_next) - ipftokentail = token->ipt_pnext; + 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_freetoken */ -/* Returns: None. */ -/* Parameters: token(I) - pointer to token structure */ +/* 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 */ /* */ -/* This function unlinks a token from the linked list and on the path to */ -/* free'ing the data, it calls the dereference function that is associated */ -/* with the type of data pointed to by the token as it is considered to */ -/* hold a reference to it. */ +/* 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. */ /* ------------------------------------------------------------------------ */ -void ipf_freetoken(token) -ipftoken_t *token; +int +ipf_token_deref(softc, token) + ipf_main_softc_t *softc; + ipftoken_t *token; { void *data, **datap; - ipf_unlinktoken(token); + ASSERT(token->ipt_ref > 0); + token->ipt_ref--; + if (token->ipt_ref > 0) + return token->ipt_ref; data = token->ipt_data; datap = &data; @@ -6737,54 +7741,96 @@ ipftoken_t *token; switch (token->ipt_type) { case IPFGENITER_IPF : - (void) fr_derefrule((frentry_t **)datap); + (void) ipf_derefrule(softc, (frentry_t **)datap); break; case IPFGENITER_IPNAT : - WRITE_ENTER(&ipf_nat); - fr_ipnatderef((ipnat_t **)datap); - RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_rule_deref(softc, (ipnat_t **)datap); + RWLOCK_EXIT(&softc->ipf_nat); break; case IPFGENITER_NAT : - fr_natderef((nat_t **)datap); + ipf_nat_deref(softc, (nat_t **)datap); break; case IPFGENITER_STATE : - fr_statederef((ipstate_t **)datap); + ipf_state_deref(softc, (ipstate_t **)datap); break; case IPFGENITER_FRAG : -#ifdef USE_MUTEXES - fr_fragderef((ipfr_t **)datap, &ipf_frag); -#else - fr_fragderef((ipfr_t **)datap); -#endif + ipf_frag_pkt_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_NATFRAG : -#ifdef USE_MUTEXES - fr_fragderef((ipfr_t **)datap, &ipf_natfrag); -#else - fr_fragderef((ipfr_t **)datap); -#endif + ipf_frag_nat_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_HOSTMAP : - WRITE_ENTER(&ipf_nat); - fr_hostmapdel((hostmap_t **)datap); - RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_hostmapdel(softc, (hostmap_t **)datap); + RWLOCK_EXIT(&softc->ipf_nat); break; default : -#ifdef IPFILTER_LOOKUP - ip_lookup_iterderef(token->ipt_type, data); -#endif + 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: t(I) - pointer to destination information to resolve */ +/* 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 */ @@ -6795,47 +7841,72 @@ ipftoken_t *token; /* 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(ipftoken_t *t, void *ptr) +int +ipf_getnextrule(softc, t, ptr) + ipf_main_softc_t *softc; + ipftoken_t *t; + void *ptr; { frentry_t *fr, *next, zero; - int error, count, out; ipfruleiter_t it; + int error, out; frgroup_t *fg; + ipfobj_t obj; + int predict; char *dst; + int unit; - if (t == NULL || ptr == NULL) + if (t == NULL || ptr == NULL) { + IPFERROR(84); return EFAULT; - error = fr_inobj(ptr, &it, IPFOBJ_IPFITER); + } + + error = ipf_inobj(softc, ptr, &obj, &it, IPFOBJ_IPFITER); if (error != 0) return error; - if ((it.iri_inout < 0) || (it.iri_inout > 3)) + + if ((it.iri_inout < 0) || (it.iri_inout > 3)) { + IPFERROR(85); return EINVAL; - if ((it.iri_active != 0) && (it.iri_active != 1)) + } + if ((it.iri_active != 0) && (it.iri_active != 1)) { + IPFERROR(86); return EINVAL; - if (it.iri_nrules == 0) + } + if (it.iri_nrules == 0) { + IPFERROR(87); return ENOSPC; - if (it.iri_rule == NULL) + } + if (it.iri_rule == NULL) { + IPFERROR(88); return EFAULT; + } - out = it.iri_inout & F_OUT; + fg = NULL; fr = t->ipt_data; - READ_ENTER(&ipf_mutex); + 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 ((it.iri_inout & F_ACIN) != 0) { - if (it.iri_v == 4) - next = ipacct[out][it.iri_active]; - else - next = ipacct6[out][it.iri_active]; + if (unit == IPL_LOGCOUNT) { + next = softc->ipf_acct[out][it.iri_active]; } else { - if (it.iri_v == 4) - next = ipfilter[out][it.iri_active]; - else - next = ipfilter6[out][it.iri_active]; + next = softc->ipf_rules[out][it.iri_active]; } + if (next == NULL) + next = ipf_nextrule(softc, it.iri_active, + unit, NULL, out); } else { - fg = fr_findgroup(it.iri_group, IPL_LOGIPF, - it.iri_active, NULL); + fg = ipf_findgroup(softc, it.iri_group, unit, + it.iri_active, NULL); if (fg != NULL) next = fg->fg_start; else @@ -6843,113 +7914,133 @@ int ipf_getnextrule(ipftoken_t *t, void *ptr) } } 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; - count = it.iri_nrules; - /* - * The ipfruleiter may ask for more than 1 rule at a time to be - * copied out, so long as that many exist in the list to start with! - */ - for (;;) { - if (next != NULL) { - if (count == 1) { - MUTEX_ENTER(&next->fr_lock); - next->fr_ref++; - MUTEX_EXIT(&next->fr_lock); - t->ipt_data = next; - } - } else { - bzero(&zero, sizeof(zero)); - next = &zero; - count = 1; - t->ipt_data = NULL; - } - RWLOCK_EXIT(&ipf_mutex); - error = COPYOUT(next, dst, sizeof(*next)); - if (error != 0) - return EFAULT; + 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) { - dst += sizeof(*next); - error = COPYOUT(next->fr_data, dst, next->fr_dsize); - if (error != 0) - error = EFAULT; + ipfobj_t dobj; + + if (next->fr_type == FR_T_IPFEXPR) + dobj.ipfo_type = IPFOBJ_IPFEXPR; else - dst += next->fr_dsize; + 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 ((count == 1) || (error != 0)) - break; - - count--; - - READ_ENTER(&ipf_mutex); - next = next->fr_next; } - if (fr != NULL) { - (void) fr_derefrule(&fr); - } + if ((fr != NULL) && (next == &zero)) + (void) ipf_derefrule(softc, &fr); return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_frruleiter */ +/* Function: ipf_frruleiter */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - the token type to match */ +/* 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 fr_ipf_ioctl and */ +/* 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(data, uid, ctx) -void *data, *ctx; -int uid; +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_findtoken(IPFGENITER_IPF, uid, ctx); - if (token != NULL) - error = ipf_getnextrule(token, data); - else - error = EFAULT; - RWLOCK_EXIT(&ipf_tokens); + 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: fr_geniter */ +/* Function: ipf_geniter */ /* Returns: int - 0 = success, else error */ -/* Parameters: token(I) - pointer to ipftoken_t structure */ -/* itp(I) - */ +/* 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(token, itp) -ipftoken_t *token; -ipfgeniter_t *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 : -#ifdef USE_MUTEXES - error = fr_nextfrag(token, itp, - &ipfr_list, &ipfr_tail, &ipf_frag); -#else - error = fr_nextfrag(token, itp, &ipfr_list, &ipfr_tail); -#endif + error = ipf_frag_pkt_next(softc, token, itp); break; default : + IPFERROR(92); error = EINVAL; break; } @@ -6959,41 +8050,50 @@ ipfgeniter_t *itp; /* ------------------------------------------------------------------------ */ -/* Function: fr_genericiter */ +/* Function: ipf_genericiter */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - the token type to match */ +/* 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(data, uid, ctx) -void *data, *ctx; -int uid; +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 = fr_inobj(data, &iter, IPFOBJ_GENITER); + error = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_GENITER); if (error != 0) return error; - token = ipf_findtoken(iter.igi_type, uid, ctx); + token = ipf_token_find(softc, iter.igi_type, uid, ctx); if (token != NULL) { token->ipt_subtype = iter.igi_type; - error = ipf_geniter(token, &iter); - } else - error = EFAULT; - RWLOCK_EXIT(&ipf_tokens); + 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: fr_ipf_ioctl */ +/* Function: ipf_ipf_ioctl */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - the token type to match */ +/* 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 */ @@ -7002,231 +8102,283 @@ int uid; /* This function handles all of the ioctl command that are actually isssued */ /* to the /dev/ipl device. */ /* ------------------------------------------------------------------------ */ -int fr_ipf_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +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)) + if (!(mode & FWRITE)) { + IPFERROR(94); error = EPERM; - else { + } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error != 0) { + IPFERROR(95); error = EFAULT; break; } - WRITE_ENTER(&ipf_global); + WRITE_ENTER(&softc->ipf_global); if (tmp) { - if (fr_running > 0) + if (softc->ipf_running > 0) error = 0; else - error = ipfattach(); + error = ipfattach(softc); if (error == 0) - fr_running = 1; + softc->ipf_running = 1; else - (void) ipfdetach(); + (void) ipfdetach(softc); } else { - error = ipfdetach(); + if (softc->ipf_running == 1) + error = ipfdetach(softc); + else + error = 0; if (error == 0) - fr_running = -1; + softc->ipf_running = -1; } - RWLOCK_EXIT(&ipf_global); + RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCIPFSET : if (!(mode & FWRITE)) { + IPFERROR(96); error = EPERM; break; } /* FALLTHRU */ case SIOCIPFGETNEXT : case SIOCIPFGET : - error = fr_ipftune(cmd, (void *)data); + error = ipf_ipftune(softc, cmd, (void *)data); break; case SIOCSETFF : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(97); error = EPERM; - else { - error = BCOPYIN(data, &fr_flags, sizeof(fr_flags)); - if (error != 0) + } else { + error = BCOPYIN(data, &softc->ipf_flags, + sizeof(softc->ipf_flags)); + if (error != 0) { + IPFERROR(98); error = EFAULT; + } } break; case SIOCGETFF : - error = BCOPYOUT(&fr_flags, data, sizeof(fr_flags)); - if (error != 0) + error = BCOPYOUT(&softc->ipf_flags, data, + sizeof(softc->ipf_flags)); + if (error != 0) { + IPFERROR(99); error = EFAULT; + } break; case SIOCFUNCL : - error = fr_resolvefunc((void *)data); + error = ipf_resolvefunc(softc, (void *)data); break; case SIOCINAFR : case SIOCRMAFR : case SIOCADAFR : case SIOCZRLST : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(100); error = EPERM; - else - error = frrequest(IPL_LOGIPF, cmd, data, fr_active, 1); + } else { + error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, + softc->ipf_active, 1); + } break; case SIOCINIFR : case SIOCRMIFR : case SIOCADIFR : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(101); error = EPERM; - else - error = frrequest(IPL_LOGIPF, cmd, data, - 1 - fr_active, 1); + } else { + error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, + 1 - softc->ipf_active, 1); + } break; case SIOCSWAPA : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(102); error = EPERM; - else { - WRITE_ENTER(&ipf_mutex); - bzero((char *)frcache, sizeof(frcache[0]) * 2); - error = BCOPYOUT(&fr_active, data, sizeof(fr_active)); - if (error != 0) + } else { + WRITE_ENTER(&softc->ipf_mutex); + error = BCOPYOUT(&softc->ipf_active, data, + sizeof(softc->ipf_active)); + if (error != 0) { + IPFERROR(103); error = EFAULT; - else - fr_active = 1 - fr_active; - RWLOCK_EXIT(&ipf_mutex); + } else { + softc->ipf_active = 1 - softc->ipf_active; + } + RWLOCK_EXIT(&softc->ipf_mutex); } break; case SIOCGETFS : - fr_getstat(&fio); - error = fr_outobj((void *)data, &fio, IPFOBJ_IPFSTAT); + 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)) + if (!(mode & FWRITE)) { + IPFERROR(104); error = EPERM; - else - error = fr_zerostats(data); + } else + error = ipf_zerostats(softc, (caddr_t)data); break; case SIOCIPFFL : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(105); error = EPERM; - else { + } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { - tmp = frflush(IPL_LOGIPF, 4, tmp); + tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); - if (error != 0) + if (error != 0) { + IPFERROR(106); error = EFAULT; - } else + } + } else { + IPFERROR(107); error = EFAULT; + } } break; #ifdef USE_INET6 case SIOCIPFL6 : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(108); error = EPERM; - else { + } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { - tmp = frflush(IPL_LOGIPF, 6, tmp); + tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); - if (error != 0) + if (error != 0) { + IPFERROR(109); error = EFAULT; - } else + } + } else { + IPFERROR(110); error = EFAULT; + } } break; #endif case SIOCSTLCK : - error = BCOPYIN(data, &tmp, sizeof(tmp)); - if (error == 0) { - fr_state_lock = tmp; - fr_nat_lock = tmp; - fr_frag_lock = tmp; - fr_auth_lock = tmp; - } else - error = EFAULT; + 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)) + if (!(mode & FWRITE)) { + IPFERROR(112); error = EPERM; - else { - tmp = ipflog_clear(IPL_LOGIPF); + } else { + tmp = ipf_log_clear(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); - if (error) + if (error) { + IPFERROR(113); error = EFAULT; + } } break; #endif /* IPFILTER_LOG */ case SIOCFRSYN : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(114); error = EPERM; - else { - WRITE_ENTER(&ipf_global); -#ifdef MENTAT + } else { + WRITE_ENTER(&softc->ipf_global); +#if (defined(MENTAT) && defined(_KERNEL)) && !defined(INSTANCES) error = ipfsync(); #else - frsync(NULL); + ipf_sync(softc, NULL); error = 0; #endif - RWLOCK_EXIT(&ipf_global); + RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCGFRST : - error = fr_outobj((void *)data, fr_fragstats(), - IPFOBJ_FRAGSTAT); + error = ipf_outobj(softc, (void *)data, + ipf_frag_stats(softc->ipf_frag_soft), + IPFOBJ_FRAGSTAT); break; #ifdef IPFILTER_LOG case FIONREAD : - tmp = (int)iplused[IPL_LOGIPF]; - + tmp = ipf_log_bytesused(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); break; #endif case SIOCIPFITER : SPL_SCHED(s); - error = ipf_frruleiter(data, uid, ctx); + error = ipf_frruleiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCGENITER : SPL_SCHED(s); - error = ipf_genericiter(data, uid, ctx); + error = ipf_genericiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCIPFDELTOK : - SPL_SCHED(s); error = BCOPYIN(data, &tmp, sizeof(tmp)); - if (error == 0) - error = ipf_deltoken(tmp, uid, ctx); - SPL_X(s); + if (error == 0) { + SPL_SCHED(s); + error = ipf_token_del(softc, tmp, uid, ctx); + SPL_X(s); + } break; default : + IPFERROR(115); error = EINVAL; break; } @@ -7236,90 +8388,610 @@ void *ctx; /* ------------------------------------------------------------------------ */ +/* 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: deletefn(I) - function to call to delete entry */ +/* 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 is */ -/* to 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 (fr_ticks.) Anything modified in the last 30 seconds */ -/* is not touched. */ +/* 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 fr_ticks 30*1.5 1800*1.5 | 43200*1.5 */ +/* 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 - fr_ticks is how long left the connection has to live in ipf */ +/* - 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 fr_ticks, the further tqe_die will be for */ -/* any given timeout queue and vice versa. */ +/* - 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". The downside */ -/* of the current implementation is that it may return removing just 1 entry*/ -/* every time (pathological case) where it could remove more. */ +/* 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(deletefn, ipfqs, userqs) -ipftq_delete_fn_t deletefn; -ipftq_t *ipfqs, *userqs; +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; + 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 (fr_ticks > IPF_TTLVAL(43200 * 15 / 10)) { + if (softc->ipf_ticks > IPF_TTLVAL(43200 * 15 / 10)) { istart = IPF_TTLVAL(86400 * 4); interval = IPF_TTLVAL(43200); - } else if (fr_ticks > IPF_TTLVAL(1800 * 15 / 10)) { + } else if (softc->ipf_ticks > IPF_TTLVAL(1800 * 15 / 10)) { istart = IPF_TTLVAL(43200); interval = IPF_TTLVAL(1800); - } else if (fr_ticks > IPF_TTLVAL(30 * 15 / 10)) { + } else if (softc->ipf_ticks > IPF_TTLVAL(30 * 15 / 10)) { istart = IPF_TTLVAL(1800); interval = IPF_TTLVAL(30); } else { return 0; } - if (istart > fr_ticks) { - if (fr_ticks - interval < interval) + if (istart > softc->ipf_ticks) { + if (softc->ipf_ticks - interval < interval) istart = interval; else - istart = (fr_ticks / interval) * interval; + istart = (softc->ipf_ticks / interval) * interval; } - iend = fr_ticks - interval; - removed = 0; + iend = softc->ipf_ticks - interval; - for (;;) { + while ((*activep * 100 / size) > low) { u_long try; - try = fr_ticks - istart; + 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)(tqe->tqe_parent) == 0) + if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } @@ -7331,14 +9003,12 @@ ipftq_t *ipfqs, *userqs; if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; - if ((*deletefn)(tqe->tqe_parent) == 0) + if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } if (try >= iend) { - if (removed > 0) - break; if (interval == IPF_TTLVAL(43200)) { interval = IPF_TTLVAL(1800); } else if (interval == IPF_TTLVAL(1800)) { @@ -7346,13 +9016,1200 @@ ipftq_t *ipfqs, *userqs; } else { break; } - if (interval >= fr_ticks) + if (interval >= softc->ipf_ticks) break; - iend = fr_ticks - interval; + 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 *)); + +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++; +# if defined(__OpenBSD__) + timeout_add(&ipf_slowtimer_ch, hz/2); +# endif +} + + +/* ------------------------------------------------------------------------ */ +/* 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 fcd891f..5a2ebec 100644 --- a/sys/contrib/ipfilter/netinet/ip_auth.c +++ b/sys/contrib/ipfilter/netinet/ip_auth.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1998-2003 by Darren Reed & Guido van Rooij. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -19,6 +19,9 @@ #if !defined(_KERNEL) # include <stdio.h> # include <stdlib.h> +# ifdef _STDC_C99 +# include <stdbool.h> +# endif # include <string.h> # define _KERNEL # ifdef __OpenBSD__ @@ -52,7 +55,7 @@ struct file; # include <sys/stream.h> # include <sys/kmem.h> #endif -#if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802) || \ +#if (defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)) || \ (defined(__FreeBSD_version) &&(__FreeBSD_version >= 400000)) # include <sys/queue.h> #endif @@ -62,11 +65,14 @@ struct file; #if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include <sys/proc.h> #endif +#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 400000) && \ + !defined(_KERNEL) +# include <stdbool.h> +#endif #include <net/if.h> #ifdef sun # include <net/af.h> #endif -#include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -125,71 +131,249 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #endif + +typedef struct ipf_auth_softc_s { #if SOLARIS && defined(_KERNEL) -extern kcondvar_t ipfauthwait; -extern struct pollhead iplpollhead[IPL_LOGSIZE]; + kcondvar_t ipf_auth_wait; #endif /* SOLARIS */ #if defined(linux) && defined(_KERNEL) -wait_queue_head_t fr_authnext_linux; + wait_queue_head_t ipf_auth_next_linux; #endif + 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; + + +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 *)); + + +/* ------------------------------------------------------------------------ */ +/* 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; +} -int fr_authsize = FR_NUMAUTH; -int fr_authused = 0; -int fr_defaultauthage = 600; -int fr_auth_lock = 0; -int fr_auth_init = 0; -fr_authstat_t fr_authstats; -static frauth_t *fr_auth = NULL; -mb_t **fr_authpkts = NULL; -int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; -frauthent_t *fae_list = NULL; -frentry_t *ipauth = NULL, - *fr_authlist = NULL; - -void fr_authderef __P((frauthent_t **)); -int fr_authgeniter __P((ipftoken_t *, ipfgeniter_t *)); -int fr_authreply __P((char *)); -int fr_authwait __P((char *)); /* ------------------------------------------------------------------------ */ -/* Function: fr_authinit */ +/* 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 fr_authinit() +int +ipf_auth_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - KMALLOCS(fr_auth, frauth_t *, fr_authsize * sizeof(*fr_auth)); - if (fr_auth != NULL) - bzero((char *)fr_auth, fr_authsize * sizeof(*fr_auth)); - else + 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(fr_authpkts, mb_t **, fr_authsize * sizeof(*fr_authpkts)); - if (fr_authpkts != NULL) - bzero((char *)fr_authpkts, fr_authsize * sizeof(*fr_authpkts)); - else + 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)); - MUTEX_INIT(&ipf_authmx, "ipf auth log mutex"); - RWLOCK_INIT(&ipf_auth, "ipf IP User-Auth rwlock"); -#if SOLARIS && defined(_KERNEL) - cv_init(&ipfauthwait, "ipf auth condvar", CV_DRIVER, NULL); -#endif #if defined(linux) && defined(_KERNEL) - init_waitqueue_head(&fr_authnext_linux); + init_waitqueue_head(&softa->ipf_auth_next_linux); #endif - fr_auth_init = 1; + 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: fr_checkauth */ +/* 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 */ @@ -198,10 +382,13 @@ int fr_authinit() /* 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 *fr_checkauth(fin, passp) -fr_info_t *fin; -u_32_t *passp; +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; @@ -209,27 +396,29 @@ u_32_t *passp; ip_t *ip; int i; - if (fr_auth_lock || !fr_authused) + if (softa->ipf_auth_lock || !softa->ipf_auth_used) return NULL; ip = fin->fin_ip; id = ip->ip_id; - READ_ENTER(&ipf_auth); - for (i = fr_authstart; i != fr_authend; ) { + 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 = fr_auth + i; + 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))) + 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 @@ -249,60 +438,65 @@ u_32_t *passp; 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; - RWLOCK_EXIT(&ipf_auth); + fin->fin_flx |= fra->fra_flx; + RWLOCK_EXIT(&softa->ipf_authlk); - WRITE_ENTER(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); /* - * fr_authlist is populated with the rules malloc'd + * 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 = fr_authlist; - fr_authlist = fr; + fr->fr_next = softa->ipf_auth_rules; + softa->ipf_auth_rules = fr; } - fr_authstats.fas_hits++; + softa->ipf_auth_stats.fas_hits++; fra->fra_index = -1; - fr_authused--; - if (i == fr_authstart) { + softa->ipf_auth_used--; + softa->ipf_auth_replies--; + if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; fra++; - if (i == fr_authsize) { + if (i == softa->ipf_auth_size) { i = 0; - fra = fr_auth; + fra = softa->ipf_auth; } - fr_authstart = i; - if (i == fr_authend) + softa->ipf_auth_start = i; + if (i == softa->ipf_auth_end) break; } - if (fr_authstart == fr_authend) { - fr_authnext = 0; - fr_authstart = fr_authend = 0; + 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(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); if (passp != NULL) *passp = pass; - ATOMIC_INC64(fr_authstats.fas_hits); + softa->ipf_auth_stats.fas_hits++; return fr; } i++; - if (i == fr_authsize) + if (i == softa->ipf_auth_size) i = 0; } - fr_authstats.fas_miss++; - RWLOCK_EXIT(&ipf_auth); - ATOMIC_INC64(fr_authstats.fas_miss); + RWLOCK_EXIT(&softa->ipf_authlk); + softa->ipf_auth_stats.fas_miss++; return NULL; } /* ------------------------------------------------------------------------ */ -/* Function: fr_newauth */ +/* 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 */ @@ -311,10 +505,13 @@ u_32_t *passp; /* packet. If we do, store it and wake up any user programs which are */ /* waiting to hear about these events. */ /* ------------------------------------------------------------------------ */ -int fr_newauth(m, fin) -mb_t *m; -fr_info_t *fin; +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 @@ -324,31 +521,33 @@ fr_info_t *fin; #endif int i; - if (fr_auth_lock) + if (softa->ipf_auth_lock) return 0; - WRITE_ENTER(&ipf_auth); - if (((fr_authend + 1) % fr_authsize) == fr_authstart) { - fr_authstats.fas_nospace++; - RWLOCK_EXIT(&ipf_auth); + 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; } - fr_authstats.fas_added++; - fr_authused++; - i = fr_authend++; - if (fr_authend == fr_authsize) - fr_authend = 0; - fra = fr_auth + i; - fra->fra_index = i; - RWLOCK_EXIT(&ipf_auth); + 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 = fr_defaultauthage; + 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 @@ -370,24 +569,24 @@ fr_info_t *fin; #if SOLARIS && defined(_KERNEL) COPYIFNAME(fin->fin_v, fin->fin_ifp, fra->fra_info.fin_ifname); m->b_rptr -= qpi->qpi_off; - fr_authpkts[i] = *(mblk_t **)fin->fin_mp; -# if !defined(_INET_IP_STACK_H) fra->fra_q = qpi->qpi_q; /* The queue can disappear! */ -# endif fra->fra_m = *fin->fin_mp; fra->fra_info.fin_mp = &fra->fra_m; - cv_signal(&ipfauthwait); - pollwakeup(&iplpollhead[IPL_LOGAUTH], POLLIN|POLLRDNORM); + 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 - fr_authpkts[i] = m; - WAKEUP(&fr_authnext,0); + softa->ipf_auth_pkts[i] = m; + RWLOCK_EXIT(&softa->ipf_authlk); + WAKEUP(&softa->ipf_auth_next, 0); #endif return 1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_auth_ioctl */ +/* Function: ipf_auth_ioctl */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - pointer to ioctl data */ /* cmd(I) - ioctl command */ @@ -396,14 +595,17 @@ fr_info_t *fin; /* 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/ipauth */ +/* in IPFilter - ie ioctls called on an open fd for /dev/ipf_auth */ /* ------------------------------------------------------------------------ */ -int fr_auth_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +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); @@ -413,18 +615,23 @@ void *ctx; { ipftoken_t *token; ipfgeniter_t iter; + ipfobj_t obj; - error = fr_inobj(data, &iter, IPFOBJ_GENITER); + error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); - token = ipf_findtoken(IPFGENITER_AUTH, uid, ctx); + token = ipf_token_find(softc, IPFGENITER_AUTH, uid, ctx); if (token != NULL) - error = fr_authgeniter(token, &iter); - else + 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; - RWLOCK_EXIT(&ipf_tokens); + } SPL_X(s); break; @@ -432,46 +639,52 @@ void *ctx; case SIOCADAFR : case SIOCRMAFR : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(10002); error = EPERM; - else - error = frrequest(IPL_LOGAUTH, cmd, data, - fr_active, 1); + } else + error = frrequest(softc, IPL_LOGAUTH, cmd, data, + softc->ipf_active, 1); break; case SIOCSTLCK : if (!(mode & FWRITE)) { + IPFERROR(10003); error = EPERM; - break; + } else { + error = ipf_lock(data, &softa->ipf_auth_lock); } - error = fr_lock(data, &fr_auth_lock); break; case SIOCATHST: - fr_authstats.fas_faelist = fae_list; - error = fr_outobj(data, &fr_authstats, IPFOBJ_AUTHSTAT); + 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(&ipf_auth); - i = fr_authflush(); - RWLOCK_EXIT(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); + i = ipf_auth_flush(softa); + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); - error = BCOPYOUT((char *)&i, data, sizeof(i)); - if (error != 0) + error = BCOPYOUT(&i, data, sizeof(i)); + if (error != 0) { + IPFERROR(10004); error = EFAULT; + } break; case SIOCAUTHW: - error = fr_authwait(data); + error = ipf_auth_wait(softc, softa, data); break; case SIOCAUTHR: - error = fr_authreply(data); + error = ipf_auth_reply(softc, softa, data); break; default : + IPFERROR(10005); error = EINVAL; break; } @@ -480,75 +693,18 @@ void *ctx; /* ------------------------------------------------------------------------ */ -/* Function: fr_authunload */ -/* Returns: None */ -/* Parameters: None */ -/* */ -/* Free all network buffer memory used to keep saved packets. */ -/* ------------------------------------------------------------------------ */ -void fr_authunload() -{ - register int i; - register frauthent_t *fae, **faep; - frentry_t *fr, **frp; - mb_t *m; - - if (fr_auth != NULL) { - KFREES(fr_auth, fr_authsize * sizeof(*fr_auth)); - fr_auth = NULL; - } - - if (fr_authpkts != NULL) { - for (i = 0; i < fr_authsize; i++) { - m = fr_authpkts[i]; - if (m != NULL) { - FREE_MB_T(m); - fr_authpkts[i] = NULL; - } - } - KFREES(fr_authpkts, fr_authsize * sizeof(*fr_authpkts)); - fr_authpkts = NULL; - } - - faep = &fae_list; - while ((fae = *faep) != NULL) { - *faep = fae->fae_next; - KFREE(fae); - } - ipauth = NULL; - - if (fr_authlist != NULL) { - for (frp = &fr_authlist; ((fr = *frp) != NULL); ) { - if (fr->fr_ref == 1) { - *frp = fr->fr_next; - KFREE(fr); - } else - frp = &fr->fr_next; - } - } - - if (fr_auth_init == 1) { -# if SOLARIS && defined(_KERNEL) - cv_destroy(&ipfauthwait); -# endif - MUTEX_DESTROY(&ipf_authmx); - RW_DESTROY(&ipf_auth); - - fr_auth_init = 0; - } -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_authexpire */ +/* 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 fr_authexpire() +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; @@ -556,70 +712,81 @@ void fr_authexpire() int i; SPL_INT(s); - if (fr_auth_lock) + if (softa->ipf_auth_lock) return; - SPL_NET(s); - WRITE_ENTER(&ipf_auth); - for (i = 0, fra = fr_auth; i < fr_authsize; i++, fra++) { + 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) && (m = fr_authpkts[i])) { - FREE_MB_T(m); - fr_authpkts[i] = NULL; - fr_auth[i].fra_index = -1; - fr_authstats.fas_expire++; - fr_authused--; + 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 = &fae_list; ((fae = *faep) != NULL); ) { + for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { fae->fae_age--; if (fae->fae_age == 0) { - fr_authderef(&fae); - fr_authstats.fas_expire++; + ipf_auth_deref(&fae); + softa->ipf_auth_stats.fas_expire++; } else faep = &fae->fae_next; } - if (fae_list != NULL) - ipauth = &fae_list->fae_fr; + if (softa->ipf_auth_entries != NULL) + softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; else - ipauth = NULL; + softa->ipf_auth_ip = NULL; - for (frp = &fr_authlist; ((fr = *frp) != 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(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: fr_preauthcmd */ +/* 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 fr_preauthcmd(cmd, fr, frptr) -ioctlcmd_t cmd; -frentry_t *fr, **frptr; +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)) + if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) { + IPFERROR(10006); return EIO; + } - for (faep = &fae_list; ((fae = *faep) != NULL); ) { + for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { if (&fae->fae_fr == fr) break; else @@ -627,17 +794,22 @@ frentry_t *fr, **frptr; } if (cmd == (ioctlcmd_t)SIOCRMAFR) { - if (fr == NULL || frptr == NULL) + if (fr == NULL || frptr == NULL) { + IPFERROR(10007); error = EINVAL; - else if (fae == NULL) + + } else if (fae == NULL) { + IPFERROR(10008); error = ESRCH; - else { + + } else { SPL_NET(s); - WRITE_ENTER(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); *faep = fae->fae_next; - if (ipauth == &fae->fae_fr) - ipauth = fae_list ? &fae_list->fae_fr : NULL; - RWLOCK_EXIT(&ipf_auth); + 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); @@ -648,161 +820,199 @@ frentry_t *fr, **frptr; bcopy((char *)fr, (char *)&fae->fae_fr, sizeof(*fr)); SPL_NET(s); - WRITE_ENTER(&ipf_auth); - fae->fae_age = fr_defaultauthage; + 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; - ipauth = &fae_list->fae_fr; - RWLOCK_EXIT(&ipf_auth); + softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; + RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); - } else + } else { + IPFERROR(10009); error = ENOMEM; - } else + } + } else { + IPFERROR(10010); error = EINVAL; + } return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_authflush */ +/* Function: ipf_auth_flush */ /* Returns: int - number of auth entries flushed */ /* Parameters: None */ -/* Locks: WRITE(ipf_auth) */ +/* Locks: WRITE(ipf_authlk) */ /* */ -/* This function flushs the fr_authpkts array of any packet data with */ +/* 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. */ /* ------------------------------------------------------------------------ */ -int fr_authflush() +static int +ipf_auth_flush(arg) + void *arg; { - register int i, num_flushed; + ipf_auth_softc_t *softa = arg; + int i, num_flushed; mb_t *m; - if (fr_auth_lock) + if (softa->ipf_auth_lock) return -1; num_flushed = 0; - for (i = 0 ; i < fr_authsize; i++) { - m = fr_authpkts[i]; - if (m != NULL) { - FREE_MB_T(m); - fr_authpkts[i] = NULL; - fr_auth[i].fra_index = -1; + 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.*/ - fr_authstats.fas_expire++; - fr_authused--; + softa->ipf_auth_stats.fas_expire++; num_flushed++; } } - fr_authstart = 0; - fr_authend = 0; - fr_authnext = 0; + 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: fr_auth_waiting */ -/* Returns: int - 0 = no packets waiting, 1 = packets waiting. */ +/* 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 fr_auth_waiting() +int +ipf_auth_waiting(softc) + ipf_main_softc_t *softc; { - return (fr_authused != 0); + ipf_auth_softc_t *softa = softc->ipf_auth_soft; + + return (softa->ipf_auth_used != 0); } /* ------------------------------------------------------------------------ */ -/* Function: fr_authgeniter */ +/* 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 */ /* ------------------------------------------------------------------------ */ -int fr_authgeniter(token, itp) -ipftoken_t *token; -ipfgeniter_t *itp; +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) + if (itp->igi_data == NULL) { + IPFERROR(10011); return EFAULT; + } - if (itp->igi_type != IPFGENITER_AUTH) + 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; - READ_ENTER(&ipf_auth); if (fae == NULL) { - next = fae_list; + 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) { - /* - * If we find an auth entry to use, bump its reference count - * so that it can be used for is_next when we come back. - */ ATOMIC_INC(next->fae_ref); - if (next->fae_next == NULL) { - ipf_freetoken(token); - token = NULL; - } else { - token->ipt_data = next; - } + token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; + token->ipt_data = NULL; } - RWLOCK_EXIT(&ipf_auth); - /* - * If we had a prior pointer to an auth entry, release it. - */ - if (fae != NULL) { - WRITE_ENTER(&ipf_auth); - fr_authderef(&fae); - RWLOCK_EXIT(&ipf_auth); - } + RWLOCK_EXIT(&softa->ipf_authlk); - /* - * This should arguably be via fr_outobj() so that the auth - * structure can (if required) be massaged going out. - */ - error = COPYOUT(next, itp->igi_data, sizeof(*next)); - if (error != 0) - error = EFAULT; + 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: fr_authderef */ +/* Function: ipf_auth_deref_unlocked */ /* Returns: None */ /* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ -/* Locks: WRITE(ipf_auth) */ +/* */ +/* 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. */ /* ------------------------------------------------------------------------ */ -void fr_authderef(faep) -frauthent_t **faep; +static void +ipf_auth_deref(faep) + frauthent_t **faep; { frauthent_t *fae; @@ -817,30 +1027,30 @@ frauthent_t **faep; /* ------------------------------------------------------------------------ */ -/* Function: fr_authwait */ +/* 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 (fr_authpkts) then we go to */ -/* sleep. */ +/* If there are no packets present in the queue (ipf_auth_pkts) then we go */ +/* to sleep. */ /* ------------------------------------------------------------------------ */ -int fr_authwait(data) -char *data; +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; -#if defined(_KERNEL) && !defined(MENTAT) && !defined(linux) && \ - (!defined(__FreeBSD_version) || (__FreeBSD_version < 501000)) SPL_INT(s); -#endif -fr_authioctlloop: - error = fr_inobj(data, au, IPFOBJ_FRAUTH); +ipf_auth_ioctlloop: + error = ipf_inobj(softc, data, NULL, au, IPFOBJ_FRAUTH); if (error != 0) return error; @@ -850,30 +1060,36 @@ fr_authioctlloop: * we are trying to guard against here is an error in the copyout * steps should not cause the packet to "disappear" from the queue. */ - READ_ENTER(&ipf_auth); + SPL_NET(s); + READ_ENTER(&softa->ipf_authlk); /* - * If fr_authnext is not equal to fr_authend it will be because there - * is a packet waiting to be delt with in the fr_authpkts array. We - * copy as much of that out to user space as requested. + * 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 (fr_authused > 0) { - while (fr_authpkts[fr_authnext] == NULL) { - fr_authnext++; - if (fr_authnext == fr_authsize) - fr_authnext = 0; + 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 = fr_outobj(data, &fr_auth[fr_authnext], IPFOBJ_FRAUTH); - if (error != 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 = fr_authpkts[fr_authnext]; + m = softa->ipf_auth_pkts[softa->ipf_auth_next]; len = MSGDSIZE(m); if (len > auth.fra_len) len = auth.fra_len; @@ -881,62 +1097,69 @@ fr_authioctlloop: for (t = auth.fra_buf; m && (len > 0); ) { i = MIN(M_LEN(m), len); - error = copyoutptr(MTOD(m, char *), &t, i); + error = copyoutptr(softc, MTOD(m, char *), + &t, i); len -= i; t += i; - if (error != 0) + if (error != 0) { + RWLOCK_EXIT(&softa->ipf_authlk); + SPL_X(s); return error; + } m = m->m_next; } } - RWLOCK_EXIT(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); SPL_NET(s); - WRITE_ENTER(&ipf_auth); - fr_authnext++; - if (fr_authnext == fr_authsize) - fr_authnext = 0; - RWLOCK_EXIT(&ipf_auth); + 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(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); + SPL_X(s); - MUTEX_ENTER(&ipf_authmx); + MUTEX_ENTER(&softa->ipf_auth_mx); #ifdef _KERNEL # if SOLARIS error = 0; - if (!cv_wait_sig(&ipfauthwait, &ipf_authmx.ipf_lk)) + if (!cv_wait_sig(&softa->ipf_auth_wait, &softa->ipf_auth_mx.ipf_lk)) { + IPFERROR(10014); error = EINTR; + } # else /* SOLARIS */ # ifdef __hpux { lock_t *l; - l = get_sleep_lock(&fr_authnext); - error = sleep(&fr_authnext, PZERO+1); + l = get_sleep_lock(&softa->ipf_auth_next); + error = sleep(&softa->ipf_auth_next, PZERO+1); spinunlock(l); } # else # ifdef __osf__ - error = mpsleep(&fr_authnext, PSUSP|PCATCH, "fr_authnext", 0, - &ipf_authmx, MS_LOCK_SIMPLE); + error = mpsleep(&softa->ipf_auth_next, PSUSP|PCATCH, "ipf_auth_next", + 0, &softa->ipf_auth_mx, MS_LOCK_SIMPLE); # else - error = SLEEP(&fr_authnext, "fr_authnext"); + error = SLEEP(&softa->ipf_auth_next, "ipf_auth_next"); # endif /* __osf__ */ # endif /* __hpux */ # endif /* SOLARIS */ #endif - MUTEX_EXIT(&ipf_authmx); + MUTEX_EXIT(&softa->ipf_auth_mx); if (error == 0) - goto fr_authioctlloop; + goto ipf_auth_ioctlloop; return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_authreply */ +/* Function: ipf_auth_reply */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ @@ -945,23 +1168,27 @@ fr_authioctlloop: /* received information using an SIOCAUTHW. The decision returned in the */ /* form of flags, the same as those used in each rule. */ /* ------------------------------------------------------------------------ */ -int fr_authreply(data) -char *data; +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 = fr_inobj(data, &auth, IPFOBJ_FRAUTH); + error = ipf_inobj(softc, data, NULL, &auth, IPFOBJ_FRAUTH); if (error != 0) return error; SPL_NET(s); - WRITE_ENTER(&ipf_auth); + WRITE_ENTER(&softa->ipf_authlk); i = au->fra_index; - fra = fr_auth + i; + fra = softa->ipf_auth + i; error = 0; /* @@ -969,19 +1196,27 @@ char *data; * 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 >= fr_authsize) || - (fra->fra_info.fin_id != au->fra_info.fin_id)) { - RWLOCK_EXIT(&ipf_auth); + 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 = fr_authpkts[i]; + m = softa->ipf_auth_pkts[i]; fra->fra_index = -2; fra->fra_pass = au->fra_pass; - fr_authpkts[i] = NULL; + softa->ipf_auth_pkts[i] = NULL; + softa->ipf_auth_replies++; + bcopy(&fra->fra_info, &fin, sizeof(fin)); - RWLOCK_EXIT(&ipf_auth); + RWLOCK_EXIT(&softa->ipf_authlk); /* * Re-insert the packet back into the packet stream flowing through @@ -992,22 +1227,25 @@ char *data; */ #ifdef _KERNEL if ((m != NULL) && (au->fra_info.fin_out != 0)) { - error = ipf_inject(&fra->fra_info, m); + error = ipf_inject(&fin, m); if (error != 0) { + IPFERROR(10016); error = ENOBUFS; - fr_authstats.fas_sendfail++; + softa->ipf_auth_stats.fas_sendfail++; } else { - fr_authstats.fas_sendok++; + softa->ipf_auth_stats.fas_sendok++; } } else if (m) { - error = ipf_inject(&fra->fra_info, m); + error = ipf_inject(&fin, m); if (error != 0) { + IPFERROR(10017); error = ENOBUFS; - fr_authstats.fas_quefail++; + softa->ipf_auth_stats.fas_quefail++; } else { - fr_authstats.fas_queok++; + softa->ipf_auth_stats.fas_queok++; } } else { + IPFERROR(10018); error = EINVAL; } @@ -1016,28 +1254,54 @@ char *data; * not being processed, make sure we advance to the next one. */ if (error == ENOBUFS) { - WRITE_ENTER(&ipf_auth); - fr_authused--; + WRITE_ENTER(&softa->ipf_authlk); + softa->ipf_auth_used--; fra->fra_index = -1; fra->fra_pass = 0; - if (i == fr_authstart) { + if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; - if (i == fr_authsize) + if (i == softa->ipf_auth_size) i = 0; - fr_authstart = i; - if (i == fr_authend) + softa->ipf_auth_start = i; + if (i == softa->ipf_auth_end) break; } - if (fr_authstart == fr_authend) { - fr_authnext = 0; - fr_authstart = fr_authend = 0; + 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(&ipf_auth); + 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 36c4bac..914f999 100644 --- a/sys/contrib/ipfilter/netinet/ip_auth.h +++ b/sys/contrib/ipfilter/netinet/ip_auth.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2001 by Darren Reed & Guido Van Rooij. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -21,6 +21,7 @@ typedef struct frauth { 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; @@ -35,7 +36,7 @@ typedef struct frauthent { int fae_ref; } frauthent_t; -typedef struct fr_authstat { +typedef struct ipf_authstat { U_QUAD_T fas_hits; U_QUAD_T fas_miss; u_long fas_nospace; @@ -46,26 +47,28 @@ typedef struct fr_authstat { u_long fas_quefail; u_long fas_expire; frauthent_t *fas_faelist; -} fr_authstat_t; +} ipf_authstat_t; -extern frentry_t *ipauth; -extern struct fr_authstat fr_authstats; -extern int fr_defaultauthage; -extern int fr_authstart; -extern int fr_authend; -extern int fr_authsize; -extern int fr_authused; -extern int fr_auth_lock; -extern frentry_t *fr_checkauth __P((fr_info_t *, u_32_t *)); -extern void fr_authexpire __P((void)); -extern int fr_authinit __P((void)); -extern void fr_authunload __P((void)); -extern int fr_authflush __P((void)); -extern mb_t **fr_authpkts; -extern int fr_newauth __P((mb_t *, fr_info_t *)); -extern int fr_preauthcmd __P((ioctlcmd_t, frentry_t *, frentry_t **)); -extern int fr_auth_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern int fr_auth_waiting __P((void)); +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 *)); #endif /* __IP_AUTH_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_compat.h b/sys/contrib/ipfilter/netinet/ip_compat.h index 4305c48..6cce591 100644 --- a/sys/contrib/ipfilter/netinet/ip_compat.h +++ b/sys/contrib/ipfilter/netinet/ip_compat.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -32,14 +32,7 @@ # define __KERNEL__ #endif -#ifndef SOLARIS #define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) -#endif -#if (defined(SOLARIS2) && (SOLARIS2 >= 8)) -# ifndef USE_INET6 -# define USE_INET6 -# endif -#endif #if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ !defined(_KERNEL) && !defined(USE_INET6) && !defined(NOINET6) # define USE_INET6 @@ -47,26 +40,22 @@ #if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 105000000) && \ !defined(_KERNEL) && !defined(USE_INET6) # define USE_INET6 +#endif +#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 106140000) && \ + defined(_KERNEL) && \ + (!defined(IPFILTER_LKM) || (__NetBSD_Version__ >= 399000100)) # define IPFILTER_M_IPFILTER #endif -#if defined(OpenBSD) && (OpenBSD >= 200206) && \ +#if !defined(USE_INET6) +# if defined(OpenBSD) && (OpenBSD >= 200206) && \ !defined(_KERNEL) && !defined(USE_INET6) -# define USE_INET6 -#endif -#if defined(__osf__) -# define USE_INET6 -#endif -#if defined(linux) && (!defined(_KERNEL) || defined(CONFIG_IPV6)) -# define USE_INET6 -#endif -#if defined(HPUXREV) && (HPUXREV >= 1111) -# define USE_INET6 +# define USE_INET6 +# endif +# if defined(HPUXREV) && (HPUXREV >= 1111) +# define USE_INET6 +# endif #endif -#if defined(BSD) && (BSD < 199103) && defined(__osf__) -# undef BSD -# define BSD 199103 -#endif #if defined(__SVR4) || defined(__svr4__) || defined(__sgi) # define index strchr @@ -95,862 +84,120 @@ struct ether_addr { }; #endif -#if defined(__sgi) && !defined(IPFILTER_LKM) -# ifdef __STDC__ -# define IPL_EXTERN(ep) ipfilter##ep -# else -# define IPL_EXTERN(ep) ipfilter/**/ep -# endif -#else # ifdef __STDC__ # define IPL_EXTERN(ep) ipl##ep # else # define IPL_EXTERN(ep) ipl/**/ep # endif -#endif /* * This is a workaround for <sys/uio.h> troubles on FreeBSD and OpenBSD. */ -#ifndef linux # ifndef _KERNEL # define ADD_KERNEL # define _KERNEL # define KERNEL # endif -# ifdef __OpenBSD__ -struct file; -# endif # include <sys/uio.h> # ifdef ADD_KERNEL # undef _KERNEL # undef KERNEL # endif -#endif - - -/* ----------------------------------------------------------------------- */ -/* S O L A R I S */ -/* ----------------------------------------------------------------------- */ -#if SOLARIS -# define MENTAT 1 -# include <sys/cmn_err.h> -# include <sys/isa_defs.h> -# include <sys/stream.h> -# include <sys/ioccom.h> -# include <sys/sysmacros.h> -# include <sys/kmem.h> -# if defined(SOLARIS2) && SOLARIS2 >= 10 -# include <sys/procset.h> -# include <sys/proc.h> -# include <sys/devops.h> -# include <sys/ddi_impldefs.h> -# endif -/* - * because Solaris 2 defines these in two places :-/ - */ -# ifndef KERNEL -# define _KERNEL -# undef RES_INIT -# endif /* _KERNEL */ - -# if defined(SOLARIS2) && SOLARIS2 >= 8 -# include <netinet/ip6.h> -# include <netinet/icmp6.h> -# endif - -# include <inet/common.h> -/* These 5 are defined in <inet/ip.h> and <netinet/ip.h> */ -# undef IPOPT_EOL -# undef IPOPT_NOP -# undef IPOPT_LSRR -# undef IPOPT_RR -# undef IPOPT_SSRR -# ifdef i386 -# define _SYS_PROMIF_H -# endif -# ifndef _KERNEL -# include "radix_ipf.h" -# else -# include "radix_ipf_local.h" -# endif -# include <inet/ip.h> -# undef COPYOUT -# include <inet/ip_ire.h> -# ifndef KERNEL -# undef _KERNEL -# endif -# if defined(SOLARIS2) && SOLARIS2 >= 8 -# define SNPRINTF snprintf - -# include <inet/ip_if.h> -# define ipif_local_addr ipif_lcl_addr -/* Only defined in private include file */ -# ifndef V4_PART_OF_V6 -# define V4_PART_OF_V6(v6) v6.s6_addr32[3] -# endif -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; -# endif /* SOLARIS2 >= 8 */ - -# if defined(SOLARIS2) && SOLARIS2 >= 6 -# include <sys/atomic.h> -typedef uint32_t u_32_t; -# else -typedef unsigned int u_32_t; -# endif -# define U_32_T 1 - -# ifdef _KERNEL -# define NEED_LOCAL_RAND 1 -# define ipf_random arc4random -# define KRWLOCK_T krwlock_t -# define KMUTEX_T kmutex_t - -# if !defined(FW_HOOKS) -# include "qif.h" -# include "pfil.h" -# else -# include <sys/neti.h> - -extern net_data_t ipfipv4; -extern net_data_t ipfipv6; - -typedef struct qpktinfo { - void *qpi_data; - mblk_t **qpi_mp; - mblk_t *qpi_m; - uintptr_t qpi_real; - int qpi_flags; - int qpi_num; - int qpi_off; -} qpktinfo_t; -# define QF_GROUP 0x01 -# endif - -# if SOLARIS2 >= 6 -# if SOLARIS2 == 6 -# define ATOMIC_INCL(x) atomic_add_long((uint32_t*)&(x), 1) -# define ATOMIC_DECL(x) atomic_add_long((uint32_t*)&(x), -1) -# else -# define ATOMIC_INCL(x) atomic_add_long(&(x), 1) -# define ATOMIC_DECL(x) atomic_add_long(&(x), -1) -# endif /* SOLARIS2 == 6 */ -# define ATOMIC_INC64(x) atomic_add_64((uint64_t*)&(x), 1) -# define ATOMIC_INC32(x) atomic_add_32((uint32_t*)&(x), 1) -# define ATOMIC_INC16(x) atomic_add_16((uint16_t*)&(x), 1) -# define ATOMIC_DEC64(x) atomic_add_64((uint64_t*)&(x), -1) -# define ATOMIC_DEC32(x) atomic_add_32((uint32_t*)&(x), -1) -# define ATOMIC_DEC16(x) atomic_add_16((uint16_t*)&(x), -1) -# else -# define ATOMIC_INC(x) { mutex_enter(&ipf_rw); (x)++; \ - mutex_exit(&ipf_rw); } -# define ATOMIC_DEC(x) { mutex_enter(&ipf_rw); (x)--; \ - mutex_exit(&ipf_rw); } -# endif /* SOLARIS2 >= 6 */ -# define USE_MUTEXES -# define MUTEX_ENTER(x) mutex_enter(&(x)->ipf_lk) -# define READ_ENTER(x) rw_enter(&(x)->ipf_lk, RW_READER) -# define WRITE_ENTER(x) rw_enter(&(x)->ipf_lk, RW_WRITER) -# define MUTEX_DOWNGRADE(x) rw_downgrade(&(x)->ipf_lk) -# define RWLOCK_INIT(x, y) rw_init(&(x)->ipf_lk, (y), \ - RW_DRIVER, NULL) -# define RWLOCK_EXIT(x) rw_exit(&(x)->ipf_lk) -# define RW_DESTROY(x) rw_destroy(&(x)->ipf_lk) -# define MUTEX_INIT(x, y) mutex_init(&(x)->ipf_lk, (y), \ - MUTEX_DRIVER, NULL) -# define MUTEX_DESTROY(x) mutex_destroy(&(x)->ipf_lk) -# define MUTEX_NUKE(x) bzero((x), sizeof(*(x))) -# define MUTEX_EXIT(x) mutex_exit(&(x)->ipf_lk) -# 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)) -# define BCOPYIN(a,b,c) copyin((caddr_t)(a), (caddr_t)(b), (c)) -# define BCOPYOUT(a,b,c) copyout((caddr_t)(a), (caddr_t)(b), (c)) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# define KFREES(x,s) kmem_free((char *)(x), (s)) -# define SPL_SCHED(x) ; -# define SPL_NET(x) ; -# define SPL_IMP(x) ; -# undef SPL_X -# define SPL_X(x) ; -# ifdef sparc -# define ntohs(x) (x) -# define ntohl(x) (x) -# define htons(x) (x) -# define htonl(x) (x) -# endif /* sparc */ -# define KMALLOC(a,b) (a) = (b)kmem_alloc(sizeof(*(a)), KM_NOSLEEP) -# define KMALLOCS(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) -# define GET_MINOR(x) getminor(x) -extern void *get_unit __P((char *, int)); -# define GETIFP(n, v) get_unit(n, v) -# if defined(_INET_IP_STACK_H) -# define COPYIFNAME(v, x, b) \ - do { \ - if ((v) == 4) { \ - (void) net_getifname(ipfipv4,\ - (uintptr_t)x, b, \ - LIFNAMSIZ); \ - } else { \ - (void) net_getifname(ipfipv6,\ - (uintptr_t)x, b, \ - LIFNAMSIZ); \ - } \ - } while (0) -# else -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, ((qif_t *)x)->qf_name, \ - LIFNAMSIZ) -# endif -# define GETKTIME(x) uniqtime((struct timeval *)x) -# define MSGDSIZE(x) msgdsize(x) -# define M_LEN(x) ((x)->b_wptr - (x)->b_rptr) -# define M_DUPLICATE(x) dupmsg((x)) -# define MTOD(m,t) ((t)((m)->b_rptr)) -# define MTYPE(m) ((m)->b_datap->db_type) -# define FREE_MB_T(m) freemsg(m) -# define m_next b_cont -# if !defined(_INET_IP_STACK_H) -# define CACHE_HASH(x) (((qpktinfo_t *)(x)->fin_qpi)->qpi_num & 7) -# else -# define CACHE_HASH(x) ((uintptr_t)(x)->fin_ifp & 7) -# endif -# define IPF_PANIC(x,y) if (x) { printf y; cmn_err(CE_PANIC, "ipf_panic"); } -typedef mblk_t mb_t; -# endif /* _KERNEL */ - -# if defined(SOLARIS2) && (SOLARIS2 >= 7) -# ifdef lint -# define ALIGN32(ptr) (ptr ? 0L : 0L) -# define ALIGN16(ptr) (ptr ? 0L : 0L) -# else -# define ALIGN32(ptr) (ptr) -# define ALIGN16(ptr) (ptr) -# endif -# endif -# if defined(SOLARIS2) && SOLARIS2 < 6 -typedef struct uio uio_t; -# endif -typedef int ioctlcmd_t; -typedef uint8_t u_int8_t; - -# define OS_RECOGNISED 1 - -#endif /* SOLARIS */ - -/* ----------------------------------------------------------------------- */ -/* H P U X */ -/* ----------------------------------------------------------------------- */ -#ifdef __hpux -# define MENTAT 1 -# include <sys/sysmacros.h> -# include <sys/spinlock.h> -# include <sys/lock.h> -# include <sys/stream.h> -# ifdef USE_INET6 -# include <netinet/if_ether.h> -# include <netinet/ip6.h> -# include <netinet/icmp6.h> -typedef struct ip6_hdr ip6_t; -# endif - -# ifdef _KERNEL -# define SNPRINTF sprintf -# if (HPUXREV >= 1111) -# define IPL_SELECT -# ifdef IPL_SELECT -# include <machine/sys/user.h> -# include <sys/kthread_iface.h> -# define READ_COLLISION 0x01 - -typedef struct iplog_select_s { - kthread_t *read_waiter; - int state; -} iplog_select_t; -# endif -# endif - -# define GETKTIME(x) uniqtime((struct timeval *)x) - -# if HPUXREV == 1111 -# include "kern_svcs.h" -# else -# include <sys/kern_svcs.h> -# endif -# undef ti_flags -# undef TCP_NODELAY -# undef TCP_MAXSEG -# include <sys/reg.h> -# include "../netinet/ip_info.h" -/* - * According to /usr/include/sys/spinlock.h on HP-UX 11.00, these functions - * are available. Attempting to use them actually results in unresolved - * symbols when it comes time to load the module. - * This has been fixed! Yipee! - */ -# if 1 -# ifdef __LP64__ -# define ATOMIC_INCL(x) lock_and_incr_int64(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_DECL(x) lock_and_incr_int64(&ipf_rw.ipf_lk, &(x), -1) -# else -# define ATOMIC_INCL(x) lock_and_incr_int32(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_DECL(x) lock_and_incr_int32(&ipf_rw.ipf_lk, &(x), -1) -# endif -# define ATOMIC_INC64(x) lock_and_incr_int64(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_INC32(x) lock_and_incr_int32(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_INC16(x) lock_and_incr_int16(&ipf_rw.ipf_lk, &(x), 1) -# define ATOMIC_DEC64(x) lock_and_incr_int64(&ipf_rw.ipf_lk, &(x), -1) -# define ATOMIC_DEC32(x) lock_and_incr_int32(&ipf_rw.ipf_lk, &(x), -1) -# define ATOMIC_DEC16(x) lock_and_incr_int16(&ipf_rw.ipf_lk, &(x), -1) -# else /* 0 */ -# define ATOMIC_INC64(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC64(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INC32(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC32(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INCL(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DECL(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# endif -# define ip_cksum ip_csuma -# define memcpy(a,b,c) bcopy((caddr_t)b, (caddr_t)a, c) -# define USE_MUTEXES -# define MUTEX_INIT(x, y) initlock(&(x)->ipf_lk, 0, 0, (y)) -# define MUTEX_ENTER(x) spinlock(&(x)->ipf_lk) -# define MUTEX_EXIT(x) spinunlock(&(x)->ipf_lk); -# define MUTEX_DESTROY(x) -# define MUTEX_NUKE(x) bzero((char *)(x), sizeof(*(x))) -# define KMUTEX_T lock_t -# define kmutex_t lock_t /* for pfil.h */ -# define krwlock_t lock_t /* for pfil.h */ -/* - * The read-write lock implementation in HP-UX 11.0 is crippled - it can - * only be used by threads working in a user context! - * This has been fixed! Yipee! (Or at least it does in 11.00, not 11.11..) - */ -# if HPUXREV < 1111 -# define MUTEX_DOWNGRADE(x) lock_write_to_read(x) -# define KRWLOCK_T struct rw_lock -# define READ_ENTER(x) lock_read(&(x)->ipf_lk) -# define WRITE_ENTER(x) lock_write(&(x)->ipf_lk) -# if HPUXREV >= 1111 -# define RWLOCK_INIT(x, y) rwlock_init4(&(x)->ipf_lk, 0, RWLCK_CANSLEEP, 0, y) -# else -# define RWLOCK_INIT(x, y) lock_init3(&(x)->ipf_lk, 0, 1, 0, 0, y) -# endif -# define RWLOCK_EXIT(x) lock_done(&(x)->ipf_lk) -# else -# define KRWLOCK_T lock_t -# define KMUTEX_T lock_t -# define READ_ENTER(x) MUTEX_ENTER(x) -# define WRITE_ENTER(x) MUTEX_ENTER(x) -# define MUTEX_DOWNGRADE(x) -# define RWLOCK_INIT(x, y) initlock(&(x)->ipf_lk, 0, 0, y) -# define RWLOCK_EXIT(x) MUTEX_EXIT(x) -# endif -# define RW_DESTROY(x) -# 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)) -# define SPL_SCHED(x) ; -# define SPL_NET(x) ; -# define SPL_IMP(x) ; -# undef SPL_X -# define SPL_X(x) ; -extern void *get_unit __P((char *, int)); -# define GETIFP(n, v) get_unit(n, v) -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, ((qif_t *)x)->qf_name, \ - LIFNAMSIZ) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define SLEEP(id, n) { lock_t *_l = get_sleep_lock((caddr_t)id); \ - sleep(id, PZERO+1); \ - spinunlock(_l); \ - } -# define WAKEUP(id,x) { lock_t *_l = get_sleep_lock((caddr_t)id); \ - wakeup(id + x); \ - spinunlock(_l); \ - } -# define POLLWAKEUP(x) ; -# define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_IOSYS, M_NOWAIT) -# define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_IOSYS, M_NOWAIT) -# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# define KFREES(x,s) kmem_free((char *)(x), (s)) -# define MSGDSIZE(x) msgdsize(x) -# define M_LEN(x) ((x)->b_wptr - (x)->b_rptr) -# define M_DUPLICATE(x) dupmsg((x)) -# define MTOD(m,t) ((t)((m)->b_rptr)) -# define MTYPE(m) ((m)->b_datap->db_type) -# define FREE_MB_T(m) freemsg(m) -# define m_next b_cont -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -typedef mblk_t mb_t; - -# define CACHE_HASH(x) (((qpktinfo_t *)(x)->fin_qpi)->qpi_num & 7) - -# include "qif.h" -# include "pfil.h" - -# else /* _KERNEL */ - -typedef unsigned char uchar_t; - -# ifndef _SYS_STREAM_INCLUDED -typedef char * mblk_t; -typedef void * queue_t; -typedef u_long ulong; -# endif -# include <netinet/ip_info.h> - -# endif /* _KERNEL */ - -# ifdef lint -# define ALIGN32(ptr) (ptr ? 0L : 0L) -# define ALIGN16(ptr) (ptr ? 0L : 0L) -# else -# define ALIGN32(ptr) (ptr) -# define ALIGN16(ptr) (ptr) -# endif - -typedef struct uio uio_t; -typedef int ioctlcmd_t; -typedef int minor_t; -typedef unsigned int u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 - -#endif /* __hpux */ - -/* ----------------------------------------------------------------------- */ -/* I R I X */ -/* ----------------------------------------------------------------------- */ -#ifdef __sgi -# undef MENTAT -# if IRIX < 60500 -typedef struct uio uio_t; -# endif -typedef int ioctlcmd_t; -typedef u_int32_t u_32_t; -# define U_32_T 1 - -# ifdef INET6 -# define USE_INET6 -# endif - -# define hz HZ -# include <sys/ksynch.h> -# define IPF_LOCK_PL plhi -# include <sys/sema.h> -# undef kmutex_t -typedef struct { - lock_t *l; - int pl; -} kmutex_t; - -# ifdef MUTEX_INIT -# define KMUTEX_T mutex_t -# else -# define KMUTEX_T kmutex_t -# define KRWLOCK_T kmutex_t -# endif - -# ifdef _KERNEL -# define NEED_LOCAL_RAND 1 -# define ipf_random arc4random -# define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); \ - (x)++; MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); \ - (x)--; MUTEX_EXIT(&ipf_rw); } -# define USE_MUTEXES -# ifdef MUTEX_INIT -# include <sys/atomic_ops.h> -# define ATOMIC_INCL(x) atomicAddUlong(&(x), 1) -# define ATOMIC_INC64(x) atomicAddUint64(&(x), 1) -# define ATOMIC_INC32(x) atomicAddUint(&(x), 1) -# define ATOMIC_INC16 ATOMIC_INC -# define ATOMIC_DECL(x) atomicAddUlong(&(x), -1) -# define ATOMIC_DEC64(x) atomicAddUint64(&(x), -1) -# define ATOMIC_DEC32(x) atomicAddUint(&(x), -1) -# define ATOMIC_DEC16 ATOMIC_DEC -# undef MUTEX_INIT -# define MUTEX_INIT(x, y) mutex_init(&(x)->ipf_lk, \ - MUTEX_DEFAULT, y) -# undef MUTEX_ENTER -# define MUTEX_ENTER(x) mutex_lock(&(x)->ipf_lk, 0) -# undef MUTEX_EXIT -# define MUTEX_EXIT(x) mutex_unlock(&(x)->ipf_lk) -# undef MUTEX_DESTROY -# define MUTEX_DESTROY(x) mutex_destroy(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) mrdemote(&(x)->ipf_lk) -# define KRWLOCK_T mrlock_t -# define RWLOCK_INIT(x, y) mrinit(&(x)->ipf_lk, y) -# undef RW_DESTROY -# define RW_DESTROY(x) mrfree(&(x)->ipf_lk) -# define READ_ENTER(x) RW_RDLOCK(&(x)->ipf_lk) -# define WRITE_ENTER(x) RW_WRLOCK(&(x)->ipf_lk) -# define RWLOCK_EXIT(x) RW_UNLOCK(&(x)->ipf_lk) -# else -# define READ_ENTER(x) MUTEX_ENTER(&(x)->ipf_lk) -# define WRITE_ENTER(x) MUTEX_ENTER(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_EXIT(x) MUTEX_EXIT(&(x)->ipf_lk) -# define MUTEX_EXIT(x) UNLOCK((x)->ipf_lk.l, (x)->ipf_lk.pl); -# define MUTEX_INIT(x,y) (x)->ipf_lk.l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP) -# define MUTEX_DESTROY(x) LOCK_DEALLOC((x)->ipf_lk.l) -# define MUTEX_ENTER(x) (x)->ipf_lk.pl = LOCK((x)->ipf_lk.l, \ - IPF_LOCK_PL); -# endif -# define MUTEX_NUKE(x) bzero((x), sizeof(*(x))) -# define FREE_MB_T(m) m_freem(m) -# define MTOD(m,t) mtod(m,t) -# 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) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define SLEEP(id, n) sleep((id), PZERO+1) -# define WAKEUP(id,x) wakeup(id+x) -# define POLLWAKEUP(x) ; -# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# define KFREES(x,s) kmem_free((char *)(x), (s)) -# define GETIFP(n,v) ifunit(n) -# include <sys/kmem.h> -# include <sys/ddi.h> -# define KMALLOC(a,b) (a) = (b)kmem_alloc(sizeof(*(a)), KM_NOSLEEP) -# define KMALLOCS(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) -# define GET_MINOR(x) getminor(x) -# define USE_SPL 1 -# define SPL_IMP(x) (x) = splimp() -# define SPL_NET(x) (x) = splnet() -# define SPL_SCHED(x) (x) = splsched() -# define SPL_X(x) (void) splx(x) -extern void m_copydata __P((struct mbuf *, int, int, caddr_t)); -extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define GETKTIME(x) microtime((struct timeval *)x) -# define IFNAME(x) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -typedef struct mbuf mb_t; -# else -# undef RW_DESTROY -# undef MUTEX_INIT -# undef MUTEX_DESTROY -# endif /* _KERNEL */ - -# define OS_RECOGNISED 1 - -#endif /* __sgi */ - -/* ----------------------------------------------------------------------- */ -/* T R U 6 4 */ -/* ----------------------------------------------------------------------- */ -#ifdef __osf__ -# undef MENTAT - -# include <kern/lock.h> -# include <sys/sysmacros.h> - -# ifdef _KERNEL -# define NEED_LOCAL_RAND 1 -# define ipf_random arc4random -# define KMUTEX_T simple_lock_data_t -# define KRWLOCK_T lock_data_t -# include <net/net_globals.h> -# define USE_MUTEXES -# define READ_ENTER(x) lock_read(&(x)->ipf_lk) -# define WRITE_ENTER(x) lock_write(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) lock_write_to_read(&(x)->ipf_lk) -# define RWLOCK_INIT(x, y) lock_init(&(x)->ipf_lk, TRUE) -# define RWLOCK_EXIT(x) lock_done(&(x)->ipf_lk) -# define RW_DESTROY(x) lock_terminate(&(x)->ipf_lk) -# define MUTEX_ENTER(x) simple_lock(&(x)->ipf_lk) -# define MUTEX_INIT(x, y) simple_lock_init(&(x)->ipf_lk) -# define MUTEX_DESTROY(x) simple_lock_terminate(&(x)->ipf_lk) -# define MUTEX_EXIT(x) simple_unlock(&(x)->ipf_lk) -# define MUTEX_NUKE(x) bzero(x, sizeof(*(x))) -# define ATOMIC_INC64(x) atomic_incq((uint64_t*)&(x)) -# define ATOMIC_DEC64(x) atomic_decq((uint64_t*)&(x)) -# define ATOMIC_INC32(x) atomic_incl((uint32_t*)&(x)) -# define ATOMIC_DEC32(x) atomic_decl((uint32_t*)&(x)) -# define ATOMIC_INC16(x) { simple_lock(&ipf_rw); (x)++; \ - simple_unlock(&ipf_rw); } -# define ATOMIC_DEC16(x) { simple_lock(&ipf_rw); (x)--; \ - simple_unlock(&ipf_rw); } -# define ATOMIC_INCL(x) atomic_incl((uint32_t*)&(x)) -# define ATOMIC_DECL(x) atomic_decl((uint32_t*)&(x)) -# define ATOMIC_INC(x) { simple_lock(&ipf_rw); (x)++; \ - simple_unlock(&ipf_rw); } -# define ATOMIC_DEC(x) { simple_lock(&ipf_rw); (x)--; \ - simple_unlock(&ipf_rw); } -# define SPL_SCHED(x) ; -# define SPL_NET(x) ; -# define SPL_IMP(x) ; -# undef SPL_X -# define SPL_X(x) ; -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a, b, d) -# define FREE_MB_T(m) m_freem(m) -# define MTOD(m,t) mtod(m,t) -# define GETIFP(n, v) ifunit(n) -# define GET_MINOR getminor -# define WAKEUP(id,x) wakeup(id + x) -# define POLLWAKEUP(x) ; -# 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)) -# define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_PFILT, M_NOWAIT) -# define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_PFILT, \ - ((c) > 4096) ? M_WAITOK : M_NOWAIT) -# define KFREE(x) FREE((x), M_PFILT) -# define KFREES(x,s) FREE((x), M_PFILT) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define GETKTIME(x) microtime((struct timeval *)x) -# define IFNAME(x) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -typedef struct mbuf mb_t; -# endif /* _KERNEL */ - -# if (defined(_KERNEL) || defined(_NO_BITFIELDS) || (__STDC__ == 1)) -# define IP_V(x) ((x)->ip_vhl >> 4) -# define IP_HL(x) ((x)->ip_vhl & 0xf) -# define IP_V_A(x,y) (x)->ip_vhl |= (((y) << 4) & 0xf0) -# define IP_HL_A(x,y) (x)->ip_vhl |= ((y) & 0xf) -# define TCP_X2(x) ((x)->th_xoff & 0xf) -# define TCP_X2_A(x,y) (x)->th_xoff |= ((y) & 0xf) -# define TCP_OFF(x) ((x)->th_xoff >> 4) -# define TCP_OFF_A(x,y) (x)->th_xoff |= (((y) << 4) & 0xf0) -# endif - -/* - * These are from's Solaris' #defines for little endian. - */ -#define IP6F_MORE_FRAG 0x0100 -#define IP6F_RESERVED_MASK 0x0600 -#define IP6F_OFF_MASK 0xf8ff - -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; - -typedef int ioctlcmd_t; -/* - * Really, any arch where sizeof(long) != sizeof(int). - */ -typedef unsigned int u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 -#endif /* __osf__ */ - -/* ----------------------------------------------------------------------- */ -/* N E T B S D */ -/* ----------------------------------------------------------------------- */ -#ifdef __NetBSD__ -# if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL) -# include "opt_ipfilter.h" -# endif -# if defined(_KERNEL) -# include <sys/systm.h> -# else -# include <stddef.h> -# endif -# if defined(_KERNEL) && !defined(IPFILTER_LKM) -# include "bpfilter.h" -# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 104110000) -# include "opt_inet.h" -# endif -# ifdef INET6 -# define USE_INET6 -# endif -# if (__NetBSD_Version__ >= 105000000) -# define HAVE_M_PULLDOWN 1 -# endif -# endif - -# if (__NetBSD_Version__ >= 499000000) -typedef char * caddr_t; -# endif - -# define ipf_random arc4random - -# ifdef _KERNEL -# if (__NetBSD_Version__ >= 399001400) -# define KMALLOCS(a, b, c) (a) = (b)malloc((c), _M_IPF, M_NOWAIT) -# endif -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define GETKTIME(x) microtime((struct timeval *)x) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -# 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)) -typedef struct mbuf mb_t; -# endif /* _KERNEL */ -# if (NetBSD <= 1991011) && (NetBSD >= 199606) -# define IFNAME(x) ((struct ifnet *)x)->if_xname -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, \ - ((struct ifnet *)x)->if_xname, \ - LIFNAMSIZ) -# define CACHE_HASH(x) ((((struct ifnet *)fin->fin_ifp)->if_index)&7) -# else -# define IFNAME(x) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# endif -typedef struct uio uio_t; -typedef u_long ioctlcmd_t; -typedef int minor_t; -typedef u_int32_t u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 -#endif /* __NetBSD__ */ +#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))) +#define FREEBSD_GE_REV(x) (defined(__FreeBSD_version) && \ + (__FreeBSD_version >= (x))) +#define FREEBSD_GT_REV(x) (defined(__FreeBSD_version) && \ + (__FreeBSD_version > (x))) +#define FREEBSD_LT_REV(x) (defined(__FreeBSD_version) && \ + (__FreeBSD_version < (x))) +#define BSDOS_GE_REV(x) (defined(_BSDI_VERSION) && \ + (_BSDI_VERSION >= (x))) +#define BSDOS_GT_REV(x) (defined(_BSDI_VERSION) && \ + (_BSDI_VERSION > (x))) +#define BSDOS_LT_REV(x) (defined(_BSDI_VERSION) && \ + (_BSDI_VERSION < (x))) +#define OPENBSD_GE_REV(x) (defined(OpenBSD) && (OpenBSD >= (x))) +#define OPENBSD_GT_REV(x) (defined(OpenBSD) && (OpenBSD > (x))) +#define OPENBSD_LT_REV(x) (defined(OpenBSD) && (OpenBSD < (x))) +#define BSD_GE_YEAR(x) (defined(BSD) && (BSD >= (x))) +#define BSD_GT_YEAR(x) (defined(BSD) && (BSD > (x))) +#define BSD_LT_YEAR(x) (defined(BSD) && (BSD < (x))) /* ----------------------------------------------------------------------- */ /* F R E E B S D */ /* ----------------------------------------------------------------------- */ -#ifdef __FreeBSD__ -# if (__FreeBSD_version < 400000) -# define NEED_LOCAL_RAND 1 -# else -# define ipf_random arc4random -# endif +# define HAS_SYS_MD5_H 1 # if defined(_KERNEL) -# if (__FreeBSD_version >= 500000) # include "opt_bpf.h" -# else -# include "bpf.h" -# endif -# if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) # include "opt_inet6.h" -# endif # if defined(INET6) && !defined(USE_INET6) # define USE_INET6 # endif # endif # if defined(_KERNEL) -# if (__FreeBSD_version >= 400000) +# include <netinet/ip_var.h> +# 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 -# endif # if !defined(IPFILTER_LKM) && (__FreeBSD_version >= 300000) # 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)) -# if (__FreeBSD_version >= 500043) # define NETBSD_PF -# endif +# else +# include <inttypes.h> # endif /* _KERNEL */ -# if (__FreeBSD_version >= 500043) +# include <sys/selinfo.h> # include <sys/mutex.h> -# if (__FreeBSD_version > 700014) +# define KRWLOCK_FILL_SZ 56 +# define KMUTEX_FILL_SZ 56 # include <sys/rwlock.h> -# 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 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) -# endif -# else -# include <sys/sx.h> -/* - * 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. - */ +# define KMUTEX_T struct mtx +# define KRWLOCK_T struct rwlock # ifdef _KERNEL -# if (__FreeBSD_version < 700000) -# define KRWLOCK_T struct mtx -# define READ_ENTER(x) mtx_lock(&(x)->ipf_lk) -# define WRITE_ENTER(x) mtx_lock(&(x)->ipf_lk) -# define RWLOCK_EXIT(x) mtx_unlock(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_INIT(x,y) mtx_init(&(x)->ipf_lk, (y), NULL,\ - MTX_DEF) -# define RW_DESTROY(x) mtx_destroy(&(x)->ipf_lk) -# else -# define KRWLOCK_T struct sx -# define READ_ENTER(x) sx_slock(&(x)->ipf_lk) -# define WRITE_ENTER(x) sx_xlock(&(x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) sx_downgrade(&(x)->ipf_lk) -# define RWLOCK_INIT(x, y) sx_init(&(x)->ipf_lk, (y)) -# define RW_DESTROY(x) sx_destroy(&(x)->ipf_lk) -# ifdef sx_unlock -# define RWLOCK_EXIT(x) sx_unlock(&(x)->ipf_lk) -# else -# define RWLOCK_EXIT(x) do { \ - if ((x)->ipf_lk.sx_cnt < 0) \ - sx_xunlock(&(x)->ipf_lk); \ +# 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 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 \ - sx_sunlock(&(x)->ipf_lk); \ + rw_runlock(&(x)->ipf_lk); \ } while (0) -# endif -# endif # endif -# endif -# define KMUTEX_T struct mtx -# endif -# if (__FreeBSD_version >= 501113) # include <net/if_var.h> # define IFNAME(x) ((struct ifnet *)x)->if_xname # define COPYIFNAME(v, x, b) \ (void) strncpy(b, \ ((struct ifnet *)x)->if_xname, \ LIFNAMSIZ) -# endif -# if (__FreeBSD_version >= 500043) -# define CACHE_HASH(x) ((((struct ifnet *)fin->fin_ifp)->if_index) & 7) -# else -# define IFNAME(x) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# endif # ifdef _KERNEL # define GETKTIME(x) microtime((struct timeval *)x) -# if (__FreeBSD_version >= 500002) # include <netinet/in_systm.h> # include <netinet/ip.h> # include <machine/in_cksum.h> -# endif -# if (__FreeBSD_version >= 500043) # define USE_MUTEXES # define MUTEX_ENTER(x) mtx_lock(&(x)->ipf_lk) # define MUTEX_EXIT(x) mtx_unlock(&(x)->ipf_lk) @@ -958,460 +205,47 @@ typedef u_int32_t u_32_t; 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 <machine/atomic.h> -# define ATOMIC_INC(x) { mtx_lock(&ipf_rw.ipf_lk); (x)++; \ - mtx_unlock(&ipf_rw.ipf_lk); } -# define ATOMIC_DEC(x) { mtx_lock(&ipf_rw.ipf_lk); (x)--; \ - mtx_unlock(&ipf_rw.ipf_lk); } +# 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_INC16(x) atomic_add_16(&(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 ATOMIC_DEC16(x) atomic_add_16(&(x), -1) # define SPL_X(x) ; # define SPL_NET(x) ; # define SPL_IMP(x) ; # define SPL_SCHED(x) ; -# else -# define SPL_SCHED(x) x = splhigh() -# endif /* __FreeBSD_version >= 500043 */ -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) +# 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_copy((x), 0, M_COPYALL) +# 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; # endif /* _KERNEL */ -# if __FreeBSD_version < 300000 -# include <machine/spl.h> -# else -# if __FreeBSD_version < 400000 -# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) -# define ACTUALLY_LKM_NOT_KERNEL -# endif -# endif -# endif -# if (__FreeBSD_version >= 300000) typedef u_long ioctlcmd_t; -# else -typedef int ioctlcmd_t; -# endif -typedef struct uio uio_t; -typedef int minor_t; -typedef u_int32_t u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 -#endif /* __FreeBSD__ */ - - -/* ----------------------------------------------------------------------- */ -/* O P E N B S D */ -/* ----------------------------------------------------------------------- */ -#ifdef __OpenBSD__ -# ifdef INET6 -# define USE_INET6 -# endif - -# ifdef _KERNEL -# if !defined(IPFILTER_LKM) -# include "bpfilter.h" -# endif -# if (OpenBSD >= 200311) -# define SNPRINTF snprintf -# if defined(USE_INET6) -# include "netinet6/in6_var.h" -# include "netinet6/nd6.h" -# endif -# endif -# if (OpenBSD >= 200012) -# define HAVE_M_PULLDOWN 1 -# 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)) -# define GETKTIME(x) microtime((struct timeval *)x) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -typedef struct mbuf mb_t; -# endif /* _KERNEL */ -# if (OpenBSD >= 199603) -# define IFNAME(x, b) ((struct ifnet *)x)->if_xname -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, \ - ((struct ifnet *)x)->if_xname, \ - LIFNAMSIZ) -# define CACHE_HASH(x) ((((struct ifnet *)fin->fin_ifp)->if_index)&7) -# else -# define IFNAME(x, b) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# endif - typedef struct uio uio_t; -typedef u_long ioctlcmd_t; typedef int minor_t; typedef u_int32_t u_32_t; # define U_32_T 1 -# define OS_RECOGNISED 1 -#endif /* __OpenBSD__ */ - - -/* ----------------------------------------------------------------------- */ -/* B S D O S */ -/* ----------------------------------------------------------------------- */ -#ifdef _BSDI_VERSION -# ifdef INET6 -# define USE_INET6 -# endif - -# ifdef _KERNEL -# define GETKTIME(x) microtime((struct timeval *)x) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define IFNAME(x, b) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -typedef struct mbuf mb_t; -# endif /* _KERNEL */ - -# if (_BSDI_VERSION >= 199701) -typedef u_long ioctlcmd_t; -# else -typedef int ioctlcmd_t; -# endif -typedef u_int32_t u_32_t; -# define U_32_T 1 - -#endif /* _BSDI_VERSION */ - - -/* ----------------------------------------------------------------------- */ -/* S U N O S 4 */ -/* ----------------------------------------------------------------------- */ -#if defined(sun) && !defined(OS_RECOGNISED) /* SunOS4 */ -# ifdef _KERNEL -# include <sys/kmem_alloc.h> -# define GETKTIME(x) uniqtime((struct timeval *)x) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define IFNAME(x, b) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# define GETIFP(n, v) ifunit(n, IFNAMSIZ) -# define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# define KFREES(x,s) kmem_free((char *)(x), (s)) -# define SLEEP(id, n) sleep((id), PZERO+1) -# define WAKEUP(id,x) wakeup(id + x) -# define POLLWAKEUP(x) ; -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } - -extern void m_copydata __P((struct mbuf *, int, int, caddr_t)); -extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); - -typedef struct mbuf mb_t; -# endif - -typedef struct uio uio_t; -typedef int ioctlcmd_t; -typedef int minor_t; -typedef unsigned int u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 - -#endif /* SunOS 4 */ - -/* ----------------------------------------------------------------------- */ -/* L I N U X */ -/* ----------------------------------------------------------------------- */ -#if defined(linux) && !defined(OS_RECOGNISED) -#include <linux/config.h> -#include <linux/version.h> -# if (LINUX >= 20600) && defined(_KERNEL) -# define HDR_T_PRIVATE 1 -# endif -# undef USE_INET6 -# ifdef USE_INET6 -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; -# endif - -# ifdef _KERNEL -# define IPF_PANIC(x,y) if (x) { printf y; panic("ipf_panic"); } -# define COPYIN(a,b,c) copy_from_user((caddr_t)(b), (caddr_t)(a), (c)) -# define COPYOUT(a,b,c) copy_to_user((caddr_t)(b), (caddr_t)(a), (c)) -# define FREE_MB_T(m) kfree_skb(m) -# define GETKTIME(x) do_gettimeofday((struct timeval *)x) -# define POLLWAKEUP(x) ; -# ifdef wait_event_interruptible -# define SLEEP(x,s) wait_event_interruptible((*(x##_linux)), 0) -# else -# define SLEEP(x,s) 0, interruptible_sleep_on(x##_linux) -# endif -# define WAKEUP(x,y) wake_up(x##_linux + y) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -# define USE_MUTEXES -# define KRWLOCK_T rwlock_t -# define KMUTEX_T spinlock_t -# define MUTEX_INIT(x,y) spin_lock_init(&(x)->ipf_lk) -# define MUTEX_ENTER(x) spin_lock(&(x)->ipf_lk) -# define MUTEX_EXIT(x) spin_unlock(&(x)->ipf_lk) -# define MUTEX_DESTROY(x) do { } while (0) -# define MUTEX_NUKE(x) bzero(&(x)->ipf_lk, sizeof((x)->ipf_lk)) -# define READ_ENTER(x) ipf_read_enter(x) -# define WRITE_ENTER(x) ipf_write_enter(x) -# define RWLOCK_INIT(x,y) ipf_rw_init(x, y) -# define RW_DESTROY(x) do { } while (0) -# define RWLOCK_EXIT(x) ipf_rw_exit(x) -# define MUTEX_DOWNGRADE(x) ipf_rw_downgrade(x) -# define ATOMIC_INCL(x) MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_DECL(x) MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_INC64(x) MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_INC32(x) MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_INC16(x) MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_DEC64(x) MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_DEC32(x) MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw) -# define ATOMIC_DEC16(x) MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw) -# define SPL_SCHED(x) do { } while (0) -# define SPL_IMP(x) do { } while (0) -# define SPL_NET(x) do { } while (0) -# define SPL_X(x) do { } while (0) -# define IFNAME(x) ((struct net_device*)x)->name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct net_device *)fin->fin_ifp)->ifindex) & 7) -typedef struct sk_buff mb_t; -extern void m_copydata __P((mb_t *, int, int, caddr_t)); -extern void m_copyback __P((mb_t *, int, int, caddr_t)); -extern void m_adj __P((mb_t *, int)); -extern mb_t *m_pullup __P((mb_t *, int)); -# define mbuf sk_buff - -# define mtod(m, t) ((t)(m)->data) -# define m_data data -# define m_len len -# define m_next next -# define M_DUPLICATE(m) skb_clone((m), in_interrupt() ? GFP_ATOMIC : \ - GFP_KERNEL) -# define MSGDSIZE(m) (m)->len -# define M_LEN(m) (m)->len - -# define splnet(x) ; -# define printf printk -# define bcopy(s,d,z) memmove(d, s, z) -# define bzero(s,z) memset(s, 0, z) -# define bcmp(a,b,z) memcmp(a, b, z) - -# define ifnet net_device -# define if_xname name -# define if_unit ifindex - -# define KMALLOC(x,t) (x) = (t)kmalloc(sizeof(*(x)), \ - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) -# define KFREE(x) kfree(x) -# define KMALLOCS(x,t,s) (x) = (t)kmalloc((s), \ - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) -# define KFREES(x,s) kfree(x) - -# define GETIFP(n,v) dev_get_by_name(n) - -# else -# include <net/ethernet.h> - -struct mbuf { -}; - -# ifndef _NET_ROUTE_H -struct rtentry { -}; -# endif - -struct ifnet { - char if_xname[IFNAMSIZ]; - int if_unit; - int (* if_output) __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); - struct ifaddr *if_addrlist; -}; -# define IFNAME(x) ((struct ifnet *)x)->if_xname - -# endif /* _KERNEL */ - -# define COPYIFNAME(v, x, b) \ - (void) strncpy(b, \ - ((struct ifnet *)x)->if_xname, \ - LIFNAMSIZ) - -# include <linux/fs.h> -# define FWRITE FMODE_WRITE -# define FREAD FMODE_READ - -# define __USE_MISC 1 -# define __FAVOR_BSD 1 - -typedef struct uio { - struct iovec *uio_iov; - void *uio_file; - char *uio_buf; - int uio_iovcnt; - int uio_offset; - size_t uio_resid; - int uio_rw; -} uio_t; - -extern int uiomove __P((caddr_t, size_t, int, struct uio *)); - -# define UIO_READ 1 -# define UIO_WRITE 2 - -typedef u_long ioctlcmd_t; -typedef int minor_t; -typedef u_int32_t u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 - -#endif - - -/* ----------------------------------------------------------------------- */ -/* A I X */ -/* ----------------------------------------------------------------------- */ -#if defined(_AIX51) -# undef MENTAT - -# include <sys/lock.h> -# include <sys/sysmacros.h> - -# ifdef _KERNEL -# define rw_read_locked(x) 0 -# include <net/net_globals.h> -# include <net/net_malloc.h> -# define KMUTEX_T simple_lock_t -# define KRWLOCK_T complex_lock_t -# define USE_MUTEXES 1 -# define USE_SPL 1 -# define READ_ENTER(x) lock_read((x)->ipf_lk) -# define WRITE_ENTER(x) lock_write((x)->ipf_lk) -# define MUTEX_DOWNGRADE(x) lock_write_to_read((x)->ipf_lk) -# define RWLOCK_INIT(x, y) lock_alloc(&(x)->ipf_lk, \ - LOCK_ALLOC_PIN, \ - (u_short)y, 0); \ - lock_init((x)->ipf_lk, TRUE) -# define RWLOCK_EXIT(x) lock_done((x)->ipf_lk) -# define RW_DESTROY(x) lock_free(&(x)->ipf_lk) -# define MUTEX_ENTER(x) simple_lock((x)->ipf_lk) -# define MUTEX_INIT(x, y) lock_alloc(&(x)->ipf_lk, \ - LOCK_ALLOC_PIN, \ - (u_short)y, 0); \ - simple_lock_init((x)->ipf_lk) -# define MUTEX_DESTROY(x) lock_free(&(x)->ipf_lk) -# define MUTEX_EXIT(x) simple_unlock((x)->ipf_lk) -# define MUTEX_NUKE(x) bzero(&(x)->ipf_lk, sizeof((x)->ipf_lk)) -# define ATOMIC_INC64(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC64(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INC32(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC32(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INCL(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DECL(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); (x)++; \ - MUTEX_EXIT(&ipf_rw); } -# define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); (x)--; \ - MUTEX_EXIT(&ipf_rw); } -# define SPL_SCHED(x) x = splsched() -# define SPL_NET(x) x = splnet() -# define SPL_IMP(x) x = splimp() -# undef SPL_X -# define SPL_X(x) splx(x) -# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) -extern void* getifp __P((char *, int)); -# define GETIFP(n, v) getifp(n, v) -# define GET_MINOR minor -# define SLEEP(id, n) sleepx((id), PZERO+1, 0) -# define WAKEUP(id,x) wakeup(id) -# define POLLWAKEUP(x) ; -# 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)) -# define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_TEMP, M_NOWAIT) -# define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_TEMP, \ - ((c) > 4096) ? M_WAITOK : M_NOWAIT) -# define KFREE(x) FREE((x), M_TEMP) -# define KFREES(x,s) FREE((x), M_TEMP) -# define MSGDSIZE(x) mbufchainlen(x) -# define M_LEN(x) (x)->m_len -# define M_DUPLICATE(x) m_copy((x), 0, M_COPYALL) -# define GETKTIME(x) -# define IFNAME(x, b) ((struct ifnet *)x)->if_name -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# define IPF_PANIC(x,y) -typedef struct mbuf mb_t; -# endif /* _KERNEL */ - -/* - * These are from's Solaris' #defines for little endian. - */ -#if !defined(IP6F_MORE_FRAG) -# define IP6F_MORE_FRAG 0x0100 -#endif -#if !defined(IP6F_RESERVED_MASK) -# define IP6F_RESERVED_MASK 0x0600 -#endif -#if !defined(IP6F_OFF_MASK) -# define IP6F_OFF_MASK 0xf8ff -#endif - -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; - -typedef int ioctlcmd_t; -typedef int minor_t; -/* - * Really, any arch where sizeof(long) != sizeof(int). - */ -typedef unsigned int u_32_t; -# define U_32_T 1 - -# define OS_RECOGNISED 1 -#endif /* _AIX51 */ - - -#ifndef OS_RECOGNISED -#error ip_compat.h does not recognise this platform/OS. -#endif - /* ----------------------------------------------------------------------- */ /* G E N E R I C */ /* ----------------------------------------------------------------------- */ -#ifndef OS_RECOGNISED -#endif /* * For BSD kernels, if bpf is in the kernel, enable ipfilter to use bpf in @@ -1427,15 +261,21 @@ typedef unsigned int u_32_t; /* * 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; -#if defined(__hpux) || defined(__linux) - char eMm_fill[8]; -#endif } eMmutex_t; typedef struct { @@ -1445,26 +285,25 @@ typedef struct { short eMrw_read; short eMrw_write; int eMrw_heldat; -#ifdef __hpux - char eMm_fill[24]; -#endif } eMrwlock_t; typedef union { + char _fill[KMUTEX_FILL_SZ]; #ifdef KMUTEX_T struct { KMUTEX_T ipf_slk; - char *ipf_lname; + 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; - char *ipf_lname; + const char *ipf_lname; int ipf_sr; int ipf_sw; u_int ipf_magic; @@ -1488,14 +327,12 @@ typedef union { # define INLINE __inline__ #endif -#if defined(linux) && defined(_KERNEL) -extern void ipf_read_enter __P((ipfrwlock_t *)); -extern void ipf_write_enter __P((ipfrwlock_t *)); -extern void ipf_rw_exit __P((ipfrwlock_t *)); -extern void ipf_rw_init __P((ipfrwlock_t *, char *)); -extern void ipf_rw_downgrade __P((ipfrwlock_t *)); +#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, @@ -1504,18 +341,40 @@ extern void ipf_rw_downgrade __P((ipfrwlock_t *)); #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 -# define MSGDSIZE(x) (x)->mb_len /* XXX - from ipt.c */ -# define M_LEN(x) (x)->mb_len -# define M_DUPLICATE(x) (x) +# 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) -# undef MTOD -# define MTOD(m, t) ((t)(m)->mb_buf) -# define FREE_MB_T(x) +# 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) ; @@ -1530,6 +389,8 @@ typedef struct mb_s { # 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), \ @@ -1541,16 +402,18 @@ typedef struct mb_s { 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)); -# ifndef CACHE_HASH -# define CACHE_HASH(x) ((IFNAME(fin->fin_ifp)[0] + \ - ((struct ifnet *)fin->fin_ifp)->if_unit) & 7) -# endif +extern mb_t *allocmbt __P((size_t)); +extern mb_t *dupmbt __P((mb_t *)); +extern void freembt __P((mb_t *)); -# define MUTEX_DESTROY(x) eMmutex_destroy(&(x)->ipf_emu) +# 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) -# define MUTEX_INIT(x,y) eMmutex_init(&(x)->ipf_emu, y) +# 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, \ @@ -1566,10 +429,10 @@ extern int bcopywrap __P((void *, void *, size_t)); # define USE_MUTEXES 1 -extern void eMmutex_destroy __P((eMmutex_t *)); +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 *)); -extern void eMmutex_init __P((eMmutex_t *, char *)); +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 *)); @@ -1579,6 +442,8 @@ extern void eMrwlock_downgrade __P((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 @@ -1590,13 +455,15 @@ extern void eMrwlock_downgrade __P((eMrwlock_t *, char *, int)); * On BSD's use quad_t as a guarantee for getting at least a 64bit sized * object. */ -#if (BSD > 199306) +#if !defined(__amd64__) && BSD_GT_YEAR(199306) # define USE_QUAD_T # define U_QUAD_T unsigned long long # define QUAD_T long long #else /* BSD > 199306 */ -# define U_QUAD_T u_long -# define QUAD_T long +# if !defined(U_QUAD_T) +# define U_QUAD_T u_long +# define QUAD_T long +# endif #endif /* BSD > 199306 */ @@ -1605,11 +472,9 @@ extern void eMrwlock_downgrade __P((eMrwlock_t *, char *, int)); defined(__osf__) || defined(linux) # include <netinet/ip6.h> # include <netinet/icmp6.h> -# if !defined(linux) # if defined(_KERNEL) && !defined(__osf__) # include <netinet6/ip6_var.h> # endif -# endif typedef struct ip6_hdr ip6_t; # endif #endif @@ -1619,23 +484,20 @@ typedef struct ip6_hdr ip6_t; #endif #if defined(_KERNEL) -# ifdef MENTAT +# if defined(MENTAT) && !defined(INSTANCES) # define COPYDATA mb_copydata # define COPYBACK mb_copyback # else # define COPYDATA m_copydata # define COPYBACK m_copyback # endif -# if (BSD >= 199306) || defined(__FreeBSD__) # if (defined(__NetBSD_Version__) && (__NetBSD_Version__ < 105180000)) || \ defined(__FreeBSD__) || (defined(OpenBSD) && (OpenBSD < 200206)) || \ defined(_BSDI_VERSION) # include <vm/vm.h> # endif -# if !defined(__FreeBSD__) || (defined (__FreeBSD_version) && \ - (__FreeBSD_version >= 300000)) -# if (defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 105180000)) || \ - (defined(OpenBSD) && (OpenBSD >= 200111)) +# if !defined(__FreeBSD__) || FREEBSD_GE_REV(300000) +# if NETBSD_GE_REV(105180000) || OPENBSD_GE_REV(200111) # include <uvm/uvm_extern.h> # else # include <vm/vm_extern.h> @@ -1661,33 +523,31 @@ MALLOC_DECLARE(M_IPFILTER); # endif /* M_IPFILTER */ # endif /* M_PFIL */ # endif /* IPFILTER_M_IPFILTER */ -# if defined(__FreeBSD__) && __FreeBSD_version >= 800051 -# define KMALLOC(a, b) do { \ - a = (b)malloc(sizeof(*(a)), _M_IPF, M_NOWAIT); \ - } while (0) -# define KMALLOCS(a, b, c) do { \ - a = (b)malloc((c), _M_IPF, ((c) > 4096) ? M_WAITOK : M_NOWAIT); \ - } while (0) -# define KFREE(x) free((x), _M_IPF) -# define KFREES(x,s) free((x), _M_IPF) -# else +# if !defined(KMALLOC) # define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), _M_IPF, M_NOWAIT) -# if !defined(KMALLOCS) -# define KMALLOCS(a, b, c) MALLOC((a), b, (c), _M_IPF, M_NOWAIT) -# endif +# endif +# if !defined(KMALLOCS) +# define KMALLOCS(a, b, c) MALLOC((a), b, (c), _M_IPF, M_NOWAIT) +# endif +# if !defined(KFREE) # define KFREE(x) FREE((x), _M_IPF) -# define KFREES(x,s) 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) -# define POLLWAKEUP(x) selwakeup(ipfselwait+x) +# if !defined(POLLWAKEUP) +# define POLLWAKEUP(x) selwakeup(softc->ipf_selwait+x) +# endif # define GETIFP(n, v) ifunit(n) -# endif /* (Free)BSD */ +# 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) # if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199407)) || \ - (defined(OpenBSD) && (OpenBSD >= 200006)) + OPENBSD_GE_REV(200006) # define SPL_NET(x) x = splsoftnet() # else # define SPL_IMP(x) x = splimp() @@ -1702,6 +562,45 @@ MALLOC_DECLARE(M_IPFILTER); # 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_DONTWAIT, 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_DONTWAIT, 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) @@ -1725,13 +624,13 @@ MALLOC_DECLARE(M_IPFILTER); #endif /* _KERNEL */ #if !defined(IFNAME) && !defined(_KERNEL) -# define IFNAME(x) ((struct ifnet *)x)->if_name +# define IFNAME(x) get_ifname((struct ifnet *)x) #endif #ifndef COPYIFNAME # define NEED_FRGETIFNAME -extern char *fr_getifname __P((struct ifnet *, char *)); +extern char *ipf_getifname __P((struct ifnet *, char *)); # define COPYIFNAME(v, x, b) \ - fr_getifname((struct ifnet *)x, b) + ipf_getifname((struct ifnet *)x, b) #endif #ifndef ASSERT @@ -1753,9 +652,7 @@ extern char *fr_getifname __P((struct ifnet *, char *)); */ #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)) @@ -1803,11 +700,9 @@ extern char *fr_getifname __P((struct ifnet *, char *)); # define ATOMIC_INCL ATOMIC_INC # define ATOMIC_INC64 ATOMIC_INC # define ATOMIC_INC32 ATOMIC_INC -# define ATOMIC_INC16 ATOMIC_INC # define ATOMIC_DECL ATOMIC_DEC # define ATOMIC_DEC64 ATOMIC_DEC # define ATOMIC_DEC32 ATOMIC_DEC -# define ATOMIC_DEC16 ATOMIC_DEC #endif #ifndef HDR_T_PRIVATE @@ -1824,7 +719,10 @@ typedef struct tcpiphdr tcpiphdr_t; #endif #ifndef offsetof -# define offsetof(t,m) (int)((&((t *)0L)->m)) +# define offsetof(t,m) (size_t)((&((t *)0L)->m)) +#endif +#ifndef stsizeof +# define stsizeof(t,m) sizeof(((t *)0L)->m) #endif /* @@ -1871,9 +769,9 @@ typedef struct tcpiphdr tcpiphdr_t; #define TCPF_ALL (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG|\ TH_ECN|TH_CWR) -#if (BSD >= 199306) && !defined(m_act) +#if BSD_GE_YEAR(199306) && !defined(m_act) # define m_act m_nextpkt -#endif +#endif /* * Security Options for Intenet Protocol (IPSO) as defined in RFC 1108. @@ -1910,7 +808,7 @@ typedef struct tcpiphdr tcpiphdr_t; * IP option #defines */ #undef IPOPT_RR -#define IPOPT_RR 7 +#define IPOPT_RR 7 #undef IPOPT_ZSU #define IPOPT_ZSU 10 /* ZSU */ #undef IPOPT_MTUP @@ -1958,6 +856,8 @@ typedef struct tcpiphdr tcpiphdr_t; #define IPOPT_UMP 152 #undef IPOPT_FINN #define IPOPT_FINN 205 /* FINN */ +#undef IPOPT_AH +#define IPOPT_AH 256+IPPROTO_AH #ifndef TCPOPT_EOL # define TCPOPT_EOL 0 @@ -2236,8 +1136,11 @@ typedef struct tcpiphdr tcpiphdr_t; #ifndef IPPROTO_HOPOPTS # define IPPROTO_HOPOPTS 0 #endif +#ifndef IPPROTO_IPIP +# define IPPROTO_IPIP 4 +#endif #ifndef IPPROTO_ENCAP -# define IPPROTO_ENCAP 4 +# define IPPROTO_ENCAP 98 #endif #ifndef IPPROTO_IPV6 # define IPPROTO_IPV6 41 @@ -2436,6 +1339,38 @@ typedef struct tcpiphdr tcpiphdr_t; # 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 */ @@ -2516,14 +1451,50 @@ typedef struct tcpiphdr tcpiphdr_t; # 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 RESCUE -# undef IPFILTER_BPF +#ifndef AF_INET6 +# define AF_INET6 26 #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 new file mode 100644 index 0000000..df863b8 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_dns_pxy.c @@ -0,0 +1,402 @@ +/* + * 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 *)); +int 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 *)); + +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 */ +int +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; + return 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 new file mode 100644 index 0000000..ce2e72e --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_dstlist.c @@ -0,0 +1,1351 @@ +/* + * 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 +#if defined(__osf__) +# define _PROTO_NET_H_ +#endif +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#if !defined(_KERNEL) && !defined(__KERNEL__) +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +#else +# include <sys/systm.h> +# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) +# include <sys/proc.h> +# endif +#endif +#include <sys/time.h> +#if !defined(linux) +# include <sys/protosw.h> +#endif +#include <sys/socket.h> +#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) +# include <sys/mbuf.h> +#endif +#if defined(__SVR4) || defined(__svr4__) +# include <sys/filio.h> +# include <sys/byteorder.h> +# ifdef _KERNEL +# include <sys/dditypes.h> +# endif +# include <sys/stream.h> +# include <sys/kmem.h> +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +#endif + +#include <net/if.h> +#include <netinet/in.h> + +#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 <sys/md5.h> +#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 *)); + +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/sys/contrib/ipfilter/netinet/ip_dstlist.h b/sys/contrib/ipfilter/netinet/ip_dstlist.h new file mode 100644 index 0000000..e2885e5 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_dstlist.h @@ -0,0 +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 *)); + +#endif /* __IP_DSTLIST_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_fil.h b/sys/contrib/ipfilter/netinet/ip_fil.h index 0cd84b9..22a11c3 100644 --- a/sys/contrib/ipfilter/netinet/ip_fil.h +++ b/sys/contrib/ipfilter/netinet/ip_fil.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -12,6 +12,21 @@ #define __IP_FIL_H__ #include "netinet/ip_compat.h" +#include "netinet/ipf_rb.h" +#if NETBSD_GE_REV(104040000) +# include <sys/callout.h> +#endif +#if defined(BSD) && defined(_KERNEL) +# if NETBSD_LT_REV(399000000) || defined(__osf__) || FREEBSD_LT_REV(500043) +# include <sys/select.h> +# else +# include <sys/selinfo.h> +# endif +#endif + +#if !defined(linux) || !defined(_KERNEL) +# include <netinet/in.h> +#endif #ifndef SOLARIS # define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) @@ -63,6 +78,8 @@ # 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) #else # define SIOCADAFR _IOW(r, 60, struct ipfobj) # define SIOCRMAFR _IOW(r, 61, struct ipfobj) @@ -101,6 +118,8 @@ # 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) #endif #define SIOCADDFR SIOCADAFR #define SIOCDELFR SIOCRMAFR @@ -111,9 +130,10 @@ struct ipscan; struct ifnet; +struct ipf_main_softc_s; - -typedef int (* lookupfunc_t) __P((void *, int, void *)); +typedef int (* lookupfunc_t) __P((struct ipf_main_softc_s *, void *, + int, void *, u_int)); /* * i6addr is used as a container for both IPv4 and IPv6 addresses, as well @@ -129,7 +149,7 @@ typedef union i6addr { struct { u_short type; u_short subtype; - char label[12]; + int name; } i6un; } i6addr_t; #else @@ -141,14 +161,14 @@ typedef union i6addr { struct { u_short type; u_short subtype; - char label[12]; + int name; } i6un; } i6addr_t; #endif #define in4_addr in4.s_addr #define iplookupnum i6[1] -#define iplookupname i6un.label +#define iplookupname i6un.name #define iplookuptype i6un.type #define iplookupsubtype i6un.subtype /* @@ -172,17 +192,25 @@ typedef union i6addr { (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_GT(a,b) (HI60(a) > HI60(b) || (HI60(a) == HI60(b) && \ - (HI61(a) > HI61(b) || (HI61(a) == HI61(b) && \ - (HI62(a) > HI62(b) || (HI62(a) == HI62(b) && \ - HI63(a) > HI63(b))))))) -#define IP6_LT(a,b) (HI60(a) < HI60(b) || (HI60(a) == HI60(b) && \ - (HI61(a) < HI61(b) || (HI61(a) == HI61(b) && \ - (HI62(a) < HI62(b) || (HI62(a) == HI62(b) && \ - HI63(a) < HI63(b))))))) +#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) \ - { u_32_t *_i6 = (u_32_t *)(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); \ @@ -193,9 +221,9 @@ typedef union i6addr { } \ } \ } \ - } + } while (0) #define IP6_ADD(a,x,d) \ - { i6addr_t *_s = (i6addr_t *)(a); \ + 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])) { \ @@ -207,26 +235,65 @@ typedef union i6addr { } \ } \ } \ - } -#define IP6_AND(a,b,d) { i6addr_t *_s1 = (i6addr_t *)(a); \ - i6addr_t *_s2 = (i6addr_t *)(d); \ + } 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) \ - { i6addr_t *_d, *_s1, *_s2; \ + 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[2] |= _s1->i6[3] & ~_s2->i6[3]; \ - } - + _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 */ @@ -237,11 +304,13 @@ typedef struct fr_ip { 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 */ - u_short fi_secmsk; /* bitmask composed from IP security options */ - u_short fi_auth; /* authentication code from IP sec. options */ + 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_res1; /* RESERVED */ + 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; /* @@ -263,16 +332,23 @@ typedef struct fr_ip { #define FI_FRAGBODY 0x2000 #define FI_BADSRC 0x4000 #define FI_LOWTTL 0x8000 -#define FI_CMP 0xcf03 /* Not FI_FRAG,FI_NATED,FI_FRAGTAIL,broadcast */ +#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 0xeffe /* Not FI_TCPUDP */ +#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_DONTCACHE 0x40000000 /* don't cache the result */ +#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 @@ -303,37 +379,87 @@ typedef struct fr_ip { #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' */ - fr_ip_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; + struct frentry *fin_fr; /* last matching rule */ 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 fin_tcpf; /* TCP header flags (SYN, ACK, etc) */ - u_char fin_icode; /* ICMP error to return */ + 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 */ - 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; + /* + * 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_short fin_id; /* IP packet id field */ + 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 */ - int fin_cksum; /* -1 bad, 1 good, 0 not done */ - void *fin_nat; - void *fin_state; + 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; - void *fin_exthdr; - ip_t *fin_ip; + 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 @@ -344,35 +470,42 @@ typedef struct fr_info { #ifdef __sgi void *fin_hbuf; #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_src6 fin_fi.fi_src.in6 #define fin_saddr fin_fi.fi_saddr #define fin_dst fin_fi.fi_dst.in4 -#define fin_dst6 fin_fi.fi_dst.in6 #define fin_daddr fin_fi.fi_daddr -#define fin_data fin_dat.fid_16 -#define fin_sport fin_dat.fid_16[0] -#define fin_dport fin_dat.fid_16[1] -#define fin_ports fin_dat.fid_32 +#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 frentry *)); +typedef int (*ipfuncinit_t) __P((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; /* @@ -401,39 +534,78 @@ typedef struct { #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_ifp; - i6addr_t fd_ip6; - char fd_ifname[LIFNAMSIZ]; + void *fd_ptr; + addrfamily_t fd_addr; + fr_dtypes_t fd_type; + int fd_name; + int fd_local; } 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 { - int frp_cmp; /* data for port comparisons */ - u_short frp_port; /* top port for <> and >< */ - u_short frp_top; /* top port for <> and >< */ + fr_ctypes_t frp_cmp; /* data for port comparisons */ + u_32_t frp_port; /* top port for <> and >< */ + u_32_t frp_top; /* top port for <> and >< */ } frpcmp_t; -#define FR_NONE 0 -#define FR_EQUAL 1 -#define FR_NEQUAL 2 -#define FR_LESST 3 -#define FR_GREATERT 4 -#define FR_LESSTE 5 -#define FR_GREATERTE 6 -#define FR_OUTRANGE 7 -#define FR_INRANGE 8 -#define FR_INCRANGE 9 /* * Structure containing all the relevant TCP things that can be checked in @@ -455,23 +627,37 @@ typedef struct frtuc { #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 */ + 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; + u_short fri_icmpm; /* data for ICMP packets (mask) */ + u_short fri_icmp; - frtuc_t fri_tuc; - int fri_satype; /* addres type */ - int fri_datype; /* addres type */ - int fri_sifpidx; /* doing dynamic addressing */ - int fri_difpidx; /* index into fr_ifps[] to use when */ + 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 @@ -483,28 +669,42 @@ typedef struct fripf { #define fri_dstptr fri_mip.fi_dstptr #define fri_srcptr fri_mip.fi_srcptr -#define FRI_NORMAL 0 /* Normal address */ -#define FRI_DYNAMIC 1 /* dynamic address */ -#define FRI_LOOKUP 2 /* address is a pool # */ -#define FRI_RANGE 3 /* address/mask is a range */ -#define FRI_NETWORK 4 /* network address from if */ -#define FRI_BROADCAST 5 /* broadcast address from if */ -#define FRI_PEERADDR 6 /* Peer address for P-to-P */ -#define FRI_NETMASKED 7 /* network address with netmask from if */ +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 { ipfmutex_t fr_lock; struct frentry *fr_next; - struct frentry **fr_grp; + 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 */ - char *fr_comment; /* text comment for rule */ - int fr_ref; /* reference count - for grouping */ + 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 -'' @@ -521,13 +721,18 @@ typedef struct frentry { /* * 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. */ - struct timeval fr_lastpkt; + union { + struct timeval frp_lastpkt; + char frp_bytes[12]; + } fr_lpu; int fr_curpps; union { void *fru_data; - caddr_t fru_caddr; + char *fru_caddr; fripf_t *fru_ipf; frentfunc_t fru_func; } fr_dun; @@ -538,29 +743,38 @@ typedef struct frentry { ipfunc_t fr_func; /* call this function */ int fr_dsize; int fr_pps; - int fr_statemax; /* max reference count */ - u_32_t fr_type; + 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_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_family; 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 */ + int fr_group; /* group to which this rule belongs */ + int fr_grhead; /* group # which this rule starts */ + int fr_ifnames[4]; + int fr_isctag; + int fr_rpc; /* XID Filtering */ ipftag_t fr_nattag; - char fr_ifnames[4][LIFNAMSIZ]; - char fr_isctag[16]; frdest_t fr_tifs[2]; /* "to"/"reply-to" interface */ frdest_t fr_dif; /* duplicate packet interface */ /* - * This must be last and will change after loaded into the kernel. + * These are all options related to stateful filtering */ - u_int fr_cksum; /* checksum on filter rules for performance */ + 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 */ + /* + * 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 @@ -589,12 +803,16 @@ typedef struct frentry { #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 @@ -616,10 +834,10 @@ typedef struct frentry { #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_ifname fr_ifnames[0] -#define fr_oifname fr_ifnames[2] #define fr_ifa fr_ifas[0] #define fr_oifa fr_ifas[2] #define fr_tif fr_tifs[0] @@ -627,33 +845,22 @@ typedef struct frentry { #define FR_NOLOGTAG 0 -#ifndef offsetof -#define offsetof(t,m) (int)((&((t *)0L)->m)) -#endif #define FR_CMPSIZ (sizeof(struct frentry) - \ offsetof(struct frentry, fr_func)) +#define FR_NAME(_f, _n) (_f)->fr_names + (_f)->_n -/* - * fr_type - */ -#define FR_T_NONE 0 -#define FR_T_IPF 1 /* IPF structures */ -#define FR_T_BPFOPC 2 /* BPF opcode */ -#define FR_T_CALLFUNC 3 /* callout to function in fr_func only */ -#define FR_T_COMPIPF 4 /* compiled C code */ -#define FR_T_BUILTIN 0x80000000 /* rule is in kernel space */ /* * fr_flags */ -#define FR_CALL 0x00000 /* call rule */ #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_DIVERT 0x00007 /* divert 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 */ @@ -674,19 +881,19 @@ typedef struct frentry { #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_DUP 0x80000 /* duplicate packet */ +#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_COPIED 0x40000000 /* copied from user space */ -#define FR_INACTIVE 0x80000000 /* only used when flush'ing rules */ #define FR_RETMASK (FR_RETICMP|FR_RETRST|FR_FAKEICMP) #define FR_ISBLOCK(x) (((x) & FR_CMDMASK) == FR_BLOCK) @@ -695,6 +902,7 @@ typedef struct frentry { #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) @@ -712,8 +920,8 @@ typedef struct frentry { * Structure that passes information on what/how to flush to the kernel. */ typedef struct ipfflush { - int ipflu_how; - int ipflu_arg; + int ipflu_how; + int ipflu_arg; } ipfflush_t; @@ -721,12 +929,12 @@ typedef struct ipfflush { * */ 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 */ + 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 { @@ -741,7 +949,43 @@ typedef struct ipfsetctl { * in this single structure so that they can all easily be collected and * copied back as required. */ -typedef struct filterstats { +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 */ @@ -749,8 +993,6 @@ typedef struct filterstats { 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 */ @@ -759,15 +1001,15 @@ typedef struct filterstats { 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_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_t; + u_long fr_blocked[FRB_MAX_VALUE + 1]; +} ipf_statistics_t; /* * Log structure. Each packet header logged is prepended by one of these. @@ -777,6 +1019,7 @@ typedef struct filterstats { 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; @@ -796,18 +1039,19 @@ typedef struct ipflog { #else u_int fl_unit; #endif - u_32_t fl_rule; - u_32_t fl_flags; - u_32_t fl_lflags; - u_32_t fl_logtag; + 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_xxx[2]; /* pad */ - char fl_ifname[LIFNAMSIZ]; + 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 @@ -817,12 +1061,12 @@ typedef struct ipflog { # define IPF_DEFAULT_PASS FR_PASS #endif -#define DEFAULT_IPFLOGSIZE 8192 +#define DEFAULT_IPFLOGSIZE 32768 #ifndef IPFILTER_LOGSIZE # define IPFILTER_LOGSIZE DEFAULT_IPFLOGSIZE #else -# if IPFILTER_LOGSIZE < DEFAULT_IPFLOGSIZE -# error IPFILTER_LOGSIZE too small. Must be >= DEFAULT_IPFLOGSIZE +# if IPFILTER_LOGSIZE < 8192 +# error IPFILTER_LOGSIZE too small. Must be >= 8192 # endif #endif @@ -867,34 +1111,30 @@ typedef struct ipflog { * For SIOCGETFS */ typedef struct friostat { - struct filterstats f_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; /* 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 */ + 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_fin6 f_ipf6[0] #define f_fout f_ipf[1] -#define f_fout6 f_ipf6[1] #define f_acctin f_acct[0] -#define f_acctin6 f_acct6[0] #define f_acctout f_acct[1] -#define f_acctout6 f_acct6[1] #define IPF_FEAT_LKM 0x001 #define IPF_FEAT_LOG 0x002 @@ -916,12 +1156,13 @@ typedef struct optlist { * Group list structure. */ typedef struct frgroup { - struct frgroup *fg_next; - struct frentry *fg_head; - struct frentry *fg_start; - u_32_t fg_flags; - int fg_ref; - char fg_name[FR_GROUPLEN]; + 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) @@ -931,24 +1172,24 @@ typedef struct frgroup { * Used by state and NAT tables */ typedef struct icmpinfo { - u_short ici_id; - u_short ici_seq; - u_char ici_type; + 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; + 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; + 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 @@ -959,9 +1200,9 @@ typedef struct tcpdata { typedef struct tcpinfo { - u_short ts_sport; - u_short ts_dport; - tcpdata_t ts_data[2]; + u_32_t ts_sport; + u_32_t ts_dport; + tcpdata_t ts_data[2]; } tcpinfo_t; @@ -969,16 +1210,28 @@ typedef struct tcpinfo { * Structures to define a GRE header as seen in a packet. */ struct grebits { - 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; - u_short grb_ptype; +#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 { @@ -986,8 +1239,8 @@ typedef struct grehdr { struct grebits gru_bits; u_short gru_flags; } gr_un; - u_short gr_len; - u_short gr_call; + u_short gr_len; + u_short gr_call; } grehdr_t; #define gr_flags gr_un.gru_flags @@ -1006,9 +1259,9 @@ typedef struct grehdr { * GRE information tracked by "keep state" */ typedef struct greinfo { - u_short gs_call[2]; - u_short gs_flags; - u_short gs_ptype; + u_short gs_call[2]; + u_short gs_flags; + u_short gs_ptype; } greinfo_t; #define GRE_REV(x) ((ntohs(x) >> 13) & 7) @@ -1018,11 +1271,11 @@ typedef struct greinfo { * 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; + 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; @@ -1035,14 +1288,15 @@ 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_long tqe_die; /* when this entriy is to die */ - u_long tqe_touched; - int tqe_flags; - int tqe_state[2]; /* current state of this entry */ + 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 /* @@ -1050,45 +1304,46 @@ typedef struct ipftqent { */ typedef struct ipftq { ipfmutex_t ifq_lock; - u_int ifq_ttl; + 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; + 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)(void *); - -/* - * Structure to define address for pool lookups. - */ -typedef struct { - u_char adf_len; - i6addr_t adf_addr; -} addrfamily_t; +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 */ - u_char ipfo_xxxpad[32]; /* reserved for future use */ + 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 */ @@ -1110,18 +1365,32 @@ typedef struct ipfobj { #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_COUNT 20 /* How many #defines are above this? */ +#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; + 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 struct ipftuneable { ipftunevalptr_t ipft_una; const char *ipft_name; @@ -1130,6 +1399,7 @@ typedef struct ipftuneable { int ipft_sz; int ipft_flags; struct ipftuneable *ipft_next; + ipftunefunc_t ipft_func; } ipftuneable_t; #define ipft_addr ipft_una.ipftp_void @@ -1141,13 +1411,6 @@ typedef struct ipftuneable { #define IPFT_RDONLY 1 /* read-only */ #define IPFT_WRDISABLED 2 /* write when disabled only */ -typedef union ipftuneval { - u_long ipftu_long; - u_int ipftu_int; - u_short ipftu_short; - u_char ipftu_char; -} ipftuneval_t; - typedef struct ipftune { void *ipft_cookie; ipftuneval_t ipft_un; @@ -1164,6 +1427,53 @@ typedef struct ipftune { #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 +*/ +#ifdef __hpux +/* HP-UX locking sequence deadlock detection module lock MAJOR ID */ +# define IPF_SMAJ 0 /* temp assignment XXX, not critical */ +#endif + +#if !defined(CDEV_MAJOR) && defined (__FreeBSD_version) && \ + (__FreeBSD_version >= 220000) +# define CDEV_MAJOR 79 +#endif + +/* + * Post NetBSD 1.2 has the PFIL interface for packet filters. This turns + * on those hooks. We don't need any special mods in non-IP Filter code + * with this! + */ +#if (defined(NetBSD) && (NetBSD > 199609) && (NetBSD <= 1991011)) || \ + (defined(NetBSD1_2) && NetBSD1_2 > 1) || \ + (defined(__FreeBSD__) && (__FreeBSD_version >= 500043)) +# if (defined(NetBSD) && NetBSD >= 199905) +# define PFIL_HOOKS +# endif +# ifdef PFIL_HOOKS +# define NETBSD_PF +# endif +#endif + +#ifdef _KERNEL +# define FR_VERBOSE(verb_pr) +# define FR_DEBUG(verb_pr) +#else +extern void ipfkdebug __P((char *, ...)); +extern void ipfkverbose __P((char *, ...)); +# define FR_VERBOSE(verb_pr) ipfkverbose verb_pr +# define FR_DEBUG(verb_pr) ipfkdebug verb_pr +#endif + +/* * */ typedef struct ipfruleiter { @@ -1171,7 +1481,7 @@ typedef struct ipfruleiter { char iri_group[FR_GROUPLEN]; int iri_active; int iri_nrules; - int iri_v; + int iri_v; /* No longer used (compatibility) */ frentry_t *iri_rule; } ipfruleiter_t; @@ -1210,6 +1520,19 @@ typedef struct ipftable { #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; + + /* * */ @@ -1222,190 +1545,246 @@ typedef struct ipftoken { int ipt_type; int ipt_uid; int ipt_subtype; - int ipt_alive; + int ipt_ref; + int ipt_complete; } ipftoken_t; /* -** HPUX Port -*/ -#ifdef __hpux -/* HP-UX locking sequence deadlock detection module lock MAJOR ID */ -# define IPF_SMAJ 0 /* temp assignment XXX, not critical */ -#endif - -#if !defined(CDEV_MAJOR) && defined (__FreeBSD_version) && \ - (__FreeBSD_version >= 220000) -# define CDEV_MAJOR 79 -#endif + * + */ +typedef struct ipfexp { + int ipfe_cmd; + int ipfe_not; + int ipfe_narg; + int ipfe_size; + int ipfe_arg0[1]; +} ipfexp_t; /* - * Post NetBSD 1.2 has the PFIL interface for packet filters. This turns - * on those hooks. We don't need any special mods in non-IP Filter code - * with this! + * 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 */ -#if (defined(NetBSD) && (NetBSD > 199609) && (NetBSD <= 1991011)) || \ - (defined(NetBSD1_2) && NetBSD1_2 > 1) || \ - (defined(__FreeBSD__) && (__FreeBSD_version >= 500043)) -# if defined(NetBSD) && (NetBSD >= 199905) -# define PFIL_HOOKS -# endif -# ifdef PFIL_HOOKS -# define NETBSD_PF +#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) && (__FreeBSD_version >= 300000) && \ + defined(_KERNEL) + struct callout_handle ipf_slow_ch; +#endif +#if defined(linux) && defined(_KERNEL) + struct timer_list ipf_timer; +#endif +#if NETBSD_GE_REV(104040000) + struct callout ipf_slow_ch; +#endif +#if SOLARIS +# if SOLARIS2 >= 7 + timeout_id_t ipf_slow_ch; +# else + int ipf_slow_ch; # endif #endif - -#ifdef _KERNEL -# define FR_VERBOSE(verb_pr) -# define FR_DEBUG(verb_pr) -#else -extern void debug __P((char *, ...)); -extern void verbose __P((char *, ...)); -# define FR_VERBOSE(verb_pr) verbose verb_pr -# define FR_DEBUG(verb_pr) debug verb_pr +#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 +# if defined(linux) && defined(_KERNEL) + struct poll_table_struct ipf_selwait[IPL_LOGSIZE]; + wait_queue_head_t iplh_linux[IPL_LOGSIZE]; +# else + struct selinfo ipf_selwait[IPL_LOGSIZE]; +# endif +# 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 fr_check __P((struct ip *, int, void *, int, mb_t **)); -extern int (*fr_checkp) __P((ip_t *, int, void *, int, mb_t **)); -extern int ipf_log __P((void)); +extern int ipf_check __P((void *, struct ip *, int, void *, int, mb_t **)); +extern int (*ipf_checkp) __P((ip_t *, int, void *, int, mb_t **)); extern struct ifnet *get_unit __P((char *, int)); extern char *get_ifname __P((struct ifnet *)); -# if defined(__NetBSD__) || defined(__OpenBSD__) || \ - (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) -extern int iplioctl __P((int, ioctlcmd_t, caddr_t, int)); -# else -extern int iplioctl __P((int, ioctlcmd_t, caddr_t, int)); -# endif -extern int iplopen __P((dev_t, int)); -extern int iplclose __P((dev_t, int)); +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)); #else /* #ifndef _KERNEL */ -# ifdef BSD -# if (defined(__NetBSD__) && (__NetBSD_Version__ < 399000000)) || \ - defined(__osf__) || \ - (defined(__FreeBSD_version) && (__FreeBSD_version < 500043)) -# include <sys/select.h> -# else -# include <sys/selinfo.h> -# endif -extern struct selinfo ipfselwait[IPL_LOGSIZE]; -# endif # if defined(__NetBSD__) && defined(PFIL_HOOKS) extern void ipfilterattach __P((int)); # endif extern int ipl_enable __P((void)); extern int ipl_disable __P((void)); -extern int ipf_inject __P((fr_info_t *, mb_t *)); # ifdef MENTAT -extern int fr_check __P((struct ip *, int, void *, int, void *, - mblk_t **)); +extern int ipf_check __P((void *, struct ip *, int, void *, int, void *, + mblk_t **)); # if SOLARIS +extern void ipf_prependmbt(fr_info_t *, mblk_t *); # if SOLARIS2 >= 7 -extern int iplioctl __P((dev_t, int, intptr_t, int, cred_t *, int *)); +extern int ipfioctl __P((dev_t, int, intptr_t, int, cred_t *, int *)); # else -extern int iplioctl __P((dev_t, int, int *, int, cred_t *, int *)); +extern int ipfioctl __P((dev_t, int, int *, int, cred_t *, int *)); # endif -extern int iplopen __P((dev_t *, int, int, cred_t *)); -extern int iplclose __P((dev_t, int, int, cred_t *)); -extern int iplread __P((dev_t, uio_t *, cred_t *)); -extern int iplwrite __P((dev_t, uio_t *, cred_t *)); # endif # ifdef __hpux -extern int iplopen __P((dev_t, int, intptr_t, int)); -extern int iplclose __P((dev_t, int, int)); -extern int iplioctl __P((dev_t, int, caddr_t, int)); -extern int iplread __P((dev_t, uio_t *)); -extern int iplwrite __P((dev_t, uio_t *)); -extern int iplselect __P((dev_t, int)); +extern int ipfioctl __P((dev_t, int, caddr_t, int)); +extern int ipf_select __P((dev_t, int)); # endif -extern int fr_qout __P((queue_t *, mblk_t *)); +extern int ipf_qout __P((queue_t *, mblk_t *)); # else /* MENTAT */ -extern int fr_check __P((struct ip *, int, void *, int, mb_t **)); +extern int ipf_check __P((void *, struct ip *, int, void *, int, mb_t **)); extern int (*fr_checkp) __P((ip_t *, int, void *, int, mb_t **)); extern size_t mbufchainlen __P((mb_t *)); # ifdef __sgi # include <sys/cred.h> -extern int iplioctl __P((dev_t, int, caddr_t, int, cred_t *, int *)); -extern int iplopen __P((dev_t *, int, int, cred_t *)); -extern int iplclose __P((dev_t, int, int, cred_t *)); -extern int iplread __P((dev_t, uio_t *, cred_t *)); -extern int iplwrite __P((dev_t, uio_t *, cred_t *)); +extern int ipfioctl __P((dev_t, int, caddr_t, int, cred_t *, int *)); extern int ipfilter_sgi_attach __P((void)); extern void ipfilter_sgi_detach __P((void)); extern void ipfilter_sgi_intfsync __P((void)); # else # ifdef IPFILTER_LKM -extern int iplidentify __P((char *)); +extern int ipf_identify __P((char *)); # endif -# if (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199510) || \ - (__FreeBSD_version >= 220000) || \ - (NetBSD >= 199511) || defined(__OpenBSD__) -# if defined(__NetBSD__) || \ - (defined(_BSDI_VERSION) && _BSDI_VERSION >= 199701) || \ - defined(__OpenBSD__) || (__FreeBSD_version >= 300000) +# if BSDOS_GE_REV(199510) || FREEBSD_GE_REV(220000) || \ + (defined(NetBSD) && (NetBSD >= 199511)) || defined(__OpenBSD__) +# if defined(__NetBSD__) || BSDOS_GE_REV(199701) || \ + defined(__OpenBSD__) || FREEBSD_GE_REV(300000) # if (__FreeBSD_version >= 500024) # if (__FreeBSD_version >= 502116) -extern int iplioctl __P((struct cdev*, u_long, caddr_t, int, struct thread *)); +extern int ipfioctl __P((struct cdev*, u_long, caddr_t, int, struct thread *)); # else -extern int iplioctl __P((dev_t, u_long, caddr_t, int, struct thread *)); +extern int ipfioctl __P((dev_t, u_long, caddr_t, int, struct thread *)); # endif /* __FreeBSD_version >= 502116 */ # else -# if (__NetBSD_Version__ >= 499001000) -extern int iplioctl __P((dev_t, u_long, void *, int, struct lwp *)); +# if NETBSD_GE_REV(499001000) +extern int ipfioctl __P((dev_t, u_long, void *, int, struct lwp *)); # else -# if (__NetBSD_Version__ >= 399001400) -extern int iplioctl __P((dev_t, u_long, caddr_t, int, struct lwp *)); +# if NETBSD_GE_REV(399001400) +extern int ipfioctl __P((dev_t, u_long, caddr_t, int, struct lwp *)); # else -extern int iplioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); +extern int ipfioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); # endif # endif # endif /* __FreeBSD_version >= 500024 */ # else -extern int iplioctl __P((dev_t, int, caddr_t, int, struct thread *)); +extern int ipfioctl __P((dev_t, int, caddr_t, int, struct proc *)); # endif -# if (__FreeBSD_version >= 500024) -# if (__FreeBSD_version >= 502116) -extern int iplopen __P((struct cdev*, int, int, struct thread *)); -extern int iplclose __P((struct cdev*, int, int, struct thread *)); -# else -extern int iplopen __P((dev_t, int, int, struct thread *)); -extern int iplclose __P((dev_t, int, int, struct thread *)); -# endif /* __FreeBSD_version >= 502116 */ -# else -# if (__NetBSD_Version__ >= 399001400) -extern int iplopen __P((dev_t, int, int, struct lwp *)); -extern int iplclose __P((dev_t, int, int, struct lwp *)); -# else -extern int iplopen __P((dev_t, int, int, struct proc *)); -extern int iplclose __P((dev_t, int, int, struct proc *)); -# endif /* __NetBSD_Version__ >= 399001400 */ -# endif /* __FreeBSD_version >= 500024 */ # else # ifdef linux -extern int iplioctl __P((struct inode *, struct file *, u_int, u_long)); +extern int ipfioctl __P((struct inode *, struct file *, u_int, u_long)); # else -extern int iplopen __P((dev_t, int)); -extern int iplclose __P((dev_t, int)); -extern int iplioctl __P((dev_t, int, caddr_t, int)); +extern int ipfioctl __P((dev_t, int, caddr_t, int)); # endif # endif /* (_BSDI_VERSION >= 199510) */ -# if BSD >= 199306 -# if (__FreeBSD_version >= 502116) -extern int iplread __P((struct cdev*, struct uio *, int)); -extern int iplwrite __P((struct cdev*, struct uio *, int)); -# else -extern int iplread __P((dev_t, struct uio *, int)); -extern int iplwrite __P((dev_t, struct uio *, int)); -# endif /* __FreeBSD_version >= 502116 */ -# else -# ifndef linux -extern int iplread __P((dev_t, struct uio *)); -extern int iplwrite __P((dev_t, struct uio *)); -# endif -# endif /* BSD >= 199306 */ # endif /* __ sgi */ # endif /* MENTAT */ @@ -1416,153 +1795,206 @@ extern void ipf_event_reg __P((void)); extern void ipf_event_dereg __P((void)); # endif -#endif /* #ifndef _KERNEL */ +# 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 *)); +# endif -extern ipfmutex_t ipl_mutex, ipf_authmx, ipf_rw, ipf_hostmap; -extern ipfmutex_t ipf_timeoutlock, ipf_stinsert, ipf_natio, ipf_nat_new; -extern ipfrwlock_t ipf_mutex, ipf_global, ip_poolrw, ipf_ipidfrag; -extern ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth; -extern ipfrwlock_t ipf_frcache, ipf_tokens; +#endif /* #ifndef _KERNEL */ extern char *memstr __P((const char *, char *, size_t, size_t)); extern int count4bits __P((u_32_t)); -extern int frrequest __P((int, ioctlcmd_t, caddr_t, int, int)); +#ifdef USE_INET6 +extern int count6bits __P((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((void)); -extern int ipfdetach __P((void)); +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((void *, void *, size_t)); -extern int copyoutptr __P((void *, void *, size_t)); -extern int fr_fastroute __P((mb_t *, mb_t **, fr_info_t *, frdest_t *)); -extern int fr_inobj __P((void *, void *, int)); -extern int fr_inobjsz __P((void *, void *, int, int)); -extern int fr_ioctlswitch __P((int, void *, ioctlcmd_t, int, int, void *)); -extern int fr_ipf_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern int fr_ipftune __P((ioctlcmd_t, void *)); -extern int fr_outobj __P((void *, void *, int)); -extern int fr_outobjsz __P((void *, void *, int, int)); -extern void *fr_pullup __P((mb_t *, fr_info_t *, int)); -extern void fr_resolvedest __P((struct frdest *, int)); -extern int fr_resolvefunc __P((void *)); -extern void *fr_resolvenic __P((char *, int)); -extern int fr_send_icmp_err __P((int, fr_info_t *, int)); -extern int fr_send_reset __P((fr_info_t *)); -#if (__FreeBSD_version < 501000) || !defined(_KERNEL) -extern int ppsratecheck __P((struct timeval *, int *, 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 *)); +#if (defined(__FreeBSD_version) && (__FreeBSD_version < 501000)) || \ + !defined(_KERNEL) || defined(linux) #endif -extern ipftq_t *fr_addtimeoutqueue __P((ipftq_t **, u_int)); -extern void fr_deletequeueentry __P((ipftqent_t *)); -extern int fr_deletetimeoutqueue __P((ipftq_t *)); -extern void fr_freetimeoutqueue __P((ipftq_t *)); -extern void fr_movequeue __P((ipftqent_t *, ipftq_t *, ipftq_t *)); -extern void fr_queueappend __P((ipftqent_t *, ipftq_t *, void *)); -extern void fr_queueback __P((ipftqent_t *)); -extern void fr_queuefront __P((ipftqent_t *)); -extern void fr_checkv4sum __P((fr_info_t *)); -extern int fr_checkl4sum __P((fr_info_t *)); -extern int fr_ifpfillv4addr __P((int, struct sockaddr_in *, +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 *, struct sockaddr_in *, struct in_addr *, struct in_addr *)); -extern int fr_coalesce __P((fr_info_t *)); +extern int ipf_coalesce __P((fr_info_t *)); #ifdef USE_INET6 -extern void fr_checkv6sum __P((fr_info_t *)); -extern int fr_ifpfillv6addr __P((int, struct sockaddr_in6 *, - struct sockaddr_in6 *, struct in_addr *, - struct in_addr *)); +extern int ipf_checkv6sum __P((fr_info_t *)); +extern int ipf_ifpfillv6addr __P((int, struct sockaddr_in6 *, + struct sockaddr_in6 *, i6addr_t *, + i6addr_t *)); #endif -extern int fr_addipftune __P((ipftuneable_t *)); -extern int fr_delipftune __P((ipftuneable_t *)); - -extern int frflush __P((minor_t, int, int)); -extern void frsync __P((void *)); -extern frgroup_t *fr_addgroup __P((char *, void *, u_32_t, minor_t, int)); -extern int fr_derefrule __P((frentry_t **)); -extern void fr_delgroup __P((char *, minor_t, int)); -extern frgroup_t *fr_findgroup __P((char *, minor_t, int, frgroup_t ***)); - -extern int fr_loginit __P((void)); -extern int ipflog_canread __P((int)); -extern int ipflog_clear __P((minor_t)); -extern int ipflog_read __P((minor_t, uio_t *)); -extern int ipflog __P((fr_info_t *, u_int)); -extern int ipllog __P((int, fr_info_t *, void **, size_t *, int *, int)); -extern void fr_logunload __P((void)); - -extern frentry_t *fr_acctpkt __P((fr_info_t *, u_32_t *)); -extern int fr_copytolog __P((int, char *, int)); -extern u_short fr_cksum __P((mb_t *, ip_t *, int, void *, int)); -extern void fr_deinitialise __P((void)); -extern frentry_t *fr_dolog __P((fr_info_t *, u_32_t *)); -extern frentry_t *fr_dstgrpmap __P((fr_info_t *, u_32_t *)); -extern void fr_fixskip __P((frentry_t **, frentry_t *, int)); -extern void fr_forgetifp __P((void *)); -extern frentry_t *fr_getrulen __P((int, char *, u_32_t)); -extern void fr_getstat __P((struct friostat *)); -extern int fr_ifpaddr __P((int, int, void *, - struct in_addr *, struct in_addr *)); -extern int fr_initialise __P((void)); -extern int fr_lock __P((caddr_t, int *)); -extern int fr_makefrip __P((int, ip_t *, fr_info_t *)); -extern int fr_matchtag __P((ipftag_t *, ipftag_t *)); -extern int fr_matchicmpqueryreply __P((int, icmpinfo_t *, - struct icmp *, int)); -extern u_32_t fr_newisn __P((fr_info_t *)); -extern u_short fr_nextipid __P((fr_info_t *)); -extern int ipf_queueflush __P((ipftq_delete_fn_t, ipftq_t *, ipftq_t *)); -extern int fr_rulen __P((int, frentry_t *)); -extern int fr_scanlist __P((fr_info_t *, u_32_t)); -extern frentry_t *fr_srcgrpmap __P((fr_info_t *, u_32_t *)); -extern int fr_tcpudpchk __P((fr_info_t *, frtuc_t *)); -extern int fr_verifysrc __P((fr_info_t *fin)); -extern int fr_zerostats __P((void *)); -extern ipftoken_t *ipf_findtoken __P((int, int, void *)); -extern int ipf_getnextrule __P((ipftoken_t *, void *)); -extern void ipf_expiretokens __P((void)); -extern void ipf_freetoken __P((ipftoken_t *)); -extern int ipf_deltoken __P((int,int, void *)); -extern int ipfsync __P((void)); -extern int ipf_genericiter __P((void *, int, void *)); -#ifndef ipf_random -extern u_32_t ipf_random __P((void)); +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 *)); +#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 *)); #endif -#ifdef NEED_LOCAL_RAND -extern void ipf_rand_push __P((void *, int)); +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_short ipf_nextipid __P((fr_info_t *)); +extern u_int ipf_pcksum __P((fr_info_t *, int, u_int)); +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 *)); +#ifdef IPFILTER_LOOKUP +extern void *ipf_resolvelookup __P((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 int fr_running; -extern u_long fr_frouteok[2]; -extern int fr_pass; -extern int fr_flags; -extern int fr_active; -extern int fr_chksrc; -extern int fr_minttl; -extern int fr_refcnt; -extern int fr_control_forwarding; -extern int fr_update_ipid; -extern int nat_logging; -extern int ipstate_logging; -extern int ipl_suppress; -extern int ipl_logmax; -extern int ipl_logall; -extern int ipl_logsize; -extern u_long fr_ticks; -extern fr_info_t frcache[2][8]; extern char ipfilter_version[]; -extern iplog_t **iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1]; -extern int iplused[IPL_LOGMAX + 1]; -extern struct frentry *ipfilter[2][2], *ipacct[2][2]; #ifdef USE_INET6 -extern struct frentry *ipfilter6[2][2], *ipacct6[2][2]; 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 *)); +#endif extern int icmpreplytype4[ICMP_MAXTYPE + 1]; -extern struct frgroup *ipfgroups[IPL_LOGSIZE][2]; -extern struct filterstats frstats[]; -extern frentry_t *ipfrule_match __P((fr_info_t *)); -extern u_char ipf_iss_secret[32]; -extern ipftuneable_t ipf_tuneables[]; + +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 *)); #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 338b69a..9d26193 100644 --- a/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c +++ b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c @@ -1,13 +1,13 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2003 by Darren Reed. + * 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: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12:51:50 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #if defined(KERNEL) || defined(_KERNEL) @@ -25,60 +25,26 @@ static const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12 # include "opt_random_ip_id.h" #endif #include <sys/param.h> -#if defined(__FreeBSD__) && !defined(__FreeBSD_version) -# if defined(IPFILTER_LKM) -# ifndef __FreeBSD_cc_version -# include <osreldate.h> -# else -# if __FreeBSD_cc_version < 430000 -# include <osreldate.h> -# endif -# endif -# endif -#endif #include <sys/errno.h> #include <sys/types.h> #include <sys/file.h> -#if __FreeBSD_version >= 220000 # include <sys/fcntl.h> # include <sys/filio.h> -#else -# include <sys/ioctl.h> -#endif #include <sys/time.h> #include <sys/systm.h> -#if (__FreeBSD_version >= 300000) # include <sys/dirent.h> -#else -# include <sys/dir.h> -#endif +# include <sys/mbuf.h> +# include <sys/sockopt.h> #if !defined(__hpux) # include <sys/mbuf.h> #endif -#include <sys/protosw.h> #include <sys/socket.h> -#if __FreeBSD_version >= 500043 # include <sys/selinfo.h> -#else -# include <sys/select.h> -#endif -#if __FreeBSD_version >= 800044 # include <netinet/tcp_var.h> -#else -#define V_path_mtu_discovery path_mtu_discovery -#define V_ipforwarding ipforwarding -#endif #include <net/if.h> -#if __FreeBSD_version >= 300000 # include <net/if_var.h> -# if __FreeBSD_version >= 500043 # include <net/netisr.h> -# endif -# if !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif -#endif #include <net/route.h> #include <netinet/in.h> #include <netinet/in_var.h> @@ -92,9 +58,6 @@ static const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12 #include <netinet/udp.h> #include <netinet/tcpip.h> #include <netinet/ip_icmp.h> -#ifndef _KERNEL -# include "netinet/ipf.h" -#endif #include "netinet/ip_compat.h" #ifdef USE_INET6 # include <netinet/icmp6.h> @@ -105,101 +68,94 @@ static const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12 #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" -#ifdef IPFILTER_SYNC #include "netinet/ip_sync.h" -#endif +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" #ifdef IPFILTER_SCAN #include "netinet/ip_scan.h" #endif #include "netinet/ip_pool.h" -#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include <sys/malloc.h> -#endif #include <sys/kernel.h> #ifdef CSUM_DATA_VALID #include <machine/in_cksum.h> #endif extern int ip_optcopy __P((struct ip *, struct ip *)); -#if (__FreeBSD_version > 460000) && (__FreeBSD_version < 800055) -extern int path_mtu_discovery; -#endif # ifdef IPFILTER_M_IPFILTER MALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures"); # endif -#if !defined(__osf__) -extern struct protosw inetsw[]; -#endif - -static int (*fr_savep) __P((ip_t *, int, void *, int, struct mbuf **)); -static int fr_send_ip __P((fr_info_t *, mb_t *, mb_t **)); -# ifdef USE_MUTEXES -ipfmutex_t ipl_mutex, ipf_authmx, ipf_rw, ipf_stinsert; -ipfmutex_t ipf_nat_new, ipf_natio, ipf_timeoutlock; -ipfrwlock_t ipf_mutex, ipf_global, ipf_ipidfrag, ipf_frcache, ipf_tokens; -ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth; -# endif +static u_short ipid = 0; +static int (*ipf_savep) __P((void *, ip_t *, int, void *, int, struct mbuf **)); +static int ipf_send_ip __P((fr_info_t *, mb_t *)); +static void ipf_timer_func __P((void *arg)); int ipf_locks_done = 0; -#if (__FreeBSD_version >= 300000) -struct callout_handle fr_slowtimer_ch; -#endif -struct selinfo ipfselwait[IPL_LOGSIZE]; +ipf_main_softc_t ipfmain; -#if (__FreeBSD_version >= 500011) # include <sys/conf.h> # if defined(NETBSD_PF) # include <net/pfil.h> -# if (__FreeBSD_version < 501108) -# include <netinet/ipprotosw.h> -# endif +# endif /* NETBSD_PF */ /* - * We provide the fr_checkp name just to minimize changes later. + * We provide the ipf_checkp name just to minimize changes later. */ -int (*fr_checkp) __P((ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); -# endif /* NETBSD_PF */ -#endif /* __FreeBSD_version >= 500011 */ +int (*ipf_checkp) __P((void *, ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); -#if (__FreeBSD_version >= 502103) static eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag; static void ipf_ifevent(void *arg); static void ipf_ifevent(arg) -void *arg; + void *arg; { - frsync(NULL); + ipf_sync(arg, NULL); } -#endif -#if (__FreeBSD_version >= 501108) && defined(_KERNEL) static int -fr_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +ipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { struct ip *ip = mtod(*mp, struct ip *); - return fr_check(ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp); + 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 + rv = ipf_check(&ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), + mp); +#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 <netinet/ip6.h> static int -fr_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +ipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { - return (fr_check(mtod(*mp, struct ip *), sizeof(struct ip6_hdr), - ifp, (dir == PFIL_OUT), mp)); + return (ipf_check(&ipfmain, mtod(*mp, struct ip *), + sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp)); } # endif -#endif /* __FreeBSD_version >= 501108 */ #if defined(IPFILTER_LKM) -int iplidentify(s) -char *s; +int ipf_identify(s) + char *s; { if (strcmp(s, "ipl") == 0) return 1; @@ -208,49 +164,67 @@ char *s; #endif /* IPFILTER_LKM */ -int ipfattach() +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 FREEBSD_GE_REV(300000) + softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2); +#else + timeout(ipf_timer_func, softc, hz/2); +#endif + } + 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 (fr_running > 0) { + if (softc->ipf_running > 0) { SPL_X(s); return EBUSY; } - MUTEX_INIT(&ipf_rw, "ipf rw mutex"); - MUTEX_INIT(&ipf_timeoutlock, "ipf timeout queue mutex"); - RWLOCK_INIT(&ipf_ipidfrag, "ipf IP NAT-Frag rwlock"); - RWLOCK_INIT(&ipf_tokens, "ipf token rwlock"); - ipf_locks_done = 1; - - if (fr_initialise() < 0) { + if (ipf_init_all(softc) < 0) { SPL_X(s); return EIO; } - if (fr_checkp != fr_check) { - fr_savep = fr_checkp; - fr_checkp = fr_check; + if (ipf_checkp != ipf_check) { + ipf_savep = ipf_checkp; + ipf_checkp = ipf_check; } - bzero((char *)ipfselwait, sizeof(ipfselwait)); - bzero((char *)frcache, sizeof(frcache)); - fr_running = 1; + bzero((char *)ipfmain.ipf_selwait, sizeof(ipfmain.ipf_selwait)); + softc->ipf_running = 1; - if (fr_control_forwarding & 1) + if (softc->ipf_control_forwarding & 1) V_ipforwarding = 1; + ipid = 0; + SPL_X(s); -#if (__FreeBSD_version >= 300000) - fr_slowtimer_ch = timeout(fr_slowtimer, NULL, - (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); -#else - timeout(fr_slowtimer, NULL, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); -#endif + softc->ipf_slow_ch = timeout(ipf_timer_func, softc, + (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); return 0; } @@ -259,44 +233,32 @@ int ipfattach() * Disable the filter by removing the hooks from the IP input/output * stream. */ -int ipfdetach() +int +ipfdetach(softc) + ipf_main_softc_t *softc; { #ifdef USE_SPL int s; #endif - if (fr_control_forwarding & 2) + + if (softc->ipf_control_forwarding & 2) V_ipforwarding = 0; SPL_NET(s); -#if (__FreeBSD_version >= 300000) - if (fr_slowtimer_ch.callout != NULL) - untimeout(fr_slowtimer, NULL, fr_slowtimer_ch); - bzero(&fr_slowtimer_ch, sizeof(fr_slowtimer_ch)); -#else - untimeout(fr_slowtimer, NULL); -#endif /* FreeBSD */ + if (softc->ipf_slow_ch.callout != NULL) + untimeout(ipf_timer_func, softc, softc->ipf_slow_ch); + bzero(&softc->ipf_slow, sizeof(softc->ipf_slow)); #ifndef NETBSD_PF - if (fr_checkp != NULL) - fr_checkp = fr_savep; - fr_savep = NULL; + if (ipf_checkp != NULL) + ipf_checkp = ipf_savep; + ipf_savep = NULL; #endif - fr_deinitialise(); - - fr_running = -2; - - (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); - (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE); + ipf_fini_all(softc); - if (ipf_locks_done == 1) { - MUTEX_DESTROY(&ipf_timeoutlock); - MUTEX_DESTROY(&ipf_rw); - RW_DESTROY(&ipf_ipidfrag); - RW_DESTROY(&ipf_tokens); - ipf_locks_done = 0; - } + softc->ipf_running = -2; SPL_X(s); @@ -307,62 +269,51 @@ int ipfdetach() /* * Filter ioctl interface. */ -int iplioctl(dev, cmd, data, mode -# if defined(_KERNEL) && ((BSD >= 199506) || (__FreeBSD_version >= 220000)) +int +ipfioctl(dev, cmd, data, mode , p) -# if (__FreeBSD_version >= 500024) -struct thread *p; -# if (__FreeBSD_version >= 500043) + struct thread *p; # define p_cred td_ucred # define p_uid td_ucred->cr_ruid -# else -# define p_cred t_proc->p_cred -# define p_uid t_proc->p_cred->p_ruid -# endif -# else -struct proc *p; -# define p_uid p_cred->p_ruid -# endif /* __FreeBSD_version >= 500024 */ -# else -) -# endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -ioctlcmd_t cmd; -caddr_t data; -int mode; + struct cdev *dev; + ioctlcmd_t cmd; + caddr_t data; + int mode; { int error = 0, unit = 0; SPL_INT(s); -#if (BSD >= 199306) && defined(_KERNEL) -# if (__FreeBSD_version >= 500034) - if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) -# else - if ((securelevel >= 3) && (mode & FWRITE)) -# endif +#if (BSD >= 199306) + if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) + { + ipfmain.ipf_interror = 130001; return EPERM; + } #endif unit = GET_MINOR(dev); - if ((IPL_LOGMAX < unit) || (unit < 0)) + if ((IPL_LOGMAX < unit) || (unit < 0)) { + ipfmain.ipf_interror = 130002; return ENXIO; + } - if (fr_running <= 0) { - if (unit != IPL_LOGIPF) + if (ipfmain.ipf_running <= 0) { + if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) { + ipfmain.ipf_interror = 130003; return EIO; + } if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && cmd != SIOCIPFSET && cmd != SIOCFRENB && - cmd != SIOCGETFS && cmd != SIOCGETFF) + cmd != SIOCGETFS && cmd != SIOCGETFF && + cmd != SIOCIPFINTERROR) { + ipfmain.ipf_interror = 130004; return EIO; + } } SPL_NET(s); - error = fr_ioctlswitch(unit, data, cmd, mode, p->p_uid, p); + error = ipf_ioctlswitch(&ipfmain, unit, data, cmd, mode, p->p_uid, p); if (error != -1) { SPL_X(s); return error; @@ -374,182 +325,13 @@ int mode; } -#if 0 -void fr_forgetifp(ifp) -void *ifp; -{ - register frentry_t *f; - - WRITE_ENTER(&ipf_mutex); - for (f = ipacct[0][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipacct[1][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipfilter[0][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipfilter[1][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; -#ifdef USE_INET6 - for (f = ipacct6[0][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipacct6[1][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipfilter6[0][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; - for (f = ipfilter6[1][fr_active]; (f != NULL); f = f->fr_next) - if (f->fr_ifa == ifp) - f->fr_ifa = (void *)-1; -#endif - RWLOCK_EXIT(&ipf_mutex); - fr_natsync(ifp); -} -#endif - - -/* - * routines below for saving IP headers to buffer - */ -int iplopen(dev, flags -#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) -, devtype, p) -int devtype; -# if (__FreeBSD_version >= 500024) -struct thread *p; -# else -struct proc *p; -# endif /* __FreeBSD_version >= 500024 */ -#else -) -#endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -int flags; -{ - u_int min = GET_MINOR(dev); - - if (IPL_LOGMAX < min) - min = ENXIO; - else - min = 0; - return min; -} - - -int iplclose(dev, flags -#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) -, devtype, p) -int devtype; -# if (__FreeBSD_version >= 500024) -struct thread *p; -# else -struct proc *p; -# endif /* __FreeBSD_version >= 500024 */ -#else -) -#endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -int flags; -{ - u_int min = GET_MINOR(dev); - - if (IPL_LOGMAX < min) - min = ENXIO; - else - min = 0; - return min; -} - -/* - * iplread/ipllog - * both of these must operate with at least splnet() lest they be - * called during packet processing and cause an inconsistancy to appear in - * the filter lists. - */ -#if (BSD >= 199306) -int iplread(dev, uio, ioflag) -int ioflag; -#else -int iplread(dev, uio) -#endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -register struct uio *uio; -{ - u_int xmin = GET_MINOR(dev); - - if (fr_running < 1) - return EIO; - - if (xmin < 0) - return ENXIO; - -# ifdef IPFILTER_SYNC - if (xmin == IPL_LOGSYNC) - return ipfsync_read(uio); -# endif - -#ifdef IPFILTER_LOG - return ipflog_read(xmin, uio); -#else - return ENXIO; -#endif -} - - /* - * iplwrite - * both of these must operate with at least splnet() lest they be - * called during packet processing and cause an inconsistancy to appear in - * the filter lists. - */ -#if (BSD >= 199306) -int iplwrite(dev, uio, ioflag) -int ioflag; -#else -int iplwrite(dev, uio) -#endif -#if defined(_KERNEL) && (__FreeBSD_version >= 502116) -struct cdev *dev; -#else -dev_t dev; -#endif -register struct uio *uio; -{ - - if (fr_running < 1) - return EIO; - -#ifdef IPFILTER_SYNC - if (GET_MINOR(dev) == IPL_LOGSYNC) - return ipfsync_write(uio); -#endif - return ENXIO; -} - - -/* - * fr_send_reset - this could conceivably be a call to tcp_respond(), but that + * 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 fr_send_reset(fin) -fr_info_t *fin; +int +ipf_send_reset(fin) + fr_info_t *fin; { struct tcphdr *tcp, *tcp2; int tlen = 0, hlen; @@ -563,7 +345,7 @@ fr_info_t *fin; if (tcp->th_flags & TH_RST) return -1; /* feedback loop */ - if (fr_checkl4sum(fin) == -1) + if (ipf_checkl4sum(fin) == -1) return -1; tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) + @@ -628,11 +410,11 @@ fr_info_t *fin; ip6->ip6_plen = htons(sizeof(struct tcphdr)); ip6->ip6_nxt = IPPROTO_TCP; ip6->ip6_hlim = 0; - ip6->ip6_src = fin->fin_dst6; - ip6->ip6_dst = fin->fin_src6; + 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 fr_send_ip(fin, m, &m); + return ipf_send_ip(fin, m); } #endif ip->ip_p = IPPROTO_TCP; @@ -640,14 +422,18 @@ fr_info_t *fin; 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 = hlen + sizeof(*tcp2); - return fr_send_ip(fin, m, &m); + ip->ip_len = htons(hlen + sizeof(*tcp2)); + return ipf_send_ip(fin, m); } -static int fr_send_ip(fin, m, mpp) -fr_info_t *fin; -mb_t *m, **mpp; +/* + * 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; @@ -655,24 +441,27 @@ mb_t *m, **mpp; 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 : - fnew.fin_v = 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; -#if (__FreeBSD_version > 460000) - ip->ip_off = V_path_mtu_discovery ? IP_DF : 0; +#if defined(FreeBSD) && (__FreeBSD_version > 460000) + ip->ip_off = htons(path_mtu_discovery ? IP_DF : 0); #else ip->ip_off = 0; #endif ip->ip_ttl = V_ip_defttl; ip->ip_sum = 0; - hlen = sizeof(*oip); break; #ifdef USE_INET6 case 6 : @@ -682,8 +471,10 @@ mb_t *m, **mpp; ip6->ip6_vfc = 0x60; ip6->ip6_hlim = IPDEFTTL; - fnew.fin_v = 6; hlen = sizeof(*ip6); + fnew.fin_p = ip6->ip6_nxt; + fnew.fin_v = 6; + fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen; break; } #endif @@ -698,28 +489,29 @@ mb_t *m, **mpp; fnew.fin_flx = FI_NOCKSUM; fnew.fin_m = m; fnew.fin_ip = ip; - fnew.fin_mp = mpp; + fnew.fin_mp = &m; fnew.fin_hlen = hlen; fnew.fin_dp = (char *)ip + hlen; - (void) fr_makefrip(hlen, ip, &fnew); + (void) ipf_makefrip(hlen, ip, &fnew); - return fr_fastroute(m, mpp, &fnew, NULL); + return ipf_fastroute(m, &m, &fnew, NULL); } -int fr_send_icmp_err(type, fin, dst) -int type; -fr_info_t *fin; -int dst; +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; - struct in6_addr dst6; #endif ip_t *ip, *ip2; @@ -728,11 +520,17 @@ int dst; code = fin->fin_icode; #ifdef USE_INET6 - if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) +#if 0 + /* XXX Fix an off by one error: s/>/>=/ + was: + if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) + Fix obtained from NetBSD ip_fil_netbsd.c r1.4: */ +#endif + if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int))) return -1; #endif - if (fr_checkl4sum(fin) == -1) + if (ipf_checkl4sum(fin) == -1) return -1; #ifdef MGETHDR MGETHDR(m, M_DONTWAIT, MT_HEADER); @@ -746,10 +544,10 @@ int dst; 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)) + if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT)) switch (ntohs(fin->fin_data[0]) >> 8) { case ICMP_ECHO : @@ -763,16 +561,18 @@ int dst; } if (dst == 0) { - if (fr_ifpaddr(4, FRI_NORMAL, ifp, - &dst4, NULL) == -1) { + if (ipf_ifpaddr(&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 @@ -783,12 +583,12 @@ int dst; 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 (hlen + sizeof(*icmp) + max_linkhdr + - fin->fin_plen > avail) { + if (iclen + max_linkhdr + fin->fin_plen > avail) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { FREE_MB_T(m); @@ -796,11 +596,11 @@ int dst; } avail = MCLBYTES; } - xtra = MIN(fin->fin_plen, - avail - hlen - sizeof(*icmp) - max_linkhdr); + xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr); + xtra = MIN(xtra, IPV6_MMTU - iclen); if (dst == 0) { - if (fr_ifpaddr(6, FRI_NORMAL, ifp, - (struct in_addr *)&dst6, NULL) == -1) { + if (ipf_ifpaddr(&ipfmain, 6, FRI_NORMAL, ifp, + &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } @@ -813,7 +613,6 @@ int dst; return -1; } - iclen = hlen + sizeof(*icmp); avail -= (max_linkhdr + iclen); if (avail < 0) { FREE_MB_T(m); @@ -834,9 +633,17 @@ int dst; icmp->icmp_code = fin->fin_icode; icmp->icmp_cksum = 0; #ifdef icmp_nextmtu - if (type == ICMP_UNREACH && - fin->fin_icode == ICMP_UNREACH_NEEDFRAG && ifp) - icmp->icmp_nextmtu = htons(((struct ifnet *)ifp)->if_mtu); + 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); @@ -848,8 +655,8 @@ int dst; ip6->ip6_plen = htons(iclen - hlen); ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 0; - ip6->ip6_src = dst6; - ip6->ip6_dst = fin->fin_src6; + 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); @@ -858,8 +665,6 @@ int dst; } else #endif { - ip2->ip_len = htons(ip2->ip_len); - ip2->ip_off = htons(ip2->ip_off); ip->ip_p = IPPROTO_ICMP; ip->ip_src.s_addr = dst4.s_addr; ip->ip_dst.s_addr = fin->fin_saddr; @@ -869,41 +674,25 @@ int dst; (char *)&icmp->icmp_ip + ohlen, xtra); icmp->icmp_cksum = ipf_cksum((u_short *)icmp, sizeof(*icmp) + 8); - ip->ip_len = iclen; + ip->ip_len = htons(iclen); ip->ip_p = IPPROTO_ICMP; } - err = fr_send_ip(fin, m, &m); + err = ipf_send_ip(fin, m); return err; } -#if !defined(IPFILTER_LKM) && (__FreeBSD_version < 300000) -# if (BSD < 199306) -int iplinit __P((void)); - -int -# else -void iplinit __P((void)); - -void -# endif -iplinit() -{ - if (ipfattach() != 0) - printf("IP Filter failed to attach\n"); - ip_init(); -} -#endif /* __FreeBSD_version < 300000 */ /* * m0 - pointer to mbuf where the IP packet starts * mpp - pointer to the mbuf pointer that is the start of the mbuf chain */ -int fr_fastroute(m0, mpp, fin, fdp) -mb_t *m0, **mpp; -fr_info_t *fin; -frdest_t *fdp; +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; @@ -913,6 +702,7 @@ frdest_t *fdp; struct sockaddr_in *dst; struct route iproute; u_short ip_off; + frdest_t node; frentry_t *fr; ro = NULL; @@ -949,33 +739,36 @@ frdest_t *fdp; * currently "to <if>" and "to <if>:ip#" are not supported * for IPv6 */ -#if (__FreeBSD_version >= 490000) - return ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); -#else - return ip6_output(m0, NULL, NULL, 0, NULL, NULL); -#endif + return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); } #endif hlen = fin->fin_hlen; ip = mtod(m0, struct ip *); + ifp = NULL; /* * Route packet. */ ro = &iproute; - bzero((caddr_t)ro, sizeof (*ro)); + bzero(ro, sizeof (*ro)); dst = (struct sockaddr_in *)&ro->ro_dst; dst->sin_family = AF_INET; dst->sin_addr = ip->ip_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_ifp; + ifp = fdp->fd_ptr; else ifp = fin->fin_ifp; - if ((ifp == NULL) && (!fr || !(fr->fr_flags & FR_FASTROUTE))) { + if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) { error = -2; goto bad; } @@ -1012,21 +805,19 @@ frdest_t *fdp; sifp = fin->fin_ifp; fin->fin_ifp = ifp; fin->fin_out = 1; - (void) fr_acctpkt(fin, NULL); + (void) ipf_acctpkt(fin, NULL); fin->fin_fr = NULL; if (!fr || !(fr->fr_flags & FR_RETMASK)) { u_32_t pass; - if (fr_checkstate(fin, &pass) != NULL) - fr_statederef((ipstate_t **)&fin->fin_state); + (void) ipf_state_check(fin, &pass); } - switch (fr_checknatout(fin, NULL)) + switch (ipf_nat_checkout(fin, NULL)) { case 0 : break; case 1 : - fr_natderef((nat_t **)&fin->fin_nat); ip->ip_sum = 0; break; case -1 : @@ -1042,14 +833,12 @@ frdest_t *fdp; /* * If small enough for interface, can just send directly. */ - if (ip->ip_len <= ifp->if_mtu) { - ip->ip_len = htons(ip->ip_len); - ip->ip_off = htons(ip->ip_off); - + 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, - ro); + ro + ); goto done; } /* @@ -1077,7 +866,7 @@ frdest_t *fdp; */ m0 = m; mhlen = sizeof (struct ip); - for (off = hlen + len; off < ip->ip_len; off += len) { + for (off = hlen + len; off < ntohs(ip->ip_len); off += len) { #ifdef MGETHDR MGETHDR(m, M_DONTWAIT, MT_HEADER); #else @@ -1097,8 +886,8 @@ frdest_t *fdp; } m->m_len = mhlen; mhip->ip_off = ((off - hlen) >> 3) + ip_off; - if (off + len >= ip->ip_len) - len = ip->ip_len - 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)); @@ -1130,21 +919,22 @@ sendorfree: m->m_act = 0; if (error == 0) error = (*ifp->if_output)(ifp, m, - (struct sockaddr *)dst, ro); + (struct sockaddr *)dst, + ro + ); else FREE_MB_T(m); } - } + } done: if (!error) - fr_frouteok[0]++; + ipfmain.ipf_frouteok[0]++; else - fr_frouteok[1]++; + ipfmain.ipf_frouteok[1]++; if ((ro != NULL) && (ro->ro_rt != NULL)) { RTFREE(ro->ro_rt); } - *mpp = NULL; return 0; bad: if (error == EMSGSIZE) { @@ -1152,7 +942,7 @@ bad: code = fin->fin_icode; fin->fin_icode = ICMP_UNREACH_NEEDFRAG; fin->fin_ifp = ifp; - (void) fr_send_icmp_err(ICMP_UNREACH, fin, 1); + (void) ipf_send_icmp_err(ICMP_UNREACH, fin, 1); fin->fin_ifp = sifp; fin->fin_icode = code; } @@ -1161,8 +951,9 @@ bad: } -int fr_verifysrc(fin) -fr_info_t *fin; +int +ipf_verifysrc(fin) + fr_info_t *fin; { struct sockaddr_in *dst; struct route iproute; @@ -1182,10 +973,12 @@ fr_info_t *fin; /* * return the first IP Address associated with an interface */ -int fr_ifpaddr(v, atype, ifptr, inp, inpmask) -int v, atype; -void *ifptr; -struct in_addr *inp, *inpmask; +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 *inp6 = NULL; @@ -1202,16 +995,12 @@ struct in_addr *inp, *inpmask; ifp = ifptr; if (v == 4) - inp->s_addr = 0; + inp->in4.s_addr = 0; #ifdef USE_INET6 else if (v == 6) - bzero((char *)inp, sizeof(struct in6_addr)); + bzero((char *)inp, sizeof(*inp)); #endif -#if (__FreeBSD_version >= 300000) ifa = TAILQ_FIRST(&ifp->if_addrhead); -#else - ifa = ifp->if_addrlist; -#endif /* __FreeBSD_version >= 300000 */ sock = ifa->ifa_addr; while (sock != NULL && ifa != NULL) { @@ -1226,11 +1015,7 @@ struct in_addr *inp, *inpmask; break; } #endif -#if (__FreeBSD_version >= 300000) ifa = TAILQ_NEXT(ifa, ifa_link); -#else - ifa = ifa->ifa_next; -#endif /* __FreeBSD_version >= 300000 */ if (ifa != NULL) sock = ifa->ifa_addr; } @@ -1249,79 +1034,45 @@ struct in_addr *inp, *inpmask; #ifdef USE_INET6 if (v == 6) { - return fr_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, - (struct sockaddr_in6 *)mask, - inp, inpmask); + return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, + (struct sockaddr_in6 *)mask, + inp, inpmask); } #endif - return fr_ifpfillv4addr(atype, (struct sockaddr_in *)sock, - (struct sockaddr_in *)mask, inp, inpmask); + return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock, + (struct sockaddr_in *)mask, + &inp->in4, &inpmask->in4); } -u_32_t fr_newisn(fin) -fr_info_t *fin; +u_32_t +ipf_newisn(fin) + fr_info_t *fin; { u_32_t newiss; -#if (__FreeBSD_version >= 400000) newiss = arc4random(); -#else - static iss_seq_off = 0; - u_char hash[16]; - 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; -#endif return newiss; } /* ------------------------------------------------------------------------ */ -/* Function: fr_nextipid */ +/* 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. */ /* ------------------------------------------------------------------------ */ -u_short fr_nextipid(fin) -fr_info_t *fin; +u_short +ipf_nextipid(fin) + fr_info_t *fin; { -#ifndef RANDOM_IP_ID - static u_short ipid = 0; u_short id; - MUTEX_ENTER(&ipf_rw); +#ifndef RANDOM_IP_ID + MUTEX_ENTER(&ipfmain.ipf_rw); id = ipid++; - MUTEX_EXIT(&ipf_rw); + MUTEX_EXIT(&ipfmain.ipf_rw); #else - u_short id; - id = ip_randomid(); #endif @@ -1329,8 +1080,9 @@ fr_info_t *fin; } -INLINE void fr_checkv4sum(fin) -fr_info_t *fin; +INLINE int +ipf_checkv4sum(fin) + fr_info_t *fin; { #ifdef CSUM_DATA_VALID int manual = 0; @@ -1339,10 +1091,13 @@ fr_info_t *fin; mb_t *m; if ((fin->fin_flx & FI_NOCKSUM) != 0) - return; + return 0; - if (fin->fin_cksum != 0) - return; + 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) { @@ -1351,56 +1106,86 @@ fr_info_t *fin; } 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; + return -1; + } if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { 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_ip->ip_len - - (fin->fin_ip->ip_hl << 2) + - fin->fin_p)); + fin->fin_dlen + fin->fin_p)); sum ^= 0xffff; if (sum != 0) { + fin->fin_cksum = FI_CK_BAD; fin->fin_flx |= FI_BAD; - fin->fin_cksum = -1; } else { - fin->fin_cksum = 1; + fin->fin_cksum = FI_CK_SUMOK; + return 0; } - } else - manual = 1; + } 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: -# ifdef IPFILTER_CKSUM - if (manual != 0) - if (fr_checkl4sum(fin) == -1) + if (manual != 0) { + if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; -# else - ; -# endif + return -1; + } + } #else -# ifdef IPFILTER_CKSUM - if (fr_checkl4sum(fin) == -1) + if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; -# endif + return -1; + } #endif + return 0; } #ifdef USE_INET6 -INLINE void fr_checkv6sum(fin) -fr_info_t *fin; +INLINE int +ipf_checkv6sum(fin) + fr_info_t *fin; { -# ifdef IPFILTER_CKSUM - if (fr_checkl4sum(fin) == -1) + 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; + + if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; -# endif + return -1; + } + return 0; } #endif /* USE_INET6 */ -size_t mbufchainlen(m0) -struct mbuf *m0; -{ +size_t +mbufchainlen(m0) + struct mbuf *m0; + { size_t len; if ((m0->m_flags & M_PKTHDR) != 0) { @@ -1416,29 +1201,30 @@ struct mbuf *m0; /* ------------------------------------------------------------------------ */ -/* Function: fr_pullup */ +/* Function: ipf_pullup */ /* Returns: NULL == pullup failed, else pointer to protocol header */ -/* Parameters: m(I) - pointer to buffer where data packet starts */ +/* 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 fr_coalesce() has */ +/* 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 'min' is a pointer to a buffer that is part of the chain */ +/* 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 *fr_pullup(min, fin, len) -mb_t *min; -fr_info_t *fin; -int len; +void * +ipf_pullup(xmin, fin, len) + mb_t *xmin; + fr_info_t *fin; + int len; { - int out = fin->fin_out, dpoff, ipoff; - mb_t *m = min; + int dpoff, ipoff; + mb_t *m = xmin; char *ip; if (m == NULL) @@ -1455,12 +1241,25 @@ int len; dpoff = 0; if (M_LEN(m) < len) { -#ifdef MHLEN + 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) @@ -1472,29 +1271,46 @@ int len; #else FREE_MB_T(*fin->fin_mp); m = NULL; + n = NULL; #endif } else { m = m_pullup(m, len); } - *fin->fin_mp = m; + 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; - ATOMIC_INCL(frstats[out].fr_pull[1]); 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; - } - ATOMIC_INCL(frstats[out].fr_pull[0]); - fin->fin_ip = (ip_t *)ip; - if (fin->fin_dp != NULL) - fin->fin_dp = (char *)fin->fin_ip + dpoff; + 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; @@ -1502,45 +1318,19 @@ int len; } -int ipf_inject(fin, m) -fr_info_t *fin; -mb_t *m; +int +ipf_inject(fin, m) + fr_info_t *fin; + mb_t *m; { int error = 0; if (fin->fin_out == 0) { -#if (__FreeBSD_version >= 501000) netisr_dispatch(NETISR_IP, m); -#else - struct ifqueue *ifq; - - ifq = &ipintrq; - -# ifdef _IF_QFULL - if (_IF_QFULL(ifq)) -# else - if (IF_QFULL(ifq)) -# endif - { -# ifdef _IF_DROP - _IF_DROP(ifq); -# else - IF_DROP(ifq); -# endif - FREE_MB_T(m); - error = ENOBUFS; - } else { - IF_ENQUEUE(ifq, m); - } -#endif } else { fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len); fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off); -#if (__FreeBSD_version >= 470102) error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); -#else - error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); -#endif } return error; @@ -1548,38 +1338,22 @@ mb_t *m; int ipf_pfil_unhook(void) { #if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) -# if __FreeBSD_version >= 501108 struct pfil_head *ph_inet; # ifdef USE_INET6 struct pfil_head *ph_inet6; # endif -# endif #endif #ifdef NETBSD_PF -# if (__FreeBSD_version >= 500011) -# if (__FreeBSD_version >= 501108) ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (ph_inet != NULL) - pfil_remove_hook((void *)fr_check_wrapper, NULL, + pfil_remove_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); -# else - pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); -# endif -# else - pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK); -# endif # ifdef USE_INET6 -# if (__FreeBSD_version >= 501108) ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); if (ph_inet6 != NULL) - pfil_remove_hook((void *)fr_check_wrapper6, NULL, + pfil_remove_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); -# else - pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, - &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); -# endif # endif #endif @@ -1588,17 +1362,13 @@ int ipf_pfil_unhook(void) { int ipf_pfil_hook(void) { #if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) -# if __FreeBSD_version >= 501108 struct pfil_head *ph_inet; # ifdef USE_INET6 struct pfil_head *ph_inet6; # endif -# endif #endif # ifdef NETBSD_PF -# if __FreeBSD_version >= 500011 -# if __FreeBSD_version >= 501108 ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); # ifdef USE_INET6 ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); @@ -1607,28 +1377,17 @@ int ipf_pfil_hook(void) { # ifdef USE_INET6 && ph_inet6 == NULL # endif - ) + ) { return ENODEV; + } if (ph_inet != NULL) - pfil_add_hook((void *)fr_check_wrapper, NULL, + pfil_add_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); -# else - pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); -# endif -# else - pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK); -# endif # ifdef USE_INET6 -# if __FreeBSD_version >= 501108 if (ph_inet6 != NULL) - pfil_add_hook((void *)fr_check_wrapper6, NULL, + pfil_add_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); -# else - pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, - &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); -# endif # endif # endif return (0); @@ -1637,22 +1396,19 @@ int ipf_pfil_hook(void) { void ipf_event_reg(void) { -#if (__FreeBSD_version >= 502103) - ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ - ipf_ifevent, NULL, \ + ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ + ipf_ifevent, &ipfmain, \ EVENTHANDLER_PRI_ANY); - ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ - ipf_ifevent, NULL, \ + ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ + ipf_ifevent, &ipfmain, \ EVENTHANDLER_PRI_ANY); - ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ - NULL, EVENTHANDLER_PRI_ANY); -#endif + ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ + &ipfmain, EVENTHANDLER_PRI_ANY); } void ipf_event_dereg(void) { -#if (__FreeBSD_version >= 502103) if (ipf_arrivetag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag); } @@ -1662,5 +1418,40 @@ ipf_event_dereg(void) if (ipf_clonetag != NULL) { EVENTHANDLER_DEREGISTER(if_clone_event, 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; } diff --git a/sys/contrib/ipfilter/netinet/ip_frag.c b/sys/contrib/ipfilter/netinet/ip_frag.c index fb21bd1..87e5b7b 100644 --- a/sys/contrib/ipfilter/netinet/ip_frag.c +++ b/sys/contrib/ipfilter/netinet/ip_frag.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -30,7 +30,8 @@ struct file; # include <sys/uio.h> # undef _KERNEL #endif -#if defined(_KERNEL) && (__FreeBSD_version >= 220000) +#if defined(_KERNEL) && \ + defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) # include <sys/filio.h> # include <sys/fcntl.h> #else @@ -62,7 +63,6 @@ struct file; #ifdef sun # include <net/af.h> #endif -#include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -79,25 +79,9 @@ struct file; #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" -#if (__FreeBSD_version >= 300000) -# include <sys/malloc.h> -# if defined(_KERNEL) -# ifndef IPFILTER_LKM -# include <sys/libkern.h> -# include <sys/systm.h> -# endif -extern struct callout_handle fr_slowtimer_ch; -# endif -#endif -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) -# include <sys/callout.h> -extern struct callout fr_slowtimer_ch; -#endif -#if defined(__OpenBSD__) -# include <sys/timeout.h> -extern struct timeout fr_slowtimer_ch; -#endif +#include "netinet/ip_sync.h" /* END OF INCLUDES */ #if !defined(lint) @@ -107,180 +91,398 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #endif -ipfr_t *ipfr_list = NULL; -ipfr_t **ipfr_tail = &ipfr_list; - -ipfr_t *ipfr_natlist = NULL; -ipfr_t **ipfr_nattail = &ipfr_natlist; +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; + 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; -ipfr_t *ipfr_ipidlist = NULL; -ipfr_t **ipfr_ipidtail = &ipfr_ipidlist; -static ipfr_t **ipfr_heads; -static ipfr_t **ipfr_nattab; -static ipfr_t **ipfr_ipidtab; - -static ipfrstat_t ipfr_stats; -static int ipfr_inuse = 0; -int ipfr_size = IPFT_SIZE; +#ifdef USE_MUTEXES +static ipfr_t *ipfr_frag_new __P((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 *)); +#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 **)); +#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 frentry_t ipfr_block; + +ipftuneable_t ipf_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) -int fr_ipfrttl = 120; /* 60 seconds */ -int fr_frag_lock = 0; -int fr_frag_init = 0; -u_long fr_ticks = 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; -static ipfr_t *ipfr_newfrag __P((fr_info_t *, u_32_t, ipfr_t **)); -static ipfr_t *fr_fraglookup __P((fr_info_t *, ipfr_t **)); -static void fr_fragdelete __P((ipfr_t *, ipfr_t ***)); -static void fr_fragfree __P((ipfr_t *)); + return 0; +} /* ------------------------------------------------------------------------ */ -/* Function: fr_fraginit */ +/* 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->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. */ /* ------------------------------------------------------------------------ */ -int fr_fraginit() +void +ipf_frag_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - KMALLOCS(ipfr_heads, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); - if (ipfr_heads == NULL) - return -1; - bzero((char *)ipfr_heads, ipfr_size * sizeof(ipfr_t *)); + ipf_frag_softc_t *softf = arg; + + RW_DESTROY(&softf->ipfr_ipidfrag); + RW_DESTROY(&softf->ipfr_frag); + RW_DESTROY(&softf->ipfr_natfrag); + + KFREE(softf); +} - KMALLOCS(ipfr_nattab, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); - if (ipfr_nattab == NULL) - return -1; - bzero((char *)ipfr_nattab, ipfr_size * sizeof(ipfr_t *)); - KMALLOCS(ipfr_ipidtab, ipfr_t **, ipfr_size * sizeof(ipfr_t *)); - if (ipfr_ipidtab == NULL) +/* ------------------------------------------------------------------------ */ +/* 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 *)ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *)); - RWLOCK_INIT(&ipf_frag, "ipf fragment rwlock"); - fr_frag_init = 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: fr_fragunload */ -/* Returns: Nil */ -/* Parameters: Nil */ +/* 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. */ /* ------------------------------------------------------------------------ */ -void fr_fragunload() +int +ipf_frag_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - if (fr_frag_init == 1) { - fr_fragclear(); + ipf_frag_softc_t *softf = arg; + + softf->ipfr_lock = 1; - RW_DESTROY(&ipf_frag); - fr_frag_init = 0; + if (softf->ipfr_inited == 1) { + ipf_frag_clear(softc); + + softf->ipfr_inited = 0; } - if (ipfr_heads != NULL) - KFREES(ipfr_heads, ipfr_size * sizeof(ipfr_t *)); - ipfr_heads = NULL; + 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 (ipfr_nattab != NULL) - KFREES(ipfr_nattab, ipfr_size * sizeof(ipfr_t *)); - ipfr_nattab = NULL; + if (softf->ipfr_ipidtab != NULL) + KFREES(softf->ipfr_ipidtab, + softf->ipfr_size * sizeof(ipfr_t *)); + softf->ipfr_ipidtab = NULL; - if (ipfr_ipidtab != NULL) - KFREES(ipfr_ipidtab, ipfr_size * sizeof(ipfr_t *)); - ipfr_ipidtab = NULL; + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragstats */ +/* 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: Nil */ +/* Parameters: arg(I) - pointer to local context to use */ /* */ /* Updates ipfr_stats with current information and returns a pointer to it */ /* ------------------------------------------------------------------------ */ -ipfrstat_t *fr_fragstats() +ipfrstat_t * +ipf_frag_stats(arg) + void *arg; { - ipfr_stats.ifs_table = ipfr_heads; - ipfr_stats.ifs_nattab = ipfr_nattab; - ipfr_stats.ifs_inuse = ipfr_inuse; - return &ipfr_stats; + 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_newfrag */ +/* 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_newfrag(fin, pass, table) -fr_info_t *fin; -u_32_t pass; -ipfr_t *table[]; +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; + ipfr_t *fra, frag, *fran; u_int idx, off; frentry_t *fr; - ip_t *ip; - if (ipfr_inuse >= IPFT_SIZE) + if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { + FBUMPD(ifs_maximum); return NULL; + } - if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) + if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { + FBUMPD(ifs_newbad); return NULL; + } - ip = fin->fin_ip; - - if (pass & FR_FRSTRICT) - if (fin->fin_off != 0) + if (pass & FR_FRSTRICT) { + if (fin->fin_off != 0) { + FBUMPD(ifs_newrestrictnot0); return NULL; + } + } - frag.ipfr_p = ip->ip_p; - idx = ip->ip_p; - frag.ipfr_id = ip->ip_id; - idx += ip->ip_id; - frag.ipfr_tos = ip->ip_tos; - frag.ipfr_src.s_addr = ip->ip_src.s_addr; - idx += ip->ip_src.s_addr; - frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; - idx += ip->ip_dst.s_addr; + 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 %= IPFT_SIZE; + 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; - /* - * 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)) { - ipfr_stats.ifs_exists++; - return NULL; + 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(fra, ipfr_t *); - if (fra == NULL) { - ipfr_stats.ifs_nomem++; + KMALLOC(fran, ipfr_t *); + if (fran == NULL) { + FBUMPD(ifs_nomem); return NULL; } + 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(fra); + return NULL; + } + + fra = fran; + fran = NULL; fr = fin->fin_fr; fra->ipfr_rule = fr; if (fr != NULL) { @@ -300,56 +502,63 @@ ipfr_t *table[]; fra->ipfr_data = NULL; table[idx] = fra; bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); - fra->ipfr_ttl = fr_ticks + fr_ipfrttl; + fra->ipfr_v = fin->fin_v; + 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. */ - off = ip->ip_off & IP_OFFMASK; if (off == 0) fra->ipfr_seen0 = 1; fra->ipfr_off = off + (fin->fin_dlen >> 3); fra->ipfr_pass = pass; fra->ipfr_ref = 1; - ipfr_stats.ifs_new++; - ipfr_inuse++; + fra->ipfr_pkts = 1; + fra->ipfr_bytes = fin->fin_plen; + FBUMP(ifs_inuse); + FBUMP(ifs_new); return fra; } /* ------------------------------------------------------------------------ */ -/* Function: fr_newfrag */ +/* 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 fr_newfrag(fin, pass) -u_32_t pass; -fr_info_t *fin; +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 ((fin->fin_v != 4) || (fr_frag_lock != 0)) + if (softf->ipfr_lock != 0) return -1; - WRITE_ENTER(&ipf_frag); - fra = ipfr_newfrag(fin, pass, ipfr_heads); +#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) { - *ipfr_tail = fra; - fra->ipfr_prev = ipfr_tail; - ipfr_tail = &fra->ipfr_next; - if (ipfr_list == NULL) - ipfr_list = fra; + *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); } - RWLOCK_EXIT(&ipf_frag); return fra ? 0 : -1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_nat_newfrag */ +/* Function: ipf_frag_natnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ @@ -357,33 +566,41 @@ fr_info_t *fin; /* Create a new NAT fragment cache entry based on the current packet and */ /* the NAT structure for this "session". */ /* ------------------------------------------------------------------------ */ -int fr_nat_newfrag(fin, pass, nat) -fr_info_t *fin; -u_32_t pass; -nat_t *nat; +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 ((fin->fin_v != 4) || (fr_frag_lock != 0)) + if (softf->ipfr_lock != 0) return 0; - WRITE_ENTER(&ipf_natfrag); - fra = ipfr_newfrag(fin, pass, ipfr_nattab); +#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; - *ipfr_nattail = fra; - fra->ipfr_prev = ipfr_nattail; - ipfr_nattail = &fra->ipfr_next; + *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; } - RWLOCK_EXIT(&ipf_natfrag); - return fra ? 0 : -1; + return -1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipid_newfrag */ +/* 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 */ @@ -391,31 +608,37 @@ nat_t *nat; /* Create a new fragment cache entry for this packet and store, as a data */ /* pointer, the new IP ID value. */ /* ------------------------------------------------------------------------ */ -int fr_ipid_newfrag(fin, ipid) -fr_info_t *fin; -u_32_t ipid; +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 ((fin->fin_v != 4) || (fr_frag_lock)) + if (softf->ipfr_lock) return 0; - WRITE_ENTER(&ipf_ipidfrag); - fra = ipfr_newfrag(fin, 0, ipfr_ipidtab); +#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 *)(uintptr_t)ipid; - *ipfr_ipidtail = fra; - fra->ipfr_prev = ipfr_ipidtail; - ipfr_ipidtail = &fra->ipfr_next; + 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); } - RWLOCK_EXIT(&ipf_ipidfrag); return fra ? 0 : -1; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fraglookup */ +/* 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 */ @@ -423,17 +646,44 @@ u_32_t ipid; /* */ /* 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 *fr_fraglookup(fin, table) -fr_info_t *fin; -ipfr_t *table[]; +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; - ip_t *ip; - if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) + /* + * 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 @@ -441,59 +691,58 @@ ipfr_t *table[]; * * build up a hash value to index the table with. */ - ip = fin->fin_ip; - frag.ipfr_p = ip->ip_p; - idx = ip->ip_p; - frag.ipfr_id = ip->ip_id; - idx += ip->ip_id; - frag.ipfr_tos = ip->ip_tos; - frag.ipfr_src.s_addr = ip->ip_src.s_addr; - idx += ip->ip_src.s_addr; - frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; - idx += ip->ip_dst.s_addr; + 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 %= IPFT_SIZE; + 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) + for (f = table[idx]; f; f = f->ipfr_hnext) { if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, IPFR_CMPSZ)) { u_short off; /* - * 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) { - ATOMIC_INCL(ipfr_stats.ifs_short); - continue; - } - - /* * 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 = ip->ip_off & IP_OFFMASK; + off = fin->fin_off >> 3; if (f->ipfr_seen0) { if (off == 0) { - ATOMIC_INCL(ipfr_stats.ifs_retrans0); + 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); + fin->fin_flx |= FI_BAD; + break; + } } else if (off == 0) f->ipfr_seen0 = 1; @@ -522,82 +771,124 @@ ipfr_t *table[]; * last (in order), shrink expiration time. */ if (off == f->ipfr_off) { - if (!(ip->ip_off & IP_MF)) - f->ipfr_ttl = fr_ticks + 1; f->ipfr_off = (fin->fin_dlen >> 3) + off; - } else if (f->ipfr_pass & FR_FRSTRICT) - continue; - ATOMIC_INCL(ipfr_stats.ifs_hits); + + /* + * 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: fr_nat_knownfrag */ +/* 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 *fr_nat_knownfrag(fin) -fr_info_t *fin; +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 ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_natlist) + if ((softf->ipfr_lock) || !softf->ipfr_natlist) return NULL; - READ_ENTER(&ipf_natfrag); - ipf = fr_fraglookup(fin, ipfr_nattab); +#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 == fr_ticks + 1) && (nat != NULL)) { + 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; - RWLOCK_EXIT(&ipf_natfrag); return nat; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipid_knownfrag */ +/* 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 fr_ipid_knownfrag(fin) -fr_info_t *fin; +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 ((fin->fin_v != 4) || (fr_frag_lock) || !ipfr_ipidlist) + if (softf->ipfr_lock || !softf->ipfr_ipidlist) return 0xffffffff; - READ_ENTER(&ipf_ipidfrag); - ipf = fr_fraglookup(fin, ipfr_ipidtab); - if (ipf != NULL) - id = (u_32_t)(uintptr_t)ipf->ipfr_data; - else +#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; - RWLOCK_EXIT(&ipf_ipidfrag); return id; } /* ------------------------------------------------------------------------ */ -/* Function: fr_knownfrag */ +/* 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 */ @@ -607,78 +898,82 @@ fr_info_t *fin; /* match is found, return the rule pointer and flags from the rule, except */ /* that if FR_LOGFIRST is set, reset FR_LOG. */ /* ------------------------------------------------------------------------ */ -frentry_t *fr_knownfrag(fin, passp) -fr_info_t *fin; -u_32_t *passp; +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 ((fin->fin_v != 4) || (fr_frag_lock) || (ipfr_list == NULL)) + if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) return NULL; - READ_ENTER(&ipf_frag); - fra = fr_fraglookup(fin, ipfr_heads); +#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) { - fr = fra->ipfr_rule; + if (fin->fin_flx & FI_BAD) { + fr = &ipfr_block; + fin->fin_reason = FRB_BADFRAG; + } 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); } - RWLOCK_EXIT(&ipf_frag); return fr; } /* ------------------------------------------------------------------------ */ -/* Function: fr_forget */ -/* Returns: Nil */ -/* Parameters: ptr(I) - pointer to data structure */ -/* */ -/* Search through all of the fragment cache entries and wherever a pointer */ -/* is found to match ptr, reset it to NULL. */ -/* ------------------------------------------------------------------------ */ -void fr_forget(ptr) -void *ptr; -{ - ipfr_t *fr; - - WRITE_ENTER(&ipf_frag); - for (fr = ipfr_list; fr; fr = fr->ipfr_next) - if (fr->ipfr_data == ptr) - fr->ipfr_data = NULL; - RWLOCK_EXIT(&ipf_frag); -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_forgetnat */ +/* Function: ipf_frag_natforget */ /* Returns: Nil */ /* Parameters: 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 fr_forgetnat(ptr) -void *ptr; +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(&ipf_natfrag); - for (fr = ipfr_natlist; fr; fr = fr->ipfr_next) + 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(&ipf_natfrag); + RWLOCK_EXIT(&softf->ipfr_natfrag); } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragdelete */ +/* Function: ipf_frag_delete */ /* Returns: Nil */ /* Parameters: fra(I) - pointer to fragment structure to delete */ /* tail(IO) - pointer to the pointer to the tail of the frag */ @@ -688,9 +983,12 @@ void *ptr; /* the filter rule it is associated with it if it is no longer used as a */ /* result of decreasing the reference count. */ /* ------------------------------------------------------------------------ */ -static void fr_fragdelete(fra, tail) -ipfr_t *fra, ***tail; +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; @@ -703,107 +1001,112 @@ ipfr_t *fra, ***tail; *fra->ipfr_hprev = fra->ipfr_hnext; if (fra->ipfr_rule != NULL) { - (void) fr_derefrule(&fra->ipfr_rule); + (void) ipf_derefrule(softc, &fra->ipfr_rule); } if (fra->ipfr_ref <= 0) - fr_fragfree(fra); + ipf_frag_free(softf, fra); } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragfree */ +/* Function: ipf_frag_free */ /* Returns: Nil */ -/* Parameters: fra - pointer to frag structure to free */ /* */ -/* Take care of the details associated with deleting an entry from the frag */ -/* cache. Currently this just means bumping stats correctly after freeing */ /* ------------------------------------------------------------------------ */ -static void fr_fragfree(fra) -ipfr_t *fra; +static void +ipf_frag_free(softf, fra) + ipf_frag_softc_t *softf; + ipfr_t *fra; { KFREE(fra); - ipfr_stats.ifs_expire++; - ipfr_inuse--; + FBUMP(ifs_expire); + softf->ipfr_stats.ifs_inuse--; } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragclear */ +/* Function: ipf_frag_clear */ /* Returns: Nil */ /* Parameters: Nil */ /* */ /* Free memory in use by fragment state information kept. Do the normal */ /* fragment state stuff first and then the NAT-fragment table. */ /* ------------------------------------------------------------------------ */ -void fr_fragclear() +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(&ipf_frag); - while ((fra = ipfr_list) != NULL) { + WRITE_ENTER(&softc->ipf_frag); + while ((fra = softf->ipfr_list) != NULL) { fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_tail); + ipf_frag_delete(softc, fra, &softf->ipfr_tail); } - ipfr_tail = &ipfr_list; - RWLOCK_EXIT(&ipf_frag); + softf->ipfr_tail = &softf->ipfr_list; + RWLOCK_EXIT(&softc->ipf_frag); - WRITE_ENTER(&ipf_nat); - WRITE_ENTER(&ipf_natfrag); - while ((fra = ipfr_natlist) != NULL) { + 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--; - fr_fragdelete(fra, &ipfr_nattail); + ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } - ipfr_nattail = &ipfr_natlist; - RWLOCK_EXIT(&ipf_natfrag); - RWLOCK_EXIT(&ipf_nat); + softf->ipfr_nattail = &softf->ipfr_natlist; + RWLOCK_EXIT(&softf->ipfr_natfrag); + RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ -/* Function: fr_fragexpire */ +/* Function: ipf_frag_expire */ /* Returns: Nil */ /* Parameters: Nil */ /* */ /* Expire entries in the fragment cache table that have been there too long */ /* ------------------------------------------------------------------------ */ -void fr_fragexpire() +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 (fr_frag_lock) + if (softf->ipfr_lock) return; SPL_NET(s); - WRITE_ENTER(&ipf_frag); + 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 fr_ticks. + * which is indicated by the ttl being less than or equal to ipf_ticks. */ - for (fp = &ipfr_list; ((fra = *fp) != NULL); ) { - if (fra->ipfr_ttl > fr_ticks) + for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { + if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_tail); + ipf_frag_delete(softc, fra, &softf->ipfr_tail); } - RWLOCK_EXIT(&ipf_frag); + RWLOCK_EXIT(&softc->ipf_frag); - WRITE_ENTER(&ipf_ipidfrag); - for (fp = &ipfr_ipidlist; ((fra = *fp) != NULL); ) { - if (fra->ipfr_ttl > fr_ticks) + WRITE_ENTER(&softf->ipfr_ipidfrag); + for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { + if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_ipidtail); + ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); } - RWLOCK_EXIT(&ipf_ipidfrag); + RWLOCK_EXIT(&softf->ipfr_ipidfrag); /* * Same again for the NAT table, except that if the structure also @@ -815,11 +1118,11 @@ void fr_fragexpire() * operations - no need to do that if there are no entries in this * list, right? */ - if (ipfr_natlist != NULL) { - WRITE_ENTER(&ipf_nat); - WRITE_ENTER(&ipf_natfrag); - for (fp = &ipfr_natlist; ((fra = *fp) != NULL); ) { - if (fra->ipfr_ttl > fr_ticks) + 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) { @@ -827,76 +1130,60 @@ void fr_fragexpire() nat->nat_data = NULL; } fra->ipfr_ref--; - fr_fragdelete(fra, &ipfr_nattail); + ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } - RWLOCK_EXIT(&ipf_natfrag); - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softf->ipfr_natfrag); + RWLOCK_EXIT(&softc->ipf_nat); } SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: fr_slowtimer */ -/* Returns: Nil */ -/* Parameters: Nil */ -/* */ -/* Slowly expire held state for fragments. Timeouts are set * in */ -/* expectation of this being called twice per second. */ -/* ------------------------------------------------------------------------ */ -#if !defined(_KERNEL) || (!SOLARIS && !defined(__hpux) && !defined(__sgi) && \ - !defined(__osf__) && !defined(linux)) -# if defined(_KERNEL) && ((BSD >= 199103) || defined(__sgi)) -void fr_slowtimer __P((void *ptr)) -# else -int fr_slowtimer() -# endif +/* Function: ipf_frag_pkt_next */ +/* ------------------------------------------------------------------------ */ +int +ipf_frag_pkt_next(softc, token, itp) + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; { - READ_ENTER(&ipf_global); - - ipf_expiretokens(); - fr_fragexpire(); - fr_timeoutstate(); - fr_natexpire(); - fr_authexpire(); - fr_ticks++; - if (fr_running <= 0) - goto done; -# ifdef _KERNEL -# if defined(__NetBSD__) && (__NetBSD_Version__ >= 104240000) - callout_reset(&fr_slowtimer_ch, hz / 2, fr_slowtimer, NULL); -# else -# if defined(__OpenBSD__) - timeout_add(&fr_slowtimer_ch, hz/2); -# else -# if (__FreeBSD_version >= 300000) - fr_slowtimer_ch = timeout(fr_slowtimer, NULL, hz/2); -# else -# ifdef linux - ; -# else - timeout(fr_slowtimer, NULL, hz/2); -# endif -# endif /* FreeBSD */ -# endif /* OpenBSD */ -# endif /* NetBSD */ -# endif -done: - RWLOCK_EXIT(&ipf_global); -# if (BSD < 199103) || !defined(_KERNEL) - return 0; -# endif + 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 } -#endif /* !SOLARIS && !defined(__hpux) && !defined(__sgi) */ /* ------------------------------------------------------------------------ */ -/* Function: fr_nextfrag */ +/* Function: ipf_frag_nat_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: token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* top(I) - top of the fragment list */ -/* tail(I) - tail of the fragment list */ /* lock(I) - fragment cache lock */ /* */ /* This function is used to interate through the list of entries in the */ @@ -904,30 +1191,39 @@ done: /* 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, tail and lock. */ +/* fragment cache - hence the reason for passing in top and lock. */ /* ------------------------------------------------------------------------ */ -int fr_nextfrag(token, itp, top, tail +static int +ipf_frag_next(softc, token, itp, top #ifdef USE_MUTEXES , lock #endif ) -ipftoken_t *token; -ipfgeniter_t *itp; -ipfr_t **top, ***tail; + ipf_main_softc_t *softc; + ipftoken_t *token; + ipfgeniter_t *itp; + ipfr_t **top; #ifdef USE_MUTEXES -ipfrwlock_t *lock; + ipfrwlock_t *lock; #endif { ipfr_t *frag, *next, zero; int error = 0; - frag = token->ipt_data; - if (frag == (ipfr_t *)-1) { - ipf_freetoken(token); - return ESRCH; + 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 @@ -941,26 +1237,72 @@ ipfrwlock_t *lock; next = &zero; token->ipt_data = NULL; } + if (next->ipfr_next == NULL) + ipf_token_mark_complete(token); + RWLOCK_EXIT(lock); - if (frag != NULL) { + error = COPYOUT(next, itp->igi_data, sizeof(*next)); + if (error != 0) + IPFERROR(20002); + + if (frag != NULL) { #ifdef USE_MUTEXES - fr_fragderef(&frag, lock); + ipf_frag_deref(softc, &frag, lock); #else - fr_fragderef(&frag); + ipf_frag_deref(softc, &frag); #endif - } + } + return error; +} - error = COPYOUT(next, itp->igi_data, sizeof(*next)); - if (error != 0) - error = EFAULT; - return error; +/* ------------------------------------------------------------------------ */ +/* Function: ipf_frag_pkt_deref */ +/* Returns: Nil */ +/* */ +/* ------------------------------------------------------------------------ */ +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 */ +/* */ +/* ------------------------------------------------------------------------ */ +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: fr_fragderef */ +/* Function: ipf_frag_deref */ /* Returns: Nil */ /* Parameters: frp(IO) - pointer to fragment structure to deference */ /* lock(I) - lock associated with the fragment */ @@ -970,16 +1312,19 @@ ipfrwlock_t *lock; /* not freed, to enforce the notion that the caller is no longer entitled */ /* to use the pointer it is dropping the reference to. */ /* ------------------------------------------------------------------------ */ -void fr_fragderef(frp +static void +ipf_frag_deref(arg, frp #ifdef USE_MUTEXES , lock #endif ) -ipfr_t **frp; + void *arg; + ipfr_t **frp; #ifdef USE_MUTEXES -ipfrwlock_t *lock; + ipfrwlock_t *lock; #endif { + ipf_frag_softc_t *softf = arg; ipfr_t *fra; fra = *frp; @@ -988,6 +1333,6 @@ ipfrwlock_t *lock; WRITE_ENTER(lock); fra->ipfr_ref--; if (fra->ipfr_ref <= 0) - fr_fragfree(fra); + 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 227dbcd..6b0c1be 100644 --- a/sys/contrib/ipfilter/netinet/ip_frag.h +++ b/sys/contrib/ipfilter/netinet/ip_frag.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -21,26 +21,33 @@ typedef struct ipfr { 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_seen0; + 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; - struct in_addr ipfr_src; - struct in_addr ipfr_dst; + i6addr_t ipfr_source; + i6addr_t ipfr_dest; u_32_t ipfr_optmsk; u_short ipfr_secmsk; u_short ipfr_auth; - u_short ipfr_id; - u_char ipfr_p; - u_char ipfr_tos; + 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 */ @@ -51,6 +58,14 @@ typedef struct ipfrstat { 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; @@ -58,51 +73,32 @@ typedef struct ipfrstat { #define IPFR_CMPSZ (offsetof(ipfr_t, ipfr_pass) - \ offsetof(ipfr_t, ipfr_ifp)) -extern ipfr_t *ipfr_list, **ipfr_tail; -extern ipfr_t *ipfr_natlist, **ipfr_nattail; -extern int ipfr_size; -extern int fr_ipfrttl; -extern int fr_frag_lock; -extern int fr_fraginit __P((void)); -extern void fr_fragunload __P((void)); -extern ipfrstat_t *fr_fragstats __P((void)); - -extern int fr_newfrag __P((fr_info_t *, u_32_t)); -extern frentry_t *fr_knownfrag __P((fr_info_t *, u_32_t *)); - -extern int fr_nat_newfrag __P((fr_info_t *, u_32_t, struct nat *)); -extern nat_t *fr_nat_knownfrag __P((fr_info_t *)); - -extern int fr_ipid_newfrag __P((fr_info_t *, u_32_t)); -extern u_32_t fr_ipid_knownfrag __P((fr_info_t *)); -#ifdef USE_MUTEXES -extern void fr_fragderef __P((ipfr_t **, ipfrwlock_t *)); -extern int fr_nextfrag __P((ipftoken_t *, ipfgeniter_t *, ipfr_t **, \ - ipfr_t ***, ipfrwlock_t *)); -#else -extern void fr_fragderef __P((ipfr_t **)); -extern int fr_nextfrag __P((ipftoken_t *, ipfgeniter_t *, ipfr_t **, \ - ipfr_t ***)); -#endif - -extern void fr_forget __P((void *)); -extern void fr_forgetnat __P((void *)); -extern void fr_fragclear __P((void)); -extern void fr_fragexpire __P((void)); - -#if defined(_KERNEL) && ((BSD >= 199306) || SOLARIS || defined(__sgi) \ - || defined(__osf__) || (defined(__sgi) && (IRIX >= 60500))) -# if defined(SOLARIS2) && (SOLARIS2 < 7) -extern void fr_slowtimer __P((void)); -# else -extern void fr_slowtimer __P((void *)); -# endif -#else -# if defined(linux) && defined(_KERNEL) -extern void fr_slowtimer __P((long)); -# else -extern int fr_slowtimer __P((void)); -# endif -#endif +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 *)); #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 f56a690..ff83976 100644 --- a/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -15,6 +15,7 @@ #define IPF_FTP_PROXY #define IPF_MINPORTLEN 18 +#define IPF_MINEPRTLEN 20 #define IPF_MAXPORTLEN 30 #define IPF_MIN227LEN 39 #define IPF_MAX227LEN 51 @@ -38,93 +39,197 @@ #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 *)); -int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_complete __P((char *, size_t)); -int ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_init __P((void)); -void ippr_ftp_fini __P((void)); -int ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); -int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); -int ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t)); -int ippr_ftp_server_valid __P((ftpside_t *, char *, size_t)); -int ippr_ftp_client_valid __P((ftpside_t *, char *, size_t)); -u_short ippr_ftp_atoi __P((char **)); -int ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, - u_int, char *, char *, u_int)); - - -int ftp_proxy_init = 0; -int ippr_ftp_pasvonly = 0; -int ippr_ftp_insecure = 0; /* Do not require logins before transfers */ -int ippr_ftp_pasvrdr = 0; -int ippr_ftp_forcepasv = 0; /* PASV must be last command prior to 227 */ -#if defined(_KERNEL) -int ippr_ftp_debug = 0; -#else -int ippr_ftp_debug = 2; -#endif /* - * 1 - security - * 2 - errors - * 3 - error debugging - * 4 - parsing errors - * 5 - parsing info - * 6 - parsing debug + * 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 ftptune = { - { &ippr_ftp_debug }, - "ippr_ftp_debug", - 0, - 10, - sizeof(ippr_ftp_debug), - 0, - NULL +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 } }; -/* - * Initialize local structures. - */ -int ippr_ftp_init() +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"); - ftp_proxy_init = 1; - (void) fr_addipftune(&ftptune); - return 0; + MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); + ipf_p_ftp_proxy_init = 1; } -void ippr_ftp_fini() +void +ipf_p_ftp_main_unload() { - (void) fr_delipftune(&ftptune); - if (ftp_proxy_init == 1) { + if (ipf_p_ftp_proxy_init == 1) { MUTEX_DESTROY(&ftppxyfr.fr_lock); - ftp_proxy_init = 0; + ipf_p_ftp_proxy_init = 0; } } -int ippr_ftp_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +/* + * 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; @@ -133,11 +238,12 @@ nat_t *nat; if (ftp == NULL) return -1; - fin = fin; /* LINT */ 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]; @@ -152,25 +258,53 @@ nat_t *nat; } -int ippr_ftp_port(fin, ip, nat, f, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -int dlen; +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; { - tcphdr_t *tcp, tcph, *tcp2 = &tcph; char newbuf[IPF_FTPBUFSZ], *s; - struct in_addr swip, swip2; u_int a1, a2, a3, a4; - int inc, off, flags; u_short a5, a6, sp; size_t nlen, olen; - fr_info_t fi; - nat_t *nat2; + 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; @@ -178,8 +312,10 @@ int dlen; * Check for client sending out PORT message. */ if (dlen < IPF_MINPORTLEN) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", + 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; } @@ -190,16 +326,18 @@ int dlen; /* * Pick out the address components, two at a time. */ - a1 = ippr_ftp_atoi(&s); + a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1); + 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 = ippr_ftp_atoi(&s); + a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2); + 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; } @@ -210,18 +348,21 @@ int dlen; a1 <<= 16; a1 |= a2; if (((nat->nat_dir == NAT_OUTBOUND) && - (a1 != ntohl(nat->nat_inip.s_addr))) || + (a1 != ntohl(nat->nat_osrcaddr))) || ((nat->nat_dir == NAT_INBOUND) && - (a1 != ntohl(nat->nat_oip.s_addr)))) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1"); + (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 = ippr_ftp_atoi(&s); + a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3); + 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 == ')') @@ -232,34 +373,31 @@ int dlen; */ if (*s == '\n') s--; - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - a6 = a5 & 0xff; - } else { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:missing %s\n", "cr-lf"); + 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; - /* - * Don't allow the PORT command to specify a port < 1024 due to - * security crap. - */ - if (sp < 1024) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:sp(%d) < 1024\n", sp); - return 0; - } + /* * Calculate new address parts for PORT command */ if (nat->nat_dir == NAT_INBOUND) - a1 = ntohl(nat->nat_oip.s_addr); + 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; @@ -276,117 +414,213 @@ int dlen; nlen = strlen(newbuf); inc = nlen - olen; - if ((inc + ip->ip_len) > 65535) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n", + 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) - bcopy(newbuf, MTOD(m, char *) + off, nlen); + M_ADJ(m, inc); #else -# if defined(MENTAT) - if (inc < 0) - (void)adjmsg(m, inc); -# else /* defined(MENTAT) */ /* * 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(MENTAT) */ + M_ADJ(m, inc); #endif /* !defined(_KERNEL) */ COPYBACK(m, off, nlen, newbuf); + fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { - ip->ip_len += inc; - fin->fin_dlen += inc; 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_state = NULL; - fi.fin_nat = NULL; 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. */ - if (nat->nat_dir == NAT_OUTBOUND) - nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - else - nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - if (nat2 == NULL) { - int slen; - - slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp2); - bzero((char *)tcp2, sizeof(*tcp2)); - tcp2->th_win = htons(8192); - tcp2->th_sport = htons(sp); - TCP_OFF_A(tcp2, 5); - tcp2->th_flags = TH_SYN; - tcp2->th_dport = 0; /* XXX - don't specify remote port */ - fi.fin_data[1] = 0; - fi.fin_dlen = sizeof(*tcp2); - fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); - fi.fin_dp = (char *)tcp2; - fi.fin_fr = &ftppxyfr; - fi.fin_out = nat->nat_dir; - fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; - swip = ip->ip_src; - swip2 = ip->ip_dst; +#ifdef USE_INET6 + if (nat->nat_v[0] == 6) { if (nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - ip->ip_src = nat->nat_inip; - } else if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; - ip->ip_src = nat->nat_oip; + 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); } - - flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT; - if (nat->nat_dir == NAT_INBOUND) - flags |= NAT_NOTRULEPORT; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir); - - if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_TCP); - nat_update(&fi, nat2, nat->nat_ptr); - fi.fin_ifp = NULL; - if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; - ip->ip_dst = nat->nat_inip; - } - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + } 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); } - ip->ip_len = slen; - ip->ip_src = swip; - ip->ip_dst = swip2; } + 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 ippr_ftp_client(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -nat_t *nat; -ftpinfo_t *ftp; -ip_t *ip; -int dlen; +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; @@ -408,6 +642,7 @@ int dlen; 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) { @@ -437,14 +672,24 @@ int dlen; !strncmp(cmd, "ACCT ", 5)) { ftp->ftp_passok = FTPXY_ACCT_1; ftp->ftp_incok = 1; - } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly && + } else if ((ftp->ftp_passok == FTPXY_GO) && + !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { - inc = ippr_ftp_port(fin, ip, nat, f, dlen); - } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly && + 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 = ippr_ftp_port(fin, ip, nat, f, dlen); + 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; @@ -452,12 +697,14 @@ int dlen; } -int ippr_ftp_pasv(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpinfo_t *ftp; -int dlen; +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]; @@ -466,11 +713,12 @@ int dlen; ftpside_t *f; char *s; - if (ippr_ftp_forcepasv != 0 && - ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n", - ftp->ftp_side[0].ftps_cmds); + 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; } @@ -481,14 +729,17 @@ int dlen; * Check for PASV reply message. */ if (dlen < IPF_MIN227LEN) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", + 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)) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:%d reply wrong\n", 227); + 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; } @@ -509,16 +760,18 @@ int dlen; /* * Pick out the address components, two at a time. */ - a1 = ippr_ftp_atoi(&s); + a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1); + 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 = ippr_ftp_atoi(&s); + a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2); + 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; } @@ -530,18 +783,21 @@ int dlen; a1 |= a2; if (((nat->nat_dir == NAT_INBOUND) && - (a1 != ntohl(nat->nat_inip.s_addr))) || + (a1 != ntohl(nat->nat_ndstaddr))) || ((nat->nat_dir == NAT_OUTBOUND) && - (a1 != ntohl(nat->nat_oip.s_addr)))) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1"); + (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 = ippr_ftp_atoi(&s); + a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3); + 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; } @@ -554,13 +810,13 @@ int dlen; /* * check for CR-LF at the end. */ - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - } else { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:missing %s", "cr-lf\n"); + 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; @@ -568,7 +824,7 @@ int dlen; * Calculate new address parts for 227 reply */ if (nat->nat_dir == NAT_INBOUND) { - data_ip = nat->nat_outip.s_addr; + data_ip = nat->nat_odstaddr; a1 = ntohl(data_ip); } else data_ip = htonl(a1); @@ -587,156 +843,165 @@ int dlen; "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, a5, a6, brackets[1]); #endif - return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6), - newbuf, s, data_ip); + return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6), + newbuf, s); } -int ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -u_int port; -char *newmsg; -char *s; -u_int data_ip; +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, sflags; + int inc, off, nflags; tcphdr_t *tcp, tcph, *tcp2; - struct in_addr swip, swip2; - struct in_addr data_addr; + 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; - data_addr.s_addr = data_ip; tcp2 = &tcph; inc = 0; - + f = &ftp->ftp_side[1]; olen = s - f->ftps_rptr; nlen = strlen(newmsg); inc = nlen - olen; - if ((inc + ip->ip_len) > 65535) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", + 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; } -#if !defined(_KERNEL) - bcopy(newmsg, MTOD(m, char *) + off, nlen); -#else -# if defined(MENTAT) - if (inc < 0) - (void)adjmsg(m, inc); -# else /* defined(MENTAT) */ - /* - * 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(MENTAT) */ -#endif /* !defined(_KERNEL) */ - COPYBACK(m, off, nlen, newmsg); - - if (inc != 0) { - ip->ip_len += inc; - fin->fin_dlen += inc; - fin->fin_plen += inc; - } + 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_state = NULL; - fi.fin_nat = NULL; fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = 0; fi.fin_data[1] = port; nflags = IPN_TCP|SI_W_SPORT; - if (ippr_ftp_pasvrdr && f->ftps_ifp) - nflags |= SI_W_DPORT; - if (nat->nat_dir == NAT_OUTBOUND) - nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH, - nat->nat_p, nat->nat_inip, nat->nat_oip); + + 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 - nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH, - nat->nat_p, nat->nat_inip, nat->nat_oip); +#endif + nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, + nflags, nat->nat_dir); + MUTEX_EXIT(&softn->ipf_nat_new); + if (nat2 == NULL) { - int slen; - - slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp2); - bzero((char *)tcp2, sizeof(*tcp2)); - tcp2->th_win = htons(8192); - tcp2->th_sport = 0; /* XXX - fake it for nat_new */ - TCP_OFF_A(tcp2, 5); - tcp2->th_flags = TH_SYN; - fi.fin_data[1] = port; - fi.fin_dlen = sizeof(*tcp2); - tcp2->th_dport = htons(port); - fi.fin_data[0] = 0; - fi.fin_dp = (char *)tcp2; - fi.fin_plen = fi.fin_hlen + sizeof(*tcp); - fi.fin_fr = &ftppxyfr; - fi.fin_out = nat->nat_dir; - fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; - swip = ip->ip_src; - swip2 = ip->ip_dst; - if (nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_daddr = data_addr.s_addr; - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - ip->ip_dst = data_addr; - ip->ip_src = nat->nat_inip; - } else if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; - fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; - ip->ip_src = nat->nat_oip; - ip->ip_dst = nat->nat_outip; - } + KFREES(ipn, ipn->in_size); + return APR_ERR(1); + } - sflags = nflags; - nflags |= NAT_SLAVE; - if (nat->nat_dir == NAT_INBOUND) - nflags |= NAT_NOTRULEPORT; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir); - if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_TCP); - nat_update(&fi, nat2, nat->nat_ptr); - fi.fin_ifp = NULL; - if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; - ip->ip_dst = nat->nat_inip; - } - (void) fr_addstate(&fi, NULL, sflags); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (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; - ip->ip_len = slen; - ip->ip_src = swip; - ip->ip_dst = swip2; + 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 inc; + + return APR_INC(inc); } -int ippr_ftp_server(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpinfo_t *ftp; -int dlen; +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; @@ -747,19 +1012,29 @@ int dlen; 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 = ippr_ftp_pasv(fin, ip, nat, ftp, dlen); + inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); else if (!strncmp(rptr, "229 ", 4)) - inc = ippr_ftp_epsv(fin, ip, nat, f, dlen); - } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) { - inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen); - } else if (ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) { - inc = ippr_ftp_epsv(fin, ip, nat, f, dlen); + 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) { @@ -784,8 +1059,13 @@ int dlen; } } } -server_cmd_ok: 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)) ; @@ -795,13 +1075,20 @@ server_cmd_ok: /* + * 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 ippr_ftp_client_valid(ftps, buf, len) -ftpside_t *ftps; -char *buf; -size_t len; +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; @@ -809,12 +1096,13 @@ size_t len; s = buf; - if (ftps->ftps_junk == 1) - return 1; + if (ftps->ftps_junk == FTPXY_JUNK_BAD) + return FTPXY_JUNK_BAD; if (i < 5) { - if (ippr_ftp_debug > 3) - printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i); + 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; } @@ -847,12 +1135,13 @@ size_t len; goto bad_client_command; } else { bad_client_command: - if (ippr_ftp_debug > 3) + 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", - "ippr_ftp_client_valid", + "ipf_p_ftp_client_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); - return 1; + return FTPXY_JUNK_BAD; } for (; i; i--) { @@ -860,25 +1149,30 @@ bad_client_command: c = *s++; if ((pc == '\r') && (c == '\n')) { cmd[4] = '\0'; - if (!strcmp(cmd, "PASV")) - ftps->ftps_cmds = FTPXY_C_PASV; - else - ftps->ftps_cmds = 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("ippr_ftp_client_valid:junk after cmd[%*.*s]\n", + printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); #endif - return 2; + return FTPXY_JUNK_EOL; } -int ippr_ftp_server_valid(ftps, buf, len) -ftpside_t *ftps; -char *buf; -size_t len; +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; @@ -887,19 +1181,22 @@ size_t len; s = buf; cmd = 0; - if (ftps->ftps_junk == 1) - return 1; + if (ftps->ftps_junk == FTPXY_JUNK_BAD) + return FTPXY_JUNK_BAD; if (i < 5) { - if (ippr_ftp_debug > 3) - printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i); + 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 == ' ') + if (c == ' ') { + cmd = -1; goto search_eol; + } if (ISDIGIT(c)) { cmd = (c - '0') * 100; @@ -915,40 +1212,54 @@ size_t len; 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: - if (ippr_ftp_debug > 3) + 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", - "ippr_ftp_server_valid", + "ipf_p_ftp_server_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); - return 1; + 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')) { - ftps->ftps_cmds = cmd; - return 0; + if (cmd == -1) { + if (ftps->ftps_junk == FTPXY_JUNK_CONT) + return FTPXY_JUNK_CONT; + } else { + ftps->ftps_cmd = cmd; + } + return FTPXY_JUNK_OK; } } - if (ippr_ftp_debug > 3) - printf("ippr_ftp_server_valid:junk after cmd[%*.*s]\n", + + 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 2; + return FTPXY_JUNK_EOL; } -int ippr_ftp_valid(ftp, side, buf, len) -ftpinfo_t *ftp; -int side; -char *buf; -size_t len; +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; @@ -956,9 +1267,9 @@ size_t len; ftps = &ftp->ftp_side[side]; if (side == 0) - ret = ippr_ftp_client_valid(ftps, buf, len); + ret = ipf_p_ftp_client_valid(softf, ftps, buf, len); else - ret = ippr_ftp_server_valid(ftps, buf, len); + ret = ipf_p_ftp_server_valid(softf, ftps, buf, len); return ret; } @@ -971,13 +1282,15 @@ size_t len; * rv == 0 for inbound processing, * rv == 1 for outbound processing. */ -int ippr_ftp_process(fin, nat, ftp, rv) -fr_info_t *fin; -nat_t *nat; -ftpinfo_t *ftp; -int rv; +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; + 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; @@ -995,14 +1308,17 @@ int rv; t = &ftp->ftp_side[1 - rv]; thseq = ntohl(tcp->th_seq); thack = ntohl(tcp->th_ack); - #ifdef __sgi mlen = fin->fin_plen - off; #else mlen = MSGDSIZE(m) - off; #endif - if (ippr_ftp_debug > 4) - printf("ippr_ftp_process: mlen %d\n", mlen); + + 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; @@ -1016,7 +1332,7 @@ int rv; sel = aps->aps_sel[1 - rv]; sel2 = aps->aps_sel[rv]; - if (rv == 0) { + if (rv == 1) { seqoff = aps->aps_seqoff[sel]; if (aps->aps_seqmin[sel] > seqoff + thseq) seqoff = aps->aps_seqoff[!sel]; @@ -1025,14 +1341,14 @@ int rv; ackoff = aps->aps_ackoff[!sel2]; } else { seqoff = aps->aps_ackoff[sel]; - if (ippr_ftp_debug > 2) + 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 (ippr_ftp_debug > 2) + 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) { @@ -1043,7 +1359,7 @@ int rv; ackoff = aps->aps_seqoff[!sel2]; } } - if (ippr_ftp_debug > 2) { + 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); @@ -1060,7 +1376,7 @@ int rv; * 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 (ippr_ftp_debug > 2) { + 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); } @@ -1078,37 +1394,44 @@ int rv; ok = 1; } } else { - if (t->ftps_seq[0] + ackoff == thack) + if (t->ftps_seq[0] + ackoff == thack) { + t->ftps_seq[0] = thack; ok = 1; - else if (t->ftps_seq[0] == thack + ackoff) + } 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 - ackoff; + } 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; + t->ftps_seq[0] = thack + ackoff; ok = 1; } } } - if (ippr_ftp_debug > 2) { + if (softf->ipf_p_ftp_debug & DEBUG_INFO) { if (!ok) printf("%s ok\n", "not"); } if (!mlen) { - if (t->ftps_seq[0] + ackoff != thack) { - if (ippr_ftp_debug > 1) { - printf("%s:seq[0](%x) + (%x) != (%x)\n", - "ippr_ftp_process", t->ftps_seq[0], + 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 (ippr_ftp_debug > 2) { - printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n", + 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]); } @@ -1117,9 +1440,10 @@ int rv; f->ftps_seq[0] = f->ftps_seq[1] - seqoff; f->ftps_seq[1] = thseq + 1 - seqoff; } else { - if (ippr_ftp_debug > 1) { - printf("FIN: thseq %x seqoff %d ftps_seq %x %x\n", - thseq, seqoff, f->ftps_seq[0], f->ftps_seq[1]); + 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); } @@ -1140,8 +1464,9 @@ int rv; } if (ok == 0) { + DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen); inc = thseq - f->ftps_seq[0]; - if (ippr_ftp_debug > 1) { + 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]); @@ -1163,68 +1488,69 @@ int rv; 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; - if (ippr_ftp_debug > 3) +whilemore: + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n", - "ippr_ftp_process", + "ipf_p_ftp_process", len, mlen, off, (u_long)wptr, f->ftps_junk, len, len, rptr); f->ftps_wptr = wptr; - if (f->ftps_junk != 0) { + if (f->ftps_junk != FTPXY_JUNK_OK) { i = f->ftps_junk; - f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, + f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr, wptr - rptr); + DT2(junk_transit, int, i, int, f->ftps_junk); - if (ippr_ftp_debug > 5) + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:junk %d -> %d\n", - "ippr_ftp_process", i, f->ftps_junk); + "ipf_p_ftp_process", i, f->ftps_junk); - if (f->ftps_junk != 0) { + if (f->ftps_junk == FTPXY_JUNK_BAD) { + DT(buffer_full); if (wptr - rptr == sizeof(f->ftps_buf)) { - if (ippr_ftp_debug > 4) + if (softf->ipf_p_ftp_debug & + DEBUG_PARSE_INFO) printf("%s:full buffer\n", - "ippr_ftp_process"); + "ipf_p_ftp_process"); f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; rptr = f->ftps_rptr; wptr = f->ftps_wptr; - /* - * Because we throw away data here that - * we would otherwise parse, set the - * junk flag to indicate just ignore - * any data upto the next CRLF. - */ - f->ftps_junk = 1; continue; } } } - while ((f->ftps_junk == 0) && (wptr > rptr)) { + while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) { len = wptr - rptr; - f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len); + f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, + rptr, len); - if (ippr_ftp_debug > 3) { + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { printf("%s=%d len %d rv %d ptr %lx/%lx ", - "ippr_ftp_valid", + "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 == 0) { + if (f->ftps_junk == FTPXY_JUNK_OK) { + f->ftps_cmds++; f->ftps_rptr = rptr; if (rv) - inc += ippr_ftp_server(fin, ip, nat, - ftp, len); + inc += ipf_p_ftp_server(softf, fin, ip, + nat, ftp, len); else - inc += ippr_ftp_client(fin, ip, nat, - ftp, len); + inc += ipf_p_ftp_client(softf, fin, ip, + nat, ftp, len); rptr = f->ftps_rptr; wptr = f->ftps_wptr; } @@ -1234,21 +1560,25 @@ int rv; * 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 == 1)) { + if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) { /* f->ftps_seq[1] += inc; */ - if (ippr_ftp_debug > 1) + DT(ftp_junk_cmd); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("%s:cmds == 0 junk == 1\n", - "ippr_ftp_process"); + "ipf_p_ftp_process"); return APR_ERR(2); } - if ((f->ftps_junk != 0) && (rptr < wptr)) { + 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; - f->ftps_junk = 0; + retry = 1; + if (f->ftps_junk != FTPXY_JUNK_CONT) + f->ftps_junk = FTPXY_JUNK_OK; break; } } @@ -1264,19 +1594,21 @@ int rv; * current state. */ if (rptr > f->ftps_buf) { - bcopy(rptr, f->ftps_buf, len); + 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 (ippr_ftp_debug > 3) { + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) { #ifdef __sgi mlen = fin->fin_plen; #else @@ -1293,11 +1625,14 @@ int rv; } -int ippr_ftp_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; @@ -1309,15 +1644,18 @@ nat_t *nat; if (ftp->ftp_side[1 - rev].ftps_ifp == NULL) ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp; - return ippr_ftp_process(fin, nat, ftp, rev); + return ipf_p_ftp_process(softf, fin, nat, ftp, rev); } -int ippr_ftp_in(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; @@ -1329,18 +1667,19 @@ nat_t *nat; if (ftp->ftp_side[rev].ftps_ifp == NULL) ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp; - return ippr_ftp_process(fin, nat, ftp, 1 - rev); + return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev); } /* - * ippr_ftp_atoi - implement a version of atoi which processes numbers in + * 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 ippr_ftp_atoi(ptr) -char **ptr; +u_short +ipf_p_ftp_atoi(ptr) + char **ptr; { register char *s = *ptr, c; register u_char i = 0, j = 0; @@ -1364,26 +1703,237 @@ char **ptr; } -int ippr_ftp_epsv(fin, ip, nat, f, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -int dlen; +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]; - char *s; 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) + if (dlen < IPF_MIN229LEN) { return (0); - else if (strncmp(f->ftps_rptr, - "229 Entering Extended Passive Mode", EPSV_REPLEN)) + } else if (strncmp(f->ftps_rptr, + "229 Entering Extended Passive Mode", EPSV_REPLEN)) { return (0); +} /* * Skip the EPSV command + space @@ -1401,8 +1951,9 @@ int dlen; ap += *s++ - '0'; } - if (!*s) + if (!s) { return 0; +} if (*s == '|') s++; @@ -1413,10 +1964,10 @@ int dlen; /* * check for CR-LF at the end. */ - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - } else + if ((*s != '\r') || (*(s + 1) != '\n')) { return 0; + } + s += 2; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n", @@ -1426,6 +1977,218 @@ int dlen; "229 Entering Extended Passive Mode", ap); #endif - return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s, - ip->ip_src.s_addr); + 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); + sprintf(s, "|%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 fc521d8..62707f4 100644 --- a/sys/contrib/ipfilter/netinet/ip_htable.c +++ b/sys/contrib/ipfilter/netinet/ip_htable.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -41,7 +41,7 @@ struct file; #if defined(_KERNEL) # include <sys/systm.h> #else -# include <stdio.h> +# include "ipf.h" #endif #include <netinet/in.h> #include <net/if.h> @@ -53,82 +53,277 @@ struct file; /* END OF INCLUDES */ #if !defined(lint) -static const char rcsid[] = "@(#)$Id: ip_htable.c,v 2.34.2.11 2007/09/20 12:51:51 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -#ifdef IPFILTER_LOOKUP -static iphtent_t *fr_iphmfind __P((iphtable_t *, struct in_addr *)); -static u_long ipht_nomem[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -static u_long ipf_nhtables[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -static u_long ipf_nhtnodes[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +# ifdef USE_INET6 +static iphtent_t *ipf_iphmfind6 __P((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 *)); + + +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; -iphtable_t *ipf_htables[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL }; + bzero((char *)softh, sizeof(*softh)); + return 0; +} -void fr_htable_unload() + +/* ------------------------------------------------------------------------ */ +/* 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; - (void)fr_flushhtable(&fop); + fop.iplf_arg = 0; + fop.iplf_count = 0; + *fop.iplf_name = '\0'; + ipf_htable_flush(softc, arg, &fop); } -int fr_gethtablestat(op) -iplookupop_t *op; +/* ------------------------------------------------------------------------ */ +/* 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)) + if (op->iplo_size != sizeof(stats)) { + IPFERROR(30001); return EINVAL; + } - stats.iphs_tables = ipf_htables[op->iplo_unit]; - stats.iphs_numtables = ipf_nhtables[op->iplo_unit]; - stats.iphs_numnodes = ipf_nhtnodes[op->iplo_unit]; - stats.iphs_nomem = ipht_nomem[op->iplo_unit]; + 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]; - return COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + if (err != 0) { + IPFERROR(30013); + return EFAULT; + } + return 0; } -/* - * Create a new hash table using the template passed. - */ -int fr_newhtable(op) -iplookupop_t *op; +/* ------------------------------------------------------------------------ */ +/* 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; { - iphtable_t *iph, *oiph; + 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 = fr_existshtable(unit, op->iplo_name); + iph = ipf_htable_exists(softh, unit, op->iplo_name); if (iph != NULL) { - if ((iph->iph_flags & IPHASH_DELETE) == 0) + 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) { - ipht_nomem[op->iplo_unit]++; + softh->ipht_nomem[op->iplo_unit + 1]++; + IPFERROR(30002); return ENOMEM; } - err = COPYIN(op->iplo_struct, iph, sizeof(*iph)); - if (err != 0) { - KFREE(iph); - return EFAULT; - } - - if (iph->iph_unit != unit) { - KFREE(iph); - return EINVAL; - } + *iph = htab; if ((op->iplo_arg & IPHASH_ANON) != 0) { i = IPHASH_ANON; @@ -139,7 +334,7 @@ iplookupop_t *op; #else (void)sprintf(name, "%u", i); #endif - for (oiph = ipf_htables[unit]; oiph != NULL; + for (oiph = softh->ipf_htables[unit + 1]; oiph != NULL; oiph = oiph->iph_next) if (strncmp(oiph->iph_name, name, sizeof(oiph->iph_name)) == 0) @@ -149,113 +344,316 @@ iplookupop_t *op; (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); - ipht_nomem[unit]++; + softh->ipht_nomem[unit + 1]++; + IPFERROR(30006); return ENOMEM; } bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); - iph->iph_masks = 0; - iph->iph_list = NULL; + 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_next = ipf_htables[unit]; - iph->iph_pnext = &ipf_htables[unit]; - if (ipf_htables[unit] != NULL) - ipf_htables[unit]->iph_pnext = &iph->iph_next; - ipf_htables[unit] = iph; - ipf_nhtables[unit]++; + 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; } -/* - */ -int fr_removehtable(unit, name) -int unit; -char *name; +/* ------------------------------------------------------------------------ */ +/* 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 = fr_findhtable(unit, name); - if (iph == NULL) + 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) { - (void) fr_clearhtable(iph); + ipf_htable_clear(softc, arg, iph); iph->iph_flags |= IPHASH_DELETE; return 0; } - fr_delhtable(iph); + ipf_htable_remove(softc, arg, iph); return 0; } -int fr_clearhtable(iph) -iphtable_t *iph; +/* ------------------------------------------------------------------------ */ +/* 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 (fr_delhtent(iph, ipe) != 0) + if (ipf_htent_remove(softc, arg, iph, ipe) != 0) return 1; return 0; } -int fr_delhtable(iph) -iphtable_t *iph; +/* ------------------------------------------------------------------------ */ +/* 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 (fr_clearhtable(iph) != 0) + 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; + } - ipf_nhtables[iph->iph_unit]--; + err = ipf_htent_remove(softc, arg, iph, ent); - return fr_derefhtable(iph); + return err; } -/* - * Delete an entry from a hash table. - */ -int fr_delhtent(iph, ipe) -iphtable_t *iph; -iphtent_t *ipe; +/* ------------------------------------------------------------------------ */ +/* 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_phnext != NULL) - *ipe->ipe_phnext = ipe->ipe_hnext; 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_pnext != NULL) - *ipe->ipe_pnext = ipe->ipe_next; 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) - fr_delgroup(ipe->ipe_group, IPL_LOGIPF, fr_active); + ipf_group_del(softc, ipe->ipe_ptr, NULL); break; default : @@ -264,35 +662,54 @@ iphtent_t *ipe; break; } - return fr_derefhtent(ipe); + return ipf_htent_deref(arg, ipe); } -int fr_derefhtable(iph) -iphtable_t *iph; +/* ------------------------------------------------------------------------ */ +/* 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) { - KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); - KFREE(iph); + ipf_htable_free(softh, iph); } return refs; } -int fr_derefhtent(ipe) -iphtent_t *ipe; +/* ------------------------------------------------------------------------ */ +/* 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) { - ipf_nhtnodes[ipe->ipe_unit]--; - + softh->ipf_nhtnodes[ipe->ipe_unit + 1]--; KFREE(ipe); return 0; @@ -302,26 +719,87 @@ iphtent_t *ipe; } -iphtable_t *fr_existshtable(unit, name) -int unit; -char *name; +/* ------------------------------------------------------------------------ */ +/* 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; - for (iph = ipf_htables[unit]; iph != NULL; iph = iph->iph_next) - if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) - break; + 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; } -iphtable_t *fr_findhtable(unit, name) -int unit; -char *name; +/* ------------------------------------------------------------------------ */ +/* 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 = fr_existshtable(unit, name); + iph = ipf_htable_exists(arg, unit, name); if ((iph != NULL) && (iph->iph_flags & IPHASH_DELETE) == 0) return iph; @@ -329,19 +807,31 @@ char *name; } -size_t fr_flushhtable(op) -iplookupflush_t *op; +/* ------------------------------------------------------------------------ */ +/* 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 = 0; i <= IPL_LOGMAX; i++) { + for (i = -1; i <= IPL_LOGMAX; i++) { if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) { - while ((iph = ipf_htables[i]) != NULL) { - if (fr_delhtable(iph) == 0) { + while ((iph = softh->ipf_htables[i + 1]) != NULL) { + if (ipf_htable_remove(softc, arg, iph) == 0) { freed++; } else { iph->iph_flags |= IPHASH_DELETE; @@ -354,13 +844,73 @@ iplookupflush_t *op; } -/* - * Add an entry to a hash table. - */ -int fr_addhtent(iph, ipeo) -iphtable_t *iph; -iphtent_t *ipeo; +/* ------------------------------------------------------------------------ */ +/* 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; @@ -370,13 +920,35 @@ iphtent_t *ipeo; return -1; bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe)); - ipe->ipe_addr.in4_addr &= ipe->ipe_mask.in4_addr; - ipe->ipe_addr.in4_addr = ntohl(ipe->ipe_addr.in4_addr); - bits = count4bits(ipe->ipe_mask.in4_addr); - ipe->ipe_mask.in4_addr = ntohl(ipe->ipe_mask.in4_addr); + 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; + } - hv = IPE_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr, - iph->iph_size); + ipe->ipe_owner = iph; ipe->ipe_ref = 1; ipe->ipe_hnext = iph->iph_table[hv]; ipe->ipe_phnext = iph->iph_table + hv; @@ -385,21 +957,63 @@ iphtent_t *ipeo; iph->iph_table[hv]->ipe_phnext = &ipe->ipe_hnext; iph->iph_table[hv] = ipe; - ipe->ipe_next = iph->iph_list; - ipe->ipe_pnext = &iph->iph_list; - if (ipe->ipe_next != NULL) - ipe->ipe_next->ipe_pnext = &ipe->ipe_next; - iph->iph_list = 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 ((bits >= 0) && (bits != 32)) - iph->iph_masks |= 1 << bits; + 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 = fr_addgroup(ipe->ipe_group, NULL, + ipe->ipe_ptr = ipf_group_add(softc, ipe->ipe_group, NULL, iph->iph_flags, IPL_LOGIPF, - fr_active); + softc->ipf_active); break; default : @@ -409,116 +1023,218 @@ iphtent_t *ipeo; } ipe->ipe_unit = iph->iph_unit; - ipf_nhtnodes[ipe->ipe_unit]++; + softh->ipf_nhtnodes[ipe->ipe_unit + 1]++; return 0; } -void *fr_iphmfindgroup(tptr, aptr) -void *tptr, *aptr; +/* ------------------------------------------------------------------------ */ +/* 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(&ip_poolrw); + READ_ENTER(&softc->ipf_poolrw); iph = tptr; addr = aptr; - ipe = fr_iphmfind(iph, addr); + ipe = ipf_iphmfind(iph, addr); if (ipe != NULL) rval = ipe->ipe_ptr; else rval = NULL; - RWLOCK_EXIT(&ip_poolrw); + RWLOCK_EXIT(&softc->ipf_poolrw); return rval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_iphmfindip */ +/* Function: ipf_iphmfindip */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ -/* Parameters: tptr(I) - pointer to the pool to search */ +/* 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. */ /* ------------------------------------------------------------------------ */ -int fr_iphmfindip(tptr, ipversion, aptr) -void *tptr, *aptr; -int ipversion; +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 (ipversion != 4) - return -1; - if (tptr == NULL || aptr == NULL) return -1; iph = tptr; addr = aptr; - READ_ENTER(&ip_poolrw); - ipe = fr_iphmfind(iph, addr); - if (ipe != NULL) + 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; - else + ipe->ipe_hits++; + ipe->ipe_bytes += bytes; + } else { rval = 1; - RWLOCK_EXIT(&ip_poolrw); + } + RWLOCK_EXIT(&softc->ipf_poolrw); return rval; } -/* Locks: ip_poolrw */ -static iphtent_t *fr_iphmfind(iph, addr) -iphtable_t *iph; -struct in_addr *addr; +/* ------------------------------------------------------------------------ */ +/* 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 hmsk, msk, ips; + u_32_t msk, ips; iphtent_t *ipe; u_int hv; + int i; - hmsk = iph->iph_masks; - msk = 0xffffffff; + i = 0; maskloop: - ips = ntohl(addr->s_addr) & msk; - hv = IPE_HASH_FN(ips, msk, iph->iph_size); + 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_mask.in4_addr != msk || - ipe->ipe_addr.in4_addr != ips) { + if ((ipe->ipe_family != AF_INET) || + (ipe->ipe_mask.in4_addr != msk) || + (ipe->ipe_addr.in4_addr != ips)) { continue; } break; } - if ((ipe == NULL) && (hmsk != 0)) { - while (hmsk != 0) { - msk <<= 1; - if (hmsk & 0x80000000) - break; - hmsk <<= 1; - } - if (hmsk != 0) { - hmsk <<= 1; + if (ipe == NULL) { + i++; + if (i < iph->iph_v4_masks.imt4_max) goto maskloop; - } } return ipe; } -int fr_htable_getnext(token, ilp) -ipftoken_t *token; -ipflookupiter_t *ilp; +/* ------------------------------------------------------------------------ */ +/* 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; @@ -527,14 +1243,14 @@ ipflookupiter_t *ilp; nextiph = NULL; nextnode = NULL; - READ_ENTER(&ip_poolrw); + READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : iph = token->ipt_data; if (iph == NULL) { - nextiph = ipf_htables[(int)ilp->ili_unit]; + nextiph = softh->ipf_htables[(int)ilp->ili_unit + 1]; } else { nextiph = iph->iph_next; } @@ -547,15 +1263,18 @@ ipflookupiter_t *ilp; nextiph = &zp; token->ipt_data = NULL; } + hnext = nextiph->iph_next; break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { - iph = fr_findhtable(ilp->ili_unit, ilp->ili_name); - if (iph == NULL) + iph = ipf_htable_find(arg, ilp->ili_unit, + ilp->ili_name); + if (iph == NULL) { + IPFERROR(30009); err = ESRCH; - else { + } else { nextnode = iph->iph_list; } } else { @@ -570,73 +1289,179 @@ ipflookupiter_t *ilp; nextnode = &zn; token->ipt_data = NULL; } + hnext = nextnode->ipe_next; break; + default : + IPFERROR(30010); err = EINVAL; + hnext = NULL; break; } - RWLOCK_EXIT(&ip_poolrw); + RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return err; switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : - if (iph != NULL) { - WRITE_ENTER(&ip_poolrw); - fr_derefhtable(iph); - RWLOCK_EXIT(&ip_poolrw); - } err = COPYOUT(nextiph, ilp->ili_data, sizeof(*nextiph)); - if (err != 0) + 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 : - if (node != NULL) { - WRITE_ENTER(&ip_poolrw); - fr_derefhtent(node); - RWLOCK_EXIT(&ip_poolrw); - } err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); - if (err != 0) + 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; } -void fr_htable_iterderef(otype, unit, data) -u_int otype; -int unit; -void *data; +/* ------------------------------------------------------------------------ */ +/* 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; + return EFAULT; - if (unit < 0 || unit > IPL_LOGMAX) - return; + if (unit < -1 || unit > IPL_LOGMAX) + return EINVAL; switch (otype) { case IPFLOOKUPITER_LIST : - WRITE_ENTER(&ip_poolrw); - fr_derefhtable((iphtable_t *)data); - RWLOCK_EXIT(&ip_poolrw); + ipf_htable_deref(softc, arg, (iphtable_t *)data); break; case IPFLOOKUPITER_NODE : - WRITE_ENTER(&ip_poolrw); - fr_derefhtent((iphtent_t *)data); - RWLOCK_EXIT(&ip_poolrw); + 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); + } +} + -#endif /* IPFILTER_LOOKUP */ +#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 2c08812..8038bbc 100644 --- a/sys/contrib/ipfilter/netinet/ip_htable.h +++ b/sys/contrib/ipfilter/netinet/ip_htable.h @@ -6,36 +6,52 @@ 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; + } ipe_un; } iphtent_t; #define ipe_value ipe_un.ipeu_int #define ipe_group ipe_un.ipeu_char -#define IPE_HASH_FN(a, m, s) (((a) * (m)) % (s)) - +#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_masks; /* IPv4 netmasks in use */ + u_int iph_maskset[4]; /* netmasks in use */ char iph_name[FR_GROUPLEN]; /* hash table number */ } iphtable_t; @@ -55,24 +71,11 @@ typedef struct iphtstat_s { } iphtstat_t; -extern iphtable_t *ipf_htables[IPL_LOGSIZE]; - -extern iphtable_t *fr_existshtable __P((int, char *)); -extern int fr_clearhtable __P((iphtable_t *)); -extern void fr_htable_unload __P((void)); -extern int fr_newhtable __P((iplookupop_t *)); -extern iphtable_t *fr_findhtable __P((int, char *)); -extern int fr_removehtable __P((int, char *)); -extern size_t fr_flushhtable __P((iplookupflush_t *)); -extern int fr_addhtent __P((iphtable_t *, iphtent_t *)); -extern int fr_delhtent __P((iphtable_t *, iphtent_t *)); -extern int fr_derefhtable __P((iphtable_t *)); -extern int fr_derefhtent __P((iphtent_t *)); -extern int fr_delhtable __P((iphtable_t *)); -extern void *fr_iphmfindgroup __P((void *, void *)); -extern int fr_iphmfindip __P((void *, int, void *)); -extern int fr_gethtablestat __P((iplookupop_t *)); -extern int fr_htable_getnext __P((ipftoken_t *, ipflookupiter_t *)); -extern void fr_htable_iterderef __P((u_int, int, void *)); +extern void *ipf_iphmfindgroup __P((ipf_main_softc_t *, void *, void *)); +extern iphtable_t *ipf_htable_find __P((void *, int, char *)); +extern ipf_lookup_t ipf_htable_backend; +#ifndef _KERNEL +extern void ipf_htable_dump __P((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 e88a6b9..980476a 100644 --- a/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c @@ -1,152 +1,228 @@ /* - * Copyright (C) 2001-2003 by Darren Reed + * 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: ip_ipsec_pxy.c,v 2.20.2.8 2006/07/14 06:12:14 darrenr Exp $ + * $Id$ * */ #define IPF_IPSEC_PROXY -int ippr_ipsec_init __P((void)); -void ippr_ipsec_fini __P((void)); -int ippr_ipsec_new __P((fr_info_t *, ap_session_t *, nat_t *)); -void ippr_ipsec_del __P((ap_session_t *)); -int ippr_ipsec_inout __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ipsec_match __P((fr_info_t *, ap_session_t *, nat_t *)); - -static frentry_t ipsecfr; -static ipftq_t *ipsecnattqe; -static ipftq_t *ipsecstatetqe; -static char ipsec_buffer[1500]; +/* + * 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 *)); -int ipsec_proxy_init = 0; -int ipsec_proxy_ttl = 60; /* * IPSec application proxy initialization. */ -int ippr_ipsec_init() +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; { - bzero((char *)&ipsecfr, sizeof(ipsecfr)); - ipsecfr.fr_ref = 1; - ipsecfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; - MUTEX_INIT(&ipsecfr.fr_lock, "IPsec proxy rule lock"); - ipsec_proxy_init = 1; - - ipsecnattqe = fr_addtimeoutqueue(&nat_utqe, ipsec_proxy_ttl); - if (ipsecnattqe == NULL) + 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; - ipsecstatetqe = fr_addtimeoutqueue(&ips_utqe, ipsec_proxy_ttl); - if (ipsecstatetqe == NULL) { - if (fr_deletetimeoutqueue(ipsecnattqe) == 0) - fr_freetimeoutqueue(ipsecnattqe); - ipsecnattqe = NULL; + 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; } - ipsecnattqe->ifq_flags |= IFQF_PROXY; - ipsecstatetqe->ifq_flags |= IFQF_PROXY; - - ipsecfr.fr_age[0] = ipsec_proxy_ttl; - ipsecfr.fr_age[1] = ipsec_proxy_ttl; + 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 ippr_ipsec_fini() +void +ipf_p_ipsec_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - if (ipsecnattqe != NULL) { - if (fr_deletetimeoutqueue(ipsecnattqe) == 0) - fr_freetimeoutqueue(ipsecnattqe); + 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); } - ipsecnattqe = NULL; - if (ipsecstatetqe != NULL) { - if (fr_deletetimeoutqueue(ipsecstatetqe) == 0) - fr_freetimeoutqueue(ipsecstatetqe); + 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); } - ipsecstatetqe = NULL; + softi->ipsec_state_tqe = NULL; +} + - if (ipsec_proxy_init == 1) { - MUTEX_DESTROY(&ipsecfr.fr_lock); - ipsec_proxy_init = 0; +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 ippr_ipsec_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; - ipnat_t *ipn; char *ptr; - int p, off, dlen, ttl; - mb_t *m; + 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(ipsec_buffer, sizeof(ipsec_buffer)); + 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(ipsec_buffer), dlen), ipsec_buffer); + COPYDATA(m, off, MIN(sizeof(softi->ipsec_buffer), dlen), + softi->ipsec_buffer); - if (nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_inip, + if (ipf_nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_nsrcip, ip->ip_dst) != NULL) return -1; - aps->aps_psiz = sizeof(*ipsec); - KMALLOCS(aps->aps_data, ipsec_pxy_t *, sizeof(*ipsec)); - if (aps->aps_data == NULL) + np = nat->nat_ptr; + size = np->in_size; + KMALLOC(ipsec, ipsec_pxy_t *); + if (ipsec == NULL) return -1; - ipsec = aps->aps_data; + 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 = &ipsec->ipsc_rule; - ttl = IPF_TTLVAL(ipsecnattqe->ifq_ttl); - ipn->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, ttl); - ipn->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, ttl); + 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_nip = ntohl(nat->nat_outip.s_addr); + ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_ippip = 1; - ipn->in_inip = nat->nat_inip.s_addr; - ipn->in_inmsk = 0xffffffff; - ipn->in_outip = fin->fin_saddr; - ipn->in_outmsk = nat->nat_outip.s_addr; - ipn->in_srcip = fin->fin_saddr; - ipn->in_srcmsk = 0xffffffff; + 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; - bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0], - sizeof(ipn->in_ifnames[0])); - ipn->in_p = IPPROTO_ESP; + 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_state = NULL; - fi.fin_nat = NULL; fi.fin_fi.fi_p = IPPROTO_ESP; - fi.fin_fr = &ipsecfr; + fi.fin_fr = &softi->ipsec_fr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; p = ip->ip_p; @@ -154,7 +230,7 @@ nat_t *nat; fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); fi.fin_flx |= FI_IGNORE; - ptr = ipsec_buffer; + 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)); @@ -166,18 +242,19 @@ nat_t *nat; if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0) ipsec->ipsc_rckset = 1; - ipsec->ipsc_nat = nat_new(&fi, ipn, &ipsec->ipsc_nat, - NAT_SLAVE|SI_WILDP, NAT_OUTBOUND); + 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) nat_proto(&fi, ipsec->ipsc_nat, 0); - nat_update(&fi, ipsec->ipsc_nat, ipn); + (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; - ipsec->ipsc_state = fr_addstate(&fi, &ipsec->ipsc_state, - SI_WILDP); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP); } ip->ip_p = p & 0xff; return 0; @@ -188,11 +265,15 @@ nat_t *nat; * For outgoing IKE packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ -int ippr_ipsec_inout(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; @@ -212,10 +293,8 @@ nat_t *nat; if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) { bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_fi.fi_p = IPPROTO_ESP; - fi.fin_fr = &ipsecfr; + fi.fin_fr = &softi->ipsec_fr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; ip->ip_p = IPPROTO_ESP; @@ -227,36 +306,42 @@ nat_t *nat; * Update NAT timeout/create NAT if missing. */ if (ipsec->ipsc_nat != NULL) - fr_queueback(&ipsec->ipsc_nat->nat_tqe); + ipf_queueback(softc->ipf_ticks, + &ipsec->ipsc_nat->nat_tqe); else { - ipsec->ipsc_nat = nat_new(&fi, &ipsec->ipsc_rule, - &ipsec->ipsc_nat, - NAT_SLAVE|SI_WILDP, - nat->nat_dir); +#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) nat_proto(&fi, ipsec->ipsc_nat, 0); - nat_update(&fi, ipsec->ipsc_nat, - &ipsec->ipsc_rule); + (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(&ipf_state); + READ_ENTER(&softc->ipf_state); if (ipsec->ipsc_state != NULL) { - fr_queueback(&ipsec->ipsc_state->is_sti); + ipf_queueback(softc->ipf_ticks, + &ipsec->ipsc_state->is_sti); ipsec->ipsc_state->is_die = nat->nat_age; - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } else { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); fi.fin_data[0] = 0; fi.fin_data[1] = 0; - ipsec->ipsc_state = fr_addstate(&fi, - &ipsec->ipsc_state, - SI_WILDP); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, + SI_WILDP); } ip->ip_p = p; } @@ -270,10 +355,11 @@ nat_t *nat; * in the same order (not reversed depending on packet flow direction as with * UDP/TCP port numbers). */ -int ippr_ipsec_match(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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]; @@ -314,8 +400,10 @@ nat_t *nat; /* * clean up after ourselves. */ -void ippr_ipsec_del(aps) -ap_session_t *aps; +void +ipf_p_ipsec_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; { ipsec_pxy_t *ipsec; @@ -327,15 +415,17 @@ ap_session_t *aps; * *_del() is on a callback from aps_free(), from nat_delete() */ - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); if (ipsec->ipsc_state != NULL) { - ipsec->ipsc_state->is_die = fr_ticks + 1; + ipsec->ipsc_state->is_die = softc->ipf_ticks + 1; ipsec->ipsc_state->is_me = NULL; - fr_queuefront(&ipsec->ipsc_state->is_sti); + ipf_queuefront(&ipsec->ipsc_state->is_sti); } - RWLOCK_EXIT(&ipf_state); + 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 5bb252a..b9954b4 100644 --- a/sys/contrib/ipfilter/netinet/ip_irc_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_irc_pxy.c @@ -1,9 +1,9 @@ /* - * Copyright (C) 2000-2003 Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * $Id: ip_irc_pxy.c,v 2.39.2.6 2006/07/14 06:12:14 darrenr Exp $ + * $Id$ */ #define IPF_IRC_PROXY @@ -11,12 +11,12 @@ #define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ -int ippr_irc_init __P((void)); -void ippr_irc_fini __P((void)); -int ippr_irc_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_irc_out __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_irc_send __P((fr_info_t *, nat_t *)); -int ippr_irc_complete __P((ircinfo_t *, char *, size_t)); +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 **)); static frentry_t ircnatfr; @@ -27,19 +27,19 @@ int irc_proxy_init = 0; /* * Initialize local structures. */ -int ippr_irc_init() +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; - - return 0; } -void ippr_irc_fini() +void +ipf_p_irc_main_unload() { if (irc_proxy_init == 1) { MUTEX_DESTROY(&ircnatfr.fr_lock); @@ -48,7 +48,7 @@ void ippr_irc_fini() } -const char *ippr_irc_dcctypes[] = { +const char *ipf_p_irc_dcctypes[] = { "CHAT ", /* CHAT chat ipnumber portnumber */ "SEND ", /* SEND filename ipnumber portnumber */ "MOVE ", @@ -64,10 +64,11 @@ const char *ippr_irc_dcctypes[] = { */ -int ippr_irc_complete(ircp, buf, len) -ircinfo_t *ircp; -char *buf; -size_t len; +int +ipf_p_irc_complete(ircp, buf, len) + ircinfo_t *ircp; + char *buf; + size_t len; { register char *s, c; register size_t i; @@ -145,12 +146,12 @@ size_t len; /* * Check for a recognised DCC command */ - for (j = 0, k = 0; ippr_irc_dcctypes[j]; j++) { - k = MIN(strlen(ippr_irc_dcctypes[j]), i); - if (!strncmp(ippr_irc_dcctypes[j], s, k)) + 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 (!ippr_irc_dcctypes[j]) + if (!ipf_p_irc_dcctypes[j]) return 0; ircp->irc_type = s; @@ -222,18 +223,22 @@ size_t len; } -int ippr_irc_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; - fin = fin; /* LINT */ nat = nat; /* LINT */ aps->aps_data = irc; @@ -244,13 +249,15 @@ nat_t *nat; } -int ippr_irc_send(fin, nat) -fr_info_t *fin; -nat_t *nat; +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; @@ -263,6 +270,7 @@ nat_t *nat; #ifdef MENTAT mb_t *m1; #endif + softc = fin->fin_main_soft; m = fin->fin_m; ip = fin->fin_ip; @@ -285,14 +293,14 @@ nat_t *nat; *newbuf = '\0'; irc = nat->nat_aps->aps_data; - if (ippr_irc_complete(irc, ctcpbuf, dlen) == 0) + if (ipf_p_irc_complete(irc, ctcpbuf, dlen) == 0) 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. + * 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_inip.s_addr)) + if (irc->irc_ipnum != ntohl(nat->nat_osrcaddr)) return 0; a5 = irc->irc_port; @@ -315,7 +323,7 @@ nat_t *nat; nlen = strlen(newbuf); inc = nlen - olen; - if ((inc + ip->ip_len) > 65535) + if ((inc + fin->fin_plen) > 65535) return 0; #ifdef MENTAT @@ -326,14 +334,14 @@ nat_t *nat; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); - PANIC((!nm),("ippr_irc_out: allocb failed")); + 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), - ("ippr_irc_out: cannot handle fragmented data block")); + ("ipf_p_irc_out: cannot handle fragmented data block")); linkb(m1, nm); } else { @@ -350,13 +358,14 @@ nat_t *nat; /* 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) || defined(__sgi) register u_32_t sum1, sum2; - sum1 = ip->ip_len; - sum2 = ip->ip_len + inc; + sum1 = fin->fin_plen; + sum2 = fin->fin_plen + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) @@ -364,9 +373,11 @@ nat_t *nat; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); - fix_outcksum(fin, &ip->ip_sum, sum2); + ipf_fix_outcksum(0, &ip->ip_sum, sum2, 0); #endif - ip->ip_len += inc; + fin->fin_plen += inc; + ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += inc; } /* @@ -389,16 +400,18 @@ nat_t *nat; bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1]; - nat2 = nat_outlookup(fin, IPN_TCP, nat->nat_p, nat->nat_inip, + 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_state = NULL; - fi.fin_nat = NULL; fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; @@ -406,16 +419,18 @@ nat_t *nat; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); swip = ip->ip_src; - ip->ip_src = nat->nat_inip; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, + 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) nat_proto(&fi, nat2, 0); - nat_update(&fi, nat2, nat2->nat_ptr); + (void) ipf_nat_proto(&fi, nat2, 0); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); } ip->ip_src = swip; } @@ -423,11 +438,13 @@ nat_t *nat; } -int ippr_irc_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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 ippr_irc_send(fin, nat); + return ipf_p_irc_send(fin, nat); } diff --git a/sys/contrib/ipfilter/netinet/ip_log.c b/sys/contrib/ipfilter/netinet/ip_log.c index 863cc9c..d41384d 100644 --- a/sys/contrib/ipfilter/netinet/ip_log.c +++ b/sys/contrib/ipfilter/netinet/ip_log.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -15,22 +15,8 @@ # define KERNEL 1 # define _KERNEL 1 #endif -#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ - defined(_KERNEL) -# if (__NetBSD_Version__ < 399001400) -# include "opt_ipfilter_log.h" -# else -# include "opt_ipfilter.h" -# endif -#endif -#if defined(__FreeBSD__) && !defined(IPFILTER_LKM) -# if defined(_KERNEL) -# if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) -# include "opt_ipfilter.h" -# endif -# else -# include <osreldate.h> -# endif +#if defined(__FreeBSD__) && !defined(_KERNEL) +# include <osreldate.h> #endif #ifndef SOLARIS # define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) @@ -104,7 +90,6 @@ struct file; #if __FreeBSD_version >= 300000 # include <net/if_var.h> #endif -#include <net/route.h> #include <netinet/in.h> #ifdef __sgi # include <sys/ddi.h> @@ -148,100 +133,240 @@ struct file; # include <machine/sys/user.h> # include <sys/kthread_iface.h> # define READ_COLLISION 0x001 - -iplog_select_t iplog_ss[IPL_LOGMAX+1]; - extern int selwait; # endif /* IPL_SELECT */ +typedef struct ipf_log_softc_s { + ipfmutex_t ipl_mutex[IPL_LOGSIZE]; +# if SOLARIS && defined(_KERNEL) + kcondvar_t ipl_wait[IPL_LOGSIZE]; +# endif # if defined(linux) && defined(_KERNEL) -wait_queue_head_t iplh_linux[IPL_LOGSIZE]; + wait_queue_head_t iplh_linux[IPL_LOGSIZE]; # endif -# if SOLARIS && defined(_KERNEL) -extern kcondvar_t iplwait; -extern struct pollhead iplpollhead[IPL_LOGSIZE]; +# if defined(__hpux) && defined(_KERNEL) + iplog_select_t ipl_ss[IPL_LOGSIZE]; # endif + iplog_t **iplh[IPL_LOGSIZE]; + iplog_t *iplt[IPL_LOGSIZE]; + iplog_t *ipll[IPL_LOGSIZE]; + u_long ipl_logfail[IPL_LOGSIZE]; + u_long ipl_logok[IPL_LOGSIZE]; + fr_info_t ipl_crc[IPL_LOGSIZE]; + u_32_t ipl_counter[IPL_LOGSIZE]; + int ipl_suppress; + int ipl_logall; + int ipl_log_init; + int ipl_logsize; + int ipl_used[IPL_LOGSIZE]; + int ipl_magic[IPL_LOGSIZE]; + ipftuneable_t *ipf_log_tune; + int ipl_readers[IPL_LOGSIZE]; +} ipf_log_softc_t; + +static int magic[IPL_LOGSIZE] = { IPL_MAGIC, IPL_MAGIC_NAT, IPL_MAGIC_STATE, + IPL_MAGIC, IPL_MAGIC, IPL_MAGIC, + IPL_MAGIC, IPL_MAGIC }; + +static ipftuneable_t ipf_log_tuneables[] = { + /* log */ + { { (void *)offsetof(ipf_log_softc_t, ipl_suppress) }, + "log_suppress", 0, 1, + stsizeof(ipf_log_softc_t, ipl_suppress), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_log_softc_t, ipl_logall) }, + "log_all", 0, 1, + stsizeof(ipf_log_softc_t, ipl_logall), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_log_softc_t, ipl_logsize) }, + "log_size", 0, 0x80000, + stsizeof(ipf_log_softc_t, ipl_logsize), + 0, NULL, NULL }, + { { NULL }, NULL, 0, 0, + 0, + 0, NULL, NULL } +}; + + +int +ipf_log_main_load() +{ + return 0; +} + + +int +ipf_log_main_unload() +{ + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_soft_create */ +/* Returns: void * - NULL = failure, else pointer to log context data */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* */ +/* Initialise log buffers & pointers. Also iniialised the CRC to a local */ +/* secret for use in calculating the "last log checksum". */ +/* ------------------------------------------------------------------------ */ +void * +ipf_log_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_log_softc_t *softl; -iplog_t **iplh[IPL_LOGSIZE], *iplt[IPL_LOGSIZE], *ipll[IPL_LOGSIZE]; -int iplused[IPL_LOGSIZE]; -static fr_info_t iplcrc[IPL_LOGSIZE]; -int ipl_suppress = 1; -int ipl_logmax = IPL_LOGMAX; -int ipl_logall = 0; -int ipl_log_init = 0; -int ipl_logsize = IPFILTER_LOGSIZE; -int ipl_magic[IPL_LOGSIZE] = { IPL_MAGIC, IPL_MAGIC_NAT, IPL_MAGIC_STATE, - IPL_MAGIC, IPL_MAGIC, IPL_MAGIC, - IPL_MAGIC, IPL_MAGIC }; + KMALLOC(softl, ipf_log_softc_t *); + if (softl == NULL) + return NULL; + bzero((char *)softl, sizeof(*softl)); + bcopy((char *)magic, (char *)softl->ipl_magic, sizeof(magic)); + + softl->ipf_log_tune = ipf_tune_array_copy(softl, + sizeof(ipf_log_tuneables), + ipf_log_tuneables); + if (softl->ipf_log_tune == NULL) { + ipf_log_soft_destroy(softc, softl); + return NULL; + } + if (ipf_tune_array_link(softc, softl->ipf_log_tune) == -1) { + ipf_log_soft_destroy(softc, softl); + return NULL; + } + + softl->ipl_suppress = 1; + softl->ipl_logall = 0; + softl->ipl_log_init = 0; + softl->ipl_logsize = IPFILTER_LOGSIZE; + + return softl; +} /* ------------------------------------------------------------------------ */ -/* Function: fr_loginit */ +/* Function: ipf_log_soft_init */ /* Returns: int - 0 == success (always returned) */ -/* Parameters: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise log buffers & pointers. Also iniialised the CRC to a local */ /* secret for use in calculating the "last log checksum". */ /* ------------------------------------------------------------------------ */ -int fr_loginit() +int +ipf_log_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - int i; + ipf_log_softc_t *softl = arg; + int i; for (i = IPL_LOGMAX; i >= 0; i--) { - iplt[i] = NULL; - ipll[i] = NULL; - iplh[i] = &iplt[i]; - iplused[i] = 0; - bzero((char *)&iplcrc[i], sizeof(iplcrc[i])); + softl->iplt[i] = NULL; + softl->ipll[i] = NULL; + softl->iplh[i] = &softl->iplt[i]; + bzero((char *)&softl->ipl_crc[i], sizeof(softl->ipl_crc[i])); # ifdef IPL_SELECT - iplog_ss[i].read_waiter = 0; - iplog_ss[i].state = 0; + softl->iplog_ss[i].read_waiter = 0; + softl->iplog_ss[i].state = 0; # endif # if defined(linux) && defined(_KERNEL) - init_waitqueue_head(iplh_linux + i); + init_waitqueue_head(softl->iplh_linux + i); # endif - } - # if SOLARIS && defined(_KERNEL) - cv_init(&iplwait, "ipl condvar", CV_DRIVER, NULL); + cv_init(&softl->ipl_wait[i], NULL, CV_DRIVER, NULL); # endif - MUTEX_INIT(&ipl_mutex, "ipf log mutex"); + MUTEX_INIT(&softl->ipl_mutex[i], "ipf log mutex"); + } + - ipl_log_init = 1; + softl->ipl_log_init = 1; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_logunload */ -/* Returns: Nil */ -/* Parameters: Nil */ +/* Function: ipf_log_soft_fini */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to log context structure */ /* */ /* Clean up any log data that has accumulated without being read. */ /* ------------------------------------------------------------------------ */ -void fr_logunload() +int +ipf_log_soft_fini(softc, arg) + ipf_main_softc_t *softc; + void *arg; { + ipf_log_softc_t *softl = arg; int i; - if (ipl_log_init == 0) - return; + if (softl->ipl_log_init == 0) + return 0; + + softl->ipl_log_init = 0; - for (i = IPL_LOGMAX; i >= 0; i--) - (void) ipflog_clear(i); + for (i = IPL_LOGMAX; i >= 0; i--) { + (void) ipf_log_clear(softc, i); + + /* + * This is a busy-wait loop so as to avoid yet another lock + * to wait on. + */ + MUTEX_ENTER(&softl->ipl_mutex[i]); + while (softl->ipl_readers[i] > 0) { +# if SOLARIS && defined(_KERNEL) + cv_broadcast(&softl->ipl_wait[i]); + MUTEX_EXIT(&softl->ipl_mutex[i]); + delay(100); + pollwakeup(&softc->ipf_poll_head[i], POLLRDNORM); +# else + MUTEX_EXIT(&softl->ipl_mutex[i]); + WAKEUP(softl->iplh, i); + POLLWAKEUP(i); +# endif + MUTEX_ENTER(&softl->ipl_mutex[i]); + } + MUTEX_EXIT(&softl->ipl_mutex[i]); + } + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_soft_destroy */ +/* Parameters: softc(I) - pointer to soft context main structure */ +/* arg(I) - pointer to log context structure */ +/* */ +/* When this function is called, it is expected that there are no longer */ +/* any threads active in the reading code path or the logging code path. */ +/* ------------------------------------------------------------------------ */ +void +ipf_log_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_log_softc_t *softl = arg; + int i; + for (i = IPL_LOGMAX; i >= 0; i--) { # if SOLARIS && defined(_KERNEL) - cv_destroy(&iplwait); + cv_destroy(&softl->ipl_wait[i]); # endif - MUTEX_DESTROY(&ipl_mutex); + MUTEX_DESTROY(&softl->ipl_mutex[i]); + } + + if (softl->ipf_log_tune != NULL) { + ipf_tune_array_unlink(softc, softl->ipf_log_tune); + KFREES(softl->ipf_log_tune, sizeof(ipf_log_tuneables)); + softl->ipf_log_tune = NULL; + } - ipl_log_init = 0; + KFREE(softl); } /* ------------------------------------------------------------------------ */ -/* Function: ipflog */ -/* Returns: int - 0 == success, -1 == failure */ +/* Function: ipf_log_pkt */ +/* Returns: int - 0 == success, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - flags from filter rules */ /* */ @@ -251,10 +376,13 @@ void fr_logunload() /* how much data to copy into the log, including part of the data body if */ /* requested. */ /* ------------------------------------------------------------------------ */ -int ipflog(fin, flags) -fr_info_t *fin; -u_int flags; +int +ipf_log_pkt(fin, flags) + fr_info_t *fin; + u_int flags; { + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_log_softc_t *softl = softc->ipf_log_soft; register size_t hlen; int types[2], mlen; size_t sizes[2]; @@ -262,8 +390,7 @@ u_int flags; ipflog_t ipfl; u_char p; mb_t *m; -# if (SOLARIS || defined(__hpux)) && defined(_KERNEL) && \ - !defined(_INET_IP_STACK_H) +# if (SOLARIS || defined(__hpux)) && defined(_KERNEL) && !defined(FW_HOOKS) qif_t *ifp; # else struct ifnet *ifp; @@ -275,10 +402,8 @@ u_int flags; ipfl.fl_nattag.ipt_num[0] = 0; ifp = fin->fin_ifp; - if (fin->fin_exthdr != NULL) - hlen = (char *)fin->fin_dp - (char *)fin->fin_ip; - else - hlen = fin->fin_hlen; + hlen = (char *)fin->fin_dp - (char *)fin->fin_ip; + /* * calculate header size. */ @@ -292,7 +417,7 @@ u_int flags; struct icmp *icmp; icmp = (struct icmp *)fin->fin_dp; - + /* * For ICMP, if the packet is an error packet, also * include the information about the packet which @@ -339,15 +464,14 @@ u_int flags; * Get the interface number and name to which this packet is * currently associated. */ -# if (SOLARIS || defined(__hpux)) && defined(_KERNEL) && \ - !defined(_INET_IP_STACK_H) +# if (SOLARIS || defined(__hpux)) && defined(_KERNEL) +# if !defined(FW_HOOKS) ipfl.fl_unit = (u_int)ifp->qf_ppa; +# endif COPYIFNAME(fin->fin_v, ifp, ipfl.fl_ifname); # else -# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ - (defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \ - (defined(__FreeBSD__) && (__FreeBSD_version >= 501113)) || \ - (SOLARIS && defined(_INET_IP_STACK_H)) +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ + OPENBSD_GE_REV(199603) || defined(linux) || FREEBSD_GE_REV(501113) COPYIFNAME(fin->fin_v, ifp, ipfl.fl_ifname); # else ipfl.fl_unit = (u_int)ifp->if_unit; @@ -357,13 +481,13 @@ u_int flags; if ((ipfl.fl_ifname[2] = ifp->if_name[2])) ipfl.fl_ifname[3] = ifp->if_name[3]; # else - COPYIFNAME(fin->fin_v, ifp, ipfl.fl_ifname); + (void) strncpy(ipfl.fl_ifname, IFNAME(ifp), sizeof(ipfl.fl_ifname)); ipfl.fl_ifname[sizeof(ipfl.fl_ifname) - 1] = '\0'; # endif # endif # endif /* __hpux || SOLARIS */ mlen = fin->fin_plen - hlen; - if (!ipl_logall) { + if (!softl->ipl_logall) { mlen = (flags & FR_LOGBODY) ? MIN(mlen, 128) : 0; } else if ((flags & FR_LOGBODY) == 0) { mlen = 0; @@ -385,8 +509,10 @@ u_int flags; bcopy(fin->fin_nattag, (void *)&ipfl.fl_nattag, sizeof(ipfl.fl_nattag)); ipfl.fl_flags = flags; + ipfl.fl_breason = (fin->fin_reason & 0xff); ipfl.fl_dir = fin->fin_out; ipfl.fl_lflags = fin->fin_flx; + ipfl.fl_family = fin->fin_family; ptrs[0] = (void *)&ipfl; sizes[0] = sizeof(ipfl); types[0] = 0; @@ -408,14 +534,15 @@ u_int flags; sizes[1] = hlen + mlen; types[1] = 1; # endif /* MENTAT */ - return ipllog(IPL_LOGIPF, fin, ptrs, sizes, types, 2); + return ipf_log_items(softc, IPL_LOGIPF, fin, ptrs, sizes, types, 2); } /* ------------------------------------------------------------------------ */ -/* Function: ipllog */ -/* Returns: int - 0 == success, -1 == failure */ -/* Parameters: dev(I) - device that owns this log record */ +/* Function: ipf_log_items */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ /* fin(I) - pointer to packet information */ /* items(I) - array of pointers to log data */ /* itemsz(I) - array of size of valid memory pointed to */ @@ -426,72 +553,51 @@ u_int flags; /* miscellaneous packet information, as well as packet data, for reading */ /* from the log device. */ /* ------------------------------------------------------------------------ */ -int ipllog(dev, fin, items, itemsz, types, cnt) -int dev; -fr_info_t *fin; -void **items; -size_t *itemsz; -int *types, cnt; +int +ipf_log_items(softc, unit, fin, items, itemsz, types, cnt) + ipf_main_softc_t *softc; + int unit; + fr_info_t *fin; + void **items; + size_t *itemsz; + int *types, cnt; { - u_char *buf, *ptr; + ipf_log_softc_t *softl = softc->ipf_log_soft; + caddr_t buf, ptr; iplog_t *ipl; size_t len; int i; SPL_INT(s); /* - * Check to see if this log record has a CRC which matches the last - * record logged. If it does, just up the count on the previous one - * rather than create a new one. - */ - if (ipl_suppress) { - MUTEX_ENTER(&ipl_mutex); - if ((fin != NULL) && (fin->fin_off == 0)) { - if ((ipll[dev] != NULL) && - bcmp((char *)fin, (char *)&iplcrc[dev], - FI_LCSIZE) == 0) { - ipll[dev]->ipl_count++; - MUTEX_EXIT(&ipl_mutex); - return 0; - } - bcopy((char *)fin, (char *)&iplcrc[dev], FI_LCSIZE); - } else - bzero((char *)&iplcrc[dev], FI_CSIZE); - MUTEX_EXIT(&ipl_mutex); - } - - /* * Get the total amount of data to be logged. */ for (i = 0, len = sizeof(iplog_t); i < cnt; i++) len += itemsz[i]; + SPL_NET(s); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + softl->ipl_counter[unit]++; /* * check that we have space to record this information and can * allocate that much. */ - KMALLOCS(buf, u_char *, len); - if (buf == NULL) - return -1; - SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); - if ((iplused[dev] + len) > ipl_logsize) { - MUTEX_EXIT(&ipl_mutex); - SPL_X(s); - KFREES(buf, len); + if ((softl->ipl_used[unit] + len) > softl->ipl_logsize) { + softl->ipl_logfail[unit]++; + MUTEX_EXIT(&softl->ipl_mutex[unit]); return -1; } - iplused[dev] += len; - MUTEX_EXIT(&ipl_mutex); - SPL_X(s); - /* - * advance the log pointer to the next empty record and deduct the - * amount of space we're going to use. - */ + KMALLOCS(buf, caddr_t, len); + if (buf == NULL) { + softl->ipl_logfail[unit]++; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + return -1; + } ipl = (iplog_t *)buf; - ipl->ipl_magic = ipl_magic[dev]; + ipl->ipl_magic = softl->ipl_magic[unit]; ipl->ipl_count = 1; + ipl->ipl_seqnum = softl->ipl_counter[unit]; ipl->ipl_next = NULL; ipl->ipl_dsize = len; #ifdef _KERNEL @@ -509,42 +615,71 @@ int *types, cnt; if (types[i] == 0) { bcopy(items[i], ptr, itemsz[i]); } else if (types[i] == 1) { - COPYDATA(items[i], 0, itemsz[i], (char *)ptr); + COPYDATA(items[i], 0, itemsz[i], ptr); } ptr += itemsz[i]; } - SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); - ipll[dev] = ipl; - *iplh[dev] = ipl; - iplh[dev] = &ipl->ipl_next; + /* + * Check to see if this log record has a CRC which matches the last + * record logged. If it does, just up the count on the previous one + * rather than create a new one. + */ + if (softl->ipl_suppress) { + if ((fin != NULL) && (fin->fin_off == 0)) { + if ((softl->ipll[unit] != NULL) && + (fin->fin_crc == softl->ipl_crc[unit].fin_crc) && + bcmp((char *)fin, (char *)&softl->ipl_crc[unit], + FI_LCSIZE) == 0) { + softl->ipll[unit]->ipl_count++; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + SPL_X(s); + KFREES(buf, len); + return 0; + } + bcopy((char *)fin, (char *)&softl->ipl_crc[unit], + FI_LCSIZE); + softl->ipl_crc[unit].fin_crc = fin->fin_crc; + } else + bzero((char *)&softl->ipl_crc[unit], FI_CSIZE); + } + + /* + * advance the log pointer to the next empty record and deduct the + * amount of space we're going to use. + */ + softl->ipl_logok[unit]++; + softl->ipll[unit] = ipl; + *softl->iplh[unit] = ipl; + softl->iplh[unit] = &ipl->ipl_next; + softl->ipl_used[unit] += len; /* * Now that the log record has been completed and added to the queue, * wake up any listeners who may want to read it. */ # if SOLARIS && defined(_KERNEL) - cv_signal(&iplwait); - MUTEX_EXIT(&ipl_mutex); - pollwakeup(&iplpollhead[dev], POLLRDNORM); + cv_signal(&softl->ipl_wait[unit]); + MUTEX_EXIT(&softl->ipl_mutex[unit]); + pollwakeup(&softc->ipf_poll_head[unit], POLLRDNORM); # else - MUTEX_EXIT(&ipl_mutex); - WAKEUP(iplh, dev); - POLLWAKEUP(dev); + MUTEX_EXIT(&softl->ipl_mutex[unit]); + WAKEUP(softl->iplh, unit); + POLLWAKEUP(unit); # endif SPL_X(s); # ifdef IPL_SELECT - iplog_input_ready(dev); + iplog_input_ready(unit); # endif return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ipflog_read */ -/* Returns: int - 0 == success, else error value. */ -/* Parameters: unit(I) - device we are reading from */ -/* uio(O) - pointer to information about where to store data */ +/* Function: ipf_log_read */ +/* Returns: int - 0 == success, else error value. */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ +/* uio(O) - pointer to information about where to store data */ /* */ /* Called to handle a read on an IPFilter device. Returns only complete */ /* log messages - will not partially copy a log record out to userland. */ @@ -552,38 +687,58 @@ int *types, cnt; /* NOTE: This function will block and wait for a signal to return data if */ /* there is none present. Asynchronous I/O is not implemented. */ /* ------------------------------------------------------------------------ */ -int ipflog_read(unit, uio) -minor_t unit; -struct uio *uio; +int +ipf_log_read(softc, unit, uio) + ipf_main_softc_t *softc; + minor_t unit; + struct uio *uio; { + ipf_log_softc_t *softl = softc->ipf_log_soft; size_t dlen, copied; int error = 0; iplog_t *ipl; SPL_INT(s); + if (softl->ipl_log_init == 0) { + IPFERROR(40007); + return 0; + } + /* * Sanity checks. Make sure the minor # is valid and we're copying * a valid chunk of data. */ - if (IPL_LOGMAX < unit) + if (IPL_LOGMAX < unit) { + IPFERROR(40001); return ENXIO; + } if (uio->uio_resid == 0) return 0; - if ((uio->uio_resid < sizeof(iplog_t)) || - (uio->uio_resid > ipl_logsize)) + + if (uio->uio_resid < sizeof(iplog_t)) { + IPFERROR(40002); + return EINVAL; + } + if (uio->uio_resid > softl->ipl_logsize) { + IPFERROR(40005); return EINVAL; + } /* * Lock the log so we can snapshot the variables. Wait for a signal * if the log is empty. */ SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + softl->ipl_readers[unit]++; - while (iplt[unit] == NULL) { + while (softl->ipl_log_init == 1 && softl->iplt[unit] == NULL) { # if SOLARIS && defined(_KERNEL) - if (!cv_wait_sig(&iplwait, &ipl_mutex.ipf_lk)) { - MUTEX_EXIT(&ipl_mutex); + if (!cv_wait_sig(&softl->ipl_wait[unit], + &softl->ipl_mutex[unit].ipf_lk)) { + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + IPFERROR(40003); return EINTR; } # else @@ -593,114 +748,206 @@ struct uio *uio; # ifdef IPL_SELECT if (uio->uio_fpflags & (FNBLOCK|FNDELAY)) { /* this is no blocking system call */ - MUTEX_EXIT(&ipl_mutex); + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); return 0; } # endif - MUTEX_EXIT(&ipl_mutex); - l = get_sleep_lock(&iplh[unit]); - error = sleep(&iplh[unit], PZERO+1); + MUTEX_EXIT(&softl->ipl_mutex[unit]); + l = get_sleep_lock(&softl->iplh[unit]); + error = sleep(&softl->iplh[unit], PZERO+1); spinunlock(l); # else # if defined(__osf__) && defined(_KERNEL) - error = mpsleep(&iplh[unit], PSUSP|PCATCH, "iplread", 0, - &ipl_mutex, MS_LOCK_SIMPLE); + error = mpsleep(&softl->iplh[unit], PSUSP|PCATCH, "ipfread", 0, + &softl->ipl_mutex, MS_LOCK_SIMPLE); # else - MUTEX_EXIT(&ipl_mutex); + MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); - error = SLEEP(unit + iplh, "ipl sleep"); + error = SLEEP(unit + softl->iplh, "ipl sleep"); # endif /* __osf__ */ # endif /* __hpux */ - if (error) - return error; SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + if (error) { + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + IPFERROR(40004); + return error; + } # endif /* SOLARIS */ } + if (softl->ipl_log_init != 1) { + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); + IPFERROR(40008); + return EIO; + } -# if (BSD >= 199101) || defined(__FreeBSD__) || defined(__osf__) +# if (defined(BSD) && (BSD >= 199101)) || defined(__FreeBSD__) || \ + defined(__osf__) uio->uio_rw = UIO_READ; # endif - for (copied = 0; (ipl = iplt[unit]) != NULL; copied += dlen) { + for (copied = 0; (ipl = softl->iplt[unit]) != NULL; copied += dlen) { dlen = ipl->ipl_dsize; if (dlen > uio->uio_resid) break; /* * Don't hold the mutex over the uiomove call. */ - iplt[unit] = ipl->ipl_next; - iplused[unit] -= dlen; - MUTEX_EXIT(&ipl_mutex); + softl->iplt[unit] = ipl->ipl_next; + softl->ipl_used[unit] -= dlen; + MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); error = UIOMOVE(ipl, dlen, UIO_READ, uio); if (error) { SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); - ipl->ipl_next = iplt[unit]; - iplt[unit] = ipl; - iplused[unit] += dlen; + MUTEX_ENTER(&softl->ipl_mutex[unit]); + IPFERROR(40006); + ipl->ipl_next = softl->iplt[unit]; + softl->iplt[unit] = ipl; + softl->ipl_used[unit] += dlen; break; } - MUTEX_ENTER(&ipl_mutex); - KFREES(ipl, dlen); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + KFREES((caddr_t)ipl, dlen); SPL_NET(s); } - if (!iplt[unit]) { - iplused[unit] = 0; - iplh[unit] = &iplt[unit]; - ipll[unit] = NULL; + if (!softl->iplt[unit]) { + softl->ipl_used[unit] = 0; + softl->iplh[unit] = &softl->iplt[unit]; + softl->ipll[unit] = NULL; } - MUTEX_EXIT(&ipl_mutex); + softl->ipl_readers[unit]--; + MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); return error; } /* ------------------------------------------------------------------------ */ -/* Function: ipflog_clear */ -/* Returns: int - number of log bytes cleared. */ -/* Parameters: unit(I) - device we are reading from */ +/* Function: ipf_log_clear */ +/* Returns: int - number of log bytes cleared. */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ /* */ /* Deletes all queued up log records for a given output device. */ /* ------------------------------------------------------------------------ */ -int ipflog_clear(unit) -minor_t unit; +int +ipf_log_clear(softc, unit) + ipf_main_softc_t *softc; + minor_t unit; { + ipf_log_softc_t *softl = softc->ipf_log_soft; iplog_t *ipl; int used; SPL_INT(s); SPL_NET(s); - MUTEX_ENTER(&ipl_mutex); - while ((ipl = iplt[unit]) != NULL) { - iplt[unit] = ipl->ipl_next; - KFREES(ipl, ipl->ipl_dsize); + MUTEX_ENTER(&softl->ipl_mutex[unit]); + while ((ipl = softl->iplt[unit]) != NULL) { + softl->iplt[unit] = ipl->ipl_next; + KFREES((caddr_t)ipl, ipl->ipl_dsize); } - iplh[unit] = &iplt[unit]; - ipll[unit] = NULL; - used = iplused[unit]; - iplused[unit] = 0; - bzero((char *)&iplcrc[unit], FI_CSIZE); - MUTEX_EXIT(&ipl_mutex); + softl->iplh[unit] = &softl->iplt[unit]; + softl->ipll[unit] = NULL; + used = softl->ipl_used[unit]; + softl->ipl_used[unit] = 0; + bzero((char *)&softl->ipl_crc[unit], FI_CSIZE); + MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); return used; } /* ------------------------------------------------------------------------ */ -/* Function: ipflog_canread */ -/* Returns: int - 0 == no data to read, 1 = data present */ -/* Parameters: unit(I) - device we are reading from */ +/* Function: ipf_log_canread */ +/* Returns: int - 0 == no data to read, 1 = data present */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ /* */ /* Returns an indication of whether or not there is data present in the */ /* current buffer for the selected ipf device. */ /* ------------------------------------------------------------------------ */ -int ipflog_canread(unit) -int unit; +int +ipf_log_canread(softc, unit) + ipf_main_softc_t *softc; + int unit; { - return iplt[unit] != NULL; + ipf_log_softc_t *softl = softc->ipf_log_soft; + + return softl->iplt[unit] != NULL; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_canread */ +/* Returns: int - 0 == no data to read, 1 = data present */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ +/* */ +/* Returns how many bytes are currently held in log buffers for the */ +/* selected ipf device. */ +/* ------------------------------------------------------------------------ */ +int +ipf_log_bytesused(softc, unit) + ipf_main_softc_t *softc; + int unit; +{ + ipf_log_softc_t *softl = softc->ipf_log_soft; + + if (softl == NULL) + return 0; + + return softl->ipl_used[unit]; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_failures */ +/* Returns: U_QUAD_T - number of log failures */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ +/* */ +/* Returns how many times we've tried to log a packet but failed to do so */ +/* for the selected ipf device. */ +/* ------------------------------------------------------------------------ */ +u_long +ipf_log_failures(softc, unit) + ipf_main_softc_t *softc; + int unit; +{ + ipf_log_softc_t *softl = softc->ipf_log_soft; + + if (softl == NULL) + return 0; + + return softl->ipl_logfail[unit]; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_log_logok */ +/* Returns: U_QUAD_T - number of packets logged */ +/* Parameters: softc(I) - pointer to main soft context */ +/* unit(I) - device we are reading from */ +/* */ +/* Returns how many times we've successfully logged a packet for the */ +/* selected ipf device. */ +/* ------------------------------------------------------------------------ */ +u_long +ipf_log_logok(softc, unit) + ipf_main_softc_t *softc; + int unit; +{ + ipf_log_softc_t *softl = softc->ipf_log_soft; + + if (softl == NULL) + return 0; + + return softl->ipl_logok[unit]; } #endif /* IPFILTER_LOG */ diff --git a/sys/contrib/ipfilter/netinet/ip_lookup.c b/sys/contrib/ipfilter/netinet/ip_lookup.c index e33a6fe..45999e0 100644 --- a/sys/contrib/ipfilter/netinet/ip_lookup.c +++ b/sys/contrib/ipfilter/netinet/ip_lookup.c @@ -1,5 +1,6 @@ +/* $FreeBSD$ */ /* - * Copyright (C) 2002-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -24,7 +25,9 @@ # include <sys/ioctl.h> #endif #if !defined(_KERNEL) +# include <stdio.h> # include <string.h> +# include <stdlib.h> # define _KERNEL # ifdef __OpenBSD__ struct file; @@ -33,106 +36,223 @@ struct file; # undef _KERNEL #endif #include <sys/socket.h> -#if (defined(__osf__) || defined(AIX) || defined(__hpux) || defined(__sgi)) && defined(_KERNEL) -# include "radix_ipf_local.h" -# define _RADIX_H_ -#endif #include <net/if.h> #if defined(__FreeBSD__) -# include <sys/cdefs.h> -# include <sys/proc.h> +# include <sys/cdefs.h> +# include <sys/proc.h> #endif #if defined(_KERNEL) # include <sys/systm.h> # if !defined(__SVR4) && !defined(__svr4__) # include <sys/mbuf.h> # endif +#else +# include "ipf.h" #endif #include <netinet/in.h> #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_lookup.h" +#include "netinet/ip_dstlist.h" /* END OF INCLUDES */ #if !defined(lint) -static const char rcsid[] = "@(#)$Id: ip_lookup.c,v 2.35.2.19 2007/10/11 09:05:51 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -#ifdef IPFILTER_LOOKUP -int ip_lookup_inited = 0; +/* + * 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 *)); + +#define MAX_BACKENDS 3 +static ipf_lookup_t *backends[MAX_BACKENDS] = { + &ipf_pool_backend, + &ipf_htable_backend, + &ipf_dstlist_backend +}; + -static int iplookup_addnode __P((caddr_t)); -static int iplookup_delnode __P((caddr_t data)); -static int iplookup_addtable __P((caddr_t)); -static int iplookup_deltable __P((caddr_t)); -static int iplookup_stats __P((caddr_t)); -static int iplookup_flush __P((caddr_t)); -static int iplookup_iterate __P((void *, int, void *)); -static int iplookup_deltok __P((void *, int, void *)); +typedef struct ipf_lookup_softc_s { + void *ipf_back[MAX_BACKENDS]; +} ipf_lookup_softc_t; /* ------------------------------------------------------------------------ */ -/* Function: iplookup_init */ -/* Returns: int - 0 = success, else error */ -/* Parameters: Nil */ +/* 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. */ /* ------------------------------------------------------------------------ */ -int ip_lookup_init() +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; +} + - if (ip_pool_init() == -1) - return -1; +/* ------------------------------------------------------------------------ */ +/* 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; - RWLOCK_INIT(&ip_poolrw, "ip pool rwlock"); + for (i = 0; i < MAX_BACKENDS; i++) { + err = (*backends[i]->ipfl_init)(softc, softl->ipf_back[i]); + if (err != 0) + break; + } - ip_lookup_inited = 1; + 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: iplookup_unload */ +/* 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: Nil */ +/* 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 */ -/* ip_lookup_init() can be called again, safely. */ +/* ipf_lookup_init() can be called again, safely. */ /* ------------------------------------------------------------------------ */ -void ip_lookup_unload() +void +ipf_lookup_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - ip_pool_fini(); - fr_htable_unload(); + ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; + int i; - if (ip_lookup_inited == 1) { - RW_DESTROY(&ip_poolrw); - ip_lookup_inited = 0; + 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: iplookup_ioctl */ +/* Function: ipf_lookup_ioctl */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(IO) - pointer to ioctl data to be copied to/from user */ +/* 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 ip_lookup_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +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); @@ -145,52 +265,53 @@ void *ctx; { case SIOCLOOKUPADDNODE : case SIOCLOOKUPADDNODEW : - WRITE_ENTER(&ip_poolrw); - err = iplookup_addnode(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_addnode(softc, data, uid); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPDELNODE : case SIOCLOOKUPDELNODEW : - WRITE_ENTER(&ip_poolrw); - err = iplookup_delnode(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_delnode(softc, data, uid); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPADDTABLE : - WRITE_ENTER(&ip_poolrw); - err = iplookup_addtable(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_addtable(softc, data); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPDELTABLE : - WRITE_ENTER(&ip_poolrw); - err = iplookup_deltable(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_deltable(softc, data); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPSTAT : case SIOCLOOKUPSTATW : - WRITE_ENTER(&ip_poolrw); - err = iplookup_stats(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_stats(softc, data); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPFLUSH : - WRITE_ENTER(&ip_poolrw); - err = iplookup_flush(data); - RWLOCK_EXIT(&ip_poolrw); + WRITE_ENTER(&softc->ipf_poolrw); + err = ipf_lookup_flush(softc, data); + RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPITER : - err = iplookup_iterate(data, uid, ctx); + err = ipf_lookup_iterate(softc, data, uid, ctx); break; case SIOCIPFDELTOK : - err = iplookup_deltok(data, uid, ctx); + err = ipf_lookup_deltok(softc, data, uid, ctx); break; default : + IPFERROR(50001); err = EINVAL; break; } @@ -200,192 +321,155 @@ void *ctx; /* ------------------------------------------------------------------------ */ -/* Function: iplookup_addnode */ +/* Function: ipf_lookup_addnode */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 iplookup_addnode(data) -caddr_t data; +static int +ipf_lookup_addnode(softc, data, uid) + ipf_main_softc_t *softc; + caddr_t data; + int uid; { - ip_pool_node_t node, *m; + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; - iphtable_t *iph; - iphtent_t hte; - ip_pool_t *p; + ipf_lookup_t **l; int err; + int i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50002); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + 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'; - switch (op.iplo_type) - { - case IPLT_POOL : - if (op.iplo_size != sizeof(node)) - return EINVAL; - - err = COPYIN(op.iplo_struct, &node, sizeof(node)); - if (err != 0) - return EFAULT; - - p = ip_pool_find(op.iplo_unit, op.iplo_name); - if (p == NULL) - return ESRCH; - - /* - * 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 = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask); - if (m) - return EEXIST; - err = ip_pool_insert(p, &node.ipn_addr.adf_addr, - &node.ipn_mask.adf_addr, node.ipn_info); - break; - - case IPLT_HASH : - if (op.iplo_size != sizeof(hte)) - return EINVAL; - - err = COPYIN(op.iplo_struct, &hte, sizeof(hte)); - if (err != 0) - return EFAULT; - - iph = fr_findhtable(op.iplo_unit, op.iplo_name); - if (iph == NULL) - return ESRCH; - err = fr_addhtent(iph, &hte); - break; + 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; + } + } - default : + if (i == MAX_BACKENDS) { + IPFERROR(50012); err = EINVAL; - break; } + return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_delnode */ +/* Function: ipf_lookup_delnode */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 iplookup_delnode(data) -caddr_t data; +static int +ipf_lookup_delnode(softc, data, uid) + ipf_main_softc_t *softc; + caddr_t data; + int uid; { - ip_pool_node_t node, *m; + ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; - iphtable_t *iph; - iphtent_t hte; - ip_pool_t *p; + ipf_lookup_t **l; int err; + int i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50042); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + 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'; - switch (op.iplo_type) - { - case IPLT_POOL : - if (op.iplo_size != sizeof(node)) - return EINVAL; - - err = COPYIN(op.iplo_struct, &node, sizeof(node)); - if (err != 0) - return EFAULT; - - p = ip_pool_find(op.iplo_unit, op.iplo_name); - if (!p) - return ESRCH; - - m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask); - if (m == NULL) - return ENOENT; - err = ip_pool_remove(p, m); - break; - - case IPLT_HASH : - if (op.iplo_size != sizeof(hte)) - return EINVAL; - - err = COPYIN(op.iplo_struct, &hte, sizeof(hte)); - if (err != 0) - return EFAULT; - - iph = fr_findhtable(op.iplo_unit, op.iplo_name); - if (iph == NULL) - return ESRCH; - err = fr_delhtent(iph, &hte); - break; + 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; + } + } - default : + if (i == MAX_BACKENDS) { + IPFERROR(50021); err = EINVAL; - break; } return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_addtable */ +/* Function: ipf_lookup_addtable */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 iplookup_addtable(data) -caddr_t data; +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; - int err; + ipf_lookup_t **l; + int err, i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50022); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + 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'; - switch (op.iplo_type) - { - case IPLT_POOL : - if (ip_pool_find(op.iplo_unit, op.iplo_name) != NULL) - err = EEXIST; - else - err = ip_pool_create(&op); - break; - - case IPLT_HASH : - if (fr_findhtable(op.iplo_unit, op.iplo_name) != NULL) - err = EEXIST; - else - err = fr_newhtable(&op); - break; + 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; + } + } - default : + if (i == MAX_BACKENDS) { + IPFERROR(50026); err = EINVAL; - break; } /* @@ -394,8 +478,10 @@ caddr_t data; */ if ((err == 0) && ((op.iplo_arg & LOOKUP_ANON) != 0)) { err = BCOPYOUT(&op, data, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50027); err = EFAULT; + } } return err; @@ -403,260 +489,317 @@ caddr_t data; /* ------------------------------------------------------------------------ */ -/* Function: iplookup_deltable */ +/* Function: ipf_lookup_deltable */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 iplookup_deltable(data) -caddr_t data; +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; - int err; + ipf_lookup_t **l; + int err, i; err = BCOPYIN(data, &op, sizeof(op)); - if (err != 0) + if (err != 0) { + IPFERROR(50028); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + 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'; - /* - * create a new pool - fail if one already exists with - * the same # - */ - switch (op.iplo_type) - { - case IPLT_POOL : - err = ip_pool_destroy(op.iplo_unit, op.iplo_name); - break; - - case IPLT_HASH : - err = fr_removehtable(op.iplo_unit, op.iplo_name); - break; + 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; + } + } - default : + if (i == MAX_BACKENDS) { + IPFERROR(50030); err = EINVAL; - break; } return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_stats */ +/* Function: ipf_lookup_stats */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 iplookup_stats(data) -caddr_t data; +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) + if (err != 0) { + IPFERROR(50031); return EFAULT; + } - if (op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) + if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && + (op.iplo_unit != IPLT_ALL)) { + IPFERROR(50032); return EINVAL; + } - switch (op.iplo_type) - { - case IPLT_POOL : - err = ip_pool_statistics(&op); - break; - - case IPLT_HASH : - err = fr_gethtablestat(&op); - break; + 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; + } + } - default : + if (i == MAX_BACKENDS) { + IPFERROR(50033); err = EINVAL; - break; } + return err; } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_flush */ +/* Function: ipf_lookup_flush */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 iplookup_flush(data) -caddr_t data; +static int +ipf_lookup_flush(softc, data) + ipf_main_softc_t *softc; + caddr_t data; { - int err, unit, num, type; + 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) + if (err != 0) { + IPFERROR(50034); return EFAULT; + } unit = flush.iplf_unit; - if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL)) + 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; - if (type == IPLT_POOL || type == IPLT_ALL) { - err = 0; - num = ip_pool_flush(&flush); - } - - if (type == IPLT_HASH || type == IPLT_ALL) { - err = 0; - num += fr_flushhtable(&flush); + 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) + if (err != 0) { + IPFERROR(50037); err = EFAULT; + } } return err; } /* ------------------------------------------------------------------------ */ -/* Function: ip_lookup_delref */ +/* Function: ipf_lookup_delref */ /* Returns: void */ -/* Parameters: type(I) - table type to operate on */ +/* 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 ip_lookup_deref(type, ptr) -int type; -void *ptr; +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; - WRITE_ENTER(&ip_poolrw); - switch (type) - { - case IPLT_POOL : - ip_pool_deref(ptr); - break; - - case IPLT_HASH : - fr_derefhtable(ptr); - break; + 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; + } } - RWLOCK_EXIT(&ip_poolrw); } /* ------------------------------------------------------------------------ */ -/* Function: iplookup_iterate */ +/* Function: ipf_lookup_iterate */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 iplookup_iterate(data, uid, ctx) -void *data; -int uid; -void *ctx; +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; + int err, i; SPL_INT(s); - err = fr_inobj(data, &iter, IPFOBJ_LOOKUPITER); + err = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_LOOKUPITER); if (err != 0) return err; - if (iter.ili_unit > IPL_LOGMAX) + if (iter.ili_unit < IPL_LOGALL && iter.ili_unit > IPL_LOGMAX) { + IPFERROR(50038); return EINVAL; + } - if (iter.ili_ival != IPFGENITER_LOOKUP) + if (iter.ili_ival != IPFGENITER_LOOKUP) { + IPFERROR(50039); return EINVAL; + } SPL_SCHED(s); - token = ipf_findtoken(iter.ili_key, uid, ctx); + token = ipf_token_find(softc, iter.ili_key, uid, ctx); if (token == NULL) { - RWLOCK_EXIT(&ipf_tokens); SPL_X(s); + IPFERROR(50040); return ESRCH; } - switch (iter.ili_type) - { - case IPLT_POOL : - err = ip_pool_getnext(token, &iter); - break; - case IPLT_HASH : - err = fr_htable_getnext(token, &iter); - break; - default : - err = EINVAL; - break; + 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; + } } - RWLOCK_EXIT(&ipf_tokens); 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: iplookup_iterderef */ -/* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 ip_lookup_iterderef(type, data) -u_32_t type; -void *data; +void +ipf_lookup_iterderef(softc, type, data) + ipf_main_softc_t *softc; + u_32_t type; + void *data; { - iplookupiterkey_t key; + 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 (key.ilik_unstr.ilik_ival != IPFGENITER_LOOKUP) + if (lkey->ilik_ival != IPFGENITER_LOOKUP) return; - switch (key.ilik_unstr.ilik_type) - { - case IPLT_HASH : - fr_htable_iterderef((u_int)key.ilik_unstr.ilik_otype, - (int)key.ilik_unstr.ilik_unit, data); - break; - case IPLT_POOL : - ip_pool_iterderef((u_int)key.ilik_unstr.ilik_otype, - (int)key.ilik_unstr.ilik_unit, data); - break; + 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: iplookup_deltok */ +/* Function: ipf_lookup_deltok */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to data from ioctl call */ +/* 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 */ /* */ @@ -664,32 +807,198 @@ void *data; /* "key" is a combination of the table type, iterator type and the unit for */ /* which the token was being used. */ /* ------------------------------------------------------------------------ */ -static int iplookup_deltok(data, uid, ctx) -void *data; -int uid; -void *ctx; +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)); + error = BCOPYIN(data, &key, sizeof(key)); if (error == 0) - error = ipf_deltoken(key, uid, ctx); + error = ipf_token_del(softc, key, uid, ctx); SPL_X(s); return error; } -#else /* IPFILTER_LOOKUP */ +/* ------------------------------------------------------------------------ */ +/* 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*/ -int ip_lookup_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +void +ipf_lookup_sync(softc, ifp) + ipf_main_softc_t *softc; + void *ifp; { - return EIO; + 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); } -#endif /* IPFILTER_LOOKUP */ + + +#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 3886df1..181e1bc 100644 --- a/sys/contrib/ipfilter/netinet/ip_lookup.h +++ b/sys/contrib/ipfilter/netinet/ip_lookup.h @@ -1,4 +1,10 @@ - +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * $Id$ + */ #ifndef __IP_LOOKUP_H__ #define __IP_LOOKUP_H__ @@ -24,6 +30,9 @@ # define SIOCLOOKUPDELNODEW _IOW(r, 68, struct iplookupop) #endif +#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* */ @@ -40,7 +49,7 @@ typedef struct iplookupflush { int iplf_type; /* IPLT_* */ int iplf_unit; /* IPL_LOG* */ u_int iplf_arg; - size_t iplf_count; + u_int iplf_count; char iplf_name[FR_GROUPLEN]; } iplookupflush_t; @@ -55,16 +64,18 @@ typedef struct iplookuplink { #define IPLT_NONE 0 #define IPLT_POOL 1 #define IPLT_HASH 2 +#define IPLT_DSTLIST 3 + #define IPLT_ANON 0x80000000 typedef union { struct iplookupiterkey { - char ilik_ival; + u_char ilik_ival; u_char ilik_type; /* IPLT_* */ u_char ilik_otype; - u_char ilik_unit; /* IPL_LOG* */ + signed char ilik_unit; /* IPL_LOG* */ } ilik_unstr; u_32_t ilik_key; } iplookupiterkey_t; @@ -86,10 +97,56 @@ typedef struct ipflookupiter { #define IPFLOOKUPITER_NODE 1 -extern int ip_lookup_init __P((void)); -extern int ip_lookup_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern void ip_lookup_unload __P((void)); -extern void ip_lookup_deref __P((int, void *)); -extern void ip_lookup_iterderef __P((u_32_t, void *)); - +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 *)); +} 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 *)); +#ifndef _KERNEL +extern void ipf_lookup_dump __P((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 f790c7d..d664708 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.c +++ b/sys/contrib/ipfilter/netinet/ip_nat.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -16,30 +16,23 @@ #include <sys/param.h> #include <sys/time.h> #include <sys/file.h> -#if defined(_KERNEL) && defined(__NetBSD_Version__) && \ - (__NetBSD_Version__ >= 399002000) +#if defined(_KERNEL) && \ + (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000)) # include <sys/kauth.h> #endif -#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ - defined(_KERNEL) -#if defined(__NetBSD_Version__) && (__NetBSD_Version__ < 399001400) -# include "opt_ipfilter_log.h" -# else -# include "opt_ipfilter.h" -# endif -#endif #if !defined(_KERNEL) # include <stdio.h> # include <string.h> # include <stdlib.h> -# define _KERNEL -# ifdef __OpenBSD__ +# define KERNEL +# ifdef _OpenBSD__ struct file; # endif # include <sys/uio.h> -# undef _KERNEL +# undef KERNEL #endif -#if defined(_KERNEL) && (__FreeBSD_version >= 220000) +#if defined(_KERNEL) && \ + defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) # include <sys/filio.h> # include <sys/fcntl.h> #else @@ -61,7 +54,7 @@ struct file; #if defined(__SVR4) || defined(__svr4__) # include <sys/filio.h> # include <sys/byteorder.h> -# ifdef _KERNEL +# ifdef KERNEL # include <sys/dditypes.h> # endif # include <sys/stream.h> @@ -73,14 +66,10 @@ struct file; #include <net/if.h> #if __FreeBSD_version >= 300000 # include <net/if_var.h> -# if defined(_KERNEL) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #endif #ifdef sun # include <net/af.h> #endif -#include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -99,17 +88,23 @@ extern struct ifnet vpnif; #include <netinet/ip_icmp.h> #include "netinet/ip_compat.h" #include <netinet/tcpip.h> +#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" -#ifdef IPFILTER_SYNC +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" -#endif -#if (__FreeBSD_version >= 300000) +#if FREEBSD_GE_REV(300000) # include <sys/malloc.h> #endif +#ifdef HAS_SYS_MD5_H +# include <sys/md5.h> +#else +# include "md5.h" +#endif /* END OF INCLUDES */ #undef SOCKADDR_IN @@ -122,195 +117,396 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #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) + +frentry_t ipfnatblock; + +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 fr_checknatout() for x */ +/* Packet going | out, processsed by ipf_nat_checkout() for x */ /* ------------> | ------------> */ /* src=10.1.1.1 | src=192.1.1.1 */ /* | */ -/* | in, processed by fr_checknatin() for x */ +/* | in, processed by ipf_nat_checkin() for x */ /* <------------ | <------------ */ /* dst=10.1.1.1 | dst=192.1.1.1 */ /* -------------------- -+- ------------------------------------- */ -/* fr_checknatout() - changes ip_src and if required, sport */ +/* ipf_nat_checkout() - changes ip_src and if required, sport */ /* - creates a new mapping, if required. */ -/* fr_checknatin() - changes ip_dst and if required, dport */ +/* 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". */ /* ======================================================================== */ -nat_t **nat_table[2] = { NULL, NULL }, - *nat_instances = NULL; -ipnat_t *nat_list = NULL; -u_int ipf_nattable_max = NAT_TABLE_MAX; -u_int ipf_nattable_sz = NAT_TABLE_SZ; -u_int ipf_natrules_sz = NAT_SIZE; -u_int ipf_rdrrules_sz = RDR_SIZE; -u_int ipf_hostmap_sz = HOSTMAP_SIZE; -u_int fr_nat_maxbucket = 0, - fr_nat_maxbucket_reset = 1; -u_32_t nat_masks = 0; -u_32_t rdr_masks = 0; -u_long nat_last_force_flush = 0; -ipnat_t **nat_rules = NULL; -ipnat_t **rdr_rules = NULL; -hostmap_t **ipf_hm_maptable = NULL; -hostmap_t *ipf_hm_maplist = NULL; -ipftq_t nat_tqb[IPF_TCP_NSTATES]; -ipftq_t nat_udptq; -ipftq_t nat_icmptq; -ipftq_t nat_iptq; -ipftq_t *nat_utqe = NULL; -int fr_nat_doflush = 0; +#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 *, + 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 *)); + +/* ------------------------------------------------------------------------ */ +/* 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() +{ + bzero((char *)&ipfnatblock, sizeof(ipfnatblock)); + ipfnatblock.fr_flags = FR_BLOCK|FR_QUICK; + ipfnatblock.fr_ref = 1; + + 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 -int nat_logging = 1; + softn->ipf_nat_logging = 1; #else -int nat_logging = 0; + softn->ipf_nat_logging = 0; #endif -u_long fr_defnatage = DEF_NAT_AGE, - fr_defnatipage = 120, /* 60 seconds */ - fr_defnaticmpage = 6; /* 3 seconds */ -natstat_t nat_stats; -int fr_nat_lock = 0; -int fr_nat_init = 0; -#if SOLARIS && !defined(_INET_IP_STACK_H) -extern int pfil_delayed_copy; -#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; -static int nat_flush_entry __P((void *)); -static int nat_flushtable __P((void)); -static int nat_clearlist __P((void)); -static void nat_addnat __P((struct ipnat *)); -static void nat_addrdr __P((struct ipnat *)); -static void nat_delrdr __P((struct ipnat *)); -static void nat_delnat __P((struct ipnat *)); -static int fr_natgetent __P((caddr_t, int)); -static int fr_natgetsz __P((caddr_t, int)); -static int fr_natputent __P((caddr_t, int)); -static int nat_extraflush __P((int)); -static int nat_gettable __P((char *)); -static void nat_tabmove __P((nat_t *)); -static int nat_match __P((fr_info_t *, ipnat_t *)); -static INLINE int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); -static INLINE int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); -static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, - struct in_addr, struct in_addr, u_32_t)); -static int nat_icmpquerytype4 __P((int)); -static int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int)); -static void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int)); -static int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *, - tcphdr_t *, nat_t **, int)); -static int nat_resolverule __P((ipnat_t *)); -static nat_t *fr_natclone __P((fr_info_t *, nat_t *)); -static void nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, u_short *)); -static int nat_wildok __P((nat_t *, int, int, int, int)); -static int nat_getnext __P((ipftoken_t *, ipfgeniter_t *)); -static int nat_iterator __P((ipftoken_t *, ipfgeniter_t *)); - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_natinit */ + 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: Nil */ +/* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise all of the NAT locks, tables and other structures. */ /* ------------------------------------------------------------------------ */ -int fr_natinit() +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(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); - if (nat_table[0] != NULL) - bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *)); - else + 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(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); - if (nat_table[1] != NULL) - bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *)); - else + 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(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz); - if (nat_rules != NULL) - bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *)); - else + 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(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz); - if (rdr_rules != NULL) - bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *)); - else + 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(ipf_hm_maptable, hostmap_t **, \ - sizeof(hostmap_t *) * ipf_hostmap_sz); - if (ipf_hm_maptable != NULL) - bzero((char *)ipf_hm_maptable, - sizeof(hostmap_t *) * ipf_hostmap_sz); - else + 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; - ipf_hm_maplist = NULL; + } + softn->ipf_hm_maplist = NULL; - KMALLOCS(nat_stats.ns_bucketlen[0], u_long *, - ipf_nattable_sz * sizeof(u_long)); - if (nat_stats.ns_bucketlen[0] == 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 *)nat_stats.ns_bucketlen[0], - ipf_nattable_sz * sizeof(u_long)); + } + bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, + softn->ipf_nat_table_sz * sizeof(u_int)); - KMALLOCS(nat_stats.ns_bucketlen[1], u_long *, - ipf_nattable_sz * sizeof(u_long)); - if (nat_stats.ns_bucketlen[1] == NULL) + 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 *)nat_stats.ns_bucketlen[1], - ipf_nattable_sz * sizeof(u_long)); + bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, + softn->ipf_nat_table_sz * sizeof(u_int)); - if (fr_nat_maxbucket == 0) { - for (i = ipf_nattable_sz; i > 0; i >>= 1) - fr_nat_maxbucket++; - fr_nat_maxbucket *= 2; + 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; } - fr_sttab_init(nat_tqb); + 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. */ - nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = fr_tcplastack; - nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &nat_udptq; - nat_udptq.ifq_ttl = fr_defnatage; - nat_udptq.ifq_ref = 1; - nat_udptq.ifq_head = NULL; - nat_udptq.ifq_tail = &nat_udptq.ifq_head; - MUTEX_INIT(&nat_udptq.ifq_lock, "nat ipftq udp tab"); - nat_udptq.ifq_next = &nat_icmptq; - nat_icmptq.ifq_ttl = fr_defnaticmpage; - nat_icmptq.ifq_ref = 1; - nat_icmptq.ifq_head = NULL; - nat_icmptq.ifq_tail = &nat_icmptq.ifq_head; - MUTEX_INIT(&nat_icmptq.ifq_lock, "nat icmp ipftq tab"); - nat_icmptq.ifq_next = &nat_iptq; - nat_iptq.ifq_ttl = fr_defnatipage; - nat_iptq.ifq_ref = 1; - nat_iptq.ifq_head = NULL; - nat_iptq.ifq_tail = &nat_iptq.ifq_head; - MUTEX_INIT(&nat_iptq.ifq_lock, "nat ip ipftq tab"); - nat_iptq.ifq_next = NULL; - - for (i = 0; i < IPF_TCP_NSTATES; i++) { - if (nat_tqb[i].ifq_ttl < fr_defnaticmpage) - nat_tqb[i].ifq_ttl = fr_defnaticmpage; + 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 (nat_tqb[i].ifq_ttl > fr_defnatage) - nat_tqb[i].ifq_ttl = fr_defnatage; + else if (tq->ifq_ttl > softn->ipf_nat_defage) + tq->ifq_ttl = softn->ipf_nat_defage; #endif } @@ -319,21 +515,124 @@ int fr_natinit() * this too and packet storms can occur if this is removed * too quickly. */ - nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl; + softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; - RWLOCK_INIT(&ipf_nat, "ipf IP NAT rwlock"); - RWLOCK_INIT(&ipf_natfrag, "ipf IP NAT-Frag rwlock"); - MUTEX_INIT(&ipf_nat_new, "ipf nat new mutex"); - MUTEX_INIT(&ipf_natio, "ipf nat io mutex"); + MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex"); + MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex"); - fr_nat_init = 1; + softn->ipf_nat_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: nat_addrdr */ +/* 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 */ /* */ @@ -341,31 +640,41 @@ int fr_natinit() /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ /* use by redirect rules. */ /* ------------------------------------------------------------------------ */ -static void nat_addrdr(n) -ipnat_t *n; +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; - k = count4bits(n->in_outmsk); - if ((k >= 0) && (k != 32)) - rdr_masks |= 1 << k; - j = (n->in_outip & n->in_outmsk); - hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz); - np = rdr_rules + hv; + 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 = hv; + n->in_hv[0] = hv; + n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ -/* Function: nat_addnat */ +/* Function: ipf_nat_addmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ @@ -373,63 +682,91 @@ ipnat_t *n; /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ /* redirect rules. */ /* ------------------------------------------------------------------------ */ -static void nat_addnat(n) -ipnat_t *n; +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; - k = count4bits(n->in_inmsk); - if ((k >= 0) && (k != 32)) - nat_masks |= 1 << k; - j = (n->in_inip & n->in_inmsk); - hv = NAT_HASH_FN(j, 0, ipf_natrules_sz); - np = nat_rules + hv; + 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 = hv; + n->in_hv[1] = rhv; + n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ -/* Function: nat_delrdr */ +/* 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. */ /* ------------------------------------------------------------------------ */ -static void nat_delrdr(n) -ipnat_t *n; +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: nat_delnat */ +/* 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. */ /* ------------------------------------------------------------------------ */ -static void nat_delnat(n) -ipnat_t *n; +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: nat_hostmap */ +/* 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 */ @@ -442,57 +779,70 @@ ipnat_t *n; /* 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 *nat_hostmap(np, src, dst, map, port) -ipnat_t *np; -struct in_addr src; -struct in_addr dst; -struct in_addr map; -u_32_t port; +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; + u_int hv, rhv; hv = (src.s_addr ^ dst.s_addr); hv += src.s_addr; hv += dst.s_addr; - hv %= HOSTMAP_SIZE; - for (hm = ipf_hm_maptable[hv]; hm; hm = hm->hm_next) - if ((hm->hm_srcip.s_addr == src.s_addr) && - (hm->hm_dstip.s_addr == 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) + if (np == NULL) { + softn->ipf_nat_stats.ns_hm_nullnp++; return NULL; + } KMALLOC(hm, hostmap_t *); if (hm) { - hm->hm_next = ipf_hm_maplist; - hm->hm_pnext = &ipf_hm_maplist; - if (ipf_hm_maplist != NULL) - ipf_hm_maplist->hm_pnext = &hm->hm_next; - ipf_hm_maplist = hm; - hm->hm_hnext = ipf_hm_maptable[hv]; - hm->hm_phnext = ipf_hm_maptable + hv; - if (ipf_hm_maptable[hv] != NULL) - ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; - ipf_hm_maptable[hv] = 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; - hm->hm_srcip = src; - hm->hm_dstip = dst; - hm->hm_mapip = map; + 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: fr_hostmapdel */ +/* Function: ipf_nat_hostmapdel */ /* Returns: Nil */ /* Parameters: hmp(I) - pointer to hostmap structure pointer */ /* Write Locks: ipf_nat */ @@ -500,8 +850,10 @@ u_32_t port; /* Decrement the references to this hostmap structure by one. If this */ /* reaches zero then remove it and free it. */ /* ------------------------------------------------------------------------ */ -void fr_hostmapdel(hmp) -struct hostmap **hmp; +void +ipf_nat_hostmapdel(softc, hmp) + ipf_main_softc_t *softc; + struct hostmap **hmp; { struct hostmap *hm; @@ -510,6 +862,7 @@ struct hostmap **hmp; 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; @@ -522,7 +875,7 @@ struct hostmap **hmp; /* ------------------------------------------------------------------------ */ -/* Function: fix_outcksum */ +/* Function: ipf_fix_outcksum */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* sp(I) - location of 16bit checksum to update */ @@ -530,10 +883,11 @@ struct hostmap **hmp; /* */ /* Adjusts the 16bit checksum by "n" for packets going out. */ /* ------------------------------------------------------------------------ */ -void fix_outcksum(fin, sp, n) -fr_info_t *fin; -u_short *sp; -u_32_t n; +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; @@ -541,11 +895,14 @@ u_32_t n; if (n == 0) return; - if (n & NAT_HW_CKSUM) { - n &= 0xffff; - n += fin->fin_dlen; - n = (n & 0xffff) + (n >> 16); - *sp = n & 0xffff; + if (cksum == 4) { + *sp = 0; + return; + } + if (cksum == 2) { + sum1 = partial; + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + *sp = htons(sum1); return; } sum1 = (~ntohs(*sp)) & 0xffff; @@ -559,7 +916,7 @@ u_32_t n; /* ------------------------------------------------------------------------ */ -/* Function: fix_incksum */ +/* Function: ipf_fix_incksum */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* sp(I) - location of 16bit checksum to update */ @@ -567,10 +924,11 @@ u_32_t n; /* */ /* Adjusts the 16bit checksum by "n" for packets going in. */ /* ------------------------------------------------------------------------ */ -void fix_incksum(fin, sp, n) -fr_info_t *fin; -u_short *sp; -u_32_t n; +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; @@ -578,13 +936,17 @@ u_32_t n; if (n == 0) return; - if (n & NAT_HW_CKSUM) { - n &= 0xffff; - n += fin->fin_dlen; - n = (n & 0xffff) + (n >> 16); - *sp = n & 0xffff; + 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); @@ -596,7 +958,7 @@ u_32_t n; /* ------------------------------------------------------------------------ */ -/* Function: fix_datacksum */ +/* Function: ipf_fix_datacksum */ /* Returns: Nil */ /* Parameters: sp(I) - location of 16bit checksum to update */ /* n((I) - amount to adjust checksum by */ @@ -613,9 +975,10 @@ u_32_t n; /* processing like hardware cksum or ntohs processing have been done by the */ /* kernel on the data section. */ /* ------------------------------------------------------------------------ */ -void fix_datacksum(sp, n) -u_short *sp; -u_32_t n; +void +ipf_fix_datacksum(sp, n) + u_short *sp; + u_32_t n; { u_short sumshort; u_32_t sum1; @@ -634,42 +997,48 @@ u_32_t n; /* ------------------------------------------------------------------------ */ -/* Function: fr_nat_ioctl */ +/* Function: ipf_nat_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 */ +/* 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 fr_nat_ioctl(data, cmd, mode, uid, ctx) -ioctlcmd_t cmd; -caddr_t data; -int mode, uid; -void *ctx; +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; { - ipnat_t *nat, *nt, *n = NULL, **np = NULL; + 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 (BSD >= 199306) && defined(_KERNEL) -# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 399002000) +#if BSD_GE_YEAR(199306) && 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)) { - return EPERM; - } + NULL, NULL, NULL)) # else # if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034) - if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) { + if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) # else - if ((securelevel >= 3) && (mode & FWRITE)) { + if ((securelevel >= 3) && (mode & FWRITE)) # endif +# endif + { + IPFERROR(60001); return EPERM; } -# endif #endif #if defined(__osf__) && defined(_KERNEL) @@ -678,48 +1047,69 @@ void *ctx; getlock = (mode & NAT_LOCKHELD) ? 0 : 1; #endif - nat = NULL; /* XXX gcc -Wuninitialized */ - if (cmd == (ioctlcmd_t)SIOCADNAT) { - KMALLOC(nt, ipnat_t *); - } else { - nt = NULL; - } + n = NULL; + nt = NULL; + nat = NULL; - if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { + 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 { - error = fr_inobj(data, &natd, IPFOBJ_IPNAT); - } - } + bzero(&natd, sizeof(natd)); + error = ipf_inobj(softc, data, NULL, &natd, + IPFOBJ_IPNAT); + if (error != 0) + goto done; - 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 - */ - if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) { - nat = &natd; - if (nat->in_v == 0) /* For backward compat. */ - nat->in_v = 4; + /* + * 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_flags & IPN_SPLIT) == 0) - nat->in_inip &= nat->in_inmsk; - if ((nat->in_flags & IPN_IPRANGE) == 0) - nat->in_outip &= nat->in_outmsk; - } - MUTEX_ENTER(&ipf_natio); - for (np = &nat_list; ((n = *np) != NULL); np = &n->in_next) - if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags, - IPN_CMPSIZ) == 0) { - if (nat->in_redir == NAT_REDIRECT && - nat->in_pnext != n->in_pnext) - continue; - break; + 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) @@ -729,115 +1119,169 @@ void *ctx; { int tmp; - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(60002); error = EPERM; - else { - tmp = ipflog_clear(IPL_LOGNAT); - error = BCOPYOUT((char *)&tmp, (char *)data, - sizeof(tmp)); - if (error != 0) + } 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)) + if (!(mode & FWRITE)) { + IPFERROR(60003); error = EPERM; - else { - error = BCOPYIN((char *)data, (char *)&nat_logging, - sizeof(nat_logging)); + } else { + error = BCOPYIN(data, &softn->ipf_nat_logging, + sizeof(softn->ipf_nat_logging)); if (error != 0) error = EFAULT; } break; case SIOCGETLG : - error = BCOPYOUT((char *)&nat_logging, (char *)data, - sizeof(nat_logging)); - if (error != 0) + error = BCOPYOUT(&softn->ipf_nat_logging, data, + sizeof(softn->ipf_nat_logging)); + if (error != 0) { + IPFERROR(60004); error = EFAULT; + } break; case FIONREAD : - arg = iplused[IPL_LOGNAT]; + arg = ipf_log_bytesused(softc, IPL_LOGNAT); error = BCOPYOUT(&arg, data, sizeof(arg)); - if (error != 0) + 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(&ipf_natio); + MUTEX_EXIT(&softn->ipf_nat_io); break; } - bcopy((char *)nat, (char *)nt, sizeof(*n)); - error = nat_siocaddnat(nt, np, getlock); - MUTEX_EXIT(&ipf_natio); - if (error == 0) + 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(&ipf_natio); + MUTEX_EXIT(&softn->ipf_nat_io); break; } - nat_siocdelnat(n, np, getlock); + 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(&ipf_natio); + MUTEX_EXIT(&softn->ipf_nat_io); n = NULL; break; case SIOCGNATS : - nat_stats.ns_table[0] = nat_table[0]; - nat_stats.ns_table[1] = nat_table[1]; - nat_stats.ns_list = nat_list; - nat_stats.ns_maptable = ipf_hm_maptable; - nat_stats.ns_maplist = ipf_hm_maplist; - nat_stats.ns_nattab_sz = ipf_nattable_sz; - nat_stats.ns_nattab_max = ipf_nattable_max; - nat_stats.ns_rultab_sz = ipf_natrules_sz; - nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; - nat_stats.ns_hostmap_sz = ipf_hostmap_sz; - nat_stats.ns_instances = nat_instances; - nat_stats.ns_apslist = ap_sess_list; - nat_stats.ns_ticks = fr_ticks; - error = fr_outobj(data, &nat_stats, IPFOBJ_NATSTAT); + { + 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 = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); + error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); if (error == 0) { void *ptr; if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } - ptr = nat_lookupredir(&nl); + + 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(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (ptr != NULL) { - error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); + error = ipf_outobj(softc, data, &nl, + IPFOBJ_NATLOOKUP); } else { + IPFERROR(60011); error = ESRCH; } } @@ -846,246 +1290,257 @@ void *ctx; case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ if (!(mode & FWRITE)) { + IPFERROR(60012); error = EPERM; break; } if (getlock) { - WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); } error = BCOPYIN(data, &arg, sizeof(arg)); - if (error != 0) + if (error != 0) { + IPFERROR(60013); error = EFAULT; - else { + } else { if (arg == 0) - ret = nat_flushtable(); + ret = ipf_nat_flushtable(softc, softn); else if (arg == 1) - ret = nat_clearlist(); + ret = ipf_nat_clearlist(softc, softn); else - ret = nat_extraflush(arg); + ret = ipf_nat_extraflush(softc, softn, arg); + ipf_proxy_flush(softc->ipf_proxy_soft, arg); } if (getlock) { - RWLOCK_EXIT(&ipf_nat); + 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 = appr_ioctl(data, cmd, mode, ctx); + error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); break; case SIOCSTLCK : if (!(mode & FWRITE)) { + IPFERROR(60015); error = EPERM; } else { - error = fr_lock(data, &fr_nat_lock); + error = ipf_lock(data, &softn->ipf_nat_lock); } break; case SIOCSTPUT : if ((mode & FWRITE) != 0) { - error = fr_natputent(data, getlock); + error = ipf_nat_putent(softc, data, getlock); } else { + IPFERROR(60016); error = EACCES; } break; case SIOCSTGSZ : - if (fr_nat_lock) { - error = fr_natgetsz(data, getlock); - } else + if (softn->ipf_nat_lock) { + error = ipf_nat_getsz(softc, data, getlock); + } else { + IPFERROR(60017); error = EACCES; + } break; case SIOCSTGET : - if (fr_nat_lock) { - error = fr_natgetent(data, getlock); - } else + 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); - error = fr_inobj(data, &iter, IPFOBJ_GENITER); - if (error == 0) { - token = ipf_findtoken(iter.igi_type, uid, ctx); - if (token != NULL) { - error = nat_iterator(token, &iter); - } - RWLOCK_EXIT(&ipf_tokens); + 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((caddr_t)data, (caddr_t)&arg, sizeof(arg)); + error = BCOPYIN(data, &arg, sizeof(arg)); if (error == 0) { SPL_SCHED(s); - error = ipf_deltoken(arg, uid, ctx); + error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } else { + IPFERROR(60019); error = EFAULT; } break; case SIOCGTQTAB : - error = fr_outobj(data, nat_tqb, IPFOBJ_STATETQTAB); + error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, + IPFOBJ_STATETQTAB); break; case SIOCGTABL : - error = nat_gettable(data); + 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) - KFREE(nt); + KFREES(nt, nt->in_size); return error; } /* ------------------------------------------------------------------------ */ -/* Function: nat_siocaddnat */ +/* Function: ipf_nat_siocaddnat */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: n(I) - pointer to new NAT rule */ +/* 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 ipf_nat is held */ -/* Mutex Locks: ipf_natio */ +/* 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 nat_siocaddnat(n, np, getlock) -ipnat_t *n, **np; -int getlock; +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, i, j; + int error = 0; - if (nat_resolverule(n) != 0) + if (ipf_nat_resolverule(softc, n) != 0) { + IPFERROR(60022); return ENOENT; + } - if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) + if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { + IPFERROR(60023); return EINVAL; + } - n->in_use = 0; - if (n->in_redir & NAT_MAPBLK) - n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); - else if (n->in_flags & IPN_AUTOPORTMAP) - n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); - else if (n->in_flags & IPN_IPRANGE) - n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); - else if (n->in_flags & IPN_SPLIT) - n->in_space = 2; - else if (n->in_outmsk != 0) - n->in_space = ~ntohl(n->in_outmsk); - else - n->in_space = 1; - - /* - * Calculate the number of valid IP addresses in the output - * mapping range. In all cases, the range is inclusive of - * the start and ending IP addresses. - * If to a CIDR address, lose 2: broadcast + network address - * (so subtract 1) - * If to a range, add one. - * If to a single IP address, set to 1. - */ - if (n->in_space) { - if ((n->in_flags & IPN_IPRANGE) != 0) - n->in_space += 1; - else - n->in_space -= 1; - } else - n->in_space = 1; - - if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && - ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) - n->in_nip = ntohl(n->in_outip) + 1; - else if ((n->in_flags & IPN_SPLIT) && - (n->in_redir & NAT_REDIRECT)) - n->in_nip = ntohl(n->in_inip); - else - n->in_nip = ntohl(n->in_outip); - if (n->in_redir & NAT_MAP) { - n->in_pnext = ntohs(n->in_pmin); + if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { /* - * Multiply by the number of ports made available. + * Prerecord whether or not the destination of the divert + * is local or not to the interface the packet is going + * to be sent out. */ - if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) { - n->in_space *= (ntohs(n->in_pmax) - - ntohs(n->in_pmin) + 1); - /* - * Because two different sources can map to - * different destinations but use the same - * local IP#/port #. - * If the result is smaller than in_space, then - * we may have wrapped around 32bits. - */ - i = n->in_inmsk; - if ((i != 0) && (i != 0xffffffff)) { - j = n->in_space * (~ntohl(i) + 1); - if (j >= n->in_space) - n->in_space = j; - else - n->in_space = 0xffffffff; - } - } - /* - * If no protocol is specified, multiple by 256 to allow for - * at least one IP:IP mapping per protocol. - */ - if ((n->in_flags & IPN_TCPUDPICMP) == 0) { - j = n->in_space * 256; - if (j >= n->in_space) - n->in_space = j; - else - n->in_space = 0xffffffff; - } + n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], + n->in_ifps[1], &n->in_ndstip6); } - /* Otherwise, these fields are preset */ - if (getlock) { - WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); } n->in_next = NULL; - *np = n; - - if (n->in_age[0] != 0) - n->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, n->in_age[0]); - - if (n->in_age[1] != 0) - n->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, n->in_age[1]); + 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; - nat_addrdr(n); + 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; - nat_addnat(n); + 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; - nat_stats.ns_rules++; -#if SOLARIS && !defined(_INET_IP_STACK_H) + ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); +#if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 0; #endif if (getlock) { - RWLOCK_EXIT(&ipf_nat); /* WRITE */ + RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ } return error; @@ -1093,30 +1548,113 @@ int getlock; /* ------------------------------------------------------------------------ */ -/* Function: nat_resolvrule */ +/* 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: n(I) - pointer to NAT rule */ +/* 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 nat_resolverule(n) -ipnat_t *n; +static int +ipf_nat_resolverule(softc, n) + ipf_main_softc_t *softc; + ipnat_t *n; { - n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; - n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4); + char *base; + + base = n->in_names; - n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; - if (n->in_ifnames[1][0] == '\0') { - (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); + 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] = fr_resolvenic(n->in_ifnames[1], 4); + n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], + n->in_v[1]); } - if (n->in_plabel[0] != '\0') { - n->in_apr = appr_lookup(n->in_p, n->in_plabel); + 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; } @@ -1125,106 +1663,93 @@ ipnat_t *n; /* ------------------------------------------------------------------------ */ -/* Function: nat_siocdelnat */ +/* Function: ipf_nat_siocdelnat */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: n(I) - pointer to new NAT rule */ -/* np(I) - pointer to where to insert new NAT rule */ -/* getlock(I) - flag indicating if lock on ipf_nat is held */ -/* Mutex Locks: ipf_natio */ +/* 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 nat_siocdelnat(n, np, getlock) -ipnat_t *n, **np; -int getlock; +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(&ipf_nat); - } - if (n->in_redir & NAT_REDIRECT) - nat_delrdr(n); - if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) - nat_delnat(n); - if (nat_list == NULL) { - nat_masks = 0; - rdr_masks = 0; - } - - if (n->in_tqehead[0] != NULL) { - if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) { - fr_freetimeoutqueue(n->in_tqehead[1]); - } - } +#ifdef IPF_NAT6 + int i; +#endif - if (n->in_tqehead[1] != NULL) { - if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) { - fr_freetimeoutqueue(n->in_tqehead[1]); - } + if (getlock) { + WRITE_ENTER(&softc->ipf_nat); } - *np = n->in_next; + ipf_nat_delrule(softc, softn, n, 1); - if (n->in_use == 0) { - if (n->in_apr) - appr_free(n->in_apr); - MUTEX_DESTROY(&n->in_lock); - KFREE(n); - nat_stats.ns_rules--; -#if SOLARIS && !defined(_INET_IP_STACK_H) - if (nat_stats.ns_rules == 0) - pfil_delayed_copy = 1; -#endif - } else { - n->in_flags |= IPN_DELETE; - n->in_next = NULL; - } if (getlock) { - RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ + RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ } } /* ------------------------------------------------------------------------ */ -/* Function: fr_natgetsz */ +/* Function: ipf_nat_getsz */ /* Returns: int - 0 == success, != 0 is the error value. */ -/* Parameters: data(I) - pointer to natget structure with kernel pointer */ -/* get the size of. */ +/* 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 fr_natgetsz(data, getlock) -caddr_t data; -int getlock; +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; - if (BCOPYIN(data, &ng, sizeof(ng)) != 0) + error = BCOPYIN(data, &ng, sizeof(ng)); + if (error != 0) { + IPFERROR(60024); return EFAULT; + } if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } nat = ng.ng_ptr; if (!nat) { - nat = nat_instances; + 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(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } - if (BCOPYOUT(&ng, data, sizeof(ng)) != 0) + error = BCOPYOUT(&ng, data, sizeof(ng)); + if (error != 0) { + IPFERROR(60025); return EFAULT; + } return 0; } } else { @@ -1233,13 +1758,14 @@ int getlock; * current list of entries. Security precaution to prevent * copying of random kernel data. */ - for (n = nat_instances; n; n = n->nat_next) + for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } + IPFERROR(60026); return ESRCH; } } @@ -1255,56 +1781,71 @@ int getlock; ng.ng_sz += aps->aps_psiz; } if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } - if (BCOPYOUT(&ng, data, sizeof(ng)) != 0) + error = BCOPYOUT(&ng, data, sizeof(ng)); + if (error != 0) { + IPFERROR(60027); return EFAULT; + } return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_natgetent */ +/* Function: ipf_nat_getent */ /* Returns: int - 0 == success, != 0 is the error value. */ -/* Parameters: data(I) - pointer to natget structure with kernel pointer */ -/* to NAT structure to copy out. */ +/* 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 fr_natgetent(data, getlock) -caddr_t data; -int getlock; +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 = fr_inobj(data, &ipns, IPFOBJ_NATSAVE); + error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); if (error != 0) return error; - if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) + 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) + if (ipn == NULL) { + IPFERROR(60029); return ENOMEM; + } if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } ipn->ipn_dsize = ipns.ipn_dsize; nat = ipns.ipn_next; if (nat == NULL) { - nat = nat_instances; + nat = softn->ipf_nat_instances; if (nat == NULL) { - if (nat_instances == NULL) + if (softn->ipf_nat_instances == NULL) { + IPFERROR(60030); error = ENOENT; + } goto finished; } } else { @@ -1313,10 +1854,11 @@ int getlock; * current list of entries. Security precaution to prevent * copying of random kernel data. */ - for (n = nat_instances; n; n = n->nat_next) + for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { + IPFERROR(60031); error = ESRCH; goto finished; } @@ -1333,7 +1875,7 @@ int getlock; */ if (nat->nat_ptr != NULL) bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, - sizeof(ipn->ipn_ipnat)); + ipn->ipn_ipnat.in_size); /* * If we also know the NAT entry has an associated filter rule, @@ -1354,6 +1896,7 @@ int getlock; char *s; if (outsize < sizeof(*aps)) { + IPFERROR(60032); error = ENOBUFS; goto finished; } @@ -1364,20 +1907,23 @@ int getlock; outsize -= sizeof(*aps); if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) bcopy(aps->aps_data, s, aps->aps_psiz); - else + else { + IPFERROR(60033); error = ENOBUFS; + } } if (error == 0) { if (getlock) { - RWLOCK_EXIT(&ipf_nat); + READ_ENTER(&softc->ipf_nat); getlock = 0; } - error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); + error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, + ipns.ipn_dsize); } finished: if (getlock) { - RWLOCK_EXIT(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } if (ipn != NULL) { KFREES(ipn, ipns.ipn_dsize); @@ -1387,21 +1933,25 @@ finished: /* ------------------------------------------------------------------------ */ -/* Function: fr_natputent */ +/* Function: ipf_nat_putent */ /* Returns: int - 0 == success, != 0 is the error value. */ -/* Parameters: data(I) - pointer to natget structure with NAT */ -/* structure information to load into the kernel */ +/* 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 ipf_nat is already held. */ +/* 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 fr_natputent(data, getlock) -caddr_t data; -int getlock; +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; @@ -1410,13 +1960,14 @@ int getlock; ipnat_t *in; int error; - error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE); + 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; @@ -1429,17 +1980,21 @@ int getlock; */ 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) + if (ipnn == NULL) { + IPFERROR(60035); return ENOMEM; + } - error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); + bzero(ipnn, ipn.ipn_dsize); + error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, + ipn.ipn_dsize); if (error != 0) { - error = EFAULT; goto junkput; } } else @@ -1447,13 +2002,29 @@ int getlock; 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 nat_delete() doesn't cause a crash. + * 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; @@ -1466,20 +2037,22 @@ int getlock; */ in = ipnn->ipn_nat.nat_ptr; if (in != NULL) { - KMALLOC(in, ipnat_t *); + KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); nat->nat_ptr = in; if (in == NULL) { + IPFERROR(60038); error = ENOMEM; goto junkput; } - bzero((char *)in, offsetof(struct ipnat, in_next6)); - bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in)); + bcopy((char *)&ipnn->ipn_ipnat, (char *)in, + ipnn->ipn_ipnat.in_size); in->in_use = 1; in->in_flags |= IPN_DELETE; - ATOMIC_INC(nat_stats.ns_rules); + ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); - if (nat_resolverule(in) != 0) { + if (ipf_nat_resolverule(softc, in) != 0) { + IPFERROR(60039); error = ESRCH; goto junkput; } @@ -1491,43 +2064,76 @@ int getlock; * 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_p = nat->nat_p; - if (nat->nat_dir == NAT_OUTBOUND) { - fin.fin_ifp = nat->nat_ifps[0]; - fin.fin_data[0] = ntohs(nat->nat_oport); - fin.fin_data[1] = ntohs(nat->nat_outport); + 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(&ipf_nat); + 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 } - n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, - nat->nat_oip, nat->nat_inip); + if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { + IPFERROR(60040); error = EEXIST; goto junkput; } - } else if (nat->nat_dir == NAT_INBOUND) { - fin.fin_ifp = nat->nat_ifps[0]; - fin.fin_data[0] = ntohs(nat->nat_outport); - fin.fin_data[1] = ntohs(nat->nat_oport); + break; + + case NAT_INBOUND : + case NAT_DIVERTIN : if (getlock) { - READ_ENTER(&ipf_nat); + 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 } - n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, - nat->nat_outip, nat->nat_oip); + if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { + IPFERROR(60041); error = EEXIST; goto junkput; } - } else { + break; + + default : + IPFERROR(60042); error = EINVAL; goto junkput; } @@ -1541,6 +2147,7 @@ int getlock; KMALLOC(aps, ap_session_t *); nat->nat_aps = aps; if (aps == NULL) { + IPFERROR(60043); error = ENOMEM; goto junkput; } @@ -1551,11 +2158,13 @@ int getlock; 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; } @@ -1577,12 +2186,13 @@ int getlock; 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) fr_outobj(data, ipnn, IPFOBJ_NATSAVE); + (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); fr->fr_ref = 1; @@ -1594,9 +2204,9 @@ int getlock; MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); } else { if (getlock) { - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); } - for (n = nat_instances; n; n = n->nat_next) + for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n->nat_fr == fr) break; @@ -1606,10 +2216,11 @@ int getlock; MUTEX_EXIT(&fr->fr_lock); } if (getlock) { - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } - if (!n) { + if (n == NULL) { + IPFERROR(60047); error = ESRCH; goto junkput; } @@ -1622,25 +2233,30 @@ int getlock; } if (getlock) { - WRITE_ENTER(&ipf_nat); - } - error = nat_insert(nat, nat->nat_rev); - if ((error == 0) && (aps != NULL)) { - aps->aps_next = ap_sess_list; - ap_sess_list = aps; + 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(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) return 0; + IPFERROR(60048); error = ENOMEM; junkput: - if (fr != NULL) - (void) fr_derefrule(&fr); + if (fr != NULL) { + (void) ipf_derefrule(softc, &fr); + } if ((ipnn != NULL) && (ipnn != &ipn)) { KFREES(ipnn, ipn.ipn_dsize); @@ -1654,8 +2270,8 @@ junkput: } if (in != NULL) { if (in->in_apr) - appr_free(in->in_apr); - KFREE(in); + ipf_proxy_deref(in->in_apr); + KFREES(in, in->in_size); } KFREE(nat); } @@ -1664,27 +2280,29 @@ junkput: /* ------------------------------------------------------------------------ */ -/* Function: nat_delete */ +/* Function: ipf_nat_delete */ /* Returns: Nil */ -/* Parameters: natd(I) - pointer to NAT structure to delete */ +/* 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 nat_delete(nat, logtype) -struct nat *nat; -int logtype; +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; - int removed = 0; - if (logtype != 0 && nat_logging != 0) - nat_log(nat, logtype); -#if defined(NEED_LOCAL_RAND) && defined(_KERNEL) - ipf_rand_push(nat, sizeof(*nat)); -#endif + 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 @@ -1693,8 +2311,19 @@ int logtype; if (nat->nat_pnext != NULL) { removed = 1; - nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--; - nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; + bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; + nss = &softn->ipf_nat_stats.ns_side[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]; + nss->ns_bucketlen[bkt]--; + if (nss->ns_bucketlen[bkt] == 0) { + nss->ns_inuse--; + } *nat->nat_pnext = nat->nat_next; if (nat->nat_next != NULL) { @@ -1717,20 +2346,34 @@ int logtype; } nat->nat_phnext[1] = NULL; - if ((nat->nat_flags & SI_WILDP) != 0) - nat_stats.ns_wilds--; + 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_tqe.tqe_ifq != NULL) - fr_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) - nat_stats.ns_expire++; + softn->ipf_nat_stats.ns_expire++; MUTEX_ENTER(&nat->nat_lock); /* @@ -1743,35 +2386,36 @@ int logtype; nat->nat_ref -= 2; MUTEX_EXIT(&nat->nat_lock); if (removed) - nat_stats.ns_orphans++; + softn->ipf_nat_stats.ns_orphans++; return; } } else if (nat->nat_ref > 1) { nat->nat_ref--; MUTEX_EXIT(&nat->nat_lock); - if (removed) - nat_stats.ns_orphans++; + if (madeorphan == 1) + softn->ipf_nat_stats.ns_orphans++; return; } + ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); - /* - * At this point, nat_ref is 1, doing "--" would make it 0.. - */ nat->nat_ref = 0; - if (!removed) - nat_stats.ns_orphans--; -#ifdef IPFILTER_SYNC - if (nat->nat_sync) - ipfsync_del(nat->nat_sync); -#endif + if (madeorphan == 0) + softn->ipf_nat_stats.ns_orphans--; - if (nat->nat_fr != NULL) - (void) fr_derefrule(&nat->nat_fr); + /* + * 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) - fr_hostmapdel(&nat->nat_hm); + 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 @@ -1779,38 +2423,51 @@ int logtype; * longer being used. */ ipn = nat->nat_ptr; + nat->nat_ptr = NULL; + if (ipn != NULL) { - fr_ipnatderef(&ipn); + 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); - aps_free(nat->nat_aps); - nat_stats.ns_inuse--; + 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. */ - fr_forgetnat((void *)nat); + ipf_frag_natforget(softc, (void *)nat); KFREE(nat); } /* ------------------------------------------------------------------------ */ -/* Function: nat_flushtable */ +/* Function: ipf_nat_flushtable */ /* Returns: int - number of NAT rules deleted */ -/* Parameters: Nil */ +/* 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 nat_delete() if NAT logging is enabled. */ +/* 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 nat_flushtable() +static int +ipf_nat_flushtable(softc, softn) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; { nat_t *nat; int j = 0; @@ -1819,67 +2476,141 @@ static int nat_flushtable() * ALL NAT mappings deleted, so lets just make the deletions * quicker. */ - if (nat_table[0] != NULL) - bzero((char *)nat_table[0], - sizeof(nat_table[0]) * ipf_nattable_sz); - if (nat_table[1] != NULL) - bzero((char *)nat_table[1], - sizeof(nat_table[1]) * ipf_nattable_sz); - - while ((nat = nat_instances) != NULL) { - nat_delete(nat, NL_FLUSH); + 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++; } - nat_stats.ns_inuse = 0; return j; } /* ------------------------------------------------------------------------ */ -/* Function: nat_clearlist */ +/* Function: ipf_nat_clearlist */ /* Returns: int - number of NAT/RDR rules deleted */ -/* Parameters: Nil */ +/* 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 nat_clearlist() +static int +ipf_nat_clearlist(softc, softn) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; { - ipnat_t *n, **np = &nat_list; + ipnat_t *n; int i = 0; - if (nat_rules != NULL) - bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); - if (rdr_rules != NULL) - bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); - - while ((n = *np) != NULL) { - *np = n->in_next; - if (n->in_use == 0) { - if (n->in_apr != NULL) - appr_free(n->in_apr); - MUTEX_DESTROY(&n->in_lock); - KFREE(n); - nat_stats.ns_rules--; - } else { - n->in_flags |= IPN_DELETE; - n->in_next = NULL; - } + 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(_INET_IP_STACK_H) +#if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 1; #endif - nat_masks = 0; - rdr_masks = 0; return i; } /* ------------------------------------------------------------------------ */ -/* Function: nat_newmap */ +/* 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 */ @@ -1891,11 +2622,14 @@ static int nat_clearlist() /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ -static INLINE int nat_newmap(fin, nat, ni) -fr_info_t *fin; -nat_t *nat; -natinfo_t *ni; +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; @@ -1912,11 +2646,17 @@ natinfo_t *ni; l = 0; hm = NULL; np = ni->nai_np; - st_ip = np->in_nip; - st_port = np->in_pnext; - flags = ni->nai_flags; - sport = ni->nai_sport; - dport = ni->nai_dport; + 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 @@ -1925,50 +2665,54 @@ natinfo_t *ni; */ do { port = 0; - in.s_addr = htonl(np->in_nip); + 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 = nat_hostmap(np, fin->fin_src, fin->fin_dst, - in, 0); + hm = ipf_nat_hostmap(softn, np, fin->fin_src, + fin->fin_dst, in, 0); if (hm != NULL) - in.s_addr = hm->hm_mapip.s_addr; + in.s_addr = hm->hm_nsrcip.s_addr; } else if ((l == 1) && (hm != NULL)) { - fr_hostmapdel(&hm); + ipf_nat_hostmapdel(softc, &hm); } in.s_addr = ntohl(in.s_addr); nat->nat_hm = hm; - if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { - if (l > 0) + if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { + if (l > 0) { + NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); return -1; + } } if (np->in_redir == NAT_BIMAP && - np->in_inmsk == np->in_outmsk) { + np->in_osrcmsk == np->in_nsrcmsk) { /* * map the address block in a 1:1 fashion */ - in.s_addr = np->in_outip; - in.s_addr |= fin->fin_saddr & ~np->in_inmsk; + 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))) + !(flags & IPN_TCPUDP))) { + NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); return -1; + } /* * map-block - Calculate destination address. */ in.s_addr = ntohl(fin->fin_saddr); - in.s_addr &= ntohl(~np->in_inmsk); + 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_outmsk); - in.s_addr += ntohl(np->in_outip); + in.s_addr &= ntohl(~np->in_nsrcmsk); + in.s_addr += ntohl(np->in_nsrcaddr); /* * Calculate destination port. */ @@ -1982,28 +2726,34 @@ natinfo_t *ni; port = htons(port); } - } else if ((np->in_outip == 0) && - (np->in_outmsk == 0xffffffff)) { + } else if ((np->in_nsrcaddr == 0) && + (np->in_nsrcmsk == 0xffffffff)) { + i6addr_t in6; + /* * 0/32 - use the interface's IP address. */ if ((l > 0) || - fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, - &in, NULL) == -1) + ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, + &in6, NULL) == -1) { + NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); return -1; - in.s_addr = ntohl(in.s_addr); + } + in.s_addr = ntohl(in6.in4.s_addr); - } else if ((np->in_outip == 0) && (np->in_outmsk == 0)) { + } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { /* * 0/0 - use the original source address/port. */ - if (l > 0) + if (l > 0) { + NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); return -1; + } in.s_addr = ntohl(fin->fin_saddr); - } else if ((np->in_outmsk != 0xffffffff) && - (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) - np->in_nip++; + } else if ((np->in_nsrcmsk != 0xffffffff) && + (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) + np->in_snip++; natl = NULL; @@ -2014,11 +2764,9 @@ natinfo_t *ni; * "ports auto" (without map-block) */ if ((l > 0) && (l % np->in_ppip == 0)) { - if (l > np->in_space) { - return -1; - } else if ((l > np->in_ppip) && - np->in_outmsk != 0xffffffff) - np->in_nip++; + if ((l > np->in_ppip) && + np->in_nsrcmsk != 0xffffffff) + np->in_snip++; } if (np->in_ppip != 0) { port = ntohs(sport); @@ -2032,35 +2780,35 @@ natinfo_t *ni; } } else if (((np->in_redir & NAT_MAPBLK) == 0) && - (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { + (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { /* * Standard port translation. Select next port. */ if (np->in_flags & IPN_SEQUENTIAL) { - port = np->in_pnext; + port = np->in_spnext; } else { - port = ipf_random() % (ntohs(np->in_pmax) - - ntohs(np->in_pmin)); - port += ntohs(np->in_pmin); + port = ipf_random() % (np->in_spmax - + np->in_spmin + 1); + port += np->in_spmin; } port = htons(port); - np->in_pnext++; + np->in_spnext++; - if (np->in_pnext > ntohs(np->in_pmax)) { - np->in_pnext = ntohs(np->in_pmin); - if (np->in_outmsk != 0xffffffff) - np->in_nip++; + 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_IPRANGE) { - if (np->in_nip > ntohl(np->in_outmsk)) - np->in_nip = ntohl(np->in_outip); + 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_outmsk != 0xffffffff) && - ((np->in_nip + 1) & ntohl(np->in_outmsk)) > - ntohl(np->in_outip)) - np->in_nip = ntohl(np->in_outip) + 1; + 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))) @@ -2080,9 +2828,9 @@ natinfo_t *ni; sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[0] = fin->fin_data[1]; - fin->fin_data[1] = htons(port); - natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), - (u_int)fin->fin_p, fin->fin_dst, inb); + 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; @@ -2091,64 +2839,41 @@ natinfo_t *ni; * start ? */ if ((natl != NULL) && - (np->in_pnext != 0) && (st_port == np->in_pnext) && - (np->in_nip != 0) && (st_ip == np->in_nip)) + (np->in_spnext != 0) && (st_port == np->in_spnext) && + (np->in_snip != 0) && (st_ip == np->in_snip)) { + NBUMPSIDED(1, ns_wrap); return -1; + } l++; } while (natl != NULL); - if (np->in_space > 0) - np->in_space--; - /* Setup the NAT table */ - nat->nat_inip = fin->fin_src; - nat->nat_outip.s_addr = htonl(in.s_addr); - nat->nat_oip = fin->fin_dst; + 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 = nat_hostmap(np, fin->fin_src, fin->fin_dst, - nat->nat_outip, 0); - - /* - * The ICMP checksum does not have a pseudo header containing - * the IP addresses - */ - ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); - ni->nai_sum2 = LONG_SUM(in.s_addr); - if ((flags & IPN_TCPUDP)) { - ni->nai_sum1 += ntohs(sport); - ni->nai_sum2 += ntohs(port); - } + nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, + fin->fin_dst, nat->nat_nsrcip, + 0); if (flags & IPN_TCPUDP) { - nat->nat_inport = sport; - nat->nat_outport = port; /* sport */ - nat->nat_oport = dport; + 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_inport = port; - nat->nat_outport = port; - } else if (fin->fin_p == IPPROTO_GRE) { -#if 0 - nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags; - if (GRE_REV(nat->nat_gre.gs_flags) == 1) { - nat->nat_oport = 0;/*fin->fin_data[1];*/ - nat->nat_inport = 0;/*fin->fin_data[0];*/ - nat->nat_outport = 0;/*fin->fin_data[0];*/ - nat->nat_call[0] = fin->fin_data[0]; - nat->nat_call[1] = fin->fin_data[0]; - } -#endif + nat->nat_nicmpid = port; } - ni->nai_ip.s_addr = in.s_addr; - ni->nai_port = port; - ni->nai_nport = dport; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: nat_newrdr */ +/* 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 */ @@ -2159,11 +2884,14 @@ natinfo_t *ni; /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ -static INLINE int nat_newrdr(fin, nat, ni) -fr_info_t *fin; -nat_t *nat; -natinfo_t *ni; +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; @@ -2177,9 +2905,18 @@ natinfo_t *ni; hm = NULL; in.s_addr = 0; np = ni->nai_np; - flags = ni->nai_flags; - sport = ni->nai_sport; - dport = ni->nai_dport; + 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 @@ -2190,13 +2927,14 @@ natinfo_t *ni; */ if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && ((np->in_flags & IPN_STICKY) != 0)) { - hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in, - (u_32_t)dport); + 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_mapip.s_addr); + in.s_addr = ntohl(hm->hm_ndstip.s_addr); np = hm->hm_ipnat; ni->nai_np = np; move = 0; + ipf_nat_hostmapdel(softc, &hm); } } @@ -2207,53 +2945,60 @@ natinfo_t *ni; * internal port. */ if (np->in_flags & IPN_SPLIT) { - in.s_addr = np->in_nip; + in.s_addr = np->in_dnip; if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { - hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, - in, (u_32_t)dport); + hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, + fin->fin_dst, in, (u_32_t)dport); if (hm != NULL) { - in.s_addr = hm->hm_mapip.s_addr; + in.s_addr = hm->hm_ndstip.s_addr; move = 0; } } if (hm == NULL || hm->hm_ref == 1) { - if (np->in_inip == htonl(in.s_addr)) { - np->in_nip = ntohl(np->in_inmsk); + if (np->in_ndstaddr == htonl(in.s_addr)) { + np->in_dnip = ntohl(np->in_ndstmsk); move = 0; } else { - np->in_nip = ntohl(np->in_inip); + 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; - } else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) { /* * 0/32 - use the interface's IP address. */ - if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) + if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, + &in6, NULL) == -1) { + NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); return -1; - in.s_addr = ntohl(in.s_addr); + } + in.s_addr = ntohl(in6.in4.s_addr); - } else if ((np->in_inip == 0) && (np->in_inmsk== 0)) { + } 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_inmsk == np->in_outmsk) { + np->in_ndstmsk == np->in_odstmsk) { /* * map the address block in a 1:1 fashion */ - in.s_addr = np->in_inip; - in.s_addr |= fin->fin_daddr & ~np->in_inmsk; + 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_inip); + in.s_addr = ntohl(np->in_ndstaddr); } - if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) + if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) nport = dport; else { /* @@ -2261,12 +3006,15 @@ natinfo_t *ni; * pmin == pmax, the gain is not significant. */ if (((np->in_flags & IPN_FIXEDDPORT) == 0) && - (np->in_pmin != np->in_pmax)) { - nport = ntohs(dport) - ntohs(np->in_pmin) + - ntohs(np->in_pnext); + (np->in_odport != np->in_dtop)) { + nport = ntohs(dport) - np->in_odport + np->in_dpmax; nport = htons(nport); - } else - nport = np->in_pnext; + } else { + nport = htons(np->in_dpnext); + np->in_dpnext++; + if (np->in_dpnext > np->in_dpmax) + np->in_dpnext = np->in_dpmin; + } } /* @@ -2275,8 +3023,10 @@ natinfo_t *ni; * setup any translation for this either. */ if (in.s_addr == 0) { - if (nport == dport) + if (nport == dport) { + NBUMPSIDED(0, ns_xlate_null); return -1; + } in.s_addr = ntohl(fin->fin_daddr); } @@ -2290,54 +3040,41 @@ natinfo_t *ni; dp = fin->fin_data[1]; fin->fin_data[1] = fin->fin_data[0]; fin->fin_data[0] = ntohs(nport); - natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), + 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) + if (natl != NULL) { + DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); + NBUMPSIDE(0, ns_xlate_exists); return -1; + } - nat->nat_inip.s_addr = htonl(in.s_addr); - nat->nat_outip = fin->fin_dst; - nat->nat_oip = fin->fin_src; + 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 = nat_hostmap(np, fin->fin_src, fin->fin_dst, in, - (u_32_t)dport); - - ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport); - ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport); - - ni->nai_ip.s_addr = in.s_addr; - ni->nai_nport = nport; - ni->nai_port = sport; + nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, + fin->fin_dst, in, (u_32_t)dport); if (flags & IPN_TCPUDP) { - nat->nat_inport = nport; - nat->nat_outport = dport; - nat->nat_oport = sport; + 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_inport = nport; - nat->nat_outport = nport; - } else if (fin->fin_p == IPPROTO_GRE) { -#if 0 - nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags; - if (GRE_REV(nat->nat_gre.gs_flags) == 1) { - nat->nat_call[0] = fin->fin_data[0]; - nat->nat_call[1] = fin->fin_data[1]; - nat->nat_oport = 0; /*fin->fin_data[0];*/ - nat->nat_inport = 0; /*fin->fin_data[1];*/ - nat->nat_outport = 0; /*fin->fin_data[1];*/ - } -#endif + nat->nat_nicmpid = nport; } return move; } /* ------------------------------------------------------------------------ */ -/* Function: nat_new */ +/* 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 */ @@ -2358,29 +3095,32 @@ natinfo_t *ni; /* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ /* as it can result in memory being corrupted. */ /* ------------------------------------------------------------------------ */ -nat_t *nat_new(fin, np, natsave, flags, direction) -fr_info_t *fin; -ipnat_t *np; -nat_t **natsave; -u_int flags; -int direction; +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; { - u_short port = 0, sport = 0, dport = 0, nport = 0; - tcphdr_t *tcp = NULL; + ipf_main_softc_t *softc = fin->fin_main_soft; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm = NULL; - struct in_addr in; nat_t *nat, *natl; + natstat_t *nsp; u_int nflags; natinfo_t ni; - u_32_t sumd; int move; -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) - qpktinfo_t *qpi = fin->fin_qpi; -#endif - if (nat_stats.ns_inuse >= ipf_nattable_max) { - nat_stats.ns_memfail++; - fr_nat_doflush = 1; + 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); return NULL; } @@ -2389,34 +3129,29 @@ int direction; nflags &= NAT_FROMRULE; ni.nai_np = np; - ni.nai_nflags = nflags; - ni.nai_flags = flags; ni.nai_dport = 0; ni.nai_sport = 0; /* Give me a new nat */ KMALLOC(nat, nat_t *); if (nat == NULL) { - nat_stats.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 (ipf_nattable_max > ipf_nattable_sz) { - ipf_nattable_max = nat_stats.ns_inuse - 100; - printf("ipf_nattable_max reduced to %d\n", - ipf_nattable_max); + 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_TCPUDP) { - tcp = fin->fin_dp; - ni.nai_sport = htons(fin->fin_sport); - ni.nai_dport = htons(fin->fin_dport); - } else if (flags & IPN_ICMPQUERY) { + 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 @@ -2430,28 +3165,34 @@ int direction; * the concept of source port. We overlay sport, so we can * maximally reuse the existing code. */ - ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id; - ni.nai_dport = ni.nai_sport; + 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; - - if ((flags & NAT_SLAVE) == 0) { - MUTEX_ENTER(&ipf_nat_new); - } + 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. + * Search the current table for a match and create a new mapping + * if there is none found. */ - if (direction == NAT_OUTBOUND) { + 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 = nat_outlookup(fin, nflags, (u_int)fin->fin_p, + natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); @@ -2459,166 +3200,184 @@ int direction; goto done; } - move = nat_newmap(fin, nat, &ni); - if (move == -1) - goto badnat; - - np = ni.nai_np; - in = ni.nai_ip; + move = ipf_nat_newmap(fin, nat, &ni); } else { /* - * NAT_INBOUND is used only for redirects rules + * NAT_INBOUND is used for redirects rules */ - natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p, - fin->fin_src, fin->fin_dst); + 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 = nat_newrdr(fin, nat, &ni); - if (move == -1) - goto badnat; - - np = ni.nai_np; - in = ni.nai_ip; + move = ipf_nat_newrdr(fin, nat, &ni); } - port = ni.nai_port; - nport = ni.nai_nport; + if (move == -1) + goto badnat; - if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { - if (np->in_redir == NAT_REDIRECT) { - nat_delrdr(np); - nat_addrdr(np); - } else if (np->in_redir == NAT_MAP) { - nat_delnat(np); - nat_addnat(np); + 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); + goto badnat; } } - if (flags & IPN_TCPUDP) { - sport = ni.nai_sport; - dport = ni.nai_dport; - } else if (flags & IPN_ICMPQUERY) { - sport = ni.nai_sport; - dport = 0; + 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]); } - CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); - nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) - if ((flags & IPN_TCP) && dohwcksum && - (((ill_t *)qpi->qpi_ill)->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { - if (direction == NAT_OUTBOUND) - ni.nai_sum1 = LONG_SUM(in.s_addr); - else - ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); - ni.nai_sum1 += LONG_SUM(ntohl(fin->fin_daddr)); - ni.nai_sum1 += 30; - ni.nai_sum1 = (ni.nai_sum1 & 0xffff) + (ni.nai_sum1 >> 16); - nat->nat_sumd[1] = NAT_HW_CKSUM|(ni.nai_sum1 & 0xffff); - } else -#endif - nat->nat_sumd[1] = nat->nat_sumd[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 ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) { - if (direction == NAT_OUTBOUND) - ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr)); - else - ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)); + if (ipf_nat_finalise(fin, nat) == -1) { + goto badnat; + } - ni.nai_sum2 = LONG_SUM(in.s_addr); + np->in_use++; - CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd); - nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); - } else { - nat->nat_ipsumd = nat->nat_sumd[0]; - if (!(flags & IPN_TCPUDPICMP)) { - nat->nat_sumd[0] = 0; - nat->nat_sumd[1] = 0; + 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 (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { - fr_nat_doflush = 1; - goto badnat; - } if (flags & SI_WILDP) - nat_stats.ns_wilds++; - fin->fin_flx |= FI_NEWNAT; + nsp->ns_wilds++; + nsp->ns_proto[nat->nat_pr[0]]++; + goto done; badnat: - nat_stats.ns_badnat++; + DT2(ns_badnatnew, fr_info_t *, fin, nat_t *, nat); + NBUMPSIDE(fin->fin_out, ns_badnatnew); if ((hm = nat->nat_hm) != NULL) - fr_hostmapdel(&hm); + ipf_nat_hostmapdel(softc, &hm); KFREE(nat); nat = NULL; done: - if ((flags & NAT_SLAVE) == 0) { - MUTEX_EXIT(&ipf_nat_new); - } + if (nat != NULL && np != NULL) + np->in_hits++; + if (natsave != NULL) + *natsave = nat; return nat; } /* ------------------------------------------------------------------------ */ -/* Function: nat_finalise */ +/* Function: ipf_nat_finalise */ /* Returns: int - 0 == sucess, -1 == failure */ /* 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 is the tail end of constructing a new NAT entry and is the same */ /* for both IPv4 and IPv6. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ -static int nat_finalise(fin, nat, ni, tcp, natsave, direction) -fr_info_t *fin; -nat_t *nat; -natinfo_t *ni; -tcphdr_t *tcp; -nat_t **natsave; -int direction; +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; - ipnat_t *np; + u_32_t flags; +#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC) + qpktinfo_t *qpi = fin->fin_qpi; +#endif - np = ni->nai_np; + flags = nat->nat_flags; - if (np->in_ifps[0] != NULL) { - COPYIFNAME(4, np->in_ifps[0], nat->nat_ifnames[0]); + 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; } - if (np->in_ifps[1] != NULL) { - COPYIFNAME(4, np->in_ifps[1], nat->nat_ifnames[1]); + + /* + * 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)); } -#ifdef IPFILTER_SYNC - if ((nat->nat_flags & SI_CLONE) == 0) - nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); -#endif + sum1 += nat->nat_pr[1]; + nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); - nat->nat_me = natsave; - nat->nat_dir = direction; - nat->nat_ifps[0] = np->in_ifps[0]; - nat->nat_ifps[1] = np->in_ifps[1]; - nat->nat_ptr = np; - nat->nat_p = fin->fin_p; - nat->nat_mssclamp = np->in_mssclamp; - if (nat->nat_p == IPPROTO_TCP) - nat->nat_seqnext[0] = ntohl(tcp->th_seq); + 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); - if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) - if (appr_new(fin, nat) == -1) - return -1; + 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); - if (nat_insert(nat, fin->fin_rev) == 0) { - if (nat_logging) - nat_log(nat, (u_int)np->in_redir); - np->in_use++; - fr = fin->fin_fr; - nat->nat_fr = fr; + 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++; @@ -2627,112 +3386,224 @@ int direction; return 0; } + NBUMPSIDED(fin->fin_out, ns_unfinalised); /* * 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: nat_insert */ -/* Returns: int - 0 == sucess, -1 == failure */ -/* Parameters: nat(I) - pointer to NAT structure */ -/* rev(I) - flag indicating forward/reverse direction of packet */ -/* Write Lock: ipf_nat */ +/* 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 nat_insert(nat, rev) -nat_t *nat; -int rev; +int +ipf_nat_insert(softc, softn, nat) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + nat_t *nat; { - u_int hv1, hv2; - nat_t **natp; + u_int hv0, hv1; + u_int 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) { - hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, - 0xffffffff); - hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport, - ipf_nattable_sz); - hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, - 0xffffffff); - hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, - ipf_nattable_sz); + 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 { - hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); - hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, ipf_nattable_sz); - hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); - hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, ipf_nattable_sz); - } + hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); + hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); + /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ - if (nat_stats.ns_bucketlen[0][hv1] >= fr_nat_maxbucket || - nat_stats.ns_bucketlen[1][hv2] >= fr_nat_maxbucket) { - return -1; + 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] = hv1; - nat->nat_hv[1] = hv2; + nat->nat_hv[0] = hv0; + nat->nat_hv[1] = hv1; MUTEX_INIT(&nat->nat_lock, "nat entry lock"); - nat->nat_rev = rev; - nat->nat_ref = 1; - nat->nat_bytes[0] = 0; - nat->nat_pkts[0] = 0; - nat->nat_bytes[1] = 0; - nat->nat_pkts[1] = 0; + in = nat->nat_ptr; + nat->nat_ref = nat->nat_me ? 2 : 1; nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; - nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4); + 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] = fr_resolvenic(nat->nat_ifnames[1], 4); - } else { - (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]; + 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]); } - nat->nat_next = nat_instances; - nat->nat_pnext = &nat_instances; - if (nat_instances) - nat_instances->nat_pnext = &nat->nat_next; - nat_instances = nat; + return ipf_nat_hashtab_add(softc, softn, nat); +} - natp = &nat_table[0][hv1]; - if (*natp) - (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_nat_hashtab_add */ +/* 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; - nat_stats.ns_bucketlen[0][hv1]++; + NBUMPSIDE(0, ns_bucketlen[hv0]); - natp = &nat_table[1][hv2]; - if (*natp) - (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; + /* + * 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; - nat_stats.ns_bucketlen[1][hv2]++; + NBUMPSIDE(1, ns_bucketlen[hv1]); - fr_setnatqueue(nat, rev); + ipf_nat_setqueue(softc, softn, nat); - nat_stats.ns_added++; - nat_stats.ns_inuse++; + if (nat->nat_dir & NAT_OUTBOUND) { + NBUMPSIDE(1, ns_added); + } else { + NBUMPSIDE(0, ns_added); + } + softn->ipf_nat_stats.ns_active++; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: nat_icmperrorlookup */ +/* 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) */ @@ -2741,12 +3612,16 @@ int rev; /* ICMP query nat entry. It is assumed that the packet is already of the */ /* the required length. */ /* ------------------------------------------------------------------------ */ -nat_t *nat_icmperrorlookup(fin, dir) -fr_info_t *fin; -int dir; +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; @@ -2755,13 +3630,16 @@ int dir; 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)) + if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { + ATOMIC_INCL(nside->ns_icmp_basic); return NULL; + } /* * Check packet size @@ -2769,35 +3647,45 @@ int dir; oip = (ip_t *)((char *)fin->fin_dp + 8); minlen = IP_HL(oip) << 2; if ((minlen < sizeof(ip_t)) || - (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) + (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 fr_check() and thus can't gaurantee it is + * do the pullup early in ipf_check() and thus can't gaurantee it is * all here now. */ -#ifdef _KERNEL +#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) + 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)) + (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) + 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) @@ -2808,7 +3696,7 @@ int dir; orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); /* see if this is related to an ICMP query */ - if (nat_icmpquerytype4(orgicmp->icmp_type)) { + if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; fin->fin_data[0] = 0; @@ -2821,21 +3709,26 @@ int dir; * message flows in the opposite direction. */ if (dir == NAT_INBOUND) - nat = nat_inlookup(fin, flags, p, oip->ip_dst, - oip->ip_src); + nat = ipf_nat_inlookup(fin, flags, p, + oip->ip_dst, + oip->ip_src); else - nat = nat_outlookup(fin, flags, p, oip->ip_dst, - oip->ip_src); + 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 */ - if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) + /* 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]; @@ -2844,10 +3737,10 @@ int dir; fin->fin_data[1] = ntohs(tcp->th_sport); if (dir == NAT_INBOUND) { - nat = nat_inlookup(fin, flags, p, oip->ip_dst, - oip->ip_src); + nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, + oip->ip_src); } else { - nat = nat_outlookup(fin, flags, p, oip->ip_dst, + nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, oip->ip_src); } fin->fin_data[0] = data[0]; @@ -2855,14 +3748,16 @@ int dir; return nat; } if (dir == NAT_INBOUND) - return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); + nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); else - return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); + nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); + + return nat; } /* ------------------------------------------------------------------------ */ -/* Function: nat_icmperror */ +/* 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 */ @@ -2874,13 +3769,16 @@ int dir; /* This should *ONLY* be used for incoming ICMP error packets to make sure */ /* a NAT'd ICMP packet gets correctly recognised. */ /* ------------------------------------------------------------------------ */ -nat_t *nat_icmperror(fin, nflags, dir) -fr_info_t *fin; -u_int *nflags; -int dir; +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; + struct in_addr a1, a2, a3, a4; int flags, dlen, odst; icmphdr_t *icmp; u_short *csump; @@ -2889,13 +3787,18 @@ int dir; ip_t *oip; void *dp; - if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) + if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { + NBUMPSIDED(fin->fin_out, ns_icmp_short); return NULL; + } + /* - * nat_icmperrorlookup() will return NULL for `defective' packets. + * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. */ - if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir))) + if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { + NBUMPSIDED(fin->fin_out, ns_icmp_notfound); return NULL; + } tcp = NULL; csump = NULL; @@ -2923,7 +3826,7 @@ int dir; /* * 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 fr_checknatout + * 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 @@ -2949,42 +3852,73 @@ int dir; * ------------ * 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_outip, OIP_DST(b)=nat_oip + * - 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_outip, OIP_DST(a)=nat_oip + * - 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(a)=nat_oip, OIP_DST(c)=nat_inip + * - 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_oip, OIP_DST(c)=nat_inip + * - 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 */ - odst = (oip->ip_dst.s_addr == nat->nat_oip.s_addr) ? 1 : 0; - if (odst == 1) { - a1.s_addr = ntohl(nat->nat_inip.s_addr); - a2.s_addr = ntohl(oip->ip_src.s_addr); + + 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_outip.s_addr); + a1.s_addr = ntohl(nat->nat_ndstaddr); a2.s_addr = ntohl(oip->ip_dst.s_addr); - oip->ip_dst.s_addr = htonl(a1.s_addr); - } - - sumd = a2.s_addr - a1.s_addr; - if (sumd != 0) { - if (a1.s_addr > a2.s_addr) - sumd--; - sumd = ~sumd; - - fix_datacksum(&oip->ip_sum, sumd); + 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; @@ -2995,6 +3929,8 @@ int dir; * 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 @@ -3003,24 +3939,33 @@ int dir; * * 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 + * 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_inport); - sum2 = ntohs(tcp->th_sport); + 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_outport); + sum1 = ntohs(nat->nat_ndport); sum2 = ntohs(tcp->th_dport); + sum3 = ntohs(nat->nat_nsport); + sum4 = ntohs(tcp->th_sport); - tcp->th_dport = htons(sum1); + tcp->th_dport = htons(sum3); + tcp->th_sport = htons(sum1); } + CALC_SUMD(sum4, sum1, sumt); + sumd += sumt; + CALC_SUMD(sum2, sum3, sumt); + sumd += sumt; - sumd += sum1 - sum2; if (sumd != 0 || sumd2 != 0) { /* * At this point, sumd is the delta to apply to the @@ -3038,39 +3983,26 @@ int dir; */ if (oip->ip_p == IPPROTO_UDP) { if ((dlen >= 8) && (*csump != 0)) { - fix_datacksum(csump, sumd); + ipf_fix_datacksum(csump, sumd); } else { - sumd2 = sum1 - sum2; - if (sum2 > sum1) - sumd2--; + CALC_SUMD(sum1, sum4, sumd2); + CALC_SUMD(sum3, sum2, sumt); + sumd2 += sumt; } } else if (oip->ip_p == IPPROTO_TCP) { if (dlen >= 18) { - fix_datacksum(csump, sumd); + ipf_fix_datacksum(csump, sumd); } else { - sumd2 = sum2 - sum1; - if (sum1 > sum2) - sumd2--; + CALC_SUMD(sum1, sum4, sumd2); + CALC_SUMD(sum3, sum2, sumt); + sumd2 += sumt; } } - if (sumd2 != 0) { - ipnat_t *np; - - np = nat->nat_ptr; sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); - - if ((odst == 0) && (dir == NAT_OUTBOUND) && - (fin->fin_rev == 0) && (np != NULL) && - (np->in_redir & NAT_REDIRECT)) { - fix_outcksum(fin, &icmp->icmp_cksum, - sumd2); - } else { - fix_incksum(fin, &icmp->icmp_cksum, - sumd2); - } + ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); } } } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { @@ -3078,12 +4010,13 @@ int dir; /* * XXX - what if this is bogus hl and we go off the end ? - * In this case, nat_icmperrorlookup() will have returned NULL. + * In this case, ipf_nat_icmperrorlookup() will have + * returned NULL. */ orgicmp = (icmphdr_t *)dp; if (odst == 1) { - if (orgicmp->icmp_id != nat->nat_inport) { + if (orgicmp->icmp_id != nat->nat_osport) { /* * Fix ICMP checksum (of the offening ICMP @@ -3098,10 +4031,10 @@ int dir; * overall icmp->icmp_cksum */ sum1 = ntohs(orgicmp->icmp_id); - sum2 = ntohs(nat->nat_inport); + sum2 = ntohs(nat->nat_oicmpid); CALC_SUMD(sum1, sum2, sumd); - orgicmp->icmp_id = nat->nat_inport; - fix_datacksum(&orgicmp->icmp_cksum, sumd); + orgicmp->icmp_id = nat->nat_oicmpid; + ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); } } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ } @@ -3110,12 +4043,19 @@ int dir; /* + * 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: nat_inlookup */ +/* Function: ipf_nat_inlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ @@ -3130,17 +4070,20 @@ int dir; /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ -/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ +/* 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 *nat_inlookup(fin, flags, p, src, mapdst) -fr_info_t *fin; -u_int flags, p; -struct in_addr src , mapdst; +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; @@ -3149,11 +4092,9 @@ struct in_addr src , mapdst; int nflags; u_32_t dst; void *ifp; - u_int hv; + u_int hv, rhv; ifp = fin->fin_ifp; - sport = 0; - dport = 0; gre = NULL; dst = mapdst.s_addr; sflags = flags & NAT_TCPUDPICMP; @@ -3166,12 +4107,17 @@ struct in_addr src , mapdst; dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMP : - if (flags & IPN_ICMPERR) + if (flags & IPN_ICMPERR) { sport = fin->fin_data[1]; - else + dport = 0; + } else { dport = fin->fin_data[1]; + sport = 0; + } break; default : + sport = 0; + dport = 0; break; } @@ -3179,57 +4125,81 @@ struct in_addr src , mapdst; if ((flags & SI_WILDP) != 0) goto find_in_wild_ports; - hv = NAT_HASH_FN(dst, dport, 0xffffffff); - hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz); - nat = nat_table[1][hv]; + 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; - } else if (ifp != NULL) - nat->nat_ifps[0] = ifp; + } - nflags = nat->nat_flags; + if (nat->nat_pr[0] != p) + continue; - if (nat->nat_oip.s_addr == src.s_addr && - nat->nat_outip.s_addr == dst && - (((p == 0) && - (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) - || (p == nat->nat_p))) { - switch (p) - { -#if 0 - case IPPROTO_GRE : - if (nat->nat_call[1] != fin->fin_data[0]) + 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; -#endif - case IPPROTO_ICMP : - if ((flags & IPN_ICMPERR) != 0) { - if (nat->nat_outport != sport) - continue; - } else { - if (nat->nat_outport != dport) - continue; } - break; - case IPPROTO_TCP : - case IPPROTO_UDP : - if (nat->nat_oport != sport) + } + 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_outport != dport) + if (nat->nat_nsport != dport) continue; - break; - default : - break; + + } 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 (appr_match(fin, nat) != 0) + if (ipf_proxy_match(fin, nat) != 0) continue; - return nat; } + if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { + nat->nat_ifps[0] = ifp; + nat->nat_mtu[0] = GETIFMTU_4(ifp); + } + return nat; } /* @@ -3240,126 +4210,191 @@ struct in_addr src , mapdst; * for "dummy" (FI_IGNORE) lookups. */ find_in_wild_ports: - if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) + if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { + NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); return NULL; - if (nat_stats.ns_wilds == 0) + } + 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(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN(dst, 0, 0xffffffff); - hv = NAT_HASH_FN(src.s_addr, hv, ipf_nattable_sz); + hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); + WRITE_ENTER(&softc->ipf_nat); - WRITE_ENTER(&ipf_nat); - - nat = nat_table[1][hv]; + 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; - } else if (ifp != NULL) - nat->nat_ifps[0] = ifp; + } - if (nat->nat_p != fin->fin_p) - continue; - if (nat->nat_oip.s_addr != src.s_addr || - nat->nat_outip.s_addr != dst) + 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 (nat_wildok(nat, (int)sport, (int)dport, nflags, - NAT_INBOUND) == 1) { + 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 = fr_natclone(fin, nat); + nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { - MUTEX_ENTER(&ipf_nat_new); - nat_stats.ns_wilds--; - MUTEX_EXIT(&ipf_nat_new); + 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_oport = sport; - nat->nat_outport = dport; nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); - nat_tabmove(nat); + ipf_nat_tabmove(softn, nat); break; } } - MUTEX_DOWNGRADE(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); + if (nat == NULL) { + NBUMPSIDE(0, ns_lookup_miss); + } return nat; } /* ------------------------------------------------------------------------ */ -/* Function: nat_tabmove */ +/* Function: ipf_nat_tabmove */ /* Returns: Nil */ -/* Parameters: nat(I) - pointer to NAT structure */ +/* 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 nat_tabmove(nat) -nat_t *nat; +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; - u_int hv; 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]; - nat_stats.ns_bucketlen[0][nat->nat_hv[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]; - nat_stats.ns_bucketlen[1][nat->nat_hv[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 */ - hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff); - hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, - ipf_nattable_sz); - nat->nat_hv[0] = hv; - natp = &nat_table[0][hv]; + 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; - nat_stats.ns_bucketlen[0][hv]++; + nsp->ns_side[0].ns_bucketlen[hv0]++; - hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff); - hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport, - ipf_nattable_sz); - nat->nat_hv[1] = hv; - natp = &nat_table[1][hv]; + 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; - nat_stats.ns_bucketlen[1][hv]++; + nsp->ns_side[1].ns_bucketlen[hv1]++; } /* ------------------------------------------------------------------------ */ -/* Function: nat_outlookup */ +/* Function: ipf_nat_outlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ @@ -3367,7 +4402,7 @@ nat_t *nat; /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* dst(I) - destination IP address */ -/* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ +/* 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, */ @@ -3375,28 +4410,28 @@ nat_t *nat; /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ -/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ +/* 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 *nat_outlookup(fin, flags, p, src, dst) -fr_info_t *fin; -u_int flags, p; -struct in_addr src , dst; +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; - u_32_t srcip; nat_t *nat; - int nflags; void *ifp; u_int hv; ifp = fin->fin_ifp; - srcip = src.s_addr; sflags = flags & IPN_TCPUDPICMP; sport = 0; dport = 0; @@ -3421,47 +4456,75 @@ struct in_addr src , dst; if ((flags & SI_WILDP) != 0) goto find_out_wild_ports; - hv = NAT_HASH_FN(srcip, sport, 0xffffffff); - hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz); - nat = nat_table[0][hv]; + 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; - } else if (ifp != NULL) - nat->nat_ifps[1] = ifp; + } - nflags = nat->nat_flags; + if (nat->nat_pr[1] != p) + continue; - if (nat->nat_inip.s_addr == srcip && - nat->nat_oip.s_addr == dst.s_addr && - (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) - || (p == nat->nat_p))) { - switch (p) - { -#if 0 - case IPPROTO_GRE : - if (nat->nat_call[1] != fin->fin_data[0]) + 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; - break; -#endif - case IPPROTO_TCP : - case IPPROTO_UDP : - if (nat->nat_oport != dport) + if (nat->nat_nsport != dport) continue; - if (nat->nat_inport != sport) + + } else if (p == IPPROTO_ICMP) { + if (nat->nat_osport != dport) { continue; - break; - default : - break; + } } + 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; - ipn = nat->nat_ptr; - if ((ipn != NULL) && (nat->nat_aps != NULL)) - if (appr_match(fin, nat) != 0) + if ((nat->nat_flags & IPN_TCPUDP) != 0) { + if (nat->nat_odport != dport) continue; - return nat; + 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; } /* @@ -3472,67 +4535,107 @@ struct in_addr src , dst; * for "dummy" (FI_IGNORE) lookups. */ find_out_wild_ports: - if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) + if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { + NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); return NULL; - if (nat_stats.ns_wilds == 0) + } + 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(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); - hv = NAT_HASH_FN(srcip, 0, 0xffffffff); - hv = NAT_HASH_FN(dst.s_addr, hv, ipf_nattable_sz); + hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); + hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); - WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); - nat = nat_table[0][hv]; + 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; - } else if (ifp != NULL) - nat->nat_ifps[1] = ifp; + } - if (nat->nat_p != fin->fin_p) - continue; - if ((nat->nat_inip.s_addr != srcip) || - (nat->nat_oip.s_addr != dst.s_addr)) + if (nat->nat_pr[1] != fin->fin_p) continue; - nflags = nat->nat_flags; - if (!(nflags & (NAT_TCPUDP|SI_WILDP))) + 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 (nat_wildok(nat, (int)sport, (int)dport, nflags, - NAT_OUTBOUND) == 1) { + if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, + NAT_OUTBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; - if ((nflags & SI_CLONE) != 0) { - nat = fr_natclone(fin, nat); + if ((nat->nat_flags & SI_CLONE) != 0) { + nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { - MUTEX_ENTER(&ipf_nat_new); - nat_stats.ns_wilds--; - MUTEX_EXIT(&ipf_nat_new); + 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_inport = sport; - nat->nat_oport = dport; - if (nat->nat_outport == 0) - nat->nat_outport = sport; nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); - nat_tabmove(nat); + ipf_nat_tabmove(softn, nat); break; } } - MUTEX_DOWNGRADE(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); + if (nat == NULL) { + NBUMPSIDE(1, ns_lookup_miss); + } return nat; } /* ------------------------------------------------------------------------ */ -/* Function: nat_lookupredir */ +/* 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 */ @@ -3550,8 +4653,9 @@ find_out_wild_ports: /* nl_in* = source information (untranslated) */ /* nl_out* = destination information (translated) */ /* ------------------------------------------------------------------------ */ -nat_t *nat_lookupredir(np) -natlookup_t *np; +nat_t * +ipf_nat_lookupredir(np) + natlookup_t *np; { fr_info_t fi; nat_t *nat; @@ -3577,34 +4681,34 @@ natlookup_t *np; * - default: we have the `in' and `out' address, look for `real'. */ if (np->nl_flags & IPN_IN) { - if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p, - np->nl_realip, np->nl_outip))) { - np->nl_inip = nat->nat_inip; - np->nl_inport = nat->nat_inport; + 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 = nat_outlookup(&fi, np->nl_flags, fi.fin_p, + 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_p; - fin.fin_data[0] = ntohs(nat->nat_outport); - fin.fin_data[1] = ntohs(nat->nat_oport); - if (nat_inlookup(&fin, np->nl_flags, fin.fin_p, - nat->nat_outip, - nat->nat_oip) != NULL) { + 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_outip; - np->nl_realport = nat->nat_outport; + np->nl_realip = nat->nat_ndstip; + np->nl_realport = nat->nat_ndport; } } @@ -3613,46 +4717,54 @@ natlookup_t *np; /* ------------------------------------------------------------------------ */ -/* Function: nat_match */ +/* 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 fr_checknatin() and lay it out properly in its own function. */ +/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ /* ------------------------------------------------------------------------ */ -static int nat_match(fin, np) -fr_info_t *fin; -ipnat_t *np; +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; - if (fin->fin_v != 4) - return 0; - - if (np->in_p && fin->fin_p != np->in_p) + 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; - if (fin->fin_out) { - if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) - return 0; - if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) - ^ ((np->in_flags & IPN_NOTSRC) != 0)) - return 0; - if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) - ^ ((np->in_flags & IPN_NOTDST) != 0)) - return 0; - } else { - if (!(np->in_redir & NAT_REDIRECT)) - return 0; - if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) - ^ ((np->in_flags & IPN_NOTSRC) != 0)) - return 0; - if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) - ^ ((np->in_flags & IPN_NOTDST) != 0)) - 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))) { @@ -3661,28 +4773,33 @@ ipnat_t *np; return 1; } - return fr_tcpudpchk(fin, ft); + return ipf_tcpudpchk(&fin->fin_fi, ft); } /* ------------------------------------------------------------------------ */ -/* Function: nat_update */ +/* Function: ipf_nat_update */ /* Returns: Nil */ -/* Parameters: nat(I) - pointer to NAT structure */ -/* np(I) - pointer to NAT rule */ +/* 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 nat_proto(). */ +/* 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 nat_update(fin, nat, np) -fr_info_t *fin; -nat_t *nat; -ipnat_t *np; +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; - MUTEX_ENTER(&nat->nat_lock); tqe = &nat->nat_tqe; ifq = tqe->tqe_ifq; @@ -3691,51 +4808,36 @@ ipnat_t *np; * TCP, however, if it is TCP and there is no rule timeout set, * then do not update the timeout here. */ - if (np != NULL) + if (np != NULL) { + np->in_bytes[fin->fin_rev] += fin->fin_plen; ifq2 = np->in_tqehead[fin->fin_rev]; - else + } else { ifq2 = NULL; + } - if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) { - u_32_t end, ack; - u_char tcpflags; - tcphdr_t *tcp; - int dsize; - - tcp = fin->fin_dp; - tcpflags = tcp->th_flags; - dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) + - ((tcpflags & TH_SYN) ? 1 : 0) + - ((tcpflags & TH_FIN) ? 1 : 0); - - ack = ntohl(tcp->th_ack); - end = ntohl(tcp->th_seq) + dsize; - - if (SEQ_GT(ack, nat->nat_seqnext[1 - fin->fin_rev])) - nat->nat_seqnext[1 - fin->fin_rev] = ack; - - if (nat->nat_seqnext[fin->fin_rev] == 0) - nat->nat_seqnext[fin->fin_rev] = end; - - (void) fr_tcp_age(&nat->nat_tqe, fin, nat_tqb, 0); + 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_p == IPPROTO_UDP) - ifq2 = &nat_udptq; - else if (nat->nat_p == IPPROTO_ICMP) - ifq2 = &nat_icmptq; + 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 = &nat_iptq; + ifq2 = &softn->ipf_nat_iptq; } - fr_movequeue(tqe, ifq, ifq2); + ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); } - MUTEX_EXIT(&nat->nat_lock); } /* ------------------------------------------------------------------------ */ -/* Function: fr_checknatout */ +/* Function: ipf_nat_checkout */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ @@ -3749,29 +4851,46 @@ ipnat_t *np; /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ -int fr_checknatout(fin, passp) -fr_info_t *fin; -u_32_t *passp; +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; - ipnat_t *np = NULL; u_int nflags = 0; u_32_t ipa, iph; int natadd = 1; frentry_t *fr; nat_t *nat; - if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) + 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_ifp; + ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; if ((ifp != NULL) && (ifp != (void *)-1)) fin->fin_ifp = ifp; } @@ -3793,117 +4912,152 @@ u_32_t *passp; * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ - if (nat_icmpquerytype4(icmp->icmp_type)) + 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(&ipf_nat); + READ_ENTER(&softc->ipf_nat); - if (((fin->fin_flx & FI_ICMPERR) != 0) && - (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND))) + 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 = fr_nat_knownfrag(fin))) + else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; - else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, - fin->fin_src, fin->fin_dst))) { + 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 { - u_32_t hv, msk, nmsk; + } 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). */ - RWLOCK_EXIT(&ipf_nat); - msk = 0xffffffff; - nmsk = nat_masks; - WRITE_ENTER(&ipf_nat); maskloop: - iph = ipa & htonl(msk); - hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); - for (np = nat_rules[hv]; np; np = np->in_mnext) - { + 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 != fin->fin_v) + if (np->in_v[0] != 4) continue; - if (np->in_p && (np->in_p != fin->fin_p)) + if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) continue; - if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) + if ((np->in_flags & IPN_RF) && + !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { - if (!nat_match(fin, np)) + switch (ipf_nat_match(fin, np)) + { + case 0 : continue; - } else if ((ipa & np->in_inmsk) != np->in_inip) + case -1 : + rval = -1; + goto outmatchfail; + case 1 : + default : + break; + } + } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) continue; if ((fr != NULL) && - !fr_matchtag(&np->in_tag, &fr->fr_nattag)) + !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) continue; - if (*np->in_plabel != '\0') { + if (np->in_plabel != -1) { if (((np->in_flags & IPN_FILTER) == 0) && - (np->in_dport != tcp->th_dport)) + (np->in_odport != fin->fin_data[1])) continue; - if (appr_ok(fin, tcp, np) == 0) + if (ipf_proxy_ok(fin, tcp, np) == 0) continue; } - if ((nat = nat_new(fin, np, NULL, nflags, - NAT_OUTBOUND))) { + if (np->in_flags & IPN_NO) { np->in_hits++; break; - } else - natfailed = -1; - } - if ((np == NULL) && (nmsk != 0)) { - while (nmsk) { - msk <<= 1; - if (nmsk & 0x80000000) - break; - nmsk <<= 1; } - if (nmsk != 0) { - nmsk <<= 1; - goto maskloop; + 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 = -1; + } + if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { + nmsk++; + goto maskloop; } - MUTEX_DOWNGRADE(&ipf_nat); } if (nat != NULL) { - rval = fr_natout(fin, nat, natadd, nflags); + rval = ipf_nat_out(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); - nat->nat_ref++; + 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); - nat->nat_touched = fr_ticks; - fin->fin_nat = nat; } } else rval = natfailed; - RWLOCK_EXIT(&ipf_nat); +outmatchfail: + RWLOCK_EXIT(&softc->ipf_nat); - if (rval == -1) { - if (passp != NULL) + switch (rval) + { + case -1 : + if (passp != NULL) { + DT1(frb_natv4out, fr_info_t *, fin); + NBUMPSIDED(1, ns_drop); *passp = FR_BLOCK; + fin->fin_reason = FRB_NATV4; + } fin->fin_flx |= FI_BADNAT; + NBUMPSIDED(1, ns_badnat); + break; + case 0 : + NBUMPSIDE(1, ns_ignored); + break; + case 1 : + NBUMPSIDE(1, ns_translated); + break; } fin->fin_ifp = sifp; return rval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_natout */ +/* 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 */ @@ -3913,30 +5067,27 @@ maskloop: /* */ /* Translate a packet coming "out" on an interface. */ /* ------------------------------------------------------------------------ */ -int fr_natout(fin, nat, natadd, nflags) -fr_info_t *fin; -nat_t *nat; -int natadd; -u_32_t nflags; +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; - u_short *csump; tcphdr_t *tcp; ipnat_t *np; + int skip; int i; tcp = NULL; icmp = NULL; - csump = NULL; np = nat->nat_ptr; if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) - (void) fr_nat_newfrag(fin, 0, nat); - - MUTEX_ENTER(&nat->nat_lock); - nat->nat_bytes[1] += fin->fin_plen; - nat->nat_pkts[1]++; - MUTEX_EXIT(&nat->nat_lock); + (void) ipf_frag_natnew(softc, fin, 0, nat); /* * Fix up checksums, not by recalculating them, but @@ -3946,90 +5097,240 @@ u_32_t nflags; * IPFilter is called before the checksum needs calculating so there * is no call to modify whatever is in the header now. */ - if (fin->fin_v == 4) { - if (nflags == IPN_ICMPERR) { - u_32_t s1, s2, sumd; + if (nflags == IPN_ICMPERR) { + u_32_t s1, s2, sumd, msumd; - s1 = LONG_SUM(ntohl(fin->fin_saddr)); - s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); - CALC_SUMD(s1, s2, sumd); - fix_outcksum(fin, &fin->fin_ip->ip_sum, sumd); + 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(__sgi) || \ defined(linux) || defined(BRIDGE_IPF) - 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. - */ - if (nat->nat_dir == NAT_OUTBOUND) - fix_outcksum(fin, &fin->fin_ip->ip_sum, - nat->nat_ipsumd); - else - fix_incksum(fin, &fin->fin_ip->ip_sum, - nat->nat_ipsumd); + 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->ip_id = htons(ipf_nextipid(fin)); + 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(__sgi) || \ + defined(linux) || defined(BRIDGE_IPF) + 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)) { - if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { + u_short *csump; + + if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; - tcp->th_sport = nat->nat_outport; - fin->fin_data[0] = ntohs(nat->nat_outport); + 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_outport != 0) && (nflags & IPN_ICMPQUERY)) { + if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; - icmp->icmp_id = nat->nat_outport; + icmp->icmp_id = nat->nat_nicmpid; } - csump = nat_proto(fin, nat, nflags); - } - - fin->fin_ip->ip_src = nat->nat_outip; - - nat_update(fin, nat, np); + 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) - fix_outcksum(fin, csump, nat->nat_sumd[1]); - else - fix_incksum(fin, csump, nat->nat_sumd[1]); + /* + * 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); + } } -#ifdef IPFILTER_SYNC - ipfsync_update(SMC_NAT, fin, nat->nat_sync); -#endif + + ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* ------------------------------------------------------------- */ - /* A few quick notes: */ - /* Following are test conditions prior to calling the */ - /* appr_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. */ + /* 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 = appr_check(fin, nat); - if (i == 0) + i = ipf_proxy_check(fin, nat); + if (i == 0) { i = 1; - } else + } else if (i == -1) { + NBUMPSIDED(1, ns_ipf_proxy_fail); + } + } else { i = 1; - ATOMIC_INCL(nat_stats.ns_mapped[1]); + } fin->fin_flx |= FI_NATED; return i; } /* ------------------------------------------------------------------------ */ -/* Function: fr_checknatin */ +/* Function: ipf_nat_checkin */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ @@ -4043,22 +5344,31 @@ u_32_t nflags; /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ -int fr_checknatin(fin, passp) -fr_info_t *fin; -u_32_t *passp; +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; - ipnat_t *np; nat_t *nat; u_32_t iph; - if (nat_stats.ns_rules == 0 || fr_nat_lock != 0) + 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; @@ -4085,182 +5395,213 @@ u_32_t *passp; * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ - if (nat_icmpquerytype4(icmp->icmp_type)) { + if ((fin->fin_flx & FI_ICMPQUERY) != 0) { nflags = IPN_ICMPQUERY; - dport = icmp->icmp_id; + dport = icmp->icmp_id; } break; default : break; } - + if ((nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; - dport = tcp->th_dport; + dport = fin->fin_data[1]; } } in = fin->fin_dst; - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); - if (((fin->fin_flx & FI_ICMPERR) != 0) && - (nat = nat_icmperror(fin, &nflags, NAT_INBOUND))) + 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 = fr_nat_knownfrag(fin))) + else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; - else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, - fin->fin_src, in))) { + else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, + (u_int)fin->fin_p, + fin->fin_src, in))) { nflags = nat->nat_flags; - } else { - u_32_t hv, msk, rmsk; + } else if (fin->fin_off == 0) { + u_32_t hv, msk, rmsk = 0; - RWLOCK_EXIT(&ipf_nat); - rmsk = rdr_masks; - msk = 0xffffffff; - WRITE_ENTER(&ipf_nat); /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: - iph = in.s_addr & htonl(msk); - hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); - for (np = rdr_rules[hv]; np; np = np->in_rnext) { + 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 != fin->fin_v) + if (np->in_v[0] != 4) continue; - if (np->in_p && (np->in_p != fin->fin_p)) + 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) { - if (!nat_match(fin, np)) + switch (ipf_nat_match(fin, np)) + { + case 0 : continue; + case -1 : + rval = -1; + goto inmatchfail; + case 1 : + default : + break; + } } else { - if ((in.s_addr & np->in_outmsk) != np->in_outip) + if ((in.s_addr & np->in_odstmsk) != + np->in_odstaddr) continue; - if (np->in_pmin && - ((ntohs(np->in_pmax) < ntohs(dport)) || - (ntohs(dport) < ntohs(np->in_pmin)))) + if (np->in_odport && + ((np->in_dtop < dport) || + (dport < np->in_odport))) continue; } - if (*np->in_plabel != '\0') { - if (!appr_ok(fin, tcp, np)) { + if (np->in_plabel != -1) { + if (!ipf_proxy_ok(fin, tcp, np)) { continue; } } - nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND); - if (nat != NULL) { + if (np->in_flags & IPN_NO) { np->in_hits++; break; - } else - natfailed = -1; - } + } - if ((np == NULL) && (rmsk != 0)) { - while (rmsk) { - msk <<= 1; - if (rmsk & 0x80000000) - break; - rmsk <<= 1; + 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; } - if (rmsk != 0) { - rmsk <<= 1; - goto maskloop; + + nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); + MUTEX_EXIT(&softn->ipf_nat_new); + if (nat != NULL) { + natfailed = 0; + break; } + natfailed = -1; + } + if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { + rmsk++; + goto maskloop; } - MUTEX_DOWNGRADE(&ipf_nat); } + if (nat != NULL) { - rval = fr_natin(fin, nat, natadd, nflags); + rval = ipf_nat_in(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); - nat->nat_ref++; + 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); - nat->nat_touched = fr_ticks; - fin->fin_nat = nat; } } else rval = natfailed; - RWLOCK_EXIT(&ipf_nat); +inmatchfail: + RWLOCK_EXIT(&softc->ipf_nat); - if (rval == -1) { - if (passp != NULL) + switch (rval) + { + case -1 : + if (passp != NULL) { + DT1(frb_natv4in, fr_info_t *, fin); + NBUMPSIDED(0, ns_drop); *passp = FR_BLOCK; + fin->fin_reason = FRB_NATV4; + } fin->fin_flx |= FI_BADNAT; + NBUMPSIDED(0, ns_badnat); + break; + case 0 : + NBUMPSIDE(0, ns_ignored); + break; + case 1 : + NBUMPSIDE(0, ns_translated); + break; } return rval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_natin */ +/* 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) */ +/* Locks Held: ipf_nat(READ) */ /* */ /* Translate a packet coming "in" on an interface. */ /* ------------------------------------------------------------------------ */ -int fr_natin(fin, nat, natadd, nflags) -fr_info_t *fin; -nat_t *nat; -int natadd; -u_32_t nflags; +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; - 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) fr_nat_newfrag(fin, 0, nat); + (void) ipf_frag_natnew(softc, fin, 0, nat); /* ------------------------------------------------------------- */ - /* A few quick notes: */ - /* Following are test conditions prior to calling the */ - /* appr_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. */ + /* 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 = appr_check(fin, nat); + i = ipf_proxy_check(fin, nat); if (i == -1) { + NBUMPSIDED(0, ns_ipf_proxy_fail); return -1; } } } -#ifdef IPFILTER_SYNC - ipfsync_update(SMC_NAT, fin, nat->nat_sync); -#endif - - MUTEX_ENTER(&nat->nat_lock); - nat->nat_bytes[0] += fin->fin_plen; - nat->nat_pkts[0]++; - MUTEX_EXIT(&nat->nat_lock); - - fin->fin_ip->ip_dst = nat->nat_inip; - fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; - if (nflags & IPN_TCPUDP) - tcp = fin->fin_dp; + 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. @@ -4271,42 +5612,166 @@ u_32_t nflags; * 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) || defined(__sgi) || \ defined(__osf__) || defined(linux) - if (nat->nat_dir == NAT_OUTBOUND) - fix_incksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); - else - fix_outcksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd); + ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); #endif + break; - if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { - if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { - tcp->th_dport = nat->nat_inport; - fin->fin_data[1] = ntohs(nat->nat_inport); + 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) || defined(__sgi) || \ + defined(__osf__) || defined(linux) + 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; - if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { - icmp = fin->fin_dp; + m = M_DUP(np->in_divmp); + if (m == NULL) { + NBUMPSIDED(0, ns_divert_dup); + return -1; + } + + ip = MTOD(m, ip_t *); + ip->ip_id = htons(ipf_nextipid(fin)); + 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); - icmp->icmp_id = nat->nat_inport; + 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) || defined(__sgi) || \ + defined(__osf__) || defined(linux) + 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; } - csump = nat_proto(fin, nat, nflags); + 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; - nat_update(fin, nat, np); + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + u_short *csump; - /* - * The above comments do not hold for layer 4 (or higher) checksums... - */ - if (csump != NULL) { - if (nat->nat_dir == NAT_OUTBOUND) - fix_incksum(fin, csump, nat->nat_sumd[0]); - else - fix_outcksum(fin, csump, nat->nat_sumd[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)) { + 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); + } } - ATOMIC_INCL(nat_stats.ns_mapped[0]); + fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; @@ -4315,7 +5780,7 @@ u_32_t nflags; /* ------------------------------------------------------------------------ */ -/* Function: nat_proto */ +/* 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. */ @@ -4328,10 +5793,11 @@ u_32_t nflags; /* 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 *nat_proto(fin, nat, nflags) -fr_info_t *fin; -nat_t *nat; -u_int nflags; +u_short * +ipf_nat_proto(fin, nat, nflags) + fr_info_t *fin; + nat_t *nat; + u_int nflags; { icmphdr_t *icmp; u_short *csump; @@ -4340,9 +5806,9 @@ u_int nflags; csump = NULL; if (fin->fin_out == 0) { - fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND); + fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); } else { - fin->fin_rev = (nat->nat_dir == NAT_INBOUND); + fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); } switch (fin->fin_p) @@ -4350,22 +5816,25 @@ u_int nflags; case IPPROTO_TCP : tcp = fin->fin_dp; - csump = &tcp->th_sum; + 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) - nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); + ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); break; case IPPROTO_UDP : udp = fin->fin_dp; - if (udp->uh_sum) - csump = &udp->uh_sum; + if ((nflags & IPN_UDP) != 0) { + if (udp->uh_sum != 0) + csump = &udp->uh_sum; + } break; case IPPROTO_ICMP : @@ -4376,165 +5845,108 @@ u_int nflags; csump = &icmp->icmp_cksum; } break; - } - return csump; -} - -/* ------------------------------------------------------------------------ */ -/* Function: fr_natunload */ -/* Returns: Nil */ -/* Parameters: Nil */ -/* */ -/* Free all memory used by NAT structures allocated at runtime. */ -/* ------------------------------------------------------------------------ */ -void fr_natunload() -{ - ipftq_t *ifq, *ifqnext; - - (void) nat_clearlist(); - (void) nat_flushtable(); - - /* - * Proxy timeout queues are not cleaned here because although they - * exist on the NAT list, appr_unload is called after fr_natunload - * 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 = nat_utqe; ifq != NULL; ifq = ifqnext) { - ifqnext = ifq->ifq_next; - if (((ifq->ifq_flags & IFQF_PROXY) == 0) && - (fr_deletetimeoutqueue(ifq) == 0)) - fr_freetimeoutqueue(ifq); - } - - if (nat_table[0] != NULL) { - KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); - nat_table[0] = NULL; - } - if (nat_table[1] != NULL) { - KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); - nat_table[1] = NULL; - } - if (nat_rules != NULL) { - KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); - nat_rules = NULL; - } - if (rdr_rules != NULL) { - KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); - rdr_rules = NULL; - } - if (ipf_hm_maptable != NULL) { - KFREES(ipf_hm_maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); - ipf_hm_maptable = NULL; - } - if (nat_stats.ns_bucketlen[0] != NULL) { - KFREES(nat_stats.ns_bucketlen[0], - sizeof(u_long *) * ipf_nattable_sz); - nat_stats.ns_bucketlen[0] = NULL; - } - if (nat_stats.ns_bucketlen[1] != NULL) { - KFREES(nat_stats.ns_bucketlen[1], - sizeof(u_long *) * ipf_nattable_sz); - nat_stats.ns_bucketlen[1] = NULL; - } - - if (fr_nat_maxbucket_reset == 1) - fr_nat_maxbucket = 0; - - if (fr_nat_init == 1) { - fr_nat_init = 0; - fr_sttab_destroy(nat_tqb); - - RW_DESTROY(&ipf_natfrag); - RW_DESTROY(&ipf_nat); +#ifdef USE_INET6 + case IPPROTO_ICMPV6 : + { + struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; - MUTEX_DESTROY(&ipf_nat_new); - MUTEX_DESTROY(&ipf_natio); + icmp6 = fin->fin_dp; - MUTEX_DESTROY(&nat_udptq.ifq_lock); - MUTEX_DESTROY(&nat_icmptq.ifq_lock); - MUTEX_DESTROY(&nat_iptq.ifq_lock); + if ((nflags & IPN_ICMPQUERY) != 0) { + if (icmp6->icmp6_cksum != 0) + csump = &icmp6->icmp6_cksum; + } + break; + } +#endif } + return csump; } /* ------------------------------------------------------------------------ */ -/* Function: fr_natexpire */ +/* Function: ipf_nat_expire */ /* Returns: Nil */ -/* Parameters: 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 fr_natexpire() +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(&ipf_nat); - for (ifq = nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) { + 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 > fr_ticks) + if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; - nat_delete(tqe->tqe_parent, NL_EXPIRE); + ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } - for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { - ifqnext = ifq->ifq_next; - + 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 > fr_ticks) + if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; - nat_delete(tqe->tqe_parent, NL_EXPIRE); + ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } - for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { + 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)) { - fr_freetimeoutqueue(ifq); + ipf_freetimeoutqueue(softc, ifq); } } - if (fr_nat_doflush != 0) { - nat_extraflush(2); - fr_nat_doflush = 0; + if (softn->ipf_nat_doflush != 0) { + ipf_nat_extraflush(softc, softn, 2); + softn->ipf_nat_doflush = 0; } - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: fr_natsync */ +/* Function: ipf_nat_sync */ /* Returns: Nil */ -/* Parameters: ifp(I) - pointer to network interface */ +/* 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 fr_natsync(ifp) -void *ifp; +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; - struct in_addr in; + i6addr_t in; ipnat_t *n; nat_t *nat; void *ifp2; + int idx; SPL_INT(s); - if (fr_running <= 0) + if (softc->ipf_running <= 0) return; /* @@ -4544,28 +5956,63 @@ void *ifp; * where the rule specifies the address is taken from the interface. */ SPL_NET(s); - WRITE_ENTER(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); - if (fr_running <= 0) { - RWLOCK_EXIT(&ipf_nat); + if (softc->ipf_running <= 0) { + RWLOCK_EXIT(&softc->ipf_nat); return; } - for (nat = nat_instances; nat; nat = nat->nat_next) { + 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) || - (n->in_outip != 0) || (n->in_outmsk != 0xffffffff)) - continue; + 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], 4); + 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], - 4); - } else + 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; @@ -4574,10 +6021,13 @@ void *ifp; * Change the map-to address to be the same as the * new one. */ - sum1 = nat->nat_outip.s_addr; - if (fr_ifpaddr(4, FRI_NORMAL, ifp2, &in, NULL) != -1) - nat->nat_outip = in; - sum2 = nat->nat_outip.s_addr; + 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; @@ -4595,27 +6045,53 @@ void *ifp; } } - for (n = nat_list; (n != NULL); n = n->in_next) { + 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] = fr_resolvenic(n->in_ifnames[0], 4); + 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] = fr_resolvenic(n->in_ifnames[1], 4); + 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(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: nat_icmpquerytype4 */ +/* 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 nat_icmpquerytype4(icmptype) -int icmptype; +static int +ipf_nat_icmpquerytype(icmptype) + int icmptype; { /* @@ -4627,10 +6103,8 @@ int icmptype; * 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 aedvertisement/solliciation is currently unsupported: */ @@ -4651,14 +6125,19 @@ int icmptype; /* ------------------------------------------------------------------------ */ /* Function: nat_log */ /* Returns: Nil */ -/* Parameters: nat(I) - pointer to NAT structure */ -/* type(I) - type of log entry to create */ +/* 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 nat_log(nat, type) -struct nat *nat; -u_int type; +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 @@ -4670,22 +6149,40 @@ u_int type; size_t sizes[1]; int types[1]; - natl.nl_inip = nat->nat_inip; - natl.nl_outip = nat->nat_outip; - natl.nl_origip = nat->nat_oip; + 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_origport = nat->nat_oport; - natl.nl_inport = nat->nat_inport; - natl.nl_outport = nat->nat_outport; - natl.nl_p = nat->nat_p; - natl.nl_type = type; + 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 = nat_list; np; np = np->in_next, rulen++) + 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; @@ -4696,63 +6193,108 @@ u_int type; sizes[0] = sizeof(natl); types[0] = 0; - (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); + (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); #endif } #if defined(__OpenBSD__) /* ------------------------------------------------------------------------ */ -/* Function: nat_ifdetach */ +/* Function: ipf_nat_ifdetach */ /* Returns: Nil */ /* Parameters: ifp(I) - pointer to network interface */ /* */ /* Compatibility interface for OpenBSD to trigger the correct updating of */ /* interface references within IPFilter. */ /* ------------------------------------------------------------------------ */ -void nat_ifdetach(ifp) -void *ifp; +void +ipf_nat_ifdetach(ifp) + void *ifp; { - frsync(ifp); + ipf_main_softc_t *softc; + + softc = ipf_get_softc(0); + + ipf_sync(ifp); return; } #endif /* ------------------------------------------------------------------------ */ -/* Function: fr_ipnatderef */ +/* Function: ipf_nat_rule_deref */ /* Returns: Nil */ -/* Parameters: isp(I) - pointer to pointer to NAT rule */ +/* 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 fr_ipnatderef(inp) -ipnat_t **inp; +void +ipf_nat_rule_deref(softc, inp) + ipf_main_softc_t *softc; + ipnat_t **inp; { - ipnat_t *in; + ipf_nat_softc_t *softn = softc->ipf_nat_soft; + ipnat_t *n; - in = *inp; + n = *inp; *inp = NULL; - in->in_space++; - in->in_use--; - if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) { - if (in->in_apr) - appr_free(in->in_apr); - MUTEX_DESTROY(&in->in_lock); - KFREE(in); - nat_stats.ns_rules--; -#if SOLARIS && !defined(_INET_IP_STACK_H) - if (nat_stats.ns_rules == 0) - pfil_delayed_copy = 1; -#endif + 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: fr_natderef */ +/* Function: ipf_nat_deref */ /* Returns: Nil */ -/* Parameters: isp(I) - pointer to pointer to NAT table entry */ +/* 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. */ @@ -4765,8 +6307,10 @@ ipnat_t **inp; /* 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 fr_natderef(natp) -nat_t **natp; +void +ipf_nat_deref(softc, natp) + ipf_main_softc_t *softc; + nat_t **natp; { nat_t *nat; @@ -4776,19 +6320,20 @@ nat_t **natp; 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(&ipf_nat); - nat_delete(nat, NL_EXPIRE); - RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_delete(softc, nat, NL_EXPIRE); + RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ -/* Function: fr_natclone */ +/* Function: ipf_nat_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ @@ -4797,24 +6342,30 @@ nat_t **natp; /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ -static nat_t *fr_natclone(fin, nat) -fr_info_t *fin; -nat_t *nat; +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) + 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 nat_delete() doesn't cause a crash. + * 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; @@ -4827,14 +6378,16 @@ nat_t *nat; if (clone->nat_hm) clone->nat_hm->hm_ref++; - if (nat_insert(clone, fin->fin_rev) == -1) { + 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 (nat_logging) - nat_log(clone, (u_int)np->in_redir); + if (softn->ipf_nat_logging) + ipf_nat_log(softc, softn, clone, NL_CLONE); np->in_use++; } fr = clone->nat_fr; @@ -4844,26 +6397,25 @@ nat_t *nat; 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_p == IPPROTO_TCP) { - (void) fr_tcp_age(&clone->nat_tqe, fin, nat_tqb, - clone->nat_flags); + if (clone->nat_pr[0] == IPPROTO_TCP) { + (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, + clone->nat_flags, 2); } -#ifdef IPFILTER_SYNC - clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone); -#endif - if (nat_logging) - nat_log(clone, NL_CLONE); + 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: nat_wildok */ +/* 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 */ @@ -4875,12 +6427,10 @@ nat_t *nat; /* Use NAT entry and packet direction to determine which combination of */ /* wildcard flags should be used. */ /* ------------------------------------------------------------------------ */ -static int nat_wildok(nat, sport, dport, flags, dir) -nat_t *nat; -int sport; -int dport; -int flags; -int dir; +int +ipf_nat_wildok(nat, sport, dport, flags, dir) + nat_t *nat; + int sport, dport, flags, dir; { /* * When called by dir is set to @@ -4891,34 +6441,33 @@ int dir; * "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) + switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) { case 3: /* outbound packet / outbound entry */ - if (((nat->nat_inport == sport) || + if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && - ((nat->nat_oport == dport) || + ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return 1; break; case 2: /* outbound packet / inbound entry */ - if (((nat->nat_outport == sport) || - (flags & SI_W_DPORT)) && - ((nat->nat_oport == dport) || - (flags & SI_W_SPORT))) + 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_oport == sport) || - (flags & SI_W_DPORT)) && - ((nat->nat_outport == dport) || - (flags & SI_W_SPORT))) + 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_oport == sport) || + if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && - ((nat->nat_outport == dport) || + ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return 1; break; @@ -4942,11 +6491,12 @@ int dir; /* then the TCP header checksum will be updated to reflect the change in */ /* the MSS. */ /* ------------------------------------------------------------------------ */ -static void nat_mssclamp(tcp, maxmss, fin, csump) -tcphdr_t *tcp; -u_32_t maxmss; -fr_info_t *fin; -u_short *csump; +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; @@ -4981,7 +6531,7 @@ u_short *csump; cp[2] = maxmss / 256; cp[3] = maxmss & 0xff; CALC_SUMD(mss, maxmss, sumd); - fix_outcksum(fin, csump, sumd); + ipf_fix_outcksum(0, csump, sumd, 0); } break; default: @@ -4996,20 +6546,24 @@ u_short *csump; /* ------------------------------------------------------------------------ */ -/* Function: fr_setnatqueue */ +/* Function: ipf_nat_setqueue */ /* Returns: Nil */ -/* Parameters: nat(I)- pointer to NAT structure */ -/* rev(I) - forward(0) or reverse(1) direction */ +/* 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 fr_setnatqueue(nat, rev) -nat_t *nat; -int rev; +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]; @@ -5017,19 +6571,20 @@ int rev; nifq = NULL; if (nifq == NULL) { - switch (nat->nat_p) + switch (nat->nat_pr[0]) { case IPPROTO_UDP : - nifq = &nat_udptq; + nifq = &softn->ipf_nat_udptq; break; case IPPROTO_ICMP : - nifq = &nat_icmptq; + nifq = &softn->ipf_nat_icmptq; break; case IPPROTO_TCP : - nifq = nat_tqb + nat->nat_tqe.tqe_state[rev]; + nifq = softn->ipf_nat_tcptq + + nat->nat_tqe.tqe_state[rev]; break; default : - nifq = &nat_iptq; + nifq = &softn->ipf_nat_iptq; break; } } @@ -5040,9 +6595,9 @@ int rev; * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) - fr_movequeue(&nat->nat_tqe, oifq, nifq); + ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); else - fr_queueappend(&nat->nat_tqe, nifq, nat); + ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); return; } @@ -5050,245 +6605,142 @@ int rev; /* ------------------------------------------------------------------------ */ /* Function: nat_getnext */ /* Returns: int - 0 == ok, else error */ -/* Parameters: t(I) - pointer to ipftoken structure */ +/* 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. */ -/* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/ -/* ipf_freetoken will call a deref function for us and we dont want to call */ -/* that twice (second time would be in the second switch statement below. */ /* ------------------------------------------------------------------------ */ -static int nat_getnext(t, itp) -ipftoken_t *t; -ipfgeniter_t *itp; +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, count; - char *dst; + int error = 0; + void *nnext; - count = itp->igi_nitems; - if (count < 1) + if (itp->igi_nitems != 1) { + IPFERROR(60075); return ENOSPC; + } - READ_ENTER(&ipf_nat); + READ_ENTER(&softc->ipf_nat); switch (itp->igi_type) { case IPFGENITER_HOSTMAP : hm = t->ipt_data; if (hm == NULL) { - nexthm = ipf_hm_maplist; + 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 = nat_list; + 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 = nat_instances; + 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(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); + IPFERROR(60055); return EINVAL; } - dst = itp->igi_data; - for (;;) { - switch (itp->igi_type) - { - case IPFGENITER_HOSTMAP : - if (nexthm != NULL) { - if (count == 1) { - ATOMIC_INC32(nexthm->hm_ref); - t->ipt_data = nexthm; - } - } else { - bzero(&zerohm, sizeof(zerohm)); - nexthm = &zerohm; - count = 1; - t->ipt_data = NULL; - } - break; - - case IPFGENITER_IPNAT : - if (nextipnat != NULL) { - if (count == 1) { - MUTEX_ENTER(&nextipnat->in_lock); - nextipnat->in_use++; - MUTEX_EXIT(&nextipnat->in_lock); - t->ipt_data = nextipnat; - } - } else { - bzero(&zeroipn, sizeof(zeroipn)); - nextipnat = &zeroipn; - count = 1; - t->ipt_data = NULL; - } - break; - - case IPFGENITER_NAT : - if (nextnat != NULL) { - if (count == 1) { - MUTEX_ENTER(&nextnat->nat_lock); - nextnat->nat_ref++; - MUTEX_EXIT(&nextnat->nat_lock); - t->ipt_data = nextnat; - } - } else { - bzero(&zeronat, sizeof(zeronat)); - nextnat = &zeronat; - count = 1; - t->ipt_data = NULL; - } - break; - default : - break; - } - RWLOCK_EXIT(&ipf_nat); - - /* - * Copying out to user space needs to be done without the lock. - */ - switch (itp->igi_type) - { - case IPFGENITER_HOSTMAP : - error = COPYOUT(nexthm, dst, sizeof(*nexthm)); - if (error != 0) - error = EFAULT; - else - dst += sizeof(*nexthm); - break; - - case IPFGENITER_IPNAT : - error = COPYOUT(nextipnat, dst, sizeof(*nextipnat)); - if (error != 0) - error = EFAULT; - else - dst += sizeof(*nextipnat); - break; - - case IPFGENITER_NAT : - error = COPYOUT(nextnat, dst, sizeof(*nextnat)); - if (error != 0) - error = EFAULT; - else - dst += sizeof(*nextnat); - break; - } - - if ((count == 1) || (error != 0)) - break; - - count--; - - READ_ENTER(&ipf_nat); - - /* - * We need to have the lock again here to make sure that - * using _next is consistent. - */ - switch (itp->igi_type) - { - case IPFGENITER_HOSTMAP : - nexthm = nexthm->hm_next; - break; - case IPFGENITER_IPNAT : - nextipnat = nextipnat->in_next; - break; - case IPFGENITER_NAT : - nextnat = nextnat->nat_next; - break; - } - } + 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(&ipf_nat); - fr_hostmapdel(&hm); - RWLOCK_EXIT(&ipf_nat); + 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) { - fr_ipnatderef(&ipn); - } - break; - case IPFGENITER_NAT : - if (nat != NULL) { - fr_natderef(&nat); + WRITE_ENTER(&softc->ipf_nat); + ipf_nat_rule_deref(softc, &ipn); + RWLOCK_EXIT(&softc->ipf_nat); } break; - default : - break; - } - - return error; -} - - -/* ------------------------------------------------------------------------ */ -/* Function: nat_iterator */ -/* Returns: int - 0 == ok, else error */ -/* Parameters: token(I) - pointer to ipftoken structure */ -/* itp(I) - pointer to ipfgeniter_t 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 nat_iterator(token, itp) -ipftoken_t *token; -ipfgeniter_t *itp; -{ - int error; - if (itp->igi_data == NULL) - return EFAULT; - - token->ipt_subtype = itp->igi_type; - - switch (itp->igi_type) - { - case IPFGENITER_HOSTMAP : - case IPFGENITER_IPNAT : case IPFGENITER_NAT : - error = nat_getnext(token, itp); - break; + 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); - case IPFGENITER_NATFRAG : -#ifdef USE_MUTEXES - error = fr_nextfrag(token, itp, &ipfr_natlist, - &ipfr_nattail, &ipf_natfrag); -#else - error = fr_nextfrag(token, itp, &ipfr_natlist, &ipfr_nattail); -#endif - break; - default : - error = EINVAL; break; } + if (nnext == NULL) + ipf_token_mark_complete(t); + return error; } @@ -5296,7 +6748,9 @@ ipfgeniter_t *itp; /* ------------------------------------------------------------------------ */ /* Function: nat_extraflush */ /* Returns: int - 0 == success, -1 == failure */ -/* Parameters: which(I) - how to flush the active NAT table */ +/* 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: */ @@ -5310,45 +6764,51 @@ ipfgeniter_t *itp; /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ -static int nat_extraflush(which) -int which; +static int +ipf_nat_extraflush(softc, softn, which) + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + int which; { - ipftq_t *ifq, *ifqnext; 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 = &nat_instances; ((nat = *natp) != NULL); ) { - nat_delete(nat, NL_FLUSH); + 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 = nat_tqb + IPF_TCPS_CLOSE_WAIT; ifq != NULL; - ifq = ifq->ifq_next) { + 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_p != IPPROTO_TCP) + if (nat->nat_pr[0] != IPPROTO_TCP || + nat->nat_pr[1] != IPPROTO_TCP) break; - nat_delete(nat, NL_EXPIRE); + ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } @@ -5356,19 +6816,20 @@ int which; /* * Also need to look through the user defined queues. */ - for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) { - ifqnext = ifq->ifq_next; + 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_p != IPPROTO_TCP) + 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)) { - nat_delete(nat, NL_EXPIRE); + ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } @@ -5386,28 +6847,31 @@ int which; case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : - tqn = nat_tqb[which].ifq_head; + 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; - nat_delete(nat, NL_FLUSH); + 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 = &nat_instances; ((nat = *natp) != NULL); ) { - if (fr_ticks - nat->nat_touched > which) { - nat_delete(nat, NL_FLUSH); + 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; @@ -5420,12 +6884,25 @@ int which; return removed; } + softn->ipf_nat_stats.ns_flush_queue++; + /* - * Asked to remove inactive entries because the table is full. + * 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 (fr_ticks - nat_last_force_flush > IPF_TTLVAL(5)) { - nat_last_force_flush = fr_ticks; - removed = ipf_queueflush(nat_flush_entry, nat_tqb, nat_utqe); + 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); @@ -5434,9 +6911,10 @@ int which; /* ------------------------------------------------------------------------ */ -/* Function: nat_flush_entry */ +/* Function: ipf_nat_flush_entry */ /* Returns: 0 - always succeeds */ -/* Parameters: entry(I) - pointer to NAT entry */ +/* 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 */ @@ -5444,50 +6922,1673 @@ int which; /* ipf_queueflush() function. Since the nat_delete() function returns void */ /* we translate that to mean it always succeeds in deleting something. */ /* ------------------------------------------------------------------------ */ -static int nat_flush_entry(entry) -void *entry; +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; { - nat_delete(entry, NL_FLUSH); + 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) */ + + 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) */ + + 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) */ + + /* + * 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 */ + + 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_gettable */ +/* 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); + 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) || defined(__sgi) || \ + defined(__osf__) || defined(linux) + 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 : + 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); + } + + } 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); + return -1; + } + new = newip.in4.s_addr; + } else { + new = htonl(na->na_nextip); + } + *dst = new; + error = 0; + + } else { + NBUMPSIDE(fin->fin_out, ns_badnextaddr); + } + + 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: data(I) - pointer to ioctl data */ +/* 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 nat_gettable(data) -char *data; +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 = fr_inobj(data, &table, IPFOBJ_GTABLE); + error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return error; switch (table.ita_type) { case IPFTABLE_BUCKETS_NATIN : - error = COPYOUT(nat_stats.ns_bucketlen[0], table.ita_table, - ipf_nattable_sz * sizeof(u_long)); + 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(nat_stats.ns_bucketlen[1], table.ita_table, - ipf_nattable_sz * sizeof(u_long)); + 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 == NULL) { + error = 60063; + goto badrehash; + } + + KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); + if (newtab == 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 c8581ef..a57bf0c 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.h +++ b/sys/contrib/ipfilter/netinet/ip_nat.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -22,11 +22,13 @@ #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) #else #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) #endif #undef LARGE_NAT /* define this if you're setting up a system to NAT @@ -78,13 +80,18 @@ #ifndef APR_LABELLEN #define APR_LABELLEN 16 #endif -#define NAT_HW_CKSUM 0x80000000 +#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; @@ -101,13 +108,15 @@ typedef struct nat { 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_inip6; - i6addr_t nat_outip6; - i6addr_t nat_oip6; /* other ip */ + 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 { @@ -115,28 +124,37 @@ typedef struct nat { tcpinfo_t nat_unt; icmpinfo_t nat_uni; greinfo_t nat_ugre; - } nat_un; - u_short nat_oport; /* other port */ - u_short nat_use; - u_char nat_p; /* protocol for NAT */ + } nat_unold, nat_unnew; + int nat_use; + int nat_pr[2]; /* protocol for NAT */ int nat_dir; int nat_ref; /* reference count */ - int nat_hv[2]; + u_int nat_hv[2]; char nat_ifnames[2][LIFNAMSIZ]; int nat_rev; /* 0 = forward, 1 = reverse */ - int nat_redir; /* copy of in_redir */ - u_32_t nat_seqnext[2]; + int nat_dlocal; + int nat_v[2]; /* 0 = old, 1 = new */ + u_int nat_redir; /* copy of in_redir */ } nat_t; -#define nat_inip nat_inip6.in4 -#define nat_outip nat_outip6.in4 -#define nat_oip nat_oip6.in4 +#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_inport nat_un.nat_unt.ts_sport -#define nat_outport nat_un.nat_unt.ts_dport -#define nat_type nat_un.nat_uni.ici_type -#define nat_seq nat_un.nat_uni.ici_seq -#define nat_id nat_un.nat_uni.ici_id +#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 @@ -146,6 +164,10 @@ typedef struct nat { */ #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 @@ -174,9 +196,29 @@ typedef struct nat { #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 */ @@ -185,49 +227,114 @@ typedef struct ipnat { void *in_ifps[2]; void *in_apr; char *in_comment; - i6addr_t in_next6; + 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; - u_int in_use; - u_int in_hv; + int in_size; + int in_use; + u_int in_hv[2]; int in_flineno; /* conf. file line number */ - u_short in_pnext; - u_char in_v; - u_char in_xxx; + 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_p; /* protocol. */ - i6addr_t in_in[2]; - i6addr_t in_out[2]; - i6addr_t in_src[2]; + 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_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. */ + 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; -#define in_pmin in_port[0] /* Also holds static redir port */ -#define in_pmax in_port[1] -#define in_nextip in_next6.in4 -#define in_nip in_next6.in4.s_addr -#define in_inip in_in[0].in4.s_addr -#define in_inmsk in_in[1].in4.s_addr -#define in_outip in_out[0].in4.s_addr -#define in_outmsk in_out[1].in4.s_addr -#define in_srcip in_src[0].in4.s_addr -#define in_srcmsk in_src[1].in4.s_addr +/* + * 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_sport in_tuc.ftu_sport -#define in_dport in_tuc.ftu_dport +#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 @@ -242,25 +349,30 @@ typedef struct ipnat { #define IPN_TCPUDPICMPQ (IPN_TCP|IPN_UDP|IPN_ICMPQUERY) #define IPN_RF (IPN_TCPUDP|IPN_DELETE|IPN_ICMPERR) #define IPN_AUTOPORTMAP 0x00010 -#define IPN_IPRANGE 0x00020 -#define IPN_FILTER 0x00040 -#define IPN_SPLIT 0x00080 -#define IPN_ROUNDR 0x00100 -#define IPN_NOTSRC 0x04000 -#define IPN_NOTDST 0x08000 -#define IPN_DYNSRCIP 0x10000 /* dynamic src IP# */ -#define IPN_DYNDSTIP 0x20000 /* dynamic dst IP# */ -#define IPN_DELETE 0x40000 -#define IPN_STICKY 0x80000 -#define IPN_FRAG 0x100000 -#define IPN_FIXEDDPORT 0x200000 -#define IPN_FINDFORWARD 0x400000 -#define IPN_IN 0x800000 -#define IPN_SEQUENTIAL 0x1000000 -#define IPN_USERFLAGS (IPN_TCPUDP|IPN_AUTOPORTMAP|IPN_IPRANGE|IPN_SPLIT|\ - IPN_ROUNDR|IPN_FILTER|IPN_NOTSRC|IPN_NOTDST|\ +#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_SEQUENTIAL) + IPN_DIPRANGE|IPN_SEQUENTIAL|IPN_PURGE) /* * Values for in_redir @@ -269,22 +381,33 @@ typedef struct ipnat { #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_flags)) +#define IPN_CMPSIZ (sizeof(ipnat_t) - offsetof(ipnat_t, in_v)) typedef struct natlookup { - struct in_addr nl_inip; - struct in_addr nl_outip; - struct in_addr nl_realip; - int nl_flags; - u_short nl_inport; - u_short nl_outport; - u_short nl_realport; + 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; @@ -315,13 +438,25 @@ typedef struct hostmap { struct hostmap *hm_next; struct hostmap **hm_pnext; struct ipnat *hm_ipnat; - struct in_addr hm_srcip; - struct in_addr hm_dstip; - struct in_addr hm_mapip; + 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. @@ -330,9 +465,7 @@ typedef struct natinfo { ipnat_t *nai_np; u_32_t nai_sum1; u_32_t nai_sum2; - u_32_t nai_nflags; - u_32_t nai_flags; - struct in_addr nai_ip; + struct in_addr nai_ip; /* In host byte order */ u_short nai_port; u_short nai_nport; u_short nai_sport; @@ -340,62 +473,134 @@ typedef struct natinfo { } natinfo_t; -typedef struct natstat { - u_long ns_mapped[2]; - u_long ns_rules; +typedef struct nat_stat_side { + u_int *ns_bucketlen; + nat_t **ns_table; u_long ns_added; - u_long ns_expire; + 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_logged; - u_long ns_logfail; + 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_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_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 { - struct in_addr nl_origip; - struct in_addr nl_outip; - struct in_addr nl_inip; - u_short nl_origport; - u_short nl_outport; - u_short nl_inport; - u_short nl_type; - int nl_rule; + 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; + u_char nl_p[2]; + u_char nl_v[2]; + u_char nl_ifnames[2][LIFNAMSIZ]; } natlog_t; -#define NL_NEWMAP NAT_MAP -#define NL_NEWRDR NAT_REDIRECT -#define NL_NEWBIMAP NAT_BIMAP -#define NL_NEWBLOCK NAT_MAPBLK -#define NL_DESTROY 0xfffc -#define NL_CLONE 0xfffd +#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 LONG_SUM(in) (((in) & 0xffff) + ((in) >> 16)) +#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); \ @@ -411,64 +616,159 @@ typedef struct natlog { #define NAT_SYSSPACE 0x80000000 #define NAT_LOCKHELD 0x40000000 - -extern u_int ipf_nattable_sz; -extern u_int ipf_nattable_max; -extern u_int ipf_natrules_sz; -extern u_int ipf_rdrrules_sz; -extern u_int ipf_hostmap_sz; -extern u_int fr_nat_maxbucket; -extern u_int fr_nat_maxbucket_reset; -extern int fr_nat_lock; -extern int fr_nat_doflush; -extern void fr_natsync __P((void *)); -extern u_long fr_defnatage; -extern u_long fr_defnaticmpage; -extern u_long fr_defnatipage; - /* nat_table[0] -> hashed list sorted by inside (ip, port) */ - /* nat_table[1] -> hashed list sorted by outside (ip, port) */ -extern nat_t **nat_table[2]; -extern nat_t *nat_instances; -extern ipnat_t *nat_list; -extern ipnat_t **nat_rules; -extern ipnat_t **rdr_rules; -extern ipftq_t *nat_utqe; -extern natstat_t nat_stats; - +/* + * 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)); #if defined(__OpenBSD__) -extern void nat_ifdetach __P((void *)); +extern void ipf_nat_ifdetach __P((void *)); #endif -extern int fr_nat_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern int fr_natinit __P((void)); -extern nat_t *nat_new __P((fr_info_t *, ipnat_t *, nat_t **, u_int, int)); -extern nat_t *nat_outlookup __P((fr_info_t *, u_int, u_int, struct in_addr, - struct in_addr)); -extern void fix_datacksum __P((u_short *, u_32_t)); -extern nat_t *nat_inlookup __P((fr_info_t *, u_int, u_int, struct in_addr, - struct in_addr)); -extern nat_t *nat_tnlookup __P((fr_info_t *, int)); -extern nat_t *nat_maplookup __P((void *, u_int, struct in_addr, +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 *nat_lookupredir __P((natlookup_t *)); -extern nat_t *nat_icmperrorlookup __P((fr_info_t *, int)); -extern nat_t *nat_icmperror __P((fr_info_t *, u_int *, int)); -extern void nat_delete __P((struct nat *, int)); -extern int nat_insert __P((nat_t *, int)); - -extern int fr_checknatout __P((fr_info_t *, u_32_t *)); -extern int fr_natout __P((fr_info_t *, nat_t *, int, u_32_t)); -extern int fr_checknatin __P((fr_info_t *, u_32_t *)); -extern int fr_natin __P((fr_info_t *, nat_t *, int, u_32_t)); -extern void fr_natunload __P((void)); -extern void fr_natexpire __P((void)); -extern void nat_log __P((struct nat *, u_int)); -extern void fix_incksum __P((fr_info_t *, u_short *, u_32_t)); -extern void fix_outcksum __P((fr_info_t *, u_short *, u_32_t)); -extern void fr_ipnatderef __P((ipnat_t **)); -extern void fr_natderef __P((nat_t **)); -extern u_short *nat_proto __P((fr_info_t *, nat_t *, u_int)); -extern void nat_update __P((fr_info_t *, nat_t *, ipnat_t *)); -extern void fr_setnatqueue __P((nat_t *, int)); -extern void fr_hostmapdel __P((hostmap_t **)); +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 *)); + +#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 *)); + +#endif + #endif /* __IP_NAT_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_nat6.c b/sys/contrib/ipfilter/netinet/ip_nat6.c new file mode 100644 index 0000000..72931c9 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_nat6.c @@ -0,0 +1,4098 @@ +/* + * 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 <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/file.h> +#if defined(_KERNEL) && defined(__NetBSD_Version__) && \ + (__NetBSD_Version__ >= 399002000) +# include <sys/kauth.h> +#endif +#if !defined(_KERNEL) +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +# define _KERNEL +# ifdef ipf_nat6__OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 220000) +# include <sys/filio.h> +# include <sys/fcntl.h> +#else +# include <sys/ioctl.h> +#endif +#if !defined(AIX) +# include <sys/fcntl.h> +#endif +#if !defined(linux) +# include <sys/protosw.h> +#endif +#include <sys/socket.h> +#if defined(_KERNEL) +# include <sys/systm.h> +# if !defined(__SVR4) && !defined(__svr4__) +# include <sys/mbuf.h> +# endif +#endif +#if defined(__SVR4) || defined(__svr4__) +# include <sys/filio.h> +# include <sys/byteorder.h> +# ifdef _KERNEL +# include <sys/dditypes.h> +# endif +# include <sys/stream.h> +# include <sys/kmem.h> +#endif +#if __FreeBSD_version >= 300000 +# include <sys/queue.h> +#endif +#include <net/if.h> +#if __FreeBSD_version >= 300000 +# include <net/if_var.h> +#endif +#ifdef sun +# include <net/af.h> +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> + +#ifdef RFC1825 +# include <vpn/md5.h> +# include <vpn/ipsec.h> +extern struct ifnet vpnif; +#endif + +#if !defined(linux) +# include <netinet/ip_var.h> +#endif +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#include "netinet/ip_compat.h" +#include <netinet/tcpip.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 (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +#endif +#ifdef HAS_SYS_MD5_H +# include <sys/md5.h> +#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 *, + 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 *)); + + +#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) && (SOLARIS2 >= 6) && 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_ndst6.in6; + np->nl_realport = nat->nat_ndport; + } + } + + 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 1a0b2a2..4e8bdc6 100644 --- a/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c @@ -1,7 +1,7 @@ /* * Simple netbios-dgm transparent proxy for in-kernel use. * For use with the NAT code. - * $Id: ip_netbios_pxy.c,v 2.8.2.1 2005/08/20 13:48:23 darrenr Exp $ + * $Id$ */ /*- @@ -29,14 +29,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_netbios_pxy.c,v 2.8.2.1 2005/08/20 13:48:23 darrenr Exp $ + * $Id$ */ #define IPF_NETBIOS_PROXY -int ippr_netbios_init __P((void)); -void ippr_netbios_fini __P((void)); -int ippr_netbios_out __P((fr_info_t *, ap_session_t *, nat_t *)); +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 *)); static frentry_t netbiosfr; @@ -45,19 +45,19 @@ int netbios_proxy_init = 0; /* * Initialize local structures. */ -int ippr_netbios_init() +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; - - return 0; } -void ippr_netbios_fini() +void +ipf_p_netbios_main_unload() { if (netbios_proxy_init == 1) { MUTEX_DESTROY(&netbiosfr.fr_lock); @@ -66,10 +66,12 @@ void ippr_netbios_fini() } -int ippr_netbios_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; diff --git a/sys/contrib/ipfilter/netinet/ip_pool.c b/sys/contrib/ipfilter/netinet/ip_pool.c index eab3310..2a43cdb 100644 --- a/sys/contrib/ipfilter/netinet/ip_pool.c +++ b/sys/contrib/ipfilter/netinet/ip_pool.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -33,15 +33,10 @@ struct file; # endif #endif #include <sys/time.h> -#if !defined(linux) -# include <sys/protosw.h> -#endif -#include <sys/socket.h> -#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) +#if defined(_KERNEL) && !defined(SOLARIS2) # include <sys/mbuf.h> #endif #if defined(__SVR4) || defined(__svr4__) -# include <sys/filio.h> # include <sys/byteorder.h> # ifdef _KERNEL # include <sys/dditypes.h> @@ -53,69 +48,91 @@ struct file; # include <sys/malloc.h> #endif -#if defined(SOLARIS2) && !defined(_KERNEL) -# include "radix_ipf.h" -#endif -#if defined(_KERNEL) && (defined(__osf__) || defined(AIX) || \ - defined(__hpux) || defined(__sgi)) -# include "radix_ipf_local.h" -# define _RADIX_H_ -#endif +#include <sys/socket.h> #include <net/if.h> #include <netinet/in.h> +#if !defined(_KERNEL) +# include "ipf.h" +#endif #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_pool.h" - -#if defined(IPFILTER_LOOKUP) && defined(_KERNEL) && \ - ((BSD >= 198911) && !defined(__osf__) && \ - !defined(__hpux) && !defined(__sgi)) -static int rn_freenode __P((struct radix_node *, void *)); -#endif +#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: ip_pool.c,v 2.55.2.24 2007/10/10 09:45:37 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -#ifdef IPFILTER_LOOKUP - -# if !defined(RADIX_NODE_HEAD_LOCK) || !defined(RADIX_NODE_HEAD_UNLOCK) || \ - !defined(_KERNEL) -# undef RADIX_NODE_HEAD_LOCK -# undef RADIX_NODE_HEAD_UNLOCK -# define RADIX_NODE_HEAD_LOCK(x) ; -# define RADIX_NODE_HEAD_UNLOCK(x) ; -# endif - -static void ip_pool_clearnodes __P((ip_pool_t *)); -static void *ip_pool_exists __P((int, char *)); - -ip_pool_stat_t ipoolstat; -ipfrwlock_t ip_poolrw; - -/* - * Binary tree routines from Sedgewick and enhanced to do ranges of addresses. - * NOTE: Insertion *MUST* be from greatest range to least for it to work! - * These should be replaced, eventually, by something else - most notably a - * interval searching method. The important feature is to be able to find - * the best match. - * - * So why not use a radix tree for this? As the first line implies, it - * has been written to work with a _range_ of addresses. A range is not - * necessarily a match with any given netmask so what we end up dealing - * with is an interval tree. Implementations of these are hard to find - * and the one herein is far from bug free. - * - * Sigh, in the end I became convinced that the bugs the code contained did - * not make it worthwhile not using radix trees. For now the radix tree from - * 4.4 BSD is used, but this is not viewed as a long term solution. - */ -ip_pool_t *ip_pool_list[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL }; +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 *)); + +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 @@ -126,96 +143,98 @@ 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(&ip_poolrw, "poolrw"); - ip_pool_init(); + RWLOCK_INIT(softc->ipf_poolrw, "poolrw"); + ipf_pool_init(); - bzero((char *)&a, sizeof(a)); - bzero((char *)&b, sizeof(b)); bzero((char *)&ip, sizeof(ip)); bzero((char *)&op, sizeof(op)); + bzero((char *)&node, sizeof(node)); strcpy(op.iplo_name, "0"); - if (ip_pool_create(&op) == 0) - ipo = ip_pool_exists(0, "0"); - - a.adf_addr.in4.s_addr = 0x0a010203; - b.adf_addr.in4.s_addr = 0xffffffff; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - - a.adf_addr.in4.s_addr = 0x0a000000; - b.adf_addr.in4.s_addr = 0xff000000; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); - - a.adf_addr.in4.s_addr = 0x0a010100; - b.adf_addr.in4.s_addr = 0xffffff00; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - - a.adf_addr.in4.s_addr = 0x0a010200; - b.adf_addr.in4.s_addr = 0xffffff00; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); - - a.adf_addr.in4.s_addr = 0x0a010000; - b.adf_addr.in4.s_addr = 0xffff0000; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - - a.adf_addr.in4.s_addr = 0x0a01020f; - b.adf_addr.in4.s_addr = 0xffffffff; - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); - ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + 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); + treeprint(ipo); #endif ip.in4.s_addr = 0x0a00aabb; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a000001; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a000101; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010001; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010101; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010201; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010203; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a01020f; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0b00aabb; printf("search(%#x) = %d (-1)\n", ip.in4.s_addr, - ip_pool_search(ipo, 4, &ip)); + ipf_pool_search(ipo, 4, &ip, 1)); #ifdef DEBUG_POOL -treeprint(ipo); + treeprint(ipo); #endif - ip_pool_fini(); + ipf_pool_fini(); return 0; } @@ -223,7 +242,7 @@ treeprint(ipo); void treeprint(ipo) -ip_pool_t *ipo; + ip_pool_t *ipo; { ip_pool_node_t *c; @@ -237,123 +256,445 @@ ip_pool_t *ipo; /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_init */ +/* 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. */ /* ------------------------------------------------------------------------ */ -int ip_pool_init() +static int +ipf_pool_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; { + ipf_pool_softc_t *softp = arg; - bzero((char *)&ipoolstat, sizeof(ipoolstat)); + ipf_rx_init(softp->ipf_radix); -#if (!defined(_KERNEL) || (BSD < 199306)) - rn_init(); -#endif return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_fini */ -/* Returns: int - 0 = success, else error */ +/* 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. ip_pool_destroy() is*/ +/* 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. */ /* ------------------------------------------------------------------------ */ -void ip_pool_fini() +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; - for (i = 0; i <= IPL_LOGMAX; i++) { - for (q = ip_pool_list[i]; (p = q) != NULL; ) { + 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) ip_pool_destroy(i, p->ipo_name); + (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 (!defined(_KERNEL) || (BSD < 199306)) - rn_fini(); + 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: ip_pool_statistics */ -/* Returns: int - 0 = success, else error */ -/* Parameters: op(I) - pointer to lookup operation arguments */ +/* 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. */ /* ------------------------------------------------------------------------ */ -int ip_pool_statistics(op) -iplookupop_t *op; +static int +ipf_pool_stats_get(softc, arg, op) + ipf_main_softc_t *softc; + void *arg; + iplookupop_t *op; { - ip_pool_stat_t stats; + ipf_pool_softc_t *softp = arg; + ipf_pool_stat_t stats; int unit, i, err = 0; - if (op->iplo_size != sizeof(ipoolstat)) + if (op->iplo_size != sizeof(ipf_pool_stat_t)) { + IPFERROR(70001); return EINVAL; + } - bcopy((char *)&ipoolstat, (char *)&stats, sizeof(stats)); + bcopy((char *)&softp->ipf_pool_stats, (char *)&stats, sizeof(stats)); unit = op->iplo_unit; if (unit == IPL_LOGALL) { - for (i = 0; i < IPL_LOGSIZE; i++) - stats.ipls_list[i] = ip_pool_list[i]; - } else if (unit >= 0 && unit < IPL_LOGSIZE) { + 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] = ip_pool_exists(unit, - op->iplo_name); + stats.ipls_list[unit] = ipf_pool_exists(softp, unit - 1, + op->iplo_name); else - stats.ipls_list[unit] = ip_pool_list[unit]; - } else + stats.ipls_list[unit] = softp->ipf_pool_list[unit]; + } else { + IPFERROR(70025); err = EINVAL; - if (err == 0) + } + if (err == 0) { err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); - return err; + if (err != 0) { + IPFERROR(70026); + return EFAULT; + } + } + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_exists */ -/* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool getting the new node. */ +/* 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 *ip_pool_exists(unit, name) -int unit; -char *name; +static void * +ipf_pool_exists(softp, unit, name) + ipf_pool_softc_t *softp; + int unit; + char *name; { ip_pool_t *p; + int i; - for (p = ip_pool_list[unit]; p != NULL; p = p->ipo_next) - if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0) - break; + 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: ip_pool_find */ -/* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool getting the new node. */ +/* 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. */ /* ------------------------------------------------------------------------ */ -void *ip_pool_find(unit, name) -int unit; -char *name; +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 = ip_pool_exists(unit, name); + p = ipf_pool_exists(softp, unit, name); if ((p != NULL) && (p->ipo_flags & IPOOL_DELETE)) return NULL; @@ -362,45 +703,75 @@ char *name; /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_findeq */ +/* 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: ipo(I) - pointer to the pool getting the new node. */ -/* addr(I) - pointer to address information to delete */ -/* mask(I) - */ +/* 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. */ /* ------------------------------------------------------------------------ */ -ip_pool_node_t *ip_pool_findeq(ipo, addr, mask) -ip_pool_t *ipo; -addrfamily_t *addr, *mask; +extern void printhostmask __P((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; { - struct radix_node *n; - SPL_INT(s); - - SPL_NET(s); - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - n = ipo->ipo_head->rnh_lookup(addr, mask, ipo->ipo_head); - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); - SPL_X(s); + ipf_rdx_node_t *n; + + n = ipo->ipo_head->lookup(ipo->ipo_head, addr, mask); return (ip_pool_node_t *)n; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_search */ +/* Function: ipf_pool_search */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ -/* Parameters: tptr(I) - pointer to the pool to search */ +/* 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. */ /* ------------------------------------------------------------------------ */ -int ip_pool_search(tptr, ipversion, dptr) -void *tptr; -int ipversion; -void *dptr; +static int +ipf_pool_search(softc, tptr, ipversion, dptr, bytes) + ipf_main_softc_t *softc; + void *tptr; + int ipversion; + void *dptr; + u_int bytes; { - struct radix_node *rn; + ipf_rdx_node_t *rn; ip_pool_node_t *m; i6addr_t *addr; addrfamily_t v; @@ -415,102 +786,154 @@ void *dptr; m = NULL; addr = (i6addr_t *)dptr; bzero(&v, sizeof(v)); - v.adf_len = offsetof(addrfamily_t, adf_addr); if (ipversion == 4) { - v.adf_len += sizeof(addr->in4); + 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_len += sizeof(addr->in6); + 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(&ip_poolrw); + READ_ENTER(&softc->ipf_poolrw); - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - rn = ipo->ipo_head->rnh_matchaddr(&v, ipo->ipo_head); - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + rn = ipo->ipo_head->matchaddr(ipo->ipo_head, &v); - if ((rn != NULL) && ((rn->rn_flags & RNF_ROOT) == 0)) { + 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(&ip_poolrw); + RWLOCK_EXIT(&softc->ipf_poolrw); return rv; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_insert */ -/* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool getting the new node. */ -/* addr(I) - address being added as a node */ -/* mask(I) - netmask to with the node being added */ -/* info(I) - extra information to store in this node. */ -/* Locks: WRITE(ip_poolrw) */ +/* 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. */ /* ------------------------------------------------------------------------ */ -int ip_pool_insert(ipo, addr, mask, info) -ip_pool_t *ipo; -i6addr_t *addr, *mask; -int info; +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; { - struct radix_node *rn; + 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; } - bzero(x, sizeof(*x)); - - x->ipn_info = info; - (void)strncpy(x->ipn_name, ipo->ipo_name, sizeof(x->ipn_name)); + *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; + } + } - bcopy(addr, &x->ipn_addr.adf_addr, sizeof(*addr)); - x->ipn_addr.adf_len = sizeof(x->ipn_addr); - bcopy(mask, &x->ipn_mask.adf_addr, sizeof(*mask)); - x->ipn_mask.adf_len = sizeof(x->ipn_mask); + 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; + } + } - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - rn = ipo->ipo_head->rnh_addaddr(&x->ipn_addr, &x->ipn_mask, - ipo->ipo_head, x->ipn_nodes); - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + 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_next = ipo->ipo_list; - x->ipn_pnext = &ipo->ipo_list; - if (ipo->ipo_list != NULL) - ipo->ipo_list->ipn_pnext = &x->ipn_next; - ipo->ipo_list = x; + x->ipn_pnext = ipo->ipo_tail; + *ipo->ipo_tail = x; + ipo->ipo_tail = &x->ipn_next; - ipoolstat.ipls_nodes++; + softp->ipf_pool_stats.ipls_nodes++; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_create */ -/* Returns: int - 0 = success, else error */ -/* Parameters: op(I) - pointer to iplookup struct with call details */ -/* Locks: WRITE(ip_poolrw) */ +/* 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 */ @@ -522,8 +945,11 @@ int info; /* 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. */ /* ------------------------------------------------------------------------ */ -int ip_pool_create(op) -iplookupop_t *op; +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; @@ -532,23 +958,27 @@ iplookupop_t *op; unit = op->iplo_unit; if ((op->iplo_arg & LOOKUP_ANON) == 0) { - h = ip_pool_exists(unit, op->iplo_name); + h = ipf_pool_exists(softp, unit, op->iplo_name); if (h != NULL) { - if ((h->ipo_flags & IPOOL_DELETE) == 0) + 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) + if (h == NULL) { + IPFERROR(70007); return ENOMEM; + } bzero(h, sizeof(*h)); - if (rn_inithead((void **)&h->ipo_head, - offsetof(addrfamily_t, adf_addr) << 3) == 0) { + if (ipf_rx_inithead(softp->ipf_radix, &h->ipo_head) != 0) { KFREE(h); + IPFERROR(70008); return ENOMEM; } @@ -564,7 +994,7 @@ iplookupop_t *op; (void)sprintf(name, "%x", poolnum); #endif - for (p = ip_pool_list[unit]; p != NULL; ) { + for (p = softp->ipf_pool_list[unit + 1]; p != NULL; ) { if (strncmp(name, p->ipo_name, sizeof(p->ipo_name)) == 0) { poolnum++; @@ -573,7 +1003,7 @@ iplookupop_t *op; #else (void)sprintf(name, "%x", poolnum); #endif - p = ip_pool_list[unit]; + p = softp->ipf_pool_list[unit + 1]; } else p = p->ipo_next; } @@ -584,121 +1014,144 @@ iplookupop_t *op; (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 = ip_pool_list[unit]; - if (ip_pool_list[unit] != NULL) - ip_pool_list[unit]->ipo_pnext = &h->ipo_next; - h->ipo_pnext = &ip_pool_list[unit]; - ip_pool_list[unit] = h; + 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; - ipoolstat.ipls_pools++; + softp->ipf_pool_stats.ipls_pools++; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_remove */ -/* Returns: int - 0 = success, else error */ -/* Parameters: ipo(I) - pointer to the pool to remove the node from. */ -/* ipe(I) - address being deleted as a node */ -/* Locks: WRITE(ip_poolrw) */ +/* 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. */ /* ------------------------------------------------------------------------ */ -int ip_pool_remove(ipo, ipe) -ip_pool_t *ipo; -ip_pool_node_t *ipe; +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; - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - ipo->ipo_head->rnh_deladdr(&ipe->ipn_addr, &ipe->ipn_mask, - ipo->ipo_head); - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + if (ipe->ipn_pdnext != NULL) + *ipe->ipn_pdnext = ipe->ipn_dnext; + if (ipe->ipn_dnext != NULL) + ipe->ipn_dnext->ipn_pdnext = ipe->ipn_pdnext; - ip_pool_node_deref(ipe); + ptr = ipo->ipo_head->deladdr(ipo->ipo_head, &ipe->ipn_addr, + &ipe->ipn_mask); - return 0; + if (ptr != NULL) { + ipf_pool_node_deref(softp, ipe); + return 0; + } + IPFERROR(70027); + return ESRCH; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_destroy */ +/* Function: ipf_pool_destroy */ /* Returns: int - 0 = success, else error */ -/* Parameters: op(I) - information about the pool to remove */ -/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* 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 ip_poolrw */ +/* 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 (ip_poolrw,ipf_global) is held. */ +/* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ -int ip_pool_destroy(unit, name) -int unit; -char *name; +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 = ip_pool_exists(unit, name); - if (ipo == NULL) + ipo = ipf_pool_exists(softp, unit, name); + if (ipo == NULL) { + IPFERROR(70009); return ESRCH; + } if (ipo->ipo_ref != 1) { - ip_pool_clearnodes(ipo); + ipf_pool_clearnodes(softc, softp, ipo); ipo->ipo_flags |= IPOOL_DELETE; return 0; } - ip_pool_free(ipo); + ipf_pool_free(softc, softp, ipo); return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_flush */ +/* Function: ipf_pool_flush */ /* Returns: int - number of pools deleted */ -/* Parameters: fp(I) - which pool(s) to flush */ -/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* 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 ip_poolrw */ +/* 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 (ip_poolrw,ipf_global) is held. */ +/* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ -int ip_pool_flush(fp) -iplookupflush_t *fp; +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; - iplookupop_t op; unit = fp->iplf_unit; - - for (i = 0; i <= IPL_LOGMAX; i++) { + for (i = -1; i <= IPL_LOGMAX; i++) { if (unit != IPLT_ALL && i != unit) continue; - for (q = ip_pool_list[i]; (p = q) != NULL; ) { - op.iplo_unit = i; - (void)strncpy(op.iplo_name, p->ipo_name, - sizeof(op.iplo_name)); + for (q = softp->ipf_pool_list[i + 1]; (p = q) != NULL; ) { q = p->ipo_next; - err = ip_pool_destroy(op.iplo_unit, op.iplo_name); + err = ipf_pool_destroy(softc, softp, i, p->ipo_name); if (err == 0) num++; - else - break; } } return num; @@ -706,125 +1159,140 @@ iplookupflush_t *fp; /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_free */ +/* Function: ipf_pool_free */ /* Returns: void */ -/* Parameters: ipo(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* 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 ip_poolrw */ +/* 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 (ip_poolrw,ipf_global) is held. */ +/* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ -void ip_pool_free(ipo) -ip_pool_t *ipo; +static void +ipf_pool_free(softc, softp, ipo) + ipf_main_softc_t *softc; + ipf_pool_softc_t *softp; + ip_pool_t *ipo; { - ip_pool_clearnodes(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; - rn_freehead(ipo->ipo_head); + ipf_rx_freehead(ipo->ipo_head); KFREE(ipo); - ipoolstat.ipls_pools--; + softp->ipf_pool_stats.ipls_pools--; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_clearnodes */ +/* Function: ipf_pool_clearnodes */ /* Returns: void */ -/* Parameters: ipo(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* 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 ip_pool_clearnodes(ipo) -ip_pool_t *ipo; +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; - - RADIX_NODE_HEAD_LOCK(ipo->ipo_head); - while ((n = ipo->ipo_list) != NULL) { - ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask, - ipo->ipo_head); - - *n->ipn_pnext = n->ipn_next; - if (n->ipn_next) - n->ipn_next->ipn_pnext = n->ipn_pnext; - - KFREE(n); + ip_pool_node_t *n, **next; - ipoolstat.ipls_nodes--; - } - RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + for (next = &ipo->ipo_list; (n = *next) != NULL; ) + ipf_pool_remove_node(softc, softp, ipo, n); ipo->ipo_list = NULL; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_deref */ +/* Function: ipf_pool_deref */ /* Returns: void */ -/* Parameters: ipo(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) */ +/* 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. */ /* ------------------------------------------------------------------------ */ -void ip_pool_deref(ipo) -ip_pool_t *ipo; +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) - ip_pool_free(ipo); + ipf_pool_free(softc, arg, ipo); else if ((ipo->ipo_ref == 1) && (ipo->ipo_flags & IPOOL_DELETE)) - ip_pool_destroy(ipo->ipo_unit, ipo->ipo_name); + ipf_pool_destroy(softc, arg, ipo->ipo_unit, ipo->ipo_name); + + return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_node_deref */ +/* Function: ipf_pool_node_deref */ /* Returns: void */ -/* Parameters: ipn(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) */ +/* 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. */ /* ------------------------------------------------------------------------ */ -void ip_pool_node_deref(ipn) -ip_pool_node_t *ipn; +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); - ipoolstat.ipls_nodes--; + softp->ipf_pool_stats.ipls_nodes--; } } /* ------------------------------------------------------------------------ */ -/* Function: ip_pool_getnext */ +/* Function: ipf_pool_iter_next */ /* Returns: void */ -/* Parameters: token(I) - pointer to pool structure */ -/* Parameters: ilp(IO) - pointer to pool iterating structure */ +/* 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 */ /* */ /* ------------------------------------------------------------------------ */ -int ip_pool_getnext(token, ilp) -ipftoken_t *token; -ipflookupiter_t *ilp; +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; @@ -833,35 +1301,38 @@ ipflookupiter_t *ilp; ipo = NULL; nextipo = NULL; - READ_ENTER(&ip_poolrw); + READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : ipo = token->ipt_data; if (ipo == NULL) { - nextipo = ip_pool_list[(int)ilp->ili_unit]; + nextipo = softp->ipf_pool_list[(int)ilp->ili_unit + 1]; } else { nextipo = ipo->ipo_next; } if (nextipo != NULL) { - ATOMIC_INC(nextipo->ipo_ref); + 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 = ip_pool_exists(ilp->ili_unit, ilp->ili_name); - if (ipo == NULL) + ipo = ipf_pool_exists(arg, ilp->ili_unit, + ilp->ili_name); + if (ipo == NULL) { + IPFERROR(70010); err = ESRCH; - else { + } else { nextnode = ipo->ipo_list; ipo = NULL; } @@ -870,123 +1341,149 @@ ipflookupiter_t *ilp; } if (nextnode != NULL) { - ATOMIC_INC(nextnode->ipn_ref); + 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(&ip_poolrw); - + RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return err; switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : - if (ipo != NULL) { - WRITE_ENTER(&ip_poolrw); - ip_pool_deref(ipo); - RWLOCK_EXIT(&ip_poolrw); - } err = COPYOUT(nextipo, ilp->ili_data, sizeof(*nextipo)); - if (err != 0) + 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 : - if (node != NULL) { - WRITE_ENTER(&ip_poolrw); - ip_pool_node_deref(node); - RWLOCK_EXIT(&ip_poolrw); - } err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); - if (err != 0) + 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: ip_pool_iterderef */ +/* Function: ipf_pool_iterderef */ /* Returns: void */ -/* Parameters: ipn(I) - pointer to pool structure */ -/* Locks: WRITE(ip_poolrw) */ +/* 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) */ /* */ /* ------------------------------------------------------------------------ */ -void ip_pool_iterderef(otype, unit, data) -u_int otype; -int unit; -void *data; +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; + return EINVAL; if (unit < 0 || unit > IPL_LOGMAX) - return; + return EINVAL; switch (otype) { case IPFLOOKUPITER_LIST : - WRITE_ENTER(&ip_poolrw); - ip_pool_deref((ip_pool_t *)data); - RWLOCK_EXIT(&ip_poolrw); + ipf_pool_deref(softc, softp, (ip_pool_t *)data); break; case IPFLOOKUPITER_NODE : - WRITE_ENTER(&ip_poolrw); - ip_pool_node_deref((ip_pool_node_t *)data); - RWLOCK_EXIT(&ip_poolrw); + ipf_pool_node_deref(softp, (ip_pool_node_t *)data); break; default : break; } + + return 0; } -# if defined(_KERNEL) && ((BSD >= 198911) && !defined(__osf__) && \ - !defined(__hpux) && !defined(__sgi)) -static int -rn_freenode(struct radix_node *n, void *p) +/* ------------------------------------------------------------------------ */ +/* 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; { - struct radix_node_head *rnh = p; - struct radix_node *d; + ipf_pool_softc_t *softp = arg; + ip_pool_node_t *n; - d = rnh->rnh_deladdr(n->rn_key, NULL, rnh); - if (d != NULL) { - FreeS(d, max_keylen + 2 * sizeof (*d)); + 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); } - return 0; } -void -rn_freehead(rnh) - struct radix_node_head *rnh; -{ - RADIX_NODE_HEAD_LOCK(rnh); - (*rnh->rnh_walktree)(rnh, rn_freenode, rnh); - rnh->rnh_addaddr = NULL; - rnh->rnh_deladdr = NULL; - rnh->rnh_matchaddr = NULL; - rnh->rnh_lookup = NULL; - rnh->rnh_walktree = NULL; - RADIX_NODE_HEAD_UNLOCK(rnh); +#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; - Free(rnh); + 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 -#endif /* IPFILTER_LOOKUP */ +#endif diff --git a/sys/contrib/ipfilter/netinet/ip_pool.h b/sys/contrib/ipfilter/netinet/ip_pool.h index 9968ef0..8524e60 100644 --- a/sys/contrib/ipfilter/netinet/ip_pool.h +++ b/sys/contrib/ipfilter/netinet/ip_pool.h @@ -1,90 +1,67 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * $Id: ip_pool.h,v 2.26.2.6 2007/10/10 09:51:43 darrenr Exp $ + * $Id$ */ #ifndef __IP_POOL_H__ #define __IP_POOL_H__ -#if defined(_KERNEL) && !defined(__osf__) && !defined(__hpux) && \ - !defined(linux) && !defined(sun) && !defined(AIX) -# include <net/radix.h> -extern void rn_freehead __P((struct radix_node_head *)); -# define FreeS(p, z) KFREES(p, z) -extern int max_keylen; -#else -# if defined(__osf__) || defined(__hpux) || defined(sun) -# include "radix_ipf_local.h" -# define radix_mask ipf_radix_mask -# define radix_node ipf_radix_node -# define radix_node_head ipf_radix_node_head -# else -# include "radix_ipf.h" -# endif -#endif #include "netinet/ip_lookup.h" +#include "radix_ipf.h" #define IP_POOL_NOMATCH 0 #define IP_POOL_POSITIVE 1 typedef struct ip_pool_node { - struct radix_node ipn_nodes[2]; + 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_long ipn_hits; + 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; - struct radix_node_head *ipo_head; - ip_pool_node_t *ipo_list; - u_long ipo_hits; - int ipo_unit; - int ipo_flags; - int ipo_ref; - char ipo_name[FR_GROUPLEN]; + 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 ip_pool_stat { - u_long ipls_pools; - u_long ipls_tables; - u_long ipls_nodes; - ip_pool_t *ipls_list[IPL_LOGSIZE]; -} ip_pool_stat_t; - +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 ip_pool_stat_t ipoolstat; -extern ip_pool_t *ip_pool_list[IPL_LOGSIZE]; +extern ipf_lookup_t ipf_pool_backend; -extern int ip_pool_search __P((void *, int, void *)); -extern int ip_pool_init __P((void)); -extern void ip_pool_fini __P((void)); -extern int ip_pool_create __P((iplookupop_t *)); -extern int ip_pool_insert __P((ip_pool_t *, i6addr_t *, i6addr_t *, int)); -extern int ip_pool_remove __P((ip_pool_t *, ip_pool_node_t *)); -extern int ip_pool_destroy __P((int, char *)); -extern void ip_pool_free __P((ip_pool_t *)); -extern void ip_pool_deref __P((ip_pool_t *)); -extern void ip_pool_node_deref __P((ip_pool_node_t *)); -extern void *ip_pool_find __P((int, char *)); -extern ip_pool_node_t *ip_pool_findeq __P((ip_pool_t *, - addrfamily_t *, addrfamily_t *)); -extern int ip_pool_flush __P((iplookupflush_t *)); -extern int ip_pool_statistics __P((iplookupop_t *)); -extern int ip_pool_getnext __P((ipftoken_t *, ipflookupiter_t *)); -extern void ip_pool_iterderef __P((u_int, int, void *)); +#ifndef _KERNEL +extern void ipf_pool_dump __P((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 3924d4b..959b107 100644 --- a/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c @@ -1,18 +1,42 @@ /* - * Copyright (C) 2002-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * Simple PPTP transparent proxy for in-kernel use. For use with the NAT * code. * - * $Id: ip_pptp_pxy.c,v 2.10.2.15 2006/10/31 12:11:23 darrenr Exp $ + * $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; + u_short pptph_len; + u_short pptph_type; + u_32_t pptph_cookie; } pptp_hdr_t; #define PPTP_MSGTYPE_CTL 1 @@ -33,41 +57,41 @@ typedef struct pptp_hdr { #define PPTP_MTCTL_LINKINFO 15 -int ippr_pptp_init __P((void)); -void ippr_pptp_fini __P((void)); -int ippr_pptp_new __P((fr_info_t *, ap_session_t *, nat_t *)); -void ippr_pptp_del __P((ap_session_t *)); -int ippr_pptp_inout __P((fr_info_t *, ap_session_t *, nat_t *)); -void ippr_pptp_donatstate __P((fr_info_t *, nat_t *, pptp_pxy_t *)); -int ippr_pptp_message __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); -int ippr_pptp_nextmessage __P((fr_info_t *, nat_t *, pptp_pxy_t *, int)); -int ippr_pptp_mctl __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); +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 *)); static frentry_t pptpfr; -int pptp_proxy_init = 0; -int ippr_pptp_debug = 0; -int ippr_pptp_gretimeout = IPF_TTLVAL(120); /* 2 minutes */ +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. */ -int ippr_pptp_init() +void +ipf_p_pptp_main_load() { bzero((char *)&pptpfr, sizeof(pptpfr)); pptpfr.fr_ref = 1; - pptpfr.fr_age[0] = ippr_pptp_gretimeout; - pptpfr.fr_age[1] = ippr_pptp_gretimeout; + 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; - - return 0; } -void ippr_pptp_fini() +void +ipf_p_pptp_main_unload() { if (pptp_proxy_init == 1) { MUTEX_DESTROY(&pptpfr.fr_lock); @@ -82,64 +106,83 @@ void ippr_pptp_fini() * 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 ippr_pptp_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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 (nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_inip, + if (ipf_nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_osrcip, ip->ip_dst) != NULL) { - if (ippr_pptp_debug > 0) - printf("ippr_pptp_new: GRE session %s\n", - "already exists"); + if (ipf_p_pptp_debug > 0) + printf("ipf_p_pptp_new: GRE session already exists\n"); return -1; } - aps->aps_psiz = sizeof(*pptp); - KMALLOCS(aps->aps_data, pptp_pxy_t *, sizeof(*pptp)); - if (aps->aps_data == NULL) { - if (ippr_pptp_debug > 0) - printf("ippr_pptp_new: malloc for aps_data %s\n", - "failed"); + 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. */ - pptp = aps->aps_data; - bzero((char *)pptp, sizeof(*pptp)); - ipn = &pptp->pptp_rule; + 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; - if (nat->nat_dir == NAT_OUTBOUND) { - ipn->in_nip = ntohl(nat->nat_outip.s_addr); - ipn->in_outip = fin->fin_saddr; - ipn->in_redir = NAT_MAP; - } else if (nat->nat_dir == NAT_INBOUND) { - ipn->in_nip = 0; - ipn->in_outip = nat->nat_outip.s_addr; - ipn->in_redir = NAT_REDIRECT; - } - ipn->in_inip = nat->nat_inip.s_addr; - ipn->in_inmsk = 0xffffffff; - ipn->in_outmsk = 0xffffffff; - ipn->in_srcip = fin->fin_saddr; - ipn->in_srcmsk = 0xffffffff; - bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0], - sizeof(ipn->in_ifnames[0])); - ipn->in_p = IPPROTO_GRE; + 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; @@ -147,11 +190,13 @@ nat_t *nat; } -void ippr_pptp_donatstate(fin, nat, pptp) -fr_info_t *fin; -nat_t *nat; -pptp_pxy_t *pptp; +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; @@ -165,8 +210,6 @@ pptp_pxy_t *pptp; if ((nat2 == NULL) || (pptp->pptp_state == NULL)) { bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)&gre, sizeof(gre)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_fi.fi_p = IPPROTO_GRE; fi.fin_fr = &pptpfr; if ((nat->nat_dir == NAT_OUTBOUND && fin->fin_out) || @@ -183,47 +226,47 @@ pptp_pxy_t *pptp; fi.fin_flx |= FI_IGNORE; fi.fin_dp = &gre; gre.gr_flags = htons(1 << 13); - if (fin->fin_out && nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_saddr = fin->fin_fi.fi_daddr; - fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; - } else if (!fin->fin_out && nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - fi.fin_fi.fi_daddr = fin->fin_fi.fi_saddr; - } + + 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) - fr_queueback(&nat2->nat_tqe); + ipf_queueback(softc->ipf_ticks, &nat2->nat_tqe); else { - nat2 = nat_new(&fi, &pptp->pptp_rule, &pptp->pptp_nat, - NAT_SLAVE, nat->nat_dir); - pptp->pptp_nat = nat2; +#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) nat_proto(&fi, nat2, 0); - nat_update(&fi, nat2, nat2->nat_ptr); + (void) ipf_nat_proto(&fi, nat2, 0); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); } } - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); if (pptp->pptp_state != NULL) { - fr_queueback(&pptp->pptp_state->is_sti); - RWLOCK_EXIT(&ipf_state); + ipf_queueback(softc->ipf_ticks, &pptp->pptp_state->is_sti); + RWLOCK_EXIT(&softc->ipf_state); } else { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); if (nat2 != NULL) { if (nat->nat_dir == NAT_INBOUND) - fi.fin_fi.fi_daddr = nat2->nat_inip.s_addr; + fi.fin_fi.fi_daddr = nat2->nat_ndstaddr; else - fi.fin_fi.fi_saddr = nat2->nat_inip.s_addr; + fi.fin_fi.fi_saddr = nat2->nat_osrcaddr; } fi.fin_ifp = NULL; - pptp->pptp_state = fr_addstate(&fi, &pptp->pptp_state, - 0); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, &pptp->pptp_state, 0); } ip->ip_p = p; return; @@ -235,13 +278,14 @@ pptp_pxy_t *pptp; * build it up completely (fits in our buffer) then pass it off to the message * parsing function. */ -int ippr_pptp_nextmessage(fin, nat, pptp, rev) -fr_info_t *fin; -nat_t *nat; -pptp_pxy_t *pptp; -int rev; +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 = "ippr_pptp_nextmessage"; + static const char *funcname = "ipf_p_pptp_nextmessage"; pptp_side_t *pptps; u_32_t start, end; pptp_hdr_t *hdr; @@ -271,7 +315,7 @@ int rev; return 0; if (pptps->pptps_next != start) { - if (ippr_pptp_debug > 5) + if (ipf_p_pptp_debug > 5) printf("%s: next (%x) != start (%x)\n", funcname, pptps->pptps_next, start); return -1; @@ -296,7 +340,7 @@ int rev; if (pptps->pptps_bytes == 8) { pptps->pptps_next += 8; if (ntohl(hdr->pptph_cookie) != 0x1a2b3c4d) { - if (ippr_pptp_debug > 1) + if (ipf_p_pptp_debug > 1) printf("%s: bad cookie (%x)\n", funcname, hdr->pptph_cookie); @@ -320,7 +364,7 @@ int rev; * bad data packet, anyway. */ if (len > sizeof(pptps->pptps_buffer)) { - if (ippr_pptp_debug > 3) + if (ipf_p_pptp_debug > 3) printf("%s: message too big (%d)\n", funcname, len); pptps->pptps_next = pptps->pptps_nexthdr; @@ -341,7 +385,7 @@ int rev; if (pptps->pptps_len > pptps->pptps_bytes) break; - ippr_pptp_message(fin, nat, pptp, pptps); + ipf_p_pptp_message(fin, nat, pptp, pptps); pptps->pptps_wptr = pptps->pptps_buffer; pptps->pptps_gothdr = 0; pptps->pptps_bytes = 0; @@ -359,18 +403,19 @@ int rev; /* * handle a complete PPTP message */ -int ippr_pptp_message(fin, nat, pptp, pptps) -fr_info_t *fin; -nat_t *nat; -pptp_pxy_t *pptp; -pptp_side_t *pptps; +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 : - ippr_pptp_mctl(fin, nat, pptp, pptps); + ipf_p_pptp_mctl(fin, nat, pptp, pptps); break; default : @@ -383,11 +428,12 @@ pptp_side_t *pptps; /* * handle a complete PPTP control message */ -int ippr_pptp_mctl(fin, nat, pptp, pptps) -fr_info_t *fin; -nat_t *nat; -pptp_pxy_t *pptp; -pptp_side_t *pptps; +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; @@ -432,7 +478,7 @@ pptp_side_t *pptps; pptps->pptps_state = PPTP_MTCTL_OUTREP; pptp->pptp_call[0] = buffer[7]; pptp->pptp_call[1] = buffer[6]; - ippr_pptp_donatstate(fin, nat, pptp); + ipf_p_pptp_donatstate(fin, nat, pptp); } break; case PPTP_MTCTL_INREQ : @@ -443,7 +489,7 @@ pptp_side_t *pptps; pptps->pptps_state = PPTP_MTCTL_INREP; pptp->pptp_call[0] = buffer[7]; pptp->pptp_call[1] = buffer[6]; - ippr_pptp_donatstate(fin, nat, pptp); + ipf_p_pptp_donatstate(fin, nat, pptp); } break; case PPTP_MTCTL_INCONNECT : @@ -471,10 +517,12 @@ pptp_side_t *pptps; * For outgoing PPTP packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ -int ippr_pptp_inout(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; @@ -495,7 +543,7 @@ nat_t *nat; pptp->pptp_side[rev].pptps_next = ntohl(tcp->th_seq) + 1; pptp->pptp_side[rev].pptps_nexthdr = ntohl(tcp->th_seq) + 1; } - return ippr_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, + return ipf_p_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, rev); } @@ -503,8 +551,10 @@ nat_t *nat; /* * clean up after ourselves. */ -void ippr_pptp_del(aps) -ap_session_t *aps; +void +ipf_p_pptp_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; { pptp_pxy_t *pptp; @@ -516,15 +566,15 @@ ap_session_t *aps; * *_del() is on a callback from aps_free(), from nat_delete() */ - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); if (pptp->pptp_state != NULL) { - pptp->pptp_state->is_die = fr_ticks + 1; - pptp->pptp_state->is_me = NULL; - fr_queuefront(&pptp->pptp_state->is_sti); + ipf_state_setpending(softc, pptp->pptp_state); } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); - pptp->pptp_state = NULL; - pptp->pptp_nat = NULL; + 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 e492a33..39addb0 100644 --- a/sys/contrib/ipfilter/netinet/ip_proxy.c +++ b/sys/contrib/ipfilter/netinet/ip_proxy.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -49,9 +49,6 @@ struct file; #if defined(_KERNEL) && (__FreeBSD_version >= 220000) # include <sys/filio.h> # include <sys/fcntl.h> -# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #else # include <sys/ioctl.h> #endif @@ -70,7 +67,6 @@ struct file; #ifdef sun # include <net/af.h> #endif -#include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -90,9 +86,12 @@ struct file; # include <sys/malloc.h> #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" +#include "netinet/ip_pptp_pxy.c" #if defined(_KERNEL) # include "netinet/ip_irc_pxy.c" # include "netinet/ip_raudio_pxy.c" @@ -101,93 +100,474 @@ struct file; #include "netinet/ip_ipsec_pxy.c" #include "netinet/ip_rpcb_pxy.c" -/* END OF INCLUDES */ - #if !defined(lint) -static const char rcsid[] = "@(#)$Id: ip_proxy.c,v 2.62.2.21 2007/06/02 21:22:28 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -static int appr_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int )); - #define AP_SESS_SIZE 53 -#if defined(_KERNEL) -int ipf_proxy_debug = 0; -#else -int ipf_proxy_debug = 2; -#endif -ap_session_t *ap_sess_tab[AP_SESS_SIZE]; -ap_session_t *ap_sess_list = NULL; -aproxy_t *ap_proxylist = NULL; -aproxy_t ap_proxies[] = { +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 *)); + +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, "ftp", (char)IPPROTO_TCP, 0, 0, ippr_ftp_init, ippr_ftp_fini, - ippr_ftp_new, NULL, ippr_ftp_in, ippr_ftp_out, NULL, NULL }, + { 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, "irc", (char)IPPROTO_TCP, 0, 0, ippr_irc_init, ippr_irc_fini, - ippr_irc_new, NULL, NULL, ippr_irc_out, NULL, NULL }, + { 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, "rcmd", (char)IPPROTO_TCP, 0, 0, ippr_rcmd_init, ippr_rcmd_fini, - ippr_rcmd_new, NULL, ippr_rcmd_in, ippr_rcmd_out, NULL, NULL }, + { 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, "raudio", (char)IPPROTO_TCP, 0, 0, ippr_raudio_init, ippr_raudio_fini, - ippr_raudio_new, NULL, ippr_raudio_in, ippr_raudio_out, NULL, NULL }, + { 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, "msnrpc", (char)IPPROTO_TCP, 0, 0, ippr_msnrpc_init, ippr_msnrpc_fini, - ippr_msnrpc_new, NULL, ippr_msnrpc_in, ippr_msnrpc_out, NULL, NULL }, + { 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, "netbios", (char)IPPROTO_UDP, 0, 0, ippr_netbios_init, ippr_netbios_fini, - NULL, NULL, NULL, ippr_netbios_out, NULL, NULL }, + { 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, "ipsec", (char)IPPROTO_UDP, 0, 0, - ippr_ipsec_init, ippr_ipsec_fini, ippr_ipsec_new, ippr_ipsec_del, - ippr_ipsec_inout, ippr_ipsec_inout, ippr_ipsec_match, NULL }, + { 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_PPTP_PROXY - { NULL, "pptp", (char)IPPROTO_TCP, 0, 0, - ippr_pptp_init, ippr_pptp_fini, ippr_pptp_new, ippr_pptp_del, - ippr_pptp_inout, ippr_pptp_inout, NULL, NULL }, +#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_H323_PROXY - { NULL, "h323", (char)IPPROTO_TCP, 0, 0, ippr_h323_init, ippr_h323_fini, - ippr_h323_new, ippr_h323_del, ippr_h323_in, NULL, NULL, NULL }, - { NULL, "h245", (char)IPPROTO_TCP, 0, 0, NULL, NULL, - ippr_h245_new, NULL, NULL, ippr_h245_out, NULL, NULL }, +#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 -# if 0 - { NULL, "rpcbt", (char)IPPROTO_TCP, 0, 0, - ippr_rpcb_init, ippr_rpcb_fini, ippr_rpcb_new, ippr_rpcb_del, - ippr_rpcb_in, ippr_rpcb_out, NULL, NULL }, +# 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, "rpcbu", (char)IPPROTO_UDP, 0, 0, - ippr_rpcb_init, ippr_rpcb_fini, ippr_rpcb_new, ippr_rpcb_del, - ippr_rpcb_in, ippr_rpcb_out, NULL, NULL }, + { 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, "", '\0', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, "", '\0', 0, 0, 0, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL } }; -/* - * Dynamically add a new kernel proxy. Ensure that it is unique in the - * collection compiled in and dynamically added. - */ -int appr_add(ap) -aproxy_t *ap; + +/* ------------------------------------------------------------------------ */ +/* 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 = ap_proxies; a->apr_p; 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 (ipf_proxy_debug > 1) - printf("appr_add: %s/%d already present (B)\n", + if (softp->ips_proxy_debug & 0x01) + printf("ipf_proxy_add: %s/%d present (B)\n", a->apr_label, a->apr_p); return -1; } @@ -196,89 +576,113 @@ aproxy_t *ap; if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { - if (ipf_proxy_debug > 1) - printf("appr_add: %s/%d already present (D)\n", + 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_init != NULL) - return (*ap->apr_init)(); + if (ap->apr_load != NULL) + (*ap->apr_load)(); return 0; } -/* - * 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 appr_ctl(ctl) -ap_ctl_t *ctl; +/* ------------------------------------------------------------------------ */ +/* 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 = appr_lookup(ctl->apc_p, ctl->apc_label); + a = ipf_proxy_lookup(arg, ctl->apc_p, ctl->apc_label); if (a == NULL) { - if (ipf_proxy_debug > 1) - printf("appr_ctl: can't find %s/%d\n", + 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 (ipf_proxy_debug > 1) - printf("appr_ctl: no ctl function for %s/%d\n", + 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)(a, ctl); - if ((error != 0) && (ipf_proxy_debug > 1)) - printf("appr_ctl: %s/%d ctl error %d\n", + 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; } -/* - * 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 appr_del(ap) -aproxy_t *ap; +/* ------------------------------------------------------------------------ */ +/* 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) + for (app = &ap_proxylist; ((a = *app) != NULL); app = &a->apr_next) { if (a == ap) { a->apr_flags |= APR_DELETE; - *app = a->apr_next; - if (ap->apr_ref != 0) { - if (ipf_proxy_debug > 2) - printf("appr_del: orphaning %s/%d\n", - ap->apr_label, ap->apr_p); - return 1; + if (ap->apr_ref == 0 && ap->apr_clones == 0) { + *app = a->apr_next; + return 0; } - return 0; + return 1; } - if (ipf_proxy_debug > 1) - printf("appr_del: proxy %lx not found\n", (u_long)ap); + } + return -1; } -/* - * Return 1 if the packet is a good match against a proxy, else 0. - */ -int appr_ok(fin, tcp, nat) -fr_info_t *fin; -tcphdr_t *tcp; -ipnat_t *nat; +/* ------------------------------------------------------------------------ */ +/* 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 = nat->in_apr; - u_short dport = nat->in_dport; + 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)) @@ -289,14 +693,26 @@ ipnat_t *nat; } -int appr_ioctl(data, cmd, mode, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode; -void *ctx; +/* ------------------------------------------------------------------------ */ +/* 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; - u_char *ptr; + caddr_t ptr; int error; mode = mode; /* LINT */ @@ -304,17 +720,19 @@ void *ctx; switch (cmd) { case SIOCPROXY : - error = BCOPYIN(data, &ctl, sizeof(ctl)); - if (error != 0) - return EFAULT; + error = ipf_inobj(softc, data, NULL, &ctl, IPFOBJ_PROXYCTL); + if (error != 0) { + return error; + } ptr = NULL; if (ctl.apc_dsize > 0) { - KMALLOCS(ptr, u_char *, ctl.apc_dsize); - if (ptr == NULL) + KMALLOCS(ptr, caddr_t, ctl.apc_dsize); + if (ptr == NULL) { + IPFERROR(80003); error = ENOMEM; - else { - error = copyinptr(ctl.apc_data, ptr, + } else { + error = copyinptr(softc, ctl.apc_data, ptr, ctl.apc_dsize); if (error == 0) ctl.apc_data = ptr; @@ -325,49 +743,61 @@ void *ctx; } if (error == 0) - error = appr_ctl(&ctl); + error = ipf_proxy_ctl(softc, softc->ipf_proxy_soft, + &ctl); - if (ptr != NULL) { + if ((error != 0) && (ptr != NULL)) { KFREES(ptr, ctl.apc_dsize); } break; default : + IPFERROR(80004); error = EINVAL; } return error; } -/* - * If a proxy has a match function, call that to do extended packet - * matching. - */ -int appr_match(fin, nat) -fr_info_t *fin; -nat_t *nat; +/* ------------------------------------------------------------------------ */ +/* 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 (ipf_proxy_debug > 8) - printf("appr_match(%lx,%lx) aps %lx ptr %lx\n", + 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 (ipf_proxy_debug > 0) - printf("appr_match: flx 0x%x (BAD|SHORT)\n", + 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 (ipf_proxy_debug > 0) - printf("appr_match:apr %lx apr_flags 0x%x\n", + 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; } @@ -375,8 +805,8 @@ nat_t *nat; if (apr->apr_match != NULL) { result = (*apr->apr_match)(fin, nat->nat_aps, nat); if (result != 0) { - if (ipf_proxy_debug > 4) - printf("appr_match: result %d\n", result); + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_match: result %d\n", result); return -1; } } @@ -384,24 +814,32 @@ nat_t *nat; } -/* - * Allocate a new application proxy structure and fill it in with the - * relevant details. call the init function once complete, prior to - * returning. - */ -int appr_new(fin, nat) -fr_info_t *fin; -nat_t *nat; +/* ------------------------------------------------------------------------ */ +/* 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 (ipf_proxy_debug > 8) - printf("appr_new(%lx,%lx) \n", (u_long)fin, (u_long)nat); + 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 (ipf_proxy_debug > 0) - printf("appr_new: nat_ptr %lx nat_aps %lx\n", + 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; } @@ -410,65 +848,71 @@ nat_t *nat; if ((apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) { - if (ipf_proxy_debug > 2) - printf("appr_new: apr_flags 0x%x p %d/%d\n", + 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 (ipf_proxy_debug > 0) - printf("appr_new: malloc failed (%lu)\n", + 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_p = fin->fin_p; aps->aps_data = NULL; aps->aps_apr = apr; aps->aps_psiz = 0; if (apr->apr_new != NULL) - if ((*apr->apr_new)(fin, aps, nat) == -1) { + 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 (ipf_proxy_debug > 2) - printf("appr_new: new(%lx) failed\n", + 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 = ap_sess_list; - ap_sess_list = aps; + aps->aps_next = softp->ips_sess_list; + softp->ips_sess_list = aps; nat->nat_aps = aps; return 0; } -/* - * 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 appr_check(fin, nat) -fr_info_t *fin; -nat_t *nat; +/* ------------------------------------------------------------------------ */ +/* 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; { -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) -# if defined(ICK_VALID) + 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 - int dosum = 1; #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; @@ -477,55 +921,54 @@ nat_t *nat; #endif if (fin->fin_flx & FI_BAD) { - if (ipf_proxy_debug > 0) - printf("appr_check: flx 0x%x (BAD)\n", fin->fin_flx); + 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) && (fr_checkl4sum(fin) == -1)) { - if (ipf_proxy_debug > 0) - printf("appr_check: l4 checksum failure %d\n", + 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) - frstats[fin->fin_out].fr_tcpbad++; + softc->ipf_stats[fin->fin_out].fr_tcpbad++; return -1; } #endif aps = nat->nat_aps; - if ((aps != NULL) && (aps->aps_p == fin->fin_p)) { + 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 (fr_coalesce(fin) == -1) { - if (ipf_proxy_debug > 0) - printf("appr_check: fr_coalesce failed %x\n", fin->fin_flx); + 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) && (SOLARIS2 >= 6) && defined(ICK_VALID) +#if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) m = fin->fin_qfm; if (dohwcksum && (m->b_ick_flag == ICK_VALID)) dosum = 0; #endif - /* - * Don't bother the proxy with these...or in fact, - * should we free up proxy stuff when seen? - */ - if ((fin->fin_tcpf & TH_RST) != 0) - break; - /*FALLTHROUGH*/ + break; case IPPROTO_UDP : udp = (udphdr_t *)fin->fin_dp; break; @@ -537,22 +980,24 @@ nat_t *nat; err = 0; if (fin->fin_out != 0) { if (apr->apr_outpkt != NULL) - err = (*apr->apr_outpkt)(fin, aps, nat); + err = (*apr->apr_outpkt)(apr->apr_soft, fin, + aps, nat); } else { if (apr->apr_inpkt != NULL) - err = (*apr->apr_inpkt)(fin, aps, nat); + err = (*apr->apr_inpkt)(apr->apr_soft, fin, + aps, nat); } rv = APR_EXIT(err); - if (((ipf_proxy_debug > 0) && (rv != 0)) || - (ipf_proxy_debug > 8)) - printf("appr_check: out %d err %x rv %d\n", + 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) { - appr_free(apr); + ipf_proxy_deref(apr); nat->nat_aps = NULL; return -1; } @@ -562,16 +1007,17 @@ nat_t *nat; * so we need to recalculate the header checksums for the * packet. */ + adjlen = APR_INC(err); #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) - if (err != 0) { - short adjlen = err & 0xffff; - - s1 = LONG_SUM(fin->fin_plen - adjlen); - s2 = LONG_SUM(fin->fin_plen); - CALC_SUMD(s1, s2, sd); - fix_outcksum(fin, &ip->ip_sum, sd); - } + 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 @@ -583,28 +1029,24 @@ nat_t *nat; * changed or not. */ if (tcp != NULL) { - err = appr_fixseqack(fin, ip, aps, APR_INC(err)); -#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) - if (dosum) - tcp->th_sum = fr_cksum(fin->fin_qfm, ip, - IPPROTO_TCP, tcp, - fin->fin_plen); -#else - tcp->th_sum = fr_cksum(fin->fin_m, ip, - IPPROTO_TCP, tcp, - fin->fin_plen); -#endif + 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 SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) - if (dosum) - udp->uh_sum = fr_cksum(fin->fin_qfm, ip, - IPPROTO_UDP, udp, - fin->fin_plen); -#else - udp->uh_sum = fr_cksum(fin->fin_m, ip, - IPPROTO_UDP, udp, - fin->fin_plen); -#endif + 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++; @@ -614,54 +1056,78 @@ nat_t *nat; } -/* - * Search for an proxy by the protocol it is being used with and its name. - */ -aproxy_t *appr_lookup(pr, name) -u_int pr; -char *name; +/* ------------------------------------------------------------------------ */ +/* 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 (ipf_proxy_debug > 8) - printf("appr_lookup(%d,%s)\n", pr, name); + if (softp->ips_proxy_debug & 0x04) + printf("ipf_proxy_lookup(%d,%s)\n", pr, name); - for (ap = ap_proxies; ap->apr_p; ap++) + 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; } - for (ap = ap_proxylist; ap; ap = ap->apr_next) - if ((ap->apr_p == pr) && - !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) { - ap->apr_ref++; - return ap; - } - if (ipf_proxy_debug > 2) - printf("appr_lookup: failed for %d/%s\n", pr, name); + if (softp->ips_proxy_debug & 0x08) + printf("ipf_proxy_lookup: failed for %d/%s\n", pr, name); return NULL; } -void appr_free(ap) -aproxy_t *ap; +/* ------------------------------------------------------------------------ */ +/* 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--; } -void aps_free(aps) -ap_session_t *aps; +/* ------------------------------------------------------------------------ */ +/* 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 = &ap_sess_list; ((a = *ap) != NULL); ap = &a->aps_next) + for (ap = &softp->ips_sess_list; ((a = *ap) != NULL); ap = &a->aps_next) if (a == aps) { *ap = a->aps_next; break; @@ -669,7 +1135,7 @@ ap_session_t *aps; apr = aps->aps_apr; if ((apr != NULL) && (apr->apr_del != NULL)) - (*apr->apr_del)(aps); + (*apr->apr_del)(softc, aps); if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) KFREES(aps->aps_data, aps->aps_psiz); @@ -677,15 +1143,28 @@ ap_session_t *aps; } -/* - * returns 2 if ack or seq number in TCP header is changed, returns 0 otherwise - */ -static int appr_fixseqack(fin, ip, aps, inc) -fr_info_t *fin; -ip_t *ip; -ap_session_t *aps; -int inc; +/* ------------------------------------------------------------------------ */ +/* 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; @@ -694,10 +1173,10 @@ int inc; tcp = (tcphdr_t *)fin->fin_dp; out = fin->fin_out; /* - * fin->fin_plen has already been adjusted by 'inc'. + * ip_len has already been adjusted by 'inc'. */ - nlen = fin->fin_plen; - nlen -= (IP_HL(ip) << 2) + (TCP_OFF(tcp) << 2); + nlen = fin->fin_dlen; + nlen -= (TCP_OFF(tcp) << 2); inc2 = inc; inc = (int)inc2; @@ -709,7 +1188,7 @@ int inc; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); @@ -729,7 +1208,7 @@ int inc; if (inc && (seq1 > aps->aps_seqmin[!sel])) { aps->aps_seqmin[sel] = seq1 + nlen - 1; aps->aps_seqoff[sel] = aps->aps_seqoff[sel] + inc; - if (ipf_proxy_debug > 7) + 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); @@ -743,7 +1222,7 @@ int inc; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { - if (ipf_proxy_debug > 7) + if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); @@ -762,7 +1241,7 @@ int inc; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { - if (ipf_proxy_debug > 7) + 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; @@ -782,7 +1261,7 @@ int inc; aps->aps_ackmin[!sel] = seq1 + nlen - 1; aps->aps_ackoff[!sel] = aps->aps_ackoff[sel] + inc; - if (ipf_proxy_debug > 7) + 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); @@ -796,14 +1275,14 @@ int inc; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { - if (ipf_proxy_debug > 7) + 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 (ipf_proxy_debug > 7) + 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]); @@ -815,45 +1294,175 @@ int inc; } } - if (ipf_proxy_debug > 8) - printf("appr_fixseqack: seq %x ack %x\n", + 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; } -/* - * Initialise hook for kernel application proxies. - * Call the initialise routine for all the compiled in kernel proxies. - */ -int appr_init() +/* ------------------------------------------------------------------------ */ +/* 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; { - aproxy_t *ap; - int err = 0; - - for (ap = ap_proxies; ap->apr_p; ap++) { - if (ap->apr_init != NULL) { - err = (*ap->apr_init)(); - if (err != 0) - break; + 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; } - return err; + + 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; } -/* - * Unload hook for kernel application proxies. - * Call the finialise routine for all the compiled in kernel proxies. - */ -void appr_unload() +/* ------------------------------------------------------------------------ */ +/* 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; { - aproxy_t *ap; + 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 + } - for (ap = ap_proxies; ap->apr_p; ap++) - if (ap->apr_fini != NULL) - (*ap->apr_fini)(); - for (ap = ap_proxylist; ap; ap = ap->apr_next) - if (ap->apr_fini != NULL) - (*ap->apr_fini)(); + 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 1bcfc60..1a1fdfa 100644 --- a/sys/contrib/ipfilter/netinet/ip_proxy.h +++ b/sys/contrib/ipfilter/netinet/ip_proxy.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -53,14 +53,11 @@ typedef struct ap_session { struct ap_tcp apu_tcp; struct ap_udp apu_udp; } aps_un; - u_int aps_flags; 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_p; /* protocol */ int aps_psiz; /* size of private data */ - struct ap_session *aps_hnext; struct ap_session *aps_next; } ap_session_t; @@ -76,6 +73,7 @@ typedef struct ap_session { 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 @@ -93,21 +91,36 @@ typedef struct ap_control { 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_ref; /* +1 per rule referencing it */ + u_char apr_p; /* protocol */ int apr_flags; - int (* apr_init) __P((void)); - void (* apr_fini) __P((void)); - int (* apr_new) __P((fr_info_t *, ap_session_t *, struct nat *)); - void (* apr_del) __P((ap_session_t *)); - int (* apr_inpkt) __P((fr_info_t *, ap_session_t *, struct nat *)); - int (* apr_outpkt) __P((fr_info_t *, ap_session_t *, struct nat *)); + 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((struct aproxy *, struct ap_control *)); + 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_soft; } aproxy_t; #define APR_DELETE 1 @@ -116,42 +129,37 @@ typedef struct aproxy { #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') || \ +# 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 +#endif /* _KERNEL */ /* - * This is the scratch buffer size used to hold strings from the TCP stream - * that we may want to parse. It's an arbitrary size, really, but it must - * be at least as large as IPF_FTPBUFSZ. - */ -#define FTP_BUFSZ 120 - -/* - * This buffer, however, doesn't need to be nearly so big. It just needs to - * be able to squeeze in the largest command it needs to rewrite, Which ones - * does it rewrite? EPRT, PORT, 227 replies. + * For the ftp proxy. */ -#define IPF_FTPBUFSZ 80 /* This *MUST* be >= 53! */ +#define FTP_BUFSZ 160 +#define IPF_FTPBUFSZ 160 typedef struct ftpside { char *ftps_rptr; @@ -159,19 +167,37 @@ typedef struct ftpside { void *ftps_ifp; u_32_t ftps_seq[2]; u_32_t ftps_len; - int ftps_junk; /* 2 = no cr/lf yet, 1 = cannot parse */ + 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 { @@ -187,6 +213,16 @@ typedef struct ircinfo { /* + * 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 { @@ -231,43 +267,6 @@ typedef struct msnrpcinfo { /* - * 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; - ipnat_t ipsc_rule; - nat_t *ipsc_nat; - struct ipstate *ipsc_state; -} ipsec_pxy_t; - -/* - * 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 { - ipnat_t pptp_rule; - nat_t *pptp_nat; - struct ipstate *pptp_state; - u_short pptp_call[2]; - pptp_side_t pptp_side[2]; -} pptp_pxy_t; - - -/* * Sun RPCBIND proxy */ #define RPCB_MAXMSG 888 @@ -439,24 +438,26 @@ typedef struct rpcb_session { */ #define XDRALIGN(x) ((((x) % 4) != 0) ? ((((x) + 3) / 4) * 4) : (x)) -extern ap_session_t *ap_sess_tab[AP_SESS_SIZE]; -extern ap_session_t *ap_sess_list; -extern aproxy_t ap_proxies[]; -extern int ippr_ftp_pasvonly; -extern int ipf_proxy_debug; - -extern int appr_add __P((aproxy_t *)); -extern int appr_ctl __P((ap_ctl_t *)); -extern int appr_del __P((aproxy_t *)); -extern int appr_init __P((void)); -extern void appr_unload __P((void)); -extern int appr_ok __P((fr_info_t *, tcphdr_t *, struct ipnat *)); -extern int appr_match __P((fr_info_t *, struct nat *)); -extern void appr_free __P((aproxy_t *)); -extern void aps_free __P((ap_session_t *)); -extern int appr_check __P((fr_info_t *, struct nat *)); -extern aproxy_t *appr_lookup __P((u_int, char *)); -extern int appr_new __P((fr_info_t *, struct nat *)); -extern int appr_ioctl __P((caddr_t, ioctlcmd_t, int, void *)); +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 *)); #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 2729dc6..0313637 100644 --- a/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c @@ -1,8 +1,7 @@ /* $FreeBSD$ */ /* - * $FreeBSD$ - * Copyright (C) 1998-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -12,11 +11,11 @@ #define IPF_RAUDIO_PROXY -int ippr_raudio_init __P((void)); -void ippr_raudio_fini __P((void)); -int ippr_raudio_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_raudio_in __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_raudio_out __P((fr_info_t *, ap_session_t *, nat_t *)); +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 *)); static frentry_t raudiofr; @@ -26,19 +25,19 @@ int raudio_proxy_init = 0; /* * Real Audio application proxy initialization. */ -int ippr_raudio_init() +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; - - return 0; } -void ippr_raudio_fini() +void +ipf_p_raudio_main_unload() { if (raudio_proxy_init == 1) { MUTEX_DESTROY(&raudiofr.fr_lock); @@ -50,20 +49,24 @@ void ippr_raudio_fini() /* * Setup for a new proxy to handle Real Audio. */ -int ippr_raudio_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; - fin = fin; /* LINT */ - nat = nat; /* LINT */ - bzero(aps->aps_data, sizeof(raudio_t)); rap = aps->aps_data; aps->aps_psiz = sizeof(raudio_t); @@ -73,10 +76,12 @@ nat_t *nat; -int ippr_raudio_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; @@ -179,14 +184,18 @@ nat_t *nat; } -int ippr_raudio_in(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; @@ -198,6 +207,8 @@ nat_t *nat; 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 @@ -272,14 +283,12 @@ nat_t *nat; swb = ip->ip_dst; ip->ip_p = IPPROTO_UDP; - ip->ip_src = nat->nat_inip; - ip->ip_dst = nat->nat_oip; + 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_state = NULL; - fi.fin_nat = NULL; fi.fin_flx |= FI_IGNORE; fi.fin_dp = (char *)tcp2; fi.fin_fr = &raudiofr; @@ -287,7 +296,7 @@ nat_t *nat; fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); tcp2->th_win = htons(8192); slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp); + 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)) { @@ -298,16 +307,19 @@ nat_t *nat; fi.fin_data[0] = dp; fi.fin_data[1] = sp; fi.fin_out = 0; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, + 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) nat_proto(&fi, nat2, IPN_UDP); - nat_update(&fi, nat2, nat2->nat_ptr); + (void) ipf_nat_proto(&fi, nat2, IPN_UDP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); - (void) fr_addstate(&fi, NULL, (sp ? 0 : SI_W_SPORT)); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, NULL, + (sp ? 0 : SI_W_SPORT)); } } @@ -318,16 +330,18 @@ nat_t *nat; fi.fin_data[0] = sp; fi.fin_data[1] = 0; fi.fin_out = 1; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, + 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) nat_proto(&fi, nat2, IPN_UDP); - nat_update(&fi, nat2, nat2->nat_ptr); + (void) ipf_nat_proto(&fi, nat2, IPN_UDP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); } } diff --git a/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c b/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c index dc92bf5..4b453c0 100644 --- a/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c @@ -1,11 +1,11 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1998-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * $Id: ip_rcmd_pxy.c,v 1.41.2.7 2006/07/14 06:12:18 darrenr Exp $ + * $Id$ * * Simple RCMD transparent proxy for in-kernel use. For use with the NAT * code. @@ -14,36 +14,45 @@ #define IPF_RCMD_PROXY - -int ippr_rcmd_init __P((void)); -void ippr_rcmd_fini __P((void)); -int ippr_rcmd_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_rcmd_out __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_rcmd_in __P((fr_info_t *, ap_session_t *, nat_t *)); +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 ippr_rcmd_portmsg __P((fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_rcmd_portmsg __P((fr_info_t *, ap_session_t *, nat_t *)); static frentry_t rcmdfr; -int rcmd_proxy_init = 0; +static int rcmd_proxy_init = 0; /* * RCMD application proxy initialization. */ -int ippr_rcmd_init() +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; - - return 0; } -void ippr_rcmd_fini() +void +ipf_p_rcmd_main_unload() { if (rcmd_proxy_init == 1) { MUTEX_DESTROY(&rcmdfr.fr_lock); @@ -55,36 +64,70 @@ void ippr_rcmd_fini() /* * Setup for a new RCMD proxy. */ -int ippr_rcmd_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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 */ - nat = nat; /* LINT */ - aps->aps_psiz = sizeof(u_32_t); - KMALLOCS(aps->aps_data, u_32_t *, sizeof(u_32_t)); - if (aps->aps_data == NULL) { + np = nat->nat_ptr; + size = np->in_size; + KMALLOC(rc, rcmdinfo_t *); + if (rc == NULL) { #ifdef IP_RCMD_PROXY_DEBUG - printf("ippr_rcmd_new:KMALLOCS(%d) failed\n", sizeof(u_32_t)); + printf("ipf_p_rcmd_new:KMALLOCS(%d) failed\n", sizeof(*rc)); #endif return -1; } - *(u_32_t *)aps->aps_data = 0; 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; +u_short +ipf_rcmd_atoi(ptr) + char *ptr; { register char *s = ptr, c; register u_short i = 0; @@ -97,44 +140,50 @@ char *ptr; } -int ippr_rcmd_portmsg(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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; - struct in_addr swip, swip2; - int off, dlen, nflags; + 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; ip_t *ip; mb_t *m; tcp = (tcphdr_t *)fin->fin_dp; - if (tcp->th_flags & TH_SYN) { - *(u_32_t *)aps->aps_data = htonl(ntohl(tcp->th_seq) + 1); - return 0; - } - - if ((*(u_32_t *)aps->aps_data != 0) && - (tcp->th_seq != *(u_32_t *)aps->aps_data)) - return 0; - m = fin->fin_m; ip = fin->fin_ip; - off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; - -#ifdef __sgi - dlen = fin->fin_plen - off; -#else - dlen = MSGDSIZE(m) - off; + 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); @@ -143,97 +192,161 @@ nat_t *nat; sp = ipf_rcmd_atoi(s); if (sp == 0) { #ifdef IP_RCMD_PROXY_DEBUG - printf("ippr_rcmd_portmsg:sp == 0 dlen %d [%s]\n", + 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; + /* - * Add skeleton NAT entry for connection which will come back the - * other way. + * 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_state = NULL; - fi.fin_nat = NULL; fi.fin_flx |= FI_IGNORE; - fi.fin_data[0] = sp; - fi.fin_data[1] = 0; - if (nat->nat_dir == NAT_OUTBOUND) - nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - else - nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - if (nat2 == NULL) { - int slen; - - slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp); - bzero((char *)tcp2, sizeof(*tcp2)); - tcp2->th_win = htons(8192); - tcp2->th_sport = htons(sp); - tcp2->th_dport = 0; /* XXX - don't specify remote port */ - 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; - nflags = NAT_SLAVE|IPN_TCP|SI_W_DPORT; - - swip = ip->ip_src; - swip2 = ip->ip_dst; + 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) { - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - ip->ip_src = nat->nat_inip; + nat2 = ipf_nat6_outlookup(&fi, NAT_SEARCH|IPN_TCP, + nat->nat_pr[1], + &nat->nat_osrc6.in6, + &nat->nat_odst6.in6); } else { - fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; - ip->ip_src = nat->nat_oip; - nflags |= NAT_NOTRULEPORT; + nat2 = ipf_nat6_inlookup(&fi, NAT_SEARCH|IPN_TCP, + nat->nat_pr[0], + &nat->nat_osrc6.in6, + &nat->nat_odst6.in6); } - - nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir); - - if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_TCP); - nat_update(&fi, nat2, nat2->nat_ptr); - fi.fin_ifp = NULL; - if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; - ip->ip_dst = nat->nat_inip; - } - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); +#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; - ip->ip_src = swip; - ip->ip_dst = swip2; } + if (nat2 == NULL) + return APR_ERR(1); return 0; } -int ippr_rcmd_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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 ippr_rcmd_portmsg(fin, aps, nat); + return ipf_p_rcmd_portmsg(fin, aps, nat); return 0; } -int ippr_rcmd_in(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +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 ippr_rcmd_portmsg(fin, aps, nat); + 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 da76fde..9493d2b 100644 --- a/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2003 by Ryan Beasley <ryanb@goddamnbastard.org> + * Copyright (C) 2002-2012 by Ryan Beasley <ryanb@goddamnbastard.org> * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -37,44 +37,43 @@ * o The enclosed hack of STREAMS support is pretty sick and most likely * broken. * - * $Id: ip_rpcb_pxy.c,v 2.25.2.7 2007/06/04 09:16:31 darrenr Exp $ + * $Id$ */ - #define IPF_RPCB_PROXY /* * Function prototypes */ -int ippr_rpcb_init __P((void)); -void ippr_rpcb_fini __P((void)); -int ippr_rpcb_new __P((fr_info_t *, ap_session_t *, nat_t *)); -void ippr_rpcb_del __P((ap_session_t *)); -int ippr_rpcb_in __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_rpcb_out __P((fr_info_t *, ap_session_t *, nat_t *)); - -static void ippr_rpcb_flush __P((rpcb_session_t *)); -static int ippr_rpcb_decodereq __P((fr_info_t *, nat_t *, +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 ippr_rpcb_skipauth __P((rpc_msg_t *, xdr_auth_t *, u_32_t **)); -static int ippr_rpcb_insert __P((rpcb_session_t *, rpcb_xact_t *)); -static int ippr_rpcb_xdrrpcb __P((rpc_msg_t *, u_32_t *, rpcb_args_t *)); -static int ippr_rpcb_getuaddr __P((rpc_msg_t *, xdr_uaddr_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 ippr_rpcb_atoi __P((char *)); -static int ippr_rpcb_modreq __P((fr_info_t *, nat_t *, rpc_msg_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 ippr_rpcb_decoderep __P((fr_info_t *, nat_t *, +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 * ippr_rpcb_lookup __P((rpcb_session_t *, u_32_t)); -static void ippr_rpcb_deref __P((rpcb_session_t *, rpcb_xact_t *)); -static int ippr_rpcb_getproto __P((rpc_msg_t *, xdr_proto_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 ippr_rpcb_getnat __P((fr_info_t *, nat_t *, u_int, u_int)); -static int ippr_rpcb_modv3 __P((fr_info_t *, nat_t *, rpc_msg_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 ippr_rpcb_modv4 __P((fr_info_t *, nat_t *, rpc_msg_t *, +static int ipf_p_rpcb_modv4 __P((fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int)); -static void ippr_rpcb_fixlen __P((fr_info_t *, int)); +static void ipf_p_rpcb_fixlen __P((fr_info_t *, int)); /* * Global variables @@ -84,7 +83,7 @@ static frentry_t rpcbfr; /* Skeleton rule for reference by entities static int rpcbcnt; /* Upper bound of allocated RPCB sessions. */ /* XXX rpcbcnt still requires locking. */ -int rpcb_proxy_init = 0; +static int rpcb_proxy_init = 0; /* @@ -98,15 +97,15 @@ int rpcb_proxy_init = 0; * Public subroutines */ -/* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_init */ -/* Returns: int - 0 == success */ -/* Parameters: (void) */ -/* */ -/* Initialize the filter rule entry and session limiter. */ -/* -------------------------------------------------------------------- */ -int -ippr_rpcb_init() +/* -------------------------------------------------------------------- */ +/* Function: ipf_p_rpcb_main_load */ +/* Returns: void */ +/* Parameters: (void) */ +/* */ +/* Initialize the filter rule entry and session limiter. */ +/* -------------------------------------------------------------------- */ +void +ipf_p_rpcb_main_load() { rpcbcnt = 0; @@ -115,19 +114,17 @@ ippr_rpcb_init() rpcbfr.fr_flags = FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&rpcbfr.fr_lock, "ipf Sun RPCB proxy rule lock"); rpcb_proxy_init = 1; - - return(0); } -/* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_fini */ -/* Returns: void */ -/* Parameters: (void) */ -/* */ -/* Destroy rpcbfr's mutex to avoid a lock leak. */ -/* -------------------------------------------------------------------- */ +/* -------------------------------------------------------------------- */ +/* Function: ipf_p_rpcb_main_unload */ +/* Returns: void */ +/* Parameters: (void) */ +/* */ +/* Destroy rpcbfr's mutex to avoid a lock leak. */ +/* -------------------------------------------------------------------- */ void -ippr_rpcb_fini() +ipf_p_rpcb_main_unload() { if (rpcb_proxy_init == 1) { MUTEX_DESTROY(&rpcbfr.fr_lock); @@ -136,7 +133,7 @@ ippr_rpcb_fini() } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_new */ +/* 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 */ @@ -145,16 +142,19 @@ ippr_rpcb_fini() /* Allocate resources for per-session proxy structures. */ /* -------------------------------------------------------------------- */ int -ippr_rpcb_new(fin, aps, nat) +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; - fin = fin; /* LINT */ nat = nat; /* LINT */ + if (fin->fin_v != 4) + return -1; + KMALLOC(rs, rpcb_session_t *); if (rs == NULL) return(-1); @@ -168,27 +168,28 @@ ippr_rpcb_new(fin, aps, nat) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_del */ +/* 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 -ippr_rpcb_del(aps) +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); - ippr_rpcb_flush(rs); + ipf_p_rpcb_flush(rs); MUTEX_EXIT(&rs->rs_rxlock); MUTEX_DESTROY(&rs->rs_rxlock); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_in */ +/* 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) */ @@ -201,7 +202,8 @@ ippr_rpcb_del(aps) /* for decoding. Also pass packet off for a rewrite if necessary. */ /* -------------------------------------------------------------------- */ int -ippr_rpcb_in(fin, aps, nat) +ipf_p_rpcb_in(arg, fin, aps, nat) + void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; @@ -235,7 +237,7 @@ ippr_rpcb_in(fin, aps, nat) rm->rm_buflen = dlen; /* Send off to decode request. */ - rv = ippr_rpcb_decodereq(fin, nat, rs, rm); + rv = ipf_p_rpcb_decodereq(fin, nat, rs, rm); switch(rv) { @@ -246,18 +248,18 @@ ippr_rpcb_in(fin, aps, nat) case 0: break; case 1: - rv = ippr_rpcb_modreq(fin, nat, rm, m, off); + rv = ipf_p_rpcb_modreq(fin, nat, rm, m, off); break; default: /*CONSTANTCONDITION*/ - IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_req)", rv)); + IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_req)", rv)); } return(rv); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_out */ +/* 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) */ @@ -272,7 +274,8 @@ ippr_rpcb_in(fin, aps, nat) /* allow direct communication between RPC client and server. */ /* -------------------------------------------------------------------- */ int -ippr_rpcb_out(fin, aps, nat) +ipf_p_rpcb_out(arg, fin, aps, nat) + void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; @@ -311,14 +314,14 @@ ippr_rpcb_out(fin, aps, nat) rx = NULL; /* XXX gcc */ /* Send off to decode reply. */ - rv = ippr_rpcb_decoderep(fin, nat, rs, rm, &rx); + rv = ipf_p_rpcb_decoderep(fin, nat, rs, rm, &rx); switch(rv) { case -1: /* Bad packet */ if (rx != NULL) { MUTEX_ENTER(&rs->rs_rxlock); - ippr_rpcb_deref(rs, rx); + ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } return(APR_ERR(1)); @@ -334,16 +337,16 @@ ippr_rpcb_out(fin, aps, nat) * same. (i.e., this box is either a router or rpcbind * only listens on loopback.) */ - if (nat->nat_inip.s_addr != nat->nat_outip.s_addr) { + if (nat->nat_odstaddr != nat->nat_ndstaddr) { if (rx->rx_type == RPCB_RES_STRING) - diff = ippr_rpcb_modv3(fin, nat, rm, m, off); + diff = ipf_p_rpcb_modv3(fin, nat, rm, m, off); else if (rx->rx_type == RPCB_RES_LIST) - diff = ippr_rpcb_modv4(fin, nat, rm, m, off); + diff = ipf_p_rpcb_modv4(fin, nat, rm, m, off); } break; default: /*CONSTANTCONDITION*/ - IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_decoderep)", rv)); + IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_decoderep)", rv)); } if (rx != NULL) { @@ -354,8 +357,8 @@ ippr_rpcb_out(fin, aps, nat) * finished with rx, and the other signals that we've * processed its reply. */ - ippr_rpcb_deref(rs, rx); - ippr_rpcb_deref(rs, rx); + ipf_p_rpcb_deref(rs, rx); + ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } @@ -367,14 +370,14 @@ ippr_rpcb_out(fin, aps, nat) */ /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_flush */ +/* 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 -ippr_rpcb_flush(rs) +ipf_p_rpcb_flush(rs) rpcb_session_t *rs; { rpcb_xact_t *r1, *r2; @@ -391,7 +394,7 @@ ippr_rpcb_flush(rs) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_decodereq */ +/* Function: ipf_p_rpcb_decodereq */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == request successfully decoded, */ /* 1 == request successfully decoded; requires */ @@ -408,7 +411,7 @@ ippr_rpcb_flush(rs) /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_decodereq(fin, nat, rs, rm) +ipf_p_rpcb_decodereq(fin, nat, rs, rm) fr_info_t *fin; nat_t *nat; rpcb_session_t *rs; @@ -440,9 +443,9 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) rc->rc_proc = p++; /* Bypass RPC authentication stuff. */ - if (ippr_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0) + if (ipf_p_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0) return(-1); - if (ippr_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0) + if (ipf_p_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0) return(-1); /* Compare RPCB version and procedure numbers. */ @@ -488,17 +491,17 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) ra = &rc->rc_rpcbargs; /* Decode the 'struct rpcb' request. */ - if (ippr_rpcb_xdrrpcb(rm, p, ra) != 0) + 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_outip.s_addr) || - (ra->ra_maddr.xu_port != nat->nat_outport)) + 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_outip.s_addr != nat->nat_inip.s_addr) || - (nat->nat_outport != nat->nat_inport)) + if ((nat->nat_ndstaddr != nat->nat_odstaddr) || + (nat->nat_ndport != nat->nat_odport)) mod = 1; break; default: @@ -506,7 +509,7 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) } MUTEX_ENTER(&rs->rs_rxlock); - if (ippr_rpcb_insert(rs, &rx) != 0) { + if (ipf_p_rpcb_insert(rs, &rx) != 0) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } @@ -516,7 +519,7 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_skipauth */ +/* 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 */ @@ -527,7 +530,7 @@ ippr_rpcb_decodereq(fin, nat, rs, rm) /* it. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_skipauth(rm, auth, buf) +ipf_p_rpcb_skipauth(rm, auth, buf) rpc_msg_t *rm; xdr_auth_t *auth; u_32_t **buf; @@ -559,20 +562,20 @@ ippr_rpcb_skipauth(rm, auth, buf) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_insert */ +/* 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 -ippr_rpcb_insert(rs, rx) +ipf_p_rpcb_insert(rs, rx) rpcb_session_t *rs; rpcb_xact_t *rx; { rpcb_xact_t *rxp; - rxp = ippr_rpcb_lookup(rs, rx->rx_xid); + rxp = ipf_p_rpcb_lookup(rs, rx->rx_xid); if (rxp != NULL) { ++rxp->rx_ref; return(0); @@ -602,7 +605,7 @@ ippr_rpcb_insert(rs, rx) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_xdrrpcb */ +/* 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 */ @@ -613,7 +616,7 @@ ippr_rpcb_insert(rs, rx) /* within only the context of TCP/UDP over IP networks. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_xdrrpcb(rm, p, ra) +ipf_p_rpcb_xdrrpcb(rm, p, ra) rpc_msg_t *rm; u_32_t *p; rpcb_args_t *ra; @@ -625,11 +628,11 @@ ippr_rpcb_xdrrpcb(rm, p, ra) p += 2; /* Decode r_netid. Must be "tcp" or "udp". */ - if (ippr_rpcb_getproto(rm, &ra->ra_netid, &p) != 0) + if (ipf_p_rpcb_getproto(rm, &ra->ra_netid, &p) != 0) return(-1); /* Decode r_maddr. */ - if (ippr_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0) + if (ipf_p_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0) return(-1); /* Advance to r_owner and make sure it's empty. */ @@ -640,7 +643,7 @@ ippr_rpcb_xdrrpcb(rm, p, ra) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_getuaddr */ +/* Function: ipf_p_rpcb_getuaddr */ /* Returns: int -- -1 == illegal string, */ /* 0 == string parsed; contents recorded */ /* Parameters: rm(I) - pointer to RPC message structure */ @@ -650,7 +653,7 @@ ippr_rpcb_xdrrpcb(rm, p, ra) /* Decode the IP address / port at p and record them in xu. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_getuaddr(rm, xu, p) +ipf_p_rpcb_getuaddr(rm, xu, p) rpc_msg_t *rm; xdr_uaddr_t *xu; u_32_t **p; @@ -699,7 +702,7 @@ ippr_rpcb_getuaddr(rm, xu, p) /* Check for ASCII byte. */ *c = '\0'; - t = ippr_rpcb_atoi(b); + t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); @@ -721,7 +724,7 @@ ippr_rpcb_getuaddr(rm, xu, p) return(-1); /* Handle the last byte (port low byte) */ - t = ippr_rpcb_atoi(b); + t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); pp[d - 4] = t & 0xff; @@ -730,14 +733,14 @@ ippr_rpcb_getuaddr(rm, xu, p) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_atoi (XXX should be generic for all proxies) */ +/* 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 -ippr_rpcb_atoi(ptr) +ipf_p_rpcb_atoi(ptr) char *ptr; { register char *s = ptr, c; @@ -751,7 +754,7 @@ ippr_rpcb_atoi(ptr) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_modreq */ +/* Function: ipf_p_rpcb_modreq */ /* Returns: int -- change in datagram length */ /* APR_ERR(2) - critical failure */ /* Parameters: fin(I) - pointer to packet information */ @@ -764,7 +767,7 @@ ippr_rpcb_atoi(ptr) /* with the latter. (This is exclusive to protocol versions 3 & 4). */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_modreq(fin, nat, rm, m, off) +ipf_p_rpcb_modreq(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; @@ -779,8 +782,8 @@ ippr_rpcb_modreq(fin, nat, rm, m, off) int diff; ra = &rm->rm_call.rc_rpcbargs; - i = (char *)&nat->nat_inip.s_addr; - p = (char *)&nat->nat_inport; + i = (char *)&nat->nat_odstaddr; + p = (char *)&nat->nat_odport; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ @@ -821,9 +824,9 @@ ippr_rpcb_modreq(fin, nat, rm, m, off) if (diff != 0) { udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + diff); - fin->fin_ip->ip_len += diff; - fin->fin_dlen += diff; fin->fin_plen += diff; + fin->fin_ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += diff; /* XXX Storage lengths. */ } @@ -831,7 +834,7 @@ ippr_rpcb_modreq(fin, nat, rm, m, off) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_decoderep */ +/* Function: ipf_p_rpcb_decoderep */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == valid, negative reply */ /* 1 == vaddlid, positive reply; needs no changes */ @@ -851,7 +854,7 @@ ippr_rpcb_modreq(fin, nat, rm, m, off) /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) +ipf_p_rpcb_decoderep(fin, nat, rs, rm, rxp) fr_info_t *fin; nat_t *nat; rpcb_session_t *rs; @@ -875,7 +878,7 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) /* Lookup XID */ MUTEX_ENTER(&rs->rs_rxlock); - if ((rx = ippr_rpcb_lookup(rs, xdr)) == NULL) { + if ((rx = ipf_p_rpcb_lookup(rs, xdr)) == NULL) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } @@ -900,7 +903,7 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) } /* Bypass RPC authentication stuff. */ - if (ippr_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0) + if (ipf_p_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0) return(-1); /* Test accept status */ @@ -916,20 +919,20 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) /* 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 (ippr_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0) + if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0) return(-1); break; case RPCB_RES_STRING: @@ -947,15 +950,15 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) return(0); /* Decode the target IP address / port. */ - if (ippr_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0) + if (ipf_p_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0) return(-1); /* Validate the IP address and port contained. */ - if (nat->nat_inip.s_addr != rr->rr_v3.xu_ip) + if (nat->nat_odstaddr != rr->rr_v3.xu_ip) return(-1); /* Create NAT & state table entries. */ - if (ippr_rpcb_getnat(fin, nat, rx->rx_proto, + if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)rr->rr_v3.xu_port) != 0) return(-1); break; @@ -980,9 +983,9 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) for(;;) { re = &rl->rl_entries[rl->rl_cnt]; - if (ippr_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0) + if (ipf_p_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0) return(-1); - if (ippr_rpcb_getproto(rm, &re->re_netid, &p) != 0) + 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)) @@ -992,7 +995,7 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) if ((xdr != 4) || strncmp((char *)p, "inet", 4)) return(-1); p++; - if (ippr_rpcb_getproto(rm, &re->re_proto, &p) != 0) + if (ipf_p_rpcb_getproto(rm, &re->re_proto, &p) != 0) return(-1); if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); @@ -1011,7 +1014,7 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) for(rl->rl_cnt = 0; rl->rl_cnt < cnt; rl->rl_cnt++) { re = &rl->rl_entries[rl->rl_cnt]; - rv = ippr_rpcb_getnat(fin, nat, + rv = ipf_p_rpcb_getnat(fin, nat, re->re_proto.xp_proto, (u_int)re->re_maddr.xu_port); if (rv != 0) @@ -1027,14 +1030,14 @@ ippr_rpcb_decoderep(fin, nat, rs, rm, rxp) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_lookup */ +/* 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 * -ippr_rpcb_lookup(rs, xid) +ipf_p_rpcb_lookup(rs, xid) rpcb_session_t *rs; u_32_t xid; { @@ -1051,7 +1054,7 @@ ippr_rpcb_lookup(rs, xid) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_deref */ +/* Function: ipf_p_rpcb_deref */ /* Returns: (void) */ /* Parameters: rs(I) - pointer to RPCB session */ /* rx(I) - pointer to RPC transaction struct to remove */ @@ -1062,7 +1065,7 @@ ippr_rpcb_lookup(rs, xid) /* Free the RPCB transaction record rx from the chain of entries. */ /* -------------------------------------------------------------------- */ static void -ippr_rpcb_deref(rs, rx) +ipf_p_rpcb_deref(rs, rx) rpcb_session_t *rs; rpcb_xact_t *rx; { @@ -1085,7 +1088,7 @@ ippr_rpcb_deref(rs, rx) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_getproto */ +/* Function: ipf_p_rpcb_getproto */ /* Returns: int - -1 == illegal protocol/netid, */ /* 0 == legal protocol/netid */ /* Parameters: rm(I) - pointer to RPC message structure */ @@ -1095,7 +1098,7 @@ ippr_rpcb_deref(rs, rx) /* Decode netid/proto stored at p and record its numeric value. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_getproto(rm, xp, p) +ipf_p_rpcb_getproto(rm, xp, p) rpc_msg_t *rm; xdr_proto_t *xp; u_32_t **p; @@ -1122,7 +1125,7 @@ ippr_rpcb_getproto(rm, xp, p) else { return(-1); } - + /* Advance past the string. */ (*p)++; @@ -1130,7 +1133,7 @@ ippr_rpcb_getproto(rm, xp, p) } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_getnat */ +/* Function: ipf_p_rpcb_getnat */ /* Returns: int -- -1 == failed to create table entries, */ /* 0 == success */ /* Parameters: fin(I) - pointer to packet information */ @@ -1142,12 +1145,13 @@ ippr_rpcb_getproto(rm, xp, p) /* attempt between RPC client and server. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_getnat(fin, nat, proto, port) +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; @@ -1159,15 +1163,13 @@ ippr_rpcb_getnat(fin, nat, proto, port) /* Generate dummy fr_info */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_out = 0; - fi.fin_src = fin->fin_dst; - fi.fin_dst = nat->nat_outip; 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); @@ -1195,18 +1197,18 @@ ippr_rpcb_getnat(fin, nat, proto, port) * If successful, fr_stlookup returns with ipf_state locked. We have * no use for this lock, so simply unlock it if necessary. */ - is = fr_stlookup(&fi, &tcp, NULL); + is = ipf_state_lookup(&fi, &tcp, NULL); if (is != NULL) { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); - WRITE_ENTER(&ipf_nat); - natl = nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst); + 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(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); return(0); } @@ -1220,6 +1222,10 @@ ippr_rpcb_getnat(fin, nat, proto, port) 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? @@ -1228,46 +1234,51 @@ ippr_rpcb_getnat(fin, nat, proto, port) bcopy((char *)ipn, (char *)&ipnat, sizeof(ipnat)); ipn->in_flags = nflags & IPN_TCPUDP; ipn->in_apr = NULL; - ipn->in_p = proto; - ipn->in_pmin = htons(fi.fin_dport); - ipn->in_pmax = htons(fi.fin_dport); - ipn->in_pnext = htons(fi.fin_dport); + 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 = '\0'; + ipn->in_plabel = -1; /* Create NAT entry. return NULL if this fails. */ - natl = nat_new(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE, + 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(&ipf_nat); + 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) nat_proto(&fi, natl, nflags); - nat_update(&fi, natl, natl->nat_ptr); + (void) ipf_nat_proto(&fi, natl, nflags); + MUTEX_ENTER(&natl->nat_lock); + ipf_nat_update(&fi, natl); + MUTEX_EXIT(&natl->nat_lock); } - MUTEX_DOWNGRADE(&ipf_nat); + MUTEX_DOWNGRADE(&softc->ipf_nat); if (is == NULL) { /* Create state entry. Return NULL if this fails. */ - fi.fin_dst = nat->nat_inip; - fi.fin_nat = (void *)natl; fi.fin_flx |= FI_NATED; fi.fin_flx &= ~FI_STATE; nflags &= NAT_TCPUDP; nflags |= SI_W_SPORT|SI_CLONE; - is = fr_addstate(&fi, NULL, nflags); - if (is == NULL) { + 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. @@ -1276,15 +1287,13 @@ ippr_rpcb_getnat(fin, nat, proto, port) */ return(-1); } - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); } return(0); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_modv3 */ +/* Function: ipf_p_rpcb_modv3 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ @@ -1296,7 +1305,7 @@ ippr_rpcb_getnat(fin, nat, proto, port) /* lengths as necessary. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_modv3(fin, nat, rm, m, off) +ipf_p_rpcb_modv3(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; @@ -1310,7 +1319,7 @@ ippr_rpcb_modv3(fin, nat, rm, m, off) int diff; rr = &rm->rm_resp; - i = (char *)&nat->nat_outip.s_addr; + i = (char *)&nat->nat_ndstaddr; p = (char *)&rr->rr_v3.xu_port; /* Form new string. */ @@ -1336,7 +1345,7 @@ ippr_rpcb_modv3(fin, nat, rm, m, off) /* Write new string. */ COPYBACK(m, off, xlen, uaddr); - + /* Determine difference in data lengths. */ diff = xlen - XDRALIGN(B(rr->rr_v3.xu_xslen)); @@ -1345,13 +1354,13 @@ ippr_rpcb_modv3(fin, nat, rm, m, off) * adjustments. */ if (diff != 0) - ippr_rpcb_fixlen(fin, diff); + ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_modv4 */ +/* Function: ipf_p_rpcb_modv4 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ @@ -1362,7 +1371,7 @@ ippr_rpcb_modv3(fin, nat, rm, m, off) /* Write new rpcb_entry list, adjusting lengths as necessary. */ /* -------------------------------------------------------------------- */ static int -ippr_rpcb_modv4(fin, nat, rm, m, off) +ipf_p_rpcb_modv4(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; @@ -1381,7 +1390,7 @@ ippr_rpcb_modv4(fin, nat, rm, m, off) rr = &rm->rm_resp; rl = &rr->rr_v4; - i = (char *)&nat->nat_outip.s_addr; + i = (char *)&nat->nat_ndstaddr; /* Determine mbuf offset to write to. */ re = &rl->rl_entries[0]; @@ -1432,14 +1441,14 @@ ippr_rpcb_modv4(fin, nat, rm, m, off) * adjustments. */ if (diff != 0) - ippr_rpcb_fixlen(fin, diff); + ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ -/* Function: ippr_rpcb_fixlen */ +/* Function: ipf_p_rpcb_fixlen */ /* Returns: (void) */ /* Parameters: fin(I) - pointer to packet information */ /* len(I) - change in packet length */ @@ -1448,7 +1457,7 @@ ippr_rpcb_modv4(fin, nat, rm, m, off) /* header fields. */ /* -------------------------------------------------------------------- */ static void -ippr_rpcb_fixlen(fin, len) +ipf_p_rpcb_fixlen(fin, len) fr_info_t *fin; int len; { @@ -1456,9 +1465,9 @@ ippr_rpcb_fixlen(fin, len) udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + len); - fin->fin_ip->ip_len += len; - fin->fin_dlen += 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.c b/sys/contrib/ipfilter/netinet/ip_rules.c index f080ec5b..434b9de 100644 --- a/sys/contrib/ipfilter/netinet/ip_rules.c +++ b/sys/contrib/ipfilter/netinet/ip_rules.c @@ -1,18 +1,29 @@ /* $FreeBSD$ */ /* -* Copyright (C) 1993-2000 by Darren Reed. +* Copyright (C) 2012 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ +#include <sys/param.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi) -# include <sys/systm.h> +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 40000) +# if defined(_KERNEL) +# include <sys/libkern.h> +# else +# include <sys/unistd.h> +# endif +#endif +#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 399000000) +#else +# if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi) +# include <sys/systm.h> +# endif #endif #include <sys/errno.h> #include <sys/param.h> @@ -40,18 +51,32 @@ #ifdef IPFILTER_COMPILED +extern ipf_main_softc_t ipfmain; + + static u_long in_rule__0[] = { -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80000000, 0x8002, 0, 0, 0, 0xffff, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +0, 0, 0, 0, 0, 0, 0, 0x8070d88, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0x1b0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40000000, 0x8002, 0, 0, 0, 0xffff, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0 }; static u_long out_rule__0[] = { -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80000000, 0x4002, 0, 0, 0, 0xffff, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +0, 0, 0, 0, 0, 0, 0, 0x8070d88, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0x1b0, 0x1, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40000000, 0x4002, 0, 0, 0, 0xffff, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0 }; frentry_t *ipf_rules_in_[1] = { (frentry_t *)&in_rule__0 }; +/* XXX This file (ip_rules.c) is not part of the ipfilter tarball, it is + XXX generated by the ipfilter build process. Unfortunately the build + XXX process did not generate the following lines so they are added + XXX by hand here. This is a bit of a hack but it works for now. Future + XXX imports/merges of ipfilter may generate this so the following will + XXX need to be removed following some future merge. + XXX */ +frentry_t *ipf_rules_out_[1] = { + (frentry_t *)&out_rule__0 +}; + frentry_t *ipfrule_match_in_(fin, passp) fr_info_t *fin; u_32_t *passp; @@ -62,10 +87,6 @@ u_32_t *passp; return fr; } -frentry_t *ipf_rules_out_[1] = { - (frentry_t *)&out_rule__0 -}; - frentry_t *ipfrule_match_out_(fin, passp) fr_info_t *fin; u_32_t *passp; @@ -87,9 +108,14 @@ int ipfrule_add_out_() fp = ipf_rules_out_[i]; fp->fr_next = NULL; for (j = i + 1; j < max; j++) - if (strncmp(fp->fr_group, + if (strncmp(fp->fr_names + fp->fr_group, + ipf_rules_out_[j]->fr_names + ipf_rules_out_[j]->fr_group, FR_GROUPLEN) == 0) { + if (ipf_rules_out_[j] != NULL) + ipf_rules_out_[j]->fr_pnext = + &fp->fr_next; + fp->fr_pnext = &ipf_rules_out_[j]; fp->fr_next = ipf_rules_out_[j]; break; } @@ -97,13 +123,14 @@ int ipfrule_add_out_() fp = &ipfrule_out_; bzero((char *)fp, sizeof(*fp)); - fp->fr_type = FR_T_CALLFUNC|FR_T_BUILTIN; + fp->fr_type = FR_T_CALLFUNC_BUILTIN; fp->fr_flags = FR_OUTQUE|FR_NOMATCH; fp->fr_data = (void *)ipf_rules_out_[0]; fp->fr_dsize = sizeof(ipf_rules_out_[0]); - fp->fr_v = 4; + fp->fr_family = AF_INET; fp->fr_func = (ipfunc_t)ipfrule_match_out_; - err = frrequest(IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, fr_active, 0); + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, + ipfmain.ipf_active, 0); return err; } @@ -129,8 +156,9 @@ int ipfrule_remove_out_() } } if (err == 0) - err = frrequest(IPL_LOGIPF, SIOCDELFR, - (caddr_t)&ipfrule_out_, fr_active, 0); + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCDELFR, + (caddr_t)&ipfrule_out_, + ipfmain.ipf_active, 0); if (err) return err; @@ -149,9 +177,14 @@ int ipfrule_add_in_() fp = ipf_rules_in_[i]; fp->fr_next = NULL; for (j = i + 1; j < max; j++) - if (strncmp(fp->fr_group, + if (strncmp(fp->fr_names + fp->fr_group, + ipf_rules_in_[j]->fr_names + ipf_rules_in_[j]->fr_group, FR_GROUPLEN) == 0) { + if (ipf_rules_in_[j] != NULL) + ipf_rules_in_[j]->fr_pnext = + &fp->fr_next; + fp->fr_pnext = &ipf_rules_in_[j]; fp->fr_next = ipf_rules_in_[j]; break; } @@ -159,13 +192,14 @@ int ipfrule_add_in_() fp = &ipfrule_in_; bzero((char *)fp, sizeof(*fp)); - fp->fr_type = FR_T_CALLFUNC|FR_T_BUILTIN; + fp->fr_type = FR_T_CALLFUNC_BUILTIN; fp->fr_flags = FR_INQUE|FR_NOMATCH; fp->fr_data = (void *)ipf_rules_in_[0]; fp->fr_dsize = sizeof(ipf_rules_in_[0]); - fp->fr_v = 4; + fp->fr_family = AF_INET; fp->fr_func = (ipfunc_t)ipfrule_match_in_; - err = frrequest(IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, fr_active, 0); + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, + ipfmain.ipf_active, 0); return err; } @@ -191,8 +225,9 @@ int ipfrule_remove_in_() } } if (err == 0) - err = frrequest(IPL_LOGIPF, SIOCDELFR, - (caddr_t)&ipfrule_in_, fr_active, 0); + err = frrequest(&ipfmain, IPL_LOGIPF, SIOCDELFR, + (caddr_t)&ipfrule_in_, + ipfmain.ipf_active, 0); if (err) return err; diff --git a/sys/contrib/ipfilter/netinet/ip_scan.c b/sys/contrib/ipfilter/netinet/ip_scan.c index 54acb2a..5b7c77e 100644 --- a/sys/contrib/ipfilter/netinet/ip_scan.c +++ b/sys/contrib/ipfilter/netinet/ip_scan.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1995-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -58,17 +58,17 @@ struct file; #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: ip_scan.c,v 2.40.2.10 2007/06/02 21:22:28 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #ifdef IPFILTER_SCAN /* endif at bottom of file */ -ipscan_t *ipsc_list = NULL, - *ipsc_tail = NULL; -ipscanstat_t ipsc_stat; +ipscan_t *ipf_scan_list = NULL, + *ipf_scan_tail = NULL; +ipscanstat_t ipf_scan_stat; # ifdef USE_MUTEXES -ipfrwlock_t ipsc_rwlock; +ipfrwlock_t ipf_scan_rwlock; # endif # ifndef isalpha @@ -77,42 +77,47 @@ ipfrwlock_t ipsc_rwlock; # endif -int ipsc_add __P((caddr_t)); -int ipsc_delete __P((caddr_t)); -struct ipscan *ipsc_lookup __P((char *)); -int ipsc_matchstr __P((sinfo_t *, char *, int)); -int ipsc_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *)); -int ipsc_match __P((ipstate_t *)); +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 *)); -static int ipsc_inited = 0; +static int ipf_scan_inited = 0; -int ipsc_init() +int +ipf_scan_init() { - RWLOCK_INIT(&ipsc_rwlock, "ip scan rwlock"); - ipsc_inited = 1; + RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock"); + ipf_scan_inited = 1; return 0; } -void fr_scanunload() +void +ipf_scan_unload(ipf_main_softc_t *arg) { - if (ipsc_inited == 1) { - RW_DESTROY(&ipsc_rwlock); - ipsc_inited = 0; + if (ipf_scan_inited == 1) { + RW_DESTROY(&ipf_scan_rwlock); + ipf_scan_inited = 0; } } -int ipsc_add(data) -caddr_t data; +int +ipf_scan_add(data) + caddr_t data; { ipscan_t *i, *isc; int err; KMALLOC(isc, ipscan_t *); - if (!isc) + if (!isc) { + ipf_interror = 90001; return ENOMEM; + } err = copyinptr(data, isc, sizeof(*isc)); if (err) { @@ -120,23 +125,24 @@ caddr_t data; return err; } - WRITE_ENTER(&ipsc_rwlock); + WRITE_ENTER(&ipf_scan_rwlock); - i = ipsc_lookup(isc->ipsc_tag); - if (i) { - RWLOCK_EXIT(&ipsc_rwlock); + i = ipf_scan_lookup(isc->ipsc_tag); + if (i != NULL) { + RWLOCK_EXIT(&ipf_scan_rwlock); KFREE(isc); + ipf_interror = 90002; return EEXIST; } - if (ipsc_tail) { - ipsc_tail->ipsc_next = isc; - isc->ipsc_pnext = &ipsc_tail->ipsc_next; - ipsc_tail = isc; + if (ipf_scan_tail) { + ipf_scan_tail->ipsc_next = isc; + isc->ipsc_pnext = &ipf_scan_tail->ipsc_next; + ipf_scan_tail = isc; } else { - ipsc_list = isc; - ipsc_tail = isc; - isc->ipsc_pnext = &ipsc_list; + ipf_scan_list = isc; + ipf_scan_tail = isc; + isc->ipsc_pnext = &ipf_scan_list; } isc->ipsc_next = NULL; @@ -145,14 +151,15 @@ caddr_t data; isc->ipsc_sref = 0; isc->ipsc_active = 0; - ipsc_stat.iscs_entries++; - RWLOCK_EXIT(&ipsc_rwlock); + ipf_scan_stat.iscs_entries++; + RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } -int ipsc_delete(data) -caddr_t data; +int +ipf_scan_remove(data) + caddr_t data; { ipscan_t isc, *i; int err; @@ -161,14 +168,15 @@ caddr_t data; if (err) return err; - WRITE_ENTER(&ipsc_rwlock); + WRITE_ENTER(&ipf_scan_rwlock); - i = ipsc_lookup(isc.ipsc_tag); + i = ipf_scan_lookup(isc.ipsc_tag); if (i == NULL) err = ENOENT; else { if (i->ipsc_fref) { - RWLOCK_EXIT(&ipsc_rwlock); + RWLOCK_EXIT(&ipf_scan_rwlock); + ipf_interror = 90003; return EBUSY; } @@ -176,61 +184,66 @@ caddr_t data; if (i->ipsc_next) i->ipsc_next->ipsc_pnext = i->ipsc_pnext; else { - if (i->ipsc_pnext == &ipsc_list) - ipsc_tail = NULL; + if (i->ipsc_pnext == &ipf_scan_list) + ipf_scan_tail = NULL; else - ipsc_tail = *(*i->ipsc_pnext)->ipsc_pnext; + ipf_scan_tail = *(*i->ipsc_pnext)->ipsc_pnext; } - ipsc_stat.iscs_entries--; + ipf_scan_stat.iscs_entries--; KFREE(i); } - RWLOCK_EXIT(&ipsc_rwlock); + RWLOCK_EXIT(&ipf_scan_rwlock); return err; } -struct ipscan *ipsc_lookup(tag) -char *tag; +struct ipscan * +ipf_scan_lookup(tag) + char *tag; { ipscan_t *i; - for (i = ipsc_list; i; i = i->ipsc_next) + for (i = ipf_scan_list; i; i = i->ipsc_next) if (!strcmp(i->ipsc_tag, tag)) return i; return NULL; } -int ipsc_attachfr(fr) -struct frentry *fr; +int +ipf_scan_attachfr(fr) + struct frentry *fr; { ipscan_t *i; - if (fr->fr_isctag[0]) { - READ_ENTER(&ipsc_rwlock); - i = ipsc_lookup(fr->fr_isctag); + 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(&ipsc_rwlock); - if (i == NULL) + RWLOCK_EXIT(&ipf_scan_rwlock); + if (i == NULL) { + ipf_interror = 90004; return ENOENT; + } fr->fr_isc = i; } return 0; } -int ipsc_attachis(is) -struct ipstate *is; +int +ipf_scan_attachis(is) + struct ipstate *is; { frentry_t *fr; ipscan_t *i; - READ_ENTER(&ipsc_rwlock); + READ_ENTER(&ipf_scan_rwlock); fr = is->is_rule; - if (fr) { + if (fr != NULL) { i = fr->fr_isc; if ((i != NULL) && (i != (ipscan_t *)-1)) { is->is_isc = i; @@ -245,13 +258,14 @@ struct ipstate *is; is->is_flags |= IS_SC_MATCHS; } } - RWLOCK_EXIT(&ipsc_rwlock); + RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } -int ipsc_detachfr(fr) -struct frentry *fr; +int +ipf_scan_detachfr(fr) + struct frentry *fr; { ipscan_t *i; @@ -263,18 +277,19 @@ struct frentry *fr; } -int ipsc_detachis(is) -struct ipstate *is; +int +ipf_scan_detachis(is) + struct ipstate *is; { ipscan_t *i; - READ_ENTER(&ipsc_rwlock); + 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(&ipsc_rwlock); + RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } @@ -282,10 +297,11 @@ struct ipstate *is; /* * 'string' compare for scanning */ -int ipsc_matchstr(sp, str, n) -sinfo_t *sp; -char *str; -int n; +int +ipf_scan_matchstr(sp, str, n) + sinfo_t *sp; + char *str; + int n; { char *s, *t, *up; int i = n; @@ -316,10 +332,11 @@ int n; * Returns 3 if both server and client match, 2 if just server, * 1 if just client */ -int ipsc_matchisc(isc, is, cl, sl, maxm) -ipscan_t *isc; -ipstate_t *is; -int cl, sl, maxm[2]; +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; @@ -348,7 +365,8 @@ int cl, sl, maxm[2]; i = 0; n = MIN(cl, isc->ipsc_clen); if ((n > 0) && (!maxm || (n >= maxm[1]))) { - if (!ipsc_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) { + if (!ipf_scan_matchstr(&isc->ipsc_cl, + is->is_sbuf[0], n)) { i++; ret |= 1; if (n > j) @@ -364,7 +382,8 @@ int cl, sl, maxm[2]; i = 0; n = MIN(cl, isc->ipsc_slen); if ((n > 0) && (!maxm || (n >= maxm[1]))) { - if (!ipsc_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) { + if (!ipf_scan_matchstr(&isc->ipsc_sl, + is->is_sbuf[1], n)) { i++; ret |= 2; if (n > k) @@ -381,8 +400,9 @@ int cl, sl, maxm[2]; } -int ipsc_match(is) -ipstate_t *is; +int +ipf_scan_match(is) + ipstate_t *is; { int i, j, k, n, cl, sl, maxm[2]; ipscan_t *isc, *lm; @@ -399,7 +419,7 @@ ipstate_t *is; /* * Known object to scan for. */ - i = ipsc_matchisc(isc, is, cl, sl, NULL); + i = ipf_scan_matchisc(isc, is, cl, sl, NULL); if (i & 1) { is->is_flags |= IS_SC_MATCHC; is->is_flags &= ~IS_SC_CLIENT; @@ -415,8 +435,8 @@ ipstate_t *is; lm = NULL; maxm[0] = 0; maxm[1] = 0; - for (k = 0, isc = ipsc_list; isc; isc = isc->ipsc_next) { - i = ipsc_matchisc(isc, is, cl, sl, maxm); + 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 @@ -475,7 +495,7 @@ ipstate_t *is; j = ISC_A_NONE; if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) { j = isc->ipsc_action; - ipsc_stat.iscs_acted++; + 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))) { @@ -483,7 +503,7 @@ ipstate_t *is; * Matching failed... */ j = isc->ipsc_else; - ipsc_stat.iscs_else++; + ipf_scan_stat.iscs_else++; } switch (j) @@ -508,9 +528,10 @@ ipstate_t *is; /* * check if a packet matches what we're scanning for */ -int ipsc_packet(fin, is) -fr_info_t *fin; -ipstate_t *is; +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; @@ -552,7 +573,7 @@ ipstate_t *is; if (j == 0) return 1; - (void) ipsc_match(is); + (void) ipf_scan_match(is); #if 0 /* * There is the potential here for plain text passwords to get @@ -567,11 +588,12 @@ ipstate_t *is; } -int fr_scan_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +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; @@ -579,17 +601,19 @@ void *ctx; switch (cmd) { case SIOCADSCA : - err = ipsc_add(data); + err = ipf_scan_add(data); break; case SIOCRMSCA : - err = ipsc_delete(data); + err = ipf_scan_remove(data); break; case SIOCGSCST : - bcopy((char *)&ipsc_stat, (char *)&ipscs, sizeof(ipscs)); - ipscs.iscs_list = ipsc_list; + bcopy((char *)&ipf_scan_stat, (char *)&ipscs, sizeof(ipscs)); + ipscs.iscs_list = ipf_scan_list; err = BCOPYOUT(&ipscs, data, sizeof(ipscs)); - if (err != 0) + if (err != 0) { + ipf_interror = 90005; err = EFAULT; + } break; default : err = EINVAL; diff --git a/sys/contrib/ipfilter/netinet/ip_scan.h b/sys/contrib/ipfilter/netinet/ip_scan.h index 4772d28..9903209 100644 --- a/sys/contrib/ipfilter/netinet/ip_scan.h +++ b/sys/contrib/ipfilter/netinet/ip_scan.h @@ -1,10 +1,10 @@ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_fil.h 1.35 6/5/96 - * $Id: ip_scan.h,v 2.9.2.2 2006/07/14 06:12:19 darrenr Exp $ + * $Id$ */ #ifndef __IP_SCAN_H__ @@ -94,13 +94,13 @@ typedef struct ipscanstat { } ipscanstat_t; -extern int fr_scan_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern int ipsc_init __P((void)); -extern int ipsc_attachis __P((struct ipstate *)); -extern int ipsc_attachfr __P((struct frentry *)); -extern int ipsc_detachis __P((struct ipstate *)); -extern int ipsc_detachfr __P((struct frentry *)); -extern int ipsc_packet __P((struct fr_info *, struct ipstate *)); -extern void fr_scanunload __P((void)); +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 *)); #endif /* __IP_SCAN_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_state.c b/sys/contrib/ipfilter/netinet/ip_state.c index 6c8b158..9c6a244 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.c +++ b/sys/contrib/ipfilter/netinet/ip_state.c @@ -1,9 +1,13 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-2003 by Darren Reed. + * 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 @@ -15,14 +19,6 @@ #include <sys/types.h> #include <sys/param.h> #include <sys/file.h> -#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ - defined(_KERNEL) -# if (__NetBSD_Version__ < 399001400) -# include "opt_ipfilter_log.h" -# else -# include "opt_ipfilter.h" -# endif -#endif #if defined(_KERNEL) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 400000) && !defined(KLD_MODULE) #include "opt_inet6.h" @@ -41,9 +37,6 @@ struct file; #if defined(_KERNEL) && (__FreeBSD_version >= 220000) # include <sys/filio.h> # include <sys/fcntl.h> -# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #else # include <sys/ioctl.h> #endif @@ -72,36 +65,31 @@ struct file; #ifdef sun # include <net/af.h> #endif -#include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/tcp.h> -#if !defined(linux) -# include <netinet/ip_var.h> -#endif #if !defined(__hpux) && !defined(linux) # include <netinet/tcp_fsm.h> #endif #include <netinet/udp.h> #include <netinet/ip_icmp.h> +#if !defined(_KERNEL) +# include "ipf.h" +#endif #include "netinet/ip_compat.h" -#include <netinet/tcpip.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" -#ifdef IPFILTER_SYNC +#include "netinet/ip_lookup.h" +#include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" -#endif -#ifdef IPFILTER_SCAN -#include "netinet/ip_scan.h" -#endif #ifdef USE_INET6 #include <netinet/icmp6.h> #endif -#if (__FreeBSD_version >= 300000) +#if FREEBSD_GE_REV(300000) # include <sys/malloc.h> # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include <sys/libkern.h> @@ -113,342 +101,501 @@ struct file; #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: ip_state.c,v 2.186.2.80 2007/10/16 09:33:23 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif -static ipstate_t **ips_table = NULL; -static u_long *ips_seed = NULL; -static int ips_num = 0; -static u_long ips_last_force_flush = 0; -ips_stat_t ips_stats; + +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 *fr_checkicmp6matchingstate __P((fr_info_t *)); +static ipstate_t *ipf_checkicmp6matchingstate __P((fr_info_t *)); #endif -static ipstate_t *fr_matchsrcdst __P((fr_info_t *, ipstate_t *, i6addr_t *, +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 *fr_checkicmpmatchingstate __P((fr_info_t *)); -static int fr_state_flush __P((int, int)); -static int fr_state_flush_entry __P((void *)); -static ips_stat_t *fr_statetstats __P((void)); -static int fr_delstate __P((ipstate_t *, int)); -static int fr_state_remove __P((caddr_t)); -static void fr_ipsmove __P((ipstate_t *, u_int)); -static int fr_tcpstate __P((fr_info_t *, tcphdr_t *, ipstate_t *)); -static int fr_tcpoptions __P((fr_info_t *, tcphdr_t *, tcpdata_t *)); -static ipstate_t *fr_stclone __P((fr_info_t *, tcphdr_t *, ipstate_t *)); -static void fr_fixinisn __P((fr_info_t *, ipstate_t *)); -static void fr_fixoutisn __P((fr_info_t *, ipstate_t *)); -static void fr_checknewisn __P((fr_info_t *, ipstate_t *)); -static int fr_stateiter __P((ipftoken_t *, ipfgeniter_t *)); -static int fr_stgettable __P((char *)); - -int fr_stputent __P((caddr_t)); -int fr_stgetent __P((caddr_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)); #define ONE_DAY IPF_TTLVAL(1 * 86400) /* 1 day */ #define FIVE_DAYS (5 * ONE_DAY) -#define DOUBLE_HASH(x) (((x) + ips_seed[(x) % fr_statesize]) % fr_statesize) - -u_long fr_tcpidletimeout = FIVE_DAYS, - fr_tcpclosewait = IPF_TTLVAL(2 * TCP_MSL), - fr_tcplastack = IPF_TTLVAL(30), - fr_tcptimeout = IPF_TTLVAL(2 * TCP_MSL), - fr_tcptimewait = IPF_TTLVAL(2 * TCP_MSL), - fr_tcpclosed = IPF_TTLVAL(30), - fr_tcphalfclosed = IPF_TTLVAL(2 * 3600), /* 2 hours */ - fr_udptimeout = IPF_TTLVAL(120), - fr_udpacktimeout = IPF_TTLVAL(12), - fr_icmptimeout = IPF_TTLVAL(60), - fr_icmpacktimeout = IPF_TTLVAL(6), - fr_iptimeout = IPF_TTLVAL(60); -int fr_statemax = IPSTATE_MAX, - fr_statesize = IPSTATE_SIZE; -int fr_state_doflush = 0, - fr_state_lock = 0, - fr_state_maxbucket = 0, - fr_state_maxbucket_reset = 1, - fr_state_init = 0; -ipftq_t ips_tqtqb[IPF_TCP_NSTATES], - ips_udptq, - ips_udpacktq, - ips_iptq, - ips_icmptq, - ips_icmpacktq, - ips_deletetq, - *ips_utqe = NULL; -#ifdef IPFILTER_LOG -int ipstate_logging = 1; -#else -int ipstate_logging = 0; -#endif -ipstate_t *ips_list = NULL; +#define DOUBLE_HASH(x) (((x) + softs->ipf_state_seed[(x) % \ + softs->ipf_state_size]) % softs->ipf_state_size) /* ------------------------------------------------------------------------ */ -/* Function: fr_stateinit */ +/* Function: ipf_state_main_load */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ -/* Initialise all the global variables used within the state code. */ -/* This action also includes initiailising locks. */ +/* A null-op function that exists as a placeholder so that the flow in */ +/* other functions is obvious. */ /* ------------------------------------------------------------------------ */ -int fr_stateinit() +int +ipf_state_main_load() { -#if defined(NEED_LOCAL_RAND) || !defined(_KERNEL) - struct timeval tv; + 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); +} + + +/* ------------------------------------------------------------------------ */ +/* 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(ips_table, ipstate_t **, fr_statesize * sizeof(ipstate_t *)); - if (ips_table == NULL) + KMALLOCS(softs->ipf_state_table, + ipstate_t **, softs->ipf_state_size * sizeof(ipstate_t *)); + if (softs->ipf_state_table == NULL) return -1; - bzero((char *)ips_table, fr_statesize * sizeof(ipstate_t *)); - KMALLOCS(ips_seed, u_long *, fr_statesize * sizeof(*ips_seed)); - if (ips_seed == NULL) + bzero((char *)softs->ipf_state_table, + softs->ipf_state_size * sizeof(ipstate_t *)); + + KMALLOCS(softs->ipf_state_seed, u_long *, + softs->ipf_state_size * sizeof(*softs->ipf_state_seed)); + if (softs->ipf_state_seed == NULL) return -2; -#if defined(NEED_LOCAL_RAND) || !defined(_KERNEL) - tv.tv_sec = 0; - GETKTIME(&tv); -#endif - for (i = 0; i < fr_statesize; i++) { + + for (i = 0; i < softs->ipf_state_size; i++) { /* - * XXX - ips_seed[X] should be a random number of sorts. + * XXX - ipf_state_seed[X] should be a random number of sorts. */ -#if !defined(NEED_LOCAL_RAND) && defined(_KERNEL) - ips_seed[i] = arc4random(); +#if FREEBSD_GE_REV(400000) + softs->ipf_state_seed[i] = arc4random(); #else - ips_seed[i] = ((u_long)ips_seed + i) * fr_statesize; - ips_seed[i] += tv.tv_sec; - ips_seed[i] *= (u_long)ips_seed; - ips_seed[i] ^= 0x5a5aa5a5; - ips_seed[i] *= fr_statemax; + softs->ipf_state_seed[i] = ((u_long)softs->ipf_state_seed + i) * + softs->ipf_state_size; + softs->ipf_state_seed[i] ^= 0xa5a55a5a; + softs->ipf_state_seed[i] *= (u_long)softs->ipf_state_seed; + softs->ipf_state_seed[i] ^= 0x5a5aa5a5; + softs->ipf_state_seed[i] *= softs->ipf_state_max; #endif } -#if defined(NEED_LOCAL_RAND) && defined(_KERNEL) - ipf_rand_push(ips_seed, fr_statesize * sizeof(*ips_seed)); -#endif - /* 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 + 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; - KMALLOCS(ips_stats.iss_bucketlen, u_long *, - fr_statesize * sizeof(u_long)); - if (ips_stats.iss_bucketlen == NULL) - return -1; - bzero((char *)ips_stats.iss_bucketlen, fr_statesize * sizeof(u_long)); - - if (fr_state_maxbucket == 0) { - for (i = fr_statesize; i > 0; i >>= 1) - fr_state_maxbucket++; - fr_state_maxbucket *= 2; - } - - ips_stats.iss_tcptab = ips_tqtqb; - fr_sttab_init(ips_tqtqb); - ips_tqtqb[IPF_TCP_NSTATES - 1].ifq_next = &ips_udptq; - ips_udptq.ifq_ttl = (u_long)fr_udptimeout; - ips_udptq.ifq_ref = 1; - ips_udptq.ifq_head = NULL; - ips_udptq.ifq_tail = &ips_udptq.ifq_head; - MUTEX_INIT(&ips_udptq.ifq_lock, "ipftq udp tab"); - ips_udptq.ifq_next = &ips_udpacktq; - ips_udpacktq.ifq_ttl = (u_long)fr_udpacktimeout; - ips_udpacktq.ifq_ref = 1; - ips_udpacktq.ifq_head = NULL; - ips_udpacktq.ifq_tail = &ips_udpacktq.ifq_head; - MUTEX_INIT(&ips_udpacktq.ifq_lock, "ipftq udpack tab"); - ips_udpacktq.ifq_next = &ips_icmptq; - ips_icmptq.ifq_ttl = (u_long)fr_icmptimeout; - ips_icmptq.ifq_ref = 1; - ips_icmptq.ifq_head = NULL; - ips_icmptq.ifq_tail = &ips_icmptq.ifq_head; - MUTEX_INIT(&ips_icmptq.ifq_lock, "ipftq icmp tab"); - ips_icmptq.ifq_next = &ips_icmpacktq; - ips_icmpacktq.ifq_ttl = (u_long)fr_icmpacktimeout; - ips_icmpacktq.ifq_ref = 1; - ips_icmpacktq.ifq_head = NULL; - ips_icmpacktq.ifq_tail = &ips_icmpacktq.ifq_head; - MUTEX_INIT(&ips_icmpacktq.ifq_lock, "ipftq icmpack tab"); - ips_icmpacktq.ifq_next = &ips_iptq; - ips_iptq.ifq_ttl = (u_long)fr_iptimeout; - ips_iptq.ifq_ref = 1; - ips_iptq.ifq_head = NULL; - ips_iptq.ifq_tail = &ips_iptq.ifq_head; - MUTEX_INIT(&ips_iptq.ifq_lock, "ipftq ip tab"); - ips_iptq.ifq_next = &ips_deletetq; - ips_deletetq.ifq_ttl = (u_long)1; - ips_deletetq.ifq_ref = 1; - ips_deletetq.ifq_head = NULL; - ips_deletetq.ifq_tail = &ips_deletetq.ifq_head; - MUTEX_INIT(&ips_deletetq.ifq_lock, "state delete queue"); - ips_deletetq.ifq_next = NULL; - - RWLOCK_INIT(&ipf_state, "ipf IP state rwlock"); - MUTEX_INIT(&ipf_stinsert, "ipf state insert mutex"); - fr_state_init = 1; - - ips_last_force_flush = fr_ticks; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stateunload */ -/* Returns: Nil */ -/* Parameters: Nil */ +/* 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. */ /* ------------------------------------------------------------------------ */ -void fr_stateunload() +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 = ips_list) != NULL) - fr_delstate(is, ISL_UNLOAD); + 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 fr_stateunload - * 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 complicationA + * 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 = ips_utqe; ifq != NULL; ifq = ifqnext) { + for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; - if (((ifq->ifq_flags & IFQF_PROXY) == 0) && - (fr_deletetimeoutqueue(ifq) == 0)) - fr_freetimeoutqueue(ifq); - } - ips_stats.iss_inuse = 0; - ips_num = 0; + if (ipf_deletetimeoutqueue(ifq) == 0) + ipf_freetimeoutqueue(softc, ifq); + } - if (fr_state_init == 1) { - fr_sttab_destroy(ips_tqtqb); - MUTEX_DESTROY(&ips_udptq.ifq_lock); - MUTEX_DESTROY(&ips_icmptq.ifq_lock); - MUTEX_DESTROY(&ips_udpacktq.ifq_lock); - MUTEX_DESTROY(&ips_icmpacktq.ifq_lock); - MUTEX_DESTROY(&ips_iptq.ifq_lock); - MUTEX_DESTROY(&ips_deletetq.ifq_lock); + 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 (ips_table != NULL) { - KFREES(ips_table, fr_statesize * sizeof(*ips_table)); - ips_table = NULL; + 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 (ips_seed != NULL) { - KFREES(ips_seed, fr_statesize * sizeof(*ips_seed)); - ips_seed = 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 (ips_stats.iss_bucketlen != NULL) { - KFREES(ips_stats.iss_bucketlen, fr_statesize * sizeof(u_long)); - ips_stats.iss_bucketlen = 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; } - if (fr_state_maxbucket_reset == 1) - fr_state_maxbucket = 0; + return 0; +} - if (fr_state_init == 1) { - fr_state_init = 0; - RW_DESTROY(&ipf_state); - MUTEX_DESTROY(&ipf_stinsert); - } + +/* ------------------------------------------------------------------------ */ +/* Function: ipf_state_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 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: fr_statetstats */ +/* Function: ipf_state_stats */ /* Returns: ips_state_t* - pointer to state stats structure */ -/* Parameters: Nil */ +/* 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 *fr_statetstats() +static ips_stat_t * +ipf_state_stats(softc) + ipf_main_softc_t *softc; { - ips_stats.iss_active = ips_num; - ips_stats.iss_statesize = fr_statesize; - ips_stats.iss_statemax = fr_statemax; - ips_stats.iss_table = ips_table; - ips_stats.iss_list = ips_list; - ips_stats.iss_ticks = fr_ticks; - return &ips_stats; + 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: fr_state_remove */ +/* Function: ipf_state_remove */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: data(I) - pointer to state structure to delete from table */ +/* 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 fr_state_remove(data) -caddr_t data; +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 = fr_inobj(data, &st, IPFOBJ_IPSTATE); + error = ipf_inobj(softc, data, NULL, &st, IPFOBJ_IPSTATE); if (error) return EFAULT; - WRITE_ENTER(&ipf_state); - for (sp = ips_list; sp; sp = sp->is_next) + 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_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))) { - fr_delstate(sp, ISL_REMOVE); - RWLOCK_EXIT(&ipf_state); + ipf_state_del(softc, sp, ISL_REMOVE); + RWLOCK_EXIT(&softc->ipf_state); return 0; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + + IPFERROR(100001); return ESRCH; } /* ------------------------------------------------------------------------ */ -/* Function: fr_state_ioctl */ +/* Function: ipf_state_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 */ +/* 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 fr_state_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +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); @@ -458,55 +605,75 @@ void *ctx; * Delete an entry from the state table. */ case SIOCDELST : - error = fr_state_remove(data); + error = ipf_state_remove(softc, data); break; /* * Flush the state table */ case SIOCIPFFL : - error = BCOPYIN(data, (char *)&arg, sizeof(arg)); + error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { + IPFERROR(100002); error = EFAULT; + } else { - WRITE_ENTER(&ipf_state); - ret = fr_state_flush(arg, 4); - RWLOCK_EXIT(&ipf_state); - error = BCOPYOUT((char *)&ret, data, sizeof(ret)); - if (error != 0) + 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, (char *)&arg, sizeof(arg)); + error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { + IPFERROR(100004); error = EFAULT; + } else { - WRITE_ENTER(&ipf_state); - ret = fr_state_flush(arg, 6); - RWLOCK_EXIT(&ipf_state); - error = BCOPYOUT((char *)&ret, data, sizeof(ret)); - if (error != 0) + 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)) + if (!(mode & FWRITE)) { + IPFERROR(100008); error = EPERM; - else { + } else { int tmp; - tmp = ipflog_clear(IPL_LOGSTATE); - error = BCOPYOUT((char *)&tmp, data, sizeof(tmp)); - if (error != 0) + tmp = ipf_log_clear(softc, IPL_LOGSTATE); + error = BCOPYOUT(&tmp, data, sizeof(tmp)); + if (error != 0) { + IPFERROR(100009); error = EFAULT; + } } break; @@ -514,13 +681,16 @@ void *ctx; * Turn logging of state information on/off. */ case SIOCSETLG : - if (!(mode & FWRITE)) + if (!(mode & FWRITE)) { + IPFERROR(100010); error = EPERM; - else { - error = BCOPYIN((char *)data, (char *)&ipstate_logging, - sizeof(ipstate_logging)); - if (error != 0) + } else { + error = BCOPYIN(data, &softs->ipf_state_logging, + sizeof(softs->ipf_state_logging)); + if (error != 0) { + IPFERROR(100011); error = EFAULT; + } } break; @@ -528,20 +698,24 @@ void *ctx; * Return the current state of logging. */ case SIOCGETLG : - error = BCOPYOUT((char *)&ipstate_logging, (char *)data, - sizeof(ipstate_logging)); - if (error != 0) + 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 = iplused[IPL_LOGSTATE]; /* returned in an int */ - error = BCOPYOUT((char *)&arg, data, sizeof(arg)); - if (error != 0) + arg = ipf_log_bytesused(softc, IPL_LOGSTATE); + error = BCOPYOUT(&arg, data, sizeof(arg)); + if (error != 0) { + IPFERROR(100013); error = EFAULT; + } break; #endif @@ -549,7 +723,8 @@ void *ctx; * Get the current state statistics. */ case SIOCGETFS : - error = fr_outobj(data, fr_statetstats(), IPFOBJ_STATESTAT); + error = ipf_outobj(softc, data, ipf_state_stats(softc), + IPFOBJ_STATESTAT); break; /* @@ -558,9 +733,10 @@ void *ctx; */ case SIOCSTLCK : if (!(mode & FWRITE)) { + IPFERROR(100014); error = EPERM; } else { - error = fr_lock(data, &fr_state_lock); + error = ipf_lock(data, &softs->ipf_state_lock); } break; @@ -568,74 +744,86 @@ void *ctx; * Add an entry to the current state table. */ case SIOCSTPUT : - if (!fr_state_lock || !(mode &FWRITE)) { + if (!softs->ipf_state_lock || !(mode &FWRITE)) { + IPFERROR(100015); error = EACCES; break; } - error = fr_stputent(data); + error = ipf_state_putent(softc, softs, data); break; /* * Get a state table entry. */ case SIOCSTGET : - if (!fr_state_lock) { + if (!softs->ipf_state_lock) { + IPFERROR(100016); error = EACCES; break; } - error = fr_stgetent(data); + error = ipf_state_getent(softc, softs, data); break; /* * Return a copy of the hash table bucket lengths */ case SIOCSTAT1 : - error = BCOPYOUT(ips_stats.iss_bucketlen, data, - fr_statesize * sizeof(u_long)); - if (error != 0) + 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 = fr_inobj(data, &iter, IPFOBJ_GENITER); + error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); - token = ipf_findtoken(IPFGENITER_STATE, uid, ctx); - if (token != NULL) - error = fr_stateiter(token, &iter); - else + 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; - RWLOCK_EXIT(&ipf_tokens); + } SPL_X(s); break; } case SIOCGTABL : - error = fr_stgettable(data); + error = ipf_state_gettable(softc, softs, data); break; case SIOCIPFDELTOK : - error = BCOPYIN(data, (char *)&arg, sizeof(arg)); + error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { + IPFERROR(100019); error = EFAULT; } else { SPL_SCHED(s); - error = ipf_deltoken(arg, uid, ctx); + error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } break; case SIOCGTQTAB : - error = fr_outobj(data, ips_tqtqb, IPFOBJ_STATETQTAB); + error = ipf_outobj(softc, data, softs->ipf_state_tcptq, + IPFOBJ_STATETQTAB); break; default : + IPFERROR(100020); error = EINVAL; break; } @@ -644,9 +832,11 @@ void *ctx; /* ------------------------------------------------------------------------ */ -/* Function: fr_stgetent */ +/* Function: ipf_state_getent */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: data(I) - pointer to state structure to retrieve from table */ +/* 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 */ @@ -654,25 +844,30 @@ void *ctx; /* the struct passed in and if not null and not found in the list of current*/ /* state entries, the retrieval fails. */ /* ------------------------------------------------------------------------ */ -int fr_stgetent(data) -caddr_t data; +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 = fr_inobj(data, &ips, IPFOBJ_STATESAVE); - if (error != 0) - return error; + error = ipf_inobj(softc, data, NULL, &ips, IPFOBJ_STATESAVE); + if (error) + return EFAULT; - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); isn = ips.ips_next; if (isn == NULL) { - isn = ips_list; + isn = softs->ipf_state_list; if (isn == NULL) { - RWLOCK_EXIT(&ipf_state); - if (ips.ips_next == NULL) + if (ips.ips_next == NULL) { + RWLOCK_EXIT(&softc->ipf_state); + IPFERROR(100021); return ENOENT; + } return 0; } } else { @@ -681,11 +876,12 @@ caddr_t data; * current list of entries. Security precaution to prevent * copying of random kernel data. */ - for (is = ips_list; is; is = is->is_next) + for (is = softs->ipf_state_list; is; is = is->is_next) if (is == isn) break; - if (is == NULL) { - RWLOCK_EXIT(&ipf_state); + if (!is) { + RWLOCK_EXIT(&softc->ipf_state); + IPFERROR(100022); return ESRCH; } } @@ -695,24 +891,29 @@ caddr_t data; if (isn->is_rule != NULL) bcopy((char *)isn->is_rule, (char *)&ips.ips_fr, sizeof(ips.ips_fr)); - RWLOCK_EXIT(&ipf_state); - error = fr_outobj(data, &ips, IPFOBJ_STATESAVE); + RWLOCK_EXIT(&softc->ipf_state); + error = ipf_outobj(softc, data, &ips, IPFOBJ_STATESAVE); return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stputent */ +/* Function: ipf_state_putent */ /* Returns: int - 0 == success, != 0 == failure */ -/* Parameters: data(I) - pointer to state information struct */ +/* 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 fr_stputent(data) -caddr_t data; +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; @@ -720,13 +921,15 @@ caddr_t data; frentry_t *fr; char *name; - error = fr_inobj(data, &ips, IPFOBJ_STATESAVE); - if (error) - return EFAULT; + error = ipf_inobj(softc, data, NULL, &ips, IPFOBJ_STATESAVE); + if (error != 0) + return error; KMALLOC(isn, ipstate_t *); - if (isn == NULL) + if (isn == NULL) { + IPFERROR(100023); return ENOMEM; + } bcopy((char *)&ips.ips_is, (char *)isn, sizeof(*isn)); bzero((char *)isn, offsetof(struct ipstate, is_pkts)); @@ -742,17 +945,21 @@ caddr_t data; fr = ips.ips_rule; if (fr == NULL) { - READ_ENTER(&ipf_state); - fr_stinsert(isn, 0); + int inserr; + + READ_ENTER(&softc->ipf_state); + inserr = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); - RWLOCK_EXIT(&ipf_state); - return 0; + 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)); @@ -766,10 +973,19 @@ caddr_t data; * Look up all the interface names in the rule. */ for (i = 0; i < 4; i++) { - name = fr->fr_ifnames[i]; - fr->fr_ifas[i] = fr_resolvenic(name, fr->fr_v); + if (fr->fr_ifnames[i] == -1) { + fr->fr_ifas[i] = NULL; + continue; + } + name = fr->fr_names + fr->fr_ifnames[i]; + fr->fr_ifas[i] = ipf_resolvenic(softc, name, + fr->fr_family); + } + + for (i = 0; i < 4; i++) { name = isn->is_ifname[i]; - isn->is_ifp[i] = fr_resolvenic(name, isn->is_v); + isn->is_ifp[i] = ipf_resolvenic(softc, name, + isn->is_v); } fr->fr_ref = 0; @@ -777,31 +993,35 @@ caddr_t data; fr->fr_data = NULL; fr->fr_type = FR_T_NONE; - fr_resolvedest(&fr->fr_tifs[0], fr->fr_v); - fr_resolvedest(&fr->fr_tifs[1], fr->fr_v); - fr_resolvedest(&fr->fr_dif, fr->fr_v); + (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 = fr_outobj(data, &ips, IPFOBJ_STATESAVE); - if (error) { + 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(&ipf_state); - fr_stinsert(isn, 0); + READ_ENTER(&softc->ipf_state); + error = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } else { - READ_ENTER(&ipf_state); - for (is = ips_list; is; is = is->is_next) + READ_ENTER(&softc->ipf_state); + for (is = softs->ipf_state_list; is; is = is->is_next) if (is->is_rule == fr) { - fr_stinsert(isn, 0); + error = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); break; } @@ -810,60 +1030,62 @@ caddr_t data; KFREE(isn); isn = NULL; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); - return (isn == NULL) ? ESRCH : 0; + if (isn == NULL) { + IPFERROR(100033); + error = ESRCH; + } } - return 0; + return error; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stinsert */ -/* Returns: Nil */ -/* Parameters: is(I) - pointer to state structure */ -/* rev(I) - flag indicating forward/reverse direction of packet */ +/* 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 filteirng assigned to it. */ +/* */ /* Locking: it is assumed that some kind of lock on ipf_state is held. */ -/* Exits with is_lock initialised and held. */ +/* Exits with is_lock initialised and held - *EVEN IF ERROR*. */ /* ------------------------------------------------------------------------ */ -void fr_stinsert(is, rev) -ipstate_t *is; -int rev; +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; - MUTEX_INIT(&is->is_lock, "ipf state entry"); - - fr = is->is_rule; - if (fr != NULL) { - MUTEX_ENTER(&fr->fr_lock); - fr->fr_ref++; - fr->fr_statecnt++; - MUTEX_EXIT(&fr->fr_lock); - } - /* * Look up all the interface names in the state entry. */ for (i = 0; i < 4; i++) { if (is->is_ifp[i] != NULL) continue; - is->is_ifp[i] = fr_resolvenic(is->is_ifname[i], is->is_v); + is->is_ifp[i] = ipf_resolvenic(softc, is->is_ifname[i], + is->is_v); } /* - * If we could trust is_hv, then the modulous would not be needed, but - * when running with IPFILTER_SYNC, this stops bad values. + * If we could trust is_hv, then the modulous would not be needed, + * but when running with IPFILTER_SYNC, this stops bad values. */ - hv = is->is_hv % fr_statesize; + hv = is->is_hv % softs->ipf_state_size; + /* TRACE is, hv */ is->is_hv = hv; /* @@ -871,37 +1093,265 @@ int rev; * 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(&ipf_stinsert); + 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 (ips_list != NULL) - ips_list->is_pnext = &is->is_next; - is->is_pnext = &ips_list; - is->is_next = ips_list; - ips_list = is; - - if (ips_table[hv] != NULL) - ips_table[hv]->is_phnext = &is->is_hnext; + 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 - ips_stats.iss_inuse++; - is->is_phnext = ips_table + hv; - is->is_hnext = ips_table[hv]; - ips_table[hv] = is; - ips_stats.iss_bucketlen[hv]++; - ips_num++; - MUTEX_EXIT(&ipf_stinsert); - - fr_setstatequeue(is, rev); + rv = 0; + + return (rv); } /* ------------------------------------------------------------------------ */ -/* Function: fr_addstate */ -/* Returns: ipstate_t* - NULL == failure, else pointer to new state */ -/* Parameters: fin(I) - pointer to packet information */ +/* 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 */ @@ -916,25 +1366,49 @@ int rev; /* either outlive this (not expired) or will deref the ip_state_t */ /* when they are deleted. */ /* ------------------------------------------------------------------------ */ -ipstate_t *fr_addstate(fin, stsave, flags) -fr_info_t *fin; -ipstate_t **stsave; -u_int flags; +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; - grehdr_t *gre; + frdest_t *fdp; int out; - if (fr_state_lock || - (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD))) - return NULL; + /* + * If a packet that was created locally is trying to go out but we + * do not match here here because of this lock, it is likely that + * the policy will block it and return network unreachable back 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_OOW) && !(fin->fin_tcpf & TH_SYN)) - return NULL; + 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 @@ -948,26 +1422,49 @@ u_int flags; */ fr = fin->fin_fr; if (fr != NULL) { - if ((ips_num >= fr_statemax) && (fr->fr_statemax == 0)) { - ATOMIC_INCL(ips_stats.iss_max); - fr_state_doflush = 1; - return 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)) { - ATOMIC_INCL(ips_stats.iss_maxref); - return NULL; + SBUMPD(ipf_state_stats, iss_max_ref); + return 2; } } - pass = (fr == NULL) ? 0 : fr->fr_flags; + 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; - is = &ips; bzero((char *)is, sizeof(*is)); - is->is_die = 1 + fr_ticks; + 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 it's 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... @@ -1008,13 +1505,8 @@ u_int flags; #endif if ((fin->fin_v == 4) && (fin->fin_flx & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST))) { - if (fin->fin_out == 0) { - flags |= SI_W_DADDR|SI_CLONE; - hv -= is->is_daddr; - } else { - flags |= SI_W_SADDR|SI_CLONE; - hv -= is->is_saddr; - } + flags |= SI_W_DADDR; + hv -= is->is_daddr; } switch (is->is_p) @@ -1026,9 +1518,8 @@ u_int flags; switch (ic->icmp_type) { case ICMP6_ECHO_REQUEST : - is->is_icmp.ici_type = ic->icmp_type; hv += (is->is_icmp.ici_id = ic->icmp_id); - break; + /*FALLTHROUGH*/ case ICMP6_MEMBERSHIP_QUERY : case ND_ROUTER_SOLICIT : case ND_NEIGHBOR_SOLICIT : @@ -1036,9 +1527,9 @@ u_int flags; is->is_icmp.ici_type = ic->icmp_type; break; default : - return NULL; + SBUMPD(ipf_state_stats, iss_icmp6_notquery); + return -2; } - ATOMIC_INCL(ips_stats.iss_icmp); break; #endif case IPPROTO_ICMP : @@ -1054,11 +1545,12 @@ u_int flags; hv += (is->is_icmp.ici_id = ic->icmp_id); break; default : - return NULL; + SBUMPD(ipf_state_stats, iss_icmp_notquery); + return -3; } - ATOMIC_INCL(ips_stats.iss_icmp); break; +#if 0 case IPPROTO_GRE : gre = fin->fin_dp; @@ -1069,12 +1561,18 @@ u_int flags; is->is_call[1] = fin->fin_data[1]; } break; +#endif case IPPROTO_TCP : tcp = fin->fin_dp; - if (tcp->th_flags & TH_RST) - return NULL; + 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. @@ -1086,6 +1584,8 @@ u_int flags; 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. @@ -1110,15 +1610,14 @@ u_int flags; if ((tcp->th_flags & ~(TH_FIN|TH_ACK|TH_ECNALL)) == TH_SYN && (TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { - if (fr_tcpoptions(fin, tcp, - &is->is_tcp.ts_data[0]) == -1) { + if (ipf_tcpoptions(softs, fin, tcp, + &is->is_tcp.ts_data[0]) == -1) fin->fin_flx |= FI_BAD; - } } if ((fin->fin_out != 0) && (pass & FR_NEWISN) != 0) { - fr_checknewisn(fin, is); - fr_fixoutisn(fin, is); + ipf_checknewisn(fin, is); + ipf_fixoutisn(fin, is); } if ((tcp->th_flags & TH_OPENING) == TH_SYN) @@ -1132,11 +1631,10 @@ u_int flags; } /* - * 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. + * 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. */ - ATOMIC_INCL(ips_stats.iss_tcp); break; case IPPROTO_UDP : @@ -1148,7 +1646,6 @@ u_int flags; hv += tcp->th_dport; hv += tcp->th_sport; } - ATOMIC_INCL(ips_stats.iss_udp); break; default : @@ -1156,144 +1653,173 @@ u_int flags; } hv = DOUBLE_HASH(hv); is->is_hv = hv; - is->is_rule = fr; - is->is_flags = flags & IS_INHERITED; /* * Look for identical state. */ - for (is = ips_table[is->is_hv % fr_statesize]; is != NULL; - is = is->is_hnext) { - if (bcmp(&ips.is_src, &is->is_src, - offsetof(struct ipstate, is_ps) - - offsetof(struct ipstate, is_src)) == 0) + 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) - return NULL; + if (is != NULL) { + SBUMPD(ipf_state_stats, iss_add_dup); + return 3; + } - if (ips_stats.iss_bucketlen[hv] >= fr_state_maxbucket) { - ATOMIC_INCL(ips_stats.iss_bucketfull); - return NULL; + if (softs->ipf_state_stats.iss_bucketlen[hv] >= + softs->ipf_state_maxbucket) { + SBUMPD(ipf_state_stats, iss_bucket_full); + return 4; } KMALLOC(is, ipstate_t *); if (is == NULL) { - ATOMIC_INCL(ips_stats.iss_nomem); - return 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 modulous here, it is done in fr_stinsert(). + * Do not do the modulous here, it is done in ipf_state_insert(). */ if (fr != NULL) { - (void) strncpy(is->is_group, fr->fr_group, FR_GROUPLEN); + ipftq_t *tq; + + (void) strncpy(is->is_group, FR_NAME(fr, fr_group), + FR_GROUPLEN); if (fr->fr_age[0] != 0) { - is->is_tqehead[0] = fr_addtimeoutqueue(&ips_utqe, - fr->fr_age[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) { - is->is_tqehead[1] = fr_addtimeoutqueue(&ips_utqe, - fr->fr_age[1]); + 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; - - /* - * 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][0] == '-' && - fr->fr_ifnames[out << 1][1] == '\0')) { - is->is_ifp[out << 1] = fr->fr_ifas[0]; - strncpy(is->is_ifname[out << 1], fr->fr_ifnames[0], - sizeof(fr->fr_ifnames[0])); - } else { - is->is_ifp[out << 1] = fin->fin_ifp; - COPYIFNAME(is->is_v, fin->fin_ifp, - is->is_ifname[out << 1]); - } - - is->is_ifp[(out << 1) + 1] = fr->fr_ifas[1]; - strncpy(is->is_ifname[(out << 1) + 1], fr->fr_ifnames[1], - sizeof(fr->fr_ifnames[1])); - - is->is_ifp[(1 - out) << 1] = fr->fr_ifas[2]; - strncpy(is->is_ifname[((1 - out) << 1)], fr->fr_ifnames[2], - sizeof(fr->fr_ifnames[2])); - - is->is_ifp[((1 - out) << 1) + 1] = fr->fr_ifas[3]; - strncpy(is->is_ifname[((1 - out) << 1) + 1], fr->fr_ifnames[3], - sizeof(fr->fr_ifnames[3])); - } else { - pass = fr_flags; - is->is_tag = FR_NOLOGTAG; - - if (fin->fin_ifp != NULL) { - is->is_ifp[out << 1] = fin->fin_ifp; - COPYIFNAME(is->is_v, fin->fin_ifp, - is->is_ifname[out << 1]); - } } /* - * It may seem strange to set is_ref to 2, but fr_check() will call - * fr_statederef() after calling fr_addstate() and the idea is to - * have it exist at the end of fr_check() with is_ref == 1. + * 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 osmething with it. */ - is->is_ref = 2; - is->is_pass = pass; + 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; - /* - * We want to check everything that is a property of this packet, - * but we don't (automatically) care about it's fragment status as - * this may change. - */ - is->is_v = fin->fin_v; - is->is_opt[0] = fin->fin_optmsk; - is->is_optmsk[0] = 0xffffffff; - is->is_optmsk[1] = 0xffffffff; - if (is->is_v == 6) { - is->is_opt[0] &= ~0x8; - is->is_optmsk[0] &= ~0x8; - is->is_optmsk[1] &= ~0x8; - } - is->is_me = stsave; - is->is_sec = fin->fin_secmsk; - is->is_secmsk = 0xffff; - is->is_auth = fin->fin_auth; - is->is_authmsk = 0xffff; - if (flags & (SI_WILDP|SI_WILDA)) { - ATOMIC_INCL(ips_stats.iss_wild); + 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; } - is->is_rulen = fin->fin_rule; + /* + * 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->fr_names + 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]); + } - if (pass & FR_LOGFIRST) - is->is_pass &= ~(FR_LOGFIRST|FR_LOG); + is->is_ifp[(out << 1) + 1] = fr->fr_ifas[1]; + if (fr->fr_ifnames[1] != -1) { + strncpy(is->is_ifname[(out << 1) + 1], + fr->fr_names + fr->fr_ifnames[1], + sizeof(fr->fr_ifnames[1])); + } - READ_ENTER(&ipf_state); + is->is_ifp[(1 - out) << 1] = fr->fr_ifas[2]; + if (fr->fr_ifnames[2] != -1) { + strncpy(is->is_ifname[((1 - out) << 1)], + fr->fr_names + fr->fr_ifnames[2], + sizeof(fr->fr_ifnames[2])); + } - fr_stinsert(is, fin->fin_rev); + 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->fr_names + 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) { /* @@ -1301,56 +1827,79 @@ u_int flags; * timer on it as we'll never see an error if it fails to * connect. */ - (void) fr_tcp_age(&is->is_sti, fin, ips_tqtqb, is->is_flags); - MUTEX_EXIT(&is->is_lock); -#ifdef IPFILTER_SCAN - if ((is->is_flags & SI_CLONE) == 0) - (void) ipsc_attachis(is); -#endif - } else { - MUTEX_EXIT(&is->is_lock); + (void) ipf_tcp_age(&is->is_sti, fin, softs->ipf_state_tcptq, + is->is_flags, 2); } -#ifdef IPFILTER_SYNC + MUTEX_EXIT(&is->is_lock); if ((is->is_flags & IS_STATESYNC) && ((is->is_flags & SI_CLONE) == 0)) - is->is_sync = ipfsync_new(SMC_STATE, fin, is); -#endif - if (ipstate_logging) - ipstate_log(is, ISL_NEW); + 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); - RWLOCK_EXIT(&ipf_state); - fin->fin_state = is; - fin->fin_rev = IP6_NEQ(&is->is_dst, &fin->fin_daddr); fin->fin_flx |= FI_STATE; if (fin->fin_flx & FI_FRAG) - (void) fr_newfrag(fin, pass ^ FR_KEEPSTATE); + (void) ipf_frag_new(softc, fin, pass); - return is; + 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: fr_tcpoptions */ +/* Function: ipf_tcpoptions */ /* Returns: int - 1 == packet matches state entry, 0 == it does not, */ /* -1 == packet has bad TCP options data */ -/* Parameters: fin(I) - pointer to packet information */ +/* 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 fr_tcpoptions(fin, tcp, td) -fr_info_t *fin; -tcphdr_t *tcp; -tcpdata_t *td; +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) + 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; @@ -1422,14 +1971,19 @@ tcpdata_t *td; len -= ol; s += ol; } + if (retval == -1) { + SBUMPD(ipf_state_stats, iss_tcp_badopt); + } return retval; } /* ------------------------------------------------------------------------ */ -/* Function: fr_tcpstate */ +/* Function: ipf_state_tcp */ /* Returns: int - 1 == packet matches state entry, 0 == it does not */ -/* Parameters: fin(I) - pointer to packet information */ +/* 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 */ /* */ @@ -1437,16 +1991,19 @@ tcpdata_t *td; /* 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 fr_tcpstate(fin, tcp, is) -fr_info_t *fin; -tcphdr_t *tcp; -ipstate_t *is; +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; { - int source, ret = 0, flags; tcpdata_t *fdata, *tdata; + int source, ret, flags; source = !fin->fin_rev; - if (((is->is_flags & IS_TCPFSM) != 0) && (source == 1) && + 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]; @@ -1462,34 +2019,37 @@ ipstate_t *is; if ((is->is_state[0] > IPF_TCPS_ESTABLISHED) && (is->is_state[1] > IPF_TCPS_ESTABLISHED)) { is->is_state[!source] = IPF_TCPS_CLOSED; - fr_movequeue(&is->is_sti, is->is_sti.tqe_ifq, - &ips_deletetq); + 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; } } - ret = fr_tcpinwindow(fin, fdata, tdata, tcp, is->is_flags); + if (is->is_flags & IS_LOOSE) + ret = 1; + else + ret = ipf_state_tcpinwindow(fin, fdata, tdata, tcp, + is->is_flags); if (ret > 0) { -#ifdef IPFILTER_SCAN - if (is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER)) { - ipsc_packet(fin, is); - if (FR_ISBLOCK(is->is_pass)) { - MUTEX_EXIT(&is->is_lock); - return 1; - } - } -#endif - /* * Nearing end of connection, start timeout. */ - ret = fr_tcp_age(&is->is_sti, fin, ips_tqtqb, is->is_flags); + 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. @@ -1503,25 +2063,29 @@ ipstate_t *is; 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 (fr_tcpoptions(fin, tcp, fdata) == -1) + if (ipf_tcpoptions(softs, fin, tcp, + fdata) == -1) fin->fin_flx |= FI_BAD; } if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) - fr_checknewisn(fin, is); + 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 (fr_tcpoptions(fin, tcp, fdata) == -1) + if (ipf_tcpoptions(softs, fin, tcp, + fdata) == -1) fin->fin_flx |= FI_BAD; } if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) - fr_checknewisn(fin, is); + ipf_checknewisn(fin, is); } ret = 1; } else { - fin->fin_flx |= FI_OOW; + 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; @@ -1529,7 +2093,7 @@ ipstate_t *is; /* ------------------------------------------------------------------------ */ -/* Function: fr_checknewisn */ +/* Function: ipf_checknewisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ @@ -1540,9 +2104,10 @@ ipstate_t *is; /* NOTE: This does not actually change the sequence numbers, only gets new */ /* one ready. */ /* ------------------------------------------------------------------------ */ -static void fr_checknewisn(fin, is) -fr_info_t *fin; -ipstate_t *is; +static void +ipf_checknewisn(fin, is) + fr_info_t *fin; + ipstate_t *is; { u_32_t sumd, old, new; tcphdr_t *tcp; @@ -1554,7 +2119,7 @@ ipstate_t *is; if (((i == 0) && !(is->is_flags & IS_ISNSYN)) || ((i == 1) && !(is->is_flags & IS_ISNACK))) { old = ntohl(tcp->th_seq); - new = fr_newisn(fin); + new = ipf_newisn(fin); is->is_isninc[i] = new - old; CALC_SUMD(old, new, sumd); is->is_sumd[i] = (sumd & 0xffff) + (sumd >> 16); @@ -1565,9 +2130,8 @@ ipstate_t *is; /* ------------------------------------------------------------------------ */ -/* Function: fr_tcpinwindow */ -/* Returns: int - 1 == packet inside TCP "window", 0 == not inside, */ -/* 2 == packet seq number matches next expected */ +/* 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) */ @@ -1577,12 +2141,15 @@ ipstate_t *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. */ /* ------------------------------------------------------------------------ */ -int fr_tcpinwindow(fin, fdata, tdata, tcp, flags) -fr_info_t *fin; -tcpdata_t *fdata, *tdata; -tcphdr_t *tcp; -int flags; +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; @@ -1651,12 +2218,17 @@ int flags; /* * Strict sequencing only allows in-order delivery. */ - if (seq != fdata->td_end) { - if ((flags & IS_STRICT) != 0) { + 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)) && @@ -1672,6 +2244,8 @@ int flags; } 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 @@ -1693,7 +2267,7 @@ int flags; * accepted, even if it appears out of sequence. */ inseq = 1; - } else + } else #endif if (!(fdata->td_winflags & (TCP_WSCALE_SEEN|TCP_WSCALE_FIRST))) { @@ -1737,12 +2311,14 @@ int flags; tdata->td_maxend = ack + win; return 1; } + SBUMP(ipf_state_stats.iss_oow); + fin->fin_flx |= FI_OOW; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stclone */ +/* Function: ipf_state_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ @@ -1751,27 +2327,40 @@ int flags; /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ -static ipstate_t *fr_stclone(fin, tcp, is) -fr_info_t *fin; -tcphdr_t *tcp; -ipstate_t *is; +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 (ips_num == fr_statemax) { - ATOMIC_INCL(ips_stats.iss_max); - fr_state_doflush = 1; + 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) + 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 + fr_ticks; + 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) + @@ -1798,49 +2387,61 @@ ipstate_t *is; clone->is_flags &= ~SI_CLONE; clone->is_flags |= SI_CLONED; - fr_stinsert(clone, fin->fin_rev); - clone->is_ref = 2; + 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) fr_tcp_age(&clone->is_sti, fin, ips_tqtqb, - clone->is_flags); + (void) ipf_tcp_age(&clone->is_sti, fin, softs->ipf_state_tcptq, + clone->is_flags, 2); } MUTEX_EXIT(&clone->is_lock); -#ifdef IPFILTER_SCAN - (void) ipsc_attachis(is); -#endif -#ifdef IPFILTER_SYNC if (is->is_flags & IS_STATESYNC) - clone->is_sync = ipfsync_new(SMC_STATE, fin, clone); -#endif + 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: fr_matchsrcdst */ +/* 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 */ +/* 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 *fr_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; +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; @@ -1872,8 +2473,12 @@ u_32_t cmask; *is->is_ifname[idx] == '*'))) ret = 1; - if (ret == 0) + 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; /* @@ -1883,7 +2488,8 @@ u_32_t cmask; 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)&& + if ((sp == is->is_sport || flags & SI_W_SPORT) + && (dp == is->is_dport || flags & SI_W_DPORT)) ret = 1; } else { @@ -1894,7 +2500,8 @@ u_32_t cmask; 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)&& + if ((dp == is->is_sport || flags & SI_W_SPORT) + && (sp == is->is_dport || flags & SI_W_DPORT)) ret = 1; } else { @@ -1903,8 +2510,12 @@ u_32_t cmask; } } - if (ret == 0) + 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 @@ -1925,55 +2536,27 @@ u_32_t cmask; if ((flags & SI_W_SADDR) != 0) { if (rev == 0) { -#ifdef USE_INET6 - if (is->is_v == 6 && - IN6_IS_ADDR_MULTICAST(&fi->fi_src.in6)) - /*EMPTY*/; - else -#endif - { - is->is_src = fi->fi_src; - is->is_flags &= ~SI_W_SADDR; - } + is->is_src = fi->fi_src; + is->is_flags &= ~SI_W_SADDR; } else { -#ifdef USE_INET6 - if (is->is_v == 6 && - IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) - /*EMPTY*/; - else -#endif - { + 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) { -#ifdef USE_INET6 - if (is->is_v == 6 && - IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) - /*EMPTY*/; - else -#endif - { + if (!(fin->fin_flx & (FI_MULTICAST|FI_MBCAST))){ is->is_dst = fi->fi_dst; is->is_flags &= ~SI_W_DADDR; } } else { -#ifdef USE_INET6 - if (is->is_v == 6 && - IN6_IS_ADDR_MULTICAST(&fi->fi_src.in6)) - /*EMPTY*/; - else -#endif - { - is->is_dst = fi->fi_src; - is->is_flags &= ~SI_W_DADDR; - } + is->is_dst = fi->fi_src; + is->is_flags &= ~SI_W_DADDR; } } if ((is->is_flags & (SI_WILDA|SI_WILDP)) == 0) { - ATOMIC_DECL(ips_stats.iss_wild); + ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } } @@ -1986,29 +2569,31 @@ u_32_t cmask; 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)) + ((fin->fin_auth & is->is_authmsk) != is->is_auth)) { + SBUMPD(ipf_state_stats, iss_miss_mask); return NULL; + } - /* - * 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 ((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 = fr_stclone(fin, tcp, is); + clone = ipf_state_clone(fin, tcp, is); if (clone == NULL) return NULL; is = clone; } else { - ATOMIC_DECL(ips_stats.iss_wild); + ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } if ((flags & SI_W_SPORT) != 0) { @@ -2031,18 +2616,21 @@ u_32_t cmask; is->is_maxdend = is->is_dend + 1; } is->is_flags &= ~(SI_W_SPORT|SI_W_DPORT); - if ((flags & SI_CLONED) && ipstate_logging) - ipstate_log(is, ISL_CLONE); + 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; - is->is_opt[rev] = fin->fin_optmsk; - if (is->is_v == 6) { - is->is_opt[rev] &= ~0x8; - is->is_optmsk[rev] &= ~0x8; + 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; + } } } @@ -2053,7 +2641,7 @@ u_32_t cmask; if (is->is_ifp[idx] == NULL && (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '*')) { is->is_ifp[idx] = ifp; - COPYIFNAME(is->is_v, ifp, is->is_ifname[idx]); + COPYIFNAME(fin->fin_v, ifp, is->is_ifname[idx]); } fin->fin_rev = rev; return is; @@ -2061,7 +2649,7 @@ u_32_t cmask; /* ------------------------------------------------------------------------ */ -/* Function: fr_checkicmpmatchingstate */ +/* Function: ipf_checkicmpmatchingstate */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* */ @@ -2071,13 +2659,13 @@ u_32_t cmask; /* 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 *fr_checkicmpmatchingstate(fin) -fr_info_t *fin; +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; - u_short sport, dport; - u_char pr; - int backward, i, oi; i6addr_t dst, src; struct icmp *ic; u_short savelen; @@ -2085,6 +2673,7 @@ fr_info_t *fin; fr_info_t ofin; tcphdr_t *tcp; int type, len; + u_char pr; ip_t *oip; u_int hv; @@ -2096,8 +2685,10 @@ fr_info_t *fin; */ if ((fin->fin_v != 4) || (fin->fin_hlen != sizeof(ip_t)) || (fin->fin_plen < ICMPERR_MINPKTLEN) || - !(fin->fin_flx & FI_ICMPERR)) + !(fin->fin_flx & FI_ICMPERR)) { + SBUMPD(ipf_state_stats, iss_icmp_bad); return NULL; + } ic = fin->fin_dp; type = ic->icmp_type; @@ -2106,15 +2697,20 @@ fr_info_t *fin; * 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)) + 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)) + 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 @@ -2122,7 +2718,7 @@ fr_info_t *fin; * 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 fr_check() and thus can't guarantee it is + * do the pullup early in ipf_check() and thus can't guarantee it is * all here now. */ #ifdef _KERNEL @@ -2131,14 +2727,19 @@ fr_info_t *fin; m = fin->fin_m; # if defined(MENTAT) - if ((char *)oip + len > (char *)m->b_wptr) + 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) + 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)); /* @@ -2154,37 +2755,42 @@ fr_info_t *fin; * 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. fr_matchsrcdst does not use this. + * 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_off - it is still in network byte order so fix it. + * oip->ip_len. */ savelen = oip->ip_len; - oip->ip_len = len; - oip->ip_off = ntohs(oip->ip_off); + 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) fr_makefrip(IP_HL(oip) << 2, oip, &ofin); + (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 fr_matchsrcdst() + * 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 and ip_off back as we don't know - * if we have to forward the packet (or process it again. + * 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; - oip->ip_off = htons(oip->ip_off); switch (oip->ip_p) { @@ -2196,75 +2802,52 @@ fr_info_t *fin; * 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) + 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 = (pr = oip->ip_p); - src.in4 = oip->ip_src; - hv += src.in4.s_addr; - dst.in4 = oip->ip_dst; - hv += dst.in4.s_addr; hv += icmp->icmp_id; hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + 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 = fr_matchsrcdst(&ofin, is, &src, &dst, + + is = ipf_matchsrcdst(&ofin, is, &src, &dst, NULL, FI_ICMPCMP); - if (is != NULL) { - /* - * 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) + ofin.fin_out; - if (is->is_icmppkts[i] > is->is_pkts[oi]) - continue; - ips_stats.iss_hits++; - is->is_icmppkts[i]++; + if ((is != NULL) && !ipf_allowstateicmp(fin, is, &src)) return is; - } } - RWLOCK_EXIT(&ipf_state); + 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)); - dport = tcp->th_dport; - sport = tcp->th_sport; - 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; - hv += dport; - hv += sport; + hv += tcp->th_dport;; + hv += tcp->th_sport;; hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + 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 @@ -2276,40 +2859,93 @@ fr_info_t *fin; * short flag is set. */ if ((is->is_p == pr) && (is->is_v == 4) && - (is = fr_matchsrcdst(&ofin, is, &src, &dst, - tcp, FI_ICMPCMP))) { - /* - * 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) + ofin.fin_out; - - if (((is->is_pass & FR_NOICMPERR) != 0) || - (is->is_icmppkts[i] > is->is_pkts[oi])) - break; - ips_stats.iss_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 is; + (is = ipf_matchsrcdst(&ofin, is, &src, &dst, + tcp, FI_ICMPCMP))) { + if (ipf_allowstateicmp(fin, is, &src) == 0) + return is; } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_3); return NULL; } /* ------------------------------------------------------------------------ */ -/* Function: fr_ipsmove */ +/* 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 */ @@ -2317,14 +2953,19 @@ fr_info_t *fin; /* */ /* Move a state entry from one position in the hash table to another. */ /* ------------------------------------------------------------------------ */ -static void fr_ipsmove(is, hv) -ipstate_t *is; -u_int hv; +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... */ @@ -2332,21 +2973,24 @@ u_int hv; if (is->is_hnext) is->is_hnext->is_phnext = isp; *isp = is->is_hnext; - if (ips_table[hvm] == NULL) - ips_stats.iss_inuse--; - ips_stats.iss_bucketlen[hvm]--; + 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; - isp = &ips_table[hvm]; + + /* TRACE is, hv, is_hv, hvm */ + + isp = &softs->ipf_state_table[hvm]; if (*isp) (*isp)->is_phnext = &is->is_hnext; else - ips_stats.iss_inuse++; - ips_stats.iss_bucketlen[hvm]++; + softs->ipf_state_stats.iss_inuse++; + softs->ipf_state_stats.iss_bucketlen[hvm]++; is->is_phnext = isp; is->is_hnext = *isp; *isp = is; @@ -2354,23 +2998,28 @@ u_int hv; /* ------------------------------------------------------------------------ */ -/* Function: fr_stlookup */ +/* 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. */ +/* 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. */ +/* 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 *fr_stlookup(fin, tcp, ifqp) -fr_info_t *fin; -tcphdr_t *tcp; -ipftq_t **ifqp; +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; @@ -2415,6 +3064,8 @@ ipftq_t **ifqp; } } + /* TRACE fin_saddr, fin_daddr, hv */ + /* * Search the hash table for matching packet header info. */ @@ -2429,28 +3080,22 @@ ipftq_t **ifqp; hv += ic->icmp_id; } } - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); icmp6again: hvm = DOUBLE_HASH(hv); - for (isp = &ips_table[hvm]; ((is = *isp) != NULL); ) { + for (isp = &softs->ipf_state_table[hvm]; + ((is = *isp) != NULL); ) { isp = &is->is_hnext; - /* - * If a connection is about to be deleted, no packets - * are allowed to match it. - */ - if (is->is_sti.tqe_ifq == &ips_deletetq) - continue; - if ((is->is_p != pr) || (is->is_v != v)) continue; - is = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); + is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if (is != NULL && - fr_matchicmpqueryreply(v, &is->is_icmp, + ipf_matchicmpqueryreply(v, &is->is_icmp, ic, fin->fin_rev)) { if (fin->fin_rev) - ifq = &ips_icmpacktq; + ifq = &softs->ipf_state_icmpacktq; else - ifq = &ips_icmptq; + ifq = &softs->ipf_state_icmptq; break; } } @@ -2461,12 +3106,12 @@ icmp6again: hv += fin->fin_fi.fi_src.i6[1]; hv += fin->fin_fi.fi_src.i6[2]; hv += fin->fin_fi.fi_src.i6[3]; - fr_ipsmove(is, hv); - MUTEX_DOWNGRADE(&ipf_state); + ipf_ipsmove(softs, is, hv); + MUTEX_DOWNGRADE(&softc->ipf_state); } break; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); /* * No matching icmp state entry. Perhaps this is a @@ -2478,18 +3123,19 @@ icmp6again: * advantage of this requires some significant code changes * to handle the specific types where that is the case. */ - if ((ips_stats.iss_wild != 0) && (v == 6) && (tryagain == 0) && - !IN6_IS_ADDR_MULTICAST(&fin->fin_fi.fi_src.in6)) { + 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(&ipf_state); + WRITE_ENTER(&softc->ipf_state); goto icmp6again; } - is = fr_checkicmp6matchingstate(fin); + is = ipf_checkicmp6matchingstate(fin); if (is != NULL) return is; break; @@ -2500,25 +3146,26 @@ icmp6again: hv += ic->icmp_id; } hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + 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 = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); + is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if ((is != NULL) && (ic->icmp_id == is->is_icmp.ici_id) && - fr_matchicmpqueryreply(v, &is->is_icmp, + ipf_matchicmpqueryreply(v, &is->is_icmp, ic, fin->fin_rev)) { if (fin->fin_rev) - ifq = &ips_icmpacktq; + ifq = &softs->ipf_state_icmpacktq; else - ifq = &ips_icmptq; + ifq = &softs->ipf_state_icmptq; break; } } if (is == NULL) { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } break; @@ -2531,18 +3178,23 @@ icmp6again: hv += dport; oow = 0; tryagain = 0; - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); retry_tcpudp: hvm = DOUBLE_HASH(hv); - for (isp = &ips_table[hvm]; ((is = *isp) != NULL); ) { + + /* 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 = fr_matchsrcdst(fin, is, &src, &dst, tcp, FI_CMP); + is = ipf_matchsrcdst(fin, is, &src, &dst, tcp, FI_CMP); if (is != NULL) { if (pr == IPPROTO_TCP) { - if (!fr_tcpstate(fin, tcp, is)) { + if (!ipf_state_tcp(softc, softs, fin, + tcp, is)) { oow |= fin->fin_flx & FI_OOW; continue; } @@ -2555,14 +3207,15 @@ retry_tcpudp: !(is->is_flags & (SI_CLONE|SI_WILDP|SI_WILDA))) { hv += dport; hv += sport; - fr_ipsmove(is, hv); - MUTEX_DOWNGRADE(&ipf_state); + ipf_ipsmove(softs, is, hv); + MUTEX_DOWNGRADE(&softc->ipf_state); } break; } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); - if (ips_stats.iss_wild) { + if ((softs->ipf_state_stats.iss_wild != 0) && + ((fin->fin_flx & FI_NOWILD) == 0)) { if (tryagain == 0) { hv -= dport; hv -= sport; @@ -2584,7 +3237,7 @@ retry_tcpudp: } tryagain++; if (tryagain <= 2) { - WRITE_ENTER(&ipf_state); + WRITE_ENTER(&softc->ipf_state); goto retry_tcpudp; } } @@ -2602,19 +3255,20 @@ retry_tcpudp: default : ifqp = NULL; hvm = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hvm]; ((is = *isp) != NULL); ) { + 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 = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); + is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if (is != NULL) { - ifq = &ips_iptq; + ifq = &softs->ipf_state_iptq; break; } } if (is == NULL) { - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } break; } @@ -2625,90 +3279,45 @@ retry_tcpudp: 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: fr_updatestate */ -/* Returns: Nil */ -/* Parameters: fin(I) - pointer to packet information */ -/* is(I) - pointer to state table entry */ -/* Read Locks: ipf_state */ -/* */ -/* Updates packet and byte counters for a newly received packet. Seeds the */ -/* fragment cache with a new entry as required. */ -/* ------------------------------------------------------------------------ */ -void fr_updatestate(fin, is, ifq) -fr_info_t *fin; -ipstate_t *is; -ipftq_t *ifq; -{ - ipftqent_t *tqe; - int i, pass; - - i = (fin->fin_rev << 1) + fin->fin_out; - - /* - * 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; - MUTEX_ENTER(&is->is_lock); - if ((tqe->tqe_flags & TQE_RULEBASED) != 0) - ifq = is->is_tqehead[fin->fin_rev]; - - if (ifq != NULL) - fr_movequeue(tqe, tqe->tqe_ifq, ifq); - - is->is_pkts[i]++; - is->is_bytes[i] += fin->fin_plen; - MUTEX_EXIT(&is->is_lock); - -#ifdef IPFILTER_SYNC - if (is->is_flags & IS_STATESYNC) - ipfsync_update(SMC_STATE, fin, is->is_sync); -#endif - - ATOMIC_INCL(ips_stats.iss_hits); - - fin->fin_fr = is->is_rule; - - /* - * If this packet is a fragment and the rule says to track fragments, - * then create a new fragment cache entry. - */ - pass = is->is_pass; - if ((fin->fin_flx & FI_FRAG) && FR_ISPASS(pass)) - (void) fr_newfrag(fin, pass ^ FR_KEEPSTATE); -} - - -/* ------------------------------------------------------------------------ */ -/* Function: fr_checkstate */ +/* Function: ipf_state_check */ /* Returns: frentry_t* - NULL == search failed, */ /* else pointer to rule for matching state */ -/* Parameters: ifp(I) - pointer to interface */ +/* 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 *fr_checkstate(fin, passp) -fr_info_t *fin; -u_32_t *passp; +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 (fr_state_lock || (ips_list == NULL) || - (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD))) + if (softs->ipf_state_lock || (softs->ipf_state_list == NULL)) return NULL; - is = 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 @@ -2719,13 +3328,12 @@ u_32_t *passp; else tcp = NULL; + ifq = NULL; /* * Search the hash table for matching packet header info. */ - ifq = NULL; - is = fin->fin_state; - if (is == NULL) - is = fr_stlookup(fin, tcp, &ifq); + is = ipf_state_lookup(fin, tcp, &ifq); + switch (fin->fin_p) { #ifdef USE_INET6 @@ -2733,9 +3341,7 @@ u_32_t *passp; if (is != NULL) break; if (fin->fin_v == 6) { - is = fr_checkicmp6matchingstate(fin); - if (is != NULL) - goto matched; + is = ipf_checkicmp6matchingstate(fin); } break; #endif @@ -2746,56 +3352,92 @@ u_32_t *passp; * No matching icmp state entry. Perhaps this is a * response to another state entry. */ - is = fr_checkicmpmatchingstate(fin); - if (is != NULL) - goto matched; + is = ipf_checkicmpmatchingstate(fin); break; + case IPPROTO_TCP : if (is == NULL) break; if (is->is_pass & FR_NEWISN) { if (fin->fin_out == 0) - fr_fixinisn(fin, is); + ipf_fixinisn(fin, is); else if (fin->fin_out == 1) - fr_fixoutisn(fin, is); + ipf_fixoutisn(fin, is); } break; default : if (fin->fin_rev) - ifq = &ips_udpacktq; + ifq = &softs->ipf_state_udpacktq; else - ifq = &ips_udptq; + ifq = &softs->ipf_state_udptq; break; } if (is == NULL) { - ATOMIC_INCL(ips_stats.iss_miss); + SBUMP(ipf_state_stats.iss_check_miss); return NULL; } -matched: fr = is->is_rule; if (fr != NULL) { if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { - if (fin->fin_nattag == NULL) + if (fin->fin_nattag == NULL) { + RWLOCK_EXIT(&softc->ipf_state); + SBUMPD(ipf_state_stats, iss_check_notag); return NULL; - if (fr_matchtag(&fr->fr_nattag, fin->fin_nattag) != 0) + } + 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->fr_group, FR_GROUPLEN); + (void) strncpy(fin->fin_group, FR_NAME(fr, fr_group), + FR_GROUPLEN); fin->fin_icode = fr->fr_icode; } fin->fin_rule = is->is_rulen; - pass = is->is_pass; - fr_updatestate(fin, is, ifq); + 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)) + (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]; - fin->fin_state = is; - is->is_touched = fr_ticks; MUTEX_ENTER(&is->is_lock); - is->is_ref++; + + 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); - RWLOCK_EXIT(&ipf_state); + + 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); @@ -2805,17 +3447,18 @@ matched: /* ------------------------------------------------------------------------ */ -/* Function: fr_fixoutisn */ +/* Function: ipf_fixoutisn */ /* Returns: Nil */ -/* Parameters: fin(I) - pointer to packet information */ +/* 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 fr_fixoutisn(fin, is) -fr_info_t *fin; -ipstate_t *is; +static void +ipf_fixoutisn(fin, is) + fr_info_t *fin; + ipstate_t *is; { tcphdr_t *tcp; int rev; @@ -2824,26 +3467,26 @@ ipstate_t *is; tcp = fin->fin_dp; rev = fin->fin_rev; if ((is->is_flags & IS_ISNSYN) != 0) { - if (rev == 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); - fix_outcksum(fin, &tcp->th_sum, is->is_sumd[0]); + ipf_fix_outcksum(0, &tcp->th_sum, is->is_sumd[0], 0); } } if ((is->is_flags & IS_ISNACK) != 0) { - if (rev == 1) { + if ((rev == 1) && (fin->fin_cksum < FI_CK_L4PART)) { seq = ntohl(tcp->th_seq); seq += is->is_isninc[1]; tcp->th_seq = htonl(seq); - fix_outcksum(fin, &tcp->th_sum, is->is_sumd[1]); + ipf_fix_outcksum(0, &tcp->th_sum, is->is_sumd[1], 0); } } } /* ------------------------------------------------------------------------ */ -/* Function: fr_fixinisn */ +/* Function: ipf_fixinisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ @@ -2851,9 +3494,10 @@ ipstate_t *is; /* Called only for inbound packets, adjusts the acknowledge number and the */ /* TCP checksum to match that change. */ /* ------------------------------------------------------------------------ */ -static void fr_fixinisn(fin, is) -fr_info_t *fin; -ipstate_t *is; +static void +ipf_fixinisn(fin, is) + fr_info_t *fin; + ipstate_t *is; { tcphdr_t *tcp; int rev; @@ -2862,28 +3506,29 @@ ipstate_t *is; tcp = fin->fin_dp; rev = fin->fin_rev; if ((is->is_flags & IS_ISNSYN) != 0) { - if (rev == 1) { + if ((rev == 1) && (fin->fin_cksum < FI_CK_L4PART)) { ack = ntohl(tcp->th_ack); ack -= is->is_isninc[0]; tcp->th_ack = htonl(ack); - fix_incksum(fin, &tcp->th_sum, is->is_sumd[0]); + ipf_fix_incksum(0, &tcp->th_sum, is->is_sumd[0], 0); } } if ((is->is_flags & IS_ISNACK) != 0) { - if (rev == 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); - fix_incksum(fin, &tcp->th_sum, is->is_sumd[1]); + ipf_fix_incksum(0, &tcp->th_sum, is->is_sumd[1], 0); } } } /* ------------------------------------------------------------------------ */ -/* Function: fr_statesync */ +/* Function: ipf_state_sync */ /* Returns: Nil */ -/* Parameters: ifp(I) - pointer to interface */ +/* 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 */ @@ -2892,40 +3537,45 @@ ipstate_t *is; /* If ifp is passed in as being non-null then we are only doing updates for */ /* existing, matching, uses of it. */ /* ------------------------------------------------------------------------ */ -void fr_statesync(ifp) -void *ifp; +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 (fr_running <= 0) + if (softc->ipf_running <= 0) return; - WRITE_ENTER(&ipf_state); + WRITE_ENTER(&softc->ipf_state); - if (fr_running <= 0) { - RWLOCK_EXIT(&ipf_state); + if (softc->ipf_running <= 0) { + RWLOCK_EXIT(&softc->ipf_state); return; } - for (is = ips_list; is; is = is->is_next) { + 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 < 4; i++) { if (ifp == NULL || ifp == is->is_ifp[i]) - is->is_ifp[i] = fr_resolvenic(is->is_ifname[i], + is->is_ifp[i] = ipf_resolvenic(softc, + is->is_ifname[i], is->is_v); } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); } /* ------------------------------------------------------------------------ */ -/* Function: fr_delstate */ -/* Returns: int - 0 = entry deleted, else reference count on struct */ -/* Parameters: is(I) - pointer to state structure to delete */ +/* 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 */ /* */ @@ -2933,10 +3583,15 @@ void *ifp; /* and timeout queue lists. Make adjustments to hash table statistics and */ /* global counters as required. */ /* ------------------------------------------------------------------------ */ -static int fr_delstate(is, why) -ipstate_t *is; -int why; +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, @@ -2946,22 +3601,23 @@ int why; *is->is_phnext = is->is_hnext; if (is->is_hnext != NULL) is->is_hnext->is_phnext = is->is_phnext; - if (ips_table[is->is_hv] == NULL) - ips_stats.iss_inuse--; - ips_stats.iss_bucketlen[is->is_hv]--; + 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 ips_stats.iss_wild is a count of entries in the state + * 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(ips_stats.iss_wild); + ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } is->is_flags &= ~(SI_WILDP|SI_WILDA); } @@ -2970,47 +3626,56 @@ int why; * Next, remove it from the timeout queue it is in. */ if (is->is_sti.tqe_ifq != NULL) - fr_deletequeueentry(&is->is_sti); - - if (is->is_me != NULL) { - *is->is_me = NULL; - is->is_me = 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? fr_state_flush() calls fr_delete() directly because it wants + * 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--; + } if (is->is_ref > 1) { + int refs; + is->is_ref--; + refs = is->is_ref; MUTEX_EXIT(&is->is_lock); - return is->is_ref; + if (!orphan) + softs->ipf_state_stats.iss_orphan++; + return refs; } MUTEX_EXIT(&is->is_lock); + 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); + } + } + is->is_ref = 0; if (is->is_tqehead[0] != NULL) { - if (fr_deletetimeoutqueue(is->is_tqehead[0]) == 0) - fr_freetimeoutqueue(is->is_tqehead[0]); + if (ipf_deletetimeoutqueue(is->is_tqehead[0]) == 0) + ipf_freetimeoutqueue(softc, is->is_tqehead[0]); } if (is->is_tqehead[1] != NULL) { - if (fr_deletetimeoutqueue(is->is_tqehead[1]) == 0) - fr_freetimeoutqueue(is->is_tqehead[1]); + if (ipf_deletetimeoutqueue(is->is_tqehead[1]) == 0) + ipf_freetimeoutqueue(softc, is->is_tqehead[1]); } -#ifdef IPFILTER_SYNC if (is->is_sync) - ipfsync_del(is->is_sync); -#endif -#ifdef IPFILTER_SCAN - (void) ipsc_detachis(is); -#endif + ipf_sync_del_state(softc->ipf_sync_soft, is->is_sync); /* * Now remove it from the linked list of known states @@ -3025,94 +3690,100 @@ int why; is->is_next = NULL; } - if (ipstate_logging != 0 && why != 0) - ipstate_log(is, why); + if (softs->ipf_state_logging != 0 && why != 0) + ipf_state_log(softc, is, why); if (is->is_p == IPPROTO_TCP) - ips_stats.iss_fin++; + softs->ipf_state_stats.iss_fin++; else - ips_stats.iss_expire++; + softs->ipf_state_stats.iss_expire++; + if (orphan) + softs->ipf_state_stats.iss_orphan--; - if (is->is_rule != NULL) { - is->is_rule->fr_statecnt--; - (void) fr_derefrule(&is->is_rule); + if (fr != NULL) { + fr->fr_statecnt--; + (void) ipf_derefrule(softc, &fr); } -#if defined(NEED_LOCAL_RAND) && defined(_KERNEL) - ipf_rand_push(is, sizeof(*is)); -#endif + softs->ipf_state_stats.iss_active_proto[is->is_p]--; MUTEX_DESTROY(&is->is_lock); KFREE(is); - ips_num--; + softs->ipf_state_stats.iss_active--; return 0; } /* ------------------------------------------------------------------------ */ -/* Function: fr_timeoutstate */ +/* Function: ipf_state_expire */ /* Returns: Nil */ -/* Parameters: 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 fr_timeoutstate() +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(&ipf_state); - for (ifq = ips_tqtqb; ifq != NULL; ifq = ifq->ifq_next) + 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 > fr_ticks) + if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; is = tqe->tqe_parent; - fr_delstate(is, ISL_EXPIRE); + ipf_state_del(softc, is, ISL_EXPIRE); } - for (ifq = ips_utqe; ifq != NULL; ifq = ifqnext) { + 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 > fr_ticks) + if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; is = tqe->tqe_parent; - fr_delstate(is, ISL_EXPIRE); + ipf_state_del(softc, is, ISL_EXPIRE); } } - for (ifq = ips_utqe; ifq != NULL; ifq = ifqnext) { + 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)) { - fr_freetimeoutqueue(ifq); + ipf_freetimeoutqueue(softc, ifq); } } - if (fr_state_doflush) { - (void) fr_state_flush(2, 0); - fr_state_doflush = 0; + 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(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); SPL_X(s); } /* ------------------------------------------------------------------------ */ -/* Function: fr_state_flush */ +/* Function: ipf_state_flush */ /* Returns: int - 0 == success, -1 == failure */ -/* Parameters: Nil */ +/* 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: */ @@ -3126,12 +3797,15 @@ void fr_timeoutstate() /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ -static int fr_state_flush(which, proto) -int which, proto; +int +ipf_state_flush(softc, which, proto) + ipf_main_softc_t *softc; + int which, proto; { - ipftq_t *ifq, *ifqnext; + 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); @@ -3142,15 +3816,16 @@ int which, proto; switch (which) { case 0 : + SBUMP(ipf_state_stats.iss_flush_all); /* * Style 0 flush removes everything... */ - for (isp = &ips_list; ((is = *isp) != NULL); ) { + for (isp = &softs->ipf_state_list; ((is = *isp) != NULL); ) { if ((proto != 0) && (is->is_v != proto)) { isp = &is->is_next; continue; } - if (fr_delstate(is, ISL_FLUSH) == 0) + if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; else isp = &is->is_next; @@ -3158,19 +3833,20 @@ int which, proto; 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 = ips_tqtqb + IPF_TCPS_CLOSE_WAIT; ifq != NULL; - ifq = ifq->ifq_next) { + 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 (fr_delstate(is, ISL_EXPIRE) == 0) + if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } } @@ -3178,8 +3854,8 @@ int which, proto; /* * Also need to look through the user defined queues. */ - for (ifq = ips_utqe; ifq != NULL; ifq = ifqnext) { - ifqnext = ifq->ifq_next; + 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; @@ -3188,7 +3864,8 @@ int which, proto; if ((is->is_state[0] > IPF_TCPS_ESTABLISHED) && (is->is_state[1] > IPF_TCPS_ESTABLISHED)) { - if (fr_delstate(is, ISL_EXPIRE) == 0) + if (ipf_state_del(softc, is, + ISL_FLUSH) == 0) removed++; } } @@ -3198,7 +3875,7 @@ int which, proto; case 2 : break; - /* + /* * Args 5-11 correspond to flushing those particular states * for TCP connections. */ @@ -3209,12 +3886,13 @@ int which, proto; case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : - tqn = ips_tqtqb[which].ifq_head; + 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 (fr_delstate(is, ISL_FLUSH) == 0) + if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } break; @@ -3223,16 +3901,18 @@ int which, proto; 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 = &ips_list; ((is = *isp) != NULL); ) { + for (isp = &softs->ipf_state_list; ((is = *isp) != NULL); ) { if ((proto == 0) || (is->is_v == proto)) { - if (fr_ticks - is->is_touched > which) { - if (fr_delstate(is, ISL_FLUSH) == 0) { + if (softc->ipf_ticks - is->is_touched > which) { + if (ipf_state_del(softc, is, + ISL_FLUSH) == 0) { removed++; continue; } @@ -3248,13 +3928,23 @@ int which, proto; return removed; } + SBUMP(ipf_state_stats.iss_flush_timeout); /* - * Asked to remove inactive entries because the table is full. + * 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 (fr_ticks - ips_last_force_flush > IPF_TTLVAL(5)) { - ips_last_force_flush = fr_ticks; - removed = ipf_queueflush(fr_state_flush_entry, ips_tqtqb, - ips_utqe); + 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); @@ -3263,29 +3953,33 @@ int which, proto; /* ------------------------------------------------------------------------ */ -/* Function: fr_state_flush_entry */ +/* Function: ipf_state_flush_entry */ /* Returns: int - 0 = entry deleted, else not deleted */ -/* Parameters: entry(I) - pointer to state structure to delete */ +/* 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 */ -/* fr_delstate(). It is used so we can provide a uniform interface via the */ -/* ipf_queueflush() function. */ +/* ipf_state_del(). It is used so we can provide a uniform interface via */ +/* the ipf_queueflush() function. */ /* ------------------------------------------------------------------------ */ -static int fr_state_flush_entry(entry) -void *entry; +static int +ipf_state_flush_entry(softc, entry) + ipf_main_softc_t *softc; + void *entry; { - return fr_delstate(entry, ISL_FLUSH); -} + return ipf_state_del(softc, entry, ISL_FLUSH); +} /* ------------------------------------------------------------------------ */ -/* Function: fr_tcp_age */ +/* Function: ipf_tcp_age */ /* Returns: int - 1 == state transition made, 0 == no change (rejected) */ -/* Parameters: tq(I) - pointer to timeout queue information */ +/* 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 <Arjan.deVet@adv.iae.nl>, 2000-07-29: */ /* */ @@ -3297,7 +3991,7 @@ void *entry; /* */ /* - 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 fr_tcp_age have been changed accordingly. */ +/* to ipf_tcp_age have been changed accordingly. */ /* */ /* Internal Parameters: */ /* */ @@ -3328,12 +4022,14 @@ void *entry; /* */ /* Locking: it is assumed that the parent of the tqe structure is locked. */ /* ------------------------------------------------------------------------ */ -int fr_tcp_age(tqe, fin, tqtab, flags) -ipftqent_t *tqe; -fr_info_t *fin; -ipftq_t *tqtab; -int flags; +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; @@ -3344,17 +4040,20 @@ int flags; 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 { - ostate = tqe->tqe_state[1 - dir]; - nstate = tqe->tqe_state[dir]; - switch (nstate) { case IPF_TCPS_LISTEN: /* 0 */ @@ -3410,7 +4109,7 @@ int flags; if ((tcpflags & ~(TH_ECN|TH_CWR)) == TH_SYN) { /* * A retransmitted SYN packet. We do not reset - * the timeout here to fr_tcptimeout because a + * 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 @@ -3578,7 +4277,7 @@ int flags; * the FIN packet here? does the window code * guarantee that? */ - nstate = IPF_TCPS_TIME_WAIT; + nstate = IPF_TCPS_LAST_ACK; } else { /* * we closed our side of the connection @@ -3594,25 +4293,18 @@ int flags; if ((tcpflags & (TH_FIN|TH_ACK)) == TH_ACK) { nstate = IPF_TCPS_TIME_WAIT; } - rval = 2; + rval = 1; break; case IPF_TCPS_LAST_ACK: /* 8 */ if (tcpflags & TH_ACK) { - if ((tcpflags & TH_PUSH) || dlen) - /* - * there is still data to be delivered, - * reset timeout - */ - rval = 1; - else - rval = 2; + 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 + * 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; @@ -3624,8 +4316,10 @@ int flags; /* we're in 2MSL timeout now */ if (ostate == IPF_TCPS_LAST_ACK) { nstate = IPF_TCPS_CLOSED; + rval = 1; + } else { + rval = 2; } - rval = 1; break; case IPF_TCPS_CLOSED: /* 11 */ @@ -3633,18 +4327,7 @@ int flags; break; default : -#if defined(_KERNEL) -# if SOLARIS - cmn_err(CE_NOTE, - "tcp %lx flags %x si %lx nstate %d ostate %d\n", - (u_long)tcp, tcpflags, (u_long)tqe, - nstate, ostate); -# else - printf("tcp %lx flags %x si %lx nstate %d ostate %d\n", - (u_long)tcp, tcpflags, (u_long)tqe, - nstate, ostate); -# endif -#else +#if !defined(_KERNEL) abort(); #endif break; @@ -3658,9 +4341,11 @@ int flags; if (rval == 2) rval = 1; else if (rval == 1) { - tqe->tqe_state[dir] = nstate; + if (ok) + tqe->tqe_state[dir] = nstate; if ((tqe->tqe_flags & TQE_RULEBASED) == 0) - fr_movequeue(tqe, tqe->tqe_ifq, tqtab + nstate); + ipf_movequeue(softc->ipf_ticks, tqe, tqe->tqe_ifq, + tqtab + nstate); } return rval; @@ -3668,18 +4353,21 @@ int flags; /* ------------------------------------------------------------------------ */ -/* Function: ipstate_log */ +/* Function: ipf_state_log */ /* Returns: Nil */ -/* Parameters: is(I) - pointer to state structure */ -/* type(I) - type of log entry to create */ +/* 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 ipstate_log(is, type) -struct ipstate *is; -u_int type; +void +ipf_state_log(softc, is, type) + ipf_main_softc_t *softc; + struct ipstate *is; + u_int type; { #ifdef IPFILTER_LOG struct ipslog ipsl; @@ -3729,18 +4417,14 @@ u_int type; sizes[0] = sizeof(ipsl); types[0] = 0; - if (ipllog(IPL_LOGSTATE, NULL, items, sizes, types, 1)) { - ATOMIC_INCL(ips_stats.iss_logged); - } else { - ATOMIC_INCL(ips_stats.iss_logfail); - } + (void) ipf_log_items(softc, IPL_LOGSTATE, NULL, items, sizes, types, 1); #endif } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ -/* Function: fr_checkicmp6matchingstate */ +/* Function: ipf_checkicmp6matchingstate */ /* Returns: ipstate_t* - NULL == no match found, */ /* else pointer to matching state entry */ /* Parameters: fin(I) - pointer to packet information */ @@ -3749,11 +4433,13 @@ u_int type; /* 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 *fr_checkicmp6matchingstate(fin) -fr_info_t *fin; +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; - int type, backward, i; ipstate_t *is, **isp; u_short sport, dport; i6addr_t dst, src; @@ -3762,8 +4448,9 @@ fr_info_t *fin; fr_info_t ofin; tcphdr_t *tcp; ip6_t *oip6; - u_char pr; + u_char pr; u_int hv; + int type; /* * Does it at least have the return (basic) IP header ? @@ -3772,15 +4459,19 @@ fr_info_t *fin; * an ICMP error header. */ if ((fin->fin_v != 6) || (fin->fin_plen < ICMP6ERR_MINPKTLEN) || - !(fin->fin_flx & FI_ICMPERR)) + !(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)) + 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; @@ -3794,21 +4485,31 @@ fr_info_t *fin; * 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. fr_matchsrcdst does not use this. + * 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 = fin->fin_dlen - ICMPERR_ICMPHLEN; + oip6->ip6_plen = htons(fin->fin_dlen - ICMPERR_ICMPHLEN); ofin.fin_flx = FI_NOCKSUM; ofin.fin_ip = (ip_t *)oip6; - (void) fr_makefrip(sizeof(*oip6), (ip_t *)oip6, &ofin); + (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 = (struct icmp6_hdr *)(oip6 + 1); + 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 @@ -3816,8 +4517,11 @@ fr_info_t *fin; * 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)) - return NULL; + 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 @@ -3831,15 +4535,16 @@ fr_info_t *fin; hv += oic->icmp6_seq; hv = DOUBLE_HASH(hv); - READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + 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 = fr_matchsrcdst(&ofin, is, &src, + (is = ipf_matchsrcdst(&ofin, is, &src, &dst, NULL, FI_ICMPCMP))) { /* * in the state table ICMP query's are stored @@ -3849,16 +4554,13 @@ fr_info_t *fin; if (((ic->ici_type == ICMP6_ECHO_REPLY) && (oic->icmp6_type == ICMP6_ECHO_REQUEST)) || (ic->ici_type - 1 == oic->icmp6_type )) { - ips_stats.iss_hits++; - backward = IP6_NEQ(&is->is_dst, &src); - fin->fin_rev = !backward; - i = (backward << 1) + fin->fin_out; - is->is_icmppkts[i]++; - return is; + if (!ipf_allowstateicmp(fin, is, &src)) + return is; } } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + SBUMPD(ipf_state_stats, iss_icmp6_miss); return NULL; } @@ -3874,18 +4576,33 @@ fr_info_t *fin; hv += dst.i6[2]; hv += dst.i6[3]; - if ((oip6->ip6_nxt == IPPROTO_TCP) || (oip6->ip6_nxt == IPPROTO_UDP)) { + 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; - } else - tcp = NULL; + 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(&ipf_state); - for (isp = &ips_table[hv]; ((is = *isp) != NULL); ) { + 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 @@ -3897,73 +4614,63 @@ fr_info_t *fin; if ((is->is_p != pr) || (is->is_v != 6) || (is->is_pass & FR_NOICMPERR)) continue; - is = fr_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP); - if (is != NULL) { - ips_stats.iss_hits++; - backward = IP6_NEQ(&is->is_dst, &src); - fin->fin_rev = !backward; - i = (backward << 1) + fin->fin_out; - 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 - */ + is = ipf_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP); + if ((is != NULL) && (ipf_allowstateicmp(fin, is, &src) == 0)) return is; - } } - RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&softc->ipf_state); + SBUMPD(ipf_state_stats, iss_icmp_miss); return NULL; } #endif /* ------------------------------------------------------------------------ */ -/* Function: fr_sttab_init */ +/* Function: ipf_sttab_init */ /* Returns: Nil */ -/* Parameters: tqp(I) - pointer to an array of timeout queues for TCP */ +/* 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 fr_sttab_init(tqp) -ipftq_t *tqp; +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--) { - tqp[i].ifq_ttl = 0; - tqp[i].ifq_ref = 1; - tqp[i].ifq_head = NULL; - tqp[i].ifq_tail = &tqp[i].ifq_head; + IPFTQ_INIT(&tqp[i], 0, "ipftq tcp tab"); tqp[i].ifq_next = tqp + i + 1; - MUTEX_INIT(&tqp[i].ifq_lock, "ipftq tcp tab"); } tqp[IPF_TCP_NSTATES - 1].ifq_next = NULL; - tqp[IPF_TCPS_CLOSED].ifq_ttl = fr_tcpclosed; - tqp[IPF_TCPS_LISTEN].ifq_ttl = fr_tcptimeout; - tqp[IPF_TCPS_SYN_SENT].ifq_ttl = fr_tcptimeout; - tqp[IPF_TCPS_SYN_RECEIVED].ifq_ttl = fr_tcptimeout; - tqp[IPF_TCPS_ESTABLISHED].ifq_ttl = fr_tcpidletimeout; - tqp[IPF_TCPS_CLOSE_WAIT].ifq_ttl = fr_tcphalfclosed; - tqp[IPF_TCPS_FIN_WAIT_1].ifq_ttl = fr_tcphalfclosed; - tqp[IPF_TCPS_CLOSING].ifq_ttl = fr_tcptimeout; - tqp[IPF_TCPS_LAST_ACK].ifq_ttl = fr_tcplastack; - tqp[IPF_TCPS_FIN_WAIT_2].ifq_ttl = fr_tcpclosewait; - tqp[IPF_TCPS_TIME_WAIT].ifq_ttl = fr_tcptimewait; - tqp[IPF_TCPS_HALF_ESTAB].ifq_ttl = fr_tcptimeout; + 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: fr_sttab_destroy */ +/* 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 fr_sttab_destroy(tqp) -ipftq_t *tqp; +void +ipf_sttab_destroy(tqp) + ipftq_t *tqp; { int i; @@ -3973,9 +4680,10 @@ ipftq_t *tqp; /* ------------------------------------------------------------------------ */ -/* Function: fr_statederef */ +/* Function: ipf_state_deref */ /* Returns: Nil */ -/* Parameters: isp(I) - pointer to pointer to state table entry */ +/* 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. */ @@ -4003,10 +4711,12 @@ ipftq_t *tqp; /* dir == 0 : a packet from source to dest */ /* dir == 1 : a packet from dest to source */ /* ------------------------------------------------------------------------ */ -void fr_statederef(isp) -ipstate_t **isp; +void +ipf_state_deref(softc, isp) + ipf_main_softc_t *softc; + ipstate_t **isp; { - ipstate_t *is; + ipstate_t *is = *isp; is = *isp; *isp = NULL; @@ -4017,37 +4727,40 @@ ipstate_t **isp; 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)) { - fr_delstate(is, ISL_ORPHAN); + (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(&ipf_state); - fr_delstate(is, ISL_EXPIRE); - RWLOCK_EXIT(&ipf_state); + WRITE_ENTER(&softc->ipf_state); + ipf_state_del(softc, is, ISL_ORPHAN); + RWLOCK_EXIT(&softc->ipf_state); } /* ------------------------------------------------------------------------ */ -/* Function: fr_setstatequeue */ +/* Function: ipf_state_setqueue */ /* Returns: Nil */ -/* Parameters: is(I) - pointer to state structure */ -/* rev(I) - forward(0) or reverse(1) direction */ +/* 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 fr_setstatequeue(is, rev) -ipstate_t *is; -int rev; +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 @@ -4059,30 +4772,30 @@ int rev; #ifdef USE_INET6 case IPPROTO_ICMPV6 : if (rev == 1) - nifq = &ips_icmpacktq; + nifq = &softs->ipf_state_icmpacktq; else - nifq = &ips_icmptq; + nifq = &softs->ipf_state_icmptq; break; #endif case IPPROTO_ICMP : if (rev == 1) - nifq = &ips_icmpacktq; + nifq = &softs->ipf_state_icmpacktq; else - nifq = &ips_icmptq; + nifq = &softs->ipf_state_icmptq; break; case IPPROTO_TCP : - nifq = ips_tqtqb + is->is_state[rev]; + nifq = softs->ipf_state_tcptq + is->is_state[rev]; break; case IPPROTO_UDP : if (rev == 1) - nifq = &ips_udpacktq; + nifq = &softs->ipf_state_udpacktq; else - nifq = &ips_udptq; + nifq = &softs->ipf_state_udptq; break; default : - nifq = &ips_iptq; + nifq = &softs->ipf_state_iptq; break; } } @@ -4093,126 +4806,560 @@ int rev; * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) - fr_movequeue(&is->is_sti, oifq, nifq); + ipf_movequeue(softc->ipf_ticks, &is->is_sti, oifq, nifq); else - fr_queueappend(&is->is_sti, nifq, is); + ipf_queueappend(softc->ipf_ticks, &is->is_sti, nifq, is); return; } /* ------------------------------------------------------------------------ */ -/* Function: fr_stateiter */ +/* Function: ipf_state_iter */ /* Returns: int - 0 == success, else error */ -/* Parameters: token(I) - pointer to ipftoken structure */ +/* 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 (ips_list.) */ +/* walks through the list of entries in the state table list (softs->ipf_state_list.) */ /* ------------------------------------------------------------------------ */ -static int fr_stateiter(token, itp) -ipftoken_t *token; -ipfgeniter_t *itp; +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, count; - char *dst; + int error; - if (itp->igi_data == NULL) + if (itp->igi_data == NULL) { + IPFERROR(100026); return EFAULT; + } - if (itp->igi_nitems < 1) + if (itp->igi_nitems < 1) { + IPFERROR(100027); return ENOSPC; + } - if (itp->igi_type != IPFGENITER_STATE) + if (itp->igi_type != IPFGENITER_STATE) { + IPFERROR(100028); return EINVAL; + } is = token->ipt_data; if (is == (void *)-1) { - ipf_freetoken(token); + IPFERROR(100029); return ESRCH; } error = 0; - dst = itp->igi_data; + obj->ipfo_type = IPFOBJ_IPSTATE; + obj->ipfo_size = sizeof(ipstate_t); - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); + + is = token->ipt_data; if (is == NULL) { - next = ips_list; + next = softs->ipf_state_list; } else { next = is->is_next; } - count = itp->igi_nitems; - for (;;) { - if (next != NULL) { - /* - * 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 (count == 1) { - MUTEX_ENTER(&next->is_lock); - next->is_ref++; - MUTEX_EXIT(&next->is_lock); - token->ipt_data = next; - } - } else { - bzero(&zero, sizeof(zero)); - next = &zero; - count = 1; - token->ipt_data = NULL; - } - RWLOCK_EXIT(&ipf_state); - - /* - * This should arguably be via fr_outobj() so that the state - * structure can (if required) be massaged going out. - */ - error = COPYOUT(next, dst, sizeof(*next)); - if (error != 0) - error = EFAULT; - if ((count == 1) || (error != 0)) - break; - - dst += sizeof(*next); - count--; - - READ_ENTER(&ipf_state); - next = next->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); - if (is != NULL) { - fr_statederef(&is); - } + 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: fr_stgettable */ +/* Function: ipf_state_gettable */ /* Returns: int - 0 = success, else error */ -/* Parameters: data(I) - pointer to ioctl data */ +/* 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 fr_stgettable(data) -char *data; +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 = fr_inobj(data, &table, IPFOBJ_GTABLE); + error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return error; - if (table.ita_type != IPFTABLE_BUCKETS) + if (table.ita_type != IPFTABLE_BUCKETS) { + IPFERROR(100031); return EINVAL; + } - error = COPYOUT(ips_stats.iss_bucketlen, table.ita_table, - fr_statesize * sizeof(u_long)); - if (error != 0) + 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_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; + } + + 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_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 9c4e814..e765ac7 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.h +++ b/sys/contrib/ipfilter/netinet/ip_state.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -27,8 +27,10 @@ struct ipscan; # define IPSTATE_MAX 4013 /* Maximum number of states held */ #endif -#define SEQ_GE(a,b) ((int)((a) - (b)) >= 0) -#define SEQ_GT(a,b) ((int)((a) - (b)) > 0) +#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 { @@ -56,6 +58,7 @@ typedef struct ipstate { 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 */ @@ -75,6 +78,8 @@ typedef struct ipstate { 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]; @@ -87,7 +92,6 @@ typedef struct ipstate { #define is_daddr is_dst.in4.s_addr #define is_icmp is_ps.is_ics #define is_type is_icmp.ici_type -#define is_code is_icmp.ici_code #define is_tcp is_ps.is_ts #define is_udp is_ps.is_us #define is_send is_tcp.ts_data[0].td_end @@ -119,6 +123,7 @@ typedef struct ipstate { #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. */ @@ -130,7 +135,7 @@ typedef struct ipstate { #define IS_SC_ALL (IS_SC_MATCHC|IS_SC_MATCHC|IS_SC_CLIENT|IS_SC_SERVER) /* - * Flags that can be passed into fr_addstate + * Flags that can be passed into ipf_addstate */ #define IS_INHERITED 0x0fffff00 @@ -181,6 +186,7 @@ typedef struct ipslog { #define ISL_NEW 0 #define ISL_CLONE 1 +#define ISL_STATECHANGE 2 #define ISL_EXPIRE 0xffff #define ISL_FLUSH 0xfffe #define ISL_REMOVE 0xfffd @@ -191,71 +197,141 @@ typedef struct ipslog { 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_miss; + 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_maxref; - u_long iss_tcp; - u_long iss_udp; - u_long iss_icmp; + u_long iss_max_ref; + u_long iss_max_track; + u_long iss_miss_mask; 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; + 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; - ipstate_t *iss_list; - u_long *iss_bucketlen; + 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; -extern u_long fr_tcpidletimeout; -extern u_long fr_tcpclosewait; -extern u_long fr_tcplastack; -extern u_long fr_tcptimeout; -extern u_long fr_tcpclosed; -extern u_long fr_tcphalfclosed; -extern u_long fr_udptimeout; -extern u_long fr_udpacktimeout; -extern u_long fr_icmptimeout; -extern u_long fr_icmpacktimeout; -extern u_long fr_iptimeout; -extern int fr_statemax; -extern int fr_statesize; -extern int fr_state_lock; -extern int fr_state_maxbucket; -extern int fr_state_maxbucket_reset; -extern ipstate_t *ips_list; -extern ipftq_t *ips_utqe; -extern ipftq_t ips_tqtqb[IPF_TCP_NSTATES]; +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; + -extern int fr_stateinit __P((void)); -extern ipstate_t *fr_addstate __P((fr_info_t *, ipstate_t **, u_int)); -extern frentry_t *fr_checkstate __P((struct fr_info *, u_32_t *)); -extern ipstate_t *fr_stlookup __P((fr_info_t *, tcphdr_t *, ipftq_t **)); -extern void fr_statesync __P((void *)); -extern void fr_timeoutstate __P((void)); -extern int fr_tcp_age __P((struct ipftqent *, struct fr_info *, - struct ipftq *, int)); -extern int fr_tcpinwindow __P((struct fr_info *, struct tcpdata *, +#ifndef _KERNEL +extern void ipf_state_dump __P((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 void fr_stateunload __P((void)); -extern void ipstate_log __P((struct ipstate *, u_int)); -extern int fr_state_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern void fr_stinsert __P((struct ipstate *, int)); -extern void fr_sttab_init __P((struct ipftq *)); -extern void fr_sttab_destroy __P((struct ipftq *)); -extern void fr_updatestate __P((fr_info_t *, ipstate_t *, ipftq_t *)); -extern void fr_statederef __P((ipstate_t **)); -extern void fr_setstatequeue __P((ipstate_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 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)); #endif /* __IP_STATE_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_sync.c b/sys/contrib/ipfilter/netinet/ip_sync.c index a72f50f..0c2fe10 100644 --- a/sys/contrib/ipfilter/netinet/ip_sync.c +++ b/sys/contrib/ipfilter/netinet/ip_sync.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1995-1998 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ @@ -32,6 +32,10 @@ struct file; # if !defined(__SVR4) && !defined(__svr4__) # include <sys/mbuf.h> # endif +# include <sys/select.h> +# if __FreeBSD_version >= 500000 +# include <sys/selinfo.h> +# endif #endif #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include <sys/proc.h> @@ -39,9 +43,6 @@ struct file; #if defined(_KERNEL) && (__FreeBSD_version >= 220000) # include <sys/filio.h> # include <sys/fcntl.h> -# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) -# include "opt_ipfilter.h" -# endif #else # include <sys/ioctl.h> #endif @@ -64,7 +65,6 @@ struct file; #ifdef sun # include <net/af.h> #endif -#include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -98,66 +98,219 @@ struct file; /* END OF INCLUDES */ #if !defined(lint) -static const char rcsid[] = "@(#)$Id: ip_sync.c,v 2.40.2.9 2007/06/02 21:22:28 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id$"; #endif #define SYNC_STATETABSZ 256 #define SYNC_NATTABSZ 256 -#ifdef IPFILTER_SYNC -ipfmutex_t ipf_syncadd, ipsl_mutex; -ipfrwlock_t ipf_syncstate, ipf_syncnat; +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; + kcondvar_t ipslwait; #endif -synclist_t *syncstatetab[SYNC_STATETABSZ]; -synclist_t *syncnattab[SYNC_NATTABSZ]; -synclogent_t synclog[SYNCLOG_SZ]; -syncupdent_t syncupd[SYNCLOG_SZ]; -u_int ipf_syncnum = 1; -u_int ipf_syncwrap = 0; -u_int sl_idx = 0, /* next available sync log entry */ - su_idx = 0, /* next available sync update entry */ - sl_tail = 0, /* next sync log entry to read */ - su_tail = 0; /* next sync update entry to read */ -int ipf_sync_debug = 0; - +#if defined(linux) && defined(_KERNEL) + wait_queue_head_t sl_tail_linux; +#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 *)); # if !defined(sparc) && !defined(__hppa) -void ipfsync_tcporder __P((int, struct tcpdata *)); -void ipfsync_natorder __P((int, struct nat *)); -void ipfsync_storder __P((int, struct ipstate *)); +void ipf_sync_tcporder __P((int, struct tcpdata *)); +void ipf_sync_natorder __P((int, struct nat *)); +void ipf_sync_storder __P((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: ipfsync_init */ +/* 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 ipfsync_init() +int +ipf_sync_soft_init(softc, arg) + ipf_main_softc_t *softc; + void *arg; { - RWLOCK_INIT(&ipf_syncstate, "add things to state sync table"); - RWLOCK_INIT(&ipf_syncnat, "add things to nat sync table"); - MUTEX_INIT(&ipf_syncadd, "add things to sync table"); - MUTEX_INIT(&ipsl_mutex, "add things to sync table"); + 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(&ipslwait, "ipsl condvar", CV_DRIVER, NULL); + 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"); - bzero((char *)syncnattab, sizeof(syncnattab)); - bzero((char *)syncstatetab, sizeof(syncstatetab)); + 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) && !defined(__hppa) /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_tcporder */ +/* Function: ipf_sync_tcporder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* td(IO) - pointer to data to be converted. */ @@ -165,9 +318,10 @@ int ipfsync_init() /* 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 ipfsync_tcporder(way, td) -int way; -tcpdata_t *td; +void +ipf_sync_tcporder(way, td) + int way; + tcpdata_t *td; { if (way) { td->td_maxwin = htons(td->td_maxwin); @@ -182,7 +336,7 @@ tcpdata_t *td; /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_natorder */ +/* Function: ipf_sync_natorder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* nat(IO) - pointer to data to be converted. */ @@ -190,9 +344,10 @@ tcpdata_t *td; /* 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 ipfsync_natorder(way, n) -int way; -nat_t *n; +void +ipf_sync_natorder(way, n) + int way; + nat_t *n; { if (way) { n->nat_age = htonl(n->nat_age); @@ -211,7 +366,7 @@ nat_t *n; /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_storder */ +/* Function: ipf_sync_storder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* ips(IO) - pointer to data to be converted. */ @@ -219,12 +374,13 @@ nat_t *n; /* 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 ipfsync_storder(way, ips) -int way; -ipstate_t *ips; +void +ipf_sync_storder(way, ips) + int way; + ipstate_t *ips; { - ipfsync_tcporder(way, &ips->is_tcp.ts_data[0]); - ipfsync_tcporder(way, &ips->is_tcp.ts_data[1]); + 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); @@ -263,36 +419,37 @@ ipstate_t *ips; } } # else /* !defined(sparc) && !defined(__hppa) */ -# define ipfsync_tcporder(x,y) -# define ipfsync_natorder(x,y) -# define ipfsync_storder(x,y) +# define ipf_sync_tcporder(x,y) +# define ipf_sync_natorder(x,y) +# define ipf_sync_storder(x,y) # endif /* !defined(sparc) && !defined(__hppa) */ -/* enable this for debugging */ -# ifdef _KERNEL /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_write */ +/* 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 ipfsync_write(uio) -struct uio *uio; +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 + * ANY POSSIBLE DATA TYPE */ - char data[2048]; + char data[2048]; int err = 0; -# if (BSD >= 199306) || defined(__FreeBSD__) || defined(__osf__) +# if BSD_GE_YEAR(199306) || defined(__FreeBSD__) || defined(__osf__) uio->uio_rw = UIO_WRITE; # endif @@ -304,7 +461,7 @@ struct uio *uio; err = UIOMOVE(&sh, sizeof(sh), UIO_WRITE, uio); if (err) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(header) failed: %d\n", err); return err; @@ -315,59 +472,65 @@ struct uio *uio; sh.sm_len = ntohl(sh.sm_len); sh.sm_num = ntohl(sh.sm_num); - if (ipf_sync_debug > 8) + 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 (ipf_sync_debug > 2) - printf("uiomove(header) invalud %s\n", + 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 (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "protocol"); + IPFERROR(110002); return EINVAL; } if (sh.sm_cmd > SMC_MAXCMD) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "command"); + IPFERROR(110003); return EINVAL; } if (sh.sm_table > SMC_MAXTBL) { - if (ipf_sync_debug > 2) + 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 (ipf_sync_debug > 2) + 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 + * We have a header, so try to read the amount of data * needed for the request */ /* not supported */ if (sh.sm_len == 0) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(data zero length %s\n", "not supported"); + IPFERROR(110006); return EINVAL; } @@ -376,33 +539,34 @@ struct uio *uio; err = UIOMOVE(data, sh.sm_len, UIO_WRITE, uio); if (err) { - if (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(data) failed: %d\n", err); return err; } - if (ipf_sync_debug > 7) + if (softs->ipf_sync_debug > 7) printf("uiomove(data) %d bytes read\n", sh.sm_len); if (sh.sm_table == SMC_STATE) - err = ipfsync_state(&sh, data); + err = ipf_sync_state(softc, &sh, data); else if (sh.sm_table == SMC_NAT) - err = ipfsync_nat(&sh, data); - if (ipf_sync_debug > 7) + 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 (ipf_sync_debug > 2) + if (softs->ipf_sync_debug > 2) printf("uiomove(data) %s %d bytes, got %d\n", "insufficient data, need", - sh.sm_len, uio->uio_resid); + sh.sm_len, (int)uio->uio_resid); + IPFERROR(110007); return EAGAIN; } - } + } /* no more data */ return 0; @@ -410,7 +574,7 @@ struct uio *uio; /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_read */ +/* Function: ipf_sync_read */ /* Returns: int - 0 == success, else error value. */ /* Parameters: uio(O) - pointer to information about where to store data */ /* */ @@ -418,89 +582,105 @@ struct uio *uio; /* 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 ipfsync_read(uio) -struct uio *uio; +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)) + if ((uio->uio_resid & 3) || (uio->uio_resid < 8)) { + IPFERROR(110008); return EINVAL; + } -# if (BSD >= 199306) || defined(__FreeBSD__) || defined(__osf__) +# if BSD_GE_YEAR(199306) || defined(__FreeBSD__) || defined(__osf__) uio->uio_rw = UIO_READ; # endif - MUTEX_ENTER(&ipsl_mutex); - while ((sl_tail == sl_idx) && (su_tail == su_idx)) { -# if SOLARIS && defined(_KERNEL) - if (!cv_wait_sig(&ipslwait, &ipsl_mutex)) { - MUTEX_EXIT(&ipsl_mutex); + 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 -# ifdef __hpux +# else +# ifdef __hpux { lock_t *l; - l = get_sleep_lock(&sl_tail); - err = sleep(&sl_tail, PZERO+1); + l = get_sleep_lock(&softs->sl_tail); + err = sleep(&softs->sl_tail, PZERO+1); if (err) { - MUTEX_EXIT(&ipsl_mutex); + MUTEX_EXIT(&softs->ipsl_mutex); + IPFERROR(110010); return EINTR; } spinunlock(l); } -# else /* __hpux */ -# ifdef __osf__ - err = mpsleep(&sl_tail, PSUSP|PCATCH, "ipl sleep", 0, - &ipsl_mutex, MS_LOCK_SIMPLE); - if (err) +# else /* __hpux */ +# ifdef __osf__ + err = mpsleep(&softs->sl_tail, PSUSP|PCATCH, "ipl sleep", 0, + &softs->ipsl_mutex, MS_LOCK_SIMPLE); + if (err) { + IPFERROR(110011); return EINTR; -# else - MUTEX_EXIT(&ipsl_mutex); - err = SLEEP(&sl_tail, "ipl sleep"); - if (err) + } +# else + MUTEX_EXIT(&softs->ipsl_mutex); + err = SLEEP(&softs->sl_tail, "ipl sleep"); + if (err) { + IPFERROR(110012); return EINTR; - MUTEX_ENTER(&ipsl_mutex); -# endif /* __osf__ */ -# endif /* __hpux */ -# endif /* SOLARIS */ + } + MUTEX_ENTER(&softs->ipsl_mutex); +# endif /* __osf__ */ +# endif /* __hpux */ +# endif /* SOLARIS */ +# endif /* _KERNEL */ } - MUTEX_EXIT(&ipsl_mutex); - READ_ENTER(&ipf_syncstate); - while ((sl_tail < sl_idx) && (uio->uio_resid > sizeof(*sl))) { - sl = synclog + sl_tail++; + 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) - break; + goto goterror; + MUTEX_ENTER(&softs->ipsl_mutex); } - while ((su_tail < su_idx) && (uio->uio_resid > sizeof(*su))) { - su = syncupd + su_tail; - su_tail++; + 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) - break; + goto goterror; + MUTEX_ENTER(&softs->ipsl_mutex); if (su->sup_hdr.sm_sl != NULL) su->sup_hdr.sm_sl->sl_idx = -1; } - - MUTEX_ENTER(&ipf_syncadd); - if (su_tail == su_idx) - su_tail = su_idx = 0; - if (sl_tail == sl_idx) - sl_tail = sl_idx = 0; - MUTEX_EXIT(&ipf_syncadd); - RWLOCK_EXIT(&ipf_syncstate); + 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: ipfsync_state */ +/* 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 */ @@ -511,10 +691,13 @@ struct uio *uio; /* create a new state entry or update one. Deletion is left to the state */ /* structures being timed out correctly. */ /* ------------------------------------------------------------------------ */ -int ipfsync_state(sp, data) -synchdr_t *sp; -void *data; +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; @@ -522,7 +705,7 @@ void *data; u_int hv; int err = 0; - hv = sp->sm_num & (SYNC_STATETABSZ - 1); + hv = sp->sm_num & (softs->ipf_sync_state_tab_sz - 1); switch (sp->sm_cmd) { @@ -531,12 +714,14 @@ void *data; 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; @@ -545,23 +730,23 @@ void *data; bzero((char *)is, offsetof(ipstate_t, is_die)); bcopy((char *)&sn.is_die, (char *)&is->is_die, sizeof(*is) - offsetof(ipstate_t, is_die)); - ipfsync_storder(0, is); + 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(&ipf_mutex); - fr = fr_getrulen(IPL_LOGIPF, sn.is_group, sn.is_rulen); + 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(&ipf_mutex); + RWLOCK_EXIT(&softc->ipf_mutex); - if (ipf_sync_debug > 4) + if (softs->ipf_sync_debug > 4) printf("[%d] Filter rules = %p\n", sp->sm_num, fr); is->is_rule = fr; @@ -571,16 +756,16 @@ void *data; sl->sl_ips = is; bcopy(sp, &sl->sl_hdr, sizeof(struct synchdr)); - WRITE_ENTER(&ipf_syncstate); - WRITE_ENTER(&ipf_state); + WRITE_ENTER(&softs->ipf_syncstate); + WRITE_ENTER(&softc->ipf_state); - sl->sl_pnext = syncstatetab + hv; - sl->sl_next = syncstatetab[hv]; - if (syncstatetab[hv] != NULL) - syncstatetab[hv]->sl_pnext = &sl->sl_next; - syncstatetab[hv] = sl; - MUTEX_DOWNGRADE(&ipf_syncstate); - fr_stinsert(is, sp->sm_rev); + 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 @@ -594,29 +779,31 @@ void *data; case SMC_UPDATE : bcopy(data, &su, sizeof(su)); - if (ipf_sync_debug > 4) + 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(&ipf_syncstate); - for (sl = syncstatetab[hv]; (sl != NULL); sl = sl->sl_next) + 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 (ipf_sync_debug > 1) + if (softs->ipf_sync_debug > 1) printf("[%d] State not found - can't update\n", sp->sm_num); - RWLOCK_EXIT(&ipf_syncstate); + RWLOCK_EXIT(&softs->ipf_syncstate); + IPFERROR(110015); err = ENOENT; break; } - READ_ENTER(&ipf_state); + READ_ENTER(&softc->ipf_state); - if (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, + 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); @@ -640,56 +827,97 @@ void *data; break; } - if (ipf_sync_debug > 6) + if (softs->ipf_sync_debug > 6) printf("[%d] Setting timers for state\n", sp->sm_num); - fr_setstatequeue(is, sp->sm_rev); + 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(&ipf_state); - RWLOCK_EXIT(&ipf_syncstate); + RWLOCK_EXIT(&softc->ipf_state); + RWLOCK_EXIT(&softs->ipf_syncstate); } - if (ipf_sync_debug > 6) + if (softs->ipf_sync_debug > 6) printf("[%d] Update completed with error %d\n", sp->sm_num, err); return err; } -# endif /* _KERNEL */ /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_del */ +/* Function: ipf_sync_del */ /* Returns: Nil */ /* Parameters: sl(I) - pointer to synclist object to delete */ /* */ -/* Deletes an object from the synclist table and free's its memory. */ +/* Deletes an object from the synclist. */ /* ------------------------------------------------------------------------ */ -void ipfsync_del(sl) -synclist_t *sl; +static void +ipf_sync_del(softs, sl) + ipf_sync_softc_t *softs; + synclist_t *sl; { - WRITE_ENTER(&ipf_syncstate); *sl->sl_pnext = sl->sl_next; if (sl->sl_next != NULL) sl->sl_next->sl_pnext = sl->sl_pnext; if (sl->sl_idx != -1) - syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; - RWLOCK_EXIT(&ipf_syncstate); + 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: ipfsync_nat */ +/* 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 */ @@ -700,29 +928,34 @@ synclist_t *sl; /* create a new NAT entry or update one. Deletion is left to the NAT */ /* structures being timed out correctly. */ /* ------------------------------------------------------------------------ */ -int ipfsync_nat(sp, data) -synchdr_t *sp; -void *data; +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; - READ_ENTER(&ipf_syncstate); + 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; @@ -732,59 +965,63 @@ void *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)); - ipfsync_natorder(0, n); + 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(&ipf_nat); - sl->sl_pnext = syncstatetab + hv; - sl->sl_next = syncstatetab[hv]; - if (syncstatetab[hv] != NULL) - syncstatetab[hv]->sl_pnext = &sl->sl_next; - syncstatetab[hv] = sl; - nat_insert(n, sl->sl_rev); - RWLOCK_EXIT(&ipf_nat); + 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)); - READ_ENTER(&ipf_syncstate); - for (sl = syncstatetab[hv]; (sl != NULL); sl = sl->sl_next) + 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(&ipf_nat); + READ_ENTER(&softc->ipf_nat); nat = sl->sl_ipn; + nat->nat_rev = sl->sl_rev; MUTEX_ENTER(&nat->nat_lock); - fr_setnatqueue(nat, sl->sl_rev); + ipf_nat_setqueue(softc, softc->ipf_nat_soft, nat); MUTEX_EXIT(&nat->nat_lock); - RWLOCK_EXIT(&ipf_nat); + RWLOCK_EXIT(&softc->ipf_nat); break; default : + IPFERROR(110020); err = EINVAL; break; } - RWLOCK_EXIT(&ipf_syncstate); + RWLOCK_EXIT(&softs->ipf_syncnat); return 0; } /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_new */ +/* 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 */ @@ -794,43 +1031,36 @@ void *data; /* Creates a new sync table entry and notifies any sleepers that it's there */ /* waiting to be processed. */ /* ------------------------------------------------------------------------ */ -synclist_t *ipfsync_new(tab, fin, ptr) -int tab; -fr_info_t *fin; -void *ptr; +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 (sl_idx == SYNCLOG_SZ) + if (softs->sl_idx == softs->ipf_sync_log_sz) return NULL; KMALLOC(sl, synclist_t *); if (sl == NULL) return NULL; - MUTEX_ENTER(&ipf_syncadd); + 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. */ - ipf_syncnum++; - if (ipf_syncnum == 0) { - ipf_syncnum = 1; - ipf_syncwrap = 1; + softs->ipf_sync_num++; + if (softs->ipf_sync_num == 0) { + softs->ipf_sync_num = 1; + softs->ipf_sync_wrap++; } - hv = ipf_syncnum & (SYNC_STATETABSZ - 1); - while (ipf_syncwrap != 0) { - for (ss = syncstatetab[hv]; ss; ss = ss->sl_next) - if (ss->sl_hdr.sm_num == ipf_syncnum) - break; - if (ss == NULL) - break; - ipf_syncnum++; - hv = ipf_syncnum & (SYNC_STATETABSZ - 1); - } /* * Use the synch number of the object as the hash key. Should end up * with relatively even distribution over time. @@ -839,11 +1069,48 @@ void *ptr; * nth connection they make, where n is a value in the interval * [0, SYNC_STATETABSZ-1]. */ - sl->sl_pnext = syncstatetab + hv; - sl->sl_next = syncstatetab[hv]; - syncstatetab[hv] = sl; - sl->sl_num = ipf_syncnum; - MUTEX_EXIT(&ipf_syncadd); + 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; @@ -868,8 +1135,8 @@ void *ptr; * 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(&ipf_syncadd); - sle = synclog + sl_idx++; + 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); @@ -877,31 +1144,20 @@ void *ptr; if (ptr != NULL) { bcopy((char *)ptr, (char *)&sle->sle_un, sz); if (tab == SMC_STATE) { - ipfsync_storder(1, &sle->sle_un.sleu_ips); + ipf_sync_storder(1, &sle->sle_un.sleu_ips); } else if (tab == SMC_NAT) { - ipfsync_natorder(1, &sle->sle_un.sleu_ipn); + ipf_sync_natorder(1, &sle->sle_un.sleu_ipn); } } - MUTEX_EXIT(&ipf_syncadd); + MUTEX_EXIT(&softs->ipf_syncadd); - MUTEX_ENTER(&ipsl_mutex); -# if SOLARIS -# ifdef _KERNEL - cv_signal(&ipslwait); -# endif - MUTEX_EXIT(&ipsl_mutex); -# else - MUTEX_EXIT(&ipsl_mutex); -# ifdef _KERNEL - wakeup(&sl_tail); -# endif -# endif + ipf_sync_wakeup(softc); return sl; } /* ------------------------------------------------------------------------ */ -/* Function: ipfsync_update */ +/* Function: ipf_sync_update */ /* Returns: Nil */ /* Parameters: tab(I) - type of synclist_t to create */ /* fin(I) - pointer to packet information */ @@ -910,24 +1166,36 @@ void *ptr; /* For outbound packets, only, create an sync update record for the user */ /* process to read. */ /* ------------------------------------------------------------------------ */ -void ipfsync_update(tab, fin, sl) -int tab; -fr_info_t *fin; -synclist_t *sl; +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; - WRITE_ENTER(&ipf_syncstate); - MUTEX_ENTER(&ipf_syncadd); + if (tab == SMC_STATE) { + lock = &softs->ipf_syncstate; + } else { + lock = &softs->ipf_syncnat; + } + + READ_ENTER(lock); if (sl->sl_idx == -1) { - slu = syncupd + su_idx; - sl->sl_idx = su_idx++; + 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); @@ -944,9 +1212,7 @@ synclist_t *sl; } # endif } else - slu = syncupd + sl->sl_idx; - MUTEX_EXIT(&ipf_syncadd); - MUTEX_DOWNGRADE(&ipf_syncstate); + slu = softs->syncupd + sl->sl_idx; /* * Only TCP has complex timeouts, others just use default timeouts. @@ -970,25 +1236,61 @@ synclist_t *sl; st->stu_age = htonl(nat->nat_age); } } - RWLOCK_EXIT(&ipf_syncstate); + RWLOCK_EXIT(lock); - MUTEX_ENTER(&ipsl_mutex); -# if SOLARIS -# ifdef _KERNEL - cv_signal(&ipslwait); -# endif - MUTEX_EXIT(&ipsl_mutex); -# else - MUTEX_EXIT(&ipsl_mutex); -# ifdef _KERNEL - wakeup(&sl_tail); -# endif -# endif + ipf_sync_wakeup(softc); } /* ------------------------------------------------------------------------ */ -/* Function: fr_sync_ioctl */ +/* 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 */ @@ -997,24 +1299,199 @@ synclist_t *sl; /* This function currently does not handle any ioctls and so just returns */ /* EINVAL on all occasions. */ /* ------------------------------------------------------------------------ */ -int fr_sync_ioctl(data, cmd, mode, uid, ctx) -caddr_t data; -ioctlcmd_t cmd; -int mode, uid; -void *ctx; +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; { - return EINVAL; + 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; } -int ipfsync_canread() +/* ------------------------------------------------------------------------ */ +/* 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; { - return !((sl_tail == sl_idx) && (su_tail == su_idx)); + ipf_sync_softc_t *softs = arg; + return !((softs->sl_tail == softs->sl_idx) && + (softs->su_tail == softs->su_idx)); } -int ipfsync_canwrite() +/* ------------------------------------------------------------------------ */ +/* 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; } -#endif /* IPFILTER_SYNC */ + + +/* ------------------------------------------------------------------------ */ +/* 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 8104db3..d9d6d41 100644 --- a/sys/contrib/ipfilter/netinet/ip_sync.h +++ b/sys/contrib/ipfilter/netinet/ip_sync.h @@ -1,10 +1,10 @@ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ip_fil.h 1.35 6/5/96 - * $Id: ip_sync.h,v 2.11.2.4 2006/07/14 06:12:20 darrenr Exp $ + * $Id$ */ #ifndef __IP_SYNC_H__ @@ -36,6 +36,7 @@ typedef struct synchdr { /* * Tables */ +#define SMC_RLOG -2 /* Only used with SIOCIPFFL */ #define SMC_NAT 0 #define SMC_STATE 1 #define SMC_MAXTBL 1 @@ -99,19 +100,22 @@ typedef struct syncupdent { /* 28 or 32 bytes */ struct synctcp_update sup_tcp; } syncupdent_t; -extern synclogent_t synclog[SYNCLOG_SZ]; - - -extern int fr_sync_ioctl __P((caddr_t, ioctlcmd_t, int, int, void *)); -extern synclist_t *ipfsync_new __P((int, fr_info_t *, void *)); -extern void ipfsync_del __P((synclist_t *)); -extern void ipfsync_update __P((int, fr_info_t *, synclist_t *)); -extern int ipfsync_init __P((void)); -extern int ipfsync_nat __P((synchdr_t *sp, void *data)); -extern int ipfsync_state __P((synchdr_t *sp, void *data)); -extern int ipfsync_read __P((struct uio *uio)); -extern int ipfsync_write __P((struct uio *uio)); -extern int ipfsync_canread __P((void)); -extern int ipfsync_canwrite __P((void)); - -#endif /* IP_SYNC */ +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 *)); + +#endif /* __IP_SYNC_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c b/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c new file mode 100644 index 0000000..409b982 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_tftp_pxy.c @@ -0,0 +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 *)); + +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; + 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/ipf_rb.h b/sys/contrib/ipfilter/netinet/ipf_rb.h new file mode 100644 index 0000000..3d7a59d --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ipf_rb.h @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ +typedef enum rbcolour_e { + C_BLACK = 0, + C_RED = 1 +} rbcolour_t; + +#define RBI_LINK(_n, _t) \ + struct _n##_rb_link { \ + struct _t *left; \ + struct _t *right; \ + struct _t *parent; \ + rbcolour_t colour; \ + } + +#define RBI_HEAD(_n, _t) \ +struct _n##_rb_head { \ + struct _t top; \ + int count; \ + int (* compare)(struct _t *, struct _t *); \ +} + +#define RBI_CODE(_n, _t, _f, _cmp) \ + \ +typedef void (*_n##_rb_walker_t)(_t *, void *); \ + \ +_t * _n##_rb_delete(struct _n##_rb_head *, _t *); \ +void _n##_rb_init(struct _n##_rb_head *); \ +void _n##_rb_insert(struct _n##_rb_head *, _t *); \ +_t * _n##_rb_search(struct _n##_rb_head *, void *); \ +void _n##_rb_walktree(struct _n##_rb_head *, _n##_rb_walker_t, void *);\ + \ +static void \ +rotate_left(struct _n##_rb_head *head, _t *node) \ +{ \ + _t *parent, *tmp1, *tmp2; \ + \ + parent = node->_f.parent; \ + tmp1 = node->_f.right; \ + tmp2 = tmp1->_f.left; \ + node->_f.right = tmp2; \ + if (tmp2 != & _n##_rb_zero) \ + tmp2->_f.parent = node; \ + if (parent == & _n##_rb_zero) \ + head->top._f.right = tmp1; \ + else if (parent->_f.right == node) \ + parent->_f.right = tmp1; \ + else \ + parent->_f.left = tmp1; \ + tmp1->_f.left = node; \ + tmp1->_f.parent = parent; \ + node->_f.parent = tmp1; \ +} \ + \ +static void \ +rotate_right(struct _n##_rb_head *head, _t *node) \ +{ \ + _t *parent, *tmp1, *tmp2; \ + \ + parent = node->_f.parent; \ + tmp1 = node->_f.left; \ + tmp2 = tmp1->_f.right; \ + node->_f.left = tmp2; \ + if (tmp2 != &_n##_rb_zero) \ + tmp2->_f.parent = node; \ + if (parent == &_n##_rb_zero) \ + head->top._f.right = tmp1; \ + else if (parent->_f.right == node) \ + parent->_f.right = tmp1; \ + else \ + parent->_f.left = tmp1; \ + tmp1->_f.right = node; \ + tmp1->_f.parent = parent; \ + node->_f.parent = tmp1; \ +} \ + \ +void \ +_n##_rb_insert(struct _n##_rb_head *head, _t *node) \ +{ \ + _t *n, *parent, **p, *tmp1, *gparent; \ + \ + parent = &head->top; \ + node->_f.left = &_n##_rb_zero; \ + node->_f.right = &_n##_rb_zero; \ + p = &head->top._f.right; \ + while ((n = *p) != &_n##_rb_zero) { \ + if (_cmp(node, n) < 0) \ + p = &n->_f.left; \ + else \ + p = &n->_f.right; \ + parent = n; \ + } \ + *p = node; \ + node->_f.colour = C_RED; \ + node->_f.parent = parent; \ + \ + while ((node != &_n##_rb_zero) && (parent->_f.colour == C_RED)){\ + gparent = parent->_f.parent; \ + if (parent == gparent->_f.left) { \ + tmp1 = gparent->_f.right; \ + if (tmp1->_f.colour == C_RED) { \ + parent->_f.colour = C_BLACK; \ + tmp1->_f.colour = C_BLACK; \ + gparent->_f.colour = C_RED; \ + node = gparent; \ + } else { \ + if (node == parent->_f.right) { \ + node = parent; \ + rotate_left(head, node); \ + parent = node->_f.parent; \ + } \ + parent->_f.colour = C_BLACK; \ + gparent->_f.colour = C_RED; \ + rotate_right(head, gparent); \ + } \ + } else { \ + tmp1 = gparent->_f.left; \ + if (tmp1->_f.colour == C_RED) { \ + parent->_f.colour = C_BLACK; \ + tmp1->_f.colour = C_BLACK; \ + gparent->_f.colour = C_RED; \ + node = gparent; \ + } else { \ + if (node == parent->_f.left) { \ + node = parent; \ + rotate_right(head, node); \ + parent = node->_f.parent; \ + } \ + parent->_f.colour = C_BLACK; \ + gparent->_f.colour = C_RED; \ + rotate_left(head, parent->_f.parent); \ + } \ + } \ + parent = node->_f.parent; \ + } \ + head->top._f.right->_f.colour = C_BLACK; \ + head->count++; \ +} \ + \ +static void \ +deleteblack(struct _n##_rb_head *head, _t *parent, _t *node) \ +{ \ + _t *tmp; \ + \ + while ((node == &_n##_rb_zero || node->_f.colour == C_BLACK) && \ + node != &head->top) { \ + if (parent->_f.left == node) { \ + tmp = parent->_f.right; \ + if (tmp->_f.colour == C_RED) { \ + tmp->_f.colour = C_BLACK; \ + parent->_f.colour = C_RED; \ + rotate_left(head, parent); \ + tmp = parent->_f.right; \ + } \ + if ((tmp->_f.left == &_n##_rb_zero || \ + tmp->_f.left->_f.colour == C_BLACK) && \ + (tmp->_f.right == &_n##_rb_zero || \ + tmp->_f.right->_f.colour == C_BLACK)) { \ + tmp->_f.colour = C_RED; \ + node = parent; \ + parent = node->_f.parent; \ + } else { \ + if (tmp->_f.right == &_n##_rb_zero || \ + tmp->_f.right->_f.colour == C_BLACK) {\ + _t *tmp2 = tmp->_f.left; \ + \ + if (tmp2 != &_n##_rb_zero) \ + tmp2->_f.colour = C_BLACK;\ + tmp->_f.colour = C_RED; \ + rotate_right(head, tmp); \ + tmp = parent->_f.right; \ + } \ + tmp->_f.colour = parent->_f.colour; \ + parent->_f.colour = C_BLACK; \ + if (tmp->_f.right != &_n##_rb_zero) \ + tmp->_f.right->_f.colour = C_BLACK;\ + rotate_left(head, parent); \ + node = head->top._f.right; \ + } \ + } else { \ + tmp = parent->_f.left; \ + if (tmp->_f.colour == C_RED) { \ + tmp->_f.colour = C_BLACK; \ + parent->_f.colour = C_RED; \ + rotate_right(head, parent); \ + tmp = parent->_f.left; \ + } \ + if ((tmp->_f.left == &_n##_rb_zero || \ + tmp->_f.left->_f.colour == C_BLACK) && \ + (tmp->_f.right == &_n##_rb_zero || \ + tmp->_f.right->_f.colour == C_BLACK)) { \ + tmp->_f.colour = C_RED; \ + node = parent; \ + parent = node->_f.parent; \ + } else { \ + if (tmp->_f.left == &_n##_rb_zero || \ + tmp->_f.left->_f.colour == C_BLACK) {\ + _t *tmp2 = tmp->_f.right; \ + \ + if (tmp2 != &_n##_rb_zero) \ + tmp2->_f.colour = C_BLACK;\ + tmp->_f.colour = C_RED; \ + rotate_left(head, tmp); \ + tmp = parent->_f.left; \ + } \ + tmp->_f.colour = parent->_f.colour; \ + parent->_f.colour = C_BLACK; \ + if (tmp->_f.left != &_n##_rb_zero) \ + tmp->_f.left->_f.colour = C_BLACK;\ + rotate_right(head, parent); \ + node = head->top._f.right; \ + break; \ + } \ + } \ + } \ + if (node != &_n##_rb_zero) \ + node->_f.colour = C_BLACK; \ +} \ + \ +_t * \ +_n##_rb_delete(struct _n##_rb_head *head, _t *node) \ +{ \ + _t *child, *parent, *old = node, *left; \ + rbcolour_t color; \ + \ + if (node->_f.left == &_n##_rb_zero) { \ + child = node->_f.right; \ + } else if (node->_f.right == &_n##_rb_zero) { \ + child = node->_f.left; \ + } else { \ + node = node->_f.right; \ + while ((left = node->_f.left) != &_n##_rb_zero) \ + node = left; \ + child = node->_f.right; \ + parent = node->_f.parent; \ + color = node->_f.colour; \ + if (child != &_n##_rb_zero) \ + child->_f.parent = parent; \ + if (parent != &_n##_rb_zero) { \ + if (parent->_f.left == node) \ + parent->_f.left = child; \ + else \ + parent->_f.right = child; \ + } else { \ + head->top._f.right = child; \ + } \ + if (node->_f.parent == old) \ + parent = node; \ + *node = *old; \ + if (old->_f.parent != &_n##_rb_zero) { \ + if (old->_f.parent->_f.left == old) \ + old->_f.parent->_f.left = node; \ + else \ + old->_f.parent->_f.right = node; \ + } else { \ + head->top._f.right = child; \ + } \ + old->_f.left->_f.parent = node; \ + if (old->_f.right != &_n##_rb_zero) \ + old->_f.right->_f.parent = node; \ + if (parent != &_n##_rb_zero) { \ + left = parent; \ + } \ + goto colour; \ + } \ + parent = node->_f.parent; \ + color= node->_f.colour; \ + if (child != &_n##_rb_zero) \ + child->_f.parent = parent; \ + if (parent != &_n##_rb_zero) { \ + if (parent->_f.left == node) \ + parent->_f.left = child; \ + else \ + parent->_f.right = child; \ + } else { \ + head->top._f.right = child; \ + } \ +colour: \ + if (color == C_BLACK) \ + deleteblack(head, parent, node); \ + head->count--; \ + return old; \ +} \ + \ +void \ +_n##_rb_init(struct _n##_rb_head *head) \ +{ \ + memset(head, 0, sizeof(*head)); \ + memset(&_n##_rb_zero, 0, sizeof(_n##_rb_zero)); \ + head->top._f.left = &_n##_rb_zero; \ + head->top._f.right = &_n##_rb_zero; \ + head->top._f.parent = &head->top; \ + _n##_rb_zero._f.left = &_n##_rb_zero; \ + _n##_rb_zero._f.right = &_n##_rb_zero; \ + _n##_rb_zero._f.parent = &_n##_rb_zero; \ +} \ + \ +void \ +_n##_rb_walktree(struct _n##_rb_head *head, _n##_rb_walker_t func, void *arg)\ +{ \ + _t *prev; \ + _t *next; \ + _t *node = head->top._f.right; \ + _t *base; \ + \ + while (node != &_n##_rb_zero) \ + node = node->_f.left; \ + \ + for (;;) { \ + base = node; \ + prev = node; \ + while ((node->_f.parent->_f.right == node) && \ + (node != &_n##_rb_zero)) { \ + prev = node; \ + node = node->_f.parent; \ + } \ + \ + node = prev; \ + for (node = node->_f.parent->_f.right; node != &_n##_rb_zero;\ + node = node->_f.left) \ + prev = node; \ + next = prev; \ + \ + if (node != &_n##_rb_zero) \ + func(node, arg); \ + \ + node = next; \ + if (node == &_n##_rb_zero) \ + break; \ + } \ +} \ + \ +_t * \ +_n##_rb_search(struct _n##_rb_head *head, void *key) \ +{ \ + int match; \ + _t *node; \ + node = head->top._f.right; \ + while (node != &_n##_rb_zero) { \ + match = _cmp(key, node); \ + if (match == 0) \ + break; \ + if (match< 0) \ + node = node->_f.left; \ + else \ + node = node->_f.right; \ + } \ + if (node == &_n##_rb_zero || match != 0) \ + return (NULL); \ + return (node); \ +} + +#define RBI_DELETE(_n, _h, _v) _n##_rb_delete(_h, _v) +#define RBI_FIELD(_n) struct _n##_rb_link +#define RBI_INIT(_n, _h) _n##_rb_init(_h) +#define RBI_INSERT(_n, _h, _v) _n##_rb_insert(_h, _v) +#define RBI_ISEMPTY(_h) ((_h)->count == 0) +#define RBI_SEARCH(_n, _h, _k) _n##_rb_search(_h, _k) +#define RBI_WALK(_n, _h, _w, _a) _n##_rb_walktree(_h, _w, _a) +#define RBI_ZERO(_n) _n##_rb_zero diff --git a/sys/contrib/ipfilter/netinet/ipl.h b/sys/contrib/ipfilter/netinet/ipl.h index 4f2f122..ca3baf2 100644 --- a/sys/contrib/ipfilter/netinet/ipl.h +++ b/sys/contrib/ipfilter/netinet/ipl.h @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -13,8 +13,8 @@ #ifndef __IPL_H__ #define __IPL_H__ -#define IPL_VERSION "IP Filter: v4.1.28" +#define IPL_VERSION "IP Filter: v5.1.2" -#define IPFILTER_VERSION 4012800 +#define IPFILTER_VERSION 5010200 -#endif +#endif /* __IPL_H__ */ diff --git a/sys/contrib/ipfilter/netinet/mlfk_ipl.c b/sys/contrib/ipfilter/netinet/mlfk_ipl.c index 6dcb821..3f4f2e9 100644 --- a/sys/contrib/ipfilter/netinet/mlfk_ipl.c +++ b/sys/contrib/ipfilter/netinet/mlfk_ipl.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2000 by Darren Reed. + * Copyright (C) 2012 by Darren Reed. * * $FreeBSD$ * See the IPFILTER.LICENCE file for details on licencing. @@ -18,20 +18,22 @@ #include <sys/select.h> #if __FreeBSD_version >= 500000 # include <sys/selinfo.h> -#endif +#endif #include <net/if.h> #include <netinet/in_systm.h> #include <netinet/in.h> -#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> +#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" + +extern ipf_main_softc_t ipfmain; #if __FreeBSD_version >= 502116 static struct cdev *ipf_devs[IPL_LOGSIZE]; @@ -39,10 +41,34 @@ static struct cdev *ipf_devs[IPL_LOGSIZE]; static dev_t ipf_devs[IPL_LOGSIZE]; #endif +#if 0 static int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ); +#endif static int ipf_modload(void); static int ipf_modunload(void); +#if (__FreeBSD_version >= 500024) +# if (__FreeBSD_version >= 502116) +static int ipfopen __P((struct cdev*, int, int, struct thread *)); +static int ipfclose __P((struct cdev*, int, int, struct thread *)); +# else +static int ipfopen __P((dev_t, int, int, struct thread *)); +static int ipfclose __P((dev_t, int, int, struct thread *)); +# endif /* __FreeBSD_version >= 502116 */ +#else +static int ipfopen __P((dev_t, int, int, struct proc *)); +static int ipfclose __P((dev_t, int, int, struct proc *)); +#endif +#if (__FreeBSD_version >= 502116) +static int ipfread __P((struct cdev*, struct uio *, int)); +static int ipfwrite __P((struct cdev*, struct uio *, int)); +#else +static int ipfread __P((dev_t, struct uio *, int)); +static int ipfwrite __P((dev_t, struct uio *, int)); +#endif /* __FreeBSD_version >= 502116 */ + + + SYSCTL_DECL(_net_inet); #define SYSCTL_IPF(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_INT|access, \ @@ -50,91 +76,91 @@ SYSCTL_DECL(_net_inet); #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, &fr_flags, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_pass, CTLFLAG_RW, &fr_pass, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &fr_active, 0, ""); +#if 0 +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_flags, CTLFLAG_RW, &ipf_flags, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_pass, CTLFLAG_RW, &ipf_pass, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &ipf_active, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpidletimeout, CTLFLAG_RWO, - &fr_tcpidletimeout, 0, ""); + &ipf_tcpidletimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcphalfclosed, CTLFLAG_RWO, - &fr_tcphalfclosed, 0, ""); + &ipf_tcphalfclosed, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosewait, CTLFLAG_RWO, - &fr_tcpclosewait, 0, ""); + &ipf_tcpclosewait, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcplastack, CTLFLAG_RWO, - &fr_tcplastack, 0, ""); + &ipf_tcplastack, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcptimeout, CTLFLAG_RWO, - &fr_tcptimeout, 0, ""); + &ipf_tcptimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosed, CTLFLAG_RWO, - &fr_tcpclosed, 0, ""); + &ipf_tcpclosed, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udptimeout, CTLFLAG_RWO, - &fr_udptimeout, 0, ""); + &ipf_udptimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udpacktimeout, CTLFLAG_RWO, - &fr_udpacktimeout, 0, ""); + &ipf_udpacktimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_icmptimeout, CTLFLAG_RWO, - &fr_icmptimeout, 0, ""); + &ipf_icmptimeout, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_defnatage, CTLFLAG_RWO, - &fr_defnatage, 0, ""); + &ipf_nat_defage, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_ipfrttl, CTLFLAG_RW, - &fr_ipfrttl, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_running, CTLFLAG_RD, - &fr_running, 0, ""); + &ipf_ipfrttl, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_running, CTLFLAG_RD, + &ipf_running, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_statesize, CTLFLAG_RWO, - &fr_statesize, 0, ""); + &ipf_state_size, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_statemax, CTLFLAG_RWO, - &fr_statemax, 0, ""); + &ipf_state_max, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_nattable_sz, CTLFLAG_RWO, - &ipf_nattable_sz, 0, ""); + &ipf_nat_table_sz, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_natrules_sz, CTLFLAG_RWO, - &ipf_natrules_sz, 0, ""); + &ipf_nat_maprules_sz, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_rdrrules_sz, CTLFLAG_RWO, - &ipf_rdrrules_sz, 0, ""); + &ipf_nat_rdrrules_sz, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_hostmap_sz, CTLFLAG_RWO, - &ipf_hostmap_sz, 0, ""); + &ipf_nat_hostmap_sz, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_authsize, CTLFLAG_RWO, - &fr_authsize, 0, ""); + &ipf_auth_size, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_authused, CTLFLAG_RD, - &fr_authused, 0, ""); + &ipf_auth_used, 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_defaultauthage, CTLFLAG_RW, - &fr_defaultauthage, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &fr_chksrc, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &fr_minttl, 0, ""); + &ipf_auth_defaultage, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &ipf_chksrc, 0, ""); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &ipf_minttl, 0, ""); +#endif #define CDEV_MAJOR 79 #include <sys/poll.h> #if __FreeBSD_version >= 500043 # include <sys/select.h> -static int iplpoll(struct cdev *dev, int events, struct thread *td); +static int ipfpoll(struct cdev *dev, int events, struct thread *td); -static struct cdevsw ipl_cdevsw = { -# if __FreeBSD_version >= 502103 +static struct cdevsw ipf_cdevsw = { +#if __FreeBSD_version >= 502103 .d_version = D_VERSION, .d_flags = 0, /* D_NEEDGIANT - Should be SMP safe */ -# endif - .d_open = iplopen, - .d_close = iplclose, - .d_read = iplread, - .d_write = iplwrite, - .d_ioctl = iplioctl, - .d_name = "ipl", -# if __FreeBSD_version >= 500043 - .d_poll = iplpoll, -# endif -# if __FreeBSD_version < 600000 +#endif + .d_open = ipfopen, + .d_close = ipfclose, + .d_read = ipfread, + .d_write = ipfwrite, + .d_ioctl = ipfioctl, + .d_poll = ipfpoll, + .d_name = "ipf", +#if __FreeBSD_version < 600000 .d_maj = CDEV_MAJOR, -# endif +#endif }; #else -static int iplpoll(dev_t dev, int events, struct proc *p); - -static struct cdevsw ipl_cdevsw = { - /* open */ iplopen, - /* close */ iplclose, - /* read */ iplread, - /* write */ iplwrite, - /* ioctl */ iplioctl, - /* poll */ iplpoll, +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 */ "ipl", + /* name */ "ipf", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, @@ -142,7 +168,7 @@ static struct cdevsw ipl_cdevsw = { # if (__FreeBSD_version < 500043) /* bmaj */ -1, # endif -# if (__FreeBSD_version > 430000) +# if (__FreeBSD_version >= 430000) /* kqfilter */ NULL # endif }; @@ -180,17 +206,15 @@ ipf_modload() char *defpass, *c, *str; int i, j, error; - RWLOCK_INIT(&ipf_global, "ipf filter load/unload mutex"); - RWLOCK_INIT(&ipf_mutex, "ipf filter rwlock"); - RWLOCK_INIT(&ipf_frcache, "ipf cache rwlock"); + if (ipf_load_all() != 0) + return EIO; - error = ipfattach(); - if (error) { - RW_DESTROY(&ipf_global); - RW_DESTROY(&ipf_mutex); - RW_DESTROY(&ipf_frcache); + if (ipf_create_all(&ipfmain) == NULL) + return EIO; + + error = ipfattach(&ipfmain); + if (error) return error; - } for (i = 0; i < IPL_LOGSIZE; i++) ipf_devs[i] = NULL; @@ -204,7 +228,7 @@ ipf_modload() } if (!c) c = str; - ipf_devs[i] = make_dev(&ipl_cdevsw, i, 0, 0, 0600, "%s", c); + ipf_devs[i] = make_dev(&ipf_cdevsw, i, 0, 0, 0600, "%s", c); } error = ipf_pfil_hook(); @@ -212,15 +236,15 @@ ipf_modload() return error; ipf_event_reg(); - if (FR_ISPASS(fr_pass)) + if (FR_ISPASS(ipfmain.ipf_pass)) defpass = "pass"; - else if (FR_ISBLOCK(fr_pass)) + else if (FR_ISBLOCK(ipfmain.ipf_pass)) defpass = "block"; - else + else defpass = "no-match -> block"; printf("%s initialized. Default = %s all, Logging = %s%s\n", - ipfilter_version, defpass, + ipfilter_version, defpass, #ifdef IPFILTER_LOG "enabled", #else @@ -231,7 +255,7 @@ ipf_modload() #else "" #endif - ); + ); return 0; } @@ -241,25 +265,24 @@ ipf_modunload() { int error, i; - if (fr_refcnt) + if (ipfmain.ipf_refcnt) return EBUSY; - if (fr_running >= 0) { - ipf_pfil_unhook(); - ipf_event_dereg(); - WRITE_ENTER(&ipf_global); - error = ipfdetach(); - RWLOCK_EXIT(&ipf_global); + error = ipf_pfil_unhook(); + if (error != 0) + return error; + + if (ipfmain.ipf_running >= 0) { + error = ipfdetach(&ipfmain); if (error != 0) return error; + + ipf_destroy_all(&ipfmain); + ipf_unload_all(); } else error = 0; - RW_DESTROY(&ipf_global); - RW_DESTROY(&ipf_mutex); - RW_DESTROY(&ipf_frcache); - - fr_running = -2; + ipfmain.ipf_running = -2; for (i = 0; ipf_devfiles[i]; i++) { if (ipf_devs[i] != NULL) @@ -285,6 +308,7 @@ MODULE_VERSION(ipfilter, 1); #endif +#if 0 #ifdef SYSCTL_IPF int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) @@ -302,7 +326,7 @@ sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) if (!arg1) error = EPERM; else { - if ((oidp->oid_kind & CTLFLAG_OFF) && (fr_running > 0)) + if ((oidp->oid_kind & CTLFLAG_OFF) && (ipfmain.ipf_running > 0)) error = EBUSY; else error = SYSCTL_IN(req, arg1, sizeof(int)); @@ -310,44 +334,43 @@ sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) return (error); } #endif +#endif static int #if __FreeBSD_version >= 500043 -iplpoll(struct cdev *dev, int events, struct thread *td) +ipfpoll(struct cdev *dev, int events, struct thread *td) #else -iplpoll(dev_t dev, int events, struct proc *td) +ipfpoll(dev_t dev, int events, struct proc *td) #endif { - u_int xmin = GET_MINOR(dev); + int unit = GET_MINOR(dev); int revents; - if (xmin < 0 || xmin > IPL_LOGMAX) + if (unit < 0 || unit > IPL_LOGMAX) return 0; revents = 0; - switch (xmin) + switch (unit) { case IPL_LOGIPF : case IPL_LOGNAT : case IPL_LOGSTATE : #ifdef IPFILTER_LOG - if ((events & (POLLIN | POLLRDNORM)) && ipflog_canread(xmin)) + if ((events & (POLLIN | POLLRDNORM)) && ipf_log_canread(&ipfmain, unit)) revents |= events & (POLLIN | POLLRDNORM); -#endif +#endif break; case IPL_LOGAUTH : - if ((events & (POLLIN | POLLRDNORM)) && fr_auth_waiting()) + if ((events & (POLLIN | POLLRDNORM)) && ipf_auth_waiting(&ipfmain)) revents |= events & (POLLIN | POLLRDNORM); - break; + break; case IPL_LOGSYNC : -#ifdef IPFILTER_SYNC - if ((events & (POLLIN | POLLRDNORM)) && ipfsync_canread()) + if ((events & (POLLIN | POLLRDNORM)) && ipf_sync_canread(&ipfmain)) revents |= events & (POLLIN | POLLRDNORM); - if ((events & (POLLOUT | POLLWRNORM)) && ipfsync_canwrite()) + if ((events & (POLLOUT | POLLWRNORM)) && ipf_sync_canwrite(&ipfmain)) revents |= events & (POLLOUT | POLLWRNORM); -#endif break; case IPL_LOGSCAN : case IPL_LOGLOOKUP : @@ -356,7 +379,152 @@ iplpoll(dev_t dev, int events, struct proc *td) } if ((revents == 0) && ((events & (POLLIN|POLLRDNORM)) != 0)) - selrecord(td, &ipfselwait[xmin]); + selrecord(td, &ipfmain.ipf_selwait[unit]); return revents; } + + +/* + * routines below for saving IP headers to buffer + */ +static int ipfopen(dev, flags +#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) +, devtype, p) + int devtype; +# if (__FreeBSD_version >= 500024) + struct thread *p; +# else + struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +#else +) +#endif +#if (__FreeBSD_version >= 502116) + 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 +#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) +, devtype, p) + int devtype; +# if (__FreeBSD_version >= 500024) + struct thread *p; +# else + struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +#else +) +#endif +#if (__FreeBSD_version >= 502116) + 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 +#if (__FreeBSD_version >= 502116) + struct cdev *dev; +#else + dev_t dev; +#endif + struct uio *uio; +{ + int unit = GET_MINOR(dev); + + if (unit < 0) + return ENXIO; + + if (ipfmain.ipf_running < 1) + return EIO; + + if (unit == IPL_LOGSYNC) + return ipf_sync_read(&ipfmain, uio); + +#ifdef IPFILTER_LOG + return ipf_log_read(&ipfmain, unit, uio); +#else + return ENXIO; +#endif +} + + +/* + * 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 +#if (__FreeBSD_version >= 502116) + struct cdev *dev; +#else + dev_t dev; +#endif + struct uio *uio; +{ + + if (ipfmain.ipf_running < 1) + return EIO; + + if (GET_MINOR(dev) == IPL_LOGSYNC) + return ipf_sync_write(&ipfmain, uio); + return ENXIO; +} diff --git a/sys/contrib/ipfilter/netinet/radix_ipf.c b/sys/contrib/ipfilter/netinet/radix_ipf.c new file mode 100644 index 0000000..f145c38 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/radix_ipf.c @@ -0,0 +1,1528 @@ +/* + * Copyright (C) 2012 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <net/if.h> +#if !defined(_KERNEL) +# include <stddef.h> +# include <stdlib.h> +# include <strings.h> +# include <string.h> +#endif +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#ifdef RDX_DEBUG +# include <arpa/inet.h> +# include <stdlib.h> +# include <stdio.h> +#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 *)); + +/* + * 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 = 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; + 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/sys/contrib/ipfilter/netinet/radix_ipf.h b/sys/contrib/ipfilter/netinet/radix_ipf.h new file mode 100644 index 0000000..73e66b0 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/radix_ipf.h @@ -0,0 +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 *, + 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 *)); + +#endif /* __RADIX_IPF_H__ */ diff --git a/sys/dev/hpt27xx/hpt27xx_osm_bsd.c b/sys/dev/hpt27xx/hpt27xx_osm_bsd.c index 636906d..3c9f0ac 100644 --- a/sys/dev/hpt27xx/hpt27xx_osm_bsd.c +++ b/sys/dev/hpt27xx/hpt27xx_osm_bsd.c @@ -52,7 +52,7 @@ static int hpt_probe(device_t dev) memset(hba, 0, sizeof(HBA)); hba->ext_type = EXT_TYPE_HBA; hba->ldm_adapter.him = him; - return 0; + return (BUS_PROBE_DEFAULT); } } } diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c index 6c9a610..b6d31e3 100644 --- a/sys/dev/usb/controller/xhci.c +++ b/sys/dev/usb/controller/xhci.c @@ -87,12 +87,18 @@ ((struct xhci_softc *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((struct xhci_softc *)0)->sc_bus)))) +static SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI"); + +static int xhcistreams; +SYSCTL_INT(_hw_usb_xhci, OID_AUTO, streams, CTLFLAG_RW | CTLFLAG_TUN, + &xhcistreams, 0, "Set to enable streams mode support"); +TUNABLE_INT("hw.usb.xhci.streams", &xhcistreams); + #ifdef USB_DEBUG static int xhcidebug; static int xhciroute; static int xhcipolling; -static SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI"); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &xhcidebug, 0, "Debug level"); TUNABLE_INT("hw.usb.xhci.debug", &xhcidebug); @@ -4127,7 +4133,8 @@ xhci_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, case USB_EP_MODE_DEFAULT: return (0); case USB_EP_MODE_STREAMS: - if ((ep->edesc->bmAttributes & UE_XFERTYPE) != UE_BULK || + if (xhcistreams == 0 || + (ep->edesc->bmAttributes & UE_XFERTYPE) != UE_BULK || udev->speed != USB_SPEED_SUPER) return (USB_ERR_INVAL); return (0); diff --git a/sys/fs/ext2fs/ext2_htree.c b/sys/fs/ext2fs/ext2_htree.c index 0b5d920..ff1e1a5 100644 --- a/sys/fs/ext2fs/ext2_htree.c +++ b/sys/fs/ext2fs/ext2_htree.c @@ -89,10 +89,12 @@ static int ext2_htree_writebuf(struct ext2fs_htree_lookup_info *info); int ext2_htree_has_idx(struct inode *ip) { +#ifdef EXT2FS_HTREE if (EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX) && ip->i_flags & EXT4_INDEX) return (1); else +#endif return (0); } diff --git a/sys/fs/ext2fs/ext2_lookup.c b/sys/fs/ext2fs/ext2_lookup.c index 990ed33..d607554 100644 --- a/sys/fs/ext2fs/ext2_lookup.c +++ b/sys/fs/ext2fs/ext2_lookup.c @@ -884,6 +884,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1); newentrysize = EXT2_DIR_REC_LEN(newdir.e2d_namlen); +#ifdef EXT2FS_HTREE if (ext2_htree_has_idx(dp)) { error = ext2_htree_add_entry(dvp, &newdir, cnp); if (error) { @@ -904,6 +905,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) return ext2_htree_create_index(dvp, cnp, &newdir); } } +#endif /* EXT2FS_HTREE */ if (dp->i_count == 0) { /* diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 3cb1264..92be947 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -580,6 +580,7 @@ hint.mse.0.irq="5" # nfe: nVidia nForce MCP on-board Ethernet Networking (BSD open source) # nve: nVidia nForce MCP on-board Ethernet Networking # sbni: Granch SBNI12-xx ISA and PCI adapters +# vmx: VMware VMXNET3 Ethernet (BSD open source) # wl: Lucent Wavelan (ISA card only). # wpi: Intel 3945ABG Wireless LAN controller # Requires the wpi firmware module @@ -629,6 +630,7 @@ hint.sbni.0.at="isa" hint.sbni.0.port="0x210" hint.sbni.0.irq="0xefdead" hint.sbni.0.flags="0" +device vmx # VMware VMXNET3 Ethernet device wl hint.wl.0.at="isa" hint.wl.0.port="0x300" diff --git a/sys/i386/i386/mp_machdep.c b/sys/i386/i386/mp_machdep.c index 479168b..60b36a2 100644 --- a/sys/i386/i386/mp_machdep.c +++ b/sys/i386/i386/mp_machdep.c @@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$"); #include <machine/psl.h> #include <machine/smp.h> #include <machine/specialreg.h> +#include <machine/cpu.h> #ifdef XENHVM #include <xen/hvm.h> @@ -170,6 +171,11 @@ u_long *ipi_lazypmap_counts[MAXCPU]; static u_long *ipi_hardclock_counts[MAXCPU]; #endif +/* Default cpu_ops implementation. */ +struct cpu_ops cpu_ops = { + .ipi_vectored = lapic_ipi_vectored +}; + /* * Local data and functions. */ @@ -1209,7 +1215,7 @@ ipi_send_cpu(int cpu, u_int ipi) if (old_pending) return; } - lapic_ipi_vectored(ipi, cpu_apic_ids[cpu]); + cpu_ops.ipi_vectored(ipi, cpu_apic_ids[cpu]); } /* @@ -1460,7 +1466,7 @@ ipi_all_but_self(u_int ipi) CPU_OR_ATOMIC(&ipi_nmi_pending, &other_cpus); CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); - lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS); + cpu_ops.ipi_vectored(ipi, APIC_IPI_DEST_OTHERS); } int diff --git a/sys/i386/include/cpu.h b/sys/i386/include/cpu.h index 5bc9f5c..9655a15 100644 --- a/sys/i386/include/cpu.h +++ b/sys/i386/include/cpu.h @@ -54,6 +54,17 @@ #define TRAPF_PC(framep) ((framep)->tf_eip) #ifdef _KERNEL +/* + * Struct containing pointers to CPU management functions whose + * implementation is run time selectable. Selection can be made, + * for example, based on detection of a particular CPU variant or + * hypervisor environment. + */ +struct cpu_ops { + void (*ipi_vectored)(u_int, int); +}; + +extern struct cpu_ops cpu_ops; extern char btext[]; extern char etext[]; diff --git a/sys/i386/include/pcpu.h b/sys/i386/include/pcpu.h index 3a684bf..60e50f8 100644 --- a/sys/i386/include/pcpu.h +++ b/sys/i386/include/pcpu.h @@ -44,15 +44,6 @@ * other processors" */ -#if defined(XEN) || defined(XENHVM) -#ifndef NR_VIRQS -#define NR_VIRQS 24 -#endif -#ifndef NR_IPIS -#define NR_IPIS 2 -#endif -#endif - #if defined(XEN) /* These are peridically updated in shared_info, and then copied here. */ diff --git a/sys/i386/include/sf_buf.h b/sys/i386/include/sf_buf.h index 415dcbb..20296b3 100644 --- a/sys/i386/include/sf_buf.h +++ b/sys/i386/include/sf_buf.h @@ -45,6 +45,9 @@ struct sf_buf { #endif }; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); + static __inline vm_offset_t sf_buf_kva(struct sf_buf *sf) { diff --git a/sys/i386/include/smp.h b/sys/i386/include/smp.h index 7c11820..43dad10 100644 --- a/sys/i386/include/smp.h +++ b/sys/i386/include/smp.h @@ -84,11 +84,6 @@ void smp_masked_invltlb(cpuset_t mask); #ifdef XEN void ipi_to_irq_init(void); - -#define RESCHEDULE_VECTOR 0 -#define CALL_FUNCTION_VECTOR 1 -#define NR_IPIS 2 - #endif #endif /* !LOCORE */ #endif /* SMP */ diff --git a/sys/i386/xen/mp_machdep.c b/sys/i386/xen/mp_machdep.c index 7ac1dbb..d6eb35b 100644 --- a/sys/i386/xen/mp_machdep.c +++ b/sys/i386/xen/mp_machdep.c @@ -99,25 +99,37 @@ extern void failsafe_callback(void); extern void pmap_lazyfix_action(void); /*--------------------------- Forward Declarations ---------------------------*/ -static void assign_cpu_ids(void); -static void set_interrupt_apic_ids(void); -static int start_all_aps(void); -static int start_ap(int apic_id); -static void release_aps(void *dummy); +static driver_filter_t smp_reschedule_interrupt; +static driver_filter_t smp_call_function_interrupt; +static void assign_cpu_ids(void); +static void set_interrupt_apic_ids(void); +static int start_all_aps(void); +static int start_ap(int apic_id); +static void release_aps(void *dummy); + +/*---------------------------------- Macros ----------------------------------*/ +#define IPI_TO_IDX(ipi) ((ipi) - APIC_IPI_INTS) /*-------------------------------- Local Types -------------------------------*/ typedef void call_data_func_t(uintptr_t , uintptr_t); -/* - * Store data from cpu_add() until later in the boot when we actually setup - * the APs. - */ struct cpu_info { int cpu_present:1; int cpu_bsp:1; int cpu_disabled:1; }; +struct xen_ipi_handler +{ + driver_filter_t *filter; + const char *description; +}; + +enum { + RESCHEDULE_VECTOR, + CALL_FUNCTION_VECTOR, +}; + /*-------------------------------- Global Data -------------------------------*/ static u_int hyperthreading_cpus; static cpuset_t hyperthreading_cpus_mask; @@ -161,8 +173,14 @@ static volatile u_int cpu_ipi_pending[MAXCPU]; static int cpu_logical; static int cpu_cores; +static const struct xen_ipi_handler xen_ipis[] = +{ + [RESCHEDULE_VECTOR] = { smp_reschedule_interrupt, "resched" }, + [CALL_FUNCTION_VECTOR] = { smp_call_function_interrupt,"callfunc" } +}; + /*------------------------------- Per-CPU Data -------------------------------*/ -DPCPU_DEFINE(xen_intr_handle_t, ipi_port[NR_IPIS]); +DPCPU_DEFINE(xen_intr_handle_t, ipi_handle[nitems(xen_ipis)]); DPCPU_DEFINE(struct vcpu_info *, vcpu_info); /*------------------------------ Implementation ------------------------------*/ @@ -362,7 +380,7 @@ iv_lazypmap(uintptr_t a, uintptr_t b) /* * These start from "IPI offset" APIC_IPI_INTS */ -static call_data_func_t *ipi_vectors[] = +static call_data_func_t *ipi_vectors[6] = { iv_rendezvous, iv_invltlb, @@ -427,7 +445,7 @@ smp_call_function_interrupt(void *unused) call_data->func_id > IPI_BITMAP_VECTOR) panic("invalid function id %u", call_data->func_id); - func = ipi_vectors[call_data->func_id - APIC_IPI_INTS]; + func = ipi_vectors[IPI_TO_IDX(call_data->func_id)]; /* * Notify initiating CPU that I've grabbed the data and am * about to execute the function @@ -473,44 +491,43 @@ cpu_mp_announce(void) static int xen_smp_cpu_init(unsigned int cpu) { - int rc; - xen_intr_handle_t irq_handle; + xen_intr_handle_t *ipi_handle; + const struct xen_ipi_handler *ipi; + int idx, rc; - DPCPU_ID_SET(cpu, ipi_port[RESCHEDULE_VECTOR], NULL); - DPCPU_ID_SET(cpu, ipi_port[CALL_FUNCTION_VECTOR], NULL); + ipi_handle = DPCPU_ID_GET(cpu, ipi_handle); + for (ipi = xen_ipis, idx = 0; idx < nitems(xen_ipis); ipi++, idx++) { - /* - * The PCPU variable pc_device is not initialized on i386 PV, - * so we have to use the root_bus device in order to setup - * the IPIs. - */ - rc = xen_intr_bind_ipi(root_bus, RESCHEDULE_VECTOR, - cpu, smp_reschedule_interrupt, INTR_TYPE_TTY, &irq_handle); - if (rc < 0) - goto fail; - xen_intr_describe(irq_handle, "resched%u", cpu); - DPCPU_ID_SET(cpu, ipi_port[RESCHEDULE_VECTOR], irq_handle); - - printf("[XEN] IPI cpu=%d port=%d vector=RESCHEDULE_VECTOR (%d)\n", - cpu, xen_intr_port(irq_handle), RESCHEDULE_VECTOR); - - rc = xen_intr_bind_ipi(root_bus, CALL_FUNCTION_VECTOR, - cpu, smp_call_function_interrupt, INTR_TYPE_TTY, &irq_handle); - if (rc < 0) - goto fail; - xen_intr_describe(irq_handle, "callfunc%u", cpu); - DPCPU_ID_SET(cpu, ipi_port[CALL_FUNCTION_VECTOR], irq_handle); - - printf("[XEN] IPI cpu=%d port=%d vector=CALL_FUNCTION_VECTOR (%d)\n", - cpu, xen_intr_port(irq_handle), CALL_FUNCTION_VECTOR); + /* + * The PCPU variable pc_device is not initialized on i386 PV, + * so we have to use the root_bus device in order to setup + * the IPIs. + */ + rc = xen_intr_alloc_and_bind_ipi(root_bus, cpu, + ipi->filter, INTR_TYPE_TTY, &ipi_handle[idx]); + if (rc != 0) { + printf("Unable to allocate a XEN IPI port. " + "Error %d\n", rc); + break; + } + xen_intr_describe(ipi_handle[idx], "%s", ipi->description); + } - return (0); + for (;idx < nitems(xen_ipis); idx++) + ipi_handle[idx] = NULL; + + if (rc == 0) + return (0); + + /* Either all are successfully mapped, or none at all. */ + for (idx = 0; idx < nitems(xen_ipis); idx++) { + if (ipi_handle[idx] == NULL) + continue; + + xen_intr_unbind(ipi_handle[idx]); + ipi_handle[idx] = NULL; + } - fail: - xen_intr_unbind(DPCPU_ID_GET(cpu, ipi_port[RESCHEDULE_VECTOR])); - DPCPU_ID_SET(cpu, ipi_port[RESCHEDULE_VECTOR], NULL); - xen_intr_unbind(DPCPU_ID_GET(cpu, ipi_port[CALL_FUNCTION_VECTOR])); - DPCPU_ID_SET(cpu, ipi_port[CALL_FUNCTION_VECTOR], NULL); return (rc); } @@ -980,8 +997,8 @@ start_ap(int apic_id) static void ipi_pcpu(int cpu, u_int ipi) { - KASSERT((ipi <= NR_IPIS), ("invalid IPI")); - xen_intr_signal(DPCPU_ID_GET(cpu, ipi_port[ipi])); + KASSERT((ipi <= nitems(xen_ipis)), ("invalid IPI")); + xen_intr_signal(DPCPU_ID_GET(cpu, ipi_handle[ipi])); } /* diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c index d8e8e05..042afa3 100644 --- a/sys/kern/subr_prf.c +++ b/sys/kern/subr_prf.c @@ -175,15 +175,24 @@ out: } /* - * tprintf prints on the controlling terminal associated with the given - * session, possibly to the log as well. + * tprintf and vtprintf print on the controlling terminal associated with the + * given session, possibly to the log as well. */ void tprintf(struct proc *p, int pri, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vtprintf(p, pri, fmt, ap); + va_end(ap); +} + +void +vtprintf(struct proc *p, int pri, const char *fmt, va_list ap) +{ struct tty *tp = NULL; int flags = 0; - va_list ap; struct putchar_arg pca; struct session *sess = NULL; @@ -208,13 +217,11 @@ tprintf(struct proc *p, int pri, const char *fmt, ...) pca.tty = tp; pca.flags = flags; pca.p_bufr = NULL; - va_start(ap, fmt); if (pca.tty != NULL) tty_lock(pca.tty); kvprintf(fmt, putchar, &pca, 10, ap); if (pca.tty != NULL) tty_unlock(pca.tty); - va_end(ap); if (sess != NULL) sess_release(sess); msgbuftrigger = 1; diff --git a/sys/mips/atheros/ar71xx_gpio.c b/sys/mips/atheros/ar71xx_gpio.c index 6ad8df4..c6933bc 100644 --- a/sys/mips/atheros/ar71xx_gpio.c +++ b/sys/mips/atheros/ar71xx_gpio.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/rman.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/gpio.h> @@ -418,7 +419,14 @@ ar71xx_gpio_attach(device_t dev) "pinon", &pinon) != 0) pinon = 0; device_printf(dev, "gpio pinmask=0x%x\n", mask); - for (i = 0, j = 0; j < maxpin; j++) { + for (j = 0; j <= maxpin; j++) { + if ((mask & (1 << j)) == 0) + continue; + sc->gpio_npins++; + } + sc->gpio_pins = malloc(sizeof(*sc->gpio_pins) * sc->gpio_npins, + M_DEVBUF, M_WAITOK | M_ZERO); + for (i = 0, j = 0; j <= maxpin; j++) { if ((mask & (1 << j)) == 0) continue; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, @@ -429,7 +437,6 @@ ar71xx_gpio_attach(device_t dev) ar71xx_gpio_pin_configure(sc, &sc->gpio_pins[i], DEFAULT_CAPS); i++; } - sc->gpio_npins = i; for (i = 0; i < sc->gpio_npins; i++) { j = sc->gpio_pins[i].gp_pin; if ((pinon & (1 << j)) != 0) @@ -455,6 +462,7 @@ ar71xx_gpio_detach(device_t dev) bus_release_resource(dev, SYS_RES_MEMORY, sc->gpio_mem_rid, sc->gpio_mem_res); + free(sc->gpio_pins, M_DEVBUF); mtx_destroy(&sc->gpio_mtx); return(0); diff --git a/sys/mips/atheros/ar71xx_gpiovar.h b/sys/mips/atheros/ar71xx_gpiovar.h index 3489f5a..a1c6e2f 100644 --- a/sys/mips/atheros/ar71xx_gpiovar.h +++ b/sys/mips/atheros/ar71xx_gpiovar.h @@ -64,7 +64,7 @@ struct ar71xx_gpio_softc { int gpio_irq_rid; void *gpio_ih; int gpio_npins; - struct gpio_pin gpio_pins[AR71XX_GPIO_PINS]; + struct gpio_pin *gpio_pins; }; #endif /* __AR71XX_GPIOVAR_H__ */ diff --git a/sys/mips/include/sf_buf.h b/sys/mips/include/sf_buf.h index 00502a0..e5d981f 100644 --- a/sys/mips/include/sf_buf.h +++ b/sys/mips/include/sf_buf.h @@ -78,6 +78,9 @@ struct sf_buf { vm_offset_t kva; /* va of mapping */ }; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); + static __inline vm_offset_t sf_buf_kva(struct sf_buf *sf) { diff --git a/sys/modules/ipfilter/Makefile b/sys/modules/ipfilter/Makefile index db780e9..6e6accf 100644 --- a/sys/modules/ipfilter/Makefile +++ b/sys/modules/ipfilter/Makefile @@ -7,7 +7,8 @@ KMOD= ipl SRCS= mlfk_ipl.c ip_nat.c ip_frag.c ip_state.c ip_proxy.c ip_auth.c \ ip_log.c ip_fil_freebsd.c fil.c ip_lookup.c ip_pool.c ip_htable.c \ - ip_sync.c + ip_sync.c \ + ip_nat6.c ip_rules.c ip_scan.c ip_dstlist.c radix_ipf.c SRCS+= opt_bpf.h opt_inet6.h .if !defined(KERNBUILDDIR) diff --git a/sys/net/netisr.c b/sys/net/netisr.c index 534d80c..3045e95 100644 --- a/sys/net/netisr.c +++ b/sys/net/netisr.c @@ -154,19 +154,6 @@ SYSCTL_PROC(_net_isr, OID_AUTO, dispatch, CTLTYPE_STRING | CTLFLAG_RW | "netisr dispatch policy"); /* - * These sysctls were used in previous versions to control and export - * dispatch policy state. Now, we provide read-only export via them so that - * older netstat binaries work. At some point they can be garbage collected. - */ -static int netisr_direct_force; -SYSCTL_INT(_net_isr, OID_AUTO, direct_force, CTLFLAG_RD, - &netisr_direct_force, 0, "compat: force direct dispatch"); - -static int netisr_direct; -SYSCTL_INT(_net_isr, OID_AUTO, direct, CTLFLAG_RD, &netisr_direct, 0, - "compat: enable direct dispatch"); - -/* * Allow the administrator to limit the number of threads (CPUs) to use for * netisr. We don't check netisr_maxthreads before creating the thread for * CPU 0, so in practice we ignore values <= 1. This must be set at boot. @@ -338,32 +325,6 @@ netisr_dispatch_policy_from_str(const char *str, u_int *dispatch_policyp) return (EINVAL); } -static void -netisr_dispatch_policy_compat(void) -{ - - switch (netisr_dispatch_policy) { - case NETISR_DISPATCH_DEFERRED: - netisr_direct_force = 0; - netisr_direct = 0; - break; - - case NETISR_DISPATCH_HYBRID: - netisr_direct_force = 0; - netisr_direct = 1; - break; - - case NETISR_DISPATCH_DIRECT: - netisr_direct_force = 1; - netisr_direct = 1; - break; - - default: - panic("%s: unknown policy %u", __func__, - netisr_dispatch_policy); - } -} - static int sysctl_netisr_dispatch_policy(SYSCTL_HANDLER_ARGS) { @@ -379,10 +340,8 @@ sysctl_netisr_dispatch_policy(SYSCTL_HANDLER_ARGS) &dispatch_policy); if (error == 0 && dispatch_policy == NETISR_DISPATCH_DEFAULT) error = EINVAL; - if (error == 0) { + if (error == 0) netisr_dispatch_policy = dispatch_policy; - netisr_dispatch_policy_compat(); - } } return (error); } @@ -1199,10 +1158,9 @@ netisr_init(void *arg) &dispatch_policy); if (error == 0 && dispatch_policy == NETISR_DISPATCH_DEFAULT) error = EINVAL; - if (error == 0) { + if (error == 0) netisr_dispatch_policy = dispatch_policy; - netisr_dispatch_policy_compat(); - } else + else printf( "%s: invalid dispatch policy %s, using default\n", __func__, tmp); diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index 7ebf7f1..26fcf85 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -789,13 +789,12 @@ doit_again: * but should we? */ if (stcb->sctp_socket) { - pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket), + pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket) >> SCTP_PARTIAL_DELIVERY_SHIFT, stcb->sctp_ep->partial_delivery_point); } else { pd_point = stcb->sctp_ep->partial_delivery_point; } if (sctp_is_all_msg_on_reasm(asoc, &tsize) || (tsize >= pd_point)) { - /* * Yes, we setup to start reception, by * backing down the TSN just in case we @@ -2491,7 +2490,7 @@ doit_again: * delivery queue and something can be delivered. */ if (stcb->sctp_socket) { - pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket), + pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket) >> SCTP_PARTIAL_DELIVERY_SHIFT, stcb->sctp_ep->partial_delivery_point); } else { pd_point = stcb->sctp_ep->partial_delivery_point; diff --git a/sys/nlm/nlm_prot_impl.c b/sys/nlm/nlm_prot_impl.c index dca0300..387b009 100644 --- a/sys/nlm/nlm_prot_impl.c +++ b/sys/nlm/nlm_prot_impl.c @@ -133,6 +133,11 @@ static time_t nlm_grace_threshold; static time_t nlm_next_idle_check; /* + * A flag to indicate the server is already running. + */ +static int nlm_is_running; + +/* * A socket to use for RPC - shared by all IPv4 RPC clients. */ static struct socket *nlm_socket; @@ -1526,51 +1531,51 @@ nlm_server_main(int addr_count, char **addrs) struct nlm_waiting_lock *nw; vop_advlock_t *old_nfs_advlock; vop_reclaim_t *old_nfs_reclaim; - int v4_used; -#ifdef INET6 - int v6_used; -#endif - if (nlm_socket) { + if (nlm_is_running != 0) { NLM_ERR("NLM: can't start server - " "it appears to be running already\n"); return (EPERM); } - memset(&opt, 0, sizeof(opt)); + if (nlm_socket == NULL) { + memset(&opt, 0, sizeof(opt)); - nlm_socket = NULL; - error = socreate(AF_INET, &nlm_socket, SOCK_DGRAM, 0, - td->td_ucred, td); - if (error) { - NLM_ERR("NLM: can't create IPv4 socket - error %d\n", error); - return (error); - } - opt.sopt_dir = SOPT_SET; - opt.sopt_level = IPPROTO_IP; - opt.sopt_name = IP_PORTRANGE; - portlow = IP_PORTRANGE_LOW; - opt.sopt_val = &portlow; - opt.sopt_valsize = sizeof(portlow); - sosetopt(nlm_socket, &opt); + error = socreate(AF_INET, &nlm_socket, SOCK_DGRAM, 0, + td->td_ucred, td); + if (error) { + NLM_ERR("NLM: can't create IPv4 socket - error %d\n", + error); + return (error); + } + opt.sopt_dir = SOPT_SET; + opt.sopt_level = IPPROTO_IP; + opt.sopt_name = IP_PORTRANGE; + portlow = IP_PORTRANGE_LOW; + opt.sopt_val = &portlow; + opt.sopt_valsize = sizeof(portlow); + sosetopt(nlm_socket, &opt); #ifdef INET6 - nlm_socket6 = NULL; - error = socreate(AF_INET6, &nlm_socket6, SOCK_DGRAM, 0, - td->td_ucred, td); - if (error) { - NLM_ERR("NLM: can't create IPv6 socket - error %d\n", error); - goto out; - return (error); - } - opt.sopt_dir = SOPT_SET; - opt.sopt_level = IPPROTO_IPV6; - opt.sopt_name = IPV6_PORTRANGE; - portlow = IPV6_PORTRANGE_LOW; - opt.sopt_val = &portlow; - opt.sopt_valsize = sizeof(portlow); - sosetopt(nlm_socket6, &opt); + nlm_socket6 = NULL; + error = socreate(AF_INET6, &nlm_socket6, SOCK_DGRAM, 0, + td->td_ucred, td); + if (error) { + NLM_ERR("NLM: can't create IPv6 socket - error %d\n", + error); + soclose(nlm_socket); + nlm_socket = NULL; + return (error); + } + opt.sopt_dir = SOPT_SET; + opt.sopt_level = IPPROTO_IPV6; + opt.sopt_name = IPV6_PORTRANGE; + portlow = IPV6_PORTRANGE_LOW; + opt.sopt_val = &portlow; + opt.sopt_valsize = sizeof(portlow); + sosetopt(nlm_socket6, &opt); #endif + } nlm_auth = authunix_create(curthread->td_ucred); @@ -1622,6 +1627,7 @@ nlm_server_main(int addr_count, char **addrs) error = EINVAL; goto out; } + nlm_is_running = 1; NLM_DEBUG(1, "NLM: local NSM state is %d\n", smstat.state); nlm_nsm_state = smstat.state; @@ -1638,6 +1644,7 @@ nlm_server_main(int addr_count, char **addrs) nfs_reclaim_p = old_nfs_reclaim; out: + nlm_is_running = 0; if (pool) svcpool_destroy(pool); @@ -1661,13 +1668,8 @@ out: * nlm_hosts to the host (which may remove it from the list * and free it). After this phase, the only entries in the * nlm_host list should be from other threads performing - * client lock requests. We arrange to defer closing the - * sockets until the last RPC client handle is released. + * client lock requests. */ - v4_used = 0; -#ifdef INET6 - v6_used = 0; -#endif mtx_lock(&nlm_global_lock); TAILQ_FOREACH(nw, &nlm_waiting_locks, nw_link) { wakeup(nw); @@ -1678,43 +1680,10 @@ out: nlm_host_release(host); mtx_lock(&nlm_global_lock); } - TAILQ_FOREACH_SAFE(host, &nlm_hosts, nh_link, nhost) { - mtx_lock(&host->nh_lock); - if (host->nh_srvrpc.nr_client - || host->nh_clntrpc.nr_client) { - if (host->nh_addr.ss_family == AF_INET) - v4_used++; -#ifdef INET6 - if (host->nh_addr.ss_family == AF_INET6) - v6_used++; -#endif - /* - * Note that the rpc over udp code copes - * correctly with the fact that a socket may - * be used by many rpc handles. - */ - if (host->nh_srvrpc.nr_client) - CLNT_CONTROL(host->nh_srvrpc.nr_client, - CLSET_FD_CLOSE, 0); - if (host->nh_clntrpc.nr_client) - CLNT_CONTROL(host->nh_clntrpc.nr_client, - CLSET_FD_CLOSE, 0); - } - mtx_unlock(&host->nh_lock); - } mtx_unlock(&nlm_global_lock); AUTH_DESTROY(nlm_auth); - if (!v4_used) - soclose(nlm_socket); - nlm_socket = NULL; -#ifdef INET6 - if (!v6_used) - soclose(nlm_socket6); - nlm_socket6 = NULL; -#endif - return (error); } @@ -2435,7 +2404,15 @@ static int nfslockd_modevent(module_t mod, int type, void *data) { - return (0); + switch (type) { + case MOD_LOAD: + return (0); + case MOD_UNLOAD: + /* The NLM module cannot be safely unloaded. */ + /* FALLTHROUGH */ + default: + return (EOPNOTSUPP); + } } static moduledata_t nfslockd_mod = { "nfslockd", diff --git a/sys/powerpc/include/sf_buf.h b/sys/powerpc/include/sf_buf.h index 7ddb981..f8a5936 100644 --- a/sys/powerpc/include/sf_buf.h +++ b/sys/powerpc/include/sf_buf.h @@ -45,6 +45,9 @@ struct sf_buf { int ref_count; /* usage of this mapping */ }; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); + /* * On 32-bit OEA, the only purpose for which sf_buf is used is to implement * an opaque pointer required by the machine-independent parts of the kernel. diff --git a/sys/sparc64/include/sf_buf.h b/sys/sparc64/include/sf_buf.h index b6ee1cc..ebbbea8 100644 --- a/sys/sparc64/include/sf_buf.h +++ b/sys/sparc64/include/sf_buf.h @@ -39,6 +39,9 @@ struct sf_buf { vm_offset_t kva; /* va of mapping */ }; +struct sf_buf * sf_buf_alloc(struct vm_page *m, int flags); +void sf_buf_free(struct sf_buf *sf); + static __inline vm_offset_t sf_buf_kva(struct sf_buf *sf) { diff --git a/sys/sys/param.h b/sys/sys/param.h index fa3e2eb..0099a4d 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1000053 /* Master, propagated to newvers */ +#define __FreeBSD_version 1000054 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/sys/sys/sf_buf.h b/sys/sys/sf_buf.h index 61643b0..2e85bc8 100644 --- a/sys/sys/sf_buf.h +++ b/sys/sys/sf_buf.h @@ -65,9 +65,6 @@ extern counter_u64_t sfstat[sizeof(struct sfstat) / sizeof(uint64_t)]; #define SFSTAT_INC(name) SFSTAT_ADD(name, 1) #endif /* _KERNEL */ -struct sf_buf * - sf_buf_alloc(struct vm_page *m, int flags); -void sf_buf_free(struct sf_buf *sf); int sf_buf_mext(struct mbuf *mb, void *addr, void *args); #endif /* !_SYS_SF_BUF_H_ */ diff --git a/sys/sys/systm.h b/sys/sys/systm.h index e3ea9cf..f097e83 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -212,6 +212,7 @@ u_long strtoul(const char *, char **, int) __nonnull(1); quad_t strtoq(const char *, char **, int) __nonnull(1); u_quad_t strtouq(const char *, char **, int) __nonnull(1); void tprintf(struct proc *p, int pri, const char *, ...) __printflike(3, 4); +void vtprintf(struct proc *, int, const char *, __va_list) __printflike(3, 0); void hexdump(const void *ptr, int length, const char *hdr, int flags); #define HD_COLUMN_MASK 0xff #define HD_DELIM_MASK 0xff00 diff --git a/sys/x86/xen/hvm.c b/sys/x86/xen/hvm.c index c689b47..0404fe9 100644 --- a/sys/x86/xen/hvm.c +++ b/sys/x86/xen/hvm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Citrix Systems, Inc. + * Copyright (c) 2008, 2013 Citrix Systems, Inc. * Copyright (c) 2012 Spectra Logic Corporation * All rights reserved. * @@ -33,9 +33,19 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/proc.h> +#include <sys/smp.h> +#include <sys/systm.h> + +#include <vm/vm.h> +#include <vm/pmap.h> #include <dev/pci/pcivar.h> + #include <machine/cpufunc.h> +#include <machine/cpu.h> +#include <machine/smp.h> + +#include <x86/apicreg.h> #include <xen/xen-os.h> #include <xen/features.h> @@ -44,30 +54,450 @@ __FBSDID("$FreeBSD$"); #include <xen/hvm.h> #include <xen/xen_intr.h> -#include <vm/vm.h> -#include <vm/pmap.h> - #include <xen/interface/hvm/params.h> #include <xen/interface/vcpu.h> +/*--------------------------- Forward Declarations ---------------------------*/ +static driver_filter_t xen_smp_rendezvous_action; +static driver_filter_t xen_invltlb; +static driver_filter_t xen_invlpg; +static driver_filter_t xen_invlrng; +static driver_filter_t xen_invlcache; +#ifdef __i386__ +static driver_filter_t xen_lazypmap; +#endif +static driver_filter_t xen_ipi_bitmap_handler; +static driver_filter_t xen_cpustop_handler; +static driver_filter_t xen_cpususpend_handler; +static driver_filter_t xen_cpustophard_handler; + +/*---------------------------- Extern Declarations ---------------------------*/ +/* Variables used by mp_machdep to perform the MMU related IPIs */ +extern volatile int smp_tlb_wait; +extern vm_offset_t smp_tlb_addr2; +#ifdef __i386__ +extern vm_offset_t smp_tlb_addr1; +#else +extern struct invpcid_descr smp_tlb_invpcid; +extern uint64_t pcid_cr3; +extern int invpcid_works; +extern int pmap_pcid_enabled; +extern pmap_t smp_tlb_pmap; +#endif + +#ifdef __i386__ +extern void pmap_lazyfix_action(void); +#endif + +/*---------------------------------- Macros ----------------------------------*/ +#define IPI_TO_IDX(ipi) ((ipi) - APIC_IPI_INTS) + +/*-------------------------------- Local Types -------------------------------*/ +struct xen_ipi_handler +{ + driver_filter_t *filter; + const char *description; +}; + +/*-------------------------------- Global Data -------------------------------*/ +enum xen_domain_type xen_domain_type = XEN_NATIVE; + static MALLOC_DEFINE(M_XENHVM, "xen_hvm", "Xen HVM PV Support"); -DPCPU_DEFINE(struct vcpu_info, vcpu_local_info); -DPCPU_DEFINE(struct vcpu_info *, vcpu_info); +static struct xen_ipi_handler xen_ipis[] = +{ + [IPI_TO_IDX(IPI_RENDEZVOUS)] = { xen_smp_rendezvous_action, "r" }, + [IPI_TO_IDX(IPI_INVLTLB)] = { xen_invltlb, "itlb"}, + [IPI_TO_IDX(IPI_INVLPG)] = { xen_invlpg, "ipg" }, + [IPI_TO_IDX(IPI_INVLRNG)] = { xen_invlrng, "irg" }, + [IPI_TO_IDX(IPI_INVLCACHE)] = { xen_invlcache, "ic" }, +#ifdef __i386__ + [IPI_TO_IDX(IPI_LAZYPMAP)] = { xen_lazypmap, "lp" }, +#endif + [IPI_TO_IDX(IPI_BITMAP_VECTOR)] = { xen_ipi_bitmap_handler, "b" }, + [IPI_TO_IDX(IPI_STOP)] = { xen_cpustop_handler, "st" }, + [IPI_TO_IDX(IPI_SUSPEND)] = { xen_cpususpend_handler, "sp" }, + [IPI_TO_IDX(IPI_STOP_HARD)] = { xen_cpustophard_handler, "sth" }, +}; -/*-------------------------------- Global Data -------------------------------*/ /** * If non-zero, the hypervisor has been configured to use a direct * IDT event callback for interrupt injection. */ int xen_vector_callback_enabled; +/*------------------------------- Per-CPU Data -------------------------------*/ +DPCPU_DEFINE(struct vcpu_info, vcpu_local_info); +DPCPU_DEFINE(struct vcpu_info *, vcpu_info); +DPCPU_DEFINE(xen_intr_handle_t, ipi_handle[nitems(xen_ipis)]); + /*------------------ Hypervisor Access Shared Memory Regions -----------------*/ /** Hypercall table accessed via HYPERVISOR_*_op() methods. */ char *hypercall_stubs; shared_info_t *HYPERVISOR_shared_info; -enum xen_domain_type xen_domain_type = XEN_NATIVE; +/*---------------------------- XEN PV IPI Handlers ---------------------------*/ +/* + * This are C clones of the ASM functions found in apic_vector.s + */ +static int +xen_ipi_bitmap_handler(void *arg) +{ + struct trapframe *frame; + + frame = arg; + ipi_bitmap_handler(*frame); + return (FILTER_HANDLED); +} + +static int +xen_smp_rendezvous_action(void *arg) +{ +#ifdef COUNT_IPIS + int cpu; + + cpu = PCPU_GET(cpuid); + (*ipi_rendezvous_counts[cpu])++; +#endif /* COUNT_IPIS */ + + smp_rendezvous_action(); + return (FILTER_HANDLED); +} + +static int +xen_invltlb(void *arg) +{ +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_gbl[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invltlb_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + + invltlb(); + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} + +#ifdef __amd64__ +static int +xen_invltlb_pcid(void *arg) +{ + uint64_t cr3; +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_gbl[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invltlb_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + + cr3 = rcr3(); + if (smp_tlb_invpcid.pcid != (uint64_t)-1 && + smp_tlb_invpcid.pcid != 0) { + + if (invpcid_works) { + invpcid(&smp_tlb_invpcid, INVPCID_CTX); + } else { + /* Otherwise reload %cr3 twice. */ + if (cr3 != pcid_cr3) { + load_cr3(pcid_cr3); + cr3 |= CR3_PCID_SAVE; + } + load_cr3(cr3); + } + } else { + invltlb_globpcid(); + } + if (smp_tlb_pmap != NULL) + CPU_CLR_ATOMIC(PCPU_GET(cpuid), &smp_tlb_pmap->pm_save); + + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} +#endif + +static int +xen_invlpg(void *arg) +{ +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_pg[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invlpg_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + +#ifdef __i386__ + invlpg(smp_tlb_addr1); +#else + invlpg(smp_tlb_invpcid.addr); +#endif + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} + +#ifdef __amd64__ +static int +xen_invlpg_pcid(void *arg) +{ +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_pg[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invlpg_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + + if (invpcid_works) { + invpcid(&smp_tlb_invpcid, INVPCID_ADDR); + } else if (smp_tlb_invpcid.pcid == 0) { + invlpg(smp_tlb_invpcid.addr); + } else if (smp_tlb_invpcid.pcid == (uint64_t)-1) { + invltlb_globpcid(); + } else { + uint64_t cr3; + + /* + * PCID supported, but INVPCID is not. + * Temporarily switch to the target address + * space and do INVLPG. + */ + cr3 = rcr3(); + if (cr3 != pcid_cr3) + load_cr3(pcid_cr3 | CR3_PCID_SAVE); + invlpg(smp_tlb_invpcid.addr); + load_cr3(cr3 | CR3_PCID_SAVE); + } + + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} +#endif + +static inline void +invlpg_range(vm_offset_t start, vm_offset_t end) +{ + do { + invlpg(start); + start += PAGE_SIZE; + } while (start < end); +} + +static int +xen_invlrng(void *arg) +{ + vm_offset_t addr; +#if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) + int cpu; + + cpu = PCPU_GET(cpuid); +#ifdef COUNT_XINVLTLB_HITS + xhits_rng[cpu]++; +#endif /* COUNT_XINVLTLB_HITS */ +#ifdef COUNT_IPIS + (*ipi_invlrng_counts[cpu])++; +#endif /* COUNT_IPIS */ +#endif /* COUNT_XINVLTLB_HITS || COUNT_IPIS */ + +#ifdef __i386__ + addr = smp_tlb_addr1; + invlpg_range(addr, smp_tlb_addr2); +#else + addr = smp_tlb_invpcid.addr; + if (pmap_pcid_enabled) { + if (invpcid_works) { + struct invpcid_descr d; + + d = smp_tlb_invpcid; + do { + invpcid(&d, INVPCID_ADDR); + d.addr += PAGE_SIZE; + } while (d.addr < smp_tlb_addr2); + } else if (smp_tlb_invpcid.pcid == 0) { + /* + * kernel pmap - use invlpg to invalidate + * global mapping. + */ + invlpg_range(addr, smp_tlb_addr2); + } else if (smp_tlb_invpcid.pcid != (uint64_t)-1) { + invltlb_globpcid(); + if (smp_tlb_pmap != NULL) { + CPU_CLR_ATOMIC(PCPU_GET(cpuid), + &smp_tlb_pmap->pm_save); + } + } else { + uint64_t cr3; + + cr3 = rcr3(); + if (cr3 != pcid_cr3) + load_cr3(pcid_cr3 | CR3_PCID_SAVE); + invlpg_range(addr, smp_tlb_addr2); + load_cr3(cr3 | CR3_PCID_SAVE); + } + } else { + invlpg_range(addr, smp_tlb_addr2); + } +#endif + + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} + +static int +xen_invlcache(void *arg) +{ +#ifdef COUNT_IPIS + int cpu = PCPU_GET(cpuid); + + cpu = PCPU_GET(cpuid); + (*ipi_invlcache_counts[cpu])++; +#endif /* COUNT_IPIS */ + + wbinvd(); + atomic_add_int(&smp_tlb_wait, 1); + return (FILTER_HANDLED); +} + +#ifdef __i386__ +static int +xen_lazypmap(void *arg) +{ + + pmap_lazyfix_action(); + return (FILTER_HANDLED); +} +#endif + +static int +xen_cpustop_handler(void *arg) +{ + + cpustop_handler(); + return (FILTER_HANDLED); +} + +static int +xen_cpususpend_handler(void *arg) +{ + + cpususpend_handler(); + return (FILTER_HANDLED); +} + +static int +xen_cpustophard_handler(void *arg) +{ + + ipi_nmi_handler(); + return (FILTER_HANDLED); +} + +/* Xen PV IPI sender */ +static void +xen_ipi_vectored(u_int vector, int dest) +{ + xen_intr_handle_t *ipi_handle; + int ipi_idx, to_cpu, self; + + ipi_idx = IPI_TO_IDX(vector); + if (ipi_idx > nitems(xen_ipis)) + panic("IPI out of range"); + + switch(dest) { + case APIC_IPI_DEST_SELF: + ipi_handle = DPCPU_GET(ipi_handle); + xen_intr_signal(ipi_handle[ipi_idx]); + break; + case APIC_IPI_DEST_ALL: + CPU_FOREACH(to_cpu) { + ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle); + xen_intr_signal(ipi_handle[ipi_idx]); + } + break; + case APIC_IPI_DEST_OTHERS: + self = PCPU_GET(cpuid); + CPU_FOREACH(to_cpu) { + if (to_cpu != self) { + ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle); + xen_intr_signal(ipi_handle[ipi_idx]); + } + } + break; + default: + to_cpu = apic_cpuid(dest); + ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle); + xen_intr_signal(ipi_handle[ipi_idx]); + break; + } +} + +static void +xen_cpu_ipi_init(int cpu) +{ + xen_intr_handle_t *ipi_handle; + const struct xen_ipi_handler *ipi; + device_t dev; + int idx, rc; + + ipi_handle = DPCPU_ID_GET(cpu, ipi_handle); + dev = pcpu_find(cpu)->pc_device; + KASSERT((dev != NULL), ("NULL pcpu device_t")); + + for (ipi = xen_ipis, idx = 0; idx < nitems(xen_ipis); ipi++, idx++) { + + if (ipi->filter == NULL) { + ipi_handle[idx] = NULL; + continue; + } + + rc = xen_intr_alloc_and_bind_ipi(dev, cpu, ipi->filter, + INTR_TYPE_TTY, &ipi_handle[idx]); + if (rc != 0) + panic("Unable to allocate a XEN IPI port"); + xen_intr_describe(ipi_handle[idx], "%s", ipi->description); + } +} + +static void +xen_init_ipis(void) +{ + int i; + + if (!xen_hvm_domain() || !xen_vector_callback_enabled) + return; + +#ifdef __amd64__ + if (pmap_pcid_enabled) { + xen_ipis[IPI_TO_IDX(IPI_INVLTLB)].filter = xen_invltlb_pcid; + xen_ipis[IPI_TO_IDX(IPI_INVLPG)].filter = xen_invlpg_pcid; + } +#endif + CPU_FOREACH(i) + xen_cpu_ipi_init(i); + + /* Set the xen pv ipi ops to replace the native ones */ + cpu_ops.ipi_vectored = xen_ipi_vectored; +} + +/*---------------------- XEN Hypervisor Probe and Setup ----------------------*/ static uint32_t xen_hvm_cpuid_base(void) { @@ -253,4 +683,5 @@ void xen_hvm_init_cpu(void) } SYSINIT(xen_hvm_init, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, xen_hvm_init, NULL); +SYSINIT(xen_init_ipis, SI_SUB_SMP, SI_ORDER_FIRST, xen_init_ipis, NULL); SYSINIT(xen_hvm_init_cpu, SI_SUB_INTR, SI_ORDER_FIRST, xen_hvm_init_cpu, NULL); diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c index 83bf487..54a6be6 100644 --- a/sys/x86/xen/xen_intr.c +++ b/sys/x86/xen/xen_intr.c @@ -1010,7 +1010,7 @@ xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu, } int -xen_intr_bind_ipi(device_t dev, u_int ipi, u_int cpu, +xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu, driver_filter_t filter, enum intr_type flags, xen_intr_handle_t *port_handlep) { diff --git a/sys/xen/xen_intr.h b/sys/xen/xen_intr.h index 109608f..3b339a5 100644 --- a/sys/xen/xen_intr.h +++ b/sys/xen/xen_intr.h @@ -141,21 +141,20 @@ int xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu, void *arg, enum intr_type irqflags, xen_intr_handle_t *handlep); /** - * Associate an interprocessor interrupt vector with an interrupt handler. + * Allocate a local event channel port for servicing interprocessor + * interupts and, if successful, associate the port with the specified + * interrupt handler. * * \param dev The device making this bind request. - * \param ipi The interprocessor interrupt vector number of the - * interrupt source being hooked. * \param cpu The cpu receiving the IPI. - * \param filter An interrupt filter handler. Specify NULL - * to always dispatch to the ithread handler. + * \param filter The interrupt filter servicing this IPI. * \param irqflags Interrupt handler flags. See sys/bus.h. * \param handlep Pointer to an opaque handle used to manage this * registration. * * \returns 0 on success, otherwise an errno. */ -int xen_intr_bind_ipi(device_t dev, u_int ipi, u_int cpu, +int xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu, driver_filter_t filter, enum intr_type irqflags, xen_intr_handle_t *handlep); |