1145522Sdarrenr/* $FreeBSD: stable/10/sys/contrib/ipfilter/netinet/fil.c 352866 2019-09-29 03:41:15Z cy $ */ 2145522Sdarrenr 353642Sguido/* 4255332Scy * Copyright (C) 2012 by Darren Reed. 553642Sguido * 680482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 7255332Scy * 8255332Scy * Copyright 2008 Sun Microsystems. 9255332Scy * 10255332Scy * $Id$ 11255332Scy * 1253642Sguido */ 13145522Sdarrenr#if defined(KERNEL) || defined(_KERNEL) 14145522Sdarrenr# undef KERNEL 15145522Sdarrenr# undef _KERNEL 16145522Sdarrenr# define KERNEL 1 17145522Sdarrenr# define _KERNEL 1 1892685Sdarrenr#endif 1953642Sguido#include <sys/errno.h> 2053642Sguido#include <sys/types.h> 2153642Sguido#include <sys/param.h> 2253642Sguido#include <sys/time.h> 23145522Sdarrenr#if defined(_KERNEL) && defined(__FreeBSD_version) && \ 2457096Sguido (__FreeBSD_version >= 220000) 2563523Sdarrenr# if (__FreeBSD_version >= 400000) 26145522Sdarrenr# if !defined(IPFILTER_LKM) 2763523Sdarrenr# include "opt_inet6.h" 2863523Sdarrenr# endif 2963523Sdarrenr# if (__FreeBSD_version == 400019) 3063523Sdarrenr# define CSUM_DELAY_DATA 3163523Sdarrenr# endif 3263523Sdarrenr# endif 3353642Sguido# include <sys/filio.h> 3453642Sguido#else 3553642Sguido# include <sys/ioctl.h> 3653642Sguido#endif 37170268Sdarrenr#if (defined(__SVR4) || defined(__svr4__)) && defined(sun) 38170268Sdarrenr# include <sys/filio.h> 39170268Sdarrenr#endif 40153876Sguido#if !defined(_AIX51) 41153876Sguido# include <sys/fcntl.h> 42153876Sguido#endif 43145522Sdarrenr#if defined(_KERNEL) 4453642Sguido# include <sys/systm.h> 45145522Sdarrenr# include <sys/file.h> 4653642Sguido#else 4753642Sguido# include <stdio.h> 4853642Sguido# include <string.h> 4953642Sguido# include <stdlib.h> 50145522Sdarrenr# include <stddef.h> 51145522Sdarrenr# include <sys/file.h> 52145522Sdarrenr# define _KERNEL 53145522Sdarrenr# ifdef __OpenBSD__ 54145522Sdarrenrstruct file; 55145522Sdarrenr# endif 56145522Sdarrenr# include <sys/uio.h> 57145522Sdarrenr# undef _KERNEL 5853642Sguido#endif 59145522Sdarrenr#if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux) && \ 60145522Sdarrenr !defined(linux) 61145522Sdarrenr# include <sys/mbuf.h> 62145522Sdarrenr#else 63145522Sdarrenr# if !defined(linux) 64145522Sdarrenr# include <sys/byteorder.h> 6553642Sguido# endif 66145522Sdarrenr# if (SOLARIS2 < 5) && defined(sun) 6753642Sguido# include <sys/dditypes.h> 6853642Sguido# endif 6953642Sguido#endif 70145522Sdarrenr#ifdef __hpux 71145522Sdarrenr# define _NET_ROUTE_INCLUDED 72145522Sdarrenr#endif 73145522Sdarrenr#if !defined(linux) 7453642Sguido# include <sys/protosw.h> 7553642Sguido#endif 76145522Sdarrenr#include <sys/socket.h> 7753642Sguido#include <net/if.h> 7853642Sguido#ifdef sun 7953642Sguido# include <net/af.h> 8053642Sguido#endif 8153642Sguido#include <netinet/in.h> 8253642Sguido#include <netinet/in_systm.h> 8353642Sguido#include <netinet/ip.h> 8453642Sguido#if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ 8553642Sguido# include <sys/hashing.h> 8653642Sguido# include <netinet/in_var.h> 8753642Sguido#endif 8853642Sguido#include <netinet/tcp.h> 89153876Sguido#if (!defined(__sgi) && !defined(AIX)) || defined(_KERNEL) 90145522Sdarrenr# include <netinet/udp.h> 91145522Sdarrenr# include <netinet/ip_icmp.h> 92145522Sdarrenr#endif 93145522Sdarrenr#ifdef __hpux 94145522Sdarrenr# undef _NET_ROUTE_INCLUDED 95145522Sdarrenr#endif 96170268Sdarrenr#ifdef __osf__ 97170268Sdarrenr# undef _RADIX_H_ 98170268Sdarrenr#endif 9953642Sguido#include "netinet/ip_compat.h" 10060850Sdarrenr#ifdef USE_INET6 10160850Sdarrenr# include <netinet/icmp6.h> 102145522Sdarrenr# if !SOLARIS && defined(_KERNEL) && !defined(__osf__) && !defined(__hpux) 10360850Sdarrenr# include <netinet6/in6_var.h> 10460850Sdarrenr# endif 10560850Sdarrenr#endif 10653642Sguido#include "netinet/ip_fil.h" 10753642Sguido#include "netinet/ip_nat.h" 10853642Sguido#include "netinet/ip_frag.h" 10953642Sguido#include "netinet/ip_state.h" 11092685Sdarrenr#include "netinet/ip_proxy.h" 11153642Sguido#include "netinet/ip_auth.h" 112145522Sdarrenr#ifdef IPFILTER_SCAN 113145522Sdarrenr# include "netinet/ip_scan.h" 114145522Sdarrenr#endif 115255332Scy#include "netinet/ip_sync.h" 116255332Scy#include "netinet/ip_lookup.h" 117145522Sdarrenr#include "netinet/ip_pool.h" 118145522Sdarrenr#include "netinet/ip_htable.h" 119145522Sdarrenr#ifdef IPFILTER_COMPILED 120145522Sdarrenr# include "netinet/ip_rules.h" 121145522Sdarrenr#endif 122145522Sdarrenr#if defined(IPFILTER_BPF) && defined(_KERNEL) 123145522Sdarrenr# include <net/bpf.h> 124145522Sdarrenr#endif 125145522Sdarrenr#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) 126145522Sdarrenr# include <sys/malloc.h> 12753642Sguido#endif 12853642Sguido#include "netinet/ipl.h" 129255332Scy 130255332Scy#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) 131255332Scy# include <sys/callout.h> 132255332Scyextern struct callout ipf_slowtimer_ch; 133255332Scy#endif 134255332Scy#if defined(__OpenBSD__) 135255332Scy# include <sys/timeout.h> 136255332Scyextern struct timeout ipf_slowtimer_ch; 137255332Scy#endif 138145522Sdarrenr/* END OF INCLUDES */ 13953642Sguido 14080482Sdarrenr#if !defined(lint) 14180482Sdarrenrstatic const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed"; 14280482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: stable/10/sys/contrib/ipfilter/netinet/fil.c 352866 2019-09-29 03:41:15Z cy $"; 143172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: fil.c,v 2.243.2.125 2007/10/10 09:27:20 darrenr Exp $"; */ 14480482Sdarrenr#endif 14580482Sdarrenr 14653642Sguido#ifndef _KERNEL 14753642Sguido# include "ipf.h" 14853642Sguido# include "ipt.h" 14953642Sguidoextern int opts; 150255332Scyextern int blockreason; 15153642Sguido#endif /* _KERNEL */ 15253642Sguido 153255332Scy#define LBUMP(x) softc->x++ 154255332Scy#define LBUMPD(x, y) do { softc->x.y++; DT(y); } while (0) 15553642Sguido 156255332Scystatic INLINE int ipf_check_ipf __P((fr_info_t *, frentry_t *, int)); 157255332Scystatic u_32_t ipf_checkcipso __P((fr_info_t *, u_char *, int)); 158255332Scystatic u_32_t ipf_checkripso __P((u_char *)); 159255332Scystatic u_32_t ipf_decaps __P((fr_info_t *, u_32_t, int)); 160255332Scy#ifdef IPFILTER_LOG 161255332Scystatic frentry_t *ipf_dolog __P((fr_info_t *, u_32_t *)); 16253642Sguido#endif 163255332Scystatic int ipf_flushlist __P((ipf_main_softc_t *, int *, 164255332Scy frentry_t **)); 165255332Scystatic int ipf_flush_groups __P((ipf_main_softc_t *, frgroup_t **, 166255332Scy int)); 167255332Scystatic ipfunc_t ipf_findfunc __P((ipfunc_t)); 168255332Scystatic void *ipf_findlookup __P((ipf_main_softc_t *, int, 169255332Scy frentry_t *, 170255332Scy i6addr_t *, i6addr_t *)); 171255332Scystatic frentry_t *ipf_firewall __P((fr_info_t *, u_32_t *)); 172255332Scystatic int ipf_fr_matcharray __P((fr_info_t *, int *)); 173255332Scystatic int ipf_frruleiter __P((ipf_main_softc_t *, void *, int, 174255332Scy void *)); 175255332Scystatic void ipf_funcfini __P((ipf_main_softc_t *, frentry_t *)); 176255332Scystatic int ipf_funcinit __P((ipf_main_softc_t *, frentry_t *)); 177255332Scystatic int ipf_geniter __P((ipf_main_softc_t *, ipftoken_t *, 178255332Scy ipfgeniter_t *)); 179255332Scystatic void ipf_getstat __P((ipf_main_softc_t *, 180255332Scy struct friostat *, int)); 181255332Scystatic int ipf_group_flush __P((ipf_main_softc_t *, frgroup_t *)); 182255332Scystatic void ipf_group_free __P((frgroup_t *)); 183255332Scystatic int ipf_grpmapfini __P((struct ipf_main_softc_s *, 184255332Scy frentry_t *)); 185255332Scystatic int ipf_grpmapinit __P((struct ipf_main_softc_s *, 186255332Scy frentry_t *)); 187255332Scystatic frentry_t *ipf_nextrule __P((ipf_main_softc_t *, int, int, 188255332Scy frentry_t *, int)); 189255332Scystatic int ipf_portcheck __P((frpcmp_t *, u_32_t)); 190255332Scystatic INLINE int ipf_pr_ah __P((fr_info_t *)); 191255332Scystatic INLINE void ipf_pr_esp __P((fr_info_t *)); 192255332Scystatic INLINE void ipf_pr_gre __P((fr_info_t *)); 193255332Scystatic INLINE void ipf_pr_udp __P((fr_info_t *)); 194255332Scystatic INLINE void ipf_pr_tcp __P((fr_info_t *)); 195255332Scystatic INLINE void ipf_pr_icmp __P((fr_info_t *)); 196255332Scystatic INLINE void ipf_pr_ipv4hdr __P((fr_info_t *)); 197255332Scystatic INLINE void ipf_pr_short __P((fr_info_t *, int)); 198255332Scystatic INLINE int ipf_pr_tcpcommon __P((fr_info_t *)); 199255332Scystatic INLINE int ipf_pr_udpcommon __P((fr_info_t *)); 200255332Scystatic void ipf_rule_delete __P((ipf_main_softc_t *, frentry_t *f, 201255332Scy int, int)); 202255332Scystatic void ipf_rule_expire_insert __P((ipf_main_softc_t *, 203255332Scy frentry_t *, int)); 204255332Scystatic int ipf_synclist __P((ipf_main_softc_t *, frentry_t *, 205255332Scy void *)); 206255332Scystatic void ipf_token_flush __P((ipf_main_softc_t *)); 207255332Scystatic void ipf_token_unlink __P((ipf_main_softc_t *, 208255332Scy ipftoken_t *)); 209255332Scystatic ipftuneable_t *ipf_tune_findbyname __P((ipftuneable_t *, 210255332Scy const char *)); 211255332Scystatic ipftuneable_t *ipf_tune_findbycookie __P((ipftuneable_t **, void *, 212255332Scy void **)); 213255332Scystatic int ipf_updateipid __P((fr_info_t *)); 214255332Scystatic int ipf_settimeout __P((struct ipf_main_softc_s *, 215255332Scy struct ipftuneable *, 216255332Scy ipftuneval_t *)); 217349931Scy#ifdef USE_INET6 218349931Scystatic u_int ipf_pcksum6 __P((fr_info_t *, ip6_t *, 219349931Scy u_int32_t, u_int32_t)); 220349931Scy#endif 221255332Scy#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && \ 222255332Scy !defined(__FreeBSD__)) || \ 223255332Scy FREEBSD_LT_REV(501000) || NETBSD_LT_REV(105000000) || \ 224255332Scy OPENBSD_LT_REV(200006) 225255332Scystatic int ppsratecheck(struct timeval *, int *, int); 226145522Sdarrenr#endif 22753642Sguido 22853642Sguido 22953642Sguido/* 23053642Sguido * bit values for identifying presence of individual IP options 231145522Sdarrenr * All of these tables should be ordered by increasing key value on the left 232145522Sdarrenr * hand side to allow for binary searching of the array and include a trailer 233145522Sdarrenr * with a 0 for the bitmask for linear searches to easily find the end with. 23453642Sguido */ 235351468Scystatic const struct optlist ipopts[] = { 23653642Sguido { IPOPT_NOP, 0x000001 }, 23753642Sguido { IPOPT_RR, 0x000002 }, 23853642Sguido { IPOPT_ZSU, 0x000004 }, 23953642Sguido { IPOPT_MTUP, 0x000008 }, 24053642Sguido { IPOPT_MTUR, 0x000010 }, 24153642Sguido { IPOPT_ENCODE, 0x000020 }, 24253642Sguido { IPOPT_TS, 0x000040 }, 24353642Sguido { IPOPT_TR, 0x000080 }, 24453642Sguido { IPOPT_SECURITY, 0x000100 }, 24553642Sguido { IPOPT_LSRR, 0x000200 }, 24653642Sguido { IPOPT_E_SEC, 0x000400 }, 24753642Sguido { IPOPT_CIPSO, 0x000800 }, 24853642Sguido { IPOPT_SATID, 0x001000 }, 24953642Sguido { IPOPT_SSRR, 0x002000 }, 25053642Sguido { IPOPT_ADDEXT, 0x004000 }, 25153642Sguido { IPOPT_VISA, 0x008000 }, 25253642Sguido { IPOPT_IMITD, 0x010000 }, 25353642Sguido { IPOPT_EIP, 0x020000 }, 25453642Sguido { IPOPT_FINN, 0x040000 }, 25553642Sguido { 0, 0x000000 } 25653642Sguido}; 25753642Sguido 258145522Sdarrenr#ifdef USE_INET6 259275690Scystatic const struct optlist ip6exthdr[] = { 260145522Sdarrenr { IPPROTO_HOPOPTS, 0x000001 }, 261145522Sdarrenr { IPPROTO_IPV6, 0x000002 }, 262145522Sdarrenr { IPPROTO_ROUTING, 0x000004 }, 263145522Sdarrenr { IPPROTO_FRAGMENT, 0x000008 }, 264145522Sdarrenr { IPPROTO_ESP, 0x000010 }, 265145522Sdarrenr { IPPROTO_AH, 0x000020 }, 266145522Sdarrenr { IPPROTO_NONE, 0x000040 }, 267145522Sdarrenr { IPPROTO_DSTOPTS, 0x000080 }, 268153876Sguido { IPPROTO_MOBILITY, 0x000100 }, 269145522Sdarrenr { 0, 0 } 270145522Sdarrenr}; 271145522Sdarrenr#endif 272145522Sdarrenr 27353642Sguido/* 27453642Sguido * bit values for identifying presence of individual IP security options 27553642Sguido */ 276351468Scystatic const struct optlist secopt[] = { 27753642Sguido { IPSO_CLASS_RES4, 0x01 }, 27853642Sguido { IPSO_CLASS_TOPS, 0x02 }, 27953642Sguido { IPSO_CLASS_SECR, 0x04 }, 28053642Sguido { IPSO_CLASS_RES3, 0x08 }, 28153642Sguido { IPSO_CLASS_CONF, 0x10 }, 28253642Sguido { IPSO_CLASS_UNCL, 0x20 }, 28353642Sguido { IPSO_CLASS_RES2, 0x40 }, 28453642Sguido { IPSO_CLASS_RES1, 0x80 } 28553642Sguido}; 28653642Sguido 287255332Scychar ipfilter_version[] = IPL_VERSION; 28853642Sguido 289255332Scyint ipf_features = 0 290255332Scy#ifdef IPFILTER_LKM 291255332Scy | IPF_FEAT_LKM 292255332Scy#endif 293255332Scy#ifdef IPFILTER_LOG 294255332Scy | IPF_FEAT_LOG 295255332Scy#endif 296255332Scy | IPF_FEAT_LOOKUP 297255332Scy#ifdef IPFILTER_BPF 298255332Scy | IPF_FEAT_BPF 299255332Scy#endif 300255332Scy#ifdef IPFILTER_COMPILED 301255332Scy | IPF_FEAT_COMPILED 302255332Scy#endif 303255332Scy#ifdef IPFILTER_CKSUM 304255332Scy | IPF_FEAT_CKSUM 305255332Scy#endif 306255332Scy | IPF_FEAT_SYNC 307255332Scy#ifdef IPFILTER_SCAN 308255332Scy | IPF_FEAT_SCAN 309255332Scy#endif 310255332Scy#ifdef USE_INET6 311255332Scy | IPF_FEAT_IPV6 312255332Scy#endif 313255332Scy ; 314255332Scy 315255332Scy 31653642Sguido/* 317145522Sdarrenr * Table of functions available for use with call rules. 31853642Sguido */ 319255332Scystatic ipfunc_resolve_t ipf_availfuncs[] = { 320255332Scy { "srcgrpmap", ipf_srcgrpmap, ipf_grpmapinit, ipf_grpmapfini }, 321255332Scy { "dstgrpmap", ipf_dstgrpmap, ipf_grpmapinit, ipf_grpmapfini }, 322255332Scy { "", NULL, NULL, NULL } 323255332Scy}; 324255332Scy 325255332Scystatic ipftuneable_t ipf_main_tuneables[] = { 326255332Scy { { (void *)offsetof(struct ipf_main_softc_s, ipf_flags) }, 327255332Scy "ipf_flags", 0, 0xffffffff, 328255332Scy stsizeof(ipf_main_softc_t, ipf_flags), 329255332Scy 0, NULL, NULL }, 330255332Scy { { (void *)offsetof(struct ipf_main_softc_s, ipf_active) }, 331255332Scy "active", 0, 0, 332255332Scy stsizeof(ipf_main_softc_t, ipf_active), 333255332Scy IPFT_RDONLY, NULL, NULL }, 334255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_control_forwarding) }, 335255332Scy "control_forwarding", 0, 1, 336255332Scy stsizeof(ipf_main_softc_t, ipf_control_forwarding), 337255332Scy 0, NULL, NULL }, 338255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_update_ipid) }, 339255332Scy "update_ipid", 0, 1, 340255332Scy stsizeof(ipf_main_softc_t, ipf_update_ipid), 341255332Scy 0, NULL, NULL }, 342255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_chksrc) }, 343255332Scy "chksrc", 0, 1, 344255332Scy stsizeof(ipf_main_softc_t, ipf_chksrc), 345255332Scy 0, NULL, NULL }, 346255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_minttl) }, 347255332Scy "min_ttl", 0, 1, 348255332Scy stsizeof(ipf_main_softc_t, ipf_minttl), 349255332Scy 0, NULL, NULL }, 350255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_icmpminfragmtu) }, 351255332Scy "icmp_minfragmtu", 0, 1, 352255332Scy stsizeof(ipf_main_softc_t, ipf_icmpminfragmtu), 353255332Scy 0, NULL, NULL }, 354255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_pass) }, 355255332Scy "default_pass", 0, 0xffffffff, 356255332Scy stsizeof(ipf_main_softc_t, ipf_pass), 357255332Scy 0, NULL, NULL }, 358255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcpidletimeout) }, 359255332Scy "tcp_idle_timeout", 1, 0x7fffffff, 360255332Scy stsizeof(ipf_main_softc_t, ipf_tcpidletimeout), 361255332Scy 0, NULL, ipf_settimeout }, 362255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosewait) }, 363255332Scy "tcp_close_wait", 1, 0x7fffffff, 364255332Scy stsizeof(ipf_main_softc_t, ipf_tcpclosewait), 365255332Scy 0, NULL, ipf_settimeout }, 366255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcplastack) }, 367255332Scy "tcp_last_ack", 1, 0x7fffffff, 368255332Scy stsizeof(ipf_main_softc_t, ipf_tcplastack), 369255332Scy 0, NULL, ipf_settimeout }, 370255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimeout) }, 371255332Scy "tcp_timeout", 1, 0x7fffffff, 372255332Scy stsizeof(ipf_main_softc_t, ipf_tcptimeout), 373255332Scy 0, NULL, ipf_settimeout }, 374255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynsent) }, 375255332Scy "tcp_syn_sent", 1, 0x7fffffff, 376255332Scy stsizeof(ipf_main_softc_t, ipf_tcpsynsent), 377255332Scy 0, NULL, ipf_settimeout }, 378255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynrecv) }, 379255332Scy "tcp_syn_received", 1, 0x7fffffff, 380255332Scy stsizeof(ipf_main_softc_t, ipf_tcpsynrecv), 381255332Scy 0, NULL, ipf_settimeout }, 382255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosed) }, 383255332Scy "tcp_closed", 1, 0x7fffffff, 384255332Scy stsizeof(ipf_main_softc_t, ipf_tcpclosed), 385255332Scy 0, NULL, ipf_settimeout }, 386255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcphalfclosed) }, 387255332Scy "tcp_half_closed", 1, 0x7fffffff, 388255332Scy stsizeof(ipf_main_softc_t, ipf_tcphalfclosed), 389255332Scy 0, NULL, ipf_settimeout }, 390255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimewait) }, 391255332Scy "tcp_time_wait", 1, 0x7fffffff, 392255332Scy stsizeof(ipf_main_softc_t, ipf_tcptimewait), 393255332Scy 0, NULL, ipf_settimeout }, 394255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_udptimeout) }, 395255332Scy "udp_timeout", 1, 0x7fffffff, 396255332Scy stsizeof(ipf_main_softc_t, ipf_udptimeout), 397255332Scy 0, NULL, ipf_settimeout }, 398255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_udpacktimeout) }, 399255332Scy "udp_ack_timeout", 1, 0x7fffffff, 400255332Scy stsizeof(ipf_main_softc_t, ipf_udpacktimeout), 401255332Scy 0, NULL, ipf_settimeout }, 402255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_icmptimeout) }, 403255332Scy "icmp_timeout", 1, 0x7fffffff, 404255332Scy stsizeof(ipf_main_softc_t, ipf_icmptimeout), 405255332Scy 0, NULL, ipf_settimeout }, 406255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_icmpacktimeout) }, 407255332Scy "icmp_ack_timeout", 1, 0x7fffffff, 408255332Scy stsizeof(ipf_main_softc_t, ipf_icmpacktimeout), 409255332Scy 0, NULL, ipf_settimeout }, 410255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_iptimeout) }, 411255332Scy "ip_timeout", 1, 0x7fffffff, 412255332Scy stsizeof(ipf_main_softc_t, ipf_iptimeout), 413255332Scy 0, NULL, ipf_settimeout }, 414255332Scy#if defined(INSTANCES) && defined(_KERNEL) 415255332Scy { { (void *)offsetof(ipf_main_softc_t, ipf_get_loopback) }, 416255332Scy "intercept_loopback", 0, 1, 417255332Scy stsizeof(ipf_main_softc_t, ipf_get_loopback), 418255332Scy 0, NULL, ipf_set_loopback }, 419145522Sdarrenr#endif 420255332Scy { { 0 }, 421255332Scy NULL, 0, 0, 422255332Scy 0, 423255332Scy 0, NULL, NULL } 424145522Sdarrenr}; 425145522Sdarrenr 426145522Sdarrenr 427145522Sdarrenr/* 428145522Sdarrenr * The next section of code is a a collection of small routines that set 429145522Sdarrenr * fields in the fr_info_t structure passed based on properties of the 430145522Sdarrenr * current packet. There are different routines for the same protocol 431145522Sdarrenr * for each of IPv4 and IPv6. Adding a new protocol, for which there 432145522Sdarrenr * will "special" inspection for setup, is now more easily done by adding 433255332Scy * a new routine and expanding the ipf_pr_ipinit*() function rather than by 434145522Sdarrenr * adding more code to a growing switch statement. 435145522Sdarrenr */ 436145522Sdarrenr#ifdef USE_INET6 437255332Scystatic INLINE int ipf_pr_ah6 __P((fr_info_t *)); 438255332Scystatic INLINE void ipf_pr_esp6 __P((fr_info_t *)); 439255332Scystatic INLINE void ipf_pr_gre6 __P((fr_info_t *)); 440255332Scystatic INLINE void ipf_pr_udp6 __P((fr_info_t *)); 441255332Scystatic INLINE void ipf_pr_tcp6 __P((fr_info_t *)); 442255332Scystatic INLINE void ipf_pr_icmp6 __P((fr_info_t *)); 443255332Scystatic INLINE void ipf_pr_ipv6hdr __P((fr_info_t *)); 444255332Scystatic INLINE void ipf_pr_short6 __P((fr_info_t *, int)); 445255332Scystatic INLINE int ipf_pr_hopopts6 __P((fr_info_t *)); 446255332Scystatic INLINE int ipf_pr_mobility6 __P((fr_info_t *)); 447255332Scystatic INLINE int ipf_pr_routing6 __P((fr_info_t *)); 448255332Scystatic INLINE int ipf_pr_dstopts6 __P((fr_info_t *)); 449255332Scystatic INLINE int ipf_pr_fragment6 __P((fr_info_t *)); 450255332Scystatic INLINE struct ip6_ext *ipf_pr_ipv6exthdr __P((fr_info_t *, int, int)); 451145522Sdarrenr 452145522Sdarrenr 453145522Sdarrenr/* ------------------------------------------------------------------------ */ 454255332Scy/* Function: ipf_pr_short6 */ 455145522Sdarrenr/* Returns: void */ 456255332Scy/* Parameters: fin(I) - pointer to packet information */ 457255332Scy/* xmin(I) - minimum header size */ 458145522Sdarrenr/* */ 459145522Sdarrenr/* IPv6 Only */ 460145522Sdarrenr/* This is function enforces the 'is a packet too short to be legit' rule */ 461145522Sdarrenr/* for IPv6 and marks the packet with FI_SHORT if so. See function comment */ 462255332Scy/* for ipf_pr_short() for more details. */ 463145522Sdarrenr/* ------------------------------------------------------------------------ */ 464255332Scystatic INLINE void 465255332Scyipf_pr_short6(fin, xmin) 466255332Scy fr_info_t *fin; 467255332Scy int xmin; 46853642Sguido{ 46953642Sguido 470153876Sguido if (fin->fin_dlen < xmin) 471153876Sguido fin->fin_flx |= FI_SHORT; 472145522Sdarrenr} 47360850Sdarrenr 474145522Sdarrenr 475145522Sdarrenr/* ------------------------------------------------------------------------ */ 476255332Scy/* Function: ipf_pr_ipv6hdr */ 477255332Scy/* Returns: void */ 478145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 479145522Sdarrenr/* */ 480145522Sdarrenr/* IPv6 Only */ 481145522Sdarrenr/* Copy values from the IPv6 header into the fr_info_t struct and call the */ 482153876Sguido/* per-protocol analyzer if it exists. In validating the packet, a protocol*/ 483153876Sguido/* analyzer may pullup or free the packet itself so we need to be vigiliant */ 484153876Sguido/* of that possibility arising. */ 485145522Sdarrenr/* ------------------------------------------------------------------------ */ 486255332Scystatic INLINE void 487255332Scyipf_pr_ipv6hdr(fin) 488255332Scy fr_info_t *fin; 489145522Sdarrenr{ 490145522Sdarrenr ip6_t *ip6 = (ip6_t *)fin->fin_ip; 491153876Sguido int p, go = 1, i, hdrcount; 492145522Sdarrenr fr_ip_t *fi = &fin->fin_fi; 493145522Sdarrenr 494145522Sdarrenr fin->fin_off = 0; 495145522Sdarrenr 496145522Sdarrenr fi->fi_tos = 0; 497145522Sdarrenr fi->fi_optmsk = 0; 498145522Sdarrenr fi->fi_secmsk = 0; 499145522Sdarrenr fi->fi_auth = 0; 500145522Sdarrenr 501145522Sdarrenr p = ip6->ip6_nxt; 502255332Scy fin->fin_crc = p; 503145522Sdarrenr fi->fi_ttl = ip6->ip6_hlim; 504145522Sdarrenr fi->fi_src.in6 = ip6->ip6_src; 505255332Scy fin->fin_crc += fi->fi_src.i6[0]; 506255332Scy fin->fin_crc += fi->fi_src.i6[1]; 507255332Scy fin->fin_crc += fi->fi_src.i6[2]; 508255332Scy fin->fin_crc += fi->fi_src.i6[3]; 509145522Sdarrenr fi->fi_dst.in6 = ip6->ip6_dst; 510255332Scy fin->fin_crc += fi->fi_dst.i6[0]; 511255332Scy fin->fin_crc += fi->fi_dst.i6[1]; 512255332Scy fin->fin_crc += fi->fi_dst.i6[2]; 513255332Scy fin->fin_crc += fi->fi_dst.i6[3]; 514255332Scy fin->fin_id = 0; 515255332Scy if (IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) 516255332Scy fin->fin_flx |= FI_MULTICAST|FI_MBCAST; 517145522Sdarrenr 518145522Sdarrenr hdrcount = 0; 519255332Scy while (go && !(fin->fin_flx & FI_SHORT)) { 520145522Sdarrenr switch (p) 521145522Sdarrenr { 522145522Sdarrenr case IPPROTO_UDP : 523255332Scy ipf_pr_udp6(fin); 524145522Sdarrenr go = 0; 525145522Sdarrenr break; 526145522Sdarrenr 527145522Sdarrenr case IPPROTO_TCP : 528255332Scy ipf_pr_tcp6(fin); 529145522Sdarrenr go = 0; 530145522Sdarrenr break; 531145522Sdarrenr 532145522Sdarrenr case IPPROTO_ICMPV6 : 533255332Scy ipf_pr_icmp6(fin); 534145522Sdarrenr go = 0; 535145522Sdarrenr break; 536145522Sdarrenr 537145522Sdarrenr case IPPROTO_GRE : 538255332Scy ipf_pr_gre6(fin); 539145522Sdarrenr go = 0; 540145522Sdarrenr break; 541145522Sdarrenr 542145522Sdarrenr case IPPROTO_HOPOPTS : 543255332Scy p = ipf_pr_hopopts6(fin); 544145522Sdarrenr break; 545145522Sdarrenr 546153876Sguido case IPPROTO_MOBILITY : 547255332Scy p = ipf_pr_mobility6(fin); 548153876Sguido break; 549153876Sguido 550145522Sdarrenr case IPPROTO_DSTOPTS : 551255332Scy p = ipf_pr_dstopts6(fin); 552145522Sdarrenr break; 553145522Sdarrenr 554145522Sdarrenr case IPPROTO_ROUTING : 555255332Scy p = ipf_pr_routing6(fin); 556145522Sdarrenr break; 557145522Sdarrenr 558153876Sguido case IPPROTO_AH : 559255332Scy p = ipf_pr_ah6(fin); 560153876Sguido break; 561153876Sguido 562145522Sdarrenr case IPPROTO_ESP : 563255332Scy ipf_pr_esp6(fin); 564153876Sguido go = 0; 565153876Sguido break; 566153876Sguido 567145522Sdarrenr case IPPROTO_IPV6 : 568145522Sdarrenr for (i = 0; ip6exthdr[i].ol_bit != 0; i++) 569145522Sdarrenr if (ip6exthdr[i].ol_val == p) { 570145522Sdarrenr fin->fin_flx |= ip6exthdr[i].ol_bit; 571145522Sdarrenr break; 572145522Sdarrenr } 573145522Sdarrenr go = 0; 574145522Sdarrenr break; 575145522Sdarrenr 576145522Sdarrenr case IPPROTO_NONE : 577145522Sdarrenr go = 0; 578145522Sdarrenr break; 579145522Sdarrenr 580145522Sdarrenr case IPPROTO_FRAGMENT : 581255332Scy p = ipf_pr_fragment6(fin); 582255332Scy /* 583255332Scy * Given that the only fragments we want to let through 584255332Scy * (where fin_off != 0) are those where the non-first 585255332Scy * fragments only have data, we can safely stop looking 586255332Scy * at headers if this is a non-leading fragment. 587255332Scy */ 588172776Sdarrenr if (fin->fin_off != 0) 589172776Sdarrenr go = 0; 590145522Sdarrenr break; 591145522Sdarrenr 592145522Sdarrenr default : 593145522Sdarrenr go = 0; 594145522Sdarrenr break; 595145522Sdarrenr } 596145522Sdarrenr hdrcount++; 597145522Sdarrenr 598145522Sdarrenr /* 599145522Sdarrenr * It is important to note that at this point, for the 600145522Sdarrenr * extension headers (go != 0), the entire header may not have 601145522Sdarrenr * been pulled up when the code gets to this point. This is 602145522Sdarrenr * only done for "go != 0" because the other header handlers 603153876Sguido * will all pullup their complete header. The other indicator 604153876Sguido * of an incomplete packet is that this was just an extension 605153876Sguido * header. 606145522Sdarrenr */ 607145522Sdarrenr if ((go != 0) && (p != IPPROTO_NONE) && 608255332Scy (ipf_pr_pullup(fin, 0) == -1)) { 609145522Sdarrenr p = IPPROTO_NONE; 610255332Scy break; 611145522Sdarrenr } 61260850Sdarrenr } 613153876Sguido 614153876Sguido /* 615255332Scy * Some of the above functions, like ipf_pr_esp6(), can call ipf_pullup 616153876Sguido * and destroy whatever packet was here. The caller of this function 617255332Scy * expects us to return if there is a problem with ipf_pullup. 618153876Sguido */ 619255332Scy if (fin->fin_m == NULL) { 620255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 621153876Sguido 622255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_bad); 623255332Scy return; 624255332Scy } 625255332Scy 626255332Scy fi->fi_p = p; 627255332Scy 628255332Scy /* 629255332Scy * IPv6 fragment case 1 - see comment for ipf_pr_fragment6(). 630255332Scy * "go != 0" imples the above loop hasn't arrived at a layer 4 header. 631255332Scy */ 632255332Scy if ((go != 0) && (fin->fin_flx & FI_FRAG) && (fin->fin_off == 0)) { 633255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 634255332Scy 635255332Scy fin->fin_flx |= FI_BAD; 636255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_badfrag); 637255332Scy LBUMP(ipf_stats[fin->fin_out].fr_v6_bad); 638255332Scy } 639145522Sdarrenr} 64060850Sdarrenr 64153642Sguido 642145522Sdarrenr/* ------------------------------------------------------------------------ */ 643255332Scy/* Function: ipf_pr_ipv6exthdr */ 644255332Scy/* Returns: struct ip6_ext * - pointer to the start of the next header */ 645255332Scy/* or NULL if there is a prolblem. */ 646153876Sguido/* Parameters: fin(I) - pointer to packet information */ 647153876Sguido/* multiple(I) - flag indicating yes/no if multiple occurances */ 648153876Sguido/* of this extension header are allowed. */ 649153876Sguido/* proto(I) - protocol number for this extension header */ 650145522Sdarrenr/* */ 651145522Sdarrenr/* IPv6 Only */ 652255332Scy/* This function embodies a number of common checks that all IPv6 extension */ 653255332Scy/* headers must be subjected to. For example, making sure the packet is */ 654255332Scy/* big enough for it to be in, checking if it is repeated and setting a */ 655255332Scy/* flag to indicate its presence. */ 656145522Sdarrenr/* ------------------------------------------------------------------------ */ 657255332Scystatic INLINE struct ip6_ext * 658255332Scyipf_pr_ipv6exthdr(fin, multiple, proto) 659255332Scy fr_info_t *fin; 660255332Scy int multiple, proto; 661145522Sdarrenr{ 662255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 663145522Sdarrenr struct ip6_ext *hdr; 664145522Sdarrenr u_short shift; 665145522Sdarrenr int i; 666145522Sdarrenr 667145522Sdarrenr fin->fin_flx |= FI_V6EXTHDR; 668145522Sdarrenr 669145522Sdarrenr /* 8 is default length of extension hdr */ 670145522Sdarrenr if ((fin->fin_dlen - 8) < 0) { 671145522Sdarrenr fin->fin_flx |= FI_SHORT; 672255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_short); 673255332Scy return NULL; 674145522Sdarrenr } 675145522Sdarrenr 676255332Scy if (ipf_pr_pullup(fin, 8) == -1) { 677255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_pullup); 678255332Scy return NULL; 679255332Scy } 680145522Sdarrenr 681145522Sdarrenr hdr = fin->fin_dp; 682170268Sdarrenr switch (proto) 683170268Sdarrenr { 684170268Sdarrenr case IPPROTO_FRAGMENT : 685170268Sdarrenr shift = 8; 686170268Sdarrenr break; 687170268Sdarrenr default : 688170268Sdarrenr shift = 8 + (hdr->ip6e_len << 3); 689170268Sdarrenr break; 690170268Sdarrenr } 691170268Sdarrenr 692145522Sdarrenr if (shift > fin->fin_dlen) { /* Nasty extension header length? */ 693145522Sdarrenr fin->fin_flx |= FI_BAD; 694255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_hlen); 695255332Scy return NULL; 696145522Sdarrenr } 697145522Sdarrenr 698255332Scy fin->fin_dp = (char *)fin->fin_dp + shift; 699255332Scy fin->fin_dlen -= shift; 700255332Scy 701255332Scy /* 702255332Scy * If we have seen a fragment header, do not set any flags to indicate 703255332Scy * the presence of this extension header as it has no impact on the 704255332Scy * end result until after it has been defragmented. 705255332Scy */ 706255332Scy if (fin->fin_flx & FI_FRAG) 707255332Scy return hdr; 708255332Scy 709145522Sdarrenr for (i = 0; ip6exthdr[i].ol_bit != 0; i++) 710153876Sguido if (ip6exthdr[i].ol_val == proto) { 711153876Sguido /* 712153876Sguido * Most IPv6 extension headers are only allowed once. 713153876Sguido */ 714153876Sguido if ((multiple == 0) && 715153876Sguido ((fin->fin_optmsk & ip6exthdr[i].ol_bit) != 0)) 716153876Sguido fin->fin_flx |= FI_BAD; 717153876Sguido else 718153876Sguido fin->fin_optmsk |= ip6exthdr[i].ol_bit; 719145522Sdarrenr break; 720145522Sdarrenr } 721145522Sdarrenr 722255332Scy return hdr; 723145522Sdarrenr} 724145522Sdarrenr 725145522Sdarrenr 726145522Sdarrenr/* ------------------------------------------------------------------------ */ 727255332Scy/* Function: ipf_pr_hopopts6 */ 728153876Sguido/* Returns: int - value of the next header or IPPROTO_NONE if error */ 729153876Sguido/* Parameters: fin(I) - pointer to packet information */ 730153876Sguido/* */ 731153876Sguido/* IPv6 Only */ 732153876Sguido/* This is function checks pending hop by hop options extension header */ 733153876Sguido/* ------------------------------------------------------------------------ */ 734255332Scystatic INLINE int 735255332Scyipf_pr_hopopts6(fin) 736255332Scy fr_info_t *fin; 737153876Sguido{ 738255332Scy struct ip6_ext *hdr; 739255332Scy 740255332Scy hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); 741255332Scy if (hdr == NULL) 742255332Scy return IPPROTO_NONE; 743255332Scy return hdr->ip6e_nxt; 744153876Sguido} 745153876Sguido 746153876Sguido 747153876Sguido/* ------------------------------------------------------------------------ */ 748255332Scy/* Function: ipf_pr_mobility6 */ 749153876Sguido/* Returns: int - value of the next header or IPPROTO_NONE if error */ 750153876Sguido/* Parameters: fin(I) - pointer to packet information */ 751153876Sguido/* */ 752153876Sguido/* IPv6 Only */ 753153876Sguido/* This is function checks the IPv6 mobility extension header */ 754153876Sguido/* ------------------------------------------------------------------------ */ 755255332Scystatic INLINE int 756255332Scyipf_pr_mobility6(fin) 757255332Scy fr_info_t *fin; 758153876Sguido{ 759255332Scy struct ip6_ext *hdr; 760255332Scy 761255332Scy hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); 762255332Scy if (hdr == NULL) 763255332Scy return IPPROTO_NONE; 764255332Scy return hdr->ip6e_nxt; 765153876Sguido} 766153876Sguido 767153876Sguido 768153876Sguido/* ------------------------------------------------------------------------ */ 769255332Scy/* Function: ipf_pr_routing6 */ 770145522Sdarrenr/* Returns: int - value of the next header or IPPROTO_NONE if error */ 771145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 772145522Sdarrenr/* */ 773145522Sdarrenr/* IPv6 Only */ 774145522Sdarrenr/* This is function checks pending routing extension header */ 775145522Sdarrenr/* ------------------------------------------------------------------------ */ 776255332Scystatic INLINE int 777255332Scyipf_pr_routing6(fin) 778255332Scy fr_info_t *fin; 779145522Sdarrenr{ 780255332Scy struct ip6_routing *hdr; 781145522Sdarrenr 782255332Scy hdr = (struct ip6_routing *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_ROUTING); 783255332Scy if (hdr == NULL) 784145522Sdarrenr return IPPROTO_NONE; 785145522Sdarrenr 786255332Scy switch (hdr->ip6r_type) 787255332Scy { 788255332Scy case 0 : 789170268Sdarrenr /* 790255332Scy * Nasty extension header length? 791170268Sdarrenr */ 792255332Scy if (((hdr->ip6r_len >> 1) < hdr->ip6r_segleft) || 793255332Scy (hdr->ip6r_segleft && (hdr->ip6r_len & 1))) { 794255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 795255332Scy 796255332Scy fin->fin_flx |= FI_BAD; 797255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_rh_bad); 798255332Scy return IPPROTO_NONE; 799255332Scy } 800255332Scy break; 801255332Scy 802255332Scy default : 803255332Scy break; 804145522Sdarrenr } 80572006Sdarrenr 806255332Scy return hdr->ip6r_nxt; 807145522Sdarrenr} 808145522Sdarrenr 809145522Sdarrenr 810145522Sdarrenr/* ------------------------------------------------------------------------ */ 811255332Scy/* Function: ipf_pr_fragment6 */ 812172776Sdarrenr/* Returns: int - value of the next header or IPPROTO_NONE if error */ 813145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 814145522Sdarrenr/* */ 815145522Sdarrenr/* IPv6 Only */ 816145522Sdarrenr/* Examine the IPv6 fragment header and extract fragment offset information.*/ 817153876Sguido/* */ 818255332Scy/* Fragments in IPv6 are extraordinarily difficult to deal with - much more */ 819255332Scy/* so than in IPv4. There are 5 cases of fragments with IPv6 that all */ 820255332Scy/* packets with a fragment header can fit into. They are as follows: */ 821255332Scy/* */ 822255332Scy/* 1. [IPv6][0-n EH][FH][0-n EH] (no L4HDR present) */ 823255332Scy/* 2. [IPV6][0-n EH][FH][0-n EH][L4HDR part] (short) */ 824255332Scy/* 3. [IPV6][0-n EH][FH][L4HDR part][0-n data] (short) */ 825255332Scy/* 4. [IPV6][0-n EH][FH][0-n EH][L4HDR][0-n data] */ 826255332Scy/* 5. [IPV6][0-n EH][FH][data] */ 827255332Scy/* */ 828255332Scy/* IPV6 = IPv6 header, FH = Fragment Header, */ 829255332Scy/* 0-n EH = 0 or more extension headers, 0-n data = 0 or more bytes of data */ 830255332Scy/* */ 831255332Scy/* Packets that match 1, 2, 3 will be dropped as the only reasonable */ 832255332Scy/* scenario in which they happen is in extreme circumstances that are most */ 833255332Scy/* likely to be an indication of an attack rather than normal traffic. */ 834255332Scy/* A type 3 packet may be sent by an attacked after a type 4 packet. There */ 835255332Scy/* are two rules that can be used to guard against type 3 packets: L4 */ 836255332Scy/* headers must always be in a packet that has the offset field set to 0 */ 837255332Scy/* and no packet is allowed to overlay that where offset = 0. */ 838145522Sdarrenr/* ------------------------------------------------------------------------ */ 839255332Scystatic INLINE int 840255332Scyipf_pr_fragment6(fin) 841255332Scy fr_info_t *fin; 842145522Sdarrenr{ 843255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 844145522Sdarrenr struct ip6_frag *frag; 845145522Sdarrenr 846153876Sguido fin->fin_flx |= FI_FRAG; 847145522Sdarrenr 848255332Scy frag = (struct ip6_frag *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT); 849255332Scy if (frag == NULL) { 850255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_frag_bad); 851172776Sdarrenr return IPPROTO_NONE; 852255332Scy } 853145522Sdarrenr 854255332Scy if ((frag->ip6f_offlg & IP6F_MORE_FRAG) != 0) { 855255332Scy /* 856255332Scy * Any fragment that isn't the last fragment must have its 857255332Scy * length as a multiple of 8. 858255332Scy */ 859255332Scy if ((fin->fin_plen & 7) != 0) 860255332Scy fin->fin_flx |= FI_BAD; 861145522Sdarrenr } 862145522Sdarrenr 863255332Scy fin->fin_fraghdr = frag; 864255332Scy fin->fin_id = frag->ip6f_ident; 865172776Sdarrenr fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK); 866145522Sdarrenr if (fin->fin_off != 0) 867145522Sdarrenr fin->fin_flx |= FI_FRAGBODY; 868145522Sdarrenr 869255332Scy /* 870255332Scy * Jumbograms aren't handled, so the max. length is 64k 871255332Scy */ 872255332Scy if ((fin->fin_off << 3) + fin->fin_dlen > 65535) 873255332Scy fin->fin_flx |= FI_BAD; 874172776Sdarrenr 875255332Scy /* 876255332Scy * We don't know where the transport layer header (or whatever is next 877255332Scy * is), as it could be behind destination options (amongst others) so 878255332Scy * return the fragment header as the type of packet this is. Note that 879255332Scy * this effectively disables the fragment cache for > 1 protocol at a 880255332Scy * time. 881255332Scy */ 882172776Sdarrenr return frag->ip6f_nxt; 883145522Sdarrenr} 884145522Sdarrenr 885145522Sdarrenr 886145522Sdarrenr/* ------------------------------------------------------------------------ */ 887255332Scy/* Function: ipf_pr_dstopts6 */ 888145522Sdarrenr/* Returns: int - value of the next header or IPPROTO_NONE if error */ 889145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 890145522Sdarrenr/* */ 891145522Sdarrenr/* IPv6 Only */ 892145522Sdarrenr/* This is function checks pending destination options extension header */ 893145522Sdarrenr/* ------------------------------------------------------------------------ */ 894255332Scystatic INLINE int 895255332Scyipf_pr_dstopts6(fin) 896255332Scy fr_info_t *fin; 897145522Sdarrenr{ 898255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 899255332Scy struct ip6_ext *hdr; 900255332Scy 901255332Scy hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_DSTOPTS); 902255332Scy if (hdr == NULL) { 903255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_dst_bad); 904255332Scy return IPPROTO_NONE; 905255332Scy } 906255332Scy return hdr->ip6e_nxt; 907145522Sdarrenr} 908145522Sdarrenr 909145522Sdarrenr 910145522Sdarrenr/* ------------------------------------------------------------------------ */ 911255332Scy/* Function: ipf_pr_icmp6 */ 912145522Sdarrenr/* Returns: void */ 913145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 914145522Sdarrenr/* */ 915145522Sdarrenr/* IPv6 Only */ 916145522Sdarrenr/* This routine is mainly concerned with determining the minimum valid size */ 917145522Sdarrenr/* for an ICMPv6 packet. */ 918145522Sdarrenr/* ------------------------------------------------------------------------ */ 919255332Scystatic INLINE void 920255332Scyipf_pr_icmp6(fin) 921255332Scy fr_info_t *fin; 922145522Sdarrenr{ 923145522Sdarrenr int minicmpsz = sizeof(struct icmp6_hdr); 924145522Sdarrenr struct icmp6_hdr *icmp6; 925145522Sdarrenr 926255332Scy if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) { 927255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 928255332Scy 929255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_icmp6_pullup); 930145522Sdarrenr return; 931255332Scy } 932145522Sdarrenr 933145522Sdarrenr if (fin->fin_dlen > 1) { 934170268Sdarrenr ip6_t *ip6; 935170268Sdarrenr 936145522Sdarrenr icmp6 = fin->fin_dp; 937145522Sdarrenr 938145522Sdarrenr fin->fin_data[0] = *(u_short *)icmp6; 939145522Sdarrenr 940255332Scy if ((icmp6->icmp6_type & ICMP6_INFOMSG_MASK) != 0) 941255332Scy fin->fin_flx |= FI_ICMPQUERY; 942255332Scy 943145522Sdarrenr switch (icmp6->icmp6_type) 944145522Sdarrenr { 945145522Sdarrenr case ICMP6_ECHO_REPLY : 946145522Sdarrenr case ICMP6_ECHO_REQUEST : 947255332Scy if (fin->fin_dlen >= 6) 948255332Scy fin->fin_data[1] = icmp6->icmp6_id; 949145522Sdarrenr minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t); 950145522Sdarrenr break; 951255332Scy 952145522Sdarrenr case ICMP6_DST_UNREACH : 953145522Sdarrenr case ICMP6_PACKET_TOO_BIG : 954145522Sdarrenr case ICMP6_TIME_EXCEEDED : 955145522Sdarrenr case ICMP6_PARAM_PROB : 956170268Sdarrenr fin->fin_flx |= FI_ICMPERR; 957172776Sdarrenr minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t); 958172776Sdarrenr if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) 959172776Sdarrenr break; 960172776Sdarrenr 961172776Sdarrenr if (M_LEN(fin->fin_m) < fin->fin_plen) { 962255332Scy if (ipf_coalesce(fin) != 1) 963145522Sdarrenr return; 96472006Sdarrenr } 965170268Sdarrenr 966255332Scy if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN) == -1) 967255332Scy return; 968255332Scy 969170268Sdarrenr /* 970170268Sdarrenr * If the destination of this packet doesn't match the 971170268Sdarrenr * source of the original packet then this packet is 972170268Sdarrenr * not correct. 973170268Sdarrenr */ 974170459Sdarrenr icmp6 = fin->fin_dp; 975170268Sdarrenr ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN); 976170268Sdarrenr if (IP6_NEQ(&fin->fin_fi.fi_dst, 977170268Sdarrenr (i6addr_t *)&ip6->ip6_src)) 978170268Sdarrenr fin->fin_flx |= FI_BAD; 979145522Sdarrenr break; 980145522Sdarrenr default : 981145522Sdarrenr break; 98272006Sdarrenr } 983145522Sdarrenr } 98472006Sdarrenr 985255332Scy ipf_pr_short6(fin, minicmpsz); 986255332Scy if ((fin->fin_flx & (FI_SHORT|FI_BAD)) == 0) { 987255332Scy u_char p = fin->fin_p; 988255332Scy 989255332Scy fin->fin_p = IPPROTO_ICMPV6; 990255332Scy ipf_checkv6sum(fin); 991255332Scy fin->fin_p = p; 992255332Scy } 993145522Sdarrenr} 99472006Sdarrenr 995145522Sdarrenr 996145522Sdarrenr/* ------------------------------------------------------------------------ */ 997255332Scy/* Function: ipf_pr_udp6 */ 998145522Sdarrenr/* Returns: void */ 999145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1000145522Sdarrenr/* */ 1001145522Sdarrenr/* IPv6 Only */ 1002145522Sdarrenr/* Analyse the packet for IPv6/UDP properties. */ 1003153876Sguido/* Is not expected to be called for fragmented packets. */ 1004145522Sdarrenr/* ------------------------------------------------------------------------ */ 1005255332Scystatic INLINE void 1006255332Scyipf_pr_udp6(fin) 1007255332Scy fr_info_t *fin; 1008145522Sdarrenr{ 1009145522Sdarrenr 1010255332Scy if (ipf_pr_udpcommon(fin) == 0) { 1011170268Sdarrenr u_char p = fin->fin_p; 1012170268Sdarrenr 1013170268Sdarrenr fin->fin_p = IPPROTO_UDP; 1014255332Scy ipf_checkv6sum(fin); 1015170268Sdarrenr fin->fin_p = p; 1016170268Sdarrenr } 1017145522Sdarrenr} 1018145522Sdarrenr 1019145522Sdarrenr 1020145522Sdarrenr/* ------------------------------------------------------------------------ */ 1021255332Scy/* Function: ipf_pr_tcp6 */ 1022145522Sdarrenr/* Returns: void */ 1023145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1024145522Sdarrenr/* */ 1025145522Sdarrenr/* IPv6 Only */ 1026145522Sdarrenr/* Analyse the packet for IPv6/TCP properties. */ 1027153876Sguido/* Is not expected to be called for fragmented packets. */ 1028145522Sdarrenr/* ------------------------------------------------------------------------ */ 1029255332Scystatic INLINE void 1030255332Scyipf_pr_tcp6(fin) 1031255332Scy fr_info_t *fin; 1032145522Sdarrenr{ 1033145522Sdarrenr 1034255332Scy if (ipf_pr_tcpcommon(fin) == 0) { 1035170268Sdarrenr u_char p = fin->fin_p; 1036170268Sdarrenr 1037170268Sdarrenr fin->fin_p = IPPROTO_TCP; 1038255332Scy ipf_checkv6sum(fin); 1039170268Sdarrenr fin->fin_p = p; 1040170268Sdarrenr } 1041153876Sguido} 1042145522Sdarrenr 1043153876Sguido 1044153876Sguido/* ------------------------------------------------------------------------ */ 1045255332Scy/* Function: ipf_pr_esp6 */ 1046153876Sguido/* Returns: void */ 1047153876Sguido/* Parameters: fin(I) - pointer to packet information */ 1048153876Sguido/* */ 1049153876Sguido/* IPv6 Only */ 1050153876Sguido/* Analyse the packet for ESP properties. */ 1051153876Sguido/* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ 1052153876Sguido/* even though the newer ESP packets must also have a sequence number that */ 1053153876Sguido/* is 32bits as well, it is not possible(?) to determine the version from a */ 1054153876Sguido/* simple packet header. */ 1055153876Sguido/* ------------------------------------------------------------------------ */ 1056255332Scystatic INLINE void 1057255332Scyipf_pr_esp6(fin) 1058255332Scy fr_info_t *fin; 1059153876Sguido{ 1060153876Sguido 1061255332Scy if ((fin->fin_off == 0) && (ipf_pr_pullup(fin, 8) == -1)) { 1062255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1063153876Sguido 1064255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_esp_pullup); 1065255332Scy return; 1066255332Scy } 1067145522Sdarrenr} 1068153876Sguido 1069153876Sguido 1070153876Sguido/* ------------------------------------------------------------------------ */ 1071255332Scy/* Function: ipf_pr_ah6 */ 1072255332Scy/* Returns: int - value of the next header or IPPROTO_NONE if error */ 1073153876Sguido/* Parameters: fin(I) - pointer to packet information */ 1074153876Sguido/* */ 1075153876Sguido/* IPv6 Only */ 1076153876Sguido/* Analyse the packet for AH properties. */ 1077153876Sguido/* The minimum length is taken to be the combination of all fields in the */ 1078153876Sguido/* header being present and no authentication data (null algorithm used.) */ 1079153876Sguido/* ------------------------------------------------------------------------ */ 1080255332Scystatic INLINE int 1081255332Scyipf_pr_ah6(fin) 1082255332Scy fr_info_t *fin; 1083153876Sguido{ 1084153876Sguido authhdr_t *ah; 1085153876Sguido 1086255332Scy fin->fin_flx |= FI_AH; 1087153876Sguido 1088255332Scy ah = (authhdr_t *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); 1089255332Scy if (ah == NULL) { 1090255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1091255332Scy 1092255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_ah_bad); 1093153876Sguido return IPPROTO_NONE; 1094255332Scy } 1095153876Sguido 1096255332Scy ipf_pr_short6(fin, sizeof(*ah)); 1097255332Scy 1098255332Scy /* 1099255332Scy * No need for another pullup, ipf_pr_ipv6exthdr() will pullup 1100255332Scy * enough data to satisfy ah_next (the very first one.) 1101255332Scy */ 1102153876Sguido return ah->ah_next; 1103153876Sguido} 1104153876Sguido 1105153876Sguido 1106153876Sguido/* ------------------------------------------------------------------------ */ 1107255332Scy/* Function: ipf_pr_gre6 */ 1108153876Sguido/* Returns: void */ 1109153876Sguido/* Parameters: fin(I) - pointer to packet information */ 1110153876Sguido/* */ 1111153876Sguido/* Analyse the packet for GRE properties. */ 1112153876Sguido/* ------------------------------------------------------------------------ */ 1113255332Scystatic INLINE void 1114255332Scyipf_pr_gre6(fin) 1115255332Scy fr_info_t *fin; 1116153876Sguido{ 1117153876Sguido grehdr_t *gre; 1118153876Sguido 1119255332Scy if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { 1120255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1121153876Sguido 1122255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v6_gre_pullup); 1123153876Sguido return; 1124255332Scy } 1125153876Sguido 1126153876Sguido gre = fin->fin_dp; 1127153876Sguido if (GRE_REV(gre->gr_flags) == 1) 1128153876Sguido fin->fin_data[0] = gre->gr_call; 1129153876Sguido} 1130145522Sdarrenr#endif /* USE_INET6 */ 1131145522Sdarrenr 1132145522Sdarrenr 1133145522Sdarrenr/* ------------------------------------------------------------------------ */ 1134255332Scy/* Function: ipf_pr_pullup */ 1135145522Sdarrenr/* Returns: int - 0 == pullup succeeded, -1 == failure */ 1136145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1137145522Sdarrenr/* plen(I) - length (excluding L3 header) to pullup */ 1138145522Sdarrenr/* */ 1139145522Sdarrenr/* Short inline function to cut down on code duplication to perform a call */ 1140255332Scy/* to ipf_pullup to ensure there is the required amount of data, */ 1141145522Sdarrenr/* consecutively in the packet buffer. */ 1142172776Sdarrenr/* */ 1143172776Sdarrenr/* This function pulls up 'extra' data at the location of fin_dp. fin_dp */ 1144172776Sdarrenr/* points to the first byte after the complete layer 3 header, which will */ 1145172776Sdarrenr/* include all of the known extension headers for IPv6 or options for IPv4. */ 1146172776Sdarrenr/* */ 1147172776Sdarrenr/* Since fr_pullup() expects the total length of bytes to be pulled up, it */ 1148172776Sdarrenr/* is necessary to add those we can already assume to be pulled up (fin_dp */ 1149172776Sdarrenr/* - fin_ip) to what is passed through. */ 1150145522Sdarrenr/* ------------------------------------------------------------------------ */ 1151255332Scyint 1152255332Scyipf_pr_pullup(fin, plen) 1153255332Scy fr_info_t *fin; 1154255332Scy int plen; 1155145522Sdarrenr{ 1156255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1157255332Scy 1158145522Sdarrenr if (fin->fin_m != NULL) { 1159145522Sdarrenr if (fin->fin_dp != NULL) 1160145522Sdarrenr plen += (char *)fin->fin_dp - 1161145522Sdarrenr ((char *)fin->fin_ip + fin->fin_hlen); 1162145522Sdarrenr plen += fin->fin_hlen; 1163255332Scy if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) { 1164170268Sdarrenr#if defined(_KERNEL) 1165255332Scy if (ipf_pullup(fin->fin_m, fin, plen) == NULL) { 1166255332Scy DT(ipf_pullup_fail); 1167255332Scy LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); 1168145522Sdarrenr return -1; 1169255332Scy } 1170255332Scy LBUMP(ipf_stats[fin->fin_out].fr_pull[0]); 1171170268Sdarrenr#else 1172255332Scy LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); 1173170268Sdarrenr /* 1174255332Scy * Fake ipf_pullup failing 1175170268Sdarrenr */ 1176255332Scy fin->fin_reason = FRB_PULLUP; 1177170268Sdarrenr *fin->fin_mp = NULL; 1178170268Sdarrenr fin->fin_m = NULL; 1179170268Sdarrenr fin->fin_ip = NULL; 1180170268Sdarrenr return -1; 1181170268Sdarrenr#endif 1182145522Sdarrenr } 118372006Sdarrenr } 1184145522Sdarrenr return 0; 1185145522Sdarrenr} 1186130886Sdarrenr 118753642Sguido 1188145522Sdarrenr/* ------------------------------------------------------------------------ */ 1189255332Scy/* Function: ipf_pr_short */ 1190145522Sdarrenr/* Returns: void */ 1191153876Sguido/* Parameters: fin(I) - pointer to packet information */ 1192153876Sguido/* xmin(I) - minimum header size */ 1193145522Sdarrenr/* */ 1194153876Sguido/* Check if a packet is "short" as defined by xmin. The rule we are */ 1195145522Sdarrenr/* applying here is that the packet must not be fragmented within the layer */ 1196145522Sdarrenr/* 4 header. That is, it must not be a fragment that has its offset set to */ 1197145522Sdarrenr/* start within the layer 4 header (hdrmin) or if it is at offset 0, the */ 1198145522Sdarrenr/* entire layer 4 header must be present (min). */ 1199145522Sdarrenr/* ------------------------------------------------------------------------ */ 1200255332Scystatic INLINE void 1201255332Scyipf_pr_short(fin, xmin) 1202255332Scy fr_info_t *fin; 1203255332Scy int xmin; 1204145522Sdarrenr{ 120567614Sdarrenr 1206153876Sguido if (fin->fin_off == 0) { 1207153876Sguido if (fin->fin_dlen < xmin) 1208153876Sguido fin->fin_flx |= FI_SHORT; 1209153876Sguido } else if (fin->fin_off < xmin) { 1210153876Sguido fin->fin_flx |= FI_SHORT; 1211145522Sdarrenr } 1212145522Sdarrenr} 121367614Sdarrenr 1214145522Sdarrenr 1215145522Sdarrenr/* ------------------------------------------------------------------------ */ 1216255332Scy/* Function: ipf_pr_icmp */ 1217145522Sdarrenr/* Returns: void */ 1218145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1219145522Sdarrenr/* */ 1220145522Sdarrenr/* IPv4 Only */ 1221145522Sdarrenr/* Do a sanity check on the packet for ICMP (v4). In nearly all cases, */ 1222145522Sdarrenr/* except extrememly bad packets, both type and code will be present. */ 1223153876Sguido/* The expected minimum size of an ICMP packet is very much dependent on */ 1224145522Sdarrenr/* the type of it. */ 1225145522Sdarrenr/* */ 1226145522Sdarrenr/* XXX - other ICMP sanity checks? */ 1227145522Sdarrenr/* ------------------------------------------------------------------------ */ 1228255332Scystatic INLINE void 1229255332Scyipf_pr_icmp(fin) 1230255332Scy fr_info_t *fin; 1231145522Sdarrenr{ 1232255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1233145522Sdarrenr int minicmpsz = sizeof(struct icmp); 1234145522Sdarrenr icmphdr_t *icmp; 1235153876Sguido ip_t *oip; 1236145522Sdarrenr 1237255332Scy ipf_pr_short(fin, ICMPERR_ICMPHLEN); 1238255332Scy 1239153876Sguido if (fin->fin_off != 0) { 1240255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_frag); 1241153876Sguido return; 1242153876Sguido } 1243153876Sguido 1244255332Scy if (ipf_pr_pullup(fin, ICMPERR_ICMPHLEN) == -1) { 1245255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_pullup); 1246145522Sdarrenr return; 1247255332Scy } 1248145522Sdarrenr 1249255332Scy icmp = fin->fin_dp; 1250145522Sdarrenr 1251255332Scy fin->fin_data[0] = *(u_short *)icmp; 1252255332Scy fin->fin_data[1] = icmp->icmp_id; 1253145522Sdarrenr 1254255332Scy switch (icmp->icmp_type) 1255255332Scy { 1256255332Scy case ICMP_ECHOREPLY : 1257255332Scy case ICMP_ECHO : 1258255332Scy /* Router discovery messaes - RFC 1256 */ 1259255332Scy case ICMP_ROUTERADVERT : 1260255332Scy case ICMP_ROUTERSOLICIT : 1261255332Scy fin->fin_flx |= FI_ICMPQUERY; 1262255332Scy minicmpsz = ICMP_MINLEN; 1263255332Scy break; 1264255332Scy /* 1265255332Scy * type(1) + code(1) + cksum(2) + id(2) seq(2) + 1266255332Scy * 3 * timestamp(3 * 4) 1267255332Scy */ 1268255332Scy case ICMP_TSTAMP : 1269255332Scy case ICMP_TSTAMPREPLY : 1270255332Scy fin->fin_flx |= FI_ICMPQUERY; 1271255332Scy minicmpsz = 20; 1272255332Scy break; 1273255332Scy /* 1274255332Scy * type(1) + code(1) + cksum(2) + id(2) seq(2) + 1275255332Scy * mask(4) 1276255332Scy */ 1277255332Scy case ICMP_IREQ : 1278255332Scy case ICMP_IREQREPLY : 1279255332Scy case ICMP_MASKREQ : 1280255332Scy case ICMP_MASKREPLY : 1281255332Scy fin->fin_flx |= FI_ICMPQUERY; 1282255332Scy minicmpsz = 12; 1283255332Scy break; 1284255332Scy /* 1285255332Scy * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) 1286255332Scy */ 1287255332Scy case ICMP_UNREACH : 1288255332Scy#ifdef icmp_nextmtu 1289255332Scy if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { 1290255332Scy if (icmp->icmp_nextmtu < softc->ipf_icmpminfragmtu) 1291255332Scy fin->fin_flx |= FI_BAD; 1292255332Scy } 1293255332Scy#endif 1294334202Scy /* FALLTHROUGH */ 1295255332Scy case ICMP_SOURCEQUENCH : 1296255332Scy case ICMP_REDIRECT : 1297255332Scy case ICMP_TIMXCEED : 1298255332Scy case ICMP_PARAMPROB : 1299255332Scy fin->fin_flx |= FI_ICMPERR; 1300255332Scy if (ipf_coalesce(fin) != 1) { 1301255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_icmp_coalesce); 1302255332Scy return; 1303255332Scy } 1304172776Sdarrenr 1305145522Sdarrenr /* 1306255332Scy * ICMP error packets should not be generated for IP 1307255332Scy * packets that are a fragment that isn't the first 1308255332Scy * fragment. 1309145522Sdarrenr */ 1310255332Scy oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); 1311255332Scy if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) 1312255332Scy fin->fin_flx |= FI_BAD; 1313255332Scy 1314145522Sdarrenr /* 1315255332Scy * If the destination of this packet doesn't match the 1316255332Scy * source of the original packet then this packet is 1317255332Scy * not correct. 1318145522Sdarrenr */ 1319255332Scy if (oip->ip_src.s_addr != fin->fin_daddr) 1320255332Scy fin->fin_flx |= FI_BAD; 1321255332Scy break; 1322255332Scy default : 1323255332Scy break; 1324145522Sdarrenr } 1325145522Sdarrenr 1326255332Scy ipf_pr_short(fin, minicmpsz); 1327153876Sguido 1328255332Scy ipf_checkv4sum(fin); 1329145522Sdarrenr} 1330145522Sdarrenr 1331145522Sdarrenr 1332145522Sdarrenr/* ------------------------------------------------------------------------ */ 1333255332Scy/* Function: ipf_pr_tcpcommon */ 1334153876Sguido/* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */ 1335145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1336145522Sdarrenr/* */ 1337145522Sdarrenr/* TCP header sanity checking. Look for bad combinations of TCP flags, */ 1338145522Sdarrenr/* and make some checks with how they interact with other fields. */ 1339145522Sdarrenr/* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is */ 1340145522Sdarrenr/* valid and mark the packet as bad if not. */ 1341145522Sdarrenr/* ------------------------------------------------------------------------ */ 1342255332Scystatic INLINE int 1343255332Scyipf_pr_tcpcommon(fin) 1344255332Scy fr_info_t *fin; 1345145522Sdarrenr{ 1346255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1347145522Sdarrenr int flags, tlen; 1348145522Sdarrenr tcphdr_t *tcp; 1349145522Sdarrenr 1350153876Sguido fin->fin_flx |= FI_TCPUDP; 1351255332Scy if (fin->fin_off != 0) { 1352255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_tcp_frag); 1353153876Sguido return 0; 1354255332Scy } 1355145522Sdarrenr 1356255332Scy if (ipf_pr_pullup(fin, sizeof(*tcp)) == -1) { 1357255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); 1358153876Sguido return -1; 1359255332Scy } 1360255332Scy 1361145522Sdarrenr tcp = fin->fin_dp; 1362145522Sdarrenr if (fin->fin_dlen > 3) { 1363145522Sdarrenr fin->fin_sport = ntohs(tcp->th_sport); 1364145522Sdarrenr fin->fin_dport = ntohs(tcp->th_dport); 1365145522Sdarrenr } 1366145522Sdarrenr 1367255332Scy if ((fin->fin_flx & FI_SHORT) != 0) { 1368255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_tcp_short); 1369153876Sguido return 1; 1370255332Scy } 1371145522Sdarrenr 1372145522Sdarrenr /* 1373145522Sdarrenr * Use of the TCP data offset *must* result in a value that is at 1374145522Sdarrenr * least the same size as the TCP header. 1375145522Sdarrenr */ 1376145522Sdarrenr tlen = TCP_OFF(tcp) << 2; 1377145522Sdarrenr if (tlen < sizeof(tcphdr_t)) { 1378255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_tcp_small); 1379145522Sdarrenr fin->fin_flx |= FI_BAD; 1380153876Sguido return 1; 1381145522Sdarrenr } 1382145522Sdarrenr 1383145522Sdarrenr flags = tcp->th_flags; 1384145522Sdarrenr fin->fin_tcpf = tcp->th_flags; 1385145522Sdarrenr 1386145522Sdarrenr /* 1387145522Sdarrenr * If the urgent flag is set, then the urgent pointer must 1388145522Sdarrenr * also be set and vice versa. Good TCP packets do not have 1389145522Sdarrenr * just one of these set. 1390145522Sdarrenr */ 1391145522Sdarrenr if ((flags & TH_URG) != 0 && (tcp->th_urp == 0)) { 1392145522Sdarrenr fin->fin_flx |= FI_BAD; 1393170268Sdarrenr#if 0 1394145522Sdarrenr } else if ((flags & TH_URG) == 0 && (tcp->th_urp != 0)) { 1395170268Sdarrenr /* 1396170268Sdarrenr * Ignore this case (#if 0) as it shows up in "real" 1397170268Sdarrenr * traffic with bogus values in the urgent pointer field. 1398170268Sdarrenr */ 1399170268Sdarrenr fin->fin_flx |= FI_BAD; 1400170268Sdarrenr#endif 1401145522Sdarrenr } else if (((flags & (TH_SYN|TH_FIN)) != 0) && 1402145522Sdarrenr ((flags & (TH_RST|TH_ACK)) == TH_RST)) { 1403145522Sdarrenr /* TH_FIN|TH_RST|TH_ACK seems to appear "naturally" */ 1404145522Sdarrenr fin->fin_flx |= FI_BAD; 1405170268Sdarrenr#if 1 1406170268Sdarrenr } else if (((flags & TH_SYN) != 0) && 1407170268Sdarrenr ((flags & (TH_URG|TH_PUSH)) != 0)) { 1408170268Sdarrenr /* 1409170268Sdarrenr * SYN with URG and PUSH set is not for normal TCP but it is 1410170268Sdarrenr * possible(?) with T/TCP...but who uses T/TCP? 1411170268Sdarrenr */ 1412170268Sdarrenr fin->fin_flx |= FI_BAD; 1413170268Sdarrenr#endif 1414145522Sdarrenr } else if (!(flags & TH_ACK)) { 1415145522Sdarrenr /* 1416145522Sdarrenr * If the ack bit isn't set, then either the SYN or 1417145522Sdarrenr * RST bit must be set. If the SYN bit is set, then 1418145522Sdarrenr * we expect the ACK field to be 0. If the ACK is 1419145522Sdarrenr * not set and if URG, PSH or FIN are set, consdier 1420145522Sdarrenr * that to indicate a bad TCP packet. 1421145522Sdarrenr */ 1422145522Sdarrenr if ((flags == TH_SYN) && (tcp->th_ack != 0)) { 1423130886Sdarrenr /* 1424145522Sdarrenr * Cisco PIX sets the ACK field to a random value. 1425145522Sdarrenr * In light of this, do not set FI_BAD until a patch 1426145522Sdarrenr * is available from Cisco to ensure that 1427145522Sdarrenr * interoperability between existing systems is 1428145522Sdarrenr * achieved. 1429130886Sdarrenr */ 1430145522Sdarrenr /*fin->fin_flx |= FI_BAD*/; 1431145522Sdarrenr } else if (!(flags & (TH_RST|TH_SYN))) { 1432145522Sdarrenr fin->fin_flx |= FI_BAD; 1433145522Sdarrenr } else if ((flags & (TH_URG|TH_PUSH|TH_FIN)) != 0) { 1434145522Sdarrenr fin->fin_flx |= FI_BAD; 1435145522Sdarrenr } 1436145522Sdarrenr } 1437255332Scy if (fin->fin_flx & FI_BAD) { 1438255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_tcp_bad_flags); 1439255332Scy return 1; 1440255332Scy } 1441145522Sdarrenr 1442145522Sdarrenr /* 1443145522Sdarrenr * At this point, it's not exactly clear what is to be gained by 1444145522Sdarrenr * marking up which TCP options are and are not present. The one we 1445145522Sdarrenr * are most interested in is the TCP window scale. This is only in 1446145522Sdarrenr * a SYN packet [RFC1323] so we don't need this here...? 1447145522Sdarrenr * Now if we were to analyse the header for passive fingerprinting, 1448145522Sdarrenr * then that might add some weight to adding this... 1449145522Sdarrenr */ 1450255332Scy if (tlen == sizeof(tcphdr_t)) { 1451153876Sguido return 0; 1452255332Scy } 1453145522Sdarrenr 1454255332Scy if (ipf_pr_pullup(fin, tlen) == -1) { 1455255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); 1456153876Sguido return -1; 1457255332Scy } 1458145522Sdarrenr 1459145522Sdarrenr#if 0 1460172776Sdarrenr tcp = fin->fin_dp; 1461145522Sdarrenr ip = fin->fin_ip; 1462145522Sdarrenr s = (u_char *)(tcp + 1); 1463145522Sdarrenr off = IP_HL(ip) << 2; 1464145522Sdarrenr# ifdef _KERNEL 1465145522Sdarrenr if (fin->fin_mp != NULL) { 1466145522Sdarrenr mb_t *m = *fin->fin_mp; 1467145522Sdarrenr 1468145522Sdarrenr if (off + tlen > M_LEN(m)) 1469145522Sdarrenr return; 1470145522Sdarrenr } 1471145522Sdarrenr# endif 1472145522Sdarrenr for (tlen -= (int)sizeof(*tcp); tlen > 0; ) { 1473145522Sdarrenr opt = *s; 1474145522Sdarrenr if (opt == '\0') 1475145522Sdarrenr break; 1476145522Sdarrenr else if (opt == TCPOPT_NOP) 1477145522Sdarrenr ol = 1; 1478145522Sdarrenr else { 1479145522Sdarrenr if (tlen < 2) 148080482Sdarrenr break; 1481145522Sdarrenr ol = (int)*(s + 1); 1482145522Sdarrenr if (ol < 2 || ol > tlen) 148380482Sdarrenr break; 1484145522Sdarrenr } 1485145522Sdarrenr 1486145522Sdarrenr for (i = 9, mv = 4; mv >= 0; ) { 1487145522Sdarrenr op = ipopts + i; 1488145522Sdarrenr if (opt == (u_char)op->ol_val) { 1489145522Sdarrenr optmsk |= op->ol_bit; 149080482Sdarrenr break; 149180482Sdarrenr } 149267853Sdarrenr } 1493145522Sdarrenr tlen -= ol; 1494145522Sdarrenr s += ol; 1495145522Sdarrenr } 1496145522Sdarrenr#endif /* 0 */ 1497153876Sguido 1498153876Sguido return 0; 1499145522Sdarrenr} 150060850Sdarrenr 1501145522Sdarrenr 1502145522Sdarrenr 1503145522Sdarrenr/* ------------------------------------------------------------------------ */ 1504255332Scy/* Function: ipf_pr_udpcommon */ 1505153876Sguido/* Returns: int - 0 = header ok, 1 = bad packet */ 1506145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1507145522Sdarrenr/* */ 1508145522Sdarrenr/* Extract the UDP source and destination ports, if present. If compiled */ 1509145522Sdarrenr/* with IPFILTER_CKSUM, check to see if the UDP checksum is valid. */ 1510145522Sdarrenr/* ------------------------------------------------------------------------ */ 1511255332Scystatic INLINE int 1512255332Scyipf_pr_udpcommon(fin) 1513255332Scy fr_info_t *fin; 1514145522Sdarrenr{ 1515145522Sdarrenr udphdr_t *udp; 1516145522Sdarrenr 1517153876Sguido fin->fin_flx |= FI_TCPUDP; 1518145522Sdarrenr 1519145522Sdarrenr if (!fin->fin_off && (fin->fin_dlen > 3)) { 1520255332Scy if (ipf_pr_pullup(fin, sizeof(*udp)) == -1) { 1521255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1522255332Scy 1523153876Sguido fin->fin_flx |= FI_SHORT; 1524255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_udp_pullup); 1525153876Sguido return 1; 1526145522Sdarrenr } 1527145522Sdarrenr 1528145522Sdarrenr udp = fin->fin_dp; 1529145522Sdarrenr 1530145522Sdarrenr fin->fin_sport = ntohs(udp->uh_sport); 1531145522Sdarrenr fin->fin_dport = ntohs(udp->uh_dport); 153253642Sguido } 1533153876Sguido 1534153876Sguido return 0; 1535145522Sdarrenr} 1536130886Sdarrenr 1537145522Sdarrenr 1538145522Sdarrenr/* ------------------------------------------------------------------------ */ 1539255332Scy/* Function: ipf_pr_tcp */ 1540145522Sdarrenr/* Returns: void */ 1541145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1542145522Sdarrenr/* */ 1543145522Sdarrenr/* IPv4 Only */ 1544145522Sdarrenr/* Analyse the packet for IPv4/TCP properties. */ 1545145522Sdarrenr/* ------------------------------------------------------------------------ */ 1546255332Scystatic INLINE void 1547255332Scyipf_pr_tcp(fin) 1548255332Scy fr_info_t *fin; 1549145522Sdarrenr{ 1550145522Sdarrenr 1551255332Scy ipf_pr_short(fin, sizeof(tcphdr_t)); 1552145522Sdarrenr 1553255332Scy if (ipf_pr_tcpcommon(fin) == 0) 1554255332Scy ipf_checkv4sum(fin); 1555145522Sdarrenr} 1556145522Sdarrenr 1557145522Sdarrenr 1558145522Sdarrenr/* ------------------------------------------------------------------------ */ 1559255332Scy/* Function: ipf_pr_udp */ 1560145522Sdarrenr/* Returns: void */ 1561145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1562145522Sdarrenr/* */ 1563145522Sdarrenr/* IPv4 Only */ 1564145522Sdarrenr/* Analyse the packet for IPv4/UDP properties. */ 1565145522Sdarrenr/* ------------------------------------------------------------------------ */ 1566255332Scystatic INLINE void 1567255332Scyipf_pr_udp(fin) 1568255332Scy fr_info_t *fin; 1569145522Sdarrenr{ 1570145522Sdarrenr 1571255332Scy ipf_pr_short(fin, sizeof(udphdr_t)); 1572145522Sdarrenr 1573255332Scy if (ipf_pr_udpcommon(fin) == 0) 1574255332Scy ipf_checkv4sum(fin); 1575145522Sdarrenr} 1576145522Sdarrenr 1577145522Sdarrenr 1578145522Sdarrenr/* ------------------------------------------------------------------------ */ 1579255332Scy/* Function: ipf_pr_esp */ 1580145522Sdarrenr/* Returns: void */ 1581145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1582145522Sdarrenr/* */ 1583145522Sdarrenr/* Analyse the packet for ESP properties. */ 1584145522Sdarrenr/* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ 1585145522Sdarrenr/* even though the newer ESP packets must also have a sequence number that */ 1586145522Sdarrenr/* is 32bits as well, it is not possible(?) to determine the version from a */ 1587145522Sdarrenr/* simple packet header. */ 1588145522Sdarrenr/* ------------------------------------------------------------------------ */ 1589255332Scystatic INLINE void 1590255332Scyipf_pr_esp(fin) 1591255332Scy fr_info_t *fin; 1592145522Sdarrenr{ 1593145522Sdarrenr 1594153876Sguido if (fin->fin_off == 0) { 1595255332Scy ipf_pr_short(fin, 8); 1596255332Scy if (ipf_pr_pullup(fin, 8) == -1) { 1597255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1598255332Scy 1599255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v4_esp_pullup); 1600255332Scy } 1601153876Sguido } 1602145522Sdarrenr} 1603130886Sdarrenr 1604130886Sdarrenr 1605145522Sdarrenr/* ------------------------------------------------------------------------ */ 1606255332Scy/* Function: ipf_pr_ah */ 1607255332Scy/* Returns: int - value of the next header or IPPROTO_NONE if error */ 1608153876Sguido/* Parameters: fin(I) - pointer to packet information */ 1609153876Sguido/* */ 1610153876Sguido/* Analyse the packet for AH properties. */ 1611153876Sguido/* The minimum length is taken to be the combination of all fields in the */ 1612153876Sguido/* header being present and no authentication data (null algorithm used.) */ 1613153876Sguido/* ------------------------------------------------------------------------ */ 1614255332Scystatic INLINE int 1615255332Scyipf_pr_ah(fin) 1616255332Scy fr_info_t *fin; 1617153876Sguido{ 1618255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1619153876Sguido authhdr_t *ah; 1620153876Sguido int len; 1621153876Sguido 1622255332Scy fin->fin_flx |= FI_AH; 1623255332Scy ipf_pr_short(fin, sizeof(*ah)); 1624153876Sguido 1625255332Scy if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) { 1626255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v4_ah_bad); 1627255332Scy return IPPROTO_NONE; 1628255332Scy } 1629153876Sguido 1630255332Scy if (ipf_pr_pullup(fin, sizeof(*ah)) == -1) { 1631255332Scy DT(fr_v4_ah_pullup_1); 1632255332Scy LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); 1633255332Scy return IPPROTO_NONE; 1634255332Scy } 1635153876Sguido 1636153876Sguido ah = (authhdr_t *)fin->fin_dp; 1637153876Sguido 1638153876Sguido len = (ah->ah_plen + 2) << 2; 1639255332Scy ipf_pr_short(fin, len); 1640255332Scy if (ipf_pr_pullup(fin, len) == -1) { 1641255332Scy DT(fr_v4_ah_pullup_2); 1642255332Scy LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); 1643255332Scy return IPPROTO_NONE; 1644255332Scy } 1645255332Scy 1646255332Scy /* 1647255332Scy * Adjust fin_dp and fin_dlen for skipping over the authentication 1648255332Scy * header. 1649255332Scy */ 1650255332Scy fin->fin_dp = (char *)fin->fin_dp + len; 1651255332Scy fin->fin_dlen -= len; 1652255332Scy return ah->ah_next; 1653153876Sguido} 1654153876Sguido 1655153876Sguido 1656153876Sguido/* ------------------------------------------------------------------------ */ 1657255332Scy/* Function: ipf_pr_gre */ 1658145522Sdarrenr/* Returns: void */ 1659145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1660145522Sdarrenr/* */ 1661145522Sdarrenr/* Analyse the packet for GRE properties. */ 1662145522Sdarrenr/* ------------------------------------------------------------------------ */ 1663255332Scystatic INLINE void 1664255332Scyipf_pr_gre(fin) 1665255332Scy fr_info_t *fin; 1666145522Sdarrenr{ 1667255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1668145522Sdarrenr grehdr_t *gre; 1669145522Sdarrenr 1670255332Scy ipf_pr_short(fin, sizeof(grehdr_t)); 1671153876Sguido 1672255332Scy if (fin->fin_off != 0) { 1673255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_frag); 1674145522Sdarrenr return; 1675255332Scy } 1676145522Sdarrenr 1677255332Scy if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { 1678255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_pullup); 1679153876Sguido return; 1680255332Scy } 1681153876Sguido 1682255332Scy gre = fin->fin_dp; 1683255332Scy if (GRE_REV(gre->gr_flags) == 1) 1684255332Scy fin->fin_data[0] = gre->gr_call; 1685145522Sdarrenr} 1686145522Sdarrenr 1687145522Sdarrenr 1688145522Sdarrenr/* ------------------------------------------------------------------------ */ 1689255332Scy/* Function: ipf_pr_ipv4hdr */ 1690145522Sdarrenr/* Returns: void */ 1691145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1692145522Sdarrenr/* */ 1693145522Sdarrenr/* IPv4 Only */ 1694145522Sdarrenr/* Analyze the IPv4 header and set fields in the fr_info_t structure. */ 1695145522Sdarrenr/* Check all options present and flag their presence if any exist. */ 1696145522Sdarrenr/* ------------------------------------------------------------------------ */ 1697255332Scystatic INLINE void 1698255332Scyipf_pr_ipv4hdr(fin) 1699255332Scy fr_info_t *fin; 1700145522Sdarrenr{ 1701145522Sdarrenr u_short optmsk = 0, secmsk = 0, auth = 0; 1702145522Sdarrenr int hlen, ol, mv, p, i; 1703145522Sdarrenr const struct optlist *op; 1704145522Sdarrenr u_char *s, opt; 1705145522Sdarrenr u_short off; 1706145522Sdarrenr fr_ip_t *fi; 1707145522Sdarrenr ip_t *ip; 1708145522Sdarrenr 1709145522Sdarrenr fi = &fin->fin_fi; 1710145522Sdarrenr hlen = fin->fin_hlen; 1711145522Sdarrenr 1712145522Sdarrenr ip = fin->fin_ip; 1713145522Sdarrenr p = ip->ip_p; 1714145522Sdarrenr fi->fi_p = p; 1715255332Scy fin->fin_crc = p; 1716145522Sdarrenr fi->fi_tos = ip->ip_tos; 1717145522Sdarrenr fin->fin_id = ip->ip_id; 1718255332Scy off = ntohs(ip->ip_off); 1719145522Sdarrenr 1720145522Sdarrenr /* Get both TTL and protocol */ 1721145522Sdarrenr fi->fi_p = ip->ip_p; 1722145522Sdarrenr fi->fi_ttl = ip->ip_ttl; 1723145522Sdarrenr 1724145522Sdarrenr /* Zero out bits not used in IPv6 address */ 1725145522Sdarrenr fi->fi_src.i6[1] = 0; 1726145522Sdarrenr fi->fi_src.i6[2] = 0; 1727145522Sdarrenr fi->fi_src.i6[3] = 0; 1728145522Sdarrenr fi->fi_dst.i6[1] = 0; 1729145522Sdarrenr fi->fi_dst.i6[2] = 0; 1730145522Sdarrenr fi->fi_dst.i6[3] = 0; 1731145522Sdarrenr 1732145522Sdarrenr fi->fi_saddr = ip->ip_src.s_addr; 1733255332Scy fin->fin_crc += fi->fi_saddr; 1734145522Sdarrenr fi->fi_daddr = ip->ip_dst.s_addr; 1735255332Scy fin->fin_crc += fi->fi_daddr; 1736255332Scy if (IN_CLASSD(ntohl(fi->fi_daddr))) 1737255332Scy fin->fin_flx |= FI_MULTICAST|FI_MBCAST; 1738145522Sdarrenr 1739145522Sdarrenr /* 1740145522Sdarrenr * set packet attribute flags based on the offset and 1741145522Sdarrenr * calculate the byte offset that it represents. 1742145522Sdarrenr */ 1743145522Sdarrenr off &= IP_MF|IP_OFFMASK; 1744145522Sdarrenr if (off != 0) { 1745170268Sdarrenr int morefrag = off & IP_MF; 1746170268Sdarrenr 1747145522Sdarrenr fi->fi_flx |= FI_FRAG; 1748145522Sdarrenr off &= IP_OFFMASK; 1749145522Sdarrenr if (off != 0) { 1750145522Sdarrenr fin->fin_flx |= FI_FRAGBODY; 1751145522Sdarrenr off <<= 3; 1752255332Scy if ((off + fin->fin_dlen > 65535) || 1753170268Sdarrenr (fin->fin_dlen == 0) || 1754170268Sdarrenr ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) { 1755255332Scy /* 1756153876Sguido * The length of the packet, starting at its 1757255332Scy * offset cannot exceed 65535 (0xffff) as the 1758153876Sguido * length of an IP packet is only 16 bits. 1759153876Sguido * 1760153876Sguido * Any fragment that isn't the last fragment 1761153876Sguido * must have a length greater than 0 and it 1762153876Sguido * must be an even multiple of 8. 1763153876Sguido */ 1764145522Sdarrenr fi->fi_flx |= FI_BAD; 1765130886Sdarrenr } 1766130886Sdarrenr } 1767145522Sdarrenr } 1768145522Sdarrenr fin->fin_off = off; 1769130886Sdarrenr 1770145522Sdarrenr /* 1771145522Sdarrenr * Call per-protocol setup and checking 1772145522Sdarrenr */ 1773255332Scy if (p == IPPROTO_AH) { 1774255332Scy /* 1775255332Scy * Treat AH differently because we expect there to be another 1776255332Scy * layer 4 header after it. 1777255332Scy */ 1778255332Scy p = ipf_pr_ah(fin); 1779255332Scy } 1780255332Scy 1781145522Sdarrenr switch (p) 1782145522Sdarrenr { 178353642Sguido case IPPROTO_UDP : 1784255332Scy ipf_pr_udp(fin); 178553642Sguido break; 1786145522Sdarrenr case IPPROTO_TCP : 1787255332Scy ipf_pr_tcp(fin); 1788145522Sdarrenr break; 1789145522Sdarrenr case IPPROTO_ICMP : 1790255332Scy ipf_pr_icmp(fin); 1791145522Sdarrenr break; 179292685Sdarrenr case IPPROTO_ESP : 1793255332Scy ipf_pr_esp(fin); 179492685Sdarrenr break; 1795145522Sdarrenr case IPPROTO_GRE : 1796255332Scy ipf_pr_gre(fin); 179753642Sguido break; 179853642Sguido } 179953642Sguido 1800145522Sdarrenr ip = fin->fin_ip; 1801145522Sdarrenr if (ip == NULL) 1802145522Sdarrenr return; 1803130886Sdarrenr 1804145522Sdarrenr /* 1805145522Sdarrenr * If it is a standard IP header (no options), set the flag fields 1806145522Sdarrenr * which relate to options to 0. 1807145522Sdarrenr */ 1808145522Sdarrenr if (hlen == sizeof(*ip)) { 180960850Sdarrenr fi->fi_optmsk = 0; 181060850Sdarrenr fi->fi_secmsk = 0; 181160850Sdarrenr fi->fi_auth = 0; 1812145522Sdarrenr return; 181360850Sdarrenr } 181453642Sguido 1815145522Sdarrenr /* 1816145522Sdarrenr * So the IP header has some IP options attached. Walk the entire 1817145522Sdarrenr * list of options present with this packet and set flags to indicate 1818145522Sdarrenr * which ones are here and which ones are not. For the somewhat out 1819145522Sdarrenr * of date and obscure security classification options, set a flag to 1820145522Sdarrenr * represent which classification is present. 1821145522Sdarrenr */ 1822145522Sdarrenr fi->fi_flx |= FI_OPTIONS; 1823145522Sdarrenr 182460295Sdarrenr for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { 182553642Sguido opt = *s; 182653642Sguido if (opt == '\0') 182753642Sguido break; 182860295Sdarrenr else if (opt == IPOPT_NOP) 182960295Sdarrenr ol = 1; 183060295Sdarrenr else { 183160295Sdarrenr if (hlen < 2) 183260295Sdarrenr break; 183360295Sdarrenr ol = (int)*(s + 1); 183460295Sdarrenr if (ol < 2 || ol > hlen) 183560295Sdarrenr break; 183660295Sdarrenr } 183753642Sguido for (i = 9, mv = 4; mv >= 0; ) { 183853642Sguido op = ipopts + i; 1839255332Scy 1840145522Sdarrenr if ((opt == (u_char)op->ol_val) && (ol > 4)) { 1841255332Scy u_32_t doi; 184253642Sguido 1843255332Scy switch (opt) 1844255332Scy { 1845255332Scy case IPOPT_SECURITY : 1846255332Scy if (optmsk & op->ol_bit) { 1847255332Scy fin->fin_flx |= FI_BAD; 1848255332Scy } else { 1849255332Scy doi = ipf_checkripso(s); 1850255332Scy secmsk = doi >> 16; 1851255332Scy auth = doi & 0xffff; 185253642Sguido } 1853255332Scy break; 1854255332Scy 1855255332Scy case IPOPT_CIPSO : 1856255332Scy 1857255332Scy if (optmsk & op->ol_bit) { 1858255332Scy fin->fin_flx |= FI_BAD; 1859255332Scy } else { 1860255332Scy doi = ipf_checkcipso(fin, 1861255332Scy s, ol); 1862255332Scy secmsk = doi >> 16; 1863255332Scy auth = doi & 0xffff; 1864255332Scy } 1865255332Scy break; 186653642Sguido } 1867255332Scy optmsk |= op->ol_bit; 186853642Sguido } 1869255332Scy 187053642Sguido if (opt < op->ol_val) 1871145522Sdarrenr i -= mv; 187253642Sguido else 1873145522Sdarrenr i += mv; 1874145522Sdarrenr mv--; 187553642Sguido } 187653642Sguido hlen -= ol; 187753642Sguido s += ol; 187853642Sguido } 1879145522Sdarrenr 1880145522Sdarrenr /* 1881145522Sdarrenr * 1882145522Sdarrenr */ 188353642Sguido if (auth && !(auth & 0x0100)) 188453642Sguido auth &= 0xff00; 188553642Sguido fi->fi_optmsk = optmsk; 188653642Sguido fi->fi_secmsk = secmsk; 188753642Sguido fi->fi_auth = auth; 188853642Sguido} 188953642Sguido 189053642Sguido 1891145522Sdarrenr/* ------------------------------------------------------------------------ */ 1892255332Scy/* Function: ipf_checkripso */ 1893145522Sdarrenr/* Returns: void */ 1894255332Scy/* Parameters: s(I) - pointer to start of RIPSO option */ 1895255332Scy/* */ 1896255332Scy/* ------------------------------------------------------------------------ */ 1897255332Scystatic u_32_t 1898255332Scyipf_checkripso(s) 1899255332Scy u_char *s; 1900255332Scy{ 1901255332Scy const struct optlist *sp; 1902255332Scy u_short secmsk = 0, auth = 0; 1903255332Scy u_char sec; 1904255332Scy int j, m; 1905255332Scy 1906255332Scy sec = *(s + 2); /* classification */ 1907255332Scy for (j = 3, m = 2; m >= 0; ) { 1908255332Scy sp = secopt + j; 1909255332Scy if (sec == sp->ol_val) { 1910255332Scy secmsk |= sp->ol_bit; 1911255332Scy auth = *(s + 3); 1912255332Scy auth *= 256; 1913255332Scy auth += *(s + 4); 1914255332Scy break; 1915255332Scy } 1916255332Scy if (sec < sp->ol_val) 1917255332Scy j -= m; 1918255332Scy else 1919255332Scy j += m; 1920255332Scy m--; 1921255332Scy } 1922255332Scy 1923255332Scy return (secmsk << 16) | auth; 1924255332Scy} 1925255332Scy 1926255332Scy 1927255332Scy/* ------------------------------------------------------------------------ */ 1928255332Scy/* Function: ipf_checkcipso */ 1929255332Scy/* Returns: u_32_t - 0 = failure, else the doi from the header */ 1930255332Scy/* Parameters: fin(IO) - pointer to packet information */ 1931255332Scy/* s(I) - pointer to start of CIPSO option */ 1932255332Scy/* ol(I) - length of CIPSO option field */ 1933255332Scy/* */ 1934255332Scy/* This function returns the domain of integrity (DOI) field from the CIPSO */ 1935255332Scy/* header and returns that whilst also storing the highest sensitivity */ 1936255332Scy/* value found in the fr_info_t structure. */ 1937255332Scy/* */ 1938255332Scy/* No attempt is made to extract the category bitmaps as these are defined */ 1939255332Scy/* by the user (rather than the protocol) and can be rather numerous on the */ 1940255332Scy/* end nodes. */ 1941255332Scy/* ------------------------------------------------------------------------ */ 1942255332Scystatic u_32_t 1943255332Scyipf_checkcipso(fin, s, ol) 1944255332Scy fr_info_t *fin; 1945255332Scy u_char *s; 1946255332Scy int ol; 1947255332Scy{ 1948255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1949255332Scy fr_ip_t *fi; 1950255332Scy u_32_t doi; 1951255332Scy u_char *t, tag, tlen, sensitivity; 1952255332Scy int len; 1953255332Scy 1954255332Scy if (ol < 6 || ol > 40) { 1955255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_bad); 1956255332Scy fin->fin_flx |= FI_BAD; 1957255332Scy return 0; 1958255332Scy } 1959255332Scy 1960255332Scy fi = &fin->fin_fi; 1961255332Scy fi->fi_sensitivity = 0; 1962255332Scy /* 1963255332Scy * The DOI field MUST be there. 1964255332Scy */ 1965255332Scy bcopy(s + 2, &doi, sizeof(doi)); 1966255332Scy 1967255332Scy t = (u_char *)s + 6; 1968255332Scy for (len = ol - 6; len >= 2; len -= tlen, t+= tlen) { 1969255332Scy tag = *t; 1970255332Scy tlen = *(t + 1); 1971255332Scy if (tlen > len || tlen < 4 || tlen > 34) { 1972255332Scy LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_tlen); 1973255332Scy fin->fin_flx |= FI_BAD; 1974255332Scy return 0; 1975255332Scy } 1976255332Scy 1977255332Scy sensitivity = 0; 1978255332Scy /* 1979255332Scy * Tag numbers 0, 1, 2, 5 are laid out in the CIPSO Internet 1980255332Scy * draft (16 July 1992) that has expired. 1981255332Scy */ 1982255332Scy if (tag == 0) { 1983255332Scy fin->fin_flx |= FI_BAD; 1984255332Scy continue; 1985255332Scy } else if (tag == 1) { 1986255332Scy if (*(t + 2) != 0) { 1987255332Scy fin->fin_flx |= FI_BAD; 1988255332Scy continue; 1989255332Scy } 1990255332Scy sensitivity = *(t + 3); 1991255332Scy /* Category bitmap for categories 0-239 */ 1992255332Scy 1993255332Scy } else if (tag == 4) { 1994255332Scy if (*(t + 2) != 0) { 1995255332Scy fin->fin_flx |= FI_BAD; 1996255332Scy continue; 1997255332Scy } 1998255332Scy sensitivity = *(t + 3); 1999255332Scy /* Enumerated categories, 16bits each, upto 15 */ 2000255332Scy 2001255332Scy } else if (tag == 5) { 2002255332Scy if (*(t + 2) != 0) { 2003255332Scy fin->fin_flx |= FI_BAD; 2004255332Scy continue; 2005255332Scy } 2006255332Scy sensitivity = *(t + 3); 2007255332Scy /* Range of categories (2*16bits), up to 7 pairs */ 2008255332Scy 2009255332Scy } else if (tag > 127) { 2010255332Scy /* Custom defined DOI */ 2011255332Scy ; 2012255332Scy } else { 2013255332Scy fin->fin_flx |= FI_BAD; 2014255332Scy continue; 2015255332Scy } 2016255332Scy 2017255332Scy if (sensitivity > fi->fi_sensitivity) 2018255332Scy fi->fi_sensitivity = sensitivity; 2019255332Scy } 2020255332Scy 2021255332Scy return doi; 2022255332Scy} 2023255332Scy 2024255332Scy 2025255332Scy/* ------------------------------------------------------------------------ */ 2026255332Scy/* Function: ipf_makefrip */ 2027255332Scy/* Returns: int - 0 == packet ok, -1 == packet freed */ 2028145522Sdarrenr/* Parameters: hlen(I) - length of IP packet header */ 2029145522Sdarrenr/* ip(I) - pointer to the IP header */ 2030145522Sdarrenr/* fin(IO) - pointer to packet information */ 2031145522Sdarrenr/* */ 2032145522Sdarrenr/* Compact the IP header into a structure which contains just the info. */ 2033145522Sdarrenr/* which is useful for comparing IP headers with and store this information */ 2034145522Sdarrenr/* in the fr_info_t structure pointer to by fin. At present, it is assumed */ 2035145522Sdarrenr/* this function will be called with either an IPv4 or IPv6 packet. */ 2036145522Sdarrenr/* ------------------------------------------------------------------------ */ 2037255332Scyint 2038255332Scyipf_makefrip(hlen, ip, fin) 2039255332Scy int hlen; 2040255332Scy ip_t *ip; 2041255332Scy fr_info_t *fin; 204253642Sguido{ 2043255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2044145522Sdarrenr int v; 204553642Sguido 2046145522Sdarrenr fin->fin_depth = 0; 2047145522Sdarrenr fin->fin_hlen = (u_short)hlen; 2048145522Sdarrenr fin->fin_ip = ip; 2049145522Sdarrenr fin->fin_rule = 0xffffffff; 2050145522Sdarrenr fin->fin_group[0] = -1; 2051145522Sdarrenr fin->fin_group[1] = '\0'; 2052145522Sdarrenr fin->fin_dp = (char *)ip + hlen; 2053145522Sdarrenr 2054145522Sdarrenr v = fin->fin_v; 2055170268Sdarrenr if (v == 4) { 2056255332Scy fin->fin_plen = ntohs(ip->ip_len); 2057170268Sdarrenr fin->fin_dlen = fin->fin_plen - hlen; 2058255332Scy ipf_pr_ipv4hdr(fin); 2059145522Sdarrenr#ifdef USE_INET6 2060170268Sdarrenr } else if (v == 6) { 2061170268Sdarrenr fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen); 2062170268Sdarrenr fin->fin_dlen = fin->fin_plen; 2063170268Sdarrenr fin->fin_plen += hlen; 2064170268Sdarrenr 2065255332Scy ipf_pr_ipv6hdr(fin); 2066170268Sdarrenr#endif 2067153876Sguido } 2068255332Scy if (fin->fin_ip == NULL) { 2069255332Scy LBUMP(ipf_stats[fin->fin_out].fr_ip_freed); 2070145522Sdarrenr return -1; 2071255332Scy } 2072145522Sdarrenr return 0; 2073145522Sdarrenr} 2074145522Sdarrenr 2075145522Sdarrenr 2076145522Sdarrenr/* ------------------------------------------------------------------------ */ 2077255332Scy/* Function: ipf_portcheck */ 2078145522Sdarrenr/* Returns: int - 1 == port matched, 0 == port match failed */ 2079145522Sdarrenr/* Parameters: frp(I) - pointer to port check `expression' */ 2080255332Scy/* pop(I) - port number to evaluate */ 2081145522Sdarrenr/* */ 2082145522Sdarrenr/* Perform a comparison of a port number against some other(s), using a */ 2083145522Sdarrenr/* structure with compare information stored in it. */ 2084145522Sdarrenr/* ------------------------------------------------------------------------ */ 2085255332Scystatic INLINE int 2086255332Scyipf_portcheck(frp, pop) 2087255332Scy frpcmp_t *frp; 2088255332Scy u_32_t pop; 2089145522Sdarrenr{ 2090145522Sdarrenr int err = 1; 2091255332Scy u_32_t po; 2092145522Sdarrenr 2093145522Sdarrenr po = frp->frp_port; 2094145522Sdarrenr 209553642Sguido /* 2096145522Sdarrenr * Do opposite test to that required and continue if that succeeds. 209753642Sguido */ 2098145522Sdarrenr switch (frp->frp_cmp) 2099145522Sdarrenr { 2100145522Sdarrenr case FR_EQUAL : 2101255332Scy if (pop != po) /* EQUAL */ 210253642Sguido err = 0; 2103145522Sdarrenr break; 2104145522Sdarrenr case FR_NEQUAL : 2105255332Scy if (pop == po) /* NOTEQUAL */ 210653642Sguido err = 0; 2107145522Sdarrenr break; 2108145522Sdarrenr case FR_LESST : 2109255332Scy if (pop >= po) /* LESSTHAN */ 211053642Sguido err = 0; 2111145522Sdarrenr break; 2112145522Sdarrenr case FR_GREATERT : 2113255332Scy if (pop <= po) /* GREATERTHAN */ 211453642Sguido err = 0; 2115145522Sdarrenr break; 2116145522Sdarrenr case FR_LESSTE : 2117255332Scy if (pop > po) /* LT or EQ */ 211853642Sguido err = 0; 2119145522Sdarrenr break; 2120145522Sdarrenr case FR_GREATERTE : 2121255332Scy if (pop < po) /* GT or EQ */ 212253642Sguido err = 0; 2123145522Sdarrenr break; 2124145522Sdarrenr case FR_OUTRANGE : 2125255332Scy if (pop >= po && pop <= frp->frp_top) /* Out of range */ 212653642Sguido err = 0; 2127145522Sdarrenr break; 2128145522Sdarrenr case FR_INRANGE : 2129255332Scy if (pop <= po || pop >= frp->frp_top) /* In range */ 213053642Sguido err = 0; 2131145522Sdarrenr break; 2132145522Sdarrenr case FR_INCRANGE : 2133255332Scy if (pop < po || pop > frp->frp_top) /* Inclusive range */ 2134145522Sdarrenr err = 0; 2135145522Sdarrenr break; 2136145522Sdarrenr default : 2137145522Sdarrenr break; 213853642Sguido } 2139145522Sdarrenr return err; 2140145522Sdarrenr} 2141145522Sdarrenr 2142145522Sdarrenr 2143145522Sdarrenr/* ------------------------------------------------------------------------ */ 2144255332Scy/* Function: ipf_tcpudpchk */ 2145145522Sdarrenr/* Returns: int - 1 == protocol matched, 0 == check failed */ 2146255332Scy/* Parameters: fda(I) - pointer to packet information */ 2147145522Sdarrenr/* ft(I) - pointer to structure with comparison data */ 2148145522Sdarrenr/* */ 2149145522Sdarrenr/* Compares the current pcket (assuming it is TCP/UDP) information with a */ 2150145522Sdarrenr/* structure containing information that we want to match against. */ 2151145522Sdarrenr/* ------------------------------------------------------------------------ */ 2152255332Scyint 2153255332Scyipf_tcpudpchk(fi, ft) 2154255332Scy fr_ip_t *fi; 2155255332Scy frtuc_t *ft; 2156145522Sdarrenr{ 2157145522Sdarrenr int err = 1; 2158145522Sdarrenr 215953642Sguido /* 2160145522Sdarrenr * Both ports should *always* be in the first fragment. 2161145522Sdarrenr * So far, I cannot find any cases where they can not be. 2162145522Sdarrenr * 2163145522Sdarrenr * compare destination ports 2164145522Sdarrenr */ 2165145522Sdarrenr if (ft->ftu_dcmp) 2166255332Scy err = ipf_portcheck(&ft->ftu_dst, fi->fi_ports[1]); 2167145522Sdarrenr 2168145522Sdarrenr /* 216953642Sguido * compare source ports 217053642Sguido */ 2171145522Sdarrenr if (err && ft->ftu_scmp) 2172255332Scy err = ipf_portcheck(&ft->ftu_src, fi->fi_ports[0]); 217353642Sguido 217453642Sguido /* 217553642Sguido * If we don't have all the TCP/UDP header, then how can we 217653642Sguido * expect to do any sort of match on it ? If we were looking for 217753642Sguido * TCP flags, then NO match. If not, then match (which should 217853642Sguido * satisfy the "short" class too). 217953642Sguido */ 2180255332Scy if (err && (fi->fi_p == IPPROTO_TCP)) { 2181255332Scy if (fi->fi_flx & FI_SHORT) 218260850Sdarrenr return !(ft->ftu_tcpf | ft->ftu_tcpfm); 218353642Sguido /* 218453642Sguido * Match the flags ? If not, abort this match. 218553642Sguido */ 218660850Sdarrenr if (ft->ftu_tcpfm && 2187255332Scy ft->ftu_tcpf != (fi->fi_tcpf & ft->ftu_tcpfm)) { 2188255332Scy FR_DEBUG(("f. %#x & %#x != %#x\n", fi->fi_tcpf, 218960850Sdarrenr ft->ftu_tcpfm, ft->ftu_tcpf)); 219053642Sguido err = 0; 219153642Sguido } 219253642Sguido } 219353642Sguido return err; 219453642Sguido} 219553642Sguido 2196145522Sdarrenr 2197145522Sdarrenr/* ------------------------------------------------------------------------ */ 2198255332Scy/* Function: ipf_check_ipf */ 2199255332Scy/* Returns: int - 0 == match, else no match */ 2200145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2201145522Sdarrenr/* fr(I) - pointer to filter rule */ 2202145522Sdarrenr/* portcmp(I) - flag indicating whether to attempt matching on */ 2203145522Sdarrenr/* TCP/UDP port data. */ 2204145522Sdarrenr/* */ 2205145522Sdarrenr/* Check to see if a packet matches an IPFilter rule. Checks of addresses, */ 2206145522Sdarrenr/* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */ 2207145522Sdarrenr/* this function. */ 2208145522Sdarrenr/* ------------------------------------------------------------------------ */ 2209255332Scystatic INLINE int 2210255332Scyipf_check_ipf(fin, fr, portcmp) 2211255332Scy fr_info_t *fin; 2212255332Scy frentry_t *fr; 2213255332Scy int portcmp; 221453642Sguido{ 2215145522Sdarrenr u_32_t *ld, *lm, *lip; 2216145522Sdarrenr fripf_t *fri; 2217145522Sdarrenr fr_ip_t *fi; 2218145522Sdarrenr int i; 221953642Sguido 2220145522Sdarrenr fi = &fin->fin_fi; 2221145522Sdarrenr fri = fr->fr_ipf; 2222145522Sdarrenr lip = (u_32_t *)fi; 2223145522Sdarrenr lm = (u_32_t *)&fri->fri_mip; 2224145522Sdarrenr ld = (u_32_t *)&fri->fri_ip; 2225145522Sdarrenr 2226145522Sdarrenr /* 2227145522Sdarrenr * first 32 bits to check coversion: 2228145522Sdarrenr * IP version, TOS, TTL, protocol 2229145522Sdarrenr */ 2230145522Sdarrenr i = ((*lip & *lm) != *ld); 2231145522Sdarrenr FR_DEBUG(("0. %#08x & %#08x != %#08x\n", 2232170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2233145522Sdarrenr if (i) 2234145522Sdarrenr return 1; 2235145522Sdarrenr 2236145522Sdarrenr /* 2237145522Sdarrenr * Next 32 bits is a constructed bitmask indicating which IP options 2238145522Sdarrenr * are present (if any) in this packet. 2239145522Sdarrenr */ 2240145522Sdarrenr lip++, lm++, ld++; 2241255332Scy i = ((*lip & *lm) != *ld); 2242145522Sdarrenr FR_DEBUG(("1. %#08x & %#08x != %#08x\n", 2243170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2244255332Scy if (i != 0) 2245145522Sdarrenr return 1; 2246145522Sdarrenr 2247145522Sdarrenr lip++, lm++, ld++; 2248145522Sdarrenr /* 2249145522Sdarrenr * Unrolled loops (4 each, for 32 bits) for address checks. 2250145522Sdarrenr */ 2251145522Sdarrenr /* 2252145522Sdarrenr * Check the source address. 2253145522Sdarrenr */ 2254145522Sdarrenr if (fr->fr_satype == FRI_LOOKUP) { 2255255332Scy i = (*fr->fr_srcfunc)(fin->fin_main_soft, fr->fr_srcptr, 2256255332Scy fi->fi_v, lip, fin->fin_plen); 2257145522Sdarrenr if (i == -1) 2258145522Sdarrenr return 1; 2259145522Sdarrenr lip += 3; 2260145522Sdarrenr lm += 3; 2261145522Sdarrenr ld += 3; 2262145522Sdarrenr } else { 2263145522Sdarrenr i = ((*lip & *lm) != *ld); 2264145522Sdarrenr FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", 2265170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2266145522Sdarrenr if (fi->fi_v == 6) { 2267145522Sdarrenr lip++, lm++, ld++; 2268145522Sdarrenr i |= ((*lip & *lm) != *ld); 2269145522Sdarrenr FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", 2270170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2271145522Sdarrenr lip++, lm++, ld++; 2272145522Sdarrenr i |= ((*lip & *lm) != *ld); 2273145522Sdarrenr FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", 2274170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2275145522Sdarrenr lip++, lm++, ld++; 2276145522Sdarrenr i |= ((*lip & *lm) != *ld); 2277145522Sdarrenr FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", 2278170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2279145522Sdarrenr } else { 2280145522Sdarrenr lip += 3; 2281145522Sdarrenr lm += 3; 2282145522Sdarrenr ld += 3; 2283145522Sdarrenr } 2284145522Sdarrenr } 2285145522Sdarrenr i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6; 2286255332Scy if (i != 0) 2287145522Sdarrenr return 1; 2288145522Sdarrenr 2289145522Sdarrenr /* 2290145522Sdarrenr * Check the destination address. 2291145522Sdarrenr */ 2292145522Sdarrenr lip++, lm++, ld++; 2293145522Sdarrenr if (fr->fr_datype == FRI_LOOKUP) { 2294255332Scy i = (*fr->fr_dstfunc)(fin->fin_main_soft, fr->fr_dstptr, 2295255332Scy fi->fi_v, lip, fin->fin_plen); 2296145522Sdarrenr if (i == -1) 2297145522Sdarrenr return 1; 2298145522Sdarrenr lip += 3; 2299145522Sdarrenr lm += 3; 2300145522Sdarrenr ld += 3; 2301145522Sdarrenr } else { 2302145522Sdarrenr i = ((*lip & *lm) != *ld); 2303145522Sdarrenr FR_DEBUG(("3a. %#08x & %#08x != %#08x\n", 2304170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2305145522Sdarrenr if (fi->fi_v == 6) { 2306145522Sdarrenr lip++, lm++, ld++; 2307145522Sdarrenr i |= ((*lip & *lm) != *ld); 2308145522Sdarrenr FR_DEBUG(("3b. %#08x & %#08x != %#08x\n", 2309170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2310145522Sdarrenr lip++, lm++, ld++; 2311145522Sdarrenr i |= ((*lip & *lm) != *ld); 2312145522Sdarrenr FR_DEBUG(("3c. %#08x & %#08x != %#08x\n", 2313170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2314145522Sdarrenr lip++, lm++, ld++; 2315145522Sdarrenr i |= ((*lip & *lm) != *ld); 2316145522Sdarrenr FR_DEBUG(("3d. %#08x & %#08x != %#08x\n", 2317170268Sdarrenr ntohl(*lip), ntohl(*lm), ntohl(*ld))); 2318145522Sdarrenr } else { 2319145522Sdarrenr lip += 3; 2320145522Sdarrenr lm += 3; 2321145522Sdarrenr ld += 3; 2322145522Sdarrenr } 2323145522Sdarrenr } 2324145522Sdarrenr i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7; 2325255332Scy if (i != 0) 2326145522Sdarrenr return 1; 2327145522Sdarrenr /* 2328145522Sdarrenr * IP addresses matched. The next 32bits contains: 2329145522Sdarrenr * mast of old IP header security & authentication bits. 2330145522Sdarrenr */ 2331145522Sdarrenr lip++, lm++, ld++; 2332255332Scy i = (*ld - (*lip & *lm)); 2333255332Scy FR_DEBUG(("4. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); 2334145522Sdarrenr 2335145522Sdarrenr /* 2336145522Sdarrenr * Next we have 32 bits of packet flags. 2337145522Sdarrenr */ 2338145522Sdarrenr lip++, lm++, ld++; 2339255332Scy i |= (*ld - (*lip & *lm)); 2340255332Scy FR_DEBUG(("5. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); 2341145522Sdarrenr 2342145522Sdarrenr if (i == 0) { 2343145522Sdarrenr /* 2344145522Sdarrenr * If a fragment, then only the first has what we're 2345145522Sdarrenr * looking for here... 2346145522Sdarrenr */ 2347145522Sdarrenr if (portcmp) { 2348255332Scy if (!ipf_tcpudpchk(&fin->fin_fi, &fr->fr_tuc)) 2349145522Sdarrenr i = 1; 2350145522Sdarrenr } else { 2351145522Sdarrenr if (fr->fr_dcmp || fr->fr_scmp || 2352145522Sdarrenr fr->fr_tcpf || fr->fr_tcpfm) 2353145522Sdarrenr i = 1; 2354145522Sdarrenr if (fr->fr_icmpm || fr->fr_icmp) { 2355145522Sdarrenr if (((fi->fi_p != IPPROTO_ICMP) && 2356145522Sdarrenr (fi->fi_p != IPPROTO_ICMPV6)) || 2357145522Sdarrenr fin->fin_off || (fin->fin_dlen < 2)) 2358145522Sdarrenr i = 1; 2359145522Sdarrenr else if ((fin->fin_data[0] & fr->fr_icmpm) != 2360145522Sdarrenr fr->fr_icmp) { 2361145522Sdarrenr FR_DEBUG(("i. %#x & %#x != %#x\n", 2362145522Sdarrenr fin->fin_data[0], 2363145522Sdarrenr fr->fr_icmpm, fr->fr_icmp)); 2364145522Sdarrenr i = 1; 2365145522Sdarrenr } 2366145522Sdarrenr } 2367145522Sdarrenr } 2368145522Sdarrenr } 2369145522Sdarrenr return i; 2370145522Sdarrenr} 2371145522Sdarrenr 2372145522Sdarrenr 2373145522Sdarrenr/* ------------------------------------------------------------------------ */ 2374255332Scy/* Function: ipf_scanlist */ 2375145522Sdarrenr/* Returns: int - result flags of scanning filter list */ 2376145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2377145522Sdarrenr/* pass(I) - default result to return for filtering */ 2378145522Sdarrenr/* */ 2379145522Sdarrenr/* Check the input/output list of rules for a match to the current packet. */ 2380145522Sdarrenr/* If a match is found, the value of fr_flags from the rule becomes the */ 2381145522Sdarrenr/* return value and fin->fin_fr points to the matched rule. */ 2382145522Sdarrenr/* */ 2383145522Sdarrenr/* This function may be called recusively upto 16 times (limit inbuilt.) */ 2384145522Sdarrenr/* When unwinding, it should finish up with fin_depth as 0. */ 2385145522Sdarrenr/* */ 2386145522Sdarrenr/* Could be per interface, but this gets real nasty when you don't have, */ 2387145522Sdarrenr/* or can't easily change, the kernel source code to . */ 2388145522Sdarrenr/* ------------------------------------------------------------------------ */ 2389255332Scyint 2390255332Scyipf_scanlist(fin, pass) 2391255332Scy fr_info_t *fin; 2392255332Scy u_32_t pass; 2393145522Sdarrenr{ 2394255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2395172776Sdarrenr int rulen, portcmp, off, skip; 2396145522Sdarrenr struct frentry *fr, *fnext; 2397153876Sguido u_32_t passt, passo; 2398145522Sdarrenr 2399145522Sdarrenr /* 2400145522Sdarrenr * Do not allow nesting deeper than 16 levels. 2401145522Sdarrenr */ 2402145522Sdarrenr if (fin->fin_depth >= 16) 2403145522Sdarrenr return pass; 2404145522Sdarrenr 240553642Sguido fr = fin->fin_fr; 2406145522Sdarrenr 2407145522Sdarrenr /* 2408145522Sdarrenr * If there are no rules in this list, return now. 2409145522Sdarrenr */ 2410145522Sdarrenr if (fr == NULL) 2411145522Sdarrenr return pass; 2412145522Sdarrenr 2413145522Sdarrenr skip = 0; 2414145522Sdarrenr portcmp = 0; 2415145522Sdarrenr fin->fin_depth++; 241653642Sguido fin->fin_fr = NULL; 241780482Sdarrenr off = fin->fin_off; 241853642Sguido 2419145522Sdarrenr if ((fin->fin_flx & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) 242053642Sguido portcmp = 1; 242153642Sguido 2422145522Sdarrenr for (rulen = 0; fr; fr = fnext, rulen++) { 2423145522Sdarrenr fnext = fr->fr_next; 2424145522Sdarrenr if (skip != 0) { 2425255332Scy FR_VERBOSE(("SKIP %d (%#x)\n", skip, fr->fr_flags)); 242653642Sguido skip--; 242753642Sguido continue; 242853642Sguido } 2429145522Sdarrenr 243053642Sguido /* 243153642Sguido * In all checks below, a null (zero) value in the 243253642Sguido * filter struture is taken to mean a wildcard. 243353642Sguido * 243453642Sguido * check that we are working for the right interface 243553642Sguido */ 243653642Sguido#ifdef _KERNEL 2437145522Sdarrenr if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) 2438145522Sdarrenr continue; 243953642Sguido#else 244053642Sguido if (opts & (OPT_VERBOSE|OPT_DEBUG)) 244153642Sguido printf("\n"); 2442145522Sdarrenr FR_VERBOSE(("%c", FR_ISSKIP(pass) ? 's' : 2443145522Sdarrenr FR_ISPASS(pass) ? 'p' : 2444145522Sdarrenr FR_ISACCOUNT(pass) ? 'A' : 2445145522Sdarrenr FR_ISAUTH(pass) ? 'a' : 2446145522Sdarrenr (pass & FR_NOMATCH) ? 'n' :'b')); 244753642Sguido if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) 244853642Sguido continue; 2449145522Sdarrenr FR_VERBOSE((":i")); 2450145522Sdarrenr#endif 245192685Sdarrenr 2452145522Sdarrenr switch (fr->fr_type) 245353642Sguido { 2454145522Sdarrenr case FR_T_IPF : 2455255332Scy case FR_T_IPF_BUILTIN : 2456255332Scy if (ipf_check_ipf(fin, fr, portcmp)) 2457145522Sdarrenr continue; 2458145522Sdarrenr break; 2459145522Sdarrenr#if defined(IPFILTER_BPF) 2460145522Sdarrenr case FR_T_BPFOPC : 2461255332Scy case FR_T_BPFOPC_BUILTIN : 2462145522Sdarrenr { 2463145522Sdarrenr u_char *mc; 2464255332Scy int wlen; 246553642Sguido 2466145522Sdarrenr if (*fin->fin_mp == NULL) 246760850Sdarrenr continue; 2468255332Scy if (fin->fin_family != fr->fr_family) 246960850Sdarrenr continue; 2470145522Sdarrenr mc = (u_char *)fin->fin_m; 2471255332Scy wlen = fin->fin_dlen + fin->fin_hlen; 2472255332Scy if (!bpf_filter(fr->fr_data, mc, wlen, 0)) 247353642Sguido continue; 2474145522Sdarrenr break; 2475145522Sdarrenr } 2476145522Sdarrenr#endif 2477255332Scy case FR_T_CALLFUNC_BUILTIN : 2478145522Sdarrenr { 2479145522Sdarrenr frentry_t *f; 2480145522Sdarrenr 2481145522Sdarrenr f = (*fr->fr_func)(fin, &pass); 2482145522Sdarrenr if (f != NULL) 2483145522Sdarrenr fr = f; 2484145522Sdarrenr else 248560850Sdarrenr continue; 2486145522Sdarrenr break; 2487145522Sdarrenr } 2488255332Scy 2489255332Scy case FR_T_IPFEXPR : 2490255332Scy case FR_T_IPFEXPR_BUILTIN : 2491255332Scy if (fin->fin_family != fr->fr_family) 2492255332Scy continue; 2493255332Scy if (ipf_fr_matcharray(fin, fr->fr_data) == 0) 2494255332Scy continue; 2495255332Scy break; 2496255332Scy 2497145522Sdarrenr default : 2498145522Sdarrenr break; 249953642Sguido } 250053642Sguido 2501145522Sdarrenr if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { 2502145522Sdarrenr if (fin->fin_nattag == NULL) 250353642Sguido continue; 2504255332Scy if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) 250553642Sguido continue; 250653642Sguido } 2507255332Scy FR_VERBOSE(("=%d/%d.%d *", fr->fr_grhead, fr->fr_group, rulen)); 250892685Sdarrenr 2509145522Sdarrenr passt = fr->fr_flags; 2510145522Sdarrenr 2511145522Sdarrenr /* 2512145522Sdarrenr * If the rule is a "call now" rule, then call the function 2513145522Sdarrenr * in the rule, if it exists and use the results from that. 2514145522Sdarrenr * If the function pointer is bad, just make like we ignore 2515145522Sdarrenr * it, except for increasing the hit counter. 2516145522Sdarrenr */ 2517145522Sdarrenr if ((passt & FR_CALLNOW) != 0) { 2518161356Sguido frentry_t *frs; 2519161356Sguido 2520145522Sdarrenr ATOMIC_INC64(fr->fr_hits); 2521255332Scy if ((fr->fr_func == NULL) || 2522161356Sguido (fr->fr_func == (ipfunc_t)-1)) 2523161356Sguido continue; 2524145522Sdarrenr 2525161356Sguido frs = fin->fin_fr; 2526161356Sguido fin->fin_fr = fr; 2527161356Sguido fr = (*fr->fr_func)(fin, &passt); 2528161356Sguido if (fr == NULL) { 2529161356Sguido fin->fin_fr = frs; 2530161356Sguido continue; 2531145522Sdarrenr } 2532161356Sguido passt = fr->fr_flags; 253392685Sdarrenr } 2534161356Sguido fin->fin_fr = fr; 253592685Sdarrenr 253653642Sguido#ifdef IPFILTER_LOG 253792685Sdarrenr /* 253892685Sdarrenr * Just log this packet... 253992685Sdarrenr */ 254053642Sguido if ((passt & FR_LOGMASK) == FR_LOG) { 2541255332Scy if (ipf_log_pkt(fin, passt) == -1) { 2542145522Sdarrenr if (passt & FR_LOGORBLOCK) { 2543255332Scy DT(frb_logfail); 2544145522Sdarrenr passt &= ~FR_CMDMASK; 254560850Sdarrenr passt |= FR_BLOCK|FR_QUICK; 2546255332Scy fin->fin_reason = FRB_LOGFAIL; 2547145522Sdarrenr } 254853642Sguido } 254953642Sguido } 255053642Sguido#endif /* IPFILTER_LOG */ 2551255332Scy 2552255332Scy MUTEX_ENTER(&fr->fr_lock); 2553145522Sdarrenr fr->fr_bytes += (U_QUAD_T)fin->fin_plen; 2554255332Scy fr->fr_hits++; 2555255332Scy MUTEX_EXIT(&fr->fr_lock); 2556255332Scy fin->fin_rule = rulen; 2557255332Scy 2558153876Sguido passo = pass; 2559255332Scy if (FR_ISSKIP(passt)) { 2560145522Sdarrenr skip = fr->fr_arg; 2561255332Scy continue; 2562255332Scy } else if (((passt & FR_LOGMASK) != FR_LOG) && 2563255332Scy ((passt & FR_LOGMASK) != FR_DECAPSULATE)) { 2564145522Sdarrenr pass = passt; 2565255332Scy } 2566255332Scy 2567145522Sdarrenr if (passt & (FR_RETICMP|FR_FAKEICMP)) 256853642Sguido fin->fin_icode = fr->fr_icode; 2569255332Scy 2570255332Scy if (fr->fr_group != -1) { 2571255332Scy (void) strncpy(fin->fin_group, 2572255332Scy FR_NAME(fr, fr_group), 2573255332Scy strlen(FR_NAME(fr, fr_group))); 2574255332Scy } else { 2575255332Scy fin->fin_group[0] = '\0'; 2576255332Scy } 2577255332Scy 2578255332Scy FR_DEBUG(("pass %#x/%#x/%x\n", passo, pass, passt)); 2579255332Scy 2580255332Scy if (fr->fr_grphead != NULL) { 2581255332Scy fin->fin_fr = fr->fr_grphead->fg_start; 2582255332Scy FR_VERBOSE(("group %s\n", FR_NAME(fr, fr_grhead))); 2583255332Scy 2584255332Scy if (FR_ISDECAPS(passt)) 2585255332Scy passt = ipf_decaps(fin, pass, fr->fr_icode); 2586255332Scy else 2587255332Scy passt = ipf_scanlist(fin, pass); 2588255332Scy 258953642Sguido if (fin->fin_fr == NULL) { 259053642Sguido fin->fin_rule = rulen; 2591255332Scy if (fr->fr_group != -1) 2592255332Scy (void) strncpy(fin->fin_group, 2593255332Scy fr->fr_names + 2594255332Scy fr->fr_group, 2595255332Scy strlen(fr->fr_names + 2596255332Scy fr->fr_group)); 259753642Sguido fin->fin_fr = fr; 2598161356Sguido passt = pass; 259953642Sguido } 2600161356Sguido pass = passt; 260153642Sguido } 2602153876Sguido 2603255332Scy if (pass & FR_QUICK) { 2604153876Sguido /* 2605153876Sguido * Finally, if we've asked to track state for this 2606153876Sguido * packet, set it up. Add state for "quick" rules 2607153876Sguido * here so that if the action fails we can consider 2608153876Sguido * the rule to "not match" and keep on processing 2609153876Sguido * filter rules. 2610153876Sguido */ 2611255332Scy if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) && 2612153876Sguido !(fin->fin_flx & FI_STATE)) { 2613153876Sguido int out = fin->fin_out; 2614153876Sguido 2615161356Sguido fin->fin_fr = fr; 2616255332Scy if (ipf_state_add(softc, fin, NULL, 0) == 0) { 2617255332Scy LBUMPD(ipf_stats[out], fr_ads); 2618153876Sguido } else { 2619255332Scy LBUMPD(ipf_stats[out], fr_bads); 2620153876Sguido pass = passo; 2621153876Sguido continue; 2622153876Sguido } 2623153876Sguido } 262453642Sguido break; 2625153876Sguido } 262653642Sguido } 2627145522Sdarrenr fin->fin_depth--; 262853642Sguido return pass; 262953642Sguido} 263053642Sguido 263153642Sguido 2632145522Sdarrenr/* ------------------------------------------------------------------------ */ 2633255332Scy/* Function: ipf_acctpkt */ 2634145522Sdarrenr/* Returns: frentry_t* - always returns NULL */ 2635145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2636145522Sdarrenr/* passp(IO) - pointer to current/new filter decision (unused) */ 2637145522Sdarrenr/* */ 2638145522Sdarrenr/* Checks a packet against accounting rules, if there are any for the given */ 2639145522Sdarrenr/* IP protocol version. */ 2640145522Sdarrenr/* */ 2641145522Sdarrenr/* N.B.: this function returns NULL to match the prototype used by other */ 2642255332Scy/* functions called from the IPFilter "mainline" in ipf_check(). */ 2643145522Sdarrenr/* ------------------------------------------------------------------------ */ 2644255332Scyfrentry_t * 2645255332Scyipf_acctpkt(fin, passp) 2646255332Scy fr_info_t *fin; 2647255332Scy u_32_t *passp; 2648145522Sdarrenr{ 2649255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2650145522Sdarrenr char group[FR_GROUPLEN]; 2651145522Sdarrenr frentry_t *fr, *frsave; 2652145522Sdarrenr u_32_t pass, rulen; 2653145522Sdarrenr 2654145522Sdarrenr passp = passp; 2655255332Scy fr = softc->ipf_acct[fin->fin_out][softc->ipf_active]; 2656145522Sdarrenr 2657145522Sdarrenr if (fr != NULL) { 2658145522Sdarrenr frsave = fin->fin_fr; 2659145522Sdarrenr bcopy(fin->fin_group, group, FR_GROUPLEN); 2660145522Sdarrenr rulen = fin->fin_rule; 2661145522Sdarrenr fin->fin_fr = fr; 2662255332Scy pass = ipf_scanlist(fin, FR_NOMATCH); 2663145522Sdarrenr if (FR_ISACCOUNT(pass)) { 2664255332Scy LBUMPD(ipf_stats[0], fr_acct); 2665145522Sdarrenr } 2666145522Sdarrenr fin->fin_fr = frsave; 2667145522Sdarrenr bcopy(group, fin->fin_group, FR_GROUPLEN); 2668145522Sdarrenr fin->fin_rule = rulen; 2669145522Sdarrenr } 2670145522Sdarrenr return NULL; 2671145522Sdarrenr} 2672145522Sdarrenr 2673145522Sdarrenr 2674145522Sdarrenr/* ------------------------------------------------------------------------ */ 2675255332Scy/* Function: ipf_firewall */ 2676145522Sdarrenr/* Returns: frentry_t* - returns pointer to matched rule, if no matches */ 2677145522Sdarrenr/* were found, returns NULL. */ 2678145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 2679145522Sdarrenr/* passp(IO) - pointer to current/new filter decision (unused) */ 2680145522Sdarrenr/* */ 2681145522Sdarrenr/* Applies an appropriate set of firewall rules to the packet, to see if */ 2682145522Sdarrenr/* there are any matches. The first check is to see if a match can be seen */ 2683145522Sdarrenr/* in the cache. If not, then search an appropriate list of rules. Once a */ 2684145522Sdarrenr/* matching rule is found, take any appropriate actions as defined by the */ 2685145522Sdarrenr/* rule - except logging. */ 2686145522Sdarrenr/* ------------------------------------------------------------------------ */ 2687255332Scystatic frentry_t * 2688255332Scyipf_firewall(fin, passp) 2689255332Scy fr_info_t *fin; 2690255332Scy u_32_t *passp; 2691145522Sdarrenr{ 2692255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 2693145522Sdarrenr frentry_t *fr; 2694145522Sdarrenr u_32_t pass; 2695145522Sdarrenr int out; 2696145522Sdarrenr 2697145522Sdarrenr out = fin->fin_out; 2698145522Sdarrenr pass = *passp; 2699145522Sdarrenr 2700145522Sdarrenr /* 2701255332Scy * This rule cache will only affect packets that are not being 2702255332Scy * statefully filtered. 2703145522Sdarrenr */ 2704255332Scy fin->fin_fr = softc->ipf_rules[out][softc->ipf_active]; 2705255332Scy if (fin->fin_fr != NULL) 2706255332Scy pass = ipf_scanlist(fin, softc->ipf_pass); 2707153876Sguido 2708255332Scy if ((pass & FR_NOMATCH)) { 2709255332Scy LBUMPD(ipf_stats[out], fr_nom); 2710145522Sdarrenr } 2711255332Scy fr = fin->fin_fr; 2712145522Sdarrenr 2713145522Sdarrenr /* 2714145522Sdarrenr * Apply packets per second rate-limiting to a rule as required. 2715145522Sdarrenr */ 2716145522Sdarrenr if ((fr != NULL) && (fr->fr_pps != 0) && 2717145522Sdarrenr !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) { 2718255332Scy DT2(frb_ppsrate, fr_info_t *, fin, frentry_t *, fr); 2719255332Scy pass &= ~(FR_CMDMASK|FR_RETICMP|FR_RETRST); 2720145522Sdarrenr pass |= FR_BLOCK; 2721255332Scy LBUMPD(ipf_stats[out], fr_ppshit); 2722255332Scy fin->fin_reason = FRB_PPSRATE; 2723145522Sdarrenr } 2724145522Sdarrenr 2725145522Sdarrenr /* 2726145522Sdarrenr * If we fail to add a packet to the authorization queue, then we 2727145522Sdarrenr * drop the packet later. However, if it was added then pretend 2728145522Sdarrenr * we've dropped it already. 2729145522Sdarrenr */ 2730145522Sdarrenr if (FR_ISAUTH(pass)) { 2731255332Scy if (ipf_auth_new(fin->fin_m, fin) != 0) { 2732255332Scy DT1(frb_authnew, fr_info_t *, fin); 2733173931Sdarrenr fin->fin_m = *fin->fin_mp = NULL; 2734255332Scy fin->fin_reason = FRB_AUTHNEW; 2735145522Sdarrenr fin->fin_error = 0; 2736255332Scy } else { 2737255332Scy IPFERROR(1); 2738145522Sdarrenr fin->fin_error = ENOSPC; 2739255332Scy } 2740145522Sdarrenr } 2741145522Sdarrenr 2742145522Sdarrenr if ((fr != NULL) && (fr->fr_func != NULL) && 2743145522Sdarrenr (fr->fr_func != (ipfunc_t)-1) && !(pass & FR_CALLNOW)) 2744145522Sdarrenr (void) (*fr->fr_func)(fin, &pass); 2745145522Sdarrenr 2746145522Sdarrenr /* 2747145522Sdarrenr * If a rule is a pre-auth rule, check again in the list of rules 2748145522Sdarrenr * loaded for authenticated use. It does not particulary matter 2749145522Sdarrenr * if this search fails because a "preauth" result, from a rule, 2750145522Sdarrenr * is treated as "not a pass", hence the packet is blocked. 2751145522Sdarrenr */ 2752145522Sdarrenr if (FR_ISPREAUTH(pass)) { 2753255332Scy pass = ipf_auth_pre_scanlist(softc, fin, pass); 2754145522Sdarrenr } 2755145522Sdarrenr 2756145522Sdarrenr /* 2757145522Sdarrenr * If the rule has "keep frag" and the packet is actually a fragment, 2758145522Sdarrenr * then create a fragment state entry. 2759145522Sdarrenr */ 2760317434Scy if (pass & FR_KEEPFRAG) { 2761145522Sdarrenr if (fin->fin_flx & FI_FRAG) { 2762255332Scy if (ipf_frag_new(softc, fin, pass) == -1) { 2763255332Scy LBUMP(ipf_stats[out].fr_bnfr); 2764145522Sdarrenr } else { 2765255332Scy LBUMP(ipf_stats[out].fr_nfr); 2766145522Sdarrenr } 2767145522Sdarrenr } else { 2768255332Scy LBUMP(ipf_stats[out].fr_cfr); 2769145522Sdarrenr } 2770145522Sdarrenr } 2771145522Sdarrenr 2772145522Sdarrenr fr = fin->fin_fr; 2773255332Scy *passp = pass; 2774145522Sdarrenr 2775145522Sdarrenr return fr; 2776145522Sdarrenr} 2777145522Sdarrenr 2778145522Sdarrenr 2779145522Sdarrenr/* ------------------------------------------------------------------------ */ 2780255332Scy/* Function: ipf_check */ 2781145522Sdarrenr/* Returns: int - 0 == packet allowed through, */ 2782145522Sdarrenr/* User space: */ 2783145522Sdarrenr/* -1 == packet blocked */ 2784145522Sdarrenr/* 1 == packet not matched */ 2785153876Sguido/* -2 == requires authentication */ 2786145522Sdarrenr/* Kernel: */ 2787145522Sdarrenr/* > 0 == filter error # for packet */ 2788343691Scy/* Parameters: ctx(I) - pointer to the instance context */ 2789343691Scy/* ip(I) - pointer to start of IPv4/6 packet */ 2790145522Sdarrenr/* hlen(I) - length of header */ 2791145522Sdarrenr/* ifp(I) - pointer to interface this packet is on */ 2792145522Sdarrenr/* out(I) - 0 == packet going in, 1 == packet going out */ 2793145522Sdarrenr/* mp(IO) - pointer to caller's buffer pointer that holds this */ 2794145522Sdarrenr/* IP packet. */ 2795145522Sdarrenr/* Solaris & HP-UX ONLY : */ 2796145522Sdarrenr/* qpi(I) - pointer to STREAMS queue information for this */ 2797145522Sdarrenr/* interface & direction. */ 2798145522Sdarrenr/* */ 2799255332Scy/* ipf_check() is the master function for all IPFilter packet processing. */ 2800145522Sdarrenr/* It orchestrates: Network Address Translation (NAT), checking for packet */ 2801145522Sdarrenr/* authorisation (or pre-authorisation), presence of related state info., */ 2802145522Sdarrenr/* generating log entries, IP packet accounting, routing of packets as */ 2803145522Sdarrenr/* directed by firewall rules and of course whether or not to allow the */ 2804145522Sdarrenr/* packet to be further processed by the kernel. */ 2805145522Sdarrenr/* */ 2806145522Sdarrenr/* For packets blocked, the contents of "mp" will be NULL'd and the buffer */ 2807145522Sdarrenr/* freed. Packets passed may be returned with the pointer pointed to by */ 2808145522Sdarrenr/* by "mp" changed to a new buffer. */ 2809145522Sdarrenr/* ------------------------------------------------------------------------ */ 2810255332Scyint 2811255332Scyipf_check(ctx, ip, hlen, ifp, out 2812145522Sdarrenr#if defined(_KERNEL) && defined(MENTAT) 2813255332Scy , qif, mp) 2814255332Scy void *qif; 281553642Sguido#else 2816255332Scy , mp) 281753642Sguido#endif 2818255332Scy mb_t **mp; 2819255332Scy ip_t *ip; 2820255332Scy int hlen; 2821255332Scy void *ifp; 2822255332Scy int out; 2823255332Scy void *ctx; 282453642Sguido{ 282553642Sguido /* 282653642Sguido * The above really sucks, but short of writing a diff 282753642Sguido */ 2828255332Scy ipf_main_softc_t *softc = ctx; 2829145522Sdarrenr fr_info_t frinfo; 2830145522Sdarrenr fr_info_t *fin = &frinfo; 2831255332Scy u_32_t pass = softc->ipf_pass; 2832145522Sdarrenr frentry_t *fr = NULL; 2833145522Sdarrenr int v = IP_V(ip); 2834145522Sdarrenr mb_t *mc = NULL; 2835145522Sdarrenr mb_t *m; 2836130886Sdarrenr /* 2837255332Scy * The first part of ipf_check() deals with making sure that what goes 2838145522Sdarrenr * into the filtering engine makes some sense. Information about the 2839145522Sdarrenr * the packet is distilled, collected into a fr_info_t structure and 2840145522Sdarrenr * the an attempt to ensure the buffer the packet is in is big enough 2841145522Sdarrenr * to hold all the required packet headers. 2842130886Sdarrenr */ 2843145522Sdarrenr#ifdef _KERNEL 2844145522Sdarrenr# ifdef MENTAT 2845145522Sdarrenr qpktinfo_t *qpi = qif; 284653642Sguido 2847255332Scy# ifdef __sparc 2848145522Sdarrenr if ((u_int)ip & 0x3) 2849145522Sdarrenr return 2; 2850172776Sdarrenr# endif 2851161356Sguido# else 2852161356Sguido SPL_INT(s); 2853145522Sdarrenr# endif 2854110915Sdarrenr 2855255332Scy if (softc->ipf_running <= 0) { 2856145522Sdarrenr return 0; 2857110915Sdarrenr } 2858145522Sdarrenr 2859145522Sdarrenr bzero((char *)fin, sizeof(*fin)); 2860145522Sdarrenr 2861145522Sdarrenr# ifdef MENTAT 2862255332Scy if (qpi->qpi_flags & QF_BROADCAST) 2863255332Scy fin->fin_flx |= FI_MBCAST|FI_BROADCAST; 2864255332Scy if (qpi->qpi_flags & QF_MULTICAST) 2865255332Scy fin->fin_flx |= FI_MBCAST|FI_MULTICAST; 2866145522Sdarrenr m = qpi->qpi_m; 2867145522Sdarrenr fin->fin_qfm = m; 2868145522Sdarrenr fin->fin_qpi = qpi; 2869145522Sdarrenr# else /* MENTAT */ 2870145522Sdarrenr 2871145522Sdarrenr m = *mp; 2872145522Sdarrenr 2873145522Sdarrenr# if defined(M_MCAST) 2874145522Sdarrenr if ((m->m_flags & M_MCAST) != 0) 2875145522Sdarrenr fin->fin_flx |= FI_MBCAST|FI_MULTICAST; 2876110915Sdarrenr# endif 2877153876Sguido# if defined(M_MLOOP) 2878153876Sguido if ((m->m_flags & M_MLOOP) != 0) 2879153876Sguido fin->fin_flx |= FI_MBCAST|FI_MULTICAST; 2880153876Sguido# endif 2881145522Sdarrenr# if defined(M_BCAST) 2882145522Sdarrenr if ((m->m_flags & M_BCAST) != 0) 2883145522Sdarrenr fin->fin_flx |= FI_MBCAST|FI_BROADCAST; 2884145522Sdarrenr# endif 288553642Sguido# ifdef M_CANFASTFWD 288653642Sguido /* 288753642Sguido * XXX For now, IP Filter and fast-forwarding of cached flows 288853642Sguido * XXX are mutually exclusive. Eventually, IP Filter should 288953642Sguido * XXX get a "can-fast-forward" filter rule. 289053642Sguido */ 289153642Sguido m->m_flags &= ~M_CANFASTFWD; 289253642Sguido# endif /* M_CANFASTFWD */ 2893255332Scy# if defined(CSUM_DELAY_DATA) && (!defined(__FreeBSD_version) || \ 2894255332Scy (__FreeBSD_version < 501108)) 289560765Sjlemon /* 289660765Sjlemon * disable delayed checksums. 289760765Sjlemon */ 2898145522Sdarrenr if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { 289960765Sjlemon in_delayed_cksum(m); 290060765Sjlemon m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 290160765Sjlemon } 290260850Sdarrenr# endif /* CSUM_DELAY_DATA */ 2903145522Sdarrenr# endif /* MENTAT */ 2904145522Sdarrenr#else 2905145522Sdarrenr bzero((char *)fin, sizeof(*fin)); 2906145522Sdarrenr m = *mp; 2907255332Scy# if defined(M_MCAST) 2908255332Scy if ((m->m_flags & M_MCAST) != 0) 2909255332Scy fin->fin_flx |= FI_MBCAST|FI_MULTICAST; 2910255332Scy# endif 2911255332Scy# if defined(M_MLOOP) 2912255332Scy if ((m->m_flags & M_MLOOP) != 0) 2913255332Scy fin->fin_flx |= FI_MBCAST|FI_MULTICAST; 2914255332Scy# endif 2915255332Scy# if defined(M_BCAST) 2916255332Scy if ((m->m_flags & M_BCAST) != 0) 2917255332Scy fin->fin_flx |= FI_MBCAST|FI_BROADCAST; 2918255332Scy# endif 2919145522Sdarrenr#endif /* _KERNEL */ 292060850Sdarrenr 2921145522Sdarrenr fin->fin_v = v; 2922145522Sdarrenr fin->fin_m = m; 2923145522Sdarrenr fin->fin_ip = ip; 2924130886Sdarrenr fin->fin_mp = mp; 2925130886Sdarrenr fin->fin_out = out; 2926145522Sdarrenr fin->fin_ifp = ifp; 2927145522Sdarrenr fin->fin_error = ENETUNREACH; 2928153876Sguido fin->fin_hlen = (u_short)hlen; 2929145522Sdarrenr fin->fin_dp = (char *)ip + hlen; 2930255332Scy fin->fin_main_soft = softc; 2931130886Sdarrenr 2932145522Sdarrenr fin->fin_ipoff = (char *)ip - MTOD(m, char *); 293353642Sguido 2934153876Sguido SPL_NET(s); 2935153876Sguido 2936145522Sdarrenr#ifdef USE_INET6 2937145522Sdarrenr if (v == 6) { 2938255332Scy LBUMP(ipf_stats[out].fr_ipv6); 2939145522Sdarrenr /* 2940145522Sdarrenr * Jumbo grams are quite likely too big for internal buffer 2941145522Sdarrenr * structures to handle comfortably, for now, so just drop 2942145522Sdarrenr * them. 2943145522Sdarrenr */ 2944170268Sdarrenr if (((ip6_t *)ip)->ip6_plen == 0) { 2945255332Scy DT1(frb_jumbo, ip6_t *, (ip6_t *)ip); 2946145522Sdarrenr pass = FR_BLOCK|FR_NOMATCH; 2947255332Scy fin->fin_reason = FRB_JUMBO; 2948153876Sguido goto finished; 2949145522Sdarrenr } 2950255332Scy fin->fin_family = AF_INET6; 295153642Sguido } else 2952145522Sdarrenr#endif 2953145522Sdarrenr { 2954255332Scy fin->fin_family = AF_INET; 2955145522Sdarrenr } 295653642Sguido 2957255332Scy if (ipf_makefrip(hlen, ip, fin) == -1) { 2958255332Scy DT1(frb_makefrip, fr_info_t *, fin); 2959153876Sguido pass = FR_BLOCK|FR_NOMATCH; 2960255332Scy fin->fin_reason = FRB_MAKEFRIP; 2961145522Sdarrenr goto finished; 2962153876Sguido } 296364580Sdarrenr 2964145522Sdarrenr /* 2965145522Sdarrenr * For at least IPv6 packets, if a m_pullup() fails then this pointer 2966145522Sdarrenr * becomes NULL and so we have no packet to free. 2967145522Sdarrenr */ 2968145522Sdarrenr if (*fin->fin_mp == NULL) 2969145522Sdarrenr goto finished; 2970145522Sdarrenr 2971145522Sdarrenr if (!out) { 2972145522Sdarrenr if (v == 4) { 2973255332Scy if (softc->ipf_chksrc && !ipf_verifysrc(fin)) { 2974255332Scy LBUMPD(ipf_stats[0], fr_v4_badsrc); 2975145522Sdarrenr fin->fin_flx |= FI_BADSRC; 2976145522Sdarrenr } 2977255332Scy if (fin->fin_ip->ip_ttl < softc->ipf_minttl) { 2978255332Scy LBUMPD(ipf_stats[0], fr_v4_badttl); 2979145522Sdarrenr fin->fin_flx |= FI_LOWTTL; 2980145522Sdarrenr } 298172006Sdarrenr } 2982145522Sdarrenr#ifdef USE_INET6 2983145522Sdarrenr else if (v == 6) { 2984255332Scy if (((ip6_t *)ip)->ip6_hlim < softc->ipf_minttl) { 2985255332Scy LBUMPD(ipf_stats[0], fr_v6_badttl); 2986145522Sdarrenr fin->fin_flx |= FI_LOWTTL; 2987145522Sdarrenr } 298872006Sdarrenr } 2989145522Sdarrenr#endif 299072006Sdarrenr } 2991145522Sdarrenr 2992145522Sdarrenr if (fin->fin_flx & FI_SHORT) { 2993255332Scy LBUMPD(ipf_stats[out], fr_short); 299460850Sdarrenr } 299560850Sdarrenr 2996255332Scy READ_ENTER(&softc->ipf_mutex); 299753642Sguido 299853642Sguido if (!out) { 2999255332Scy switch (fin->fin_v) 3000255332Scy { 3001255332Scy case 4 : 3002255332Scy if (ipf_nat_checkin(fin, &pass) == -1) { 3003255332Scy goto filterdone; 3004255332Scy } 3005255332Scy break; 3006255332Scy#ifdef USE_INET6 3007255332Scy case 6 : 3008255332Scy if (ipf_nat6_checkin(fin, &pass) == -1) { 3009255332Scy goto filterdone; 3010255332Scy } 3011255332Scy break; 3012255332Scy#endif 3013255332Scy default : 3014255332Scy break; 301553642Sguido } 301653642Sguido } 3017255332Scy /* 3018255332Scy * Check auth now. 3019255332Scy * If a packet is found in the auth table, then skip checking 3020255332Scy * the access lists for permission but we do need to consider 3021255332Scy * the result as if it were from the ACL's. In addition, being 3022255332Scy * found in the auth table means it has been seen before, so do 3023255332Scy * not pass it through accounting (again), lest it be counted twice. 3024255332Scy */ 3025255332Scy fr = ipf_auth_check(fin, &pass); 3026255332Scy if (!out && (fr == NULL)) 3027255332Scy (void) ipf_acctpkt(fin, NULL); 302853642Sguido 3029172776Sdarrenr if (fr == NULL) { 3030255332Scy if ((fin->fin_flx & FI_FRAG) != 0) 3031255332Scy fr = ipf_frag_known(fin, &pass); 3032255332Scy 3033172776Sdarrenr if (fr == NULL) 3034255332Scy fr = ipf_state_check(fin, &pass); 3035172776Sdarrenr } 303680482Sdarrenr 3037145522Sdarrenr if ((pass & FR_NOMATCH) || (fr == NULL)) 3038255332Scy fr = ipf_firewall(fin, &pass); 303953642Sguido 3040161356Sguido /* 3041161356Sguido * If we've asked to track state for this packet, set it up. 3042255332Scy * Here rather than ipf_firewall because ipf_checkauth may decide 3043161356Sguido * to return a packet for "keep state" 3044161356Sguido */ 3045173931Sdarrenr if ((pass & FR_KEEPSTATE) && (fin->fin_m != NULL) && 3046173931Sdarrenr !(fin->fin_flx & FI_STATE)) { 3047255332Scy if (ipf_state_add(softc, fin, NULL, 0) == 0) { 3048255332Scy LBUMP(ipf_stats[out].fr_ads); 3049161356Sguido } else { 3050255332Scy LBUMP(ipf_stats[out].fr_bads); 3051161356Sguido if (FR_ISPASS(pass)) { 3052255332Scy DT(frb_stateadd); 3053161356Sguido pass &= ~FR_CMDMASK; 3054161356Sguido pass |= FR_BLOCK; 3055255332Scy fin->fin_reason = FRB_STATEADD; 3056161356Sguido } 3057161356Sguido } 3058161356Sguido } 3059161356Sguido 3060145522Sdarrenr fin->fin_fr = fr; 3061255332Scy if ((fr != NULL) && !(fin->fin_flx & FI_STATE)) { 3062255332Scy fin->fin_dif = &fr->fr_dif; 3063255332Scy fin->fin_tif = &fr->fr_tifs[fin->fin_rev]; 3064255332Scy } 306553642Sguido 306653642Sguido /* 306753642Sguido * Only count/translate packets which will be passed on, out the 306853642Sguido * interface. 306953642Sguido */ 3070145522Sdarrenr if (out && FR_ISPASS(pass)) { 3071255332Scy (void) ipf_acctpkt(fin, NULL); 307292685Sdarrenr 3073255332Scy switch (fin->fin_v) 3074255332Scy { 3075255332Scy case 4 : 3076255332Scy if (ipf_nat_checkout(fin, &pass) == -1) { 3077255332Scy ; 3078255332Scy } else if ((softc->ipf_update_ipid != 0) && (v == 4)) { 3079255332Scy if (ipf_updateipid(fin) == -1) { 3080255332Scy DT(frb_updateipid); 3081255332Scy LBUMP(ipf_stats[1].fr_ipud); 3082255332Scy pass &= ~FR_CMDMASK; 3083255332Scy pass |= FR_BLOCK; 3084255332Scy fin->fin_reason = FRB_UPDATEIPID; 3085255332Scy } else { 3086255332Scy LBUMP(ipf_stats[0].fr_ipud); 3087255332Scy } 308892685Sdarrenr } 3089255332Scy break; 3090255332Scy#ifdef USE_INET6 3091255332Scy case 6 : 3092255332Scy (void) ipf_nat6_checkout(fin, &pass); 3093255332Scy break; 3094255332Scy#endif 3095255332Scy default : 3096255332Scy break; 309753642Sguido } 3098145522Sdarrenr } 309953642Sguido 3100170268Sdarrenrfilterdone: 310153642Sguido#ifdef IPFILTER_LOG 3102255332Scy if ((softc->ipf_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { 3103255332Scy (void) ipf_dolog(fin, &pass); 310453642Sguido } 3105145522Sdarrenr#endif 310655929Sguido 3107170268Sdarrenr /* 3108255332Scy * The FI_STATE flag is cleared here so that calling ipf_state_check 3109170268Sdarrenr * will work when called from inside of fr_fastroute. Although 3110170268Sdarrenr * there is a similar flag, FI_NATED, for NAT, it does have the same 3111170268Sdarrenr * impact on code execution. 3112170268Sdarrenr */ 3113255332Scy fin->fin_flx &= ~FI_STATE; 3114145522Sdarrenr 3115255332Scy#if defined(FASTROUTE_RECURSION) 311653642Sguido /* 3117255332Scy * Up the reference on fr_lock and exit ipf_mutex. The generation of 3118255332Scy * a packet below can sometimes cause a recursive call into IPFilter. 3119255332Scy * On those platforms where that does happen, we need to hang onto 3120255332Scy * the filter rule just in case someone decides to remove or flush it 3121255332Scy * in the meantime. 312253642Sguido */ 3123153876Sguido if (fr != NULL) { 3124153876Sguido MUTEX_ENTER(&fr->fr_lock); 3125153876Sguido fr->fr_ref++; 3126153876Sguido MUTEX_EXIT(&fr->fr_lock); 3127145522Sdarrenr } 3128145522Sdarrenr 3129255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 3130255332Scy#endif 3131153876Sguido 3132161356Sguido if ((pass & FR_RETMASK) != 0) { 313353642Sguido /* 313453642Sguido * Should we return an ICMP packet to indicate error 313553642Sguido * status passing through the packet filter ? 313653642Sguido * WARNING: ICMP error packets AND TCP RST packets should 3137255332Scy * ONLY be sent in repsonse to incoming packets. Sending 3138255332Scy * them in response to outbound packets can result in a 3139255332Scy * panic on some operating systems. 314053642Sguido */ 314153642Sguido if (!out) { 3142145522Sdarrenr if (pass & FR_RETICMP) { 314360850Sdarrenr int dst; 314453642Sguido 314553642Sguido if ((pass & FR_RETMASK) == FR_FAKEICMP) 314660850Sdarrenr dst = 1; 314753642Sguido else 314860850Sdarrenr dst = 0; 3149255332Scy (void) ipf_send_icmp_err(ICMP_UNREACH, fin, 3150255332Scy dst); 3151255332Scy LBUMP(ipf_stats[0].fr_ret); 315253642Sguido } else if (((pass & FR_RETMASK) == FR_RETRST) && 3153145522Sdarrenr !(fin->fin_flx & FI_SHORT)) { 3154153876Sguido if (((fin->fin_flx & FI_OOW) != 0) || 3155255332Scy (ipf_send_reset(fin) == 0)) { 3156255332Scy LBUMP(ipf_stats[1].fr_ret); 315753642Sguido } 315853642Sguido } 3159161356Sguido 3160161356Sguido /* 3161161356Sguido * When using return-* with auth rules, the auth code 3162161356Sguido * takes over disposing of this packet. 3163161356Sguido */ 3164161356Sguido if (FR_ISAUTH(pass) && (fin->fin_m != NULL)) { 3165255332Scy DT1(frb_authcapture, fr_info_t *, fin); 3166161356Sguido fin->fin_m = *fin->fin_mp = NULL; 3167255332Scy fin->fin_reason = FRB_AUTHCAPTURE; 3168255332Scy m = NULL; 3169161356Sguido } 317053642Sguido } else { 3171255332Scy if (pass & FR_RETRST) { 3172145522Sdarrenr fin->fin_error = ECONNRESET; 3173255332Scy } 317453642Sguido } 317553642Sguido } 317653642Sguido 317753642Sguido /* 3178255332Scy * After the above so that ICMP unreachables and TCP RSTs get 3179255332Scy * created properly. 3180255332Scy */ 3181255332Scy if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) 3182255332Scy ipf_nat_uncreate(fin); 3183255332Scy 3184255332Scy /* 318553642Sguido * If we didn't drop off the bottom of the list of rules (and thus 318653642Sguido * the 'current' rule fr is not NULL), then we may have some extra 318753642Sguido * instructions about what to do with a packet. 318853642Sguido * Once we're finished return to our caller, freeing the packet if 3189255332Scy * we are dropping it. 319053642Sguido */ 3191145522Sdarrenr if (fr != NULL) { 3192145522Sdarrenr frdest_t *fdp; 3193145522Sdarrenr 3194255332Scy /* 3195255332Scy * Generate a duplicated packet first because ipf_fastroute 3196255332Scy * can lead to fin_m being free'd... not good. 3197255332Scy */ 3198255332Scy fdp = fin->fin_dif; 3199255332Scy if ((fdp != NULL) && (fdp->fd_ptr != NULL) && 3200255332Scy (fdp->fd_ptr != (void *)-1)) { 3201255332Scy mc = M_COPY(fin->fin_m); 3202255332Scy if (mc != NULL) 3203255332Scy ipf_fastroute(mc, &mc, fin, fdp); 3204255332Scy } 3205145522Sdarrenr 3206255332Scy fdp = fin->fin_tif; 3207145522Sdarrenr if (!out && (pass & FR_FASTROUTE)) { 3208145522Sdarrenr /* 3209255332Scy * For fastroute rule, no destination interface defined 3210145522Sdarrenr * so pass NULL as the frdest_t parameter 3211145522Sdarrenr */ 3212255332Scy (void) ipf_fastroute(fin->fin_m, mp, fin, NULL); 3213145522Sdarrenr m = *mp = NULL; 3214255332Scy } else if ((fdp != NULL) && (fdp->fd_ptr != NULL) && 3215255332Scy (fdp->fd_ptr != (struct ifnet *)-1)) { 3216145522Sdarrenr /* this is for to rules: */ 3217255332Scy ipf_fastroute(fin->fin_m, mp, fin, fdp); 3218145522Sdarrenr m = *mp = NULL; 321992685Sdarrenr } 322080482Sdarrenr 3221255332Scy#if defined(FASTROUTE_RECURSION) 3222255332Scy (void) ipf_derefrule(softc, &fr); 3223255332Scy#endif 322453642Sguido } 3225255332Scy#if !defined(FASTROUTE_RECURSION) 3226255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 3227255332Scy#endif 322880482Sdarrenr 3229145522Sdarrenrfinished: 3230145522Sdarrenr if (!FR_ISPASS(pass)) { 3231255332Scy LBUMP(ipf_stats[out].fr_block); 3232145522Sdarrenr if (*mp != NULL) { 3233255332Scy#ifdef _KERNEL 3234145522Sdarrenr FREE_MB_T(*mp); 3235255332Scy#endif 3236145522Sdarrenr m = *mp = NULL; 3237145522Sdarrenr } 3238145522Sdarrenr } else { 3239255332Scy LBUMP(ipf_stats[out].fr_pass); 3240145522Sdarrenr#if defined(_KERNEL) && defined(__sgi) 3241145522Sdarrenr if ((fin->fin_hbuf != NULL) && 3242145522Sdarrenr (mtod(fin->fin_m, struct ip *) != fin->fin_ip)) { 3243153876Sguido COPYBACK(fin->fin_m, 0, fin->fin_plen, fin->fin_hbuf); 3244145522Sdarrenr } 3245145522Sdarrenr#endif 324680482Sdarrenr } 324753642Sguido 3248153876Sguido SPL_X(s); 3249153876Sguido 3250145522Sdarrenr#ifdef _KERNEL 3251255332Scy if (FR_ISPASS(pass)) 3252255332Scy return 0; 3253255332Scy LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); 3254255332Scy return fin->fin_error; 325553642Sguido#else /* _KERNEL */ 3256255332Scy if (*mp != NULL) 3257255332Scy (*mp)->mb_ifp = fin->fin_ifp; 3258255332Scy blockreason = fin->fin_reason; 3259145522Sdarrenr FR_VERBOSE(("fin_flx %#x pass %#x ", fin->fin_flx, pass)); 3260255332Scy /*if ((pass & FR_CMDMASK) == (softc->ipf_pass & FR_CMDMASK))*/ 3261255332Scy if ((pass & FR_NOMATCH) != 0) 3262255332Scy return 1; 3263145522Sdarrenr 3264145522Sdarrenr if ((pass & FR_RETMASK) != 0) 3265145522Sdarrenr switch (pass & FR_RETMASK) 3266145522Sdarrenr { 3267145522Sdarrenr case FR_RETRST : 3268145522Sdarrenr return 3; 3269145522Sdarrenr case FR_RETICMP : 3270145522Sdarrenr return 4; 3271145522Sdarrenr case FR_FAKEICMP : 3272145522Sdarrenr return 5; 3273145522Sdarrenr } 3274145522Sdarrenr 3275145522Sdarrenr switch (pass & FR_CMDMASK) 3276145522Sdarrenr { 3277145522Sdarrenr case FR_PASS : 327853642Sguido return 0; 3279145522Sdarrenr case FR_BLOCK : 3280145522Sdarrenr return -1; 3281145522Sdarrenr case FR_AUTH : 328253642Sguido return -2; 3283145522Sdarrenr case FR_ACCOUNT : 328492685Sdarrenr return -3; 3285145522Sdarrenr case FR_PREAUTH : 328692685Sdarrenr return -4; 3287145522Sdarrenr } 3288145522Sdarrenr return 2; 328953642Sguido#endif /* _KERNEL */ 329053642Sguido} 329153642Sguido 329253642Sguido 3293145522Sdarrenr#ifdef IPFILTER_LOG 3294145522Sdarrenr/* ------------------------------------------------------------------------ */ 3295255332Scy/* Function: ipf_dolog */ 3296145522Sdarrenr/* Returns: frentry_t* - returns contents of fin_fr (no change made) */ 3297145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 3298145522Sdarrenr/* passp(IO) - pointer to current/new filter decision (unused) */ 3299145522Sdarrenr/* */ 3300145522Sdarrenr/* Checks flags set to see how a packet should be logged, if it is to be */ 3301145522Sdarrenr/* logged. Adjust statistics based on its success or not. */ 3302145522Sdarrenr/* ------------------------------------------------------------------------ */ 3303255332Scyfrentry_t * 3304255332Scyipf_dolog(fin, passp) 3305255332Scy fr_info_t *fin; 3306255332Scy u_32_t *passp; 3307145522Sdarrenr{ 3308255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 3309145522Sdarrenr u_32_t pass; 3310145522Sdarrenr int out; 3311145522Sdarrenr 3312145522Sdarrenr out = fin->fin_out; 3313145522Sdarrenr pass = *passp; 3314145522Sdarrenr 3315255332Scy if ((softc->ipf_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { 3316145522Sdarrenr pass |= FF_LOGNOMATCH; 3317255332Scy LBUMPD(ipf_stats[out], fr_npkl); 3318145522Sdarrenr goto logit; 3319255332Scy 3320145522Sdarrenr } else if (((pass & FR_LOGMASK) == FR_LOGP) || 3321255332Scy (FR_ISPASS(pass) && (softc->ipf_flags & FF_LOGPASS))) { 3322145522Sdarrenr if ((pass & FR_LOGMASK) != FR_LOGP) 3323145522Sdarrenr pass |= FF_LOGPASS; 3324255332Scy LBUMPD(ipf_stats[out], fr_ppkl); 3325145522Sdarrenr goto logit; 3326255332Scy 3327145522Sdarrenr } else if (((pass & FR_LOGMASK) == FR_LOGB) || 3328255332Scy (FR_ISBLOCK(pass) && (softc->ipf_flags & FF_LOGBLOCK))) { 3329145522Sdarrenr if ((pass & FR_LOGMASK) != FR_LOGB) 3330145522Sdarrenr pass |= FF_LOGBLOCK; 3331255332Scy LBUMPD(ipf_stats[out], fr_bpkl); 3332255332Scy 3333145522Sdarrenrlogit: 3334255332Scy if (ipf_log_pkt(fin, pass) == -1) { 3335145522Sdarrenr /* 3336145522Sdarrenr * If the "or-block" option has been used then 3337145522Sdarrenr * block the packet if we failed to log it. 3338145522Sdarrenr */ 3339255332Scy if ((pass & FR_LOGORBLOCK) && FR_ISPASS(pass)) { 3340255332Scy DT1(frb_logfail2, u_int, pass); 3341145522Sdarrenr pass &= ~FR_CMDMASK; 3342145522Sdarrenr pass |= FR_BLOCK; 3343255332Scy fin->fin_reason = FRB_LOGFAIL2; 3344145522Sdarrenr } 3345145522Sdarrenr } 3346145522Sdarrenr *passp = pass; 3347145522Sdarrenr } 3348145522Sdarrenr 3349145522Sdarrenr return fin->fin_fr; 3350145522Sdarrenr} 3351145522Sdarrenr#endif /* IPFILTER_LOG */ 3352145522Sdarrenr 3353145522Sdarrenr 3354145522Sdarrenr/* ------------------------------------------------------------------------ */ 3355145522Sdarrenr/* Function: ipf_cksum */ 3356145522Sdarrenr/* Returns: u_short - IP header checksum */ 3357145522Sdarrenr/* Parameters: addr(I) - pointer to start of buffer to checksum */ 3358145522Sdarrenr/* len(I) - length of buffer in bytes */ 3359145522Sdarrenr/* */ 3360145522Sdarrenr/* Calculate the two's complement 16 bit checksum of the buffer passed. */ 3361145522Sdarrenr/* */ 3362145522Sdarrenr/* N.B.: addr should be 16bit aligned. */ 3363145522Sdarrenr/* ------------------------------------------------------------------------ */ 3364255332Scyu_short 3365255332Scyipf_cksum(addr, len) 3366255332Scy u_short *addr; 3367255332Scy int len; 336853642Sguido{ 3369145522Sdarrenr u_32_t sum = 0; 337053642Sguido 337153642Sguido for (sum = 0; len > 1; len -= 2) 337253642Sguido sum += *addr++; 337353642Sguido 337453642Sguido /* mop up an odd byte, if necessary */ 337553642Sguido if (len == 1) 337653642Sguido sum += *(u_char *)addr; 337753642Sguido 337853642Sguido /* 337953642Sguido * add back carry outs from top 16 bits to low 16 bits 338053642Sguido */ 338153642Sguido sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 338253642Sguido sum += (sum >> 16); /* add carry */ 338353642Sguido return (u_short)(~sum); 338453642Sguido} 338553642Sguido 338653642Sguido 3387145522Sdarrenr/* ------------------------------------------------------------------------ */ 3388145522Sdarrenr/* Function: fr_cksum */ 3389145522Sdarrenr/* Returns: u_short - layer 4 checksum */ 3390255332Scy/* Parameters: fin(I) - pointer to packet information */ 3391145522Sdarrenr/* ip(I) - pointer to IP header */ 3392145522Sdarrenr/* l4proto(I) - protocol to caclulate checksum for */ 3393145522Sdarrenr/* l4hdr(I) - pointer to layer 4 header */ 3394145522Sdarrenr/* */ 3395145522Sdarrenr/* Calculates the TCP checksum for the packet held in "m", using the data */ 3396145522Sdarrenr/* in the IP header "ip" to seed it. */ 3397145522Sdarrenr/* */ 3398145522Sdarrenr/* NB: This function assumes we've pullup'd enough for all of the IP header */ 3399145522Sdarrenr/* and the TCP header. We also assume that data blocks aren't allocated in */ 3400145522Sdarrenr/* odd sizes. */ 3401145522Sdarrenr/* */ 3402255332Scy/* Expects ip_len and ip_off to be in network byte order when called. */ 3403145522Sdarrenr/* ------------------------------------------------------------------------ */ 3404255332Scyu_short 3405255332Scyfr_cksum(fin, ip, l4proto, l4hdr) 3406255332Scy fr_info_t *fin; 3407255332Scy ip_t *ip; 3408255332Scy int l4proto; 3409255332Scy void *l4hdr; 341053642Sguido{ 3411255332Scy u_short *sp, slen, sumsave, *csump; 341253642Sguido u_int sum, sum2; 341353642Sguido int hlen; 3414255332Scy int off; 3415145522Sdarrenr#ifdef USE_INET6 3416145522Sdarrenr ip6_t *ip6; 3417145522Sdarrenr#endif 341853642Sguido 3419145522Sdarrenr csump = NULL; 3420145522Sdarrenr sumsave = 0; 3421145522Sdarrenr sp = NULL; 3422145522Sdarrenr slen = 0; 3423145522Sdarrenr hlen = 0; 3424145522Sdarrenr sum = 0; 3425145522Sdarrenr 3426255332Scy sum = htons((u_short)l4proto); 342753642Sguido /* 342853642Sguido * Add up IP Header portion 342953642Sguido */ 3430145522Sdarrenr#ifdef USE_INET6 3431145522Sdarrenr if (IP_V(ip) == 4) { 3432145522Sdarrenr#endif 3433145522Sdarrenr hlen = IP_HL(ip) << 2; 3434255332Scy off = hlen; 3435145522Sdarrenr sp = (u_short *)&ip->ip_src; 3436145522Sdarrenr sum += *sp++; /* ip_src */ 3437145522Sdarrenr sum += *sp++; 3438145522Sdarrenr sum += *sp++; /* ip_dst */ 3439145522Sdarrenr sum += *sp++; 3440349927Scy slen = fin->fin_plen - off; 3441349927Scy sum += htons(slen); 3442145522Sdarrenr#ifdef USE_INET6 3443145522Sdarrenr } else if (IP_V(ip) == 6) { 3444349927Scy mb_t *m; 3445349927Scy 3446349927Scy m = fin->fin_m; 3447145522Sdarrenr ip6 = (ip6_t *)ip; 3448349927Scy off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr); 3449349927Scy int len = ntohs(ip6->ip6_plen) - (off - sizeof(*ip6)); 3450349927Scy return(ipf_pcksum6(fin, ip6, off, len)); 3451255332Scy } else { 3452255332Scy return 0xffff; 3453145522Sdarrenr } 3454145522Sdarrenr#endif 3455145522Sdarrenr 3456145522Sdarrenr switch (l4proto) 3457145522Sdarrenr { 3458145522Sdarrenr case IPPROTO_UDP : 3459145522Sdarrenr csump = &((udphdr_t *)l4hdr)->uh_sum; 3460145522Sdarrenr break; 3461145522Sdarrenr 3462145522Sdarrenr case IPPROTO_TCP : 3463145522Sdarrenr csump = &((tcphdr_t *)l4hdr)->th_sum; 3464145522Sdarrenr break; 3465145522Sdarrenr case IPPROTO_ICMP : 3466145522Sdarrenr csump = &((icmphdr_t *)l4hdr)->icmp_cksum; 3467255332Scy sum = 0; /* Pseudo-checksum is not included */ 3468145522Sdarrenr break; 3469255332Scy#ifdef USE_INET6 3470255332Scy case IPPROTO_ICMPV6 : 3471255332Scy csump = &((struct icmp6_hdr *)l4hdr)->icmp6_cksum; 3472255332Scy break; 3473255332Scy#endif 3474145522Sdarrenr default : 3475145522Sdarrenr break; 3476145522Sdarrenr } 3477145522Sdarrenr 3478145522Sdarrenr if (csump != NULL) { 3479145522Sdarrenr sumsave = *csump; 3480145522Sdarrenr *csump = 0; 3481145522Sdarrenr } 3482145522Sdarrenr 3483255332Scy sum2 = ipf_pcksum(fin, off, sum); 3484145522Sdarrenr if (csump != NULL) 3485145522Sdarrenr *csump = sumsave; 348653642Sguido return sum2; 348753642Sguido} 348853642Sguido 348953642Sguido 3490145522Sdarrenr/* ------------------------------------------------------------------------ */ 3491255332Scy/* Function: ipf_findgroup */ 3492145522Sdarrenr/* Returns: frgroup_t * - NULL = group not found, else pointer to group */ 3493255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3494255332Scy/* group(I) - group name to search for */ 3495145522Sdarrenr/* unit(I) - device to which this group belongs */ 3496145522Sdarrenr/* set(I) - which set of rules (inactive/inactive) this is */ 3497145522Sdarrenr/* fgpp(O) - pointer to place to store pointer to the pointer */ 3498145522Sdarrenr/* to where to add the next (last) group or where */ 3499145522Sdarrenr/* to delete group from. */ 3500145522Sdarrenr/* */ 3501145522Sdarrenr/* Search amongst the defined groups for a particular group number. */ 3502145522Sdarrenr/* ------------------------------------------------------------------------ */ 3503255332Scyfrgroup_t * 3504255332Scyipf_findgroup(softc, group, unit, set, fgpp) 3505255332Scy ipf_main_softc_t *softc; 3506255332Scy char *group; 3507255332Scy minor_t unit; 3508255332Scy int set; 3509255332Scy frgroup_t ***fgpp; 351053642Sguido{ 351153642Sguido frgroup_t *fg, **fgp; 351253642Sguido 3513145522Sdarrenr /* 3514153876Sguido * Which list of groups to search in is dependent on which list of 3515145522Sdarrenr * rules are being operated on. 3516145522Sdarrenr */ 3517255332Scy fgp = &softc->ipf_groups[unit][set]; 351853642Sguido 3519145522Sdarrenr while ((fg = *fgp) != NULL) { 3520145522Sdarrenr if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0) 352153642Sguido break; 352253642Sguido else 352353642Sguido fgp = &fg->fg_next; 3524145522Sdarrenr } 3525145522Sdarrenr if (fgpp != NULL) 352653642Sguido *fgpp = fgp; 352753642Sguido return fg; 352853642Sguido} 352953642Sguido 353053642Sguido 3531145522Sdarrenr/* ------------------------------------------------------------------------ */ 3532255332Scy/* Function: ipf_group_add */ 3533145522Sdarrenr/* Returns: frgroup_t * - NULL == did not create group, */ 3534145522Sdarrenr/* != NULL == pointer to the group */ 3535255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3536255332Scy/* num(I) - group number to add */ 3537145522Sdarrenr/* head(I) - rule pointer that is using this as the head */ 3538145522Sdarrenr/* flags(I) - rule flags which describe the type of rule it is */ 3539145522Sdarrenr/* unit(I) - device to which this group will belong to */ 3540145522Sdarrenr/* set(I) - which set of rules (inactive/inactive) this is */ 3541145522Sdarrenr/* Write Locks: ipf_mutex */ 3542145522Sdarrenr/* */ 3543145522Sdarrenr/* Add a new group head, or if it already exists, increase the reference */ 3544145522Sdarrenr/* count to it. */ 3545145522Sdarrenr/* ------------------------------------------------------------------------ */ 3546255332Scyfrgroup_t * 3547255332Scyipf_group_add(softc, group, head, flags, unit, set) 3548255332Scy ipf_main_softc_t *softc; 3549255332Scy char *group; 3550255332Scy void *head; 3551255332Scy u_32_t flags; 3552255332Scy minor_t unit; 3553255332Scy int set; 355453642Sguido{ 355553642Sguido frgroup_t *fg, **fgp; 3556145522Sdarrenr u_32_t gflags; 355753642Sguido 3558145522Sdarrenr if (group == NULL) 3559145522Sdarrenr return NULL; 3560145522Sdarrenr 3561145522Sdarrenr if (unit == IPL_LOGIPF && *group == '\0') 3562145522Sdarrenr return NULL; 3563145522Sdarrenr 3564145522Sdarrenr fgp = NULL; 3565145522Sdarrenr gflags = flags & FR_INOUT; 3566145522Sdarrenr 3567255332Scy fg = ipf_findgroup(softc, group, unit, set, &fgp); 3568145522Sdarrenr if (fg != NULL) { 3569255332Scy if (fg->fg_head == NULL && head != NULL) 3570255332Scy fg->fg_head = head; 3571145522Sdarrenr if (fg->fg_flags == 0) 3572145522Sdarrenr fg->fg_flags = gflags; 3573145522Sdarrenr else if (gflags != fg->fg_flags) 3574145522Sdarrenr return NULL; 3575145522Sdarrenr fg->fg_ref++; 357653642Sguido return fg; 3577145522Sdarrenr } 3578255332Scy 357953642Sguido KMALLOC(fg, frgroup_t *); 3580145522Sdarrenr if (fg != NULL) { 3581145522Sdarrenr fg->fg_head = head; 3582145522Sdarrenr fg->fg_start = NULL; 358353642Sguido fg->fg_next = *fgp; 3584255332Scy bcopy(group, fg->fg_name, strlen(group) + 1); 3585145522Sdarrenr fg->fg_flags = gflags; 3586145522Sdarrenr fg->fg_ref = 1; 3587255332Scy fg->fg_set = &softc->ipf_groups[unit][set]; 358853642Sguido *fgp = fg; 358953642Sguido } 359053642Sguido return fg; 359153642Sguido} 359253642Sguido 359353642Sguido 3594145522Sdarrenr/* ------------------------------------------------------------------------ */ 3595255332Scy/* Function: ipf_group_del */ 3596255332Scy/* Returns: int - number of rules deleted */ 3597255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3598255332Scy/* group(I) - group name to delete */ 3599255332Scy/* fr(I) - filter rule from which group is referenced */ 3600145522Sdarrenr/* Write Locks: ipf_mutex */ 3601145522Sdarrenr/* */ 3602255332Scy/* This function is called whenever a reference to a group is to be dropped */ 3603255332Scy/* and thus its reference count needs to be lowered and the group free'd if */ 3604255332Scy/* the reference count reaches zero. Passing in fr is really for the sole */ 3605255332Scy/* purpose of knowing when the head rule is being deleted. */ 3606145522Sdarrenr/* ------------------------------------------------------------------------ */ 3607255332Scyvoid 3608255332Scyipf_group_del(softc, group, fr) 3609255332Scy ipf_main_softc_t *softc; 3610255332Scy frgroup_t *group; 3611255332Scy frentry_t *fr; 361253642Sguido{ 3613145522Sdarrenr 3614255332Scy if (group->fg_head == fr) 3615255332Scy group->fg_head = NULL; 3616145522Sdarrenr 3617255332Scy group->fg_ref--; 3618255332Scy if ((group->fg_ref == 0) && (group->fg_start == NULL)) 3619255332Scy ipf_group_free(group); 3620255332Scy} 3621255332Scy 3622255332Scy 3623255332Scy/* ------------------------------------------------------------------------ */ 3624255332Scy/* Function: ipf_group_free */ 3625255332Scy/* Returns: Nil */ 3626255332Scy/* Parameters: group(I) - pointer to filter rule group */ 3627255332Scy/* */ 3628255332Scy/* Remove the group from the list of groups and free it. */ 3629255332Scy/* ------------------------------------------------------------------------ */ 3630255332Scystatic void 3631255332Scyipf_group_free(group) 3632255332Scy frgroup_t *group; 3633255332Scy{ 3634255332Scy frgroup_t **gp; 3635255332Scy 3636255332Scy for (gp = group->fg_set; *gp != NULL; gp = &(*gp)->fg_next) { 3637255332Scy if (*gp == group) { 3638255332Scy *gp = group->fg_next; 3639255332Scy break; 3640255332Scy } 3641145522Sdarrenr } 3642255332Scy KFREE(group); 364353642Sguido} 364453642Sguido 364553642Sguido 3646145522Sdarrenr/* ------------------------------------------------------------------------ */ 3647255332Scy/* Function: ipf_group_flush */ 3648255332Scy/* Returns: int - number of rules flush from group */ 3649255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3650255332Scy/* Parameters: group(I) - pointer to filter rule group */ 3651255332Scy/* */ 3652255332Scy/* Remove all of the rules that currently are listed under the given group. */ 3653255332Scy/* ------------------------------------------------------------------------ */ 3654255332Scystatic int 3655255332Scyipf_group_flush(softc, group) 3656255332Scy ipf_main_softc_t *softc; 3657255332Scy frgroup_t *group; 3658255332Scy{ 3659255332Scy int gone = 0; 3660255332Scy 3661255332Scy (void) ipf_flushlist(softc, &gone, &group->fg_start); 3662255332Scy 3663255332Scy return gone; 3664255332Scy} 3665255332Scy 3666255332Scy 3667255332Scy/* ------------------------------------------------------------------------ */ 3668255332Scy/* Function: ipf_getrulen */ 3669145522Sdarrenr/* Returns: frentry_t * - NULL == not found, else pointer to rule n */ 3670255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3671145522Sdarrenr/* Parameters: unit(I) - device for which to count the rule's number */ 3672145522Sdarrenr/* flags(I) - which set of rules to find the rule in */ 3673145522Sdarrenr/* group(I) - group name */ 3674145522Sdarrenr/* n(I) - rule number to find */ 3675145522Sdarrenr/* */ 3676145522Sdarrenr/* Find rule # n in group # g and return a pointer to it. Return NULl if */ 3677145522Sdarrenr/* group # g doesn't exist or there are less than n rules in the group. */ 3678145522Sdarrenr/* ------------------------------------------------------------------------ */ 3679255332Scyfrentry_t * 3680255332Scyipf_getrulen(softc, unit, group, n) 3681255332Scy ipf_main_softc_t *softc; 3682255332Scy int unit; 3683255332Scy char *group; 3684255332Scy u_32_t n; 3685145522Sdarrenr{ 3686145522Sdarrenr frentry_t *fr; 3687145522Sdarrenr frgroup_t *fg; 368853642Sguido 3689255332Scy fg = ipf_findgroup(softc, group, unit, softc->ipf_active, NULL); 3690145522Sdarrenr if (fg == NULL) 3691145522Sdarrenr return NULL; 3692255332Scy for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--) 3693145522Sdarrenr ; 3694145522Sdarrenr if (n != 0) 3695145522Sdarrenr return NULL; 3696145522Sdarrenr return fr; 3697145522Sdarrenr} 3698145522Sdarrenr 3699145522Sdarrenr 3700145522Sdarrenr/* ------------------------------------------------------------------------ */ 3701255332Scy/* Function: ipf_flushlist */ 3702145522Sdarrenr/* Returns: int - >= 0 - number of flushed rules */ 3703255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3704145522Sdarrenr/* nfreedp(O) - pointer to int where flush count is stored */ 3705145522Sdarrenr/* listp(I) - pointer to list to flush pointer */ 3706145522Sdarrenr/* Write Locks: ipf_mutex */ 3707145522Sdarrenr/* */ 3708145522Sdarrenr/* Recursively flush rules from the list, descending groups as they are */ 3709145522Sdarrenr/* encountered. if a rule is the head of a group and it has lost all its */ 3710145522Sdarrenr/* group members, then also delete the group reference. nfreedp is needed */ 3711145522Sdarrenr/* to store the accumulating count of rules removed, whereas the returned */ 3712145522Sdarrenr/* value is just the number removed from the current list. The latter is */ 3713145522Sdarrenr/* needed to correctly adjust reference counts on rules that define groups. */ 3714145522Sdarrenr/* */ 3715145522Sdarrenr/* NOTE: Rules not loaded from user space cannot be flushed. */ 3716145522Sdarrenr/* ------------------------------------------------------------------------ */ 3717255332Scystatic int 3718255332Scyipf_flushlist(softc, nfreedp, listp) 3719255332Scy ipf_main_softc_t *softc; 3720255332Scy int *nfreedp; 3721255332Scy frentry_t **listp; 372253642Sguido{ 3723172776Sdarrenr int freed = 0; 3724145522Sdarrenr frentry_t *fp; 372553642Sguido 3726145522Sdarrenr while ((fp = *listp) != NULL) { 3727145522Sdarrenr if ((fp->fr_type & FR_T_BUILTIN) || 3728145522Sdarrenr !(fp->fr_flags & FR_COPIED)) { 3729145522Sdarrenr listp = &fp->fr_next; 3730145522Sdarrenr continue; 3731145522Sdarrenr } 373253642Sguido *listp = fp->fr_next; 3733255332Scy if (fp->fr_next != NULL) 3734255332Scy fp->fr_next->fr_pnext = fp->fr_pnext; 3735255332Scy fp->fr_pnext = NULL; 3736255332Scy 3737255332Scy if (fp->fr_grphead != NULL) { 3738255332Scy freed += ipf_group_flush(softc, fp->fr_grphead); 3739255332Scy fp->fr_names[fp->fr_grhead] = '\0'; 374053642Sguido } 374153642Sguido 3742255332Scy if (fp->fr_icmpgrp != NULL) { 3743255332Scy freed += ipf_group_flush(softc, fp->fr_icmpgrp); 3744255332Scy fp->fr_names[fp->fr_icmphead] = '\0'; 374555929Sguido } 3746145522Sdarrenr 3747255332Scy if (fp->fr_srctrack.ht_max_nodes) 3748255332Scy ipf_rb_ht_flush(&fp->fr_srctrack); 3749255332Scy 3750255332Scy fp->fr_next = NULL; 3751255332Scy 3752145522Sdarrenr ASSERT(fp->fr_ref > 0); 3753255332Scy if (ipf_derefrule(softc, &fp) == 0) 375455929Sguido freed++; 375553642Sguido } 375653642Sguido *nfreedp += freed; 375753642Sguido return freed; 375853642Sguido} 375953642Sguido 376053642Sguido 3761145522Sdarrenr/* ------------------------------------------------------------------------ */ 3762255332Scy/* Function: ipf_flush */ 3763145522Sdarrenr/* Returns: int - >= 0 - number of flushed rules */ 3764255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 3765255332Scy/* unit(I) - device for which to flush rules */ 3766145522Sdarrenr/* flags(I) - which set of rules to flush */ 3767145522Sdarrenr/* */ 3768145522Sdarrenr/* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */ 3769145522Sdarrenr/* and IPv6) as defined by the value of flags. */ 3770145522Sdarrenr/* ------------------------------------------------------------------------ */ 3771255332Scyint 3772255332Scyipf_flush(softc, unit, flags) 3773255332Scy ipf_main_softc_t *softc; 3774255332Scy minor_t unit; 3775255332Scy int flags; 377653642Sguido{ 377753642Sguido int flushed = 0, set; 377853642Sguido 3779255332Scy WRITE_ENTER(&softc->ipf_mutex); 378053642Sguido 3781255332Scy set = softc->ipf_active; 3782145522Sdarrenr if ((flags & FR_INACTIVE) == FR_INACTIVE) 378353642Sguido set = 1 - set; 378453642Sguido 378553642Sguido if (flags & FR_OUTQUE) { 3786255332Scy ipf_flushlist(softc, &flushed, &softc->ipf_rules[1][set]); 3787255332Scy ipf_flushlist(softc, &flushed, &softc->ipf_acct[1][set]); 378853642Sguido } 378953642Sguido if (flags & FR_INQUE) { 3790255332Scy ipf_flushlist(softc, &flushed, &softc->ipf_rules[0][set]); 3791255332Scy ipf_flushlist(softc, &flushed, &softc->ipf_acct[0][set]); 379253642Sguido } 3793145522Sdarrenr 3794255332Scy flushed += ipf_flush_groups(softc, &softc->ipf_groups[unit][set], 3795255332Scy flags & (FR_INQUE|FR_OUTQUE)); 3796255332Scy 3797255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 3798255332Scy 3799145522Sdarrenr if (unit == IPL_LOGIPF) { 3800145522Sdarrenr int tmp; 3801145522Sdarrenr 3802255332Scy tmp = ipf_flush(softc, IPL_LOGCOUNT, flags); 3803145522Sdarrenr if (tmp >= 0) 3804145522Sdarrenr flushed += tmp; 3805145522Sdarrenr } 380653642Sguido return flushed; 380753642Sguido} 380853642Sguido 380953642Sguido 3810145522Sdarrenr/* ------------------------------------------------------------------------ */ 3811255332Scy/* Function: ipf_flush_groups */ 3812255332Scy/* Returns: int - >= 0 - number of flushed rules */ 3813255332Scy/* Parameters: softc(I) - soft context pointerto work with */ 3814255332Scy/* grhead(I) - pointer to the start of the group list to flush */ 3815255332Scy/* flags(I) - which set of rules to flush */ 3816255332Scy/* */ 3817255332Scy/* Walk through all of the groups under the given group head and remove all */ 3818255332Scy/* of those that match the flags passed in. The for loop here is bit more */ 3819255332Scy/* complicated than usual because the removal of a rule with ipf_derefrule */ 3820255332Scy/* may end up removing not only the structure pointed to by "fg" but also */ 3821255332Scy/* what is fg_next and fg_next after that. So if a filter rule is actually */ 3822255332Scy/* removed from the group then it is necessary to start again. */ 3823255332Scy/* ------------------------------------------------------------------------ */ 3824255332Scystatic int 3825255332Scyipf_flush_groups(softc, grhead, flags) 3826255332Scy ipf_main_softc_t *softc; 3827255332Scy frgroup_t **grhead; 3828255332Scy int flags; 3829255332Scy{ 3830255332Scy frentry_t *fr, **frp; 3831255332Scy frgroup_t *fg, **fgp; 3832255332Scy int flushed = 0; 3833255332Scy int removed = 0; 3834255332Scy 3835255332Scy for (fgp = grhead; (fg = *fgp) != NULL; ) { 3836255332Scy while ((fg != NULL) && ((fg->fg_flags & flags) == 0)) 3837255332Scy fg = fg->fg_next; 3838255332Scy if (fg == NULL) 3839255332Scy break; 3840255332Scy removed = 0; 3841255332Scy frp = &fg->fg_start; 3842255332Scy while ((removed == 0) && ((fr = *frp) != NULL)) { 3843255332Scy if ((fr->fr_flags & flags) == 0) { 3844255332Scy frp = &fr->fr_next; 3845255332Scy } else { 3846255332Scy if (fr->fr_next != NULL) 3847255332Scy fr->fr_next->fr_pnext = fr->fr_pnext; 3848255332Scy *frp = fr->fr_next; 3849255332Scy fr->fr_pnext = NULL; 3850255332Scy fr->fr_next = NULL; 3851255332Scy (void) ipf_derefrule(softc, &fr); 3852255332Scy flushed++; 3853255332Scy removed++; 3854255332Scy } 3855255332Scy } 3856255332Scy if (removed == 0) 3857255332Scy fgp = &fg->fg_next; 3858255332Scy } 3859255332Scy return flushed; 3860255332Scy} 3861255332Scy 3862255332Scy 3863255332Scy/* ------------------------------------------------------------------------ */ 3864145522Sdarrenr/* Function: memstr */ 3865145522Sdarrenr/* Returns: char * - NULL if failed, != NULL pointer to matching bytes */ 3866145522Sdarrenr/* Parameters: src(I) - pointer to byte sequence to match */ 3867145522Sdarrenr/* dst(I) - pointer to byte sequence to search */ 3868145522Sdarrenr/* slen(I) - match length */ 3869145522Sdarrenr/* dlen(I) - length available to search in */ 3870145522Sdarrenr/* */ 3871145522Sdarrenr/* Search dst for a sequence of bytes matching those at src and extend for */ 3872145522Sdarrenr/* slen bytes. */ 3873145522Sdarrenr/* ------------------------------------------------------------------------ */ 3874255332Scychar * 3875255332Scymemstr(src, dst, slen, dlen) 3876255332Scy const char *src; 3877255332Scy char *dst; 3878255332Scy size_t slen, dlen; 387953642Sguido{ 388053642Sguido char *s = NULL; 388153642Sguido 388253642Sguido while (dlen >= slen) { 388353642Sguido if (bcmp(src, dst, slen) == 0) { 388453642Sguido s = dst; 388553642Sguido break; 388653642Sguido } 388753642Sguido dst++; 388853642Sguido dlen--; 388953642Sguido } 389053642Sguido return s; 389153642Sguido} 3892145522Sdarrenr/* ------------------------------------------------------------------------ */ 3893255332Scy/* Function: ipf_fixskip */ 3894145522Sdarrenr/* Returns: Nil */ 3895145522Sdarrenr/* Parameters: listp(IO) - pointer to start of list with skip rule */ 3896145522Sdarrenr/* rp(I) - rule added/removed with skip in it. */ 3897145522Sdarrenr/* addremove(I) - adjustment (-1/+1) to make to skip count, */ 3898145522Sdarrenr/* depending on whether a rule was just added */ 3899145522Sdarrenr/* or removed. */ 3900145522Sdarrenr/* */ 3901145522Sdarrenr/* Adjust all the rules in a list which would have skip'd past the position */ 3902145522Sdarrenr/* where we are inserting to skip to the right place given the change. */ 3903145522Sdarrenr/* ------------------------------------------------------------------------ */ 3904255332Scyvoid 3905255332Scyipf_fixskip(listp, rp, addremove) 3906255332Scy frentry_t **listp, *rp; 3907255332Scy int addremove; 390853642Sguido{ 3909145522Sdarrenr int rules, rn; 391053642Sguido frentry_t *fp; 391153642Sguido 3912145522Sdarrenr rules = 0; 3913145522Sdarrenr for (fp = *listp; (fp != NULL) && (fp != rp); fp = fp->fr_next) 3914145522Sdarrenr rules++; 391553642Sguido 391653642Sguido if (!fp) 391753642Sguido return; 391853642Sguido 3919145522Sdarrenr for (rn = 0, fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) 3920145522Sdarrenr if (FR_ISSKIP(fp->fr_flags) && (rn + fp->fr_arg >= rules)) 3921145522Sdarrenr fp->fr_arg += addremove; 392253642Sguido} 392353642Sguido 392453642Sguido 392553642Sguido#ifdef _KERNEL 3926145522Sdarrenr/* ------------------------------------------------------------------------ */ 3927145522Sdarrenr/* Function: count4bits */ 3928145522Sdarrenr/* Returns: int - >= 0 - number of consecutive bits in input */ 3929145522Sdarrenr/* Parameters: ip(I) - 32bit IP address */ 3930145522Sdarrenr/* */ 3931145522Sdarrenr/* IPv4 ONLY */ 3932145522Sdarrenr/* count consecutive 1's in bit mask. If the mask generated by counting */ 3933145522Sdarrenr/* consecutive 1's is different to that passed, return -1, else return # */ 3934145522Sdarrenr/* of bits. */ 3935145522Sdarrenr/* ------------------------------------------------------------------------ */ 3936255332Scyint 3937255332Scycount4bits(ip) 3938255332Scy u_32_t ip; 393953642Sguido{ 394053642Sguido u_32_t ipn; 394153642Sguido int cnt = 0, i, j; 394253642Sguido 394353642Sguido ip = ipn = ntohl(ip); 394453642Sguido for (i = 32; i; i--, ipn *= 2) 394553642Sguido if (ipn & 0x80000000) 394653642Sguido cnt++; 394753642Sguido else 394853642Sguido break; 394953642Sguido ipn = 0; 395053642Sguido for (i = 32, j = cnt; i; i--, j--) { 395153642Sguido ipn *= 2; 395253642Sguido if (j > 0) 395353642Sguido ipn++; 395453642Sguido } 395553642Sguido if (ipn == ip) 395653642Sguido return cnt; 395753642Sguido return -1; 395853642Sguido} 395953642Sguido 396053642Sguido 3961145522Sdarrenr/* ------------------------------------------------------------------------ */ 3962145522Sdarrenr/* Function: count6bits */ 3963145522Sdarrenr/* Returns: int - >= 0 - number of consecutive bits in input */ 3964145522Sdarrenr/* Parameters: msk(I) - pointer to start of IPv6 bitmask */ 3965145522Sdarrenr/* */ 3966145522Sdarrenr/* IPv6 ONLY */ 3967145522Sdarrenr/* count consecutive 1's in bit mask. */ 3968145522Sdarrenr/* ------------------------------------------------------------------------ */ 3969255332Scy# ifdef USE_INET6 3970255332Scyint 3971255332Scycount6bits(msk) 3972255332Scy u_32_t *msk; 397353642Sguido{ 3974145522Sdarrenr int i = 0, k; 3975145522Sdarrenr u_32_t j; 397653642Sguido 3977145522Sdarrenr for (k = 3; k >= 0; k--) 3978145522Sdarrenr if (msk[k] == 0xffffffff) 3979145522Sdarrenr i += 32; 3980145522Sdarrenr else { 3981145522Sdarrenr for (j = msk[k]; j; j <<= 1) 3982145522Sdarrenr if (j & 0x80000000) 3983145522Sdarrenr i++; 398460850Sdarrenr } 3985145522Sdarrenr return i; 398653642Sguido} 3987145522Sdarrenr# endif 3988145522Sdarrenr#endif /* _KERNEL */ 398955929Sguido 399055929Sguido 3991145522Sdarrenr/* ------------------------------------------------------------------------ */ 3992255332Scy/* Function: ipf_synclist */ 3993255332Scy/* Returns: int - 0 = no failures, else indication of first failure */ 3994145522Sdarrenr/* Parameters: fr(I) - start of filter list to sync interface names for */ 3995145522Sdarrenr/* ifp(I) - interface pointer for limiting sync lookups */ 3996145522Sdarrenr/* Write Locks: ipf_mutex */ 3997145522Sdarrenr/* */ 3998145522Sdarrenr/* Walk through a list of filter rules and resolve any interface names into */ 3999145522Sdarrenr/* pointers. Where dynamic addresses are used, also update the IP address */ 4000145522Sdarrenr/* used in the rule. The interface pointer is used to limit the lookups to */ 4001145522Sdarrenr/* a specific set of matching names if it is non-NULL. */ 4002255332Scy/* Errors can occur when resolving the destination name of to/dup-to fields */ 4003255332Scy/* when the name points to a pool and that pool doest not exist. If this */ 4004255332Scy/* does happen then it is necessary to check if there are any lookup refs */ 4005255332Scy/* that need to be dropped before returning with an error. */ 4006145522Sdarrenr/* ------------------------------------------------------------------------ */ 4007255332Scystatic int 4008255332Scyipf_synclist(softc, fr, ifp) 4009255332Scy ipf_main_softc_t *softc; 4010255332Scy frentry_t *fr; 4011255332Scy void *ifp; 401255929Sguido{ 4013255332Scy frentry_t *frt, *start = fr; 4014130886Sdarrenr frdest_t *fdp; 4015255332Scy char *name; 4016255332Scy int error; 4017255332Scy void *ifa; 4018145522Sdarrenr int v, i; 4019130886Sdarrenr 4020255332Scy error = 0; 4021255332Scy 402255929Sguido for (; fr; fr = fr->fr_next) { 4023255332Scy if (fr->fr_family == AF_INET) 4024255332Scy v = 4; 4025255332Scy else if (fr->fr_family == AF_INET6) 4026255332Scy v = 6; 4027255332Scy else 4028255332Scy v = 0; 4029145522Sdarrenr 4030145522Sdarrenr /* 4031145522Sdarrenr * Lookup all the interface names that are part of the rule. 4032145522Sdarrenr */ 4033130886Sdarrenr for (i = 0; i < 4; i++) { 4034145522Sdarrenr if ((ifp != NULL) && (fr->fr_ifas[i] != ifp)) 4035145522Sdarrenr continue; 4036255332Scy if (fr->fr_ifnames[i] == -1) 4037255332Scy continue; 4038255332Scy name = FR_NAME(fr, fr_ifnames[i]); 4039255332Scy fr->fr_ifas[i] = ipf_resolvenic(softc, name, v); 4040145522Sdarrenr } 4041145522Sdarrenr 4042255332Scy if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { 4043145522Sdarrenr if (fr->fr_satype != FRI_NORMAL && 4044145522Sdarrenr fr->fr_satype != FRI_LOOKUP) { 4045255332Scy ifa = ipf_resolvenic(softc, fr->fr_names + 4046255332Scy fr->fr_sifpidx, v); 4047255332Scy ipf_ifpaddr(softc, v, fr->fr_satype, ifa, 4048255332Scy &fr->fr_src6, &fr->fr_smsk6); 4049130886Sdarrenr } 4050145522Sdarrenr if (fr->fr_datype != FRI_NORMAL && 4051145522Sdarrenr fr->fr_datype != FRI_LOOKUP) { 4052255332Scy ifa = ipf_resolvenic(softc, fr->fr_names + 4053255332Scy fr->fr_sifpidx, v); 4054255332Scy ipf_ifpaddr(softc, v, fr->fr_datype, ifa, 4055255332Scy &fr->fr_dst6, &fr->fr_dmsk6); 4056145522Sdarrenr } 405755929Sguido } 4058130886Sdarrenr 4059145522Sdarrenr fdp = &fr->fr_tifs[0]; 4060255332Scy if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { 4061255332Scy error = ipf_resolvedest(softc, fr->fr_names, fdp, v); 4062255332Scy if (error != 0) 4063255332Scy goto unwind; 4064255332Scy } 4065145522Sdarrenr 4066145522Sdarrenr fdp = &fr->fr_tifs[1]; 4067255332Scy if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { 4068255332Scy error = ipf_resolvedest(softc, fr->fr_names, fdp, v); 4069255332Scy if (error != 0) 4070255332Scy goto unwind; 4071255332Scy } 4072145522Sdarrenr 4073130886Sdarrenr fdp = &fr->fr_dif; 4074255332Scy if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { 4075255332Scy error = ipf_resolvedest(softc, fr->fr_names, fdp, v); 4076255332Scy if (error != 0) 4077255332Scy goto unwind; 4078130886Sdarrenr } 4079130886Sdarrenr 4080255332Scy if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && 4081255332Scy (fr->fr_satype == FRI_LOOKUP) && (fr->fr_srcptr == NULL)) { 4082255332Scy fr->fr_srcptr = ipf_lookup_res_num(softc, 4083255332Scy fr->fr_srctype, 4084255332Scy IPL_LOGIPF, 4085255332Scy fr->fr_srcnum, 4086255332Scy &fr->fr_srcfunc); 4087130886Sdarrenr } 4088255332Scy if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && 4089255332Scy (fr->fr_datype == FRI_LOOKUP) && (fr->fr_dstptr == NULL)) { 4090255332Scy fr->fr_dstptr = ipf_lookup_res_num(softc, 4091255332Scy fr->fr_dsttype, 4092255332Scy IPL_LOGIPF, 4093255332Scy fr->fr_dstnum, 4094255332Scy &fr->fr_dstfunc); 4095145522Sdarrenr } 409655929Sguido } 4097255332Scy return 0; 4098255332Scy 4099255332Scyunwind: 4100255332Scy for (frt = start; frt != fr; fr = fr->fr_next) { 4101255332Scy if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && 4102255332Scy (frt->fr_satype == FRI_LOOKUP) && (frt->fr_srcptr != NULL)) 4103255332Scy ipf_lookup_deref(softc, frt->fr_srctype, 4104255332Scy frt->fr_srcptr); 4105255332Scy if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && 4106255332Scy (frt->fr_datype == FRI_LOOKUP) && (frt->fr_dstptr != NULL)) 4107255332Scy ipf_lookup_deref(softc, frt->fr_dsttype, 4108255332Scy frt->fr_dstptr); 4109255332Scy } 4110255332Scy return error; 411155929Sguido} 411255929Sguido 411355929Sguido 4114145522Sdarrenr/* ------------------------------------------------------------------------ */ 4115255332Scy/* Function: ipf_sync */ 4116145522Sdarrenr/* Returns: void */ 4117145522Sdarrenr/* Parameters: Nil */ 4118145522Sdarrenr/* */ 4119255332Scy/* ipf_sync() is called when we suspect that the interface list or */ 4120145522Sdarrenr/* information about interfaces (like IP#) has changed. Go through all */ 4121145522Sdarrenr/* filter rules, NAT entries and the state table and check if anything */ 4122145522Sdarrenr/* needs to be changed/updated. */ 4123145522Sdarrenr/* ------------------------------------------------------------------------ */ 4124255332Scyint 4125255332Scyipf_sync(softc, ifp) 4126255332Scy ipf_main_softc_t *softc; 4127255332Scy void *ifp; 412855929Sguido{ 4129145522Sdarrenr int i; 413055929Sguido 4131145522Sdarrenr# if !SOLARIS 4132255332Scy ipf_nat_sync(softc, ifp); 4133255332Scy ipf_state_sync(softc, ifp); 4134255332Scy ipf_lookup_sync(softc, ifp); 4135145522Sdarrenr# endif 4136145522Sdarrenr 4137255332Scy WRITE_ENTER(&softc->ipf_mutex); 4138255332Scy (void) ipf_synclist(softc, softc->ipf_acct[0][softc->ipf_active], ifp); 4139255332Scy (void) ipf_synclist(softc, softc->ipf_acct[1][softc->ipf_active], ifp); 4140255332Scy (void) ipf_synclist(softc, softc->ipf_rules[0][softc->ipf_active], ifp); 4141255332Scy (void) ipf_synclist(softc, softc->ipf_rules[1][softc->ipf_active], ifp); 4142145522Sdarrenr 4143145522Sdarrenr for (i = 0; i < IPL_LOGSIZE; i++) { 4144145522Sdarrenr frgroup_t *g; 4145145522Sdarrenr 4146255332Scy for (g = softc->ipf_groups[i][0]; g != NULL; g = g->fg_next) 4147255332Scy (void) ipf_synclist(softc, g->fg_start, ifp); 4148255332Scy for (g = softc->ipf_groups[i][1]; g != NULL; g = g->fg_next) 4149255332Scy (void) ipf_synclist(softc, g->fg_start, ifp); 4150145522Sdarrenr } 4151255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 4152255332Scy 4153255332Scy return 0; 415455929Sguido} 415555929Sguido 415660850Sdarrenr 415760850Sdarrenr/* 415860850Sdarrenr * In the functions below, bcopy() is called because the pointer being 415960850Sdarrenr * copied _from_ in this instance is a pointer to a char buf (which could 416060850Sdarrenr * end up being unaligned) and on the kernel's local stack. 416160850Sdarrenr */ 4162145522Sdarrenr/* ------------------------------------------------------------------------ */ 4163145522Sdarrenr/* Function: copyinptr */ 4164145522Sdarrenr/* Returns: int - 0 = success, else failure */ 4165145522Sdarrenr/* Parameters: src(I) - pointer to the source address */ 4166145522Sdarrenr/* dst(I) - destination address */ 4167145522Sdarrenr/* size(I) - number of bytes to copy */ 4168145522Sdarrenr/* */ 4169145522Sdarrenr/* Copy a block of data in from user space, given a pointer to the pointer */ 4170145522Sdarrenr/* to start copying from (src) and a pointer to where to store it (dst). */ 4171145522Sdarrenr/* NB: src - pointer to user space pointer, dst - kernel space pointer */ 4172145522Sdarrenr/* ------------------------------------------------------------------------ */ 4173255332Scyint 4174255332Scycopyinptr(softc, src, dst, size) 4175255332Scy ipf_main_softc_t *softc; 4176255332Scy void *src, *dst; 4177255332Scy size_t size; 417860850Sdarrenr{ 417960850Sdarrenr caddr_t ca; 4180170268Sdarrenr int error; 418160850Sdarrenr 4182145522Sdarrenr# if SOLARIS 4183172776Sdarrenr error = COPYIN(src, &ca, sizeof(ca)); 4184170268Sdarrenr if (error != 0) 4185170268Sdarrenr return error; 4186145522Sdarrenr# else 4187145522Sdarrenr bcopy(src, (caddr_t)&ca, sizeof(ca)); 4188145522Sdarrenr# endif 4189170268Sdarrenr error = COPYIN(ca, dst, size); 4190255332Scy if (error != 0) { 4191255332Scy IPFERROR(3); 4192170268Sdarrenr error = EFAULT; 4193255332Scy } 4194170268Sdarrenr return error; 419560850Sdarrenr} 419653642Sguido 419753642Sguido 4198145522Sdarrenr/* ------------------------------------------------------------------------ */ 4199145522Sdarrenr/* Function: copyoutptr */ 4200145522Sdarrenr/* Returns: int - 0 = success, else failure */ 4201145522Sdarrenr/* Parameters: src(I) - pointer to the source address */ 4202145522Sdarrenr/* dst(I) - destination address */ 4203145522Sdarrenr/* size(I) - number of bytes to copy */ 4204145522Sdarrenr/* */ 4205145522Sdarrenr/* Copy a block of data out to user space, given a pointer to the pointer */ 4206145522Sdarrenr/* to start copying from (src) and a pointer to where to store it (dst). */ 4207145522Sdarrenr/* NB: src - kernel space pointer, dst - pointer to user space pointer. */ 4208145522Sdarrenr/* ------------------------------------------------------------------------ */ 4209255332Scyint 4210255332Scycopyoutptr(softc, src, dst, size) 4211255332Scy ipf_main_softc_t *softc; 4212255332Scy void *src, *dst; 4213255332Scy size_t size; 421460850Sdarrenr{ 421560850Sdarrenr caddr_t ca; 4216170268Sdarrenr int error; 421760850Sdarrenr 4218145522Sdarrenr bcopy(dst, (caddr_t)&ca, sizeof(ca)); 4219170268Sdarrenr error = COPYOUT(src, ca, size); 4220255332Scy if (error != 0) { 4221255332Scy IPFERROR(4); 4222170268Sdarrenr error = EFAULT; 4223255332Scy } 4224170268Sdarrenr return error; 422560850Sdarrenr} 422660850Sdarrenr 422760850Sdarrenr 4228145522Sdarrenr/* ------------------------------------------------------------------------ */ 4229255332Scy/* Function: ipf_lock */ 4230172776Sdarrenr/* Returns: int - 0 = success, else error */ 4231145522Sdarrenr/* Parameters: data(I) - pointer to lock value to set */ 4232145522Sdarrenr/* lockp(O) - pointer to location to store old lock value */ 4233145522Sdarrenr/* */ 4234145522Sdarrenr/* Get the new value for the lock integer, set it and return the old value */ 4235145522Sdarrenr/* in *lockp. */ 4236145522Sdarrenr/* ------------------------------------------------------------------------ */ 4237255332Scyint 4238255332Scyipf_lock(data, lockp) 4239255332Scy caddr_t data; 4240255332Scy int *lockp; 424160850Sdarrenr{ 4242172776Sdarrenr int arg, err; 424360850Sdarrenr 4244172776Sdarrenr err = BCOPYIN(data, &arg, sizeof(arg)); 4245172776Sdarrenr if (err != 0) 4246172776Sdarrenr return EFAULT; 4247172776Sdarrenr err = BCOPYOUT(lockp, data, sizeof(*lockp)); 4248172776Sdarrenr if (err != 0) 4249172776Sdarrenr return EFAULT; 4250145522Sdarrenr *lockp = arg; 4251172776Sdarrenr return 0; 425260850Sdarrenr} 425360850Sdarrenr 425460850Sdarrenr 4255145522Sdarrenr/* ------------------------------------------------------------------------ */ 4256255332Scy/* Function: ipf_getstat */ 4257145522Sdarrenr/* Returns: Nil */ 4258255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 4259255332Scy/* fiop(I) - pointer to ipfilter stats structure */ 4260255332Scy/* rev(I) - version claim by program doing ioctl */ 4261145522Sdarrenr/* */ 4262145522Sdarrenr/* Stores a copy of current pointers, counters, etc, in the friostat */ 4263145522Sdarrenr/* structure. */ 4264255332Scy/* If IPFILTER_COMPAT is compiled, we pretend to be whatever version the */ 4265255332Scy/* program is looking for. This ensure that validation of the version it */ 4266255332Scy/* expects will always succeed. Thus kernels with IPFILTER_COMPAT will */ 4267255332Scy/* allow older binaries to work but kernels without it will not. */ 4268145522Sdarrenr/* ------------------------------------------------------------------------ */ 4269255332Scy/*ARGSUSED*/ 4270255332Scystatic void 4271255332Scyipf_getstat(softc, fiop, rev) 4272255332Scy ipf_main_softc_t *softc; 4273255332Scy friostat_t *fiop; 4274255332Scy int rev; 427560850Sdarrenr{ 4276255332Scy int i; 4277145522Sdarrenr 4278255332Scy bcopy((char *)softc->ipf_stats, (char *)fiop->f_st, 4279255332Scy sizeof(ipf_statistics_t) * 2); 4280255332Scy fiop->f_locks[IPL_LOGSTATE] = -1; 4281255332Scy fiop->f_locks[IPL_LOGNAT] = -1; 4282255332Scy fiop->f_locks[IPL_LOGIPF] = -1; 4283255332Scy fiop->f_locks[IPL_LOGAUTH] = -1; 4284145522Sdarrenr 4285255332Scy fiop->f_ipf[0][0] = softc->ipf_rules[0][0]; 4286255332Scy fiop->f_acct[0][0] = softc->ipf_acct[0][0]; 4287255332Scy fiop->f_ipf[0][1] = softc->ipf_rules[0][1]; 4288255332Scy fiop->f_acct[0][1] = softc->ipf_acct[0][1]; 4289255332Scy fiop->f_ipf[1][0] = softc->ipf_rules[1][0]; 4290255332Scy fiop->f_acct[1][0] = softc->ipf_acct[1][0]; 4291255332Scy fiop->f_ipf[1][1] = softc->ipf_rules[1][1]; 4292255332Scy fiop->f_acct[1][1] = softc->ipf_acct[1][1]; 4293145522Sdarrenr 4294255332Scy fiop->f_ticks = softc->ipf_ticks; 4295255332Scy fiop->f_active = softc->ipf_active; 4296255332Scy fiop->f_froute[0] = softc->ipf_frouteok[0]; 4297255332Scy fiop->f_froute[1] = softc->ipf_frouteok[1]; 4298255332Scy fiop->f_rb_no_mem = softc->ipf_rb_no_mem; 4299255332Scy fiop->f_rb_node_max = softc->ipf_rb_node_max; 430060850Sdarrenr 4301255332Scy fiop->f_running = softc->ipf_running; 4302145522Sdarrenr for (i = 0; i < IPL_LOGSIZE; i++) { 4303255332Scy fiop->f_groups[i][0] = softc->ipf_groups[i][0]; 4304255332Scy fiop->f_groups[i][1] = softc->ipf_groups[i][1]; 4305145522Sdarrenr } 430660850Sdarrenr#ifdef IPFILTER_LOG 4307255332Scy fiop->f_log_ok = ipf_log_logok(softc, IPL_LOGIPF); 4308255332Scy fiop->f_log_fail = ipf_log_failures(softc, IPL_LOGIPF); 430960850Sdarrenr fiop->f_logging = 1; 431060850Sdarrenr#else 4311255332Scy fiop->f_log_ok = 0; 4312255332Scy fiop->f_log_fail = 0; 431360850Sdarrenr fiop->f_logging = 0; 431460850Sdarrenr#endif 4315255332Scy fiop->f_defpass = softc->ipf_pass; 4316255332Scy fiop->f_features = ipf_features; 4317255332Scy 4318255332Scy#ifdef IPFILTER_COMPAT 4319255332Scy sprintf(fiop->f_version, "IP Filter: v%d.%d.%d", 4320255332Scy (rev / 1000000) % 100, 4321255332Scy (rev / 10000) % 100, 4322255332Scy (rev / 100) % 100); 4323255332Scy#else 4324255332Scy rev = rev; 4325145522Sdarrenr (void) strncpy(fiop->f_version, ipfilter_version, 4326145522Sdarrenr sizeof(fiop->f_version)); 4327255332Scy#endif 432860850Sdarrenr} 432960850Sdarrenr 433060850Sdarrenr 433160850Sdarrenr#ifdef USE_INET6 433260850Sdarrenrint icmptoicmp6types[ICMP_MAXTYPE+1] = { 433360850Sdarrenr ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */ 433460850Sdarrenr -1, /* 1: UNUSED */ 433560850Sdarrenr -1, /* 2: UNUSED */ 433660850Sdarrenr ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */ 433760850Sdarrenr -1, /* 4: ICMP_SOURCEQUENCH */ 433860850Sdarrenr ND_REDIRECT, /* 5: ICMP_REDIRECT */ 433960850Sdarrenr -1, /* 6: UNUSED */ 434060850Sdarrenr -1, /* 7: UNUSED */ 434160850Sdarrenr ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */ 434260850Sdarrenr -1, /* 9: UNUSED */ 434360850Sdarrenr -1, /* 10: UNUSED */ 434460850Sdarrenr ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */ 434560850Sdarrenr ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */ 434660850Sdarrenr -1, /* 13: ICMP_TSTAMP */ 434760850Sdarrenr -1, /* 14: ICMP_TSTAMPREPLY */ 434860850Sdarrenr -1, /* 15: ICMP_IREQ */ 434960850Sdarrenr -1, /* 16: ICMP_IREQREPLY */ 435060850Sdarrenr -1, /* 17: ICMP_MASKREQ */ 435160850Sdarrenr -1, /* 18: ICMP_MASKREPLY */ 435260850Sdarrenr}; 435360850Sdarrenr 435460850Sdarrenr 435560850Sdarrenrint icmptoicmp6unreach[ICMP_MAX_UNREACH] = { 435660850Sdarrenr ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */ 435760850Sdarrenr ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */ 435860850Sdarrenr -1, /* 2: ICMP_UNREACH_PROTOCOL */ 435960850Sdarrenr ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */ 436060850Sdarrenr -1, /* 4: ICMP_UNREACH_NEEDFRAG */ 436160850Sdarrenr ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */ 436260850Sdarrenr ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */ 436360850Sdarrenr ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */ 436460850Sdarrenr -1, /* 8: ICMP_UNREACH_ISOLATED */ 436560850Sdarrenr ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */ 436660850Sdarrenr ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */ 436760850Sdarrenr -1, /* 11: ICMP_UNREACH_TOSNET */ 436860850Sdarrenr -1, /* 12: ICMP_UNREACH_TOSHOST */ 436960850Sdarrenr ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */ 437060850Sdarrenr}; 4371145522Sdarrenrint icmpreplytype6[ICMP6_MAXTYPE + 1]; 437260850Sdarrenr#endif 437395563Sdarrenr 4374145522Sdarrenrint icmpreplytype4[ICMP_MAXTYPE + 1]; 437595563Sdarrenr 4376145522Sdarrenr 4377145522Sdarrenr/* ------------------------------------------------------------------------ */ 4378255332Scy/* Function: ipf_matchicmpqueryreply */ 4379145522Sdarrenr/* Returns: int - 1 if "icmp" is a valid reply to "ic" else 0. */ 4380145522Sdarrenr/* Parameters: v(I) - IP protocol version (4 or 6) */ 4381145522Sdarrenr/* ic(I) - ICMP information */ 4382145522Sdarrenr/* icmp(I) - ICMP packet header */ 4383145522Sdarrenr/* rev(I) - direction (0 = forward/1 = reverse) of packet */ 4384145522Sdarrenr/* */ 4385145522Sdarrenr/* Check if the ICMP packet defined by the header pointed to by icmp is a */ 4386145522Sdarrenr/* reply to one as described by what's in ic. If it is a match, return 1, */ 4387145522Sdarrenr/* else return 0 for no match. */ 4388145522Sdarrenr/* ------------------------------------------------------------------------ */ 4389255332Scyint 4390255332Scyipf_matchicmpqueryreply(v, ic, icmp, rev) 4391255332Scy int v; 4392255332Scy icmpinfo_t *ic; 4393255332Scy icmphdr_t *icmp; 4394255332Scy int rev; 439595563Sdarrenr{ 4396145522Sdarrenr int ictype; 439795563Sdarrenr 4398145522Sdarrenr ictype = ic->ici_type; 4399145522Sdarrenr 4400145522Sdarrenr if (v == 4) { 4401145522Sdarrenr /* 4402145522Sdarrenr * If we matched its type on the way in, then when going out 4403145522Sdarrenr * it will still be the same type. 4404145522Sdarrenr */ 4405145522Sdarrenr if ((!rev && (icmp->icmp_type == ictype)) || 4406145522Sdarrenr (rev && (icmpreplytype4[ictype] == icmp->icmp_type))) { 4407145522Sdarrenr if (icmp->icmp_type != ICMP_ECHOREPLY) 4408145522Sdarrenr return 1; 4409145522Sdarrenr if (icmp->icmp_id == ic->ici_id) 4410145522Sdarrenr return 1; 4411145522Sdarrenr } 4412145522Sdarrenr } 4413145522Sdarrenr#ifdef USE_INET6 4414145522Sdarrenr else if (v == 6) { 4415145522Sdarrenr if ((!rev && (icmp->icmp_type == ictype)) || 4416145522Sdarrenr (rev && (icmpreplytype6[ictype] == icmp->icmp_type))) { 4417145522Sdarrenr if (icmp->icmp_type != ICMP6_ECHO_REPLY) 4418145522Sdarrenr return 1; 4419145522Sdarrenr if (icmp->icmp_id == ic->ici_id) 4420145522Sdarrenr return 1; 4421145522Sdarrenr } 4422145522Sdarrenr } 4423145522Sdarrenr#endif 4424145522Sdarrenr return 0; 442595563Sdarrenr} 4426145522Sdarrenr 4427145522Sdarrenr 4428145522Sdarrenr/* ------------------------------------------------------------------------ */ 4429272995Scy/* Function: ipf_rule_compare */ 4430272995Scy/* Parameters: fr1(I) - first rule structure to compare */ 4431272995Scy/* fr2(I) - second rule structure to compare */ 4432272995Scy/* Returns: int - 0 == rules are the same, else mismatch */ 4433272995Scy/* */ 4434272995Scy/* Compare two rules and return 0 if they match or a number indicating */ 4435272995Scy/* which of the individual checks failed. */ 4436272995Scy/* ------------------------------------------------------------------------ */ 4437272995Scystatic int 4438272995Scyipf_rule_compare(frentry_t *fr1, frentry_t *fr2) 4439272995Scy{ 4440272995Scy if (fr1->fr_cksum != fr2->fr_cksum) 4441272995Scy return 1; 4442272995Scy if (fr1->fr_size != fr2->fr_size) 4443272995Scy return 2; 4444272995Scy if (fr1->fr_dsize != fr2->fr_dsize) 4445272995Scy return 3; 4446272995Scy if (bcmp((char *)&fr1->fr_func, (char *)&fr2->fr_func, 4447272995Scy fr1->fr_size - offsetof(struct frentry, fr_func)) != 0) 4448272995Scy return 4; 4449272995Scy if (fr1->fr_data && !fr2->fr_data) 4450272995Scy return 5; 4451272995Scy if (!fr1->fr_data && fr2->fr_data) 4452272995Scy return 6; 4453272995Scy if (fr1->fr_data) { 4454272995Scy if (bcmp(fr1->fr_caddr, fr2->fr_caddr, fr1->fr_dsize)) 4455272995Scy return 7; 4456272995Scy } 4457272995Scy return 0; 4458272995Scy} 4459272995Scy 4460272995Scy 4461272995Scy/* ------------------------------------------------------------------------ */ 4462145522Sdarrenr/* Function: frrequest */ 4463145522Sdarrenr/* Returns: int - 0 == success, > 0 == errno value */ 4464145522Sdarrenr/* Parameters: unit(I) - device for which this is for */ 4465145522Sdarrenr/* req(I) - ioctl command (SIOC*) */ 4466145522Sdarrenr/* data(I) - pointr to ioctl data */ 4467145522Sdarrenr/* set(I) - 1 or 0 (filter set) */ 4468145522Sdarrenr/* makecopy(I) - flag indicating whether data points to a rule */ 4469145522Sdarrenr/* in kernel space & hence doesn't need copying. */ 4470145522Sdarrenr/* */ 4471145522Sdarrenr/* This function handles all the requests which operate on the list of */ 4472145522Sdarrenr/* filter rules. This includes adding, deleting, insertion. It is also */ 4473145522Sdarrenr/* responsible for creating groups when a "head" rule is loaded. Interface */ 4474145522Sdarrenr/* names are resolved here and other sanity checks are made on the content */ 4475145522Sdarrenr/* of the rule structure being loaded. If a rule has user defined timeouts */ 4476145522Sdarrenr/* then make sure they are created and initialised before exiting. */ 4477145522Sdarrenr/* ------------------------------------------------------------------------ */ 4478255332Scyint 4479255332Scyfrrequest(softc, unit, req, data, set, makecopy) 4480255332Scy ipf_main_softc_t *softc; 4481255332Scy int unit; 4482255332Scy ioctlcmd_t req; 4483255332Scy int set, makecopy; 4484255332Scy caddr_t data; 4485145522Sdarrenr{ 4486255332Scy int error = 0, in, family, addrem, need_free = 0; 4487145522Sdarrenr frentry_t frd, *fp, *f, **fprev, **ftail; 4488255332Scy void *ptr, *uptr, *cptr; 4489145522Sdarrenr u_int *p, *pp; 4490145522Sdarrenr frgroup_t *fg; 4491145522Sdarrenr char *group; 4492145522Sdarrenr 4493255332Scy ptr = NULL; 4494255332Scy cptr = NULL; 4495145522Sdarrenr fg = NULL; 4496145522Sdarrenr fp = &frd; 4497145522Sdarrenr if (makecopy != 0) { 4498255332Scy bzero(fp, sizeof(frd)); 4499255332Scy error = ipf_inobj(softc, data, NULL, fp, IPFOBJ_FRENTRY); 4500255332Scy if (error) { 4501255332Scy return error; 4502255332Scy } 4503255332Scy if ((fp->fr_type & FR_T_BUILTIN) != 0) { 4504255332Scy IPFERROR(6); 4505145522Sdarrenr return EINVAL; 4506255332Scy } 4507255332Scy KMALLOCS(f, frentry_t *, fp->fr_size); 4508255332Scy if (f == NULL) { 4509255332Scy IPFERROR(131); 4510255332Scy return ENOMEM; 4511255332Scy } 4512255332Scy bzero(f, fp->fr_size); 4513255332Scy error = ipf_inobjsz(softc, data, f, IPFOBJ_FRENTRY, 4514255332Scy fp->fr_size); 4515255332Scy if (error) { 4516255332Scy KFREES(f, fp->fr_size); 4517255332Scy return error; 4518255332Scy } 4519255332Scy 4520255332Scy fp = f; 4521255332Scy f = NULL; 4522272994Scy fp->fr_next = NULL; 4523255332Scy fp->fr_dnext = NULL; 4524272994Scy fp->fr_pnext = NULL; 4525272994Scy fp->fr_pdnext = NULL; 4526272994Scy fp->fr_grp = NULL; 4527272994Scy fp->fr_grphead = NULL; 4528272994Scy fp->fr_icmpgrp = NULL; 4529272994Scy fp->fr_isc = (void *)-1; 4530272994Scy fp->fr_ptr = NULL; 4531145522Sdarrenr fp->fr_ref = 0; 4532145522Sdarrenr fp->fr_flags |= FR_COPIED; 4533145522Sdarrenr } else { 4534145522Sdarrenr fp = (frentry_t *)data; 4535255332Scy if ((fp->fr_type & FR_T_BUILTIN) == 0) { 4536255332Scy IPFERROR(7); 4537145522Sdarrenr return EINVAL; 4538255332Scy } 4539145522Sdarrenr fp->fr_flags &= ~FR_COPIED; 4540145522Sdarrenr } 4541145522Sdarrenr 4542145522Sdarrenr if (((fp->fr_dsize == 0) && (fp->fr_data != NULL)) || 4543255332Scy ((fp->fr_dsize != 0) && (fp->fr_data == NULL))) { 4544255332Scy IPFERROR(8); 4545255332Scy error = EINVAL; 4546255332Scy goto donenolock; 4547255332Scy } 4548145522Sdarrenr 4549255332Scy family = fp->fr_family; 4550145522Sdarrenr uptr = fp->fr_data; 4551145522Sdarrenr 4552255332Scy if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR || 4553255332Scy req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) 4554255332Scy addrem = 0; 4555255332Scy else if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) 4556255332Scy addrem = 1; 4557255332Scy else if (req == (ioctlcmd_t)SIOCZRLST) 4558255332Scy addrem = 2; 4559255332Scy else { 4560255332Scy IPFERROR(9); 4561255332Scy error = EINVAL; 4562255332Scy goto donenolock; 4563255332Scy } 4564255332Scy 4565145522Sdarrenr /* 4566145522Sdarrenr * Only filter rules for IPv4 or IPv6 are accepted. 4567145522Sdarrenr */ 4568255332Scy if (family == AF_INET) { 4569145522Sdarrenr /*EMPTY*/; 4570145522Sdarrenr#ifdef USE_INET6 4571255332Scy } else if (family == AF_INET6) { 4572145522Sdarrenr /*EMPTY*/; 4573145522Sdarrenr#endif 4574255332Scy } else if (family != 0) { 4575255332Scy IPFERROR(10); 4576255332Scy error = EINVAL; 4577255332Scy goto donenolock; 4578145522Sdarrenr } 4579145522Sdarrenr 4580145522Sdarrenr /* 4581145522Sdarrenr * If the rule is being loaded from user space, i.e. we had to copy it 4582145522Sdarrenr * into kernel space, then do not trust the function pointer in the 4583145522Sdarrenr * rule. 4584145522Sdarrenr */ 4585145522Sdarrenr if ((makecopy == 1) && (fp->fr_func != NULL)) { 4586255332Scy if (ipf_findfunc(fp->fr_func) == NULL) { 4587255332Scy IPFERROR(11); 4588255332Scy error = ESRCH; 4589255332Scy goto donenolock; 4590255332Scy } 4591255332Scy 4592255332Scy if (addrem == 0) { 4593255332Scy error = ipf_funcinit(softc, fp); 4594255332Scy if (error != 0) 4595255332Scy goto donenolock; 4596255332Scy } 4597145522Sdarrenr } 4598255332Scy if ((fp->fr_flags & FR_CALLNOW) && 4599255332Scy ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { 4600255332Scy IPFERROR(142); 4601255332Scy error = ESRCH; 4602255332Scy goto donenolock; 4603255332Scy } 4604255332Scy if (((fp->fr_flags & FR_CMDMASK) == FR_CALL) && 4605255332Scy ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { 4606255332Scy IPFERROR(143); 4607255332Scy error = ESRCH; 4608255332Scy goto donenolock; 4609255332Scy } 4610145522Sdarrenr 4611145522Sdarrenr ptr = NULL; 4612255332Scy cptr = NULL; 4613145522Sdarrenr 4614145522Sdarrenr if (FR_ISACCOUNT(fp->fr_flags)) 4615145522Sdarrenr unit = IPL_LOGCOUNT; 4616145522Sdarrenr 4617255332Scy /* 4618255332Scy * Check that each group name in the rule has a start index that 4619255332Scy * is valid. 4620255332Scy */ 4621255332Scy if (fp->fr_icmphead != -1) { 4622255332Scy if ((fp->fr_icmphead < 0) || 4623255332Scy (fp->fr_icmphead >= fp->fr_namelen)) { 4624255332Scy IPFERROR(136); 4625255332Scy error = EINVAL; 4626255332Scy goto donenolock; 4627255332Scy } 4628255332Scy if (!strcmp(FR_NAME(fp, fr_icmphead), "0")) 4629255332Scy fp->fr_names[fp->fr_icmphead] = '\0'; 4630145522Sdarrenr } 4631145522Sdarrenr 4632255332Scy if (fp->fr_grhead != -1) { 4633255332Scy if ((fp->fr_grhead < 0) || 4634255332Scy (fp->fr_grhead >= fp->fr_namelen)) { 4635255332Scy IPFERROR(137); 4636255332Scy error = EINVAL; 4637255332Scy goto donenolock; 4638255332Scy } 4639255332Scy if (!strcmp(FR_NAME(fp, fr_grhead), "0")) 4640255332Scy fp->fr_names[fp->fr_grhead] = '\0'; 4641255332Scy } 4642255332Scy 4643255332Scy if (fp->fr_group != -1) { 4644255332Scy if ((fp->fr_group < 0) || 4645255332Scy (fp->fr_group >= fp->fr_namelen)) { 4646255332Scy IPFERROR(138); 4647255332Scy error = EINVAL; 4648255332Scy goto donenolock; 4649255332Scy } 4650255332Scy if ((req != (int)SIOCZRLST) && (fp->fr_group != -1)) { 4651255332Scy /* 4652255332Scy * Allow loading rules that are in groups to cause 4653255332Scy * them to be created if they don't already exit. 4654255332Scy */ 4655255332Scy group = FR_NAME(fp, fr_group); 4656255332Scy if (addrem == 0) { 4657255332Scy fg = ipf_group_add(softc, group, NULL, 4658255332Scy fp->fr_flags, unit, set); 4659255332Scy fp->fr_grp = fg; 4660255332Scy } else { 4661255332Scy fg = ipf_findgroup(softc, group, unit, 4662255332Scy set, NULL); 4663255332Scy if (fg == NULL) { 4664255332Scy IPFERROR(12); 4665255332Scy error = ESRCH; 4666255332Scy goto donenolock; 4667255332Scy } 4668255332Scy } 4669255332Scy 4670255332Scy if (fg->fg_flags == 0) { 4671255332Scy fg->fg_flags = fp->fr_flags & FR_INOUT; 4672255332Scy } else if (fg->fg_flags != (fp->fr_flags & FR_INOUT)) { 4673255332Scy IPFERROR(13); 4674255332Scy error = ESRCH; 4675255332Scy goto donenolock; 4676255332Scy } 4677255332Scy } 4678255332Scy } else { 4679255332Scy /* 4680255332Scy * If a rule is going to be part of a group then it does 4681255332Scy * not matter whether it is an in or out rule, but if it 4682255332Scy * isn't in a group, then it does... 4683255332Scy */ 4684255332Scy if ((fp->fr_flags & (FR_INQUE|FR_OUTQUE)) == 0) { 4685255332Scy IPFERROR(14); 4686255332Scy error = EINVAL; 4687255332Scy goto donenolock; 4688255332Scy } 4689255332Scy } 4690145522Sdarrenr in = (fp->fr_flags & FR_INQUE) ? 0 : 1; 4691145522Sdarrenr 4692145522Sdarrenr /* 4693145522Sdarrenr * Work out which rule list this change is being applied to. 4694145522Sdarrenr */ 4695145522Sdarrenr ftail = NULL; 4696145522Sdarrenr fprev = NULL; 4697255332Scy if (unit == IPL_LOGAUTH) { 4698255332Scy if ((fp->fr_tifs[0].fd_ptr != NULL) || 4699255332Scy (fp->fr_tifs[1].fd_ptr != NULL) || 4700255332Scy (fp->fr_dif.fd_ptr != NULL) || 4701255332Scy (fp->fr_flags & FR_FASTROUTE)) { 4702255332Scy softc->ipf_interror = 145; 4703255332Scy error = EINVAL; 4704255332Scy goto donenolock; 4705255332Scy } 4706255332Scy fprev = ipf_auth_rulehead(softc); 4707255332Scy } else { 4708145522Sdarrenr if (FR_ISACCOUNT(fp->fr_flags)) 4709255332Scy fprev = &softc->ipf_acct[in][set]; 4710145522Sdarrenr else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) 4711255332Scy fprev = &softc->ipf_rules[in][set]; 4712145522Sdarrenr } 4713255332Scy if (fprev == NULL) { 4714255332Scy IPFERROR(15); 4715255332Scy error = ESRCH; 4716255332Scy goto donenolock; 4717255332Scy } 4718145522Sdarrenr 4719255332Scy if (fg != NULL) 4720145522Sdarrenr fprev = &fg->fg_start; 4721145522Sdarrenr 4722145522Sdarrenr /* 4723145522Sdarrenr * Copy in extra data for the rule. 4724145522Sdarrenr */ 4725145522Sdarrenr if (fp->fr_dsize != 0) { 4726145522Sdarrenr if (makecopy != 0) { 4727145522Sdarrenr KMALLOCS(ptr, void *, fp->fr_dsize); 4728255332Scy if (ptr == NULL) { 4729255332Scy IPFERROR(16); 4730255332Scy error = ENOMEM; 4731255332Scy goto donenolock; 4732255332Scy } 4733255332Scy 4734255332Scy /* 4735255332Scy * The bcopy case is for when the data is appended 4736255332Scy * to the rule by ipf_in_compat(). 4737255332Scy */ 4738255332Scy if (uptr >= (void *)fp && 4739255332Scy uptr < (void *)((char *)fp + fp->fr_size)) { 4740255332Scy bcopy(uptr, ptr, fp->fr_dsize); 4741255332Scy error = 0; 4742255332Scy } else { 4743255332Scy error = COPYIN(uptr, ptr, fp->fr_dsize); 4744255332Scy if (error != 0) { 4745255332Scy IPFERROR(17); 4746255332Scy error = EFAULT; 4747255332Scy goto donenolock; 4748255332Scy } 4749255332Scy } 4750145522Sdarrenr } else { 4751145522Sdarrenr ptr = uptr; 4752145522Sdarrenr } 4753145522Sdarrenr fp->fr_data = ptr; 4754255332Scy } else { 4755145522Sdarrenr fp->fr_data = NULL; 4756255332Scy } 4757145522Sdarrenr 4758145522Sdarrenr /* 4759145522Sdarrenr * Perform per-rule type sanity checks of their members. 4760255332Scy * All code after this needs to be aware that allocated memory 4761255332Scy * may need to be free'd before exiting. 4762145522Sdarrenr */ 4763145522Sdarrenr switch (fp->fr_type & ~FR_T_BUILTIN) 4764145522Sdarrenr { 4765145522Sdarrenr#if defined(IPFILTER_BPF) 4766145522Sdarrenr case FR_T_BPFOPC : 4767255332Scy if (fp->fr_dsize == 0) { 4768255332Scy IPFERROR(19); 4769255332Scy error = EINVAL; 4770255332Scy break; 4771255332Scy } 4772145522Sdarrenr if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) { 4773255332Scy IPFERROR(20); 4774255332Scy error = EINVAL; 4775255332Scy break; 4776145522Sdarrenr } 4777145522Sdarrenr break; 4778145522Sdarrenr#endif 4779145522Sdarrenr case FR_T_IPF : 4780255332Scy /* 4781255332Scy * Preparation for error case at the bottom of this function. 4782255332Scy */ 4783255332Scy if (fp->fr_datype == FRI_LOOKUP) 4784255332Scy fp->fr_dstptr = NULL; 4785255332Scy if (fp->fr_satype == FRI_LOOKUP) 4786255332Scy fp->fr_srcptr = NULL; 4787145522Sdarrenr 4788255332Scy if (fp->fr_dsize != sizeof(fripf_t)) { 4789255332Scy IPFERROR(21); 4790255332Scy error = EINVAL; 4791255332Scy break; 4792255332Scy } 4793255332Scy 4794145522Sdarrenr /* 4795145522Sdarrenr * Allowing a rule with both "keep state" and "with oow" is 4796145522Sdarrenr * pointless because adding a state entry to the table will 4797145522Sdarrenr * fail with the out of window (oow) flag set. 4798145522Sdarrenr */ 4799255332Scy if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW)) { 4800255332Scy IPFERROR(22); 4801255332Scy error = EINVAL; 4802255332Scy break; 4803255332Scy } 4804145522Sdarrenr 4805145522Sdarrenr switch (fp->fr_satype) 4806145522Sdarrenr { 4807145522Sdarrenr case FRI_BROADCAST : 4808145522Sdarrenr case FRI_DYNAMIC : 4809145522Sdarrenr case FRI_NETWORK : 4810145522Sdarrenr case FRI_NETMASKED : 4811145522Sdarrenr case FRI_PEERADDR : 4812255332Scy if (fp->fr_sifpidx < 0) { 4813255332Scy IPFERROR(23); 4814255332Scy error = EINVAL; 4815145522Sdarrenr } 4816145522Sdarrenr break; 4817145522Sdarrenr case FRI_LOOKUP : 4818255332Scy fp->fr_srcptr = ipf_findlookup(softc, unit, fp, 4819255332Scy &fp->fr_src6, 4820255332Scy &fp->fr_smsk6); 4821255332Scy if (fp->fr_srcfunc == NULL) { 4822255332Scy IPFERROR(132); 4823255332Scy error = ESRCH; 4824255332Scy break; 4825255332Scy } 4826145522Sdarrenr break; 4827255332Scy case FRI_NORMAL : 4828255332Scy break; 4829145522Sdarrenr default : 4830255332Scy IPFERROR(133); 4831255332Scy error = EINVAL; 4832145522Sdarrenr break; 4833145522Sdarrenr } 4834255332Scy if (error != 0) 4835255332Scy break; 4836145522Sdarrenr 4837145522Sdarrenr switch (fp->fr_datype) 4838145522Sdarrenr { 4839145522Sdarrenr case FRI_BROADCAST : 4840145522Sdarrenr case FRI_DYNAMIC : 4841145522Sdarrenr case FRI_NETWORK : 4842145522Sdarrenr case FRI_NETMASKED : 4843145522Sdarrenr case FRI_PEERADDR : 4844255332Scy if (fp->fr_difpidx < 0) { 4845255332Scy IPFERROR(24); 4846255332Scy error = EINVAL; 4847145522Sdarrenr } 4848145522Sdarrenr break; 4849145522Sdarrenr case FRI_LOOKUP : 4850255332Scy fp->fr_dstptr = ipf_findlookup(softc, unit, fp, 4851255332Scy &fp->fr_dst6, 4852255332Scy &fp->fr_dmsk6); 4853255332Scy if (fp->fr_dstfunc == NULL) { 4854255332Scy IPFERROR(134); 4855255332Scy error = ESRCH; 4856255332Scy } 4857145522Sdarrenr break; 4858255332Scy case FRI_NORMAL : 4859255332Scy break; 4860145522Sdarrenr default : 4861255332Scy IPFERROR(135); 4862255332Scy error = EINVAL; 4863145522Sdarrenr } 4864145522Sdarrenr break; 4865255332Scy 4866145522Sdarrenr case FR_T_NONE : 4867145522Sdarrenr case FR_T_CALLFUNC : 4868145522Sdarrenr case FR_T_COMPIPF : 4869145522Sdarrenr break; 4870255332Scy 4871255332Scy case FR_T_IPFEXPR : 4872255332Scy if (ipf_matcharray_verify(fp->fr_data, fp->fr_dsize) == -1) { 4873255332Scy IPFERROR(25); 4874255332Scy error = EINVAL; 4875255332Scy } 4876255332Scy break; 4877255332Scy 4878145522Sdarrenr default : 4879255332Scy IPFERROR(26); 4880255332Scy error = EINVAL; 4881255332Scy break; 4882255332Scy } 4883255332Scy if (error != 0) 4884255332Scy goto donenolock; 4885255332Scy 4886255332Scy if (fp->fr_tif.fd_name != -1) { 4887255332Scy if ((fp->fr_tif.fd_name < 0) || 4888255332Scy (fp->fr_tif.fd_name >= fp->fr_namelen)) { 4889255332Scy IPFERROR(139); 4890255332Scy error = EINVAL; 4891255332Scy goto donenolock; 4892145522Sdarrenr } 4893145522Sdarrenr } 4894145522Sdarrenr 4895255332Scy if (fp->fr_dif.fd_name != -1) { 4896255332Scy if ((fp->fr_dif.fd_name < 0) || 4897255332Scy (fp->fr_dif.fd_name >= fp->fr_namelen)) { 4898255332Scy IPFERROR(140); 4899255332Scy error = EINVAL; 4900255332Scy goto donenolock; 4901255332Scy } 4902255332Scy } 4903255332Scy 4904255332Scy if (fp->fr_rif.fd_name != -1) { 4905255332Scy if ((fp->fr_rif.fd_name < 0) || 4906255332Scy (fp->fr_rif.fd_name >= fp->fr_namelen)) { 4907255332Scy IPFERROR(141); 4908255332Scy error = EINVAL; 4909255332Scy goto donenolock; 4910255332Scy } 4911255332Scy } 4912255332Scy 4913145522Sdarrenr /* 4914145522Sdarrenr * Lookup all the interface names that are part of the rule. 4915145522Sdarrenr */ 4916255332Scy error = ipf_synclist(softc, fp, NULL); 4917255332Scy if (error != 0) 4918255332Scy goto donenolock; 4919145522Sdarrenr fp->fr_statecnt = 0; 4920255332Scy if (fp->fr_srctrack.ht_max_nodes != 0) 4921255332Scy ipf_rb_ht_init(&fp->fr_srctrack); 4922145522Sdarrenr 4923145522Sdarrenr /* 4924145522Sdarrenr * Look for an existing matching filter rule, but don't include the 4925145522Sdarrenr * next or interface pointer in the comparison (fr_next, fr_ifa). 4926145522Sdarrenr * This elminates rules which are indentical being loaded. Checksum 4927145522Sdarrenr * the constant part of the filter rule to make comparisons quicker 4928145522Sdarrenr * (this meaning no pointers are included). 4929145522Sdarrenr */ 4930145522Sdarrenr for (fp->fr_cksum = 0, p = (u_int *)&fp->fr_func, pp = &fp->fr_cksum; 4931145522Sdarrenr p < pp; p++) 4932145522Sdarrenr fp->fr_cksum += *p; 4933145522Sdarrenr pp = (u_int *)(fp->fr_caddr + fp->fr_dsize); 4934145522Sdarrenr for (p = (u_int *)fp->fr_data; p < pp; p++) 4935145522Sdarrenr fp->fr_cksum += *p; 4936145522Sdarrenr 4937255332Scy WRITE_ENTER(&softc->ipf_mutex); 4938170268Sdarrenr 4939170268Sdarrenr /* 4940170268Sdarrenr * Now that the filter rule lists are locked, we can walk the 4941170268Sdarrenr * chain of them without fear. 4942170268Sdarrenr */ 4943170268Sdarrenr ftail = fprev; 4944170268Sdarrenr for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) { 4945170268Sdarrenr if (fp->fr_collect <= f->fr_collect) { 4946170268Sdarrenr ftail = fprev; 4947170268Sdarrenr f = NULL; 4948170268Sdarrenr break; 4949170268Sdarrenr } 4950170268Sdarrenr fprev = ftail; 4951170268Sdarrenr } 4952145522Sdarrenr 4953147547Sdarrenr for (; (f = *ftail) != NULL; ftail = &f->fr_next) { 4954272995Scy if (ipf_rule_compare(fp, f) == 0) 4955145522Sdarrenr break; 4956147547Sdarrenr } 4957145522Sdarrenr 4958145522Sdarrenr /* 4959145522Sdarrenr * If zero'ing statistics, copy current to caller and zero. 4960145522Sdarrenr */ 4961255332Scy if (addrem == 2) { 4962255332Scy if (f == NULL) { 4963255332Scy IPFERROR(27); 4964145522Sdarrenr error = ESRCH; 4965255332Scy } else { 4966145522Sdarrenr /* 4967145522Sdarrenr * Copy and reduce lock because of impending copyout. 4968145522Sdarrenr * Well we should, but if we do then the atomicity of 4969145522Sdarrenr * this call and the correctness of fr_hits and 4970145522Sdarrenr * fr_bytes cannot be guaranteed. As it is, this code 4971145522Sdarrenr * only resets them to 0 if they are successfully 4972145522Sdarrenr * copied out into user space. 4973145522Sdarrenr */ 4974255332Scy bcopy((char *)f, (char *)fp, f->fr_size); 4975255332Scy /* MUTEX_DOWNGRADE(&softc->ipf_mutex); */ 4976145522Sdarrenr 4977145522Sdarrenr /* 4978145522Sdarrenr * When we copy this rule back out, set the data 4979145522Sdarrenr * pointer to be what it was in user space. 4980145522Sdarrenr */ 4981145522Sdarrenr fp->fr_data = uptr; 4982255332Scy error = ipf_outobj(softc, data, fp, IPFOBJ_FRENTRY); 4983145522Sdarrenr 4984145522Sdarrenr if (error == 0) { 4985145522Sdarrenr if ((f->fr_dsize != 0) && (uptr != NULL)) 4986145522Sdarrenr error = COPYOUT(f->fr_data, uptr, 4987145522Sdarrenr f->fr_dsize); 4988255332Scy if (error != 0) { 4989255332Scy IPFERROR(28); 4990170268Sdarrenr error = EFAULT; 4991255332Scy } 4992145522Sdarrenr if (error == 0) { 4993145522Sdarrenr f->fr_hits = 0; 4994145522Sdarrenr f->fr_bytes = 0; 4995145522Sdarrenr } 4996145522Sdarrenr } 4997145522Sdarrenr } 4998145522Sdarrenr 4999255332Scy if (makecopy != 0) { 5000255332Scy if (ptr != NULL) { 5001255332Scy KFREES(ptr, fp->fr_dsize); 5002255332Scy } 5003255332Scy KFREES(fp, fp->fr_size); 5004145522Sdarrenr } 5005255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 5006145522Sdarrenr return error; 5007145522Sdarrenr } 5008145522Sdarrenr 5009255332Scy if (!f) { 5010147547Sdarrenr /* 5011147547Sdarrenr * At the end of this, ftail must point to the place where the 5012147547Sdarrenr * new rule is to be saved/inserted/added. 5013147547Sdarrenr * For SIOCAD*FR, this should be the last rule in the group of 5014147547Sdarrenr * rules that have equal fr_collect fields. 5015147547Sdarrenr * For SIOCIN*FR, ... 5016147547Sdarrenr */ 5017147547Sdarrenr if (req == (ioctlcmd_t)SIOCADAFR || 5018147547Sdarrenr req == (ioctlcmd_t)SIOCADIFR) { 5019147547Sdarrenr 5020147547Sdarrenr for (ftail = fprev; (f = *ftail) != NULL; ) { 5021147547Sdarrenr if (f->fr_collect > fp->fr_collect) 5022147547Sdarrenr break; 5023147547Sdarrenr ftail = &f->fr_next; 5024272994Scy fprev = ftail; 5025147547Sdarrenr } 5026272994Scy ftail = fprev; 5027147547Sdarrenr f = NULL; 5028147547Sdarrenr ptr = NULL; 5029147547Sdarrenr } else if (req == (ioctlcmd_t)SIOCINAFR || 5030147547Sdarrenr req == (ioctlcmd_t)SIOCINIFR) { 5031147547Sdarrenr while ((f = *fprev) != NULL) { 5032147547Sdarrenr if (f->fr_collect >= fp->fr_collect) 5033147547Sdarrenr break; 5034147547Sdarrenr fprev = &f->fr_next; 5035147547Sdarrenr } 5036255332Scy ftail = fprev; 5037255332Scy if (fp->fr_hits != 0) { 5038147547Sdarrenr while (fp->fr_hits && (f = *ftail)) { 5039147547Sdarrenr if (f->fr_collect != fp->fr_collect) 5040147547Sdarrenr break; 5041147547Sdarrenr fprev = ftail; 5042255332Scy ftail = &f->fr_next; 5043147547Sdarrenr fp->fr_hits--; 5044147547Sdarrenr } 5045255332Scy } 5046255332Scy f = NULL; 5047255332Scy ptr = NULL; 5048145522Sdarrenr } 5049145522Sdarrenr } 5050145522Sdarrenr 5051145522Sdarrenr /* 5052145522Sdarrenr * Request to remove a rule. 5053145522Sdarrenr */ 5054255332Scy if (addrem == 1) { 5055255332Scy if (!f) { 5056255332Scy IPFERROR(29); 5057145522Sdarrenr error = ESRCH; 5058255332Scy } else { 5059145522Sdarrenr /* 5060145522Sdarrenr * Do not allow activity from user space to interfere 5061145522Sdarrenr * with rules not loaded that way. 5062145522Sdarrenr */ 5063145522Sdarrenr if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) { 5064255332Scy IPFERROR(30); 5065145522Sdarrenr error = EPERM; 5066145522Sdarrenr goto done; 5067145522Sdarrenr } 5068145522Sdarrenr 5069145522Sdarrenr /* 5070145522Sdarrenr * Return EBUSY if the rule is being reference by 5071172776Sdarrenr * something else (eg state information.) 5072145522Sdarrenr */ 5073145522Sdarrenr if (f->fr_ref > 1) { 5074255332Scy IPFERROR(31); 5075145522Sdarrenr error = EBUSY; 5076145522Sdarrenr goto done; 5077145522Sdarrenr } 5078145522Sdarrenr#ifdef IPFILTER_SCAN 5079255332Scy if (f->fr_isctag != -1 && 5080145522Sdarrenr (f->fr_isc != (struct ipscan *)-1)) 5081255332Scy ipf_scan_detachfr(f); 5082145522Sdarrenr#endif 5083255332Scy 5084145522Sdarrenr if (unit == IPL_LOGAUTH) { 5085255332Scy error = ipf_auth_precmd(softc, req, f, ftail); 5086145522Sdarrenr goto done; 5087145522Sdarrenr } 5088255332Scy 5089255332Scy ipf_rule_delete(softc, f, unit, set); 5090255332Scy 5091255332Scy need_free = makecopy; 5092145522Sdarrenr } 5093145522Sdarrenr } else { 5094145522Sdarrenr /* 5095145522Sdarrenr * Not removing, so we must be adding/inserting a rule. 5096145522Sdarrenr */ 5097255332Scy if (f != NULL) { 5098255332Scy IPFERROR(32); 5099145522Sdarrenr error = EEXIST; 5100255332Scy goto done; 5101145522Sdarrenr } 5102255332Scy if (unit == IPL_LOGAUTH) { 5103255332Scy error = ipf_auth_precmd(softc, req, fp, ftail); 5104255332Scy goto done; 5105255332Scy } 5106255332Scy 5107255332Scy MUTEX_NUKE(&fp->fr_lock); 5108255332Scy MUTEX_INIT(&fp->fr_lock, "filter rule lock"); 5109255332Scy if (fp->fr_die != 0) 5110255332Scy ipf_rule_expire_insert(softc, fp, set); 5111255332Scy 5112255332Scy fp->fr_hits = 0; 5113255332Scy if (makecopy != 0) 5114255332Scy fp->fr_ref = 1; 5115255332Scy fp->fr_pnext = ftail; 5116255332Scy fp->fr_next = *ftail; 5117272994Scy if (fp->fr_next != NULL) 5118272994Scy fp->fr_next->fr_pnext = &fp->fr_next; 5119255332Scy *ftail = fp; 5120255332Scy if (addrem == 0) 5121255332Scy ipf_fixskip(ftail, fp, 1); 5122255332Scy 5123255332Scy fp->fr_icmpgrp = NULL; 5124255332Scy if (fp->fr_icmphead != -1) { 5125255332Scy group = FR_NAME(fp, fr_icmphead); 5126255332Scy fg = ipf_group_add(softc, group, fp, 0, unit, set); 5127255332Scy fp->fr_icmpgrp = fg; 5128255332Scy } 5129255332Scy 5130255332Scy fp->fr_grphead = NULL; 5131255332Scy if (fp->fr_grhead != -1) { 5132255332Scy group = FR_NAME(fp, fr_grhead); 5133255332Scy fg = ipf_group_add(softc, group, fp, fp->fr_flags, 5134255332Scy unit, set); 5135255332Scy fp->fr_grphead = fg; 5136255332Scy } 5137145522Sdarrenr } 5138145522Sdarrenrdone: 5139255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 5140255332Scydonenolock: 5141255332Scy if (need_free || (error != 0)) { 5142255332Scy if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { 5143255332Scy if ((fp->fr_satype == FRI_LOOKUP) && 5144255332Scy (fp->fr_srcptr != NULL)) 5145255332Scy ipf_lookup_deref(softc, fp->fr_srctype, 5146255332Scy fp->fr_srcptr); 5147255332Scy if ((fp->fr_datype == FRI_LOOKUP) && 5148255332Scy (fp->fr_dstptr != NULL)) 5149255332Scy ipf_lookup_deref(softc, fp->fr_dsttype, 5150255332Scy fp->fr_dstptr); 5151255332Scy } 5152255332Scy if (fp->fr_grp != NULL) { 5153255332Scy WRITE_ENTER(&softc->ipf_mutex); 5154255332Scy ipf_group_del(softc, fp->fr_grp, fp); 5155255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 5156255332Scy } 5157255332Scy if ((ptr != NULL) && (makecopy != 0)) { 5158255332Scy KFREES(ptr, fp->fr_dsize); 5159255332Scy } 5160255332Scy KFREES(fp, fp->fr_size); 5161145522Sdarrenr } 5162145522Sdarrenr return (error); 5163145522Sdarrenr} 5164145522Sdarrenr 5165145522Sdarrenr 5166145522Sdarrenr/* ------------------------------------------------------------------------ */ 5167255332Scy/* Function: ipf_rule_delete */ 5168255332Scy/* Returns: Nil */ 5169255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5170255332Scy/* f(I) - pointer to the rule being deleted */ 5171255332Scy/* ftail(I) - pointer to the pointer to f */ 5172255332Scy/* unit(I) - device for which this is for */ 5173255332Scy/* set(I) - 1 or 0 (filter set) */ 5174255332Scy/* */ 5175255332Scy/* This function attempts to do what it can to delete a filter rule: remove */ 5176255332Scy/* it from any linked lists and remove any groups it is responsible for. */ 5177255332Scy/* But in the end, removing a rule can only drop the reference count - we */ 5178255332Scy/* must use that as the guide for whether or not it can be freed. */ 5179255332Scy/* ------------------------------------------------------------------------ */ 5180255332Scystatic void 5181255332Scyipf_rule_delete(softc, f, unit, set) 5182255332Scy ipf_main_softc_t *softc; 5183255332Scy frentry_t *f; 5184255332Scy int unit, set; 5185255332Scy{ 5186255332Scy 5187255332Scy /* 5188255332Scy * If fr_pdnext is set, then the rule is on the expire list, so 5189255332Scy * remove it from there. 5190255332Scy */ 5191255332Scy if (f->fr_pdnext != NULL) { 5192255332Scy *f->fr_pdnext = f->fr_dnext; 5193255332Scy if (f->fr_dnext != NULL) 5194255332Scy f->fr_dnext->fr_pdnext = f->fr_pdnext; 5195255332Scy f->fr_pdnext = NULL; 5196255332Scy f->fr_dnext = NULL; 5197255332Scy } 5198255332Scy 5199255332Scy ipf_fixskip(f->fr_pnext, f, -1); 5200255332Scy if (f->fr_pnext != NULL) 5201255332Scy *f->fr_pnext = f->fr_next; 5202255332Scy if (f->fr_next != NULL) 5203255332Scy f->fr_next->fr_pnext = f->fr_pnext; 5204255332Scy f->fr_pnext = NULL; 5205255332Scy f->fr_next = NULL; 5206255332Scy 5207255332Scy (void) ipf_derefrule(softc, &f); 5208255332Scy} 5209255332Scy 5210255332Scy/* ------------------------------------------------------------------------ */ 5211255332Scy/* Function: ipf_rule_expire_insert */ 5212255332Scy/* Returns: Nil */ 5213255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5214255332Scy/* f(I) - pointer to rule to be added to expire list */ 5215255332Scy/* set(I) - 1 or 0 (filter set) */ 5216255332Scy/* */ 5217255332Scy/* If the new rule has a given expiration time, insert it into the list of */ 5218255332Scy/* expiring rules with the ones to be removed first added to the front of */ 5219255332Scy/* the list. The insertion is O(n) but it is kept sorted for quick scans at */ 5220255332Scy/* expiration interval checks. */ 5221255332Scy/* ------------------------------------------------------------------------ */ 5222255332Scystatic void 5223255332Scyipf_rule_expire_insert(softc, f, set) 5224255332Scy ipf_main_softc_t *softc; 5225255332Scy frentry_t *f; 5226255332Scy int set; 5227255332Scy{ 5228255332Scy frentry_t *fr; 5229255332Scy 5230255332Scy /* 5231255332Scy */ 5232255332Scy 5233255332Scy f->fr_die = softc->ipf_ticks + IPF_TTLVAL(f->fr_die); 5234255332Scy for (fr = softc->ipf_rule_explist[set]; fr != NULL; 5235255332Scy fr = fr->fr_dnext) { 5236255332Scy if (f->fr_die < fr->fr_die) 5237255332Scy break; 5238255332Scy if (fr->fr_dnext == NULL) { 5239255332Scy /* 5240255332Scy * We've got to the last rule and everything 5241255332Scy * wanted to be expired before this new node, 5242255332Scy * so we have to tack it on the end... 5243255332Scy */ 5244255332Scy fr->fr_dnext = f; 5245255332Scy f->fr_pdnext = &fr->fr_dnext; 5246255332Scy fr = NULL; 5247255332Scy break; 5248255332Scy } 5249255332Scy } 5250255332Scy 5251255332Scy if (softc->ipf_rule_explist[set] == NULL) { 5252255332Scy softc->ipf_rule_explist[set] = f; 5253255332Scy f->fr_pdnext = &softc->ipf_rule_explist[set]; 5254255332Scy } else if (fr != NULL) { 5255255332Scy f->fr_dnext = fr; 5256255332Scy f->fr_pdnext = fr->fr_pdnext; 5257255332Scy fr->fr_pdnext = &f->fr_dnext; 5258255332Scy } 5259255332Scy} 5260255332Scy 5261255332Scy 5262255332Scy/* ------------------------------------------------------------------------ */ 5263255332Scy/* Function: ipf_findlookup */ 5264255332Scy/* Returns: NULL = failure, else success */ 5265255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5266255332Scy/* unit(I) - ipf device we want to find match for */ 5267255332Scy/* fp(I) - rule for which lookup is for */ 5268255332Scy/* addrp(I) - pointer to lookup information in address struct */ 5269255332Scy/* maskp(O) - pointer to lookup information for storage */ 5270255332Scy/* */ 5271255332Scy/* When using pools and hash tables to store addresses for matching in */ 5272255332Scy/* rules, it is necessary to resolve both the object referred to by the */ 5273255332Scy/* name or address (and return that pointer) and also provide the means by */ 5274255332Scy/* which to determine if an address belongs to that object to make the */ 5275255332Scy/* packet matching quicker. */ 5276255332Scy/* ------------------------------------------------------------------------ */ 5277255332Scystatic void * 5278255332Scyipf_findlookup(softc, unit, fr, addrp, maskp) 5279255332Scy ipf_main_softc_t *softc; 5280255332Scy int unit; 5281255332Scy frentry_t *fr; 5282255332Scy i6addr_t *addrp, *maskp; 5283255332Scy{ 5284255332Scy void *ptr = NULL; 5285255332Scy 5286255332Scy switch (addrp->iplookupsubtype) 5287255332Scy { 5288255332Scy case 0 : 5289255332Scy ptr = ipf_lookup_res_num(softc, unit, addrp->iplookuptype, 5290255332Scy addrp->iplookupnum, 5291255332Scy &maskp->iplookupfunc); 5292255332Scy break; 5293255332Scy case 1 : 5294255332Scy if (addrp->iplookupname < 0) 5295255332Scy break; 5296255332Scy if (addrp->iplookupname >= fr->fr_namelen) 5297255332Scy break; 5298255332Scy ptr = ipf_lookup_res_name(softc, unit, addrp->iplookuptype, 5299255332Scy fr->fr_names + addrp->iplookupname, 5300255332Scy &maskp->iplookupfunc); 5301255332Scy break; 5302255332Scy default : 5303255332Scy break; 5304255332Scy } 5305255332Scy 5306255332Scy return ptr; 5307255332Scy} 5308255332Scy 5309255332Scy 5310255332Scy/* ------------------------------------------------------------------------ */ 5311255332Scy/* Function: ipf_funcinit */ 5312145522Sdarrenr/* Returns: int - 0 == success, else ESRCH: cannot resolve rule details */ 5313255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5314255332Scy/* fr(I) - pointer to filter rule */ 5315145522Sdarrenr/* */ 5316145522Sdarrenr/* If a rule is a call rule, then check if the function it points to needs */ 5317145522Sdarrenr/* an init function to be called now the rule has been loaded. */ 5318145522Sdarrenr/* ------------------------------------------------------------------------ */ 5319255332Scystatic int 5320255332Scyipf_funcinit(softc, fr) 5321255332Scy ipf_main_softc_t *softc; 5322255332Scy frentry_t *fr; 5323145522Sdarrenr{ 5324145522Sdarrenr ipfunc_resolve_t *ft; 5325145522Sdarrenr int err; 5326145522Sdarrenr 5327255332Scy IPFERROR(34); 5328145522Sdarrenr err = ESRCH; 5329145522Sdarrenr 5330255332Scy for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) 5331145522Sdarrenr if (ft->ipfu_addr == fr->fr_func) { 5332145522Sdarrenr err = 0; 5333145522Sdarrenr if (ft->ipfu_init != NULL) 5334255332Scy err = (*ft->ipfu_init)(softc, fr); 5335145522Sdarrenr break; 5336145522Sdarrenr } 5337145522Sdarrenr return err; 5338145522Sdarrenr} 5339145522Sdarrenr 5340145522Sdarrenr 5341145522Sdarrenr/* ------------------------------------------------------------------------ */ 5342255332Scy/* Function: ipf_funcfini */ 5343255332Scy/* Returns: Nil */ 5344255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5345255332Scy/* fr(I) - pointer to filter rule */ 5346255332Scy/* */ 5347255332Scy/* For a given filter rule, call the matching "fini" function if the rule */ 5348255332Scy/* is using a known function that would have resulted in the "init" being */ 5349255332Scy/* called for ealier. */ 5350255332Scy/* ------------------------------------------------------------------------ */ 5351255332Scystatic void 5352255332Scyipf_funcfini(softc, fr) 5353255332Scy ipf_main_softc_t *softc; 5354255332Scy frentry_t *fr; 5355255332Scy{ 5356255332Scy ipfunc_resolve_t *ft; 5357255332Scy 5358255332Scy for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) 5359255332Scy if (ft->ipfu_addr == fr->fr_func) { 5360255332Scy if (ft->ipfu_fini != NULL) 5361255332Scy (void) (*ft->ipfu_fini)(softc, fr); 5362255332Scy break; 5363255332Scy } 5364255332Scy} 5365255332Scy 5366255332Scy 5367255332Scy/* ------------------------------------------------------------------------ */ 5368255332Scy/* Function: ipf_findfunc */ 5369145522Sdarrenr/* Returns: ipfunc_t - pointer to function if found, else NULL */ 5370145522Sdarrenr/* Parameters: funcptr(I) - function pointer to lookup */ 5371145522Sdarrenr/* */ 5372145522Sdarrenr/* Look for a function in the table of known functions. */ 5373145522Sdarrenr/* ------------------------------------------------------------------------ */ 5374255332Scystatic ipfunc_t 5375255332Scyipf_findfunc(funcptr) 5376255332Scy ipfunc_t funcptr; 5377145522Sdarrenr{ 5378145522Sdarrenr ipfunc_resolve_t *ft; 5379145522Sdarrenr 5380255332Scy for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) 5381145522Sdarrenr if (ft->ipfu_addr == funcptr) 5382145522Sdarrenr return funcptr; 5383145522Sdarrenr return NULL; 5384145522Sdarrenr} 5385145522Sdarrenr 5386145522Sdarrenr 5387145522Sdarrenr/* ------------------------------------------------------------------------ */ 5388255332Scy/* Function: ipf_resolvefunc */ 5389145522Sdarrenr/* Returns: int - 0 == success, else error */ 5390145522Sdarrenr/* Parameters: data(IO) - ioctl data pointer to ipfunc_resolve_t struct */ 5391145522Sdarrenr/* */ 5392145522Sdarrenr/* Copy in a ipfunc_resolve_t structure and then fill in the missing field. */ 5393145522Sdarrenr/* This will either be the function name (if the pointer is set) or the */ 5394145522Sdarrenr/* function pointer if the name is set. When found, fill in the other one */ 5395145522Sdarrenr/* so that the entire, complete, structure can be copied back to user space.*/ 5396145522Sdarrenr/* ------------------------------------------------------------------------ */ 5397255332Scyint 5398255332Scyipf_resolvefunc(softc, data) 5399255332Scy ipf_main_softc_t *softc; 5400255332Scy void *data; 5401145522Sdarrenr{ 5402145522Sdarrenr ipfunc_resolve_t res, *ft; 5403255332Scy int error; 5404145522Sdarrenr 5405255332Scy error = BCOPYIN(data, &res, sizeof(res)); 5406255332Scy if (error != 0) { 5407255332Scy IPFERROR(123); 5408172776Sdarrenr return EFAULT; 5409255332Scy } 5410145522Sdarrenr 5411145522Sdarrenr if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') { 5412255332Scy for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) 5413145522Sdarrenr if (strncmp(res.ipfu_name, ft->ipfu_name, 5414145522Sdarrenr sizeof(res.ipfu_name)) == 0) { 5415145522Sdarrenr res.ipfu_addr = ft->ipfu_addr; 5416145522Sdarrenr res.ipfu_init = ft->ipfu_init; 5417255332Scy if (COPYOUT(&res, data, sizeof(res)) != 0) { 5418255332Scy IPFERROR(35); 5419145522Sdarrenr return EFAULT; 5420255332Scy } 5421145522Sdarrenr return 0; 5422145522Sdarrenr } 5423145522Sdarrenr } 5424145522Sdarrenr if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') { 5425255332Scy for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) 5426145522Sdarrenr if (ft->ipfu_addr == res.ipfu_addr) { 5427145522Sdarrenr (void) strncpy(res.ipfu_name, ft->ipfu_name, 5428145522Sdarrenr sizeof(res.ipfu_name)); 5429145522Sdarrenr res.ipfu_init = ft->ipfu_init; 5430255332Scy if (COPYOUT(&res, data, sizeof(res)) != 0) { 5431255332Scy IPFERROR(36); 5432145522Sdarrenr return EFAULT; 5433255332Scy } 5434145522Sdarrenr return 0; 5435145522Sdarrenr } 5436145522Sdarrenr } 5437255332Scy IPFERROR(37); 5438145522Sdarrenr return ESRCH; 5439145522Sdarrenr} 5440145522Sdarrenr 5441145522Sdarrenr 5442255332Scy#if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && \ 5443255332Scy !defined(__FreeBSD__)) || \ 5444255332Scy FREEBSD_LT_REV(501000) || NETBSD_LT_REV(105000000) || \ 5445255332Scy OPENBSD_LT_REV(200006) 5446145522Sdarrenr/* 5447145522Sdarrenr * From: NetBSD 5448145522Sdarrenr * ppsratecheck(): packets (or events) per second limitation. 5449145522Sdarrenr */ 5450145522Sdarrenrint 5451145522Sdarrenrppsratecheck(lasttime, curpps, maxpps) 5452145522Sdarrenr struct timeval *lasttime; 5453145522Sdarrenr int *curpps; 5454145522Sdarrenr int maxpps; /* maximum pps allowed */ 5455145522Sdarrenr{ 5456145522Sdarrenr struct timeval tv, delta; 5457145522Sdarrenr int rv; 5458145522Sdarrenr 5459145522Sdarrenr GETKTIME(&tv); 5460145522Sdarrenr 5461145522Sdarrenr delta.tv_sec = tv.tv_sec - lasttime->tv_sec; 5462145522Sdarrenr delta.tv_usec = tv.tv_usec - lasttime->tv_usec; 5463145522Sdarrenr if (delta.tv_usec < 0) { 5464145522Sdarrenr delta.tv_sec--; 5465145522Sdarrenr delta.tv_usec += 1000000; 5466145522Sdarrenr } 5467145522Sdarrenr 5468145522Sdarrenr /* 5469145522Sdarrenr * check for 0,0 is so that the message will be seen at least once. 5470145522Sdarrenr * if more than one second have passed since the last update of 5471145522Sdarrenr * lasttime, reset the counter. 5472145522Sdarrenr * 5473145522Sdarrenr * we do increment *curpps even in *curpps < maxpps case, as some may 5474145522Sdarrenr * try to use *curpps for stat purposes as well. 5475145522Sdarrenr */ 5476145522Sdarrenr if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || 5477145522Sdarrenr delta.tv_sec >= 1) { 5478145522Sdarrenr *lasttime = tv; 5479145522Sdarrenr *curpps = 0; 5480145522Sdarrenr rv = 1; 5481145522Sdarrenr } else if (maxpps < 0) 5482145522Sdarrenr rv = 1; 5483145522Sdarrenr else if (*curpps < maxpps) 5484145522Sdarrenr rv = 1; 5485145522Sdarrenr else 5486145522Sdarrenr rv = 0; 5487145522Sdarrenr *curpps = *curpps + 1; 5488145522Sdarrenr 5489145522Sdarrenr return (rv); 5490145522Sdarrenr} 5491145522Sdarrenr#endif 5492145522Sdarrenr 5493145522Sdarrenr 5494145522Sdarrenr/* ------------------------------------------------------------------------ */ 5495255332Scy/* Function: ipf_derefrule */ 5496145522Sdarrenr/* Returns: int - 0 == rule freed up, else rule not freed */ 5497145522Sdarrenr/* Parameters: fr(I) - pointer to filter rule */ 5498145522Sdarrenr/* */ 5499145522Sdarrenr/* Decrement the reference counter to a rule by one. If it reaches zero, */ 5500145522Sdarrenr/* free it and any associated storage space being used by it. */ 5501145522Sdarrenr/* ------------------------------------------------------------------------ */ 5502255332Scyint 5503255332Scyipf_derefrule(softc, frp) 5504255332Scy ipf_main_softc_t *softc; 5505255332Scy frentry_t **frp; 5506145522Sdarrenr{ 5507145522Sdarrenr frentry_t *fr; 5508255332Scy frdest_t *fdp; 5509145522Sdarrenr 5510145522Sdarrenr fr = *frp; 5511170268Sdarrenr *frp = NULL; 5512145522Sdarrenr 5513145522Sdarrenr MUTEX_ENTER(&fr->fr_lock); 5514145522Sdarrenr fr->fr_ref--; 5515145522Sdarrenr if (fr->fr_ref == 0) { 5516145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 5517145522Sdarrenr MUTEX_DESTROY(&fr->fr_lock); 5518145522Sdarrenr 5519255332Scy ipf_funcfini(softc, fr); 5520145522Sdarrenr 5521255332Scy fdp = &fr->fr_tif; 5522255332Scy if (fdp->fd_type == FRD_DSTLIST) 5523255332Scy ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); 5524255332Scy 5525255332Scy fdp = &fr->fr_rif; 5526255332Scy if (fdp->fd_type == FRD_DSTLIST) 5527255332Scy ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); 5528255332Scy 5529255332Scy fdp = &fr->fr_dif; 5530255332Scy if (fdp->fd_type == FRD_DSTLIST) 5531255332Scy ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); 5532255332Scy 5533255332Scy if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && 5534255332Scy fr->fr_satype == FRI_LOOKUP) 5535255332Scy ipf_lookup_deref(softc, fr->fr_srctype, fr->fr_srcptr); 5536255332Scy if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && 5537255332Scy fr->fr_datype == FRI_LOOKUP) 5538255332Scy ipf_lookup_deref(softc, fr->fr_dsttype, fr->fr_dstptr); 5539255332Scy 5540255332Scy if (fr->fr_grp != NULL) 5541255332Scy ipf_group_del(softc, fr->fr_grp, fr); 5542255332Scy 5543255332Scy if (fr->fr_grphead != NULL) 5544255332Scy ipf_group_del(softc, fr->fr_grphead, fr); 5545255332Scy 5546255332Scy if (fr->fr_icmpgrp != NULL) 5547255332Scy ipf_group_del(softc, fr->fr_icmpgrp, fr); 5548255332Scy 5549145522Sdarrenr if ((fr->fr_flags & FR_COPIED) != 0) { 5550255332Scy if (fr->fr_dsize) { 5551255332Scy KFREES(fr->fr_data, fr->fr_dsize); 5552255332Scy } 5553255332Scy KFREES(fr, fr->fr_size); 5554145522Sdarrenr return 0; 5555145522Sdarrenr } 5556145522Sdarrenr return 1; 5557145522Sdarrenr } else { 5558145522Sdarrenr MUTEX_EXIT(&fr->fr_lock); 5559145522Sdarrenr } 5560145522Sdarrenr return -1; 5561145522Sdarrenr} 5562145522Sdarrenr 5563145522Sdarrenr 5564145522Sdarrenr/* ------------------------------------------------------------------------ */ 5565255332Scy/* Function: ipf_grpmapinit */ 5566145522Sdarrenr/* Returns: int - 0 == success, else ESRCH because table entry not found*/ 5567145522Sdarrenr/* Parameters: fr(I) - pointer to rule to find hash table for */ 5568145522Sdarrenr/* */ 5569145522Sdarrenr/* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr. */ 5570255332Scy/* fr_ptr is later used by ipf_srcgrpmap and ipf_dstgrpmap. */ 5571145522Sdarrenr/* ------------------------------------------------------------------------ */ 5572255332Scystatic int 5573255332Scyipf_grpmapinit(softc, fr) 5574255332Scy ipf_main_softc_t *softc; 5575255332Scy frentry_t *fr; 5576145522Sdarrenr{ 5577145522Sdarrenr char name[FR_GROUPLEN]; 5578145522Sdarrenr iphtable_t *iph; 5579145522Sdarrenr 5580145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL) 5581145522Sdarrenr SNPRINTF(name, sizeof(name), "%d", fr->fr_arg); 5582145522Sdarrenr#else 5583145522Sdarrenr (void) sprintf(name, "%d", fr->fr_arg); 5584145522Sdarrenr#endif 5585255332Scy iph = ipf_lookup_find_htable(softc, IPL_LOGIPF, name); 5586255332Scy if (iph == NULL) { 5587255332Scy IPFERROR(38); 5588145522Sdarrenr return ESRCH; 5589255332Scy } 5590255332Scy if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT)) { 5591255332Scy IPFERROR(39); 5592145522Sdarrenr return ESRCH; 5593255332Scy } 5594255332Scy iph->iph_ref++; 5595145522Sdarrenr fr->fr_ptr = iph; 5596145522Sdarrenr return 0; 5597145522Sdarrenr} 5598145522Sdarrenr 5599145522Sdarrenr 5600145522Sdarrenr/* ------------------------------------------------------------------------ */ 5601255332Scy/* Function: ipf_grpmapfini */ 5602255332Scy/* Returns: int - 0 == success, else ESRCH because table entry not found*/ 5603255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 5604255332Scy/* fr(I) - pointer to rule to release hash table for */ 5605255332Scy/* */ 5606255332Scy/* For rules that have had ipf_grpmapinit called, ipf_lookup_deref needs to */ 5607255332Scy/* be called to undo what ipf_grpmapinit caused to be done. */ 5608255332Scy/* ------------------------------------------------------------------------ */ 5609255332Scystatic int 5610255332Scyipf_grpmapfini(softc, fr) 5611255332Scy ipf_main_softc_t *softc; 5612255332Scy frentry_t *fr; 5613255332Scy{ 5614255332Scy iphtable_t *iph; 5615255332Scy iph = fr->fr_ptr; 5616255332Scy if (iph != NULL) 5617255332Scy ipf_lookup_deref(softc, IPLT_HASH, iph); 5618255332Scy return 0; 5619255332Scy} 5620255332Scy 5621255332Scy 5622255332Scy/* ------------------------------------------------------------------------ */ 5623255332Scy/* Function: ipf_srcgrpmap */ 5624145522Sdarrenr/* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ 5625145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5626145522Sdarrenr/* passp(IO) - pointer to current/new filter decision (unused) */ 5627145522Sdarrenr/* */ 5628145522Sdarrenr/* Look for a rule group head in a hash table, using the source address as */ 5629145522Sdarrenr/* the key, and descend into that group and continue matching rules against */ 5630145522Sdarrenr/* the packet. */ 5631145522Sdarrenr/* ------------------------------------------------------------------------ */ 5632255332Scyfrentry_t * 5633255332Scyipf_srcgrpmap(fin, passp) 5634255332Scy fr_info_t *fin; 5635255332Scy u_32_t *passp; 5636130886Sdarrenr{ 5637145522Sdarrenr frgroup_t *fg; 5638145522Sdarrenr void *rval; 5639130886Sdarrenr 5640255332Scy rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, 5641255332Scy &fin->fin_src); 5642145522Sdarrenr if (rval == NULL) 5643130886Sdarrenr return NULL; 5644130886Sdarrenr 5645145522Sdarrenr fg = rval; 5646145522Sdarrenr fin->fin_fr = fg->fg_start; 5647255332Scy (void) ipf_scanlist(fin, *passp); 5648145522Sdarrenr return fin->fin_fr; 5649145522Sdarrenr} 5650145522Sdarrenr 5651145522Sdarrenr 5652145522Sdarrenr/* ------------------------------------------------------------------------ */ 5653255332Scy/* Function: ipf_dstgrpmap */ 5654145522Sdarrenr/* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ 5655145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 5656145522Sdarrenr/* passp(IO) - pointer to current/new filter decision (unused) */ 5657145522Sdarrenr/* */ 5658145522Sdarrenr/* Look for a rule group head in a hash table, using the destination */ 5659145522Sdarrenr/* address as the key, and descend into that group and continue matching */ 5660145522Sdarrenr/* rules against the packet. */ 5661145522Sdarrenr/* ------------------------------------------------------------------------ */ 5662255332Scyfrentry_t * 5663255332Scyipf_dstgrpmap(fin, passp) 5664255332Scy fr_info_t *fin; 5665255332Scy u_32_t *passp; 5666145522Sdarrenr{ 5667145522Sdarrenr frgroup_t *fg; 5668145522Sdarrenr void *rval; 5669145522Sdarrenr 5670255332Scy rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, 5671255332Scy &fin->fin_dst); 5672145522Sdarrenr if (rval == NULL) 5673145522Sdarrenr return NULL; 5674145522Sdarrenr 5675145522Sdarrenr fg = rval; 5676145522Sdarrenr fin->fin_fr = fg->fg_start; 5677255332Scy (void) ipf_scanlist(fin, *passp); 5678145522Sdarrenr return fin->fin_fr; 5679145522Sdarrenr} 5680145522Sdarrenr 5681145522Sdarrenr/* 5682145522Sdarrenr * Queue functions 5683145522Sdarrenr * =============== 5684255332Scy * These functions manage objects on queues for efficient timeouts. There 5685255332Scy * are a number of system defined queues as well as user defined timeouts. 5686255332Scy * It is expected that a lock is held in the domain in which the queue 5687255332Scy * belongs (i.e. either state or NAT) when calling any of these functions 5688255332Scy * that prevents ipf_freetimeoutqueue() from being called at the same time 5689255332Scy * as any other. 5690145522Sdarrenr */ 5691145522Sdarrenr 5692145522Sdarrenr 5693145522Sdarrenr/* ------------------------------------------------------------------------ */ 5694255332Scy/* Function: ipf_addtimeoutqueue */ 5695145522Sdarrenr/* Returns: struct ifqtq * - NULL if malloc fails, else pointer to */ 5696145522Sdarrenr/* timeout queue with given interval. */ 5697145522Sdarrenr/* Parameters: parent(I) - pointer to pointer to parent node of this list */ 5698145522Sdarrenr/* of interface queues. */ 5699145522Sdarrenr/* seconds(I) - timeout value in seconds for this queue. */ 5700145522Sdarrenr/* */ 5701145522Sdarrenr/* This routine first looks for a timeout queue that matches the interval */ 5702145522Sdarrenr/* being requested. If it finds one, increments the reference counter and */ 5703145522Sdarrenr/* returns a pointer to it. If none are found, it allocates a new one and */ 5704145522Sdarrenr/* inserts it at the top of the list. */ 5705145522Sdarrenr/* */ 5706145522Sdarrenr/* Locking. */ 5707145522Sdarrenr/* It is assumed that the caller of this function has an appropriate lock */ 5708145522Sdarrenr/* held (exclusively) in the domain that encompases 'parent'. */ 5709145522Sdarrenr/* ------------------------------------------------------------------------ */ 5710255332Scyipftq_t * 5711255332Scyipf_addtimeoutqueue(softc, parent, seconds) 5712255332Scy ipf_main_softc_t *softc; 5713255332Scy ipftq_t **parent; 5714255332Scy u_int seconds; 5715145522Sdarrenr{ 5716145522Sdarrenr ipftq_t *ifq; 5717145522Sdarrenr u_int period; 5718145522Sdarrenr 5719145522Sdarrenr period = seconds * IPF_HZ_DIVIDE; 5720145522Sdarrenr 5721255332Scy MUTEX_ENTER(&softc->ipf_timeoutlock); 5722145522Sdarrenr for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) { 5723145522Sdarrenr if (ifq->ifq_ttl == period) { 5724145522Sdarrenr /* 5725145522Sdarrenr * Reset the delete flag, if set, so the structure 5726145522Sdarrenr * gets reused rather than freed and reallocated. 5727145522Sdarrenr */ 5728145522Sdarrenr MUTEX_ENTER(&ifq->ifq_lock); 5729145522Sdarrenr ifq->ifq_flags &= ~IFQF_DELETE; 5730145522Sdarrenr ifq->ifq_ref++; 5731145522Sdarrenr MUTEX_EXIT(&ifq->ifq_lock); 5732255332Scy MUTEX_EXIT(&softc->ipf_timeoutlock); 5733145522Sdarrenr 5734145522Sdarrenr return ifq; 5735145522Sdarrenr } 5736145522Sdarrenr } 5737145522Sdarrenr 5738145522Sdarrenr KMALLOC(ifq, ipftq_t *); 5739145522Sdarrenr if (ifq != NULL) { 5740255332Scy MUTEX_NUKE(&ifq->ifq_lock); 5741255332Scy IPFTQ_INIT(ifq, period, "ipftq mutex"); 5742145522Sdarrenr ifq->ifq_next = *parent; 5743145522Sdarrenr ifq->ifq_pnext = parent; 5744145522Sdarrenr ifq->ifq_flags = IFQF_USER; 5745255332Scy ifq->ifq_ref++; 5746145522Sdarrenr *parent = ifq; 5747255332Scy softc->ipf_userifqs++; 5748145522Sdarrenr } 5749255332Scy MUTEX_EXIT(&softc->ipf_timeoutlock); 5750145522Sdarrenr return ifq; 5751145522Sdarrenr} 5752145522Sdarrenr 5753145522Sdarrenr 5754145522Sdarrenr/* ------------------------------------------------------------------------ */ 5755255332Scy/* Function: ipf_deletetimeoutqueue */ 5756145522Sdarrenr/* Returns: int - new reference count value of the timeout queue */ 5757145522Sdarrenr/* Parameters: ifq(I) - timeout queue which is losing a reference. */ 5758145522Sdarrenr/* Locks: ifq->ifq_lock */ 5759145522Sdarrenr/* */ 5760145522Sdarrenr/* This routine must be called when we're discarding a pointer to a timeout */ 5761145522Sdarrenr/* queue object, taking care of the reference counter. */ 5762145522Sdarrenr/* */ 5763145522Sdarrenr/* Now that this just sets a DELETE flag, it requires the expire code to */ 5764145522Sdarrenr/* check the list of user defined timeout queues and call the free function */ 5765145522Sdarrenr/* below (currently commented out) to stop memory leaking. It is done this */ 5766145522Sdarrenr/* way because the locking may not be sufficient to safely do a free when */ 5767145522Sdarrenr/* this function is called. */ 5768145522Sdarrenr/* ------------------------------------------------------------------------ */ 5769255332Scyint 5770255332Scyipf_deletetimeoutqueue(ifq) 5771255332Scy ipftq_t *ifq; 5772145522Sdarrenr{ 5773145522Sdarrenr 5774145522Sdarrenr ifq->ifq_ref--; 5775145522Sdarrenr if ((ifq->ifq_ref == 0) && ((ifq->ifq_flags & IFQF_USER) != 0)) { 5776145522Sdarrenr ifq->ifq_flags |= IFQF_DELETE; 5777145522Sdarrenr } 5778145522Sdarrenr 5779145522Sdarrenr return ifq->ifq_ref; 5780145522Sdarrenr} 5781145522Sdarrenr 5782145522Sdarrenr 5783145522Sdarrenr/* ------------------------------------------------------------------------ */ 5784255332Scy/* Function: ipf_freetimeoutqueue */ 5785145522Sdarrenr/* Parameters: ifq(I) - timeout queue which is losing a reference. */ 5786145522Sdarrenr/* Returns: Nil */ 5787145522Sdarrenr/* */ 5788145522Sdarrenr/* Locking: */ 5789145522Sdarrenr/* It is assumed that the caller of this function has an appropriate lock */ 5790145522Sdarrenr/* held (exclusively) in the domain that encompases the callers "domain". */ 5791145522Sdarrenr/* The ifq_lock for this structure should not be held. */ 5792145522Sdarrenr/* */ 5793255332Scy/* Remove a user defined timeout queue from the list of queues it is in and */ 5794145522Sdarrenr/* tidy up after this is done. */ 5795145522Sdarrenr/* ------------------------------------------------------------------------ */ 5796255332Scyvoid 5797255332Scyipf_freetimeoutqueue(softc, ifq) 5798255332Scy ipf_main_softc_t *softc; 5799255332Scy ipftq_t *ifq; 5800145522Sdarrenr{ 5801145522Sdarrenr 5802145522Sdarrenr if (((ifq->ifq_flags & IFQF_DELETE) == 0) || (ifq->ifq_ref != 0) || 5803145522Sdarrenr ((ifq->ifq_flags & IFQF_USER) == 0)) { 5804255332Scy printf("ipf_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n", 5805145522Sdarrenr (u_long)ifq, ifq->ifq_flags, ifq->ifq_ttl, 5806145522Sdarrenr ifq->ifq_ref); 5807145522Sdarrenr return; 5808145522Sdarrenr } 5809145522Sdarrenr 5810145522Sdarrenr /* 5811145522Sdarrenr * Remove from its position in the list. 5812145522Sdarrenr */ 5813145522Sdarrenr *ifq->ifq_pnext = ifq->ifq_next; 5814145522Sdarrenr if (ifq->ifq_next != NULL) 5815145522Sdarrenr ifq->ifq_next->ifq_pnext = ifq->ifq_pnext; 5816255332Scy ifq->ifq_next = NULL; 5817255332Scy ifq->ifq_pnext = NULL; 5818145522Sdarrenr 5819145522Sdarrenr MUTEX_DESTROY(&ifq->ifq_lock); 5820255332Scy ATOMIC_DEC(softc->ipf_userifqs); 5821145522Sdarrenr KFREE(ifq); 5822145522Sdarrenr} 5823145522Sdarrenr 5824145522Sdarrenr 5825145522Sdarrenr/* ------------------------------------------------------------------------ */ 5826255332Scy/* Function: ipf_deletequeueentry */ 5827145522Sdarrenr/* Returns: Nil */ 5828145522Sdarrenr/* Parameters: tqe(I) - timeout queue entry to delete */ 5829145522Sdarrenr/* */ 5830145522Sdarrenr/* Remove a tail queue entry from its queue and make it an orphan. */ 5831255332Scy/* ipf_deletetimeoutqueue is called to make sure the reference count on the */ 5832255332Scy/* queue is correct. We can't, however, call ipf_freetimeoutqueue because */ 5833145522Sdarrenr/* the correct lock(s) may not be held that would make it safe to do so. */ 5834145522Sdarrenr/* ------------------------------------------------------------------------ */ 5835255332Scyvoid 5836255332Scyipf_deletequeueentry(tqe) 5837255332Scy ipftqent_t *tqe; 5838145522Sdarrenr{ 5839145522Sdarrenr ipftq_t *ifq; 5840145522Sdarrenr 5841145522Sdarrenr ifq = tqe->tqe_ifq; 5842145522Sdarrenr 5843145522Sdarrenr MUTEX_ENTER(&ifq->ifq_lock); 5844145522Sdarrenr 5845145522Sdarrenr if (tqe->tqe_pnext != NULL) { 5846145522Sdarrenr *tqe->tqe_pnext = tqe->tqe_next; 5847145522Sdarrenr if (tqe->tqe_next != NULL) 5848145522Sdarrenr tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; 5849145522Sdarrenr else /* we must be the tail anyway */ 5850145522Sdarrenr ifq->ifq_tail = tqe->tqe_pnext; 5851145522Sdarrenr 5852145522Sdarrenr tqe->tqe_pnext = NULL; 5853145522Sdarrenr tqe->tqe_ifq = NULL; 5854145522Sdarrenr } 5855145522Sdarrenr 5856255332Scy (void) ipf_deletetimeoutqueue(ifq); 5857255332Scy ASSERT(ifq->ifq_ref > 0); 5858145522Sdarrenr 5859145522Sdarrenr MUTEX_EXIT(&ifq->ifq_lock); 5860145522Sdarrenr} 5861145522Sdarrenr 5862145522Sdarrenr 5863145522Sdarrenr/* ------------------------------------------------------------------------ */ 5864255332Scy/* Function: ipf_queuefront */ 5865145522Sdarrenr/* Returns: Nil */ 5866145522Sdarrenr/* Parameters: tqe(I) - pointer to timeout queue entry */ 5867145522Sdarrenr/* */ 5868145522Sdarrenr/* Move a queue entry to the front of the queue, if it isn't already there. */ 5869145522Sdarrenr/* ------------------------------------------------------------------------ */ 5870255332Scyvoid 5871255332Scyipf_queuefront(tqe) 5872255332Scy ipftqent_t *tqe; 5873145522Sdarrenr{ 5874145522Sdarrenr ipftq_t *ifq; 5875145522Sdarrenr 5876145522Sdarrenr ifq = tqe->tqe_ifq; 5877145522Sdarrenr if (ifq == NULL) 5878145522Sdarrenr return; 5879145522Sdarrenr 5880145522Sdarrenr MUTEX_ENTER(&ifq->ifq_lock); 5881145522Sdarrenr if (ifq->ifq_head != tqe) { 5882145522Sdarrenr *tqe->tqe_pnext = tqe->tqe_next; 5883145522Sdarrenr if (tqe->tqe_next) 5884145522Sdarrenr tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; 5885145522Sdarrenr else 5886145522Sdarrenr ifq->ifq_tail = tqe->tqe_pnext; 5887145522Sdarrenr 5888145522Sdarrenr tqe->tqe_next = ifq->ifq_head; 5889145522Sdarrenr ifq->ifq_head->tqe_pnext = &tqe->tqe_next; 5890145522Sdarrenr ifq->ifq_head = tqe; 5891145522Sdarrenr tqe->tqe_pnext = &ifq->ifq_head; 5892145522Sdarrenr } 5893145522Sdarrenr MUTEX_EXIT(&ifq->ifq_lock); 5894145522Sdarrenr} 5895145522Sdarrenr 5896145522Sdarrenr 5897145522Sdarrenr/* ------------------------------------------------------------------------ */ 5898255332Scy/* Function: ipf_queueback */ 5899145522Sdarrenr/* Returns: Nil */ 5900255332Scy/* Parameters: ticks(I) - ipf tick time to use with this call */ 5901255332Scy/* tqe(I) - pointer to timeout queue entry */ 5902145522Sdarrenr/* */ 5903145522Sdarrenr/* Move a queue entry to the back of the queue, if it isn't already there. */ 5904255332Scy/* We use use ticks to calculate the expiration and mark for when we last */ 5905255332Scy/* touched the structure. */ 5906145522Sdarrenr/* ------------------------------------------------------------------------ */ 5907255332Scyvoid 5908255332Scyipf_queueback(ticks, tqe) 5909255332Scy u_long ticks; 5910255332Scy ipftqent_t *tqe; 5911145522Sdarrenr{ 5912145522Sdarrenr ipftq_t *ifq; 5913145522Sdarrenr 5914145522Sdarrenr ifq = tqe->tqe_ifq; 5915145522Sdarrenr if (ifq == NULL) 5916145522Sdarrenr return; 5917255332Scy tqe->tqe_die = ticks + ifq->ifq_ttl; 5918255332Scy tqe->tqe_touched = ticks; 5919145522Sdarrenr 5920145522Sdarrenr MUTEX_ENTER(&ifq->ifq_lock); 5921161356Sguido if (tqe->tqe_next != NULL) { /* at the end already ? */ 5922161356Sguido /* 5923161356Sguido * Remove from list 5924161356Sguido */ 5925161356Sguido *tqe->tqe_pnext = tqe->tqe_next; 5926161356Sguido tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; 5927161356Sguido 5928161356Sguido /* 5929161356Sguido * Make it the last entry. 5930161356Sguido */ 5931161356Sguido tqe->tqe_next = NULL; 5932161356Sguido tqe->tqe_pnext = ifq->ifq_tail; 5933161356Sguido *ifq->ifq_tail = tqe; 5934161356Sguido ifq->ifq_tail = &tqe->tqe_next; 5935145522Sdarrenr } 5936145522Sdarrenr MUTEX_EXIT(&ifq->ifq_lock); 5937145522Sdarrenr} 5938145522Sdarrenr 5939145522Sdarrenr 5940145522Sdarrenr/* ------------------------------------------------------------------------ */ 5941255332Scy/* Function: ipf_queueappend */ 5942145522Sdarrenr/* Returns: Nil */ 5943255332Scy/* Parameters: ticks(I) - ipf tick time to use with this call */ 5944255332Scy/* tqe(I) - pointer to timeout queue entry */ 5945145522Sdarrenr/* ifq(I) - pointer to timeout queue */ 5946145522Sdarrenr/* parent(I) - owing object pointer */ 5947145522Sdarrenr/* */ 5948145522Sdarrenr/* Add a new item to this queue and put it on the very end. */ 5949255332Scy/* We use use ticks to calculate the expiration and mark for when we last */ 5950255332Scy/* touched the structure. */ 5951145522Sdarrenr/* ------------------------------------------------------------------------ */ 5952255332Scyvoid 5953255332Scyipf_queueappend(ticks, tqe, ifq, parent) 5954255332Scy u_long ticks; 5955255332Scy ipftqent_t *tqe; 5956255332Scy ipftq_t *ifq; 5957255332Scy void *parent; 5958145522Sdarrenr{ 5959145522Sdarrenr 5960145522Sdarrenr MUTEX_ENTER(&ifq->ifq_lock); 5961145522Sdarrenr tqe->tqe_parent = parent; 5962145522Sdarrenr tqe->tqe_pnext = ifq->ifq_tail; 5963145522Sdarrenr *ifq->ifq_tail = tqe; 5964145522Sdarrenr ifq->ifq_tail = &tqe->tqe_next; 5965145522Sdarrenr tqe->tqe_next = NULL; 5966145522Sdarrenr tqe->tqe_ifq = ifq; 5967255332Scy tqe->tqe_die = ticks + ifq->ifq_ttl; 5968255332Scy tqe->tqe_touched = ticks; 5969145522Sdarrenr ifq->ifq_ref++; 5970145522Sdarrenr MUTEX_EXIT(&ifq->ifq_lock); 5971145522Sdarrenr} 5972145522Sdarrenr 5973145522Sdarrenr 5974145522Sdarrenr/* ------------------------------------------------------------------------ */ 5975255332Scy/* Function: ipf_movequeue */ 5976145522Sdarrenr/* Returns: Nil */ 5977145522Sdarrenr/* Parameters: tq(I) - pointer to timeout queue information */ 5978145522Sdarrenr/* oifp(I) - old timeout queue entry was on */ 5979145522Sdarrenr/* nifp(I) - new timeout queue to put entry on */ 5980145522Sdarrenr/* */ 5981145522Sdarrenr/* Move a queue entry from one timeout queue to another timeout queue. */ 5982145522Sdarrenr/* If it notices that the current entry is already last and does not need */ 5983145522Sdarrenr/* to move queue, the return. */ 5984145522Sdarrenr/* ------------------------------------------------------------------------ */ 5985255332Scyvoid 5986255332Scyipf_movequeue(ticks, tqe, oifq, nifq) 5987255332Scy u_long ticks; 5988255332Scy ipftqent_t *tqe; 5989255332Scy ipftq_t *oifq, *nifq; 5990145522Sdarrenr{ 5991255332Scy 5992145522Sdarrenr /* 5993255332Scy * If the queue hasn't changed and we last touched this entry at the 5994255332Scy * same ipf time, then we're not going to achieve anything by either 5995255332Scy * changing the ttl or moving it on the queue. 5996145522Sdarrenr */ 5997255332Scy if (oifq == nifq && tqe->tqe_touched == ticks) 5998255332Scy return; 5999255332Scy 6000255332Scy /* 6001255332Scy * For any of this to be outside the lock, there is a risk that two 6002255332Scy * packets entering simultaneously, with one changing to a different 6003255332Scy * queue and one not, could end up with things in a bizarre state. 6004255332Scy */ 6005145522Sdarrenr MUTEX_ENTER(&oifq->ifq_lock); 6006145522Sdarrenr 6007255332Scy tqe->tqe_touched = ticks; 6008255332Scy tqe->tqe_die = ticks + nifq->ifq_ttl; 6009255332Scy /* 6010255332Scy * Is the operation here going to be a no-op ? 6011255332Scy */ 6012255332Scy if (oifq == nifq) { 6013255332Scy if ((tqe->tqe_next == NULL) || 6014255332Scy (tqe->tqe_next->tqe_die == tqe->tqe_die)) { 6015255332Scy MUTEX_EXIT(&oifq->ifq_lock); 6016255332Scy return; 6017255332Scy } 6018255332Scy } 6019130886Sdarrenr 6020255332Scy /* 6021255332Scy * Remove from the old queue 6022255332Scy */ 6023255332Scy *tqe->tqe_pnext = tqe->tqe_next; 6024255332Scy if (tqe->tqe_next) 6025255332Scy tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; 6026255332Scy else 6027255332Scy oifq->ifq_tail = tqe->tqe_pnext; 6028255332Scy tqe->tqe_next = NULL; 6029130886Sdarrenr 6030255332Scy /* 6031255332Scy * If we're moving from one queue to another, release the 6032255332Scy * lock on the old queue and get a lock on the new queue. 6033255332Scy * For user defined queues, if we're moving off it, call 6034255332Scy * delete in case it can now be freed. 6035255332Scy */ 6036255332Scy if (oifq != nifq) { 6037255332Scy tqe->tqe_ifq = NULL; 6038145522Sdarrenr 6039255332Scy (void) ipf_deletetimeoutqueue(oifq); 6040145522Sdarrenr 6041255332Scy MUTEX_EXIT(&oifq->ifq_lock); 6042145522Sdarrenr 6043255332Scy MUTEX_ENTER(&nifq->ifq_lock); 6044255332Scy 6045255332Scy tqe->tqe_ifq = nifq; 6046255332Scy nifq->ifq_ref++; 6047145522Sdarrenr } 6048255332Scy 6049255332Scy /* 6050255332Scy * Add to the bottom of the new queue 6051255332Scy */ 6052255332Scy tqe->tqe_pnext = nifq->ifq_tail; 6053255332Scy *nifq->ifq_tail = tqe; 6054255332Scy nifq->ifq_tail = &tqe->tqe_next; 6055145522Sdarrenr MUTEX_EXIT(&nifq->ifq_lock); 6056145522Sdarrenr} 6057145522Sdarrenr 6058145522Sdarrenr 6059145522Sdarrenr/* ------------------------------------------------------------------------ */ 6060255332Scy/* Function: ipf_updateipid */ 6061145522Sdarrenr/* Returns: int - 0 == success, -1 == error (packet should be droppped) */ 6062145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 6063145522Sdarrenr/* */ 6064145522Sdarrenr/* When we are doing NAT, change the IP of every packet to represent a */ 6065145522Sdarrenr/* single sequence of packets coming from the host, hiding any host */ 6066145522Sdarrenr/* specific sequencing that might otherwise be revealed. If the packet is */ 6067145522Sdarrenr/* a fragment, then store the 'new' IPid in the fragment cache and look up */ 6068145522Sdarrenr/* the fragment cache for non-leading fragments. If a non-leading fragment */ 6069145522Sdarrenr/* has no match in the cache, return an error. */ 6070145522Sdarrenr/* ------------------------------------------------------------------------ */ 6071255332Scystatic int 6072255332Scyipf_updateipid(fin) 6073255332Scy fr_info_t *fin; 6074145522Sdarrenr{ 6075145522Sdarrenr u_short id, ido, sums; 6076145522Sdarrenr u_32_t sumd, sum; 6077145522Sdarrenr ip_t *ip; 6078145522Sdarrenr 6079145522Sdarrenr if (fin->fin_off != 0) { 6080255332Scy sum = ipf_frag_ipidknown(fin); 6081145522Sdarrenr if (sum == 0xffffffff) 6082145522Sdarrenr return -1; 6083145522Sdarrenr sum &= 0xffff; 6084145522Sdarrenr id = (u_short)sum; 6085145522Sdarrenr } else { 6086255332Scy id = ipf_nextipid(fin); 6087145522Sdarrenr if (fin->fin_off == 0 && (fin->fin_flx & FI_FRAG) != 0) 6088255332Scy (void) ipf_frag_ipidnew(fin, (u_32_t)id); 6089145522Sdarrenr } 6090145522Sdarrenr 6091145522Sdarrenr ip = fin->fin_ip; 6092145522Sdarrenr ido = ntohs(ip->ip_id); 6093145522Sdarrenr if (id == ido) 6094145522Sdarrenr return 0; 6095145522Sdarrenr ip->ip_id = htons(id); 6096145522Sdarrenr CALC_SUMD(ido, id, sumd); /* DESTRUCTIVE MACRO! id,ido change */ 6097145522Sdarrenr sum = (~ntohs(ip->ip_sum)) & 0xffff; 6098145522Sdarrenr sum += sumd; 6099145522Sdarrenr sum = (sum >> 16) + (sum & 0xffff); 6100145522Sdarrenr sum = (sum >> 16) + (sum & 0xffff); 6101145522Sdarrenr sums = ~(u_short)sum; 6102145522Sdarrenr ip->ip_sum = htons(sums); 6103145522Sdarrenr return 0; 6104145522Sdarrenr} 6105145522Sdarrenr 6106145522Sdarrenr 6107145522Sdarrenr#ifdef NEED_FRGETIFNAME 6108145522Sdarrenr/* ------------------------------------------------------------------------ */ 6109255332Scy/* Function: ipf_getifname */ 6110145522Sdarrenr/* Returns: char * - pointer to interface name */ 6111145522Sdarrenr/* Parameters: ifp(I) - pointer to network interface */ 6112145522Sdarrenr/* buffer(O) - pointer to where to store interface name */ 6113145522Sdarrenr/* */ 6114145522Sdarrenr/* Constructs an interface name in the buffer passed. The buffer passed is */ 6115145522Sdarrenr/* expected to be at least LIFNAMSIZ in bytes big. If buffer is passed in */ 6116145522Sdarrenr/* as a NULL pointer then return a pointer to a static array. */ 6117145522Sdarrenr/* ------------------------------------------------------------------------ */ 6118255332Scychar * 6119255332Scyipf_getifname(ifp, buffer) 6120255332Scy struct ifnet *ifp; 6121255332Scy char *buffer; 6122145522Sdarrenr{ 6123145522Sdarrenr static char namebuf[LIFNAMSIZ]; 6124145522Sdarrenr# if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \ 6125153876Sguido defined(__sgi) || defined(linux) || defined(_AIX51) || \ 6126145522Sdarrenr (defined(sun) && !defined(__SVR4) && !defined(__svr4__)) 6127145522Sdarrenr int unit, space; 6128145522Sdarrenr char temp[20]; 6129145522Sdarrenr char *s; 6130145522Sdarrenr# endif 6131145522Sdarrenr 6132145522Sdarrenr if (buffer == NULL) 6133145522Sdarrenr buffer = namebuf; 6134145522Sdarrenr (void) strncpy(buffer, ifp->if_name, LIFNAMSIZ); 6135145522Sdarrenr buffer[LIFNAMSIZ - 1] = '\0'; 6136145522Sdarrenr# if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \ 6137153876Sguido defined(__sgi) || defined(_AIX51) || \ 6138145522Sdarrenr (defined(sun) && !defined(__SVR4) && !defined(__svr4__)) 6139145522Sdarrenr for (s = buffer; *s; s++) 6140145522Sdarrenr ; 6141145522Sdarrenr unit = ifp->if_unit; 6142145522Sdarrenr space = LIFNAMSIZ - (s - buffer); 6143255332Scy if ((space > 0) && (unit >= 0)) { 6144145522Sdarrenr# if defined(SNPRINTF) && defined(_KERNEL) 6145145522Sdarrenr SNPRINTF(temp, sizeof(temp), "%d", unit); 6146145522Sdarrenr# else 6147145522Sdarrenr (void) sprintf(temp, "%d", unit); 6148145522Sdarrenr# endif 6149145522Sdarrenr (void) strncpy(s, temp, space); 6150145522Sdarrenr } 6151145522Sdarrenr# endif 6152145522Sdarrenr return buffer; 6153145522Sdarrenr} 6154145522Sdarrenr#endif 6155145522Sdarrenr 6156145522Sdarrenr 6157145522Sdarrenr/* ------------------------------------------------------------------------ */ 6158255332Scy/* Function: ipf_ioctlswitch */ 6159145522Sdarrenr/* Returns: int - -1 continue processing, else ioctl return value */ 6160145522Sdarrenr/* Parameters: unit(I) - device unit opened */ 6161145522Sdarrenr/* data(I) - pointer to ioctl data */ 6162145522Sdarrenr/* cmd(I) - ioctl command */ 6163145522Sdarrenr/* mode(I) - mode value */ 6164170268Sdarrenr/* uid(I) - uid making the ioctl call */ 6165170268Sdarrenr/* ctx(I) - pointer to context data */ 6166145522Sdarrenr/* */ 6167145522Sdarrenr/* Based on the value of unit, call the appropriate ioctl handler or return */ 6168145522Sdarrenr/* EIO if ipfilter is not running. Also checks if write perms are req'd */ 6169255332Scy/* for the device in order to execute the ioctl. A special case is made */ 6170255332Scy/* SIOCIPFINTERROR so that the same code isn't required in every handler. */ 6171255332Scy/* The context data pointer is passed through as this is used as the key */ 6172255332Scy/* for locating a matching token for continued access for walking lists, */ 6173255332Scy/* etc. */ 6174145522Sdarrenr/* ------------------------------------------------------------------------ */ 6175255332Scyint 6176255332Scyipf_ioctlswitch(softc, unit, data, cmd, mode, uid, ctx) 6177255332Scy ipf_main_softc_t *softc; 6178255332Scy int unit, mode, uid; 6179255332Scy ioctlcmd_t cmd; 6180255332Scy void *data, *ctx; 6181145522Sdarrenr{ 6182145522Sdarrenr int error = 0; 6183145522Sdarrenr 6184255332Scy switch (cmd) 6185255332Scy { 6186255332Scy case SIOCIPFINTERROR : 6187255332Scy error = BCOPYOUT(&softc->ipf_interror, data, 6188255332Scy sizeof(softc->ipf_interror)); 6189255332Scy if (error != 0) { 6190255332Scy IPFERROR(40); 6191255332Scy error = EFAULT; 6192255332Scy } 6193255332Scy return error; 6194255332Scy default : 6195255332Scy break; 6196255332Scy } 6197255332Scy 6198145522Sdarrenr switch (unit) 6199145522Sdarrenr { 6200145522Sdarrenr case IPL_LOGIPF : 6201255332Scy error = ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx); 6202145522Sdarrenr break; 6203145522Sdarrenr case IPL_LOGNAT : 6204255332Scy if (softc->ipf_running > 0) { 6205255332Scy error = ipf_nat_ioctl(softc, data, cmd, mode, 6206255332Scy uid, ctx); 6207255332Scy } else { 6208255332Scy IPFERROR(42); 6209145522Sdarrenr error = EIO; 6210255332Scy } 6211145522Sdarrenr break; 6212145522Sdarrenr case IPL_LOGSTATE : 6213255332Scy if (softc->ipf_running > 0) { 6214255332Scy error = ipf_state_ioctl(softc, data, cmd, mode, 6215255332Scy uid, ctx); 6216255332Scy } else { 6217255332Scy IPFERROR(43); 6218145522Sdarrenr error = EIO; 6219255332Scy } 6220145522Sdarrenr break; 6221145522Sdarrenr case IPL_LOGAUTH : 6222255332Scy if (softc->ipf_running > 0) { 6223255332Scy error = ipf_auth_ioctl(softc, data, cmd, mode, 6224255332Scy uid, ctx); 6225255332Scy } else { 6226255332Scy IPFERROR(44); 6227145522Sdarrenr error = EIO; 6228255332Scy } 6229145522Sdarrenr break; 6230145522Sdarrenr case IPL_LOGSYNC : 6231255332Scy if (softc->ipf_running > 0) { 6232255332Scy error = ipf_sync_ioctl(softc, data, cmd, mode, 6233255332Scy uid, ctx); 6234255332Scy } else { 6235145522Sdarrenr error = EIO; 6236255332Scy IPFERROR(45); 6237255332Scy } 6238145522Sdarrenr break; 6239145522Sdarrenr case IPL_LOGSCAN : 6240145522Sdarrenr#ifdef IPFILTER_SCAN 6241255332Scy if (softc->ipf_running > 0) 6242255332Scy error = ipf_scan_ioctl(softc, data, cmd, mode, 6243255332Scy uid, ctx); 6244145522Sdarrenr else 6245145522Sdarrenr#endif 6246255332Scy { 6247145522Sdarrenr error = EIO; 6248255332Scy IPFERROR(46); 6249255332Scy } 6250145522Sdarrenr break; 6251145522Sdarrenr case IPL_LOGLOOKUP : 6252255332Scy if (softc->ipf_running > 0) { 6253255332Scy error = ipf_lookup_ioctl(softc, data, cmd, mode, 6254255332Scy uid, ctx); 6255255332Scy } else { 6256145522Sdarrenr error = EIO; 6257255332Scy IPFERROR(47); 6258255332Scy } 6259145522Sdarrenr break; 6260145522Sdarrenr default : 6261255332Scy IPFERROR(48); 6262145522Sdarrenr error = EIO; 6263145522Sdarrenr break; 6264145522Sdarrenr } 6265145522Sdarrenr 6266145522Sdarrenr return error; 6267145522Sdarrenr} 6268145522Sdarrenr 6269145522Sdarrenr 6270145522Sdarrenr/* 6271145522Sdarrenr * This array defines the expected size of objects coming into the kernel 6272255332Scy * for the various recognised object types. The first column is flags (see 6273255332Scy * below), 2nd column is current size, 3rd column is the version number of 6274255332Scy * when the current size became current. 6275255332Scy * Flags: 6276255332Scy * 1 = minimum size, not absolute size 6277145522Sdarrenr */ 6278255332Scystatic int ipf_objbytes[IPFOBJ_COUNT][3] = { 6279255332Scy { 1, sizeof(struct frentry), 5010000 }, /* 0 */ 6280255332Scy { 1, sizeof(struct friostat), 5010000 }, 6281255332Scy { 0, sizeof(struct fr_info), 5010000 }, 6282255332Scy { 0, sizeof(struct ipf_authstat), 4010100 }, 6283255332Scy { 0, sizeof(struct ipfrstat), 5010000 }, 6284255332Scy { 1, sizeof(struct ipnat), 5010000 }, /* 5 */ 6285255332Scy { 0, sizeof(struct natstat), 5010000 }, 6286255332Scy { 0, sizeof(struct ipstate_save), 5010000 }, 6287255332Scy { 1, sizeof(struct nat_save), 5010000 }, 6288255332Scy { 0, sizeof(struct natlookup), 5010000 }, 6289255332Scy { 1, sizeof(struct ipstate), 5010000 }, /* 10 */ 6290255332Scy { 0, sizeof(struct ips_stat), 5010000 }, 6291255332Scy { 0, sizeof(struct frauth), 5010000 }, 6292255332Scy { 0, sizeof(struct ipftune), 4010100 }, 6293255332Scy { 0, sizeof(struct nat), 5010000 }, 6294255332Scy { 0, sizeof(struct ipfruleiter), 4011400 }, /* 15 */ 6295255332Scy { 0, sizeof(struct ipfgeniter), 4011400 }, 6296255332Scy { 0, sizeof(struct ipftable), 4011400 }, 6297255332Scy { 0, sizeof(struct ipflookupiter), 4011400 }, 6298170268Sdarrenr { 0, sizeof(struct ipftq) * IPF_TCP_NSTATES }, 6299255332Scy { 1, 0, 0 }, /* IPFEXPR */ 6300255332Scy { 0, 0, 0 }, /* PROXYCTL */ 6301255332Scy { 0, sizeof (struct fripf), 5010000 } 6302145522Sdarrenr}; 6303145522Sdarrenr 6304145522Sdarrenr 6305145522Sdarrenr/* ------------------------------------------------------------------------ */ 6306255332Scy/* Function: ipf_inobj */ 6307145522Sdarrenr/* Returns: int - 0 = success, else failure */ 6308255332Scy/* Parameters: softc(I) - soft context pointerto work with */ 6309255332Scy/* data(I) - pointer to ioctl data */ 6310255332Scy/* objp(O) - where to store ipfobj structure */ 6311255332Scy/* ptr(I) - pointer to data to copy out */ 6312255332Scy/* type(I) - type of structure being moved */ 6313145522Sdarrenr/* */ 6314145522Sdarrenr/* Copy in the contents of what the ipfobj_t points to. In future, we */ 6315145522Sdarrenr/* add things to check for version numbers, sizes, etc, to make it backward */ 6316145522Sdarrenr/* compatible at the ABI for user land. */ 6317255332Scy/* If objp is not NULL then we assume that the caller wants to see what is */ 6318255332Scy/* in the ipfobj_t structure being copied in. As an example, this can tell */ 6319255332Scy/* the caller what version of ipfilter the ioctl program was written to. */ 6320145522Sdarrenr/* ------------------------------------------------------------------------ */ 6321255332Scyint 6322255332Scyipf_inobj(softc, data, objp, ptr, type) 6323255332Scy ipf_main_softc_t *softc; 6324255332Scy void *data; 6325255332Scy ipfobj_t *objp; 6326255332Scy void *ptr; 6327255332Scy int type; 6328145522Sdarrenr{ 6329145522Sdarrenr ipfobj_t obj; 6330255332Scy int error; 6331255332Scy int size; 6332145522Sdarrenr 6333255332Scy if ((type < 0) || (type >= IPFOBJ_COUNT)) { 6334255332Scy IPFERROR(49); 6335145522Sdarrenr return EINVAL; 6336255332Scy } 6337145522Sdarrenr 6338255332Scy if (objp == NULL) 6339255332Scy objp = &obj; 6340255332Scy error = BCOPYIN(data, objp, sizeof(*objp)); 6341255332Scy if (error != 0) { 6342255332Scy IPFERROR(124); 6343172776Sdarrenr return EFAULT; 6344255332Scy } 6345145522Sdarrenr 6346255332Scy if (objp->ipfo_type != type) { 6347255332Scy IPFERROR(50); 6348145522Sdarrenr return EINVAL; 6349255332Scy } 6350145522Sdarrenr 6351255332Scy if (objp->ipfo_rev >= ipf_objbytes[type][2]) { 6352255332Scy if ((ipf_objbytes[type][0] & 1) != 0) { 6353255332Scy if (objp->ipfo_size < ipf_objbytes[type][1]) { 6354255332Scy IPFERROR(51); 6355255332Scy return EINVAL; 6356255332Scy } 6357255332Scy size = ipf_objbytes[type][1]; 6358255332Scy } else if (objp->ipfo_size == ipf_objbytes[type][1]) { 6359255332Scy size = objp->ipfo_size; 6360255332Scy } else { 6361255332Scy IPFERROR(52); 6362145522Sdarrenr return EINVAL; 6363255332Scy } 6364255332Scy error = COPYIN(objp->ipfo_ptr, ptr, size); 6365255332Scy if (error != 0) { 6366255332Scy IPFERROR(55); 6367255332Scy error = EFAULT; 6368255332Scy } 6369255332Scy } else { 6370255332Scy#ifdef IPFILTER_COMPAT 6371255332Scy error = ipf_in_compat(softc, objp, ptr, 0); 6372145522Sdarrenr#else 6373255332Scy IPFERROR(54); 6374255332Scy error = EINVAL; 6375145522Sdarrenr#endif 6376145522Sdarrenr } 6377145522Sdarrenr return error; 6378145522Sdarrenr} 6379145522Sdarrenr 6380145522Sdarrenr 6381145522Sdarrenr/* ------------------------------------------------------------------------ */ 6382255332Scy/* Function: ipf_inobjsz */ 6383145522Sdarrenr/* Returns: int - 0 = success, else failure */ 6384255332Scy/* Parameters: softc(I) - soft context pointerto work with */ 6385255332Scy/* data(I) - pointer to ioctl data */ 6386255332Scy/* ptr(I) - pointer to store real data in */ 6387255332Scy/* type(I) - type of structure being moved */ 6388255332Scy/* sz(I) - size of data to copy */ 6389145522Sdarrenr/* */ 6390255332Scy/* As per ipf_inobj, except the size of the object to copy in is passed in */ 6391145522Sdarrenr/* but it must not be smaller than the size defined for the type and the */ 6392145522Sdarrenr/* type must allow for varied sized objects. The extra requirement here is */ 6393145522Sdarrenr/* that sz must match the size of the object being passed in - this is not */ 6394255332Scy/* not possible nor required in ipf_inobj(). */ 6395145522Sdarrenr/* ------------------------------------------------------------------------ */ 6396255332Scyint 6397255332Scyipf_inobjsz(softc, data, ptr, type, sz) 6398255332Scy ipf_main_softc_t *softc; 6399255332Scy void *data; 6400255332Scy void *ptr; 6401255332Scy int type, sz; 6402145522Sdarrenr{ 6403145522Sdarrenr ipfobj_t obj; 6404145522Sdarrenr int error; 6405145522Sdarrenr 6406255332Scy if ((type < 0) || (type >= IPFOBJ_COUNT)) { 6407255332Scy IPFERROR(56); 6408145522Sdarrenr return EINVAL; 6409255332Scy } 6410145522Sdarrenr 6411172776Sdarrenr error = BCOPYIN(data, &obj, sizeof(obj)); 6412255332Scy if (error != 0) { 6413255332Scy IPFERROR(125); 6414172776Sdarrenr return EFAULT; 6415255332Scy } 6416145522Sdarrenr 6417255332Scy if (obj.ipfo_type != type) { 6418255332Scy IPFERROR(58); 6419145522Sdarrenr return EINVAL; 6420255332Scy } 6421145522Sdarrenr 6422255332Scy if (obj.ipfo_rev >= ipf_objbytes[type][2]) { 6423255332Scy if (((ipf_objbytes[type][0] & 1) == 0) || 6424255332Scy (sz < ipf_objbytes[type][1])) { 6425255332Scy IPFERROR(57); 6426255332Scy return EINVAL; 6427255332Scy } 6428255332Scy error = COPYIN(obj.ipfo_ptr, ptr, sz); 6429255332Scy if (error != 0) { 6430255332Scy IPFERROR(61); 6431255332Scy error = EFAULT; 6432255332Scy } 6433255332Scy } else { 6434255332Scy#ifdef IPFILTER_COMPAT 6435255332Scy error = ipf_in_compat(softc, &obj, ptr, sz); 6436145522Sdarrenr#else 6437255332Scy IPFERROR(60); 6438255332Scy error = EINVAL; 6439145522Sdarrenr#endif 6440255332Scy } 6441145522Sdarrenr return error; 6442145522Sdarrenr} 6443145522Sdarrenr 6444145522Sdarrenr 6445145522Sdarrenr/* ------------------------------------------------------------------------ */ 6446255332Scy/* Function: ipf_outobjsz */ 6447145522Sdarrenr/* Returns: int - 0 = success, else failure */ 6448145522Sdarrenr/* Parameters: data(I) - pointer to ioctl data */ 6449145522Sdarrenr/* ptr(I) - pointer to store real data in */ 6450145522Sdarrenr/* type(I) - type of structure being moved */ 6451145522Sdarrenr/* sz(I) - size of data to copy */ 6452145522Sdarrenr/* */ 6453255332Scy/* As per ipf_outobj, except the size of the object to copy out is passed in*/ 6454145522Sdarrenr/* but it must not be smaller than the size defined for the type and the */ 6455145522Sdarrenr/* type must allow for varied sized objects. The extra requirement here is */ 6456145522Sdarrenr/* that sz must match the size of the object being passed in - this is not */ 6457255332Scy/* not possible nor required in ipf_outobj(). */ 6458145522Sdarrenr/* ------------------------------------------------------------------------ */ 6459255332Scyint 6460255332Scyipf_outobjsz(softc, data, ptr, type, sz) 6461255332Scy ipf_main_softc_t *softc; 6462255332Scy void *data; 6463255332Scy void *ptr; 6464255332Scy int type, sz; 6465145522Sdarrenr{ 6466145522Sdarrenr ipfobj_t obj; 6467145522Sdarrenr int error; 6468145522Sdarrenr 6469255332Scy if ((type < 0) || (type >= IPFOBJ_COUNT)) { 6470255332Scy IPFERROR(62); 6471145522Sdarrenr return EINVAL; 6472255332Scy } 6473145522Sdarrenr 6474172776Sdarrenr error = BCOPYIN(data, &obj, sizeof(obj)); 6475255332Scy if (error != 0) { 6476255332Scy IPFERROR(127); 6477172776Sdarrenr return EFAULT; 6478255332Scy } 6479145522Sdarrenr 6480255332Scy if (obj.ipfo_type != type) { 6481255332Scy IPFERROR(63); 6482145522Sdarrenr return EINVAL; 6483255332Scy } 6484145522Sdarrenr 6485255332Scy if (obj.ipfo_rev >= ipf_objbytes[type][2]) { 6486255332Scy if (((ipf_objbytes[type][0] & 1) == 0) || 6487255332Scy (sz < ipf_objbytes[type][1])) { 6488255332Scy IPFERROR(146); 6489255332Scy return EINVAL; 6490255332Scy } 6491255332Scy error = COPYOUT(ptr, obj.ipfo_ptr, sz); 6492255332Scy if (error != 0) { 6493255332Scy IPFERROR(66); 6494255332Scy error = EFAULT; 6495255332Scy } 6496255332Scy } else { 6497255332Scy#ifdef IPFILTER_COMPAT 6498255332Scy error = ipf_out_compat(softc, &obj, ptr); 6499145522Sdarrenr#else 6500255332Scy IPFERROR(65); 6501255332Scy error = EINVAL; 6502145522Sdarrenr#endif 6503255332Scy } 6504145522Sdarrenr return error; 6505145522Sdarrenr} 6506145522Sdarrenr 6507145522Sdarrenr 6508145522Sdarrenr/* ------------------------------------------------------------------------ */ 6509255332Scy/* Function: ipf_outobj */ 6510145522Sdarrenr/* Returns: int - 0 = success, else failure */ 6511145522Sdarrenr/* Parameters: data(I) - pointer to ioctl data */ 6512145522Sdarrenr/* ptr(I) - pointer to store real data in */ 6513145522Sdarrenr/* type(I) - type of structure being moved */ 6514145522Sdarrenr/* */ 6515145522Sdarrenr/* Copy out the contents of what ptr is to where ipfobj points to. In */ 6516145522Sdarrenr/* future, we add things to check for version numbers, sizes, etc, to make */ 6517145522Sdarrenr/* it backward compatible at the ABI for user land. */ 6518145522Sdarrenr/* ------------------------------------------------------------------------ */ 6519255332Scyint 6520255332Scyipf_outobj(softc, data, ptr, type) 6521255332Scy ipf_main_softc_t *softc; 6522255332Scy void *data; 6523255332Scy void *ptr; 6524255332Scy int type; 6525145522Sdarrenr{ 6526145522Sdarrenr ipfobj_t obj; 6527145522Sdarrenr int error; 6528145522Sdarrenr 6529255332Scy if ((type < 0) || (type >= IPFOBJ_COUNT)) { 6530255332Scy IPFERROR(67); 6531145522Sdarrenr return EINVAL; 6532255332Scy } 6533145522Sdarrenr 6534172776Sdarrenr error = BCOPYIN(data, &obj, sizeof(obj)); 6535255332Scy if (error != 0) { 6536255332Scy IPFERROR(126); 6537172776Sdarrenr return EFAULT; 6538255332Scy } 6539145522Sdarrenr 6540255332Scy if (obj.ipfo_type != type) { 6541255332Scy IPFERROR(68); 6542145522Sdarrenr return EINVAL; 6543255332Scy } 6544145522Sdarrenr 6545255332Scy if (obj.ipfo_rev >= ipf_objbytes[type][2]) { 6546255332Scy if ((ipf_objbytes[type][0] & 1) != 0) { 6547255332Scy if (obj.ipfo_size < ipf_objbytes[type][1]) { 6548255332Scy IPFERROR(69); 6549255332Scy return EINVAL; 6550255332Scy } 6551255332Scy } else if (obj.ipfo_size != ipf_objbytes[type][1]) { 6552255332Scy IPFERROR(70); 6553145522Sdarrenr return EINVAL; 6554255332Scy } 6555255332Scy 6556255332Scy error = COPYOUT(ptr, obj.ipfo_ptr, obj.ipfo_size); 6557255332Scy if (error != 0) { 6558255332Scy IPFERROR(73); 6559255332Scy error = EFAULT; 6560255332Scy } 6561255332Scy } else { 6562255332Scy#ifdef IPFILTER_COMPAT 6563255332Scy error = ipf_out_compat(softc, &obj, ptr); 6564255332Scy#else 6565255332Scy IPFERROR(72); 6566255332Scy error = EINVAL; 6567255332Scy#endif 6568255332Scy } 6569255332Scy return error; 6570255332Scy} 6571255332Scy 6572255332Scy 6573255332Scy/* ------------------------------------------------------------------------ */ 6574255332Scy/* Function: ipf_outobjk */ 6575255332Scy/* Returns: int - 0 = success, else failure */ 6576255332Scy/* Parameters: obj(I) - pointer to data description structure */ 6577255332Scy/* ptr(I) - pointer to kernel data to copy out */ 6578255332Scy/* */ 6579255332Scy/* In the above functions, the ipfobj_t structure is copied into the kernel,*/ 6580255332Scy/* telling ipfilter how to copy out data. In this instance, the ipfobj_t is */ 6581255332Scy/* already populated with information and now we just need to use it. */ 6582255332Scy/* There is no need for this function to have a "type" parameter as there */ 6583255332Scy/* is no point in validating information that comes from the kernel with */ 6584255332Scy/* itself. */ 6585255332Scy/* ------------------------------------------------------------------------ */ 6586255332Scyint 6587255332Scyipf_outobjk(softc, obj, ptr) 6588255332Scy ipf_main_softc_t *softc; 6589255332Scy ipfobj_t *obj; 6590255332Scy void *ptr; 6591255332Scy{ 6592255332Scy int type = obj->ipfo_type; 6593255332Scy int error; 6594255332Scy 6595255332Scy if ((type < 0) || (type >= IPFOBJ_COUNT)) { 6596255332Scy IPFERROR(147); 6597145522Sdarrenr return EINVAL; 6598255332Scy } 6599255332Scy 6600255332Scy if (obj->ipfo_rev >= ipf_objbytes[type][2]) { 6601255332Scy if ((ipf_objbytes[type][0] & 1) != 0) { 6602255332Scy if (obj->ipfo_size < ipf_objbytes[type][1]) { 6603255332Scy IPFERROR(148); 6604255332Scy return EINVAL; 6605255332Scy } 6606255332Scy 6607255332Scy } else if (obj->ipfo_size != ipf_objbytes[type][1]) { 6608255332Scy IPFERROR(149); 6609255332Scy return EINVAL; 6610255332Scy } 6611255332Scy 6612255332Scy error = COPYOUT(ptr, obj->ipfo_ptr, obj->ipfo_size); 6613255332Scy if (error != 0) { 6614255332Scy IPFERROR(150); 6615255332Scy error = EFAULT; 6616255332Scy } 6617255332Scy } else { 6618255332Scy#ifdef IPFILTER_COMPAT 6619255332Scy error = ipf_out_compat(softc, obj, ptr); 6620145522Sdarrenr#else 6621255332Scy IPFERROR(151); 6622255332Scy error = EINVAL; 6623145522Sdarrenr#endif 6624255332Scy } 6625145522Sdarrenr return error; 6626145522Sdarrenr} 6627145522Sdarrenr 6628145522Sdarrenr 6629145522Sdarrenr/* ------------------------------------------------------------------------ */ 6630255332Scy/* Function: ipf_checkl4sum */ 6631145522Sdarrenr/* Returns: int - 0 = good, -1 = bad, 1 = cannot check */ 6632145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 6633145522Sdarrenr/* */ 6634145522Sdarrenr/* If possible, calculate the layer 4 checksum for the packet. If this is */ 6635145522Sdarrenr/* not possible, return without indicating a failure or success but in a */ 6636255332Scy/* way that is ditinguishable. This function should only be called by the */ 6637255332Scy/* ipf_checkv6sum() for each platform. */ 6638145522Sdarrenr/* ------------------------------------------------------------------------ */ 6639255332ScyINLINE int 6640255332Scyipf_checkl4sum(fin) 6641255332Scy fr_info_t *fin; 6642145522Sdarrenr{ 6643145522Sdarrenr u_short sum, hdrsum, *csump; 6644145522Sdarrenr udphdr_t *udp; 6645145522Sdarrenr int dosum; 6646145522Sdarrenr 6647145522Sdarrenr /* 6648145522Sdarrenr * If the TCP packet isn't a fragment, isn't too short and otherwise 6649145522Sdarrenr * isn't already considered "bad", then validate the checksum. If 6650145522Sdarrenr * this check fails then considered the packet to be "bad". 6651145522Sdarrenr */ 6652145522Sdarrenr if ((fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) != 0) 6653145522Sdarrenr return 1; 6654145522Sdarrenr 6655349927Scy DT2(l4sumo, int, fin->fin_out, int, (int)fin->fin_p); 6656349927Scy if (fin->fin_out == 1) { 6657349927Scy fin->fin_cksum = FI_CK_SUMOK; 6658349927Scy return 0; 6659349927Scy } 6660349927Scy 6661145522Sdarrenr csump = NULL; 6662145522Sdarrenr hdrsum = 0; 6663145522Sdarrenr dosum = 0; 6664145522Sdarrenr sum = 0; 6665145522Sdarrenr 6666255332Scy switch (fin->fin_p) 6667255332Scy { 6668255332Scy case IPPROTO_TCP : 6669255332Scy csump = &((tcphdr_t *)fin->fin_dp)->th_sum; 6670255332Scy dosum = 1; 6671255332Scy break; 6672255332Scy 6673255332Scy case IPPROTO_UDP : 6674255332Scy udp = fin->fin_dp; 6675255332Scy if (udp->uh_sum != 0) { 6676255332Scy csump = &udp->uh_sum; 6677145522Sdarrenr dosum = 1; 6678255332Scy } 6679255332Scy break; 6680145522Sdarrenr 6681255332Scy#ifdef USE_INET6 6682255332Scy case IPPROTO_ICMPV6 : 6683255332Scy csump = &((struct icmp6_hdr *)fin->fin_dp)->icmp6_cksum; 6684255332Scy dosum = 1; 6685255332Scy break; 6686255332Scy#endif 6687145522Sdarrenr 6688255332Scy case IPPROTO_ICMP : 6689255332Scy csump = &((struct icmp *)fin->fin_dp)->icmp_cksum; 6690255332Scy dosum = 1; 6691255332Scy break; 6692145522Sdarrenr 6693255332Scy default : 6694255332Scy return 1; 6695255332Scy /*NOTREACHED*/ 6696255332Scy } 6697145522Sdarrenr 6698352866Scy if (csump != NULL) { 6699255332Scy hdrsum = *csump; 6700352866Scy if (fin->fin_p == IPPROTO_UDP && hdrsum == 0xffff) 6701352866Scy hdrsum = 0x0000; 6702352866Scy } 6703145522Sdarrenr 6704255332Scy if (dosum) { 6705255332Scy sum = fr_cksum(fin, fin->fin_ip, fin->fin_p, fin->fin_dp); 6706145522Sdarrenr } 6707145522Sdarrenr#if !defined(_KERNEL) 6708145522Sdarrenr if (sum == hdrsum) { 6709145522Sdarrenr FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum)); 6710145522Sdarrenr } else { 6711145522Sdarrenr FR_DEBUG(("checkl4sum: %hx != %hx\n", sum, hdrsum)); 6712145522Sdarrenr } 6713145522Sdarrenr#endif 6714255332Scy DT2(l4sums, u_short, hdrsum, u_short, sum); 6715349927Scy#ifdef USE_INET6 6716349927Scy if (hdrsum == sum || (sum == 0 && fin->fin_p == IPPROTO_ICMPV6)) { 6717349927Scy#else 6718172776Sdarrenr if (hdrsum == sum) { 6719349927Scy#endif 6720255332Scy fin->fin_cksum = FI_CK_SUMOK; 6721145522Sdarrenr return 0; 6722172776Sdarrenr } 6723255332Scy fin->fin_cksum = FI_CK_BAD; 6724145522Sdarrenr return -1; 6725145522Sdarrenr} 6726145522Sdarrenr 6727145522Sdarrenr 6728145522Sdarrenr/* ------------------------------------------------------------------------ */ 6729255332Scy/* Function: ipf_ifpfillv4addr */ 6730145522Sdarrenr/* Returns: int - 0 = address update, -1 = address not updated */ 6731145522Sdarrenr/* Parameters: atype(I) - type of network address update to perform */ 6732145522Sdarrenr/* sin(I) - pointer to source of address information */ 6733145522Sdarrenr/* mask(I) - pointer to source of netmask information */ 6734145522Sdarrenr/* inp(I) - pointer to destination address store */ 6735145522Sdarrenr/* inpmask(I) - pointer to destination netmask store */ 6736145522Sdarrenr/* */ 6737145522Sdarrenr/* Given a type of network address update (atype) to perform, copy */ 6738145522Sdarrenr/* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ 6739145522Sdarrenr/* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ 6740145522Sdarrenr/* which case the operation fails. For all values of atype other than */ 6741145522Sdarrenr/* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ 6742145522Sdarrenr/* value. */ 6743145522Sdarrenr/* ------------------------------------------------------------------------ */ 6744255332Scyint 6745255332Scyipf_ifpfillv4addr(atype, sin, mask, inp, inpmask) 6746255332Scy int atype; 6747255332Scy struct sockaddr_in *sin, *mask; 6748255332Scy struct in_addr *inp, *inpmask; 6749145522Sdarrenr{ 6750145522Sdarrenr if (inpmask != NULL && atype != FRI_NETMASKED) 6751145522Sdarrenr inpmask->s_addr = 0xffffffff; 6752145522Sdarrenr 6753145522Sdarrenr if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { 6754145522Sdarrenr if (atype == FRI_NETMASKED) { 6755145522Sdarrenr if (inpmask == NULL) 6756145522Sdarrenr return -1; 6757145522Sdarrenr inpmask->s_addr = mask->sin_addr.s_addr; 6758145522Sdarrenr } 6759145522Sdarrenr inp->s_addr = sin->sin_addr.s_addr & mask->sin_addr.s_addr; 6760145522Sdarrenr } else { 6761145522Sdarrenr inp->s_addr = sin->sin_addr.s_addr; 6762145522Sdarrenr } 6763145522Sdarrenr return 0; 6764145522Sdarrenr} 6765145522Sdarrenr 6766145522Sdarrenr 6767145522Sdarrenr#ifdef USE_INET6 6768145522Sdarrenr/* ------------------------------------------------------------------------ */ 6769255332Scy/* Function: ipf_ifpfillv6addr */ 6770145522Sdarrenr/* Returns: int - 0 = address update, -1 = address not updated */ 6771145522Sdarrenr/* Parameters: atype(I) - type of network address update to perform */ 6772145522Sdarrenr/* sin(I) - pointer to source of address information */ 6773145522Sdarrenr/* mask(I) - pointer to source of netmask information */ 6774145522Sdarrenr/* inp(I) - pointer to destination address store */ 6775145522Sdarrenr/* inpmask(I) - pointer to destination netmask store */ 6776145522Sdarrenr/* */ 6777145522Sdarrenr/* Given a type of network address update (atype) to perform, copy */ 6778145522Sdarrenr/* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ 6779145522Sdarrenr/* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ 6780145522Sdarrenr/* which case the operation fails. For all values of atype other than */ 6781145522Sdarrenr/* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ 6782145522Sdarrenr/* value. */ 6783145522Sdarrenr/* ------------------------------------------------------------------------ */ 6784255332Scyint 6785255332Scyipf_ifpfillv6addr(atype, sin, mask, inp, inpmask) 6786255332Scy int atype; 6787255332Scy struct sockaddr_in6 *sin, *mask; 6788255332Scy i6addr_t *inp, *inpmask; 6789145522Sdarrenr{ 6790255332Scy i6addr_t *src, *and; 6791145522Sdarrenr 6792145522Sdarrenr src = (i6addr_t *)&sin->sin6_addr; 6793145522Sdarrenr and = (i6addr_t *)&mask->sin6_addr; 6794145522Sdarrenr 6795145522Sdarrenr if (inpmask != NULL && atype != FRI_NETMASKED) { 6796255332Scy inpmask->i6[0] = 0xffffffff; 6797255332Scy inpmask->i6[1] = 0xffffffff; 6798255332Scy inpmask->i6[2] = 0xffffffff; 6799255332Scy inpmask->i6[3] = 0xffffffff; 6800145522Sdarrenr } 6801145522Sdarrenr 6802145522Sdarrenr if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { 6803145522Sdarrenr if (atype == FRI_NETMASKED) { 6804145522Sdarrenr if (inpmask == NULL) 6805145522Sdarrenr return -1; 6806255332Scy inpmask->i6[0] = and->i6[0]; 6807255332Scy inpmask->i6[1] = and->i6[1]; 6808255332Scy inpmask->i6[2] = and->i6[2]; 6809255332Scy inpmask->i6[3] = and->i6[3]; 6810145522Sdarrenr } 6811145522Sdarrenr 6812255332Scy inp->i6[0] = src->i6[0] & and->i6[0]; 6813255332Scy inp->i6[1] = src->i6[1] & and->i6[1]; 6814255332Scy inp->i6[2] = src->i6[2] & and->i6[2]; 6815255332Scy inp->i6[3] = src->i6[3] & and->i6[3]; 6816145522Sdarrenr } else { 6817255332Scy inp->i6[0] = src->i6[0]; 6818255332Scy inp->i6[1] = src->i6[1]; 6819255332Scy inp->i6[2] = src->i6[2]; 6820255332Scy inp->i6[3] = src->i6[3]; 6821145522Sdarrenr } 6822145522Sdarrenr return 0; 6823145522Sdarrenr} 6824145522Sdarrenr#endif 6825145522Sdarrenr 6826145522Sdarrenr 6827145522Sdarrenr/* ------------------------------------------------------------------------ */ 6828255332Scy/* Function: ipf_matchtag */ 6829145522Sdarrenr/* Returns: 0 == mismatch, 1 == match. */ 6830145522Sdarrenr/* Parameters: tag1(I) - pointer to first tag to compare */ 6831145522Sdarrenr/* tag2(I) - pointer to second tag to compare */ 6832145522Sdarrenr/* */ 6833145522Sdarrenr/* Returns true (non-zero) or false(0) if the two tag structures can be */ 6834145522Sdarrenr/* considered to be a match or not match, respectively. The tag is 16 */ 6835145522Sdarrenr/* bytes long (16 characters) but that is overlayed with 4 32bit ints so */ 6836145522Sdarrenr/* compare the ints instead, for speed. tag1 is the master of the */ 6837145522Sdarrenr/* comparison. This function should only be called with both tag1 and tag2 */ 6838145522Sdarrenr/* as non-NULL pointers. */ 6839145522Sdarrenr/* ------------------------------------------------------------------------ */ 6840255332Scyint 6841255332Scyipf_matchtag(tag1, tag2) 6842255332Scy ipftag_t *tag1, *tag2; 6843145522Sdarrenr{ 6844145522Sdarrenr if (tag1 == tag2) 6845145522Sdarrenr return 1; 6846145522Sdarrenr 6847145522Sdarrenr if ((tag1->ipt_num[0] == 0) && (tag2->ipt_num[0] == 0)) 6848145522Sdarrenr return 1; 6849145522Sdarrenr 6850145522Sdarrenr if ((tag1->ipt_num[0] == tag2->ipt_num[0]) && 6851145522Sdarrenr (tag1->ipt_num[1] == tag2->ipt_num[1]) && 6852145522Sdarrenr (tag1->ipt_num[2] == tag2->ipt_num[2]) && 6853145522Sdarrenr (tag1->ipt_num[3] == tag2->ipt_num[3])) 6854145522Sdarrenr return 1; 6855145522Sdarrenr return 0; 6856145522Sdarrenr} 6857145522Sdarrenr 6858145522Sdarrenr 6859145522Sdarrenr/* ------------------------------------------------------------------------ */ 6860255332Scy/* Function: ipf_coalesce */ 6861145522Sdarrenr/* Returns: 1 == success, -1 == failure, 0 == no change */ 6862145522Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 6863145522Sdarrenr/* */ 6864145522Sdarrenr/* Attempt to get all of the packet data into a single, contiguous buffer. */ 6865145522Sdarrenr/* If this call returns a failure then the buffers have also been freed. */ 6866145522Sdarrenr/* ------------------------------------------------------------------------ */ 6867255332Scyint 6868255332Scyipf_coalesce(fin) 6869255332Scy fr_info_t *fin; 6870145522Sdarrenr{ 6871255332Scy 6872145522Sdarrenr if ((fin->fin_flx & FI_COALESCE) != 0) 6873145522Sdarrenr return 1; 6874145522Sdarrenr 6875145522Sdarrenr /* 6876145522Sdarrenr * If the mbuf pointers indicate that there is no mbuf to work with, 6877145522Sdarrenr * return but do not indicate success or failure. 6878145522Sdarrenr */ 6879145522Sdarrenr if (fin->fin_m == NULL || fin->fin_mp == NULL) 6880145522Sdarrenr return 0; 6881145522Sdarrenr 6882145522Sdarrenr#if defined(_KERNEL) 6883255332Scy if (ipf_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) { 6884255332Scy ipf_main_softc_t *softc = fin->fin_main_soft; 6885255332Scy 6886255332Scy DT1(frb_coalesce, fr_info_t *, fin); 6887255332Scy LBUMP(ipf_stats[fin->fin_out].fr_badcoalesces); 6888145522Sdarrenr# ifdef MENTAT 6889145522Sdarrenr FREE_MB_T(*fin->fin_mp); 6890145522Sdarrenr# endif 6891255332Scy fin->fin_reason = FRB_COALESCE; 6892145522Sdarrenr *fin->fin_mp = NULL; 6893145522Sdarrenr fin->fin_m = NULL; 6894145522Sdarrenr return -1; 6895145522Sdarrenr } 6896145522Sdarrenr#else 6897145522Sdarrenr fin = fin; /* LINT */ 6898145522Sdarrenr#endif 6899145522Sdarrenr return 1; 6900145522Sdarrenr} 6901145522Sdarrenr 6902145522Sdarrenr 6903145522Sdarrenr/* 6904145522Sdarrenr * The following table lists all of the tunable variables that can be 6905145522Sdarrenr * accessed via SIOCIPFGET/SIOCIPFSET/SIOCIPFGETNEXt. The format of each row 6906145522Sdarrenr * in the table below is as follows: 6907145522Sdarrenr * 6908145522Sdarrenr * pointer to value, name of value, minimum, maximum, size of the value's 6909145522Sdarrenr * container, value attribute flags 6910145522Sdarrenr * 6911145522Sdarrenr * For convienience, IPFT_RDONLY means the value is read-only, IPFT_WRDISABLED 6912145522Sdarrenr * means the value can only be written to when IPFilter is loaded but disabled. 6913145522Sdarrenr * The obvious implication is if neither of these are set then the value can be 6914145522Sdarrenr * changed at any time without harm. 6915145522Sdarrenr */ 6916145522Sdarrenr 6917145522Sdarrenr 6918145522Sdarrenr/* ------------------------------------------------------------------------ */ 6919255332Scy/* Function: ipf_tune_findbycookie */ 6920145522Sdarrenr/* Returns: NULL = search failed, else pointer to tune struct */ 6921145522Sdarrenr/* Parameters: cookie(I) - cookie value to search for amongst tuneables */ 6922145522Sdarrenr/* next(O) - pointer to place to store the cookie for the */ 6923145522Sdarrenr/* "next" tuneable, if it is desired. */ 6924145522Sdarrenr/* */ 6925145522Sdarrenr/* This function is used to walk through all of the existing tunables with */ 6926145522Sdarrenr/* successive calls. It searches the known tunables for the one which has */ 6927145522Sdarrenr/* a matching value for "cookie" - ie its address. When returning a match, */ 6928145522Sdarrenr/* the next one to be found may be returned inside next. */ 6929145522Sdarrenr/* ------------------------------------------------------------------------ */ 6930255332Scystatic ipftuneable_t * 6931255332Scyipf_tune_findbycookie(ptop, cookie, next) 6932255332Scy ipftuneable_t **ptop; 6933255332Scy void *cookie, **next; 6934145522Sdarrenr{ 6935145522Sdarrenr ipftuneable_t *ta, **tap; 6936145522Sdarrenr 6937255332Scy for (ta = *ptop; ta->ipft_name != NULL; ta++) 6938145522Sdarrenr if (ta == cookie) { 6939145522Sdarrenr if (next != NULL) { 6940145522Sdarrenr /* 6941145522Sdarrenr * If the next entry in the array has a name 6942145522Sdarrenr * present, then return a pointer to it for 6943145522Sdarrenr * where to go next, else return a pointer to 6944145522Sdarrenr * the dynaminc list as a key to search there 6945145522Sdarrenr * next. This facilitates a weak linking of 6946145522Sdarrenr * the two "lists" together. 6947145522Sdarrenr */ 6948145522Sdarrenr if ((ta + 1)->ipft_name != NULL) 6949145522Sdarrenr *next = ta + 1; 6950130886Sdarrenr else 6951255332Scy *next = ptop; 6952130886Sdarrenr } 6953145522Sdarrenr return ta; 6954130886Sdarrenr } 6955145522Sdarrenr 6956255332Scy for (tap = ptop; (ta = *tap) != NULL; tap = &ta->ipft_next) 6957145522Sdarrenr if (tap == cookie) { 6958145522Sdarrenr if (next != NULL) 6959145522Sdarrenr *next = &ta->ipft_next; 6960145522Sdarrenr return ta; 6961130886Sdarrenr } 6962145522Sdarrenr 6963145522Sdarrenr if (next != NULL) 6964145522Sdarrenr *next = NULL; 6965145522Sdarrenr return NULL; 6966145522Sdarrenr} 6967145522Sdarrenr 6968145522Sdarrenr 6969145522Sdarrenr/* ------------------------------------------------------------------------ */ 6970255332Scy/* Function: ipf_tune_findbyname */ 6971145522Sdarrenr/* Returns: NULL = search failed, else pointer to tune struct */ 6972145522Sdarrenr/* Parameters: name(I) - name of the tuneable entry to find. */ 6973145522Sdarrenr/* */ 6974145522Sdarrenr/* Search the static array of tuneables and the list of dynamic tuneables */ 6975145522Sdarrenr/* for an entry with a matching name. If we can find one, return a pointer */ 6976145522Sdarrenr/* to the matching structure. */ 6977145522Sdarrenr/* ------------------------------------------------------------------------ */ 6978255332Scystatic ipftuneable_t * 6979255332Scyipf_tune_findbyname(top, name) 6980255332Scy ipftuneable_t *top; 6981255332Scy const char *name; 6982145522Sdarrenr{ 6983145522Sdarrenr ipftuneable_t *ta; 6984145522Sdarrenr 6985255332Scy for (ta = top; ta != NULL; ta = ta->ipft_next) 6986145522Sdarrenr if (!strcmp(ta->ipft_name, name)) { 6987145522Sdarrenr return ta; 6988130886Sdarrenr } 6989145522Sdarrenr 6990255332Scy return NULL; 6991255332Scy} 6992255332Scy 6993255332Scy 6994255332Scy/* ------------------------------------------------------------------------ */ 6995255332Scy/* Function: ipf_tune_add_array */ 6996255332Scy/* Returns: int - 0 == success, else failure */ 6997255332Scy/* Parameters: newtune - pointer to new tune array to add to tuneables */ 6998255332Scy/* */ 6999255332Scy/* Appends tune structures from the array passed in (newtune) to the end of */ 7000255332Scy/* the current list of "dynamic" tuneable parameters. */ 7001255332Scy/* If any entry to be added is already present (by name) then the operation */ 7002255332Scy/* is aborted - entries that have been added are removed before returning. */ 7003255332Scy/* An entry with no name (NULL) is used as the indication that the end of */ 7004255332Scy/* the array has been reached. */ 7005255332Scy/* ------------------------------------------------------------------------ */ 7006255332Scyint 7007255332Scyipf_tune_add_array(softc, newtune) 7008255332Scy ipf_main_softc_t *softc; 7009255332Scy ipftuneable_t *newtune; 7010255332Scy{ 7011255332Scy ipftuneable_t *nt, *dt; 7012255332Scy int error = 0; 7013255332Scy 7014255332Scy for (nt = newtune; nt->ipft_name != NULL; nt++) { 7015255332Scy error = ipf_tune_add(softc, nt); 7016255332Scy if (error != 0) { 7017255332Scy for (dt = newtune; dt != nt; dt++) { 7018255332Scy (void) ipf_tune_del(softc, dt); 7019255332Scy } 7020145522Sdarrenr } 7021255332Scy } 7022145522Sdarrenr 7023255332Scy return error; 7024145522Sdarrenr} 7025145522Sdarrenr 7026145522Sdarrenr 7027145522Sdarrenr/* ------------------------------------------------------------------------ */ 7028255332Scy/* Function: ipf_tune_array_link */ 7029255332Scy/* Returns: 0 == success, -1 == failure */ 7030255332Scy/* Parameters: softc(I) - soft context pointerto work with */ 7031255332Scy/* array(I) - pointer to an array of tuneables */ 7032255332Scy/* */ 7033255332Scy/* Given an array of tunables (array), append them to the current list of */ 7034255332Scy/* tuneables for this context (softc->ipf_tuners.) To properly prepare the */ 7035255332Scy/* the array for being appended to the list, initialise all of the next */ 7036255332Scy/* pointers so we don't need to walk parts of it with ++ and others with */ 7037255332Scy/* next. The array is expected to have an entry with a NULL name as the */ 7038255332Scy/* terminator. Trying to add an array with no non-NULL names will return as */ 7039255332Scy/* a failure. */ 7040255332Scy/* ------------------------------------------------------------------------ */ 7041255332Scyint 7042255332Scyipf_tune_array_link(softc, array) 7043255332Scy ipf_main_softc_t *softc; 7044255332Scy ipftuneable_t *array; 7045255332Scy{ 7046255332Scy ipftuneable_t *t, **p; 7047255332Scy 7048255332Scy t = array; 7049255332Scy if (t->ipft_name == NULL) 7050255332Scy return -1; 7051255332Scy 7052255332Scy for (; t[1].ipft_name != NULL; t++) 7053255332Scy t[0].ipft_next = &t[1]; 7054255332Scy t->ipft_next = NULL; 7055255332Scy 7056255332Scy /* 7057255332Scy * Since a pointer to the last entry isn't kept, we need to find it 7058255332Scy * each time we want to add new variables to the list. 7059255332Scy */ 7060255332Scy for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) 7061255332Scy if (t->ipft_name == NULL) 7062255332Scy break; 7063255332Scy *p = array; 7064255332Scy 7065255332Scy return 0; 7066255332Scy} 7067255332Scy 7068255332Scy 7069255332Scy/* ------------------------------------------------------------------------ */ 7070255332Scy/* Function: ipf_tune_array_unlink */ 7071255332Scy/* Returns: 0 == success, -1 == failure */ 7072255332Scy/* Parameters: softc(I) - soft context pointerto work with */ 7073255332Scy/* array(I) - pointer to an array of tuneables */ 7074255332Scy/* */ 7075255332Scy/* ------------------------------------------------------------------------ */ 7076255332Scyint 7077255332Scyipf_tune_array_unlink(softc, array) 7078255332Scy ipf_main_softc_t *softc; 7079255332Scy ipftuneable_t *array; 7080255332Scy{ 7081255332Scy ipftuneable_t *t, **p; 7082255332Scy 7083255332Scy for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) 7084255332Scy if (t == array) 7085255332Scy break; 7086255332Scy if (t == NULL) 7087255332Scy return -1; 7088255332Scy 7089255332Scy for (; t[1].ipft_name != NULL; t++) 7090255332Scy ; 7091255332Scy 7092255332Scy *p = t->ipft_next; 7093255332Scy 7094255332Scy return 0; 7095255332Scy} 7096255332Scy 7097255332Scy 7098255332Scy/* ------------------------------------------------------------------------ */ 7099255332Scy/* Function: ipf_tune_array_copy */ 7100255332Scy/* Returns: NULL = failure, else pointer to new array */ 7101255332Scy/* Parameters: base(I) - pointer to structure base */ 7102255332Scy/* size(I) - size of the array at template */ 7103255332Scy/* template(I) - original array to copy */ 7104255332Scy/* */ 7105255332Scy/* Allocate memory for a new set of tuneable values and copy everything */ 7106255332Scy/* from template into the new region of memory. The new region is full of */ 7107255332Scy/* uninitialised pointers (ipft_next) so set them up. Now, ipftp_offset... */ 7108255332Scy/* */ 7109255332Scy/* NOTE: the following assumes that sizeof(long) == sizeof(void *) */ 7110255332Scy/* In the array template, ipftp_offset is the offset (in bytes) of the */ 7111255332Scy/* location of the tuneable value inside the structure pointed to by base. */ 7112255332Scy/* As ipftp_offset is a union over the pointers to the tuneable values, if */ 7113255332Scy/* we add base to the copy's ipftp_offset, copy ends up with a pointer in */ 7114255332Scy/* ipftp_void that points to the stored value. */ 7115255332Scy/* ------------------------------------------------------------------------ */ 7116255332Scyipftuneable_t * 7117255332Scyipf_tune_array_copy(base, size, template) 7118255332Scy void *base; 7119255332Scy size_t size; 7120255332Scy ipftuneable_t *template; 7121255332Scy{ 7122255332Scy ipftuneable_t *copy; 7123255332Scy int i; 7124255332Scy 7125255332Scy 7126255332Scy KMALLOCS(copy, ipftuneable_t *, size); 7127255332Scy if (copy == NULL) { 7128255332Scy return NULL; 7129255332Scy } 7130255332Scy bcopy(template, copy, size); 7131255332Scy 7132255332Scy for (i = 0; copy[i].ipft_name; i++) { 7133255332Scy copy[i].ipft_una.ipftp_offset += (u_long)base; 7134255332Scy copy[i].ipft_next = copy + i + 1; 7135255332Scy } 7136255332Scy 7137255332Scy return copy; 7138255332Scy} 7139255332Scy 7140255332Scy 7141255332Scy/* ------------------------------------------------------------------------ */ 7142255332Scy/* Function: ipf_tune_add */ 7143145522Sdarrenr/* Returns: int - 0 == success, else failure */ 7144255332Scy/* Parameters: newtune - pointer to new tune entry to add to tuneables */ 7145145522Sdarrenr/* */ 7146255332Scy/* Appends tune structures from the array passed in (newtune) to the end of */ 7147255332Scy/* the current list of "dynamic" tuneable parameters. Once added, the */ 7148255332Scy/* owner of the object is not expected to ever change "ipft_next". */ 7149145522Sdarrenr/* ------------------------------------------------------------------------ */ 7150255332Scyint 7151255332Scyipf_tune_add(softc, newtune) 7152255332Scy ipf_main_softc_t *softc; 7153255332Scy ipftuneable_t *newtune; 7154145522Sdarrenr{ 7155145522Sdarrenr ipftuneable_t *ta, **tap; 7156145522Sdarrenr 7157255332Scy ta = ipf_tune_findbyname(softc->ipf_tuners, newtune->ipft_name); 7158255332Scy if (ta != NULL) { 7159255332Scy IPFERROR(74); 7160145522Sdarrenr return EEXIST; 7161255332Scy } 7162145522Sdarrenr 7163255332Scy for (tap = &softc->ipf_tuners; *tap != NULL; tap = &(*tap)->ipft_next) 7164145522Sdarrenr ; 7165145522Sdarrenr 7166145522Sdarrenr newtune->ipft_next = NULL; 7167145522Sdarrenr *tap = newtune; 7168145522Sdarrenr return 0; 7169145522Sdarrenr} 7170145522Sdarrenr 7171145522Sdarrenr 7172145522Sdarrenr/* ------------------------------------------------------------------------ */ 7173255332Scy/* Function: ipf_tune_del */ 7174145522Sdarrenr/* Returns: int - 0 == success, else failure */ 7175255332Scy/* Parameters: oldtune - pointer to tune entry to remove from the list of */ 7176145522Sdarrenr/* current dynamic tuneables */ 7177145522Sdarrenr/* */ 7178145522Sdarrenr/* Search for the tune structure, by pointer, in the list of those that are */ 7179145522Sdarrenr/* dynamically added at run time. If found, adjust the list so that this */ 7180145522Sdarrenr/* structure is no longer part of it. */ 7181145522Sdarrenr/* ------------------------------------------------------------------------ */ 7182255332Scyint 7183255332Scyipf_tune_del(softc, oldtune) 7184255332Scy ipf_main_softc_t *softc; 7185255332Scy ipftuneable_t *oldtune; 7186145522Sdarrenr{ 7187145522Sdarrenr ipftuneable_t *ta, **tap; 7188255332Scy int error = 0; 7189145522Sdarrenr 7190255332Scy for (tap = &softc->ipf_tuners; (ta = *tap) != NULL; 7191255332Scy tap = &ta->ipft_next) { 7192145522Sdarrenr if (ta == oldtune) { 7193145522Sdarrenr *tap = oldtune->ipft_next; 7194145522Sdarrenr oldtune->ipft_next = NULL; 7195255332Scy break; 7196145522Sdarrenr } 7197255332Scy } 7198145522Sdarrenr 7199255332Scy if (ta == NULL) { 7200255332Scy error = ESRCH; 7201255332Scy IPFERROR(75); 7202255332Scy } 7203255332Scy return error; 7204145522Sdarrenr} 7205145522Sdarrenr 7206145522Sdarrenr 7207145522Sdarrenr/* ------------------------------------------------------------------------ */ 7208255332Scy/* Function: ipf_tune_del_array */ 7209145522Sdarrenr/* Returns: int - 0 == success, else failure */ 7210255332Scy/* Parameters: oldtune - pointer to tuneables array */ 7211255332Scy/* */ 7212255332Scy/* Remove each tuneable entry in the array from the list of "dynamic" */ 7213255332Scy/* tunables. If one entry should fail to be found, an error will be */ 7214255332Scy/* returned and no further ones removed. */ 7215255332Scy/* An entry with a NULL name is used as the indicator of the last entry in */ 7216255332Scy/* the array. */ 7217255332Scy/* ------------------------------------------------------------------------ */ 7218255332Scyint 7219255332Scyipf_tune_del_array(softc, oldtune) 7220255332Scy ipf_main_softc_t *softc; 7221255332Scy ipftuneable_t *oldtune; 7222255332Scy{ 7223255332Scy ipftuneable_t *ot; 7224255332Scy int error = 0; 7225255332Scy 7226255332Scy for (ot = oldtune; ot->ipft_name != NULL; ot++) { 7227255332Scy error = ipf_tune_del(softc, ot); 7228255332Scy if (error != 0) 7229255332Scy break; 7230255332Scy } 7231255332Scy 7232255332Scy return error; 7233255332Scy 7234255332Scy} 7235255332Scy 7236255332Scy 7237255332Scy/* ------------------------------------------------------------------------ */ 7238255332Scy/* Function: ipf_tune */ 7239255332Scy/* Returns: int - 0 == success, else failure */ 7240145522Sdarrenr/* Parameters: cmd(I) - ioctl command number */ 7241145522Sdarrenr/* data(I) - pointer to ioctl data structure */ 7242145522Sdarrenr/* */ 7243145522Sdarrenr/* Implement handling of SIOCIPFGETNEXT, SIOCIPFGET and SIOCIPFSET. These */ 7244145522Sdarrenr/* three ioctls provide the means to access and control global variables */ 7245145522Sdarrenr/* within IPFilter, allowing (for example) timeouts and table sizes to be */ 7246145522Sdarrenr/* changed without rebooting, reloading or recompiling. The initialisation */ 7247145522Sdarrenr/* and 'destruction' routines of the various components of ipfilter are all */ 7248145522Sdarrenr/* each responsible for handling their own values being too big. */ 7249145522Sdarrenr/* ------------------------------------------------------------------------ */ 7250255332Scyint 7251255332Scyipf_ipftune(softc, cmd, data) 7252255332Scy ipf_main_softc_t *softc; 7253255332Scy ioctlcmd_t cmd; 7254255332Scy void *data; 7255145522Sdarrenr{ 7256145522Sdarrenr ipftuneable_t *ta; 7257145522Sdarrenr ipftune_t tu; 7258145522Sdarrenr void *cookie; 7259145522Sdarrenr int error; 7260145522Sdarrenr 7261255332Scy error = ipf_inobj(softc, data, NULL, &tu, IPFOBJ_TUNEABLE); 7262145522Sdarrenr if (error != 0) 7263145522Sdarrenr return error; 7264145522Sdarrenr 7265145522Sdarrenr tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0'; 7266145522Sdarrenr cookie = tu.ipft_cookie; 7267145522Sdarrenr ta = NULL; 7268145522Sdarrenr 7269145522Sdarrenr switch (cmd) 7270145522Sdarrenr { 7271145522Sdarrenr case SIOCIPFGETNEXT : 7272145522Sdarrenr /* 7273145522Sdarrenr * If cookie is non-NULL, assume it to be a pointer to the last 7274145522Sdarrenr * entry we looked at, so find it (if possible) and return a 7275145522Sdarrenr * pointer to the next one after it. The last entry in the 7276145522Sdarrenr * the table is a NULL entry, so when we get to it, set cookie 7277145522Sdarrenr * to NULL and return that, indicating end of list, erstwhile 7278145522Sdarrenr * if we come in with cookie set to NULL, we are starting anew 7279145522Sdarrenr * at the front of the list. 7280145522Sdarrenr */ 7281145522Sdarrenr if (cookie != NULL) { 7282255332Scy ta = ipf_tune_findbycookie(&softc->ipf_tuners, 7283255332Scy cookie, &tu.ipft_cookie); 7284145522Sdarrenr } else { 7285255332Scy ta = softc->ipf_tuners; 7286145522Sdarrenr tu.ipft_cookie = ta + 1; 7287145522Sdarrenr } 7288145522Sdarrenr if (ta != NULL) { 7289145522Sdarrenr /* 7290145522Sdarrenr * Entry found, but does the data pointed to by that 7291145522Sdarrenr * row fit in what we can return? 7292145522Sdarrenr */ 7293255332Scy if (ta->ipft_sz > sizeof(tu.ipft_un)) { 7294255332Scy IPFERROR(76); 7295145522Sdarrenr return EINVAL; 7296255332Scy } 7297145522Sdarrenr 7298145522Sdarrenr tu.ipft_vlong = 0; 7299145522Sdarrenr if (ta->ipft_sz == sizeof(u_long)) 7300145522Sdarrenr tu.ipft_vlong = *ta->ipft_plong; 7301145522Sdarrenr else if (ta->ipft_sz == sizeof(u_int)) 7302145522Sdarrenr tu.ipft_vint = *ta->ipft_pint; 7303145522Sdarrenr else if (ta->ipft_sz == sizeof(u_short)) 7304145522Sdarrenr tu.ipft_vshort = *ta->ipft_pshort; 7305145522Sdarrenr else if (ta->ipft_sz == sizeof(u_char)) 7306145522Sdarrenr tu.ipft_vchar = *ta->ipft_pchar; 7307145522Sdarrenr 7308145522Sdarrenr tu.ipft_sz = ta->ipft_sz; 7309145522Sdarrenr tu.ipft_min = ta->ipft_min; 7310145522Sdarrenr tu.ipft_max = ta->ipft_max; 7311145522Sdarrenr tu.ipft_flags = ta->ipft_flags; 7312145522Sdarrenr bcopy(ta->ipft_name, tu.ipft_name, 7313145522Sdarrenr MIN(sizeof(tu.ipft_name), 7314145522Sdarrenr strlen(ta->ipft_name) + 1)); 7315145522Sdarrenr } 7316255332Scy error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); 7317145522Sdarrenr break; 7318145522Sdarrenr 7319145522Sdarrenr case SIOCIPFGET : 7320145522Sdarrenr case SIOCIPFSET : 7321145522Sdarrenr /* 7322145522Sdarrenr * Search by name or by cookie value for a particular entry 7323145522Sdarrenr * in the tuning paramter table. 7324145522Sdarrenr */ 7325255332Scy IPFERROR(77); 7326145522Sdarrenr error = ESRCH; 7327145522Sdarrenr if (cookie != NULL) { 7328255332Scy ta = ipf_tune_findbycookie(&softc->ipf_tuners, 7329255332Scy cookie, NULL); 7330145522Sdarrenr if (ta != NULL) 7331145522Sdarrenr error = 0; 7332145522Sdarrenr } else if (tu.ipft_name[0] != '\0') { 7333255332Scy ta = ipf_tune_findbyname(softc->ipf_tuners, 7334255332Scy tu.ipft_name); 7335145522Sdarrenr if (ta != NULL) 7336145522Sdarrenr error = 0; 7337145522Sdarrenr } 7338145522Sdarrenr if (error != 0) 7339145522Sdarrenr break; 7340145522Sdarrenr 7341145522Sdarrenr if (cmd == (ioctlcmd_t)SIOCIPFGET) { 7342145522Sdarrenr /* 7343145522Sdarrenr * Fetch the tuning parameters for a particular value 7344145522Sdarrenr */ 7345145522Sdarrenr tu.ipft_vlong = 0; 7346145522Sdarrenr if (ta->ipft_sz == sizeof(u_long)) 7347145522Sdarrenr tu.ipft_vlong = *ta->ipft_plong; 7348145522Sdarrenr else if (ta->ipft_sz == sizeof(u_int)) 7349145522Sdarrenr tu.ipft_vint = *ta->ipft_pint; 7350145522Sdarrenr else if (ta->ipft_sz == sizeof(u_short)) 7351145522Sdarrenr tu.ipft_vshort = *ta->ipft_pshort; 7352145522Sdarrenr else if (ta->ipft_sz == sizeof(u_char)) 7353145522Sdarrenr tu.ipft_vchar = *ta->ipft_pchar; 7354147547Sdarrenr tu.ipft_cookie = ta; 7355145522Sdarrenr tu.ipft_sz = ta->ipft_sz; 7356145522Sdarrenr tu.ipft_min = ta->ipft_min; 7357145522Sdarrenr tu.ipft_max = ta->ipft_max; 7358145522Sdarrenr tu.ipft_flags = ta->ipft_flags; 7359255332Scy error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); 7360145522Sdarrenr 7361145522Sdarrenr } else if (cmd == (ioctlcmd_t)SIOCIPFSET) { 7362145522Sdarrenr /* 7363145522Sdarrenr * Set an internal parameter. The hard part here is 7364145522Sdarrenr * getting the new value safely and correctly out of 7365145522Sdarrenr * the kernel (given we only know its size, not type.) 7366145522Sdarrenr */ 7367145522Sdarrenr u_long in; 7368145522Sdarrenr 7369145522Sdarrenr if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) && 7370255332Scy (softc->ipf_running > 0)) { 7371255332Scy IPFERROR(78); 7372145522Sdarrenr error = EBUSY; 7373145522Sdarrenr break; 7374145522Sdarrenr } 7375145522Sdarrenr 7376145522Sdarrenr in = tu.ipft_vlong; 7377145522Sdarrenr if (in < ta->ipft_min || in > ta->ipft_max) { 7378255332Scy IPFERROR(79); 7379145522Sdarrenr error = EINVAL; 7380145522Sdarrenr break; 7381145522Sdarrenr } 7382145522Sdarrenr 7383255332Scy if (ta->ipft_func != NULL) { 7384255332Scy SPL_INT(s); 7385255332Scy 7386255332Scy SPL_NET(s); 7387255332Scy error = (*ta->ipft_func)(softc, ta, 7388255332Scy &tu.ipft_un); 7389255332Scy SPL_X(s); 7390255332Scy 7391255332Scy } else if (ta->ipft_sz == sizeof(u_long)) { 7392145522Sdarrenr tu.ipft_vlong = *ta->ipft_plong; 7393145522Sdarrenr *ta->ipft_plong = in; 7394255332Scy 7395145522Sdarrenr } else if (ta->ipft_sz == sizeof(u_int)) { 7396145522Sdarrenr tu.ipft_vint = *ta->ipft_pint; 7397145522Sdarrenr *ta->ipft_pint = (u_int)(in & 0xffffffff); 7398255332Scy 7399145522Sdarrenr } else if (ta->ipft_sz == sizeof(u_short)) { 7400145522Sdarrenr tu.ipft_vshort = *ta->ipft_pshort; 7401145522Sdarrenr *ta->ipft_pshort = (u_short)(in & 0xffff); 7402255332Scy 7403145522Sdarrenr } else if (ta->ipft_sz == sizeof(u_char)) { 7404145522Sdarrenr tu.ipft_vchar = *ta->ipft_pchar; 7405145522Sdarrenr *ta->ipft_pchar = (u_char)(in & 0xff); 7406145522Sdarrenr } 7407255332Scy error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); 7408145522Sdarrenr } 7409145522Sdarrenr break; 7410145522Sdarrenr 7411145522Sdarrenr default : 7412255332Scy IPFERROR(80); 7413145522Sdarrenr error = EINVAL; 7414145522Sdarrenr break; 7415130886Sdarrenr } 7416145522Sdarrenr 7417145522Sdarrenr return error; 7418130886Sdarrenr} 7419145522Sdarrenr 7420145522Sdarrenr 7421145522Sdarrenr/* ------------------------------------------------------------------------ */ 7422255332Scy/* Function: ipf_zerostats */ 7423145522Sdarrenr/* Returns: int - 0 = success, else failure */ 7424145522Sdarrenr/* Parameters: data(O) - pointer to pointer for copying data back to */ 7425145522Sdarrenr/* */ 7426145522Sdarrenr/* Copies the current statistics out to userspace and then zero's the */ 7427145522Sdarrenr/* current ones in the kernel. The lock is only held across the bzero() as */ 7428145522Sdarrenr/* the copyout may result in paging (ie network activity.) */ 7429145522Sdarrenr/* ------------------------------------------------------------------------ */ 7430255332Scyint 7431255332Scyipf_zerostats(softc, data) 7432255332Scy ipf_main_softc_t *softc; 7433255332Scy caddr_t data; 7434145522Sdarrenr{ 7435145522Sdarrenr friostat_t fio; 7436255332Scy ipfobj_t obj; 7437145522Sdarrenr int error; 7438145522Sdarrenr 7439255332Scy error = ipf_inobj(softc, data, &obj, &fio, IPFOBJ_IPFSTAT); 7440255332Scy if (error != 0) 7441255332Scy return error; 7442255332Scy ipf_getstat(softc, &fio, obj.ipfo_rev); 7443255332Scy error = ipf_outobj(softc, data, &fio, IPFOBJ_IPFSTAT); 7444255332Scy if (error != 0) 7445255332Scy return error; 7446145522Sdarrenr 7447255332Scy WRITE_ENTER(&softc->ipf_mutex); 7448255332Scy bzero(&softc->ipf_stats, sizeof(softc->ipf_stats)); 7449255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 7450145522Sdarrenr 7451145522Sdarrenr return 0; 7452145522Sdarrenr} 7453145522Sdarrenr 7454145522Sdarrenr 7455145522Sdarrenr/* ------------------------------------------------------------------------ */ 7456255332Scy/* Function: ipf_resolvedest */ 7457145522Sdarrenr/* Returns: Nil */ 7458255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7459255332Scy/* base(I) - where strings are stored */ 7460255332Scy/* fdp(IO) - pointer to destination information to resolve */ 7461255332Scy/* v(I) - IP protocol version to match */ 7462145522Sdarrenr/* */ 7463145522Sdarrenr/* Looks up an interface name in the frdest structure pointed to by fdp and */ 7464145522Sdarrenr/* if a matching name can be found for the particular IP protocol version */ 7465145522Sdarrenr/* then store the interface pointer in the frdest struct. If no match is */ 7466145522Sdarrenr/* found, then set the interface pointer to be -1 as NULL is considered to */ 7467145522Sdarrenr/* indicate there is no information at all in the structure. */ 7468145522Sdarrenr/* ------------------------------------------------------------------------ */ 7469255332Scyint 7470255332Scyipf_resolvedest(softc, base, fdp, v) 7471255332Scy ipf_main_softc_t *softc; 7472255332Scy char *base; 7473255332Scy frdest_t *fdp; 7474255332Scy int v; 7475145522Sdarrenr{ 7476255332Scy int errval = 0; 7477145522Sdarrenr void *ifp; 7478145522Sdarrenr 7479145522Sdarrenr ifp = NULL; 7480145522Sdarrenr 7481255332Scy if (fdp->fd_name != -1) { 7482255332Scy if (fdp->fd_type == FRD_DSTLIST) { 7483255332Scy ifp = ipf_lookup_res_name(softc, IPL_LOGIPF, 7484255332Scy IPLT_DSTLIST, 7485255332Scy base + fdp->fd_name, 7486255332Scy NULL); 7487255332Scy if (ifp == NULL) { 7488255332Scy IPFERROR(144); 7489255332Scy errval = ESRCH; 7490255332Scy } 7491255332Scy } else { 7492255332Scy ifp = GETIFP(base + fdp->fd_name, v); 7493255332Scy if (ifp == NULL) 7494255332Scy ifp = (void *)-1; 7495255332Scy } 7496145522Sdarrenr } 7497255332Scy fdp->fd_ptr = ifp; 7498255332Scy 7499255332Scy if ((ifp != NULL) && (ifp != (void *)-1)) { 7500255332Scy fdp->fd_local = ipf_deliverlocal(softc, v, ifp, &fdp->fd_ip6); 7501255332Scy } 7502255332Scy 7503255332Scy return errval; 7504145522Sdarrenr} 7505145522Sdarrenr 7506145522Sdarrenr 7507145522Sdarrenr/* ------------------------------------------------------------------------ */ 7508255332Scy/* Function: ipf_resolvenic */ 7509145522Sdarrenr/* Returns: void* - NULL = wildcard name, -1 = failed to find NIC, else */ 7510145522Sdarrenr/* pointer to interface structure for NIC */ 7511255332Scy/* Parameters: softc(I)- pointer to soft context main structure */ 7512255332Scy/* name(I) - complete interface name */ 7513145522Sdarrenr/* v(I) - IP protocol version */ 7514145522Sdarrenr/* */ 7515145522Sdarrenr/* Look for a network interface structure that firstly has a matching name */ 7516145522Sdarrenr/* to that passed in and that is also being used for that IP protocol */ 7517145522Sdarrenr/* version (necessary on some platforms where there are separate listings */ 7518145522Sdarrenr/* for both IPv4 and IPv6 on the same physical NIC. */ 7519145522Sdarrenr/* ------------------------------------------------------------------------ */ 7520255332Scyvoid * 7521255332Scyipf_resolvenic(softc, name, v) 7522255332Scy ipf_main_softc_t *softc; 7523255332Scy char *name; 7524255332Scy int v; 7525145522Sdarrenr{ 7526145522Sdarrenr void *nic; 7527145522Sdarrenr 7528255332Scy softc = softc; /* gcc -Wextra */ 7529145522Sdarrenr if (name[0] == '\0') 7530145522Sdarrenr return NULL; 7531145522Sdarrenr 7532145522Sdarrenr if ((name[1] == '\0') && ((name[0] == '-') || (name[0] == '*'))) { 7533145522Sdarrenr return NULL; 7534145522Sdarrenr } 7535145522Sdarrenr 7536145522Sdarrenr nic = GETIFP(name, v); 7537145522Sdarrenr if (nic == NULL) 7538145522Sdarrenr nic = (void *)-1; 7539145522Sdarrenr return nic; 7540145522Sdarrenr} 7541170268Sdarrenr 7542170268Sdarrenr 7543170268Sdarrenr/* ------------------------------------------------------------------------ */ 7544255332Scy/* Function: ipf_token_expire */ 7545170268Sdarrenr/* Returns: None. */ 7546255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7547170268Sdarrenr/* */ 7548170268Sdarrenr/* This function is run every ipf tick to see if there are any tokens that */ 7549170268Sdarrenr/* have been held for too long and need to be freed up. */ 7550170268Sdarrenr/* ------------------------------------------------------------------------ */ 7551255332Scyvoid 7552255332Scyipf_token_expire(softc) 7553255332Scy ipf_main_softc_t *softc; 7554170268Sdarrenr{ 7555170268Sdarrenr ipftoken_t *it; 7556170268Sdarrenr 7557255332Scy WRITE_ENTER(&softc->ipf_tokens); 7558255332Scy while ((it = softc->ipf_token_head) != NULL) { 7559255332Scy if (it->ipt_die > softc->ipf_ticks) 7560170268Sdarrenr break; 7561170268Sdarrenr 7562255332Scy ipf_token_deref(softc, it); 7563170268Sdarrenr } 7564255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 7565170268Sdarrenr} 7566170268Sdarrenr 7567170268Sdarrenr 7568170268Sdarrenr/* ------------------------------------------------------------------------ */ 7569255332Scy/* Function: ipf_token_flush */ 7570255332Scy/* Returns: None. */ 7571255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7572255332Scy/* */ 7573255332Scy/* Loop through all of the existing tokens and call deref to see if they */ 7574255332Scy/* can be freed. Normally a function like this might just loop on */ 7575255332Scy/* ipf_token_head but there is a chance that a token might have a ref count */ 7576255332Scy/* of greater than one and in that case the the reference would drop twice */ 7577255332Scy/* by code that is only entitled to drop it once. */ 7578255332Scy/* ------------------------------------------------------------------------ */ 7579255332Scystatic void 7580255332Scyipf_token_flush(softc) 7581255332Scy ipf_main_softc_t *softc; 7582255332Scy{ 7583255332Scy ipftoken_t *it, *next; 7584255332Scy 7585255332Scy WRITE_ENTER(&softc->ipf_tokens); 7586255332Scy for (it = softc->ipf_token_head; it != NULL; it = next) { 7587255332Scy next = it->ipt_next; 7588255332Scy (void) ipf_token_deref(softc, it); 7589255332Scy } 7590255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 7591255332Scy} 7592255332Scy 7593255332Scy 7594255332Scy/* ------------------------------------------------------------------------ */ 7595255332Scy/* Function: ipf_token_del */ 7596170268Sdarrenr/* Returns: int - 0 = success, else error */ 7597255332Scy/* Parameters: softc(I)- pointer to soft context main structure */ 7598255332Scy/* type(I) - the token type to match */ 7599170268Sdarrenr/* uid(I) - uid owning the token */ 7600170268Sdarrenr/* ptr(I) - context pointer for the token */ 7601170268Sdarrenr/* */ 7602170268Sdarrenr/* This function looks for a a token in the current list that matches up */ 7603170268Sdarrenr/* the fields (type, uid, ptr). If none is found, ESRCH is returned, else */ 7604255332Scy/* call ipf_token_dewref() to remove it from the list. In the event that */ 7605255332Scy/* the token has a reference held elsewhere, setting ipt_complete to 2 */ 7606255332Scy/* enables debugging to distinguish between the two paths that ultimately */ 7607255332Scy/* lead to a token to be deleted. */ 7608170268Sdarrenr/* ------------------------------------------------------------------------ */ 7609255332Scyint 7610255332Scyipf_token_del(softc, type, uid, ptr) 7611255332Scy ipf_main_softc_t *softc; 7612255332Scy int type, uid; 7613255332Scy void *ptr; 7614170268Sdarrenr{ 7615170268Sdarrenr ipftoken_t *it; 7616255332Scy int error; 7617170268Sdarrenr 7618255332Scy IPFERROR(82); 7619255332Scy error = ESRCH; 7620255332Scy 7621255332Scy WRITE_ENTER(&softc->ipf_tokens); 7622255332Scy for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { 7623170268Sdarrenr if (ptr == it->ipt_ctx && type == it->ipt_type && 7624170268Sdarrenr uid == it->ipt_uid) { 7625255332Scy it->ipt_complete = 2; 7626255332Scy ipf_token_deref(softc, it); 7627170268Sdarrenr error = 0; 7628170268Sdarrenr break; 7629255332Scy } 7630170268Sdarrenr } 7631255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 7632170268Sdarrenr 7633170268Sdarrenr return error; 7634170268Sdarrenr} 7635170268Sdarrenr 7636170268Sdarrenr 7637170268Sdarrenr/* ------------------------------------------------------------------------ */ 7638255332Scy/* Function: ipf_token_mark_complete */ 7639255332Scy/* Returns: None. */ 7640255332Scy/* Parameters: token(I) - pointer to token structure */ 7641255332Scy/* */ 7642255332Scy/* Mark a token as being ineligable for being found with ipf_token_find. */ 7643255332Scy/* ------------------------------------------------------------------------ */ 7644255332Scyvoid 7645255332Scyipf_token_mark_complete(token) 7646255332Scy ipftoken_t *token; 7647255332Scy{ 7648255332Scy if (token->ipt_complete == 0) 7649255332Scy token->ipt_complete = 1; 7650255332Scy} 7651255332Scy 7652255332Scy 7653255332Scy/* ------------------------------------------------------------------------ */ 7654255332Scy/* Function: ipf_token_find */ 7655170268Sdarrenr/* Returns: ipftoken_t * - NULL if no memory, else pointer to token */ 7656255332Scy/* Parameters: softc(I)- pointer to soft context main structure */ 7657255332Scy/* type(I) - the token type to match */ 7658170268Sdarrenr/* uid(I) - uid owning the token */ 7659170268Sdarrenr/* ptr(I) - context pointer for the token */ 7660170268Sdarrenr/* */ 7661170268Sdarrenr/* This function looks for a live token in the list of current tokens that */ 7662170268Sdarrenr/* matches the tuple (type, uid, ptr). If one cannot be found then one is */ 7663170268Sdarrenr/* allocated. If one is found then it is moved to the top of the list of */ 7664170268Sdarrenr/* currently active tokens. */ 7665170268Sdarrenr/* ------------------------------------------------------------------------ */ 7666255332Scyipftoken_t * 7667255332Scyipf_token_find(softc, type, uid, ptr) 7668255332Scy ipf_main_softc_t *softc; 7669255332Scy int type, uid; 7670255332Scy void *ptr; 7671170268Sdarrenr{ 7672170268Sdarrenr ipftoken_t *it, *new; 7673170268Sdarrenr 7674170268Sdarrenr KMALLOC(new, ipftoken_t *); 7675255332Scy if (new != NULL) 7676255332Scy bzero((char *)new, sizeof(*new)); 7677170268Sdarrenr 7678255332Scy WRITE_ENTER(&softc->ipf_tokens); 7679255332Scy for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { 7680255332Scy if ((ptr == it->ipt_ctx) && (type == it->ipt_type) && 7681255332Scy (uid == it->ipt_uid) && (it->ipt_complete < 2)) 7682170268Sdarrenr break; 7683170268Sdarrenr } 7684170268Sdarrenr 7685170268Sdarrenr if (it == NULL) { 7686170268Sdarrenr it = new; 7687170268Sdarrenr new = NULL; 7688255332Scy if (it == NULL) { 7689255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 7690170268Sdarrenr return NULL; 7691255332Scy } 7692170268Sdarrenr it->ipt_ctx = ptr; 7693170268Sdarrenr it->ipt_uid = uid; 7694170268Sdarrenr it->ipt_type = type; 7695255332Scy it->ipt_ref = 1; 7696170268Sdarrenr } else { 7697170268Sdarrenr if (new != NULL) { 7698170268Sdarrenr KFREE(new); 7699170268Sdarrenr new = NULL; 7700170268Sdarrenr } 7701170268Sdarrenr 7702255332Scy if (it->ipt_complete > 0) 7703255332Scy it = NULL; 7704255332Scy else 7705255332Scy ipf_token_unlink(softc, it); 7706170268Sdarrenr } 7707170268Sdarrenr 7708255332Scy if (it != NULL) { 7709255332Scy it->ipt_pnext = softc->ipf_token_tail; 7710255332Scy *softc->ipf_token_tail = it; 7711255332Scy softc->ipf_token_tail = &it->ipt_next; 7712255332Scy it->ipt_next = NULL; 7713255332Scy it->ipt_ref++; 7714170268Sdarrenr 7715255332Scy it->ipt_die = softc->ipf_ticks + 20; 7716255332Scy } 7717170268Sdarrenr 7718255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 7719255332Scy 7720170268Sdarrenr return it; 7721170268Sdarrenr} 7722170268Sdarrenr 7723170268Sdarrenr 7724170268Sdarrenr/* ------------------------------------------------------------------------ */ 7725255332Scy/* Function: ipf_token_unlink */ 7726170268Sdarrenr/* Returns: None. */ 7727255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7728255332Scy/* token(I) - pointer to token structure */ 7729255332Scy/* Write Locks: ipf_tokens */ 7730170268Sdarrenr/* */ 7731170268Sdarrenr/* This function unlinks a token structure from the linked list of tokens */ 7732170268Sdarrenr/* that "own" it. The head pointer never needs to be explicitly adjusted */ 7733170268Sdarrenr/* but the tail does due to the linked list implementation. */ 7734170268Sdarrenr/* ------------------------------------------------------------------------ */ 7735255332Scystatic void 7736255332Scyipf_token_unlink(softc, token) 7737255332Scy ipf_main_softc_t *softc; 7738255332Scy ipftoken_t *token; 7739170268Sdarrenr{ 7740170268Sdarrenr 7741255332Scy if (softc->ipf_token_tail == &token->ipt_next) 7742255332Scy softc->ipf_token_tail = token->ipt_pnext; 7743170268Sdarrenr 7744170268Sdarrenr *token->ipt_pnext = token->ipt_next; 7745170268Sdarrenr if (token->ipt_next != NULL) 7746170268Sdarrenr token->ipt_next->ipt_pnext = token->ipt_pnext; 7747255332Scy token->ipt_next = NULL; 7748255332Scy token->ipt_pnext = NULL; 7749170268Sdarrenr} 7750170268Sdarrenr 7751170268Sdarrenr 7752170268Sdarrenr/* ------------------------------------------------------------------------ */ 7753255332Scy/* Function: ipf_token_deref */ 7754255332Scy/* Returns: int - 0 == token freed, else reference count */ 7755255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7756255332Scy/* token(I) - pointer to token structure */ 7757255332Scy/* Write Locks: ipf_tokens */ 7758170268Sdarrenr/* */ 7759255332Scy/* Drop the reference count on the token structure and if it drops to zero, */ 7760255332Scy/* call the dereference function for the token type because it is then */ 7761255332Scy/* possible to free the token data structure. */ 7762170268Sdarrenr/* ------------------------------------------------------------------------ */ 7763255332Scyint 7764255332Scyipf_token_deref(softc, token) 7765255332Scy ipf_main_softc_t *softc; 7766255332Scy ipftoken_t *token; 7767170268Sdarrenr{ 7768170268Sdarrenr void *data, **datap; 7769170268Sdarrenr 7770255332Scy ASSERT(token->ipt_ref > 0); 7771255332Scy token->ipt_ref--; 7772255332Scy if (token->ipt_ref > 0) 7773255332Scy return token->ipt_ref; 7774170268Sdarrenr 7775170268Sdarrenr data = token->ipt_data; 7776170268Sdarrenr datap = &data; 7777170268Sdarrenr 7778170268Sdarrenr if ((data != NULL) && (data != (void *)-1)) { 7779170268Sdarrenr switch (token->ipt_type) 7780170268Sdarrenr { 7781170268Sdarrenr case IPFGENITER_IPF : 7782255332Scy (void) ipf_derefrule(softc, (frentry_t **)datap); 7783170268Sdarrenr break; 7784170268Sdarrenr case IPFGENITER_IPNAT : 7785255332Scy WRITE_ENTER(&softc->ipf_nat); 7786255332Scy ipf_nat_rule_deref(softc, (ipnat_t **)datap); 7787255332Scy RWLOCK_EXIT(&softc->ipf_nat); 7788170268Sdarrenr break; 7789170268Sdarrenr case IPFGENITER_NAT : 7790255332Scy ipf_nat_deref(softc, (nat_t **)datap); 7791170268Sdarrenr break; 7792170268Sdarrenr case IPFGENITER_STATE : 7793255332Scy ipf_state_deref(softc, (ipstate_t **)datap); 7794170268Sdarrenr break; 7795170268Sdarrenr case IPFGENITER_FRAG : 7796255332Scy ipf_frag_pkt_deref(softc, (ipfr_t **)datap); 7797170268Sdarrenr break; 7798170268Sdarrenr case IPFGENITER_NATFRAG : 7799255332Scy ipf_frag_nat_deref(softc, (ipfr_t **)datap); 7800170268Sdarrenr break; 7801170268Sdarrenr case IPFGENITER_HOSTMAP : 7802255332Scy WRITE_ENTER(&softc->ipf_nat); 7803255332Scy ipf_nat_hostmapdel(softc, (hostmap_t **)datap); 7804255332Scy RWLOCK_EXIT(&softc->ipf_nat); 7805170268Sdarrenr break; 7806170268Sdarrenr default : 7807255332Scy ipf_lookup_iterderef(softc, token->ipt_type, data); 7808170268Sdarrenr break; 7809170268Sdarrenr } 7810170268Sdarrenr } 7811170268Sdarrenr 7812255332Scy ipf_token_unlink(softc, token); 7813170268Sdarrenr KFREE(token); 7814255332Scy return 0; 7815170268Sdarrenr} 7816170268Sdarrenr 7817170268Sdarrenr 7818170268Sdarrenr/* ------------------------------------------------------------------------ */ 7819255332Scy/* Function: ipf_nextrule */ 7820255332Scy/* Returns: frentry_t * - NULL == no more rules, else pointer to next */ 7821255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 7822255332Scy/* fr(I) - pointer to filter rule */ 7823255332Scy/* out(I) - 1 == out rules, 0 == input rules */ 7824255332Scy/* */ 7825255332Scy/* Starting with "fr", find the next rule to visit. This includes visiting */ 7826255332Scy/* the list of rule groups if either fr is NULL (empty list) or it is the */ 7827255332Scy/* last rule in the list. When walking rule lists, it is either input or */ 7828255332Scy/* output rules that are returned, never both. */ 7829255332Scy/* ------------------------------------------------------------------------ */ 7830255332Scystatic frentry_t * 7831255332Scyipf_nextrule(softc, active, unit, fr, out) 7832255332Scy ipf_main_softc_t *softc; 7833255332Scy int active, unit; 7834255332Scy frentry_t *fr; 7835255332Scy int out; 7836255332Scy{ 7837255332Scy frentry_t *next; 7838255332Scy frgroup_t *fg; 7839255332Scy 7840255332Scy if (fr != NULL && fr->fr_group != -1) { 7841255332Scy fg = ipf_findgroup(softc, fr->fr_names + fr->fr_group, 7842255332Scy unit, active, NULL); 7843255332Scy if (fg != NULL) 7844255332Scy fg = fg->fg_next; 7845255332Scy } else { 7846255332Scy fg = softc->ipf_groups[unit][active]; 7847255332Scy } 7848255332Scy 7849255332Scy while (fg != NULL) { 7850255332Scy next = fg->fg_start; 7851255332Scy while (next != NULL) { 7852255332Scy if (out) { 7853255332Scy if (next->fr_flags & FR_OUTQUE) 7854255332Scy return next; 7855255332Scy } else if (next->fr_flags & FR_INQUE) { 7856255332Scy return next; 7857255332Scy } 7858255332Scy next = next->fr_next; 7859255332Scy } 7860255332Scy if (next == NULL) 7861255332Scy fg = fg->fg_next; 7862255332Scy } 7863255332Scy 7864255332Scy return NULL; 7865255332Scy} 7866255332Scy 7867255332Scy/* ------------------------------------------------------------------------ */ 7868170268Sdarrenr/* Function: ipf_getnextrule */ 7869170268Sdarrenr/* Returns: int - 0 = success, else error */ 7870255332Scy/* Parameters: softc(I)- pointer to soft context main structure */ 7871255332Scy/* t(I) - pointer to destination information to resolve */ 7872170268Sdarrenr/* ptr(I) - pointer to ipfobj_t to copyin from user space */ 7873170268Sdarrenr/* */ 7874170268Sdarrenr/* This function's first job is to bring in the ipfruleiter_t structure via */ 7875170268Sdarrenr/* the ipfobj_t structure to determine what should be the next rule to */ 7876170268Sdarrenr/* return. Once the ipfruleiter_t has been brought in, it then tries to */ 7877170268Sdarrenr/* find the 'next rule'. This may include searching rule group lists or */ 7878170268Sdarrenr/* just be as simple as looking at the 'next' field in the rule structure. */ 7879170268Sdarrenr/* When we have found the rule to return, increase its reference count and */ 7880170268Sdarrenr/* if we used an existing rule to get here, decrease its reference count. */ 7881170268Sdarrenr/* ------------------------------------------------------------------------ */ 7882255332Scyint 7883255332Scyipf_getnextrule(softc, t, ptr) 7884255332Scy ipf_main_softc_t *softc; 7885255332Scy ipftoken_t *t; 7886255332Scy void *ptr; 7887170268Sdarrenr{ 7888170268Sdarrenr frentry_t *fr, *next, zero; 7889170268Sdarrenr ipfruleiter_t it; 7890255332Scy int error, out; 7891170268Sdarrenr frgroup_t *fg; 7892255332Scy ipfobj_t obj; 7893255332Scy int predict; 7894170268Sdarrenr char *dst; 7895255332Scy int unit; 7896170268Sdarrenr 7897255332Scy if (t == NULL || ptr == NULL) { 7898255332Scy IPFERROR(84); 7899170268Sdarrenr return EFAULT; 7900255332Scy } 7901255332Scy 7902255332Scy error = ipf_inobj(softc, ptr, &obj, &it, IPFOBJ_IPFITER); 7903170268Sdarrenr if (error != 0) 7904170268Sdarrenr return error; 7905255332Scy 7906255332Scy if ((it.iri_inout < 0) || (it.iri_inout > 3)) { 7907255332Scy IPFERROR(85); 7908170268Sdarrenr return EINVAL; 7909255332Scy } 7910255332Scy if ((it.iri_active != 0) && (it.iri_active != 1)) { 7911255332Scy IPFERROR(86); 7912170268Sdarrenr return EINVAL; 7913255332Scy } 7914255332Scy if (it.iri_nrules == 0) { 7915255332Scy IPFERROR(87); 7916170268Sdarrenr return ENOSPC; 7917255332Scy } 7918255332Scy if (it.iri_rule == NULL) { 7919255332Scy IPFERROR(88); 7920170268Sdarrenr return EFAULT; 7921255332Scy } 7922170268Sdarrenr 7923255332Scy fg = NULL; 7924170268Sdarrenr fr = t->ipt_data; 7925255332Scy if ((it.iri_inout & F_OUT) != 0) 7926255332Scy out = 1; 7927255332Scy else 7928255332Scy out = 0; 7929255332Scy if ((it.iri_inout & F_ACIN) != 0) 7930255332Scy unit = IPL_LOGCOUNT; 7931255332Scy else 7932255332Scy unit = IPL_LOGIPF; 7933255332Scy 7934255332Scy READ_ENTER(&softc->ipf_mutex); 7935170268Sdarrenr if (fr == NULL) { 7936170268Sdarrenr if (*it.iri_group == '\0') { 7937255332Scy if (unit == IPL_LOGCOUNT) { 7938255332Scy next = softc->ipf_acct[out][it.iri_active]; 7939170268Sdarrenr } else { 7940255332Scy next = softc->ipf_rules[out][it.iri_active]; 7941170268Sdarrenr } 7942255332Scy if (next == NULL) 7943255332Scy next = ipf_nextrule(softc, it.iri_active, 7944255332Scy unit, NULL, out); 7945170268Sdarrenr } else { 7946255332Scy fg = ipf_findgroup(softc, it.iri_group, unit, 7947255332Scy it.iri_active, NULL); 7948170268Sdarrenr if (fg != NULL) 7949170268Sdarrenr next = fg->fg_start; 7950170268Sdarrenr else 7951170268Sdarrenr next = NULL; 7952170268Sdarrenr } 7953170268Sdarrenr } else { 7954170268Sdarrenr next = fr->fr_next; 7955255332Scy if (next == NULL) 7956255332Scy next = ipf_nextrule(softc, it.iri_active, unit, 7957255332Scy fr, out); 7958170268Sdarrenr } 7959170268Sdarrenr 7960255332Scy if (next != NULL && next->fr_next != NULL) 7961255332Scy predict = 1; 7962255332Scy else if (ipf_nextrule(softc, it.iri_active, unit, next, out) != NULL) 7963255332Scy predict = 1; 7964255332Scy else 7965255332Scy predict = 0; 7966255332Scy 7967255332Scy if (fr != NULL) 7968255332Scy (void) ipf_derefrule(softc, &fr); 7969255332Scy 7970255332Scy obj.ipfo_type = IPFOBJ_FRENTRY; 7971170268Sdarrenr dst = (char *)it.iri_rule; 7972170268Sdarrenr 7973255332Scy if (next != NULL) { 7974255332Scy obj.ipfo_size = next->fr_size; 7975255332Scy MUTEX_ENTER(&next->fr_lock); 7976255332Scy next->fr_ref++; 7977255332Scy MUTEX_EXIT(&next->fr_lock); 7978255332Scy t->ipt_data = next; 7979255332Scy } else { 7980255332Scy obj.ipfo_size = sizeof(frentry_t); 7981255332Scy bzero(&zero, sizeof(zero)); 7982255332Scy next = &zero; 7983255332Scy t->ipt_data = NULL; 7984255332Scy } 7985255332Scy it.iri_rule = predict ? next : NULL; 7986255332Scy if (predict == 0) 7987255332Scy ipf_token_mark_complete(t); 7988170268Sdarrenr 7989255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 7990255332Scy 7991255332Scy obj.ipfo_ptr = dst; 7992255332Scy error = ipf_outobjk(softc, &obj, next); 7993255332Scy if (error == 0 && t->ipt_data != NULL) { 7994255332Scy dst += obj.ipfo_size; 7995170268Sdarrenr if (next->fr_data != NULL) { 7996255332Scy ipfobj_t dobj; 7997255332Scy 7998255332Scy if (next->fr_type == FR_T_IPFEXPR) 7999255332Scy dobj.ipfo_type = IPFOBJ_IPFEXPR; 8000170268Sdarrenr else 8001255332Scy dobj.ipfo_type = IPFOBJ_FRIPF; 8002255332Scy dobj.ipfo_size = next->fr_dsize; 8003255332Scy dobj.ipfo_rev = obj.ipfo_rev; 8004255332Scy dobj.ipfo_ptr = dst; 8005255332Scy error = ipf_outobjk(softc, &dobj, next->fr_data); 8006170268Sdarrenr } 8007170268Sdarrenr } 8008170268Sdarrenr 8009255332Scy if ((fr != NULL) && (next == &zero)) 8010255332Scy (void) ipf_derefrule(softc, &fr); 8011172776Sdarrenr 8012170268Sdarrenr return error; 8013170268Sdarrenr} 8014170268Sdarrenr 8015170268Sdarrenr 8016170268Sdarrenr/* ------------------------------------------------------------------------ */ 8017255332Scy/* Function: ipf_frruleiter */ 8018170268Sdarrenr/* Returns: int - 0 = success, else error */ 8019255332Scy/* Parameters: softc(I)- pointer to soft context main structure */ 8020255332Scy/* data(I) - the token type to match */ 8021170268Sdarrenr/* uid(I) - uid owning the token */ 8022170268Sdarrenr/* ptr(I) - context pointer for the token */ 8023170268Sdarrenr/* */ 8024255332Scy/* This function serves as a stepping stone between ipf_ipf_ioctl and */ 8025170268Sdarrenr/* ipf_getnextrule. It's role is to find the right token in the kernel for */ 8026170268Sdarrenr/* the process doing the ioctl and use that to ask for the next rule. */ 8027170268Sdarrenr/* ------------------------------------------------------------------------ */ 8028255332Scystatic int 8029255332Scyipf_frruleiter(softc, data, uid, ctx) 8030255332Scy ipf_main_softc_t *softc; 8031255332Scy void *data, *ctx; 8032255332Scy int uid; 8033170268Sdarrenr{ 8034170268Sdarrenr ipftoken_t *token; 8035255332Scy ipfruleiter_t it; 8036255332Scy ipfobj_t obj; 8037170268Sdarrenr int error; 8038170268Sdarrenr 8039255332Scy token = ipf_token_find(softc, IPFGENITER_IPF, uid, ctx); 8040255332Scy if (token != NULL) { 8041255332Scy error = ipf_getnextrule(softc, token, data); 8042255332Scy WRITE_ENTER(&softc->ipf_tokens); 8043255332Scy ipf_token_deref(softc, token); 8044255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 8045255332Scy } else { 8046255332Scy error = ipf_inobj(softc, data, &obj, &it, IPFOBJ_IPFITER); 8047255332Scy if (error != 0) 8048255332Scy return error; 8049255332Scy it.iri_rule = NULL; 8050255332Scy error = ipf_outobj(softc, data, &it, IPFOBJ_IPFITER); 8051255332Scy } 8052170268Sdarrenr 8053170268Sdarrenr return error; 8054170268Sdarrenr} 8055170268Sdarrenr 8056170268Sdarrenr 8057170268Sdarrenr/* ------------------------------------------------------------------------ */ 8058255332Scy/* Function: ipf_geniter */ 8059170268Sdarrenr/* Returns: int - 0 = success, else error */ 8060255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8061255332Scy/* token(I) - pointer to ipftoken_t structure */ 8062255332Scy/* itp(I) - pointer to iterator data */ 8063170268Sdarrenr/* */ 8064255332Scy/* Decide which iterator function to call using information passed through */ 8065255332Scy/* the ipfgeniter_t structure at itp. */ 8066170268Sdarrenr/* ------------------------------------------------------------------------ */ 8067255332Scystatic int 8068255332Scyipf_geniter(softc, token, itp) 8069255332Scy ipf_main_softc_t *softc; 8070255332Scy ipftoken_t *token; 8071255332Scy ipfgeniter_t *itp; 8072170268Sdarrenr{ 8073170268Sdarrenr int error; 8074170268Sdarrenr 8075170268Sdarrenr switch (itp->igi_type) 8076170268Sdarrenr { 8077170268Sdarrenr case IPFGENITER_FRAG : 8078255332Scy error = ipf_frag_pkt_next(softc, token, itp); 8079170268Sdarrenr break; 8080170268Sdarrenr default : 8081255332Scy IPFERROR(92); 8082170268Sdarrenr error = EINVAL; 8083170268Sdarrenr break; 8084170268Sdarrenr } 8085170268Sdarrenr 8086170268Sdarrenr return error; 8087170268Sdarrenr} 8088170268Sdarrenr 8089170268Sdarrenr 8090170268Sdarrenr/* ------------------------------------------------------------------------ */ 8091255332Scy/* Function: ipf_genericiter */ 8092170268Sdarrenr/* Returns: int - 0 = success, else error */ 8093255332Scy/* Parameters: softc(I)- pointer to soft context main structure */ 8094255332Scy/* data(I) - the token type to match */ 8095170268Sdarrenr/* uid(I) - uid owning the token */ 8096170268Sdarrenr/* ptr(I) - context pointer for the token */ 8097170268Sdarrenr/* */ 8098255332Scy/* Handle the SIOCGENITER ioctl for the ipfilter device. The primary role */ 8099170268Sdarrenr/* ------------------------------------------------------------------------ */ 8100255332Scyint 8101255332Scyipf_genericiter(softc, data, uid, ctx) 8102255332Scy ipf_main_softc_t *softc; 8103255332Scy void *data, *ctx; 8104255332Scy int uid; 8105170268Sdarrenr{ 8106170268Sdarrenr ipftoken_t *token; 8107170268Sdarrenr ipfgeniter_t iter; 8108170268Sdarrenr int error; 8109170268Sdarrenr 8110255332Scy error = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_GENITER); 8111170268Sdarrenr if (error != 0) 8112170268Sdarrenr return error; 8113170268Sdarrenr 8114255332Scy token = ipf_token_find(softc, iter.igi_type, uid, ctx); 8115170268Sdarrenr if (token != NULL) { 8116170268Sdarrenr token->ipt_subtype = iter.igi_type; 8117255332Scy error = ipf_geniter(softc, token, &iter); 8118255332Scy WRITE_ENTER(&softc->ipf_tokens); 8119255332Scy ipf_token_deref(softc, token); 8120255332Scy RWLOCK_EXIT(&softc->ipf_tokens); 8121255332Scy } else { 8122255332Scy IPFERROR(93); 8123255332Scy error = 0; 8124255332Scy } 8125170268Sdarrenr 8126170268Sdarrenr return error; 8127170268Sdarrenr} 8128170268Sdarrenr 8129170268Sdarrenr 8130170268Sdarrenr/* ------------------------------------------------------------------------ */ 8131255332Scy/* Function: ipf_ipf_ioctl */ 8132170268Sdarrenr/* Returns: int - 0 = success, else error */ 8133255332Scy/* Parameters: softc(I)- pointer to soft context main structure */ 8134255332Scy/* data(I) - the token type to match */ 8135170268Sdarrenr/* cmd(I) - the ioctl command number */ 8136170268Sdarrenr/* mode(I) - mode flags for the ioctl */ 8137170268Sdarrenr/* uid(I) - uid owning the token */ 8138170268Sdarrenr/* ptr(I) - context pointer for the token */ 8139170268Sdarrenr/* */ 8140170268Sdarrenr/* This function handles all of the ioctl command that are actually isssued */ 8141170268Sdarrenr/* to the /dev/ipl device. */ 8142170268Sdarrenr/* ------------------------------------------------------------------------ */ 8143255332Scyint 8144255332Scyipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx) 8145255332Scy ipf_main_softc_t *softc; 8146255332Scy caddr_t data; 8147255332Scy ioctlcmd_t cmd; 8148255332Scy int mode, uid; 8149255332Scy void *ctx; 8150170268Sdarrenr{ 8151170268Sdarrenr friostat_t fio; 8152170268Sdarrenr int error, tmp; 8153255332Scy ipfobj_t obj; 8154170268Sdarrenr SPL_INT(s); 8155170268Sdarrenr 8156170268Sdarrenr switch (cmd) 8157170268Sdarrenr { 8158170268Sdarrenr case SIOCFRENB : 8159255332Scy if (!(mode & FWRITE)) { 8160255332Scy IPFERROR(94); 8161170268Sdarrenr error = EPERM; 8162255332Scy } else { 8163172776Sdarrenr error = BCOPYIN(data, &tmp, sizeof(tmp)); 8164170268Sdarrenr if (error != 0) { 8165255332Scy IPFERROR(95); 8166170268Sdarrenr error = EFAULT; 8167170268Sdarrenr break; 8168170268Sdarrenr } 8169170268Sdarrenr 8170255332Scy WRITE_ENTER(&softc->ipf_global); 8171170268Sdarrenr if (tmp) { 8172255332Scy if (softc->ipf_running > 0) 8173170268Sdarrenr error = 0; 8174170268Sdarrenr else 8175255332Scy error = ipfattach(softc); 8176170268Sdarrenr if (error == 0) 8177255332Scy softc->ipf_running = 1; 8178170268Sdarrenr else 8179255332Scy (void) ipfdetach(softc); 8180170268Sdarrenr } else { 8181255332Scy if (softc->ipf_running == 1) 8182255332Scy error = ipfdetach(softc); 8183255332Scy else 8184255332Scy error = 0; 8185170268Sdarrenr if (error == 0) 8186255332Scy softc->ipf_running = -1; 8187170268Sdarrenr } 8188255332Scy RWLOCK_EXIT(&softc->ipf_global); 8189170268Sdarrenr } 8190170268Sdarrenr break; 8191170268Sdarrenr 8192170268Sdarrenr case SIOCIPFSET : 8193170268Sdarrenr if (!(mode & FWRITE)) { 8194255332Scy IPFERROR(96); 8195170268Sdarrenr error = EPERM; 8196170268Sdarrenr break; 8197170268Sdarrenr } 8198170268Sdarrenr /* FALLTHRU */ 8199170268Sdarrenr case SIOCIPFGETNEXT : 8200170268Sdarrenr case SIOCIPFGET : 8201255332Scy error = ipf_ipftune(softc, cmd, (void *)data); 8202170268Sdarrenr break; 8203170268Sdarrenr 8204170268Sdarrenr case SIOCSETFF : 8205255332Scy if (!(mode & FWRITE)) { 8206255332Scy IPFERROR(97); 8207170268Sdarrenr error = EPERM; 8208255332Scy } else { 8209255332Scy error = BCOPYIN(data, &softc->ipf_flags, 8210255332Scy sizeof(softc->ipf_flags)); 8211255332Scy if (error != 0) { 8212255332Scy IPFERROR(98); 8213170268Sdarrenr error = EFAULT; 8214255332Scy } 8215170268Sdarrenr } 8216170268Sdarrenr break; 8217170268Sdarrenr 8218170268Sdarrenr case SIOCGETFF : 8219255332Scy error = BCOPYOUT(&softc->ipf_flags, data, 8220255332Scy sizeof(softc->ipf_flags)); 8221255332Scy if (error != 0) { 8222255332Scy IPFERROR(99); 8223170268Sdarrenr error = EFAULT; 8224255332Scy } 8225170268Sdarrenr break; 8226170268Sdarrenr 8227170268Sdarrenr case SIOCFUNCL : 8228255332Scy error = ipf_resolvefunc(softc, (void *)data); 8229170268Sdarrenr break; 8230170268Sdarrenr 8231170268Sdarrenr case SIOCINAFR : 8232170268Sdarrenr case SIOCRMAFR : 8233170268Sdarrenr case SIOCADAFR : 8234170268Sdarrenr case SIOCZRLST : 8235255332Scy if (!(mode & FWRITE)) { 8236255332Scy IPFERROR(100); 8237170268Sdarrenr error = EPERM; 8238255332Scy } else { 8239255332Scy error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, 8240255332Scy softc->ipf_active, 1); 8241255332Scy } 8242170268Sdarrenr break; 8243170268Sdarrenr 8244170268Sdarrenr case SIOCINIFR : 8245170268Sdarrenr case SIOCRMIFR : 8246170268Sdarrenr case SIOCADIFR : 8247255332Scy if (!(mode & FWRITE)) { 8248255332Scy IPFERROR(101); 8249170268Sdarrenr error = EPERM; 8250255332Scy } else { 8251255332Scy error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, 8252255332Scy 1 - softc->ipf_active, 1); 8253255332Scy } 8254170268Sdarrenr break; 8255170268Sdarrenr 8256170268Sdarrenr case SIOCSWAPA : 8257255332Scy if (!(mode & FWRITE)) { 8258255332Scy IPFERROR(102); 8259170268Sdarrenr error = EPERM; 8260255332Scy } else { 8261255332Scy WRITE_ENTER(&softc->ipf_mutex); 8262255332Scy error = BCOPYOUT(&softc->ipf_active, data, 8263255332Scy sizeof(softc->ipf_active)); 8264255332Scy if (error != 0) { 8265255332Scy IPFERROR(103); 8266170268Sdarrenr error = EFAULT; 8267255332Scy } else { 8268255332Scy softc->ipf_active = 1 - softc->ipf_active; 8269255332Scy } 8270255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 8271170268Sdarrenr } 8272170268Sdarrenr break; 8273170268Sdarrenr 8274170268Sdarrenr case SIOCGETFS : 8275255332Scy error = ipf_inobj(softc, (void *)data, &obj, &fio, 8276255332Scy IPFOBJ_IPFSTAT); 8277255332Scy if (error != 0) 8278255332Scy break; 8279255332Scy ipf_getstat(softc, &fio, obj.ipfo_rev); 8280255332Scy error = ipf_outobj(softc, (void *)data, &fio, IPFOBJ_IPFSTAT); 8281170268Sdarrenr break; 8282170268Sdarrenr 8283170268Sdarrenr case SIOCFRZST : 8284255332Scy if (!(mode & FWRITE)) { 8285255332Scy IPFERROR(104); 8286170268Sdarrenr error = EPERM; 8287255332Scy } else 8288255332Scy error = ipf_zerostats(softc, (caddr_t)data); 8289170268Sdarrenr break; 8290170268Sdarrenr 8291170268Sdarrenr case SIOCIPFFL : 8292255332Scy if (!(mode & FWRITE)) { 8293255332Scy IPFERROR(105); 8294170268Sdarrenr error = EPERM; 8295255332Scy } else { 8296172776Sdarrenr error = BCOPYIN(data, &tmp, sizeof(tmp)); 8297170268Sdarrenr if (!error) { 8298255332Scy tmp = ipf_flush(softc, IPL_LOGIPF, tmp); 8299172776Sdarrenr error = BCOPYOUT(&tmp, data, sizeof(tmp)); 8300255332Scy if (error != 0) { 8301255332Scy IPFERROR(106); 8302170268Sdarrenr error = EFAULT; 8303255332Scy } 8304255332Scy } else { 8305255332Scy IPFERROR(107); 8306170268Sdarrenr error = EFAULT; 8307255332Scy } 8308170268Sdarrenr } 8309170268Sdarrenr break; 8310170268Sdarrenr 8311170268Sdarrenr#ifdef USE_INET6 8312170268Sdarrenr case SIOCIPFL6 : 8313255332Scy if (!(mode & FWRITE)) { 8314255332Scy IPFERROR(108); 8315170268Sdarrenr error = EPERM; 8316255332Scy } else { 8317172776Sdarrenr error = BCOPYIN(data, &tmp, sizeof(tmp)); 8318170268Sdarrenr if (!error) { 8319255332Scy tmp = ipf_flush(softc, IPL_LOGIPF, tmp); 8320172776Sdarrenr error = BCOPYOUT(&tmp, data, sizeof(tmp)); 8321255332Scy if (error != 0) { 8322255332Scy IPFERROR(109); 8323170268Sdarrenr error = EFAULT; 8324255332Scy } 8325255332Scy } else { 8326255332Scy IPFERROR(110); 8327170268Sdarrenr error = EFAULT; 8328255332Scy } 8329170268Sdarrenr } 8330170268Sdarrenr break; 8331170268Sdarrenr#endif 8332170268Sdarrenr 8333170268Sdarrenr case SIOCSTLCK : 8334255332Scy if (!(mode & FWRITE)) { 8335255332Scy IPFERROR(122); 8336255332Scy error = EPERM; 8337255332Scy } else { 8338255332Scy error = BCOPYIN(data, &tmp, sizeof(tmp)); 8339255332Scy if (error == 0) { 8340255332Scy ipf_state_setlock(softc->ipf_state_soft, tmp); 8341255332Scy ipf_nat_setlock(softc->ipf_nat_soft, tmp); 8342255332Scy ipf_frag_setlock(softc->ipf_frag_soft, tmp); 8343255332Scy ipf_auth_setlock(softc->ipf_auth_soft, tmp); 8344255332Scy } else { 8345255332Scy IPFERROR(111); 8346255332Scy error = EFAULT; 8347255332Scy } 8348255332Scy } 8349170268Sdarrenr break; 8350170268Sdarrenr 8351170268Sdarrenr#ifdef IPFILTER_LOG 8352170268Sdarrenr case SIOCIPFFB : 8353255332Scy if (!(mode & FWRITE)) { 8354255332Scy IPFERROR(112); 8355170268Sdarrenr error = EPERM; 8356255332Scy } else { 8357255332Scy tmp = ipf_log_clear(softc, IPL_LOGIPF); 8358172776Sdarrenr error = BCOPYOUT(&tmp, data, sizeof(tmp)); 8359255332Scy if (error) { 8360255332Scy IPFERROR(113); 8361170268Sdarrenr error = EFAULT; 8362255332Scy } 8363170268Sdarrenr } 8364170268Sdarrenr break; 8365170268Sdarrenr#endif /* IPFILTER_LOG */ 8366170268Sdarrenr 8367170268Sdarrenr case SIOCFRSYN : 8368255332Scy if (!(mode & FWRITE)) { 8369255332Scy IPFERROR(114); 8370170268Sdarrenr error = EPERM; 8371255332Scy } else { 8372255332Scy WRITE_ENTER(&softc->ipf_global); 8373255332Scy#if (defined(MENTAT) && defined(_KERNEL)) && !defined(INSTANCES) 8374170268Sdarrenr error = ipfsync(); 8375170268Sdarrenr#else 8376255332Scy ipf_sync(softc, NULL); 8377170268Sdarrenr error = 0; 8378170268Sdarrenr#endif 8379255332Scy RWLOCK_EXIT(&softc->ipf_global); 8380170268Sdarrenr 8381170268Sdarrenr } 8382170268Sdarrenr break; 8383170268Sdarrenr 8384170268Sdarrenr case SIOCGFRST : 8385255332Scy error = ipf_outobj(softc, (void *)data, 8386255332Scy ipf_frag_stats(softc->ipf_frag_soft), 8387255332Scy IPFOBJ_FRAGSTAT); 8388170268Sdarrenr break; 8389170268Sdarrenr 8390170268Sdarrenr#ifdef IPFILTER_LOG 8391170268Sdarrenr case FIONREAD : 8392255332Scy tmp = ipf_log_bytesused(softc, IPL_LOGIPF); 8393172776Sdarrenr error = BCOPYOUT(&tmp, data, sizeof(tmp)); 8394170268Sdarrenr break; 8395170268Sdarrenr#endif 8396170268Sdarrenr 8397170268Sdarrenr case SIOCIPFITER : 8398170268Sdarrenr SPL_SCHED(s); 8399255332Scy error = ipf_frruleiter(softc, data, uid, ctx); 8400170268Sdarrenr SPL_X(s); 8401170268Sdarrenr break; 8402170268Sdarrenr 8403170268Sdarrenr case SIOCGENITER : 8404170268Sdarrenr SPL_SCHED(s); 8405255332Scy error = ipf_genericiter(softc, data, uid, ctx); 8406170268Sdarrenr SPL_X(s); 8407170268Sdarrenr break; 8408170268Sdarrenr 8409170268Sdarrenr case SIOCIPFDELTOK : 8410172776Sdarrenr error = BCOPYIN(data, &tmp, sizeof(tmp)); 8411255332Scy if (error == 0) { 8412255332Scy SPL_SCHED(s); 8413255332Scy error = ipf_token_del(softc, tmp, uid, ctx); 8414255332Scy SPL_X(s); 8415255332Scy } 8416170268Sdarrenr break; 8417170268Sdarrenr 8418170268Sdarrenr default : 8419255332Scy IPFERROR(115); 8420170268Sdarrenr error = EINVAL; 8421170268Sdarrenr break; 8422170268Sdarrenr } 8423170268Sdarrenr 8424170268Sdarrenr return error; 8425170268Sdarrenr} 8426170268Sdarrenr 8427170268Sdarrenr 8428170268Sdarrenr/* ------------------------------------------------------------------------ */ 8429255332Scy/* Function: ipf_decaps */ 8430255332Scy/* Returns: int - -1 == decapsulation failed, else bit mask of */ 8431255332Scy/* flags indicating packet filtering decision. */ 8432255332Scy/* Parameters: fin(I) - pointer to packet information */ 8433255332Scy/* pass(I) - IP protocol version to match */ 8434255332Scy/* l5proto(I) - layer 5 protocol to decode UDP data as. */ 8435255332Scy/* */ 8436255332Scy/* This function is called for packets that are wrapt up in other packets, */ 8437255332Scy/* for example, an IP packet that is the entire data segment for another IP */ 8438255332Scy/* packet. If the basic constraints for this are satisfied, change the */ 8439255332Scy/* buffer to point to the start of the inner packet and start processing */ 8440255332Scy/* rules belonging to the head group this rule specifies. */ 8441255332Scy/* ------------------------------------------------------------------------ */ 8442255332Scyu_32_t 8443255332Scyipf_decaps(fin, pass, l5proto) 8444255332Scy fr_info_t *fin; 8445255332Scy u_32_t pass; 8446255332Scy int l5proto; 8447255332Scy{ 8448255332Scy fr_info_t fin2, *fino = NULL; 8449255332Scy int elen, hlen, nh; 8450255332Scy grehdr_t gre; 8451255332Scy ip_t *ip; 8452255332Scy mb_t *m; 8453255332Scy 8454255332Scy if ((fin->fin_flx & FI_COALESCE) == 0) 8455255332Scy if (ipf_coalesce(fin) == -1) 8456255332Scy goto cantdecaps; 8457255332Scy 8458255332Scy m = fin->fin_m; 8459255332Scy hlen = fin->fin_hlen; 8460255332Scy 8461255332Scy switch (fin->fin_p) 8462255332Scy { 8463255332Scy case IPPROTO_UDP : 8464255332Scy /* 8465255332Scy * In this case, the specific protocol being decapsulated 8466255332Scy * inside UDP frames comes from the rule. 8467255332Scy */ 8468255332Scy nh = fin->fin_fr->fr_icode; 8469255332Scy break; 8470255332Scy 8471255332Scy case IPPROTO_GRE : /* 47 */ 8472255332Scy bcopy(fin->fin_dp, (char *)&gre, sizeof(gre)); 8473255332Scy hlen += sizeof(grehdr_t); 8474255332Scy if (gre.gr_R|gre.gr_s) 8475255332Scy goto cantdecaps; 8476255332Scy if (gre.gr_C) 8477255332Scy hlen += 4; 8478255332Scy if (gre.gr_K) 8479255332Scy hlen += 4; 8480255332Scy if (gre.gr_S) 8481255332Scy hlen += 4; 8482255332Scy 8483255332Scy nh = IPPROTO_IP; 8484255332Scy 8485255332Scy /* 8486255332Scy * If the routing options flag is set, validate that it is 8487255332Scy * there and bounce over it. 8488255332Scy */ 8489255332Scy#if 0 8490255332Scy /* This is really heavy weight and lots of room for error, */ 8491255332Scy /* so for now, put it off and get the simple stuff right. */ 8492255332Scy if (gre.gr_R) { 8493255332Scy u_char off, len, *s; 8494255332Scy u_short af; 8495255332Scy int end; 8496255332Scy 8497255332Scy end = 0; 8498255332Scy s = fin->fin_dp; 8499255332Scy s += hlen; 8500255332Scy aplen = fin->fin_plen - hlen; 8501255332Scy while (aplen > 3) { 8502255332Scy af = (s[0] << 8) | s[1]; 8503255332Scy off = s[2]; 8504255332Scy len = s[3]; 8505255332Scy aplen -= 4; 8506255332Scy s += 4; 8507255332Scy if (af == 0 && len == 0) { 8508255332Scy end = 1; 8509255332Scy break; 8510255332Scy } 8511255332Scy if (aplen < len) 8512255332Scy break; 8513255332Scy s += len; 8514255332Scy aplen -= len; 8515255332Scy } 8516255332Scy if (end != 1) 8517255332Scy goto cantdecaps; 8518255332Scy hlen = s - (u_char *)fin->fin_dp; 8519255332Scy } 8520255332Scy#endif 8521255332Scy break; 8522255332Scy 8523255332Scy#ifdef IPPROTO_IPIP 8524255332Scy case IPPROTO_IPIP : /* 4 */ 8525255332Scy#endif 8526255332Scy nh = IPPROTO_IP; 8527255332Scy break; 8528255332Scy 8529255332Scy default : /* Includes ESP, AH is special for IPv4 */ 8530255332Scy goto cantdecaps; 8531255332Scy } 8532255332Scy 8533255332Scy switch (nh) 8534255332Scy { 8535255332Scy case IPPROTO_IP : 8536255332Scy case IPPROTO_IPV6 : 8537255332Scy break; 8538255332Scy default : 8539255332Scy goto cantdecaps; 8540255332Scy } 8541255332Scy 8542255332Scy bcopy((char *)fin, (char *)&fin2, sizeof(fin2)); 8543255332Scy fino = fin; 8544255332Scy fin = &fin2; 8545255332Scy elen = hlen; 8546255332Scy#if defined(MENTAT) && defined(_KERNEL) 8547255332Scy m->b_rptr += elen; 8548255332Scy#else 8549255332Scy m->m_data += elen; 8550255332Scy m->m_len -= elen; 8551255332Scy#endif 8552255332Scy fin->fin_plen -= elen; 8553255332Scy 8554255332Scy ip = (ip_t *)((char *)fin->fin_ip + elen); 8555255332Scy 8556255332Scy /* 8557255332Scy * Make sure we have at least enough data for the network layer 8558255332Scy * header. 8559255332Scy */ 8560255332Scy if (IP_V(ip) == 4) 8561255332Scy hlen = IP_HL(ip) << 2; 8562255332Scy#ifdef USE_INET6 8563255332Scy else if (IP_V(ip) == 6) 8564255332Scy hlen = sizeof(ip6_t); 8565255332Scy#endif 8566255332Scy else 8567255332Scy goto cantdecaps2; 8568255332Scy 8569255332Scy if (fin->fin_plen < hlen) 8570255332Scy goto cantdecaps2; 8571255332Scy 8572255332Scy fin->fin_dp = (char *)ip + hlen; 8573255332Scy 8574255332Scy if (IP_V(ip) == 4) { 8575255332Scy /* 8576255332Scy * Perform IPv4 header checksum validation. 8577255332Scy */ 8578255332Scy if (ipf_cksum((u_short *)ip, hlen)) 8579255332Scy goto cantdecaps2; 8580255332Scy } 8581255332Scy 8582255332Scy if (ipf_makefrip(hlen, ip, fin) == -1) { 8583255332Scycantdecaps2: 8584255332Scy if (m != NULL) { 8585255332Scy#if defined(MENTAT) && defined(_KERNEL) 8586255332Scy m->b_rptr -= elen; 8587255332Scy#else 8588255332Scy m->m_data -= elen; 8589255332Scy m->m_len += elen; 8590255332Scy#endif 8591255332Scy } 8592255332Scycantdecaps: 8593255332Scy DT1(frb_decapfrip, fr_info_t *, fin); 8594255332Scy pass &= ~FR_CMDMASK; 8595255332Scy pass |= FR_BLOCK|FR_QUICK; 8596255332Scy fin->fin_reason = FRB_DECAPFRIP; 8597255332Scy return -1; 8598255332Scy } 8599255332Scy 8600255332Scy pass = ipf_scanlist(fin, pass); 8601255332Scy 8602255332Scy /* 8603255332Scy * Copy the packet filter "result" fields out of the fr_info_t struct 8604255332Scy * that is local to the decapsulation processing and back into the 8605255332Scy * one we were called with. 8606255332Scy */ 8607255332Scy fino->fin_flx = fin->fin_flx; 8608255332Scy fino->fin_rev = fin->fin_rev; 8609255332Scy fino->fin_icode = fin->fin_icode; 8610255332Scy fino->fin_rule = fin->fin_rule; 8611255332Scy (void) strncpy(fino->fin_group, fin->fin_group, FR_GROUPLEN); 8612255332Scy fino->fin_fr = fin->fin_fr; 8613255332Scy fino->fin_error = fin->fin_error; 8614255332Scy fino->fin_mp = fin->fin_mp; 8615255332Scy fino->fin_m = fin->fin_m; 8616255332Scy m = fin->fin_m; 8617255332Scy if (m != NULL) { 8618255332Scy#if defined(MENTAT) && defined(_KERNEL) 8619255332Scy m->b_rptr -= elen; 8620255332Scy#else 8621255332Scy m->m_data -= elen; 8622255332Scy m->m_len += elen; 8623255332Scy#endif 8624255332Scy } 8625255332Scy return pass; 8626255332Scy} 8627255332Scy 8628255332Scy 8629255332Scy/* ------------------------------------------------------------------------ */ 8630255332Scy/* Function: ipf_matcharray_load */ 8631255332Scy/* Returns: int - 0 = success, else error */ 8632255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8633255332Scy/* data(I) - pointer to ioctl data */ 8634255332Scy/* objp(I) - ipfobj_t structure to load data into */ 8635255332Scy/* arrayptr(I) - pointer to location to store array pointer */ 8636255332Scy/* */ 8637255332Scy/* This function loads in a mathing array through the ipfobj_t struct that */ 8638255332Scy/* describes it. Sanity checking and array size limitations are enforced */ 8639255332Scy/* in this function to prevent userspace from trying to load in something */ 8640255332Scy/* that is insanely big. Once the size of the array is known, the memory */ 8641255332Scy/* required is malloc'd and returned through changing *arrayptr. The */ 8642255332Scy/* contents of the array are verified before returning. Only in the event */ 8643255332Scy/* of a successful call is the caller required to free up the malloc area. */ 8644255332Scy/* ------------------------------------------------------------------------ */ 8645255332Scyint 8646255332Scyipf_matcharray_load(softc, data, objp, arrayptr) 8647255332Scy ipf_main_softc_t *softc; 8648255332Scy caddr_t data; 8649255332Scy ipfobj_t *objp; 8650255332Scy int **arrayptr; 8651255332Scy{ 8652255332Scy int arraysize, *array, error; 8653255332Scy 8654255332Scy *arrayptr = NULL; 8655255332Scy 8656255332Scy error = BCOPYIN(data, objp, sizeof(*objp)); 8657255332Scy if (error != 0) { 8658255332Scy IPFERROR(116); 8659255332Scy return EFAULT; 8660255332Scy } 8661255332Scy 8662255332Scy if (objp->ipfo_type != IPFOBJ_IPFEXPR) { 8663255332Scy IPFERROR(117); 8664255332Scy return EINVAL; 8665255332Scy } 8666255332Scy 8667255332Scy if (((objp->ipfo_size & 3) != 0) || (objp->ipfo_size == 0) || 8668255332Scy (objp->ipfo_size > 1024)) { 8669255332Scy IPFERROR(118); 8670255332Scy return EINVAL; 8671255332Scy } 8672255332Scy 8673255332Scy arraysize = objp->ipfo_size * sizeof(*array); 8674255332Scy KMALLOCS(array, int *, arraysize); 8675255332Scy if (array == NULL) { 8676255332Scy IPFERROR(119); 8677255332Scy return ENOMEM; 8678255332Scy } 8679255332Scy 8680255332Scy error = COPYIN(objp->ipfo_ptr, array, arraysize); 8681255332Scy if (error != 0) { 8682255332Scy KFREES(array, arraysize); 8683255332Scy IPFERROR(120); 8684255332Scy return EFAULT; 8685255332Scy } 8686255332Scy 8687255332Scy if (ipf_matcharray_verify(array, arraysize) != 0) { 8688255332Scy KFREES(array, arraysize); 8689255332Scy IPFERROR(121); 8690255332Scy return EINVAL; 8691255332Scy } 8692255332Scy 8693255332Scy *arrayptr = array; 8694255332Scy return 0; 8695255332Scy} 8696255332Scy 8697255332Scy 8698255332Scy/* ------------------------------------------------------------------------ */ 8699255332Scy/* Function: ipf_matcharray_verify */ 8700255332Scy/* Returns: Nil */ 8701255332Scy/* Parameters: array(I) - pointer to matching array */ 8702255332Scy/* arraysize(I) - number of elements in the array */ 8703255332Scy/* */ 8704255332Scy/* Verify the contents of a matching array by stepping through each element */ 8705255332Scy/* in it. The actual commands in the array are not verified for */ 8706255332Scy/* correctness, only that all of the sizes are correctly within limits. */ 8707255332Scy/* ------------------------------------------------------------------------ */ 8708255332Scyint 8709255332Scyipf_matcharray_verify(array, arraysize) 8710255332Scy int *array, arraysize; 8711255332Scy{ 8712255332Scy int i, nelem, maxidx; 8713255332Scy ipfexp_t *e; 8714255332Scy 8715255332Scy nelem = arraysize / sizeof(*array); 8716255332Scy 8717255332Scy /* 8718255332Scy * Currently, it makes no sense to have an array less than 6 8719255332Scy * elements long - the initial size at the from, a single operation 8720255332Scy * (minimum 4 in length) and a trailer, for a total of 6. 8721255332Scy */ 8722255332Scy if ((array[0] < 6) || (arraysize < 24) || (arraysize > 4096)) { 8723255332Scy return -1; 8724255332Scy } 8725255332Scy 8726255332Scy /* 8727255332Scy * Verify the size of data pointed to by array with how long 8728255332Scy * the array claims to be itself. 8729255332Scy */ 8730255332Scy if (array[0] * sizeof(*array) != arraysize) { 8731255332Scy return -1; 8732255332Scy } 8733255332Scy 8734255332Scy maxidx = nelem - 1; 8735255332Scy /* 8736255332Scy * The last opcode in this array should be an IPF_EXP_END. 8737255332Scy */ 8738255332Scy if (array[maxidx] != IPF_EXP_END) { 8739255332Scy return -1; 8740255332Scy } 8741255332Scy 8742255332Scy for (i = 1; i < maxidx; ) { 8743255332Scy e = (ipfexp_t *)(array + i); 8744255332Scy 8745255332Scy /* 8746255332Scy * The length of the bits to check must be at least 1 8747255332Scy * (or else there is nothing to comapre with!) and it 8748255332Scy * cannot exceed the length of the data present. 8749255332Scy */ 8750255332Scy if ((e->ipfe_size < 1 ) || 8751255332Scy (e->ipfe_size + i > maxidx)) { 8752255332Scy return -1; 8753255332Scy } 8754255332Scy i += e->ipfe_size; 8755255332Scy } 8756255332Scy return 0; 8757255332Scy} 8758255332Scy 8759255332Scy 8760255332Scy/* ------------------------------------------------------------------------ */ 8761255332Scy/* Function: ipf_fr_matcharray */ 8762255332Scy/* Returns: int - 0 = match failed, else positive match */ 8763255332Scy/* Parameters: fin(I) - pointer to packet information */ 8764255332Scy/* array(I) - pointer to matching array */ 8765255332Scy/* */ 8766255332Scy/* This function is used to apply a matching array against a packet and */ 8767255332Scy/* return an indication of whether or not the packet successfully matches */ 8768255332Scy/* all of the commands in it. */ 8769255332Scy/* ------------------------------------------------------------------------ */ 8770255332Scystatic int 8771255332Scyipf_fr_matcharray(fin, array) 8772255332Scy fr_info_t *fin; 8773255332Scy int *array; 8774255332Scy{ 8775255332Scy int i, n, *x, rv, p; 8776255332Scy ipfexp_t *e; 8777255332Scy 8778255332Scy rv = 0; 8779255332Scy n = array[0]; 8780255332Scy x = array + 1; 8781255332Scy 8782255332Scy for (; n > 0; x += 3 + x[3], rv = 0) { 8783255332Scy e = (ipfexp_t *)x; 8784255332Scy if (e->ipfe_cmd == IPF_EXP_END) 8785255332Scy break; 8786255332Scy n -= e->ipfe_size; 8787255332Scy 8788255332Scy /* 8789255332Scy * The upper 16 bits currently store the protocol value. 8790255332Scy * This is currently used with TCP and UDP port compares and 8791255332Scy * allows "tcp.port = 80" without requiring an explicit 8792255332Scy " "ip.pr = tcp" first. 8793255332Scy */ 8794255332Scy p = e->ipfe_cmd >> 16; 8795255332Scy if ((p != 0) && (p != fin->fin_p)) 8796255332Scy break; 8797255332Scy 8798255332Scy switch (e->ipfe_cmd) 8799255332Scy { 8800255332Scy case IPF_EXP_IP_PR : 8801255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8802255332Scy rv |= (fin->fin_p == e->ipfe_arg0[i]); 8803255332Scy } 8804255332Scy break; 8805255332Scy 8806255332Scy case IPF_EXP_IP_SRCADDR : 8807255332Scy if (fin->fin_v != 4) 8808255332Scy break; 8809255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8810255332Scy rv |= ((fin->fin_saddr & 8811255332Scy e->ipfe_arg0[i * 2 + 1]) == 8812255332Scy e->ipfe_arg0[i * 2]); 8813255332Scy } 8814255332Scy break; 8815255332Scy 8816255332Scy case IPF_EXP_IP_DSTADDR : 8817255332Scy if (fin->fin_v != 4) 8818255332Scy break; 8819255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8820255332Scy rv |= ((fin->fin_daddr & 8821255332Scy e->ipfe_arg0[i * 2 + 1]) == 8822255332Scy e->ipfe_arg0[i * 2]); 8823255332Scy } 8824255332Scy break; 8825255332Scy 8826255332Scy case IPF_EXP_IP_ADDR : 8827255332Scy if (fin->fin_v != 4) 8828255332Scy break; 8829255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8830255332Scy rv |= ((fin->fin_saddr & 8831255332Scy e->ipfe_arg0[i * 2 + 1]) == 8832255332Scy e->ipfe_arg0[i * 2]) || 8833255332Scy ((fin->fin_daddr & 8834255332Scy e->ipfe_arg0[i * 2 + 1]) == 8835255332Scy e->ipfe_arg0[i * 2]); 8836255332Scy } 8837255332Scy break; 8838255332Scy 8839255332Scy#ifdef USE_INET6 8840255332Scy case IPF_EXP_IP6_SRCADDR : 8841255332Scy if (fin->fin_v != 6) 8842255332Scy break; 8843255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8844255332Scy rv |= IP6_MASKEQ(&fin->fin_src6, 8845255332Scy &e->ipfe_arg0[i * 8 + 4], 8846255332Scy &e->ipfe_arg0[i * 8]); 8847255332Scy } 8848255332Scy break; 8849255332Scy 8850255332Scy case IPF_EXP_IP6_DSTADDR : 8851255332Scy if (fin->fin_v != 6) 8852255332Scy break; 8853255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8854255332Scy rv |= IP6_MASKEQ(&fin->fin_dst6, 8855255332Scy &e->ipfe_arg0[i * 8 + 4], 8856255332Scy &e->ipfe_arg0[i * 8]); 8857255332Scy } 8858255332Scy break; 8859255332Scy 8860255332Scy case IPF_EXP_IP6_ADDR : 8861255332Scy if (fin->fin_v != 6) 8862255332Scy break; 8863255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8864255332Scy rv |= IP6_MASKEQ(&fin->fin_src6, 8865255332Scy &e->ipfe_arg0[i * 8 + 4], 8866255332Scy &e->ipfe_arg0[i * 8]) || 8867255332Scy IP6_MASKEQ(&fin->fin_dst6, 8868255332Scy &e->ipfe_arg0[i * 8 + 4], 8869255332Scy &e->ipfe_arg0[i * 8]); 8870255332Scy } 8871255332Scy break; 8872255332Scy#endif 8873255332Scy 8874255332Scy case IPF_EXP_UDP_PORT : 8875255332Scy case IPF_EXP_TCP_PORT : 8876255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8877255332Scy rv |= (fin->fin_sport == e->ipfe_arg0[i]) || 8878255332Scy (fin->fin_dport == e->ipfe_arg0[i]); 8879255332Scy } 8880255332Scy break; 8881255332Scy 8882255332Scy case IPF_EXP_UDP_SPORT : 8883255332Scy case IPF_EXP_TCP_SPORT : 8884255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8885255332Scy rv |= (fin->fin_sport == e->ipfe_arg0[i]); 8886255332Scy } 8887255332Scy break; 8888255332Scy 8889255332Scy case IPF_EXP_UDP_DPORT : 8890255332Scy case IPF_EXP_TCP_DPORT : 8891255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8892255332Scy rv |= (fin->fin_dport == e->ipfe_arg0[i]); 8893255332Scy } 8894255332Scy break; 8895255332Scy 8896255332Scy case IPF_EXP_TCP_FLAGS : 8897255332Scy for (i = 0; !rv && i < e->ipfe_narg; i++) { 8898255332Scy rv |= ((fin->fin_tcpf & 8899255332Scy e->ipfe_arg0[i * 2 + 1]) == 8900255332Scy e->ipfe_arg0[i * 2]); 8901255332Scy } 8902255332Scy break; 8903255332Scy } 8904255332Scy rv ^= e->ipfe_not; 8905255332Scy 8906255332Scy if (rv == 0) 8907255332Scy break; 8908255332Scy } 8909255332Scy 8910255332Scy return rv; 8911255332Scy} 8912255332Scy 8913255332Scy 8914255332Scy/* ------------------------------------------------------------------------ */ 8915170268Sdarrenr/* Function: ipf_queueflush */ 8916170268Sdarrenr/* Returns: int - number of entries flushed (0 = none) */ 8917255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 8918255332Scy/* deletefn(I) - function to call to delete entry */ 8919170268Sdarrenr/* ipfqs(I) - top of the list of ipf internal queues */ 8920170268Sdarrenr/* userqs(I) - top of the list of user defined timeouts */ 8921170268Sdarrenr/* */ 8922170268Sdarrenr/* This fucntion gets called when the state/NAT hash tables fill up and we */ 8923255332Scy/* need to try a bit harder to free up some space. The algorithm used here */ 8924255332Scy/* split into two parts but both halves have the same goal: to reduce the */ 8925255332Scy/* number of connections considered to be "active" to the low watermark. */ 8926255332Scy/* There are two steps in doing this: */ 8927255332Scy/* 1) Remove any TCP connections that are already considered to be "closed" */ 8928255332Scy/* but have not yet been removed from the state table. The two states */ 8929255332Scy/* TCPS_TIME_WAIT and TCPS_CLOSED are considered to be the perfect */ 8930255332Scy/* candidates for this style of removal. If freeing up entries in */ 8931255332Scy/* CLOSED or both CLOSED and TIME_WAIT brings us to the low watermark, */ 8932255332Scy/* we do not go on to step 2. */ 8933255332Scy/* */ 8934255332Scy/* 2) Look for the oldest entries on each timeout queue and free them if */ 8935255332Scy/* they are within the given window we are considering. Where the */ 8936255332Scy/* window starts and the steps taken to increase its size depend upon */ 8937255332Scy/* how long ipf has been running (ipf_ticks.) Anything modified in the */ 8938255332Scy/* last 30 seconds is not touched. */ 8939170268Sdarrenr/* touched */ 8940255332Scy/* die ipf_ticks 30*1.5 1800*1.5 | 43200*1.5 */ 8941170268Sdarrenr/* | | | | | | */ 8942170268Sdarrenr/* future <--+----------+--------+-----------+-----+-----+-----------> past */ 8943170268Sdarrenr/* now \_int=30s_/ \_int=1hr_/ \_int=12hr */ 8944170268Sdarrenr/* */ 8945170268Sdarrenr/* Points to note: */ 8946170268Sdarrenr/* - tqe_die is the time, in the future, when entries die. */ 8947255332Scy/* - tqe_die - ipf_ticks is how long left the connection has to live in ipf */ 8948170268Sdarrenr/* ticks. */ 8949170268Sdarrenr/* - tqe_touched is when the entry was last used by NAT/state */ 8950255332Scy/* - the closer tqe_touched is to ipf_ticks, the further tqe_die will be */ 8951255332Scy/* ipf_ticks any given timeout queue and vice versa. */ 8952170268Sdarrenr/* - both tqe_die and tqe_touched increase over time */ 8953170268Sdarrenr/* - timeout queues are sorted with the highest value of tqe_die at the */ 8954170268Sdarrenr/* bottom and therefore the smallest values of each are at the top */ 8955255332Scy/* - the pointer passed in as ipfqs should point to an array of timeout */ 8956255332Scy/* queues representing each of the TCP states */ 8957170268Sdarrenr/* */ 8958170268Sdarrenr/* We start by setting up a maximum range to scan for things to move of */ 8959170268Sdarrenr/* iend (newest) to istart (oldest) in chunks of "interval". If nothing is */ 8960170268Sdarrenr/* found in that range, "interval" is adjusted (so long as it isn't 30) and */ 8961255332Scy/* we start again with a new value for "iend" and "istart". This is */ 8962255332Scy/* continued until we either finish the scan of 30 second intervals or the */ 8963255332Scy/* low water mark is reached. */ 8964170268Sdarrenr/* ------------------------------------------------------------------------ */ 8965255332Scyint 8966255332Scyipf_queueflush(softc, deletefn, ipfqs, userqs, activep, size, low) 8967255332Scy ipf_main_softc_t *softc; 8968255332Scy ipftq_delete_fn_t deletefn; 8969255332Scy ipftq_t *ipfqs, *userqs; 8970255332Scy u_int *activep; 8971255332Scy int size, low; 8972170268Sdarrenr{ 8973170268Sdarrenr u_long interval, istart, iend; 8974170268Sdarrenr ipftq_t *ifq, *ifqnext; 8975170268Sdarrenr ipftqent_t *tqe, *tqn; 8976255332Scy int removed = 0; 8977170268Sdarrenr 8978255332Scy for (tqn = ipfqs[IPF_TCPS_CLOSED].ifq_head; ((tqe = tqn) != NULL); ) { 8979255332Scy tqn = tqe->tqe_next; 8980255332Scy if ((*deletefn)(softc, tqe->tqe_parent) == 0) 8981255332Scy removed++; 8982255332Scy } 8983255332Scy if ((*activep * 100 / size) > low) { 8984255332Scy for (tqn = ipfqs[IPF_TCPS_TIME_WAIT].ifq_head; 8985255332Scy ((tqe = tqn) != NULL); ) { 8986255332Scy tqn = tqe->tqe_next; 8987255332Scy if ((*deletefn)(softc, tqe->tqe_parent) == 0) 8988255332Scy removed++; 8989255332Scy } 8990255332Scy } 8991255332Scy 8992255332Scy if ((*activep * 100 / size) <= low) { 8993255332Scy return removed; 8994255332Scy } 8995255332Scy 8996170268Sdarrenr /* 8997170268Sdarrenr * NOTE: Use of "* 15 / 10" is required here because if "* 1.5" is 8998170268Sdarrenr * used then the operations are upgraded to floating point 8999170268Sdarrenr * and kernels don't like floating point... 9000170268Sdarrenr */ 9001255332Scy if (softc->ipf_ticks > IPF_TTLVAL(43200 * 15 / 10)) { 9002170268Sdarrenr istart = IPF_TTLVAL(86400 * 4); 9003170268Sdarrenr interval = IPF_TTLVAL(43200); 9004255332Scy } else if (softc->ipf_ticks > IPF_TTLVAL(1800 * 15 / 10)) { 9005170268Sdarrenr istart = IPF_TTLVAL(43200); 9006170268Sdarrenr interval = IPF_TTLVAL(1800); 9007255332Scy } else if (softc->ipf_ticks > IPF_TTLVAL(30 * 15 / 10)) { 9008170268Sdarrenr istart = IPF_TTLVAL(1800); 9009170268Sdarrenr interval = IPF_TTLVAL(30); 9010170268Sdarrenr } else { 9011170268Sdarrenr return 0; 9012170268Sdarrenr } 9013255332Scy if (istart > softc->ipf_ticks) { 9014255332Scy if (softc->ipf_ticks - interval < interval) 9015172776Sdarrenr istart = interval; 9016172776Sdarrenr else 9017255332Scy istart = (softc->ipf_ticks / interval) * interval; 9018170268Sdarrenr } 9019170268Sdarrenr 9020255332Scy iend = softc->ipf_ticks - interval; 9021170268Sdarrenr 9022255332Scy while ((*activep * 100 / size) > low) { 9023170268Sdarrenr u_long try; 9024170268Sdarrenr 9025255332Scy try = softc->ipf_ticks - istart; 9026170268Sdarrenr 9027170268Sdarrenr for (ifq = ipfqs; ifq != NULL; ifq = ifq->ifq_next) { 9028170268Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { 9029170268Sdarrenr if (try < tqe->tqe_touched) 9030170268Sdarrenr break; 9031170268Sdarrenr tqn = tqe->tqe_next; 9032255332Scy if ((*deletefn)(softc, tqe->tqe_parent) == 0) 9033170268Sdarrenr removed++; 9034170268Sdarrenr } 9035170268Sdarrenr } 9036170268Sdarrenr 9037170268Sdarrenr for (ifq = userqs; ifq != NULL; ifq = ifqnext) { 9038170268Sdarrenr ifqnext = ifq->ifq_next; 9039170268Sdarrenr 9040170268Sdarrenr for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { 9041170268Sdarrenr if (try < tqe->tqe_touched) 9042170268Sdarrenr break; 9043170268Sdarrenr tqn = tqe->tqe_next; 9044255332Scy if ((*deletefn)(softc, tqe->tqe_parent) == 0) 9045170268Sdarrenr removed++; 9046170268Sdarrenr } 9047170268Sdarrenr } 9048170268Sdarrenr 9049170268Sdarrenr if (try >= iend) { 9050170268Sdarrenr if (interval == IPF_TTLVAL(43200)) { 9051170268Sdarrenr interval = IPF_TTLVAL(1800); 9052170268Sdarrenr } else if (interval == IPF_TTLVAL(1800)) { 9053170268Sdarrenr interval = IPF_TTLVAL(30); 9054170268Sdarrenr } else { 9055170268Sdarrenr break; 9056170268Sdarrenr } 9057255332Scy if (interval >= softc->ipf_ticks) 9058170268Sdarrenr break; 9059170268Sdarrenr 9060255332Scy iend = softc->ipf_ticks - interval; 9061170268Sdarrenr } 9062172776Sdarrenr istart -= interval; 9063170268Sdarrenr } 9064170268Sdarrenr 9065170268Sdarrenr return removed; 9066170268Sdarrenr} 9067255332Scy 9068255332Scy 9069255332Scy/* ------------------------------------------------------------------------ */ 9070255332Scy/* Function: ipf_deliverlocal */ 9071255332Scy/* Returns: int - 1 = local address, 0 = non-local address */ 9072255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9073255332Scy/* ipversion(I) - IP protocol version (4 or 6) */ 9074255332Scy/* ifp(I) - network interface pointer */ 9075255332Scy/* ipaddr(I) - IPv4/6 destination address */ 9076255332Scy/* */ 9077255332Scy/* This fucntion is used to determine in the address "ipaddr" belongs to */ 9078255332Scy/* the network interface represented by ifp. */ 9079255332Scy/* ------------------------------------------------------------------------ */ 9080255332Scyint 9081255332Scyipf_deliverlocal(softc, ipversion, ifp, ipaddr) 9082255332Scy ipf_main_softc_t *softc; 9083255332Scy int ipversion; 9084255332Scy void *ifp; 9085255332Scy i6addr_t *ipaddr; 9086255332Scy{ 9087255332Scy i6addr_t addr; 9088255332Scy int islocal = 0; 9089255332Scy 9090255332Scy if (ipversion == 4) { 9091255332Scy if (ipf_ifpaddr(softc, 4, FRI_NORMAL, ifp, &addr, NULL) == 0) { 9092255332Scy if (addr.in4.s_addr == ipaddr->in4.s_addr) 9093255332Scy islocal = 1; 9094255332Scy } 9095255332Scy 9096255332Scy#ifdef USE_INET6 9097255332Scy } else if (ipversion == 6) { 9098255332Scy if (ipf_ifpaddr(softc, 6, FRI_NORMAL, ifp, &addr, NULL) == 0) { 9099255332Scy if (IP6_EQ(&addr, ipaddr)) 9100255332Scy islocal = 1; 9101255332Scy } 9102255332Scy#endif 9103255332Scy } 9104255332Scy 9105255332Scy return islocal; 9106255332Scy} 9107255332Scy 9108255332Scy 9109255332Scy/* ------------------------------------------------------------------------ */ 9110255332Scy/* Function: ipf_settimeout */ 9111255332Scy/* Returns: int - 0 = success, -1 = failure */ 9112255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9113255332Scy/* t(I) - pointer to tuneable array entry */ 9114255332Scy/* p(I) - pointer to values passed in to apply */ 9115255332Scy/* */ 9116255332Scy/* This function is called to set the timeout values for each distinct */ 9117255332Scy/* queue timeout that is available. When called, it calls into both the */ 9118255332Scy/* state and NAT code, telling them to update their timeout queues. */ 9119255332Scy/* ------------------------------------------------------------------------ */ 9120255332Scystatic int 9121255332Scyipf_settimeout(softc, t, p) 9122255332Scy struct ipf_main_softc_s *softc; 9123255332Scy ipftuneable_t *t; 9124255332Scy ipftuneval_t *p; 9125255332Scy{ 9126255332Scy 9127255332Scy /* 9128255332Scy * ipf_interror should be set by the functions called here, not 9129255332Scy * by this function - it's just a middle man. 9130255332Scy */ 9131255332Scy if (ipf_state_settimeout(softc, t, p) == -1) 9132255332Scy return -1; 9133255332Scy if (ipf_nat_settimeout(softc, t, p) == -1) 9134255332Scy return -1; 9135255332Scy return 0; 9136255332Scy} 9137255332Scy 9138255332Scy 9139255332Scy/* ------------------------------------------------------------------------ */ 9140255332Scy/* Function: ipf_apply_timeout */ 9141255332Scy/* Returns: int - 0 = success, -1 = failure */ 9142255332Scy/* Parameters: head(I) - pointer to tuneable array entry */ 9143255332Scy/* seconds(I) - pointer to values passed in to apply */ 9144255332Scy/* */ 9145255332Scy/* This function applies a timeout of "seconds" to the timeout queue that */ 9146255332Scy/* is pointed to by "head". All entries on this list have an expiration */ 9147255332Scy/* set to be the current tick value of ipf plus the ttl. Given that this */ 9148255332Scy/* function should only be called when the delta is non-zero, the task is */ 9149255332Scy/* to walk the entire list and apply the change. The sort order will not */ 9150255332Scy/* change. The only catch is that this is O(n) across the list, so if the */ 9151255332Scy/* queue has lots of entries (10s of thousands or 100s of thousands), it */ 9152255332Scy/* could take a relatively long time to work through them all. */ 9153255332Scy/* ------------------------------------------------------------------------ */ 9154255332Scyvoid 9155255332Scyipf_apply_timeout(head, seconds) 9156255332Scy ipftq_t *head; 9157255332Scy u_int seconds; 9158255332Scy{ 9159255332Scy u_int oldtimeout, newtimeout; 9160255332Scy ipftqent_t *tqe; 9161255332Scy int delta; 9162255332Scy 9163255332Scy MUTEX_ENTER(&head->ifq_lock); 9164255332Scy oldtimeout = head->ifq_ttl; 9165255332Scy newtimeout = IPF_TTLVAL(seconds); 9166255332Scy delta = oldtimeout - newtimeout; 9167255332Scy 9168255332Scy head->ifq_ttl = newtimeout; 9169255332Scy 9170255332Scy for (tqe = head->ifq_head; tqe != NULL; tqe = tqe->tqe_next) { 9171255332Scy tqe->tqe_die += delta; 9172255332Scy } 9173255332Scy MUTEX_EXIT(&head->ifq_lock); 9174255332Scy} 9175255332Scy 9176255332Scy 9177255332Scy/* ------------------------------------------------------------------------ */ 9178255332Scy/* Function: ipf_settimeout_tcp */ 9179255332Scy/* Returns: int - 0 = successfully applied, -1 = failed */ 9180255332Scy/* Parameters: t(I) - pointer to tuneable to change */ 9181255332Scy/* p(I) - pointer to new timeout information */ 9182255332Scy/* tab(I) - pointer to table of TCP queues */ 9183255332Scy/* */ 9184255332Scy/* This function applies the new timeout (p) to the TCP tunable (t) and */ 9185255332Scy/* updates all of the entries on the relevant timeout queue by calling */ 9186255332Scy/* ipf_apply_timeout(). */ 9187255332Scy/* ------------------------------------------------------------------------ */ 9188255332Scyint 9189255332Scyipf_settimeout_tcp(t, p, tab) 9190255332Scy ipftuneable_t *t; 9191255332Scy ipftuneval_t *p; 9192255332Scy ipftq_t *tab; 9193255332Scy{ 9194255332Scy if (!strcmp(t->ipft_name, "tcp_idle_timeout") || 9195255332Scy !strcmp(t->ipft_name, "tcp_established")) { 9196255332Scy ipf_apply_timeout(&tab[IPF_TCPS_ESTABLISHED], p->ipftu_int); 9197255332Scy } else if (!strcmp(t->ipft_name, "tcp_close_wait")) { 9198255332Scy ipf_apply_timeout(&tab[IPF_TCPS_CLOSE_WAIT], p->ipftu_int); 9199255332Scy } else if (!strcmp(t->ipft_name, "tcp_last_ack")) { 9200255332Scy ipf_apply_timeout(&tab[IPF_TCPS_LAST_ACK], p->ipftu_int); 9201255332Scy } else if (!strcmp(t->ipft_name, "tcp_timeout")) { 9202255332Scy ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); 9203255332Scy ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); 9204255332Scy ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); 9205255332Scy } else if (!strcmp(t->ipft_name, "tcp_listen")) { 9206255332Scy ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); 9207255332Scy } else if (!strcmp(t->ipft_name, "tcp_half_established")) { 9208255332Scy ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); 9209255332Scy } else if (!strcmp(t->ipft_name, "tcp_closing")) { 9210255332Scy ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); 9211255332Scy } else if (!strcmp(t->ipft_name, "tcp_syn_received")) { 9212255332Scy ipf_apply_timeout(&tab[IPF_TCPS_SYN_RECEIVED], p->ipftu_int); 9213255332Scy } else if (!strcmp(t->ipft_name, "tcp_syn_sent")) { 9214255332Scy ipf_apply_timeout(&tab[IPF_TCPS_SYN_SENT], p->ipftu_int); 9215255332Scy } else if (!strcmp(t->ipft_name, "tcp_closed")) { 9216255332Scy ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); 9217255332Scy } else if (!strcmp(t->ipft_name, "tcp_half_closed")) { 9218255332Scy ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); 9219255332Scy } else if (!strcmp(t->ipft_name, "tcp_time_wait")) { 9220255332Scy ipf_apply_timeout(&tab[IPF_TCPS_TIME_WAIT], p->ipftu_int); 9221255332Scy } else { 9222255332Scy /* 9223255332Scy * ipf_interror isn't set here because it should be set 9224255332Scy * by whatever called this function. 9225255332Scy */ 9226255332Scy return -1; 9227255332Scy } 9228255332Scy return 0; 9229255332Scy} 9230255332Scy 9231255332Scy 9232255332Scy/* ------------------------------------------------------------------------ */ 9233255332Scy/* Function: ipf_main_soft_create */ 9234255332Scy/* Returns: NULL = failure, else success */ 9235255332Scy/* Parameters: arg(I) - pointer to soft context structure if already allocd */ 9236255332Scy/* */ 9237255332Scy/* Create the foundation soft context structure. In circumstances where it */ 9238255332Scy/* is not required to dynamically allocate the context, a pointer can be */ 9239255332Scy/* passed in (rather than NULL) to a structure to be initialised. */ 9240255332Scy/* The main thing of interest is that a number of locks are initialised */ 9241255332Scy/* here instead of in the where might be expected - in the relevant create */ 9242255332Scy/* function elsewhere. This is done because the current locking design has */ 9243255332Scy/* some areas where these locks are used outside of their module. */ 9244255332Scy/* Possibly the most important exercise that is done here is setting of all */ 9245255332Scy/* the timeout values, allowing them to be changed before init(). */ 9246255332Scy/* ------------------------------------------------------------------------ */ 9247255332Scyvoid * 9248255332Scyipf_main_soft_create(arg) 9249255332Scy void *arg; 9250255332Scy{ 9251255332Scy ipf_main_softc_t *softc; 9252255332Scy 9253255332Scy if (arg == NULL) { 9254255332Scy KMALLOC(softc, ipf_main_softc_t *); 9255255332Scy if (softc == NULL) 9256255332Scy return NULL; 9257255332Scy } else { 9258255332Scy softc = arg; 9259255332Scy } 9260255332Scy 9261255332Scy bzero((char *)softc, sizeof(*softc)); 9262255332Scy 9263255332Scy /* 9264255332Scy * This serves as a flag as to whether or not the softc should be 9265255332Scy * free'd when _destroy is called. 9266255332Scy */ 9267255332Scy softc->ipf_dynamic_softc = (arg == NULL) ? 1 : 0; 9268255332Scy 9269255332Scy softc->ipf_tuners = ipf_tune_array_copy(softc, 9270255332Scy sizeof(ipf_main_tuneables), 9271255332Scy ipf_main_tuneables); 9272255332Scy if (softc->ipf_tuners == NULL) { 9273255332Scy ipf_main_soft_destroy(softc); 9274255332Scy return NULL; 9275255332Scy } 9276255332Scy 9277255332Scy MUTEX_INIT(&softc->ipf_rw, "ipf rw mutex"); 9278255332Scy MUTEX_INIT(&softc->ipf_timeoutlock, "ipf timeout lock"); 9279255332Scy RWLOCK_INIT(&softc->ipf_global, "ipf filter load/unload mutex"); 9280255332Scy RWLOCK_INIT(&softc->ipf_mutex, "ipf filter rwlock"); 9281255332Scy RWLOCK_INIT(&softc->ipf_tokens, "ipf token rwlock"); 9282255332Scy RWLOCK_INIT(&softc->ipf_state, "ipf state rwlock"); 9283255332Scy RWLOCK_INIT(&softc->ipf_nat, "ipf IP NAT rwlock"); 9284255332Scy RWLOCK_INIT(&softc->ipf_poolrw, "ipf pool rwlock"); 9285255332Scy RWLOCK_INIT(&softc->ipf_frag, "ipf frag rwlock"); 9286255332Scy 9287255332Scy softc->ipf_token_head = NULL; 9288255332Scy softc->ipf_token_tail = &softc->ipf_token_head; 9289255332Scy 9290255332Scy softc->ipf_tcpidletimeout = FIVE_DAYS; 9291255332Scy softc->ipf_tcpclosewait = IPF_TTLVAL(2 * TCP_MSL); 9292255332Scy softc->ipf_tcplastack = IPF_TTLVAL(30); 9293255332Scy softc->ipf_tcptimewait = IPF_TTLVAL(2 * TCP_MSL); 9294255332Scy softc->ipf_tcptimeout = IPF_TTLVAL(2 * TCP_MSL); 9295255332Scy softc->ipf_tcpsynsent = IPF_TTLVAL(2 * TCP_MSL); 9296255332Scy softc->ipf_tcpsynrecv = IPF_TTLVAL(2 * TCP_MSL); 9297255332Scy softc->ipf_tcpclosed = IPF_TTLVAL(30); 9298255332Scy softc->ipf_tcphalfclosed = IPF_TTLVAL(2 * 3600); 9299255332Scy softc->ipf_udptimeout = IPF_TTLVAL(120); 9300255332Scy softc->ipf_udpacktimeout = IPF_TTLVAL(12); 9301255332Scy softc->ipf_icmptimeout = IPF_TTLVAL(60); 9302255332Scy softc->ipf_icmpacktimeout = IPF_TTLVAL(6); 9303255332Scy softc->ipf_iptimeout = IPF_TTLVAL(60); 9304255332Scy 9305255332Scy#if defined(IPFILTER_DEFAULT_BLOCK) 9306255332Scy softc->ipf_pass = FR_BLOCK|FR_NOMATCH; 9307255332Scy#else 9308255332Scy softc->ipf_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH; 9309255332Scy#endif 9310255332Scy softc->ipf_minttl = 4; 9311255332Scy softc->ipf_icmpminfragmtu = 68; 9312255332Scy softc->ipf_flags = IPF_LOGGING; 9313255332Scy 9314255332Scy return softc; 9315255332Scy} 9316255332Scy 9317255332Scy/* ------------------------------------------------------------------------ */ 9318255332Scy/* Function: ipf_main_soft_init */ 9319255332Scy/* Returns: 0 = success, -1 = failure */ 9320255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9321255332Scy/* */ 9322255332Scy/* A null-op function that exists as a placeholder so that the flow in */ 9323255332Scy/* other functions is obvious. */ 9324255332Scy/* ------------------------------------------------------------------------ */ 9325255332Scy/*ARGSUSED*/ 9326255332Scyint 9327255332Scyipf_main_soft_init(softc) 9328255332Scy ipf_main_softc_t *softc; 9329255332Scy{ 9330255332Scy return 0; 9331255332Scy} 9332255332Scy 9333255332Scy 9334255332Scy/* ------------------------------------------------------------------------ */ 9335255332Scy/* Function: ipf_main_soft_destroy */ 9336255332Scy/* Returns: void */ 9337255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9338255332Scy/* */ 9339255332Scy/* Undo everything that we did in ipf_main_soft_create. */ 9340255332Scy/* */ 9341255332Scy/* The most important check that needs to be made here is whether or not */ 9342255332Scy/* the structure was allocated by ipf_main_soft_create() by checking what */ 9343255332Scy/* value is stored in ipf_dynamic_main. */ 9344255332Scy/* ------------------------------------------------------------------------ */ 9345255332Scy/*ARGSUSED*/ 9346255332Scyvoid 9347255332Scyipf_main_soft_destroy(softc) 9348255332Scy ipf_main_softc_t *softc; 9349255332Scy{ 9350255332Scy 9351255332Scy RW_DESTROY(&softc->ipf_frag); 9352255332Scy RW_DESTROY(&softc->ipf_poolrw); 9353255332Scy RW_DESTROY(&softc->ipf_nat); 9354255332Scy RW_DESTROY(&softc->ipf_state); 9355255332Scy RW_DESTROY(&softc->ipf_tokens); 9356255332Scy RW_DESTROY(&softc->ipf_mutex); 9357255332Scy RW_DESTROY(&softc->ipf_global); 9358255332Scy MUTEX_DESTROY(&softc->ipf_timeoutlock); 9359255332Scy MUTEX_DESTROY(&softc->ipf_rw); 9360255332Scy 9361255332Scy if (softc->ipf_tuners != NULL) { 9362255332Scy KFREES(softc->ipf_tuners, sizeof(ipf_main_tuneables)); 9363255332Scy } 9364255332Scy if (softc->ipf_dynamic_softc == 1) { 9365255332Scy KFREE(softc); 9366255332Scy } 9367255332Scy} 9368255332Scy 9369255332Scy 9370255332Scy/* ------------------------------------------------------------------------ */ 9371255332Scy/* Function: ipf_main_soft_fini */ 9372255332Scy/* Returns: 0 = success, -1 = failure */ 9373255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9374255332Scy/* */ 9375255332Scy/* Clean out the rules which have been added since _init was last called, */ 9376255332Scy/* the only dynamic part of the mainline. */ 9377255332Scy/* ------------------------------------------------------------------------ */ 9378255332Scyint 9379255332Scyipf_main_soft_fini(softc) 9380255332Scy ipf_main_softc_t *softc; 9381255332Scy{ 9382255332Scy (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE|FR_INACTIVE); 9383255332Scy (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE); 9384255332Scy (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE|FR_INACTIVE); 9385255332Scy (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE); 9386255332Scy 9387255332Scy return 0; 9388255332Scy} 9389255332Scy 9390255332Scy 9391255332Scy/* ------------------------------------------------------------------------ */ 9392255332Scy/* Function: ipf_main_load */ 9393255332Scy/* Returns: 0 = success, -1 = failure */ 9394255332Scy/* Parameters: none */ 9395255332Scy/* */ 9396255332Scy/* Handle global initialisation that needs to be done for the base part of */ 9397255332Scy/* IPFilter. At present this just amounts to initialising some ICMP lookup */ 9398255332Scy/* arrays that get used by the state/NAT code. */ 9399255332Scy/* ------------------------------------------------------------------------ */ 9400255332Scyint 9401255332Scyipf_main_load() 9402255332Scy{ 9403255332Scy int i; 9404255332Scy 9405255332Scy /* fill icmp reply type table */ 9406255332Scy for (i = 0; i <= ICMP_MAXTYPE; i++) 9407255332Scy icmpreplytype4[i] = -1; 9408255332Scy icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; 9409255332Scy icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; 9410255332Scy icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; 9411255332Scy icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; 9412255332Scy 9413255332Scy#ifdef USE_INET6 9414255332Scy /* fill icmp reply type table */ 9415255332Scy for (i = 0; i <= ICMP6_MAXTYPE; i++) 9416255332Scy icmpreplytype6[i] = -1; 9417255332Scy icmpreplytype6[ICMP6_ECHO_REQUEST] = ICMP6_ECHO_REPLY; 9418255332Scy icmpreplytype6[ICMP6_MEMBERSHIP_QUERY] = ICMP6_MEMBERSHIP_REPORT; 9419255332Scy icmpreplytype6[ICMP6_NI_QUERY] = ICMP6_NI_REPLY; 9420255332Scy icmpreplytype6[ND_ROUTER_SOLICIT] = ND_ROUTER_ADVERT; 9421255332Scy icmpreplytype6[ND_NEIGHBOR_SOLICIT] = ND_NEIGHBOR_ADVERT; 9422255332Scy#endif 9423255332Scy 9424255332Scy return 0; 9425255332Scy} 9426255332Scy 9427255332Scy 9428255332Scy/* ------------------------------------------------------------------------ */ 9429255332Scy/* Function: ipf_main_unload */ 9430255332Scy/* Returns: 0 = success, -1 = failure */ 9431255332Scy/* Parameters: none */ 9432255332Scy/* */ 9433255332Scy/* A null-op function that exists as a placeholder so that the flow in */ 9434255332Scy/* other functions is obvious. */ 9435255332Scy/* ------------------------------------------------------------------------ */ 9436255332Scyint 9437255332Scyipf_main_unload() 9438255332Scy{ 9439255332Scy return 0; 9440255332Scy} 9441255332Scy 9442255332Scy 9443255332Scy/* ------------------------------------------------------------------------ */ 9444255332Scy/* Function: ipf_load_all */ 9445255332Scy/* Returns: 0 = success, -1 = failure */ 9446255332Scy/* Parameters: none */ 9447255332Scy/* */ 9448255332Scy/* Work through all of the subsystems inside IPFilter and call the load */ 9449255332Scy/* function for each in an order that won't lead to a crash :) */ 9450255332Scy/* ------------------------------------------------------------------------ */ 9451255332Scyint 9452255332Scyipf_load_all() 9453255332Scy{ 9454255332Scy if (ipf_main_load() == -1) 9455255332Scy return -1; 9456255332Scy 9457255332Scy if (ipf_state_main_load() == -1) 9458255332Scy return -1; 9459255332Scy 9460255332Scy if (ipf_nat_main_load() == -1) 9461255332Scy return -1; 9462255332Scy 9463255332Scy if (ipf_frag_main_load() == -1) 9464255332Scy return -1; 9465255332Scy 9466255332Scy if (ipf_auth_main_load() == -1) 9467255332Scy return -1; 9468255332Scy 9469255332Scy if (ipf_proxy_main_load() == -1) 9470255332Scy return -1; 9471255332Scy 9472255332Scy return 0; 9473255332Scy} 9474255332Scy 9475255332Scy 9476255332Scy/* ------------------------------------------------------------------------ */ 9477255332Scy/* Function: ipf_unload_all */ 9478255332Scy/* Returns: 0 = success, -1 = failure */ 9479255332Scy/* Parameters: none */ 9480255332Scy/* */ 9481255332Scy/* Work through all of the subsystems inside IPFilter and call the unload */ 9482255332Scy/* function for each in an order that won't lead to a crash :) */ 9483255332Scy/* ------------------------------------------------------------------------ */ 9484255332Scyint 9485255332Scyipf_unload_all() 9486255332Scy{ 9487255332Scy if (ipf_proxy_main_unload() == -1) 9488255332Scy return -1; 9489255332Scy 9490255332Scy if (ipf_auth_main_unload() == -1) 9491255332Scy return -1; 9492255332Scy 9493255332Scy if (ipf_frag_main_unload() == -1) 9494255332Scy return -1; 9495255332Scy 9496255332Scy if (ipf_nat_main_unload() == -1) 9497255332Scy return -1; 9498255332Scy 9499255332Scy if (ipf_state_main_unload() == -1) 9500255332Scy return -1; 9501255332Scy 9502255332Scy if (ipf_main_unload() == -1) 9503255332Scy return -1; 9504255332Scy 9505255332Scy return 0; 9506255332Scy} 9507255332Scy 9508255332Scy 9509255332Scy/* ------------------------------------------------------------------------ */ 9510255332Scy/* Function: ipf_create_all */ 9511255332Scy/* Returns: NULL = failure, else success */ 9512255332Scy/* Parameters: arg(I) - pointer to soft context main structure */ 9513255332Scy/* */ 9514255332Scy/* Work through all of the subsystems inside IPFilter and call the create */ 9515255332Scy/* function for each in an order that won't lead to a crash :) */ 9516255332Scy/* ------------------------------------------------------------------------ */ 9517255332Scyipf_main_softc_t * 9518255332Scyipf_create_all(arg) 9519255332Scy void *arg; 9520255332Scy{ 9521255332Scy ipf_main_softc_t *softc; 9522255332Scy 9523255332Scy softc = ipf_main_soft_create(arg); 9524255332Scy if (softc == NULL) 9525255332Scy return NULL; 9526255332Scy 9527255332Scy#ifdef IPFILTER_LOG 9528255332Scy softc->ipf_log_soft = ipf_log_soft_create(softc); 9529255332Scy if (softc->ipf_log_soft == NULL) { 9530255332Scy ipf_destroy_all(softc); 9531255332Scy return NULL; 9532255332Scy } 9533255332Scy#endif 9534255332Scy 9535255332Scy softc->ipf_lookup_soft = ipf_lookup_soft_create(softc); 9536255332Scy if (softc->ipf_lookup_soft == NULL) { 9537255332Scy ipf_destroy_all(softc); 9538255332Scy return NULL; 9539255332Scy } 9540255332Scy 9541255332Scy softc->ipf_sync_soft = ipf_sync_soft_create(softc); 9542255332Scy if (softc->ipf_sync_soft == NULL) { 9543255332Scy ipf_destroy_all(softc); 9544255332Scy return NULL; 9545255332Scy } 9546255332Scy 9547255332Scy softc->ipf_state_soft = ipf_state_soft_create(softc); 9548255332Scy if (softc->ipf_state_soft == NULL) { 9549255332Scy ipf_destroy_all(softc); 9550255332Scy return NULL; 9551255332Scy } 9552255332Scy 9553255332Scy softc->ipf_nat_soft = ipf_nat_soft_create(softc); 9554255332Scy if (softc->ipf_nat_soft == NULL) { 9555255332Scy ipf_destroy_all(softc); 9556255332Scy return NULL; 9557255332Scy } 9558255332Scy 9559255332Scy softc->ipf_frag_soft = ipf_frag_soft_create(softc); 9560255332Scy if (softc->ipf_frag_soft == NULL) { 9561255332Scy ipf_destroy_all(softc); 9562255332Scy return NULL; 9563255332Scy } 9564255332Scy 9565255332Scy softc->ipf_auth_soft = ipf_auth_soft_create(softc); 9566255332Scy if (softc->ipf_auth_soft == NULL) { 9567255332Scy ipf_destroy_all(softc); 9568255332Scy return NULL; 9569255332Scy } 9570255332Scy 9571255332Scy softc->ipf_proxy_soft = ipf_proxy_soft_create(softc); 9572255332Scy if (softc->ipf_proxy_soft == NULL) { 9573255332Scy ipf_destroy_all(softc); 9574255332Scy return NULL; 9575255332Scy } 9576255332Scy 9577255332Scy return softc; 9578255332Scy} 9579255332Scy 9580255332Scy 9581255332Scy/* ------------------------------------------------------------------------ */ 9582255332Scy/* Function: ipf_destroy_all */ 9583255332Scy/* Returns: void */ 9584255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9585255332Scy/* */ 9586255332Scy/* Work through all of the subsystems inside IPFilter and call the destroy */ 9587255332Scy/* function for each in an order that won't lead to a crash :) */ 9588255332Scy/* */ 9589255332Scy/* Every one of these functions is expected to succeed, so there is no */ 9590255332Scy/* checking of return values. */ 9591255332Scy/* ------------------------------------------------------------------------ */ 9592255332Scyvoid 9593255332Scyipf_destroy_all(softc) 9594255332Scy ipf_main_softc_t *softc; 9595255332Scy{ 9596255332Scy 9597255332Scy if (softc->ipf_state_soft != NULL) { 9598255332Scy ipf_state_soft_destroy(softc, softc->ipf_state_soft); 9599255332Scy softc->ipf_state_soft = NULL; 9600255332Scy } 9601255332Scy 9602255332Scy if (softc->ipf_nat_soft != NULL) { 9603255332Scy ipf_nat_soft_destroy(softc, softc->ipf_nat_soft); 9604255332Scy softc->ipf_nat_soft = NULL; 9605255332Scy } 9606255332Scy 9607255332Scy if (softc->ipf_frag_soft != NULL) { 9608255332Scy ipf_frag_soft_destroy(softc, softc->ipf_frag_soft); 9609255332Scy softc->ipf_frag_soft = NULL; 9610255332Scy } 9611255332Scy 9612255332Scy if (softc->ipf_auth_soft != NULL) { 9613255332Scy ipf_auth_soft_destroy(softc, softc->ipf_auth_soft); 9614255332Scy softc->ipf_auth_soft = NULL; 9615255332Scy } 9616255332Scy 9617255332Scy if (softc->ipf_proxy_soft != NULL) { 9618255332Scy ipf_proxy_soft_destroy(softc, softc->ipf_proxy_soft); 9619255332Scy softc->ipf_proxy_soft = NULL; 9620255332Scy } 9621255332Scy 9622255332Scy if (softc->ipf_sync_soft != NULL) { 9623255332Scy ipf_sync_soft_destroy(softc, softc->ipf_sync_soft); 9624255332Scy softc->ipf_sync_soft = NULL; 9625255332Scy } 9626255332Scy 9627255332Scy if (softc->ipf_lookup_soft != NULL) { 9628255332Scy ipf_lookup_soft_destroy(softc, softc->ipf_lookup_soft); 9629255332Scy softc->ipf_lookup_soft = NULL; 9630255332Scy } 9631255332Scy 9632255332Scy#ifdef IPFILTER_LOG 9633255332Scy if (softc->ipf_log_soft != NULL) { 9634255332Scy ipf_log_soft_destroy(softc, softc->ipf_log_soft); 9635255332Scy softc->ipf_log_soft = NULL; 9636255332Scy } 9637255332Scy#endif 9638255332Scy 9639255332Scy ipf_main_soft_destroy(softc); 9640255332Scy} 9641255332Scy 9642255332Scy 9643255332Scy/* ------------------------------------------------------------------------ */ 9644255332Scy/* Function: ipf_init_all */ 9645255332Scy/* Returns: 0 = success, -1 = failure */ 9646255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9647255332Scy/* */ 9648255332Scy/* Work through all of the subsystems inside IPFilter and call the init */ 9649255332Scy/* function for each in an order that won't lead to a crash :) */ 9650255332Scy/* ------------------------------------------------------------------------ */ 9651255332Scyint 9652255332Scyipf_init_all(softc) 9653255332Scy ipf_main_softc_t *softc; 9654255332Scy{ 9655255332Scy 9656255332Scy if (ipf_main_soft_init(softc) == -1) 9657255332Scy return -1; 9658255332Scy 9659255332Scy#ifdef IPFILTER_LOG 9660255332Scy if (ipf_log_soft_init(softc, softc->ipf_log_soft) == -1) 9661255332Scy return -1; 9662255332Scy#endif 9663255332Scy 9664255332Scy if (ipf_lookup_soft_init(softc, softc->ipf_lookup_soft) == -1) 9665255332Scy return -1; 9666255332Scy 9667255332Scy if (ipf_sync_soft_init(softc, softc->ipf_sync_soft) == -1) 9668255332Scy return -1; 9669255332Scy 9670255332Scy if (ipf_state_soft_init(softc, softc->ipf_state_soft) == -1) 9671255332Scy return -1; 9672255332Scy 9673255332Scy if (ipf_nat_soft_init(softc, softc->ipf_nat_soft) == -1) 9674255332Scy return -1; 9675255332Scy 9676255332Scy if (ipf_frag_soft_init(softc, softc->ipf_frag_soft) == -1) 9677255332Scy return -1; 9678255332Scy 9679255332Scy if (ipf_auth_soft_init(softc, softc->ipf_auth_soft) == -1) 9680255332Scy return -1; 9681255332Scy 9682255332Scy if (ipf_proxy_soft_init(softc, softc->ipf_proxy_soft) == -1) 9683255332Scy return -1; 9684255332Scy 9685255332Scy return 0; 9686255332Scy} 9687255332Scy 9688255332Scy 9689255332Scy/* ------------------------------------------------------------------------ */ 9690255332Scy/* Function: ipf_fini_all */ 9691255332Scy/* Returns: 0 = success, -1 = failure */ 9692255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9693255332Scy/* */ 9694255332Scy/* Work through all of the subsystems inside IPFilter and call the fini */ 9695255332Scy/* function for each in an order that won't lead to a crash :) */ 9696255332Scy/* ------------------------------------------------------------------------ */ 9697255332Scyint 9698255332Scyipf_fini_all(softc) 9699255332Scy ipf_main_softc_t *softc; 9700255332Scy{ 9701255332Scy 9702255332Scy ipf_token_flush(softc); 9703255332Scy 9704255332Scy if (ipf_proxy_soft_fini(softc, softc->ipf_proxy_soft) == -1) 9705255332Scy return -1; 9706255332Scy 9707255332Scy if (ipf_auth_soft_fini(softc, softc->ipf_auth_soft) == -1) 9708255332Scy return -1; 9709255332Scy 9710255332Scy if (ipf_frag_soft_fini(softc, softc->ipf_frag_soft) == -1) 9711255332Scy return -1; 9712255332Scy 9713255332Scy if (ipf_nat_soft_fini(softc, softc->ipf_nat_soft) == -1) 9714255332Scy return -1; 9715255332Scy 9716255332Scy if (ipf_state_soft_fini(softc, softc->ipf_state_soft) == -1) 9717255332Scy return -1; 9718255332Scy 9719255332Scy if (ipf_sync_soft_fini(softc, softc->ipf_sync_soft) == -1) 9720255332Scy return -1; 9721255332Scy 9722255332Scy if (ipf_lookup_soft_fini(softc, softc->ipf_lookup_soft) == -1) 9723255332Scy return -1; 9724255332Scy 9725255332Scy#ifdef IPFILTER_LOG 9726255332Scy if (ipf_log_soft_fini(softc, softc->ipf_log_soft) == -1) 9727255332Scy return -1; 9728255332Scy#endif 9729255332Scy 9730255332Scy if (ipf_main_soft_fini(softc) == -1) 9731255332Scy return -1; 9732255332Scy 9733255332Scy return 0; 9734255332Scy} 9735255332Scy 9736255332Scy 9737255332Scy/* ------------------------------------------------------------------------ */ 9738255332Scy/* Function: ipf_rule_expire */ 9739255332Scy/* Returns: Nil */ 9740255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9741255332Scy/* */ 9742255332Scy/* At present this function exists just to support temporary addition of */ 9743255332Scy/* firewall rules. Both inactive and active lists are scanned for items to */ 9744255332Scy/* purge, as by rights, the expiration is computed as soon as the rule is */ 9745255332Scy/* loaded in. */ 9746255332Scy/* ------------------------------------------------------------------------ */ 9747255332Scyvoid 9748255332Scyipf_rule_expire(softc) 9749255332Scy ipf_main_softc_t *softc; 9750255332Scy{ 9751255332Scy frentry_t *fr; 9752255332Scy 9753255332Scy if ((softc->ipf_rule_explist[0] == NULL) && 9754255332Scy (softc->ipf_rule_explist[1] == NULL)) 9755255332Scy return; 9756255332Scy 9757255332Scy WRITE_ENTER(&softc->ipf_mutex); 9758255332Scy 9759255332Scy while ((fr = softc->ipf_rule_explist[0]) != NULL) { 9760255332Scy /* 9761255332Scy * Because the list is kept sorted on insertion, the fist 9762255332Scy * one that dies in the future means no more work to do. 9763255332Scy */ 9764255332Scy if (fr->fr_die > softc->ipf_ticks) 9765255332Scy break; 9766255332Scy ipf_rule_delete(softc, fr, IPL_LOGIPF, 0); 9767255332Scy } 9768255332Scy 9769255332Scy while ((fr = softc->ipf_rule_explist[1]) != NULL) { 9770255332Scy /* 9771255332Scy * Because the list is kept sorted on insertion, the fist 9772255332Scy * one that dies in the future means no more work to do. 9773255332Scy */ 9774255332Scy if (fr->fr_die > softc->ipf_ticks) 9775255332Scy break; 9776255332Scy ipf_rule_delete(softc, fr, IPL_LOGIPF, 1); 9777255332Scy } 9778255332Scy 9779255332Scy RWLOCK_EXIT(&softc->ipf_mutex); 9780255332Scy} 9781255332Scy 9782255332Scy 9783255332Scystatic int ipf_ht_node_cmp __P((struct host_node_s *, struct host_node_s *)); 9784255332Scystatic void ipf_ht_node_make_key __P((host_track_t *, host_node_t *, int, 9785255332Scy i6addr_t *)); 9786255332Scy 9787255332Scyhost_node_t RBI_ZERO(ipf_rb); 9788255332ScyRBI_CODE(ipf_rb, host_node_t, hn_entry, ipf_ht_node_cmp) 9789255332Scy 9790255332Scy 9791255332Scy/* ------------------------------------------------------------------------ */ 9792255332Scy/* Function: ipf_ht_node_cmp */ 9793255332Scy/* Returns: int - 0 == nodes are the same, .. */ 9794255332Scy/* Parameters: k1(I) - pointer to first key to compare */ 9795255332Scy/* k2(I) - pointer to second key to compare */ 9796255332Scy/* */ 9797255332Scy/* The "key" for the node is a combination of two fields: the address */ 9798255332Scy/* family and the address itself. */ 9799255332Scy/* */ 9800255332Scy/* Because we're not actually interpreting the address data, it isn't */ 9801255332Scy/* necessary to convert them to/from network/host byte order. The mask is */ 9802255332Scy/* just used to remove bits that aren't significant - it doesn't matter */ 9803255332Scy/* where they are, as long as they're always in the same place. */ 9804255332Scy/* */ 9805255332Scy/* As with IP6_EQ, comparing IPv6 addresses starts at the bottom because */ 9806255332Scy/* this is where individual ones will differ the most - but not true for */ 9807255332Scy/* for /48's, etc. */ 9808255332Scy/* ------------------------------------------------------------------------ */ 9809255332Scystatic int 9810255332Scyipf_ht_node_cmp(k1, k2) 9811255332Scy struct host_node_s *k1, *k2; 9812255332Scy{ 9813255332Scy int i; 9814255332Scy 9815255332Scy i = (k2->hn_addr.adf_family - k1->hn_addr.adf_family); 9816255332Scy if (i != 0) 9817255332Scy return i; 9818255332Scy 9819255332Scy if (k1->hn_addr.adf_family == AF_INET) 9820255332Scy return (k2->hn_addr.adf_addr.in4.s_addr - 9821255332Scy k1->hn_addr.adf_addr.in4.s_addr); 9822255332Scy 9823255332Scy i = k2->hn_addr.adf_addr.i6[3] - k1->hn_addr.adf_addr.i6[3]; 9824255332Scy if (i != 0) 9825255332Scy return i; 9826255332Scy i = k2->hn_addr.adf_addr.i6[2] - k1->hn_addr.adf_addr.i6[2]; 9827255332Scy if (i != 0) 9828255332Scy return i; 9829255332Scy i = k2->hn_addr.adf_addr.i6[1] - k1->hn_addr.adf_addr.i6[1]; 9830255332Scy if (i != 0) 9831255332Scy return i; 9832255332Scy i = k2->hn_addr.adf_addr.i6[0] - k1->hn_addr.adf_addr.i6[0]; 9833255332Scy return i; 9834255332Scy} 9835255332Scy 9836255332Scy 9837255332Scy/* ------------------------------------------------------------------------ */ 9838255332Scy/* Function: ipf_ht_node_make_key */ 9839255332Scy/* Returns: Nil */ 9840255332Scy/* parameters: htp(I) - pointer to address tracking structure */ 9841255332Scy/* key(I) - where to store masked address for lookup */ 9842255332Scy/* family(I) - protocol family of address */ 9843255332Scy/* addr(I) - pointer to network address */ 9844255332Scy/* */ 9845255332Scy/* Using the "netmask" (number of bits) stored parent host tracking struct, */ 9846255332Scy/* copy the address passed in into the key structure whilst masking out the */ 9847255332Scy/* bits that we don't want. */ 9848255332Scy/* */ 9849255332Scy/* Because the parser will set ht_netmask to 128 if there is no protocol */ 9850255332Scy/* specified (the parser doesn't know if it should be a v4 or v6 rule), we */ 9851255332Scy/* have to be wary of that and not allow 32-128 to happen. */ 9852255332Scy/* ------------------------------------------------------------------------ */ 9853255332Scystatic void 9854255332Scyipf_ht_node_make_key(htp, key, family, addr) 9855255332Scy host_track_t *htp; 9856255332Scy host_node_t *key; 9857255332Scy int family; 9858255332Scy i6addr_t *addr; 9859255332Scy{ 9860255332Scy key->hn_addr.adf_family = family; 9861255332Scy if (family == AF_INET) { 9862255332Scy u_32_t mask; 9863255332Scy int bits; 9864255332Scy 9865255332Scy key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in4); 9866255332Scy bits = htp->ht_netmask; 9867255332Scy if (bits >= 32) { 9868255332Scy mask = 0xffffffff; 9869255332Scy } else { 9870255332Scy mask = htonl(0xffffffff << (32 - bits)); 9871255332Scy } 9872255332Scy key->hn_addr.adf_addr.in4.s_addr = addr->in4.s_addr & mask; 9873255332Scy#ifdef USE_INET6 9874255332Scy } else { 9875255332Scy int bits = htp->ht_netmask; 9876255332Scy 9877255332Scy key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in6); 9878255332Scy if (bits > 96) { 9879255332Scy key->hn_addr.adf_addr.i6[3] = addr->i6[3] & 9880255332Scy htonl(0xffffffff << (128 - bits)); 9881255332Scy key->hn_addr.adf_addr.i6[2] = addr->i6[2]; 9882255332Scy key->hn_addr.adf_addr.i6[1] = addr->i6[2]; 9883255332Scy key->hn_addr.adf_addr.i6[0] = addr->i6[2]; 9884255332Scy } else if (bits > 64) { 9885255332Scy key->hn_addr.adf_addr.i6[3] = 0; 9886255332Scy key->hn_addr.adf_addr.i6[2] = addr->i6[2] & 9887255332Scy htonl(0xffffffff << (96 - bits)); 9888255332Scy key->hn_addr.adf_addr.i6[1] = addr->i6[1]; 9889255332Scy key->hn_addr.adf_addr.i6[0] = addr->i6[0]; 9890255332Scy } else if (bits > 32) { 9891255332Scy key->hn_addr.adf_addr.i6[3] = 0; 9892255332Scy key->hn_addr.adf_addr.i6[2] = 0; 9893255332Scy key->hn_addr.adf_addr.i6[1] = addr->i6[1] & 9894255332Scy htonl(0xffffffff << (64 - bits)); 9895255332Scy key->hn_addr.adf_addr.i6[0] = addr->i6[0]; 9896255332Scy } else { 9897255332Scy key->hn_addr.adf_addr.i6[3] = 0; 9898255332Scy key->hn_addr.adf_addr.i6[2] = 0; 9899255332Scy key->hn_addr.adf_addr.i6[1] = 0; 9900255332Scy key->hn_addr.adf_addr.i6[0] = addr->i6[0] & 9901255332Scy htonl(0xffffffff << (32 - bits)); 9902255332Scy } 9903255355Sglebius#endif 9904255332Scy } 9905255332Scy} 9906255332Scy 9907255332Scy 9908255332Scy/* ------------------------------------------------------------------------ */ 9909255332Scy/* Function: ipf_ht_node_add */ 9910255332Scy/* Returns: int - 0 == success, -1 == failure */ 9911255332Scy/* Parameters: softc(I) - pointer to soft context main structure */ 9912255332Scy/* htp(I) - pointer to address tracking structure */ 9913255332Scy/* family(I) - protocol family of address */ 9914255332Scy/* addr(I) - pointer to network address */ 9915255332Scy/* */ 9916255332Scy/* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ 9917255332Scy/* ipf_ht_node_del FROM RUNNING CONCURRENTLY ON THE SAME htp. */ 9918255332Scy/* */ 9919255332Scy/* After preparing the key with the address information to find, look in */ 9920255332Scy/* the red-black tree to see if the address is known. A successful call to */ 9921255332Scy/* this function can mean one of two things: a new node was added to the */ 9922255332Scy/* tree or a matching node exists and we're able to bump up its activity. */ 9923255332Scy/* ------------------------------------------------------------------------ */ 9924255332Scyint 9925255332Scyipf_ht_node_add(softc, htp, family, addr) 9926255332Scy ipf_main_softc_t *softc; 9927255332Scy host_track_t *htp; 9928255332Scy int family; 9929255332Scy i6addr_t *addr; 9930255332Scy{ 9931255332Scy host_node_t *h; 9932255332Scy host_node_t k; 9933255332Scy 9934255332Scy ipf_ht_node_make_key(htp, &k, family, addr); 9935255332Scy 9936255332Scy h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); 9937255332Scy if (h == NULL) { 9938255332Scy if (htp->ht_cur_nodes >= htp->ht_max_nodes) 9939255332Scy return -1; 9940255332Scy KMALLOC(h, host_node_t *); 9941255332Scy if (h == NULL) { 9942255332Scy DT(ipf_rb_no_mem); 9943255332Scy LBUMP(ipf_rb_no_mem); 9944255332Scy return -1; 9945255332Scy } 9946255332Scy 9947255332Scy /* 9948255332Scy * If there was a macro to initialise the RB node then that 9949255332Scy * would get used here, but there isn't... 9950255332Scy */ 9951255332Scy bzero((char *)h, sizeof(*h)); 9952255332Scy h->hn_addr = k.hn_addr; 9953255332Scy h->hn_addr.adf_family = k.hn_addr.adf_family; 9954255332Scy RBI_INSERT(ipf_rb, &htp->ht_root, h); 9955255332Scy htp->ht_cur_nodes++; 9956255332Scy } else { 9957255332Scy if ((htp->ht_max_per_node != 0) && 9958255332Scy (h->hn_active >= htp->ht_max_per_node)) { 9959255332Scy DT(ipf_rb_node_max); 9960255332Scy LBUMP(ipf_rb_node_max); 9961255332Scy return -1; 9962255332Scy } 9963255332Scy } 9964255332Scy 9965255332Scy h->hn_active++; 9966255332Scy 9967255332Scy return 0; 9968255332Scy} 9969255332Scy 9970255332Scy 9971255332Scy/* ------------------------------------------------------------------------ */ 9972255332Scy/* Function: ipf_ht_node_del */ 9973255332Scy/* Returns: int - 0 == success, -1 == failure */ 9974255332Scy/* parameters: htp(I) - pointer to address tracking structure */ 9975255332Scy/* family(I) - protocol family of address */ 9976255332Scy/* addr(I) - pointer to network address */ 9977255332Scy/* */ 9978255332Scy/* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ 9979255332Scy/* ipf_ht_node_add FROM RUNNING CONCURRENTLY ON THE SAME htp. */ 9980255332Scy/* */ 9981255332Scy/* Try and find the address passed in amongst the leavese on this tree to */ 9982255332Scy/* be friend. If found then drop the active account for that node drops by */ 9983255332Scy/* one. If that count reaches 0, it is time to free it all up. */ 9984255332Scy/* ------------------------------------------------------------------------ */ 9985255332Scyint 9986255332Scyipf_ht_node_del(htp, family, addr) 9987255332Scy host_track_t *htp; 9988255332Scy int family; 9989255332Scy i6addr_t *addr; 9990255332Scy{ 9991255332Scy host_node_t *h; 9992255332Scy host_node_t k; 9993255332Scy 9994255332Scy ipf_ht_node_make_key(htp, &k, family, addr); 9995255332Scy 9996255332Scy h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); 9997255332Scy if (h == NULL) { 9998255332Scy return -1; 9999255332Scy } else { 10000255332Scy h->hn_active--; 10001255332Scy if (h->hn_active == 0) { 10002255332Scy (void) RBI_DELETE(ipf_rb, &htp->ht_root, h); 10003255332Scy htp->ht_cur_nodes--; 10004255332Scy KFREE(h); 10005255332Scy } 10006255332Scy } 10007255332Scy 10008255332Scy return 0; 10009255332Scy} 10010255332Scy 10011255332Scy 10012255332Scy/* ------------------------------------------------------------------------ */ 10013255332Scy/* Function: ipf_rb_ht_init */ 10014255332Scy/* Returns: Nil */ 10015255332Scy/* Parameters: head(I) - pointer to host tracking structure */ 10016255332Scy/* */ 10017255332Scy/* Initialise the host tracking structure to be ready for use above. */ 10018255332Scy/* ------------------------------------------------------------------------ */ 10019255332Scyvoid 10020255332Scyipf_rb_ht_init(head) 10021255332Scy host_track_t *head; 10022255332Scy{ 10023255332Scy RBI_INIT(ipf_rb, &head->ht_root); 10024255332Scy} 10025255332Scy 10026255332Scy 10027255332Scy/* ------------------------------------------------------------------------ */ 10028255332Scy/* Function: ipf_rb_ht_freenode */ 10029255332Scy/* Returns: Nil */ 10030255332Scy/* Parameters: head(I) - pointer to host tracking structure */ 10031255332Scy/* arg(I) - additional argument from walk caller */ 10032255332Scy/* */ 10033255332Scy/* Free an actual host_node_t structure. */ 10034255332Scy/* ------------------------------------------------------------------------ */ 10035255332Scyvoid 10036255332Scyipf_rb_ht_freenode(node, arg) 10037255332Scy host_node_t *node; 10038255332Scy void *arg; 10039255332Scy{ 10040255332Scy KFREE(node); 10041255332Scy} 10042255332Scy 10043255332Scy 10044255332Scy/* ------------------------------------------------------------------------ */ 10045255332Scy/* Function: ipf_rb_ht_flush */ 10046255332Scy/* Returns: Nil */ 10047255332Scy/* Parameters: head(I) - pointer to host tracking structure */ 10048255332Scy/* */ 10049255332Scy/* Remove all of the nodes in the tree tracking hosts by calling a walker */ 10050255332Scy/* and free'ing each one. */ 10051255332Scy/* ------------------------------------------------------------------------ */ 10052255332Scyvoid 10053255332Scyipf_rb_ht_flush(head) 10054255332Scy host_track_t *head; 10055255332Scy{ 10056255332Scy RBI_WALK(ipf_rb, &head->ht_root, ipf_rb_ht_freenode, NULL); 10057255332Scy} 10058255332Scy 10059255332Scy 10060255332Scy/* ------------------------------------------------------------------------ */ 10061255332Scy/* Function: ipf_slowtimer */ 10062255332Scy/* Returns: Nil */ 10063255332Scy/* Parameters: ptr(I) - pointer to main ipf soft context structure */ 10064255332Scy/* */ 10065255332Scy/* Slowly expire held state for fragments. Timeouts are set * in */ 10066255332Scy/* expectation of this being called twice per second. */ 10067255332Scy/* ------------------------------------------------------------------------ */ 10068255332Scyvoid 10069255332Scyipf_slowtimer(softc) 10070255332Scy ipf_main_softc_t *softc; 10071255332Scy{ 10072255332Scy 10073255332Scy ipf_token_expire(softc); 10074255332Scy ipf_frag_expire(softc); 10075255332Scy ipf_state_expire(softc); 10076255332Scy ipf_nat_expire(softc); 10077255332Scy ipf_auth_expire(softc); 10078255332Scy ipf_lookup_expire(softc); 10079255332Scy ipf_rule_expire(softc); 10080255332Scy ipf_sync_expire(softc); 10081255332Scy softc->ipf_ticks++; 10082255332Scy# if defined(__OpenBSD__) 10083255332Scy timeout_add(&ipf_slowtimer_ch, hz/2); 10084255332Scy# endif 10085255332Scy} 10086255332Scy 10087255332Scy 10088255332Scy/* ------------------------------------------------------------------------ */ 10089255332Scy/* Function: ipf_inet_mask_add */ 10090255332Scy/* Returns: Nil */ 10091255332Scy/* Parameters: bits(I) - pointer to nat context information */ 10092255332Scy/* mtab(I) - pointer to mask hash table structure */ 10093255332Scy/* */ 10094255332Scy/* When called, bits represents the mask of a new NAT rule that has just */ 10095255332Scy/* been added. This function inserts a bitmask into the array of masks to */ 10096255332Scy/* search when searching for a matching NAT rule for a packet. */ 10097255332Scy/* Prevention of duplicate masks is achieved by checking the use count for */ 10098255332Scy/* a given netmask. */ 10099255332Scy/* ------------------------------------------------------------------------ */ 10100255332Scyvoid 10101255332Scyipf_inet_mask_add(bits, mtab) 10102255332Scy int bits; 10103255332Scy ipf_v4_masktab_t *mtab; 10104255332Scy{ 10105255332Scy u_32_t mask; 10106255332Scy int i, j; 10107255332Scy 10108255332Scy mtab->imt4_masks[bits]++; 10109255332Scy if (mtab->imt4_masks[bits] > 1) 10110255332Scy return; 10111255332Scy 10112255332Scy if (bits == 0) 10113255332Scy mask = 0; 10114255332Scy else 10115255332Scy mask = 0xffffffff << (32 - bits); 10116255332Scy 10117255332Scy for (i = 0; i < 33; i++) { 10118255332Scy if (ntohl(mtab->imt4_active[i]) < mask) { 10119255332Scy for (j = 32; j > i; j--) 10120255332Scy mtab->imt4_active[j] = mtab->imt4_active[j - 1]; 10121255332Scy mtab->imt4_active[i] = htonl(mask); 10122255332Scy break; 10123255332Scy } 10124255332Scy } 10125255332Scy mtab->imt4_max++; 10126255332Scy} 10127255332Scy 10128255332Scy 10129255332Scy/* ------------------------------------------------------------------------ */ 10130255332Scy/* Function: ipf_inet_mask_del */ 10131255332Scy/* Returns: Nil */ 10132255332Scy/* Parameters: bits(I) - number of bits set in the netmask */ 10133255332Scy/* mtab(I) - pointer to mask hash table structure */ 10134255332Scy/* */ 10135255332Scy/* Remove the 32bit bitmask represented by "bits" from the collection of */ 10136255332Scy/* netmasks stored inside of mtab. */ 10137255332Scy/* ------------------------------------------------------------------------ */ 10138255332Scyvoid 10139255332Scyipf_inet_mask_del(bits, mtab) 10140255332Scy int bits; 10141255332Scy ipf_v4_masktab_t *mtab; 10142255332Scy{ 10143255332Scy u_32_t mask; 10144255332Scy int i, j; 10145255332Scy 10146255332Scy mtab->imt4_masks[bits]--; 10147255332Scy if (mtab->imt4_masks[bits] > 0) 10148255332Scy return; 10149255332Scy 10150255332Scy mask = htonl(0xffffffff << (32 - bits)); 10151255332Scy for (i = 0; i < 33; i++) { 10152255332Scy if (mtab->imt4_active[i] == mask) { 10153255332Scy for (j = i + 1; j < 33; j++) 10154255332Scy mtab->imt4_active[j - 1] = mtab->imt4_active[j]; 10155255332Scy break; 10156255332Scy } 10157255332Scy } 10158255332Scy mtab->imt4_max--; 10159255332Scy ASSERT(mtab->imt4_max >= 0); 10160255332Scy} 10161255332Scy 10162255332Scy 10163255332Scy#ifdef USE_INET6 10164255332Scy/* ------------------------------------------------------------------------ */ 10165255332Scy/* Function: ipf_inet6_mask_add */ 10166255332Scy/* Returns: Nil */ 10167255332Scy/* Parameters: bits(I) - number of bits set in mask */ 10168255332Scy/* mask(I) - pointer to mask to add */ 10169255332Scy/* mtab(I) - pointer to mask hash table structure */ 10170255332Scy/* */ 10171255332Scy/* When called, bitcount represents the mask of a IPv6 NAT map rule that */ 10172255332Scy/* has just been added. This function inserts a bitmask into the array of */ 10173255332Scy/* masks to search when searching for a matching NAT rule for a packet. */ 10174255332Scy/* Prevention of duplicate masks is achieved by checking the use count for */ 10175255332Scy/* a given netmask. */ 10176255332Scy/* ------------------------------------------------------------------------ */ 10177255332Scyvoid 10178255332Scyipf_inet6_mask_add(bits, mask, mtab) 10179255332Scy int bits; 10180255332Scy i6addr_t *mask; 10181255332Scy ipf_v6_masktab_t *mtab; 10182255332Scy{ 10183255332Scy i6addr_t zero; 10184255332Scy int i, j; 10185255332Scy 10186255332Scy mtab->imt6_masks[bits]++; 10187255332Scy if (mtab->imt6_masks[bits] > 1) 10188255332Scy return; 10189255332Scy 10190255332Scy if (bits == 0) { 10191255332Scy mask = &zero; 10192255332Scy zero.i6[0] = 0; 10193255332Scy zero.i6[1] = 0; 10194255332Scy zero.i6[2] = 0; 10195255332Scy zero.i6[3] = 0; 10196255332Scy } 10197255332Scy 10198255332Scy for (i = 0; i < 129; i++) { 10199255332Scy if (IP6_LT(&mtab->imt6_active[i], mask)) { 10200255332Scy for (j = 128; j > i; j--) 10201255332Scy mtab->imt6_active[j] = mtab->imt6_active[j - 1]; 10202255332Scy mtab->imt6_active[i] = *mask; 10203255332Scy break; 10204255332Scy } 10205255332Scy } 10206255332Scy mtab->imt6_max++; 10207255332Scy} 10208255332Scy 10209255332Scy 10210255332Scy/* ------------------------------------------------------------------------ */ 10211255332Scy/* Function: ipf_inet6_mask_del */ 10212255332Scy/* Returns: Nil */ 10213255332Scy/* Parameters: bits(I) - number of bits set in mask */ 10214255332Scy/* mask(I) - pointer to mask to remove */ 10215255332Scy/* mtab(I) - pointer to mask hash table structure */ 10216255332Scy/* */ 10217255332Scy/* Remove the 128bit bitmask represented by "bits" from the collection of */ 10218255332Scy/* netmasks stored inside of mtab. */ 10219255332Scy/* ------------------------------------------------------------------------ */ 10220255332Scyvoid 10221255332Scyipf_inet6_mask_del(bits, mask, mtab) 10222255332Scy int bits; 10223255332Scy i6addr_t *mask; 10224255332Scy ipf_v6_masktab_t *mtab; 10225255332Scy{ 10226255332Scy i6addr_t zero; 10227255332Scy int i, j; 10228255332Scy 10229255332Scy mtab->imt6_masks[bits]--; 10230255332Scy if (mtab->imt6_masks[bits] > 0) 10231255332Scy return; 10232255332Scy 10233255332Scy if (bits == 0) 10234255332Scy mask = &zero; 10235255332Scy zero.i6[0] = 0; 10236255332Scy zero.i6[1] = 0; 10237255332Scy zero.i6[2] = 0; 10238255332Scy zero.i6[3] = 0; 10239255332Scy 10240255332Scy for (i = 0; i < 129; i++) { 10241255332Scy if (IP6_EQ(&mtab->imt6_active[i], mask)) { 10242255332Scy for (j = i + 1; j < 129; j++) { 10243255332Scy mtab->imt6_active[j - 1] = mtab->imt6_active[j]; 10244255332Scy if (IP6_EQ(&mtab->imt6_active[j - 1], &zero)) 10245255332Scy break; 10246255332Scy } 10247255332Scy break; 10248255332Scy } 10249255332Scy } 10250255332Scy mtab->imt6_max--; 10251255332Scy ASSERT(mtab->imt6_max >= 0); 10252255332Scy} 10253349931Scy 10254349931Scy#ifdef _KERNEL 10255349931Scystatic u_int 10256349931Scyipf_pcksum6(fin, ip6, off, len) 10257349931Scy fr_info_t *fin; 10258349931Scy ip6_t *ip6; 10259349931Scy u_int32_t off; 10260349931Scy u_int32_t len; 10261349931Scy{ 10262349931Scy struct mbuf *m; 10263349931Scy int sum; 10264349931Scy 10265349931Scy m = fin->fin_m; 10266349931Scy if (m->m_len < sizeof(struct ip6_hdr)) { 10267349931Scy return 0xffff; 10268349931Scy } 10269349931Scy 10270349931Scy sum = in6_cksum(m, ip6->ip6_nxt, off, len); 10271349931Scy return(sum); 10272349931Scy} 10273349931Scy#else 10274349931Scystatic u_int 10275349931Scyipf_pcksum6(fin, ip6, off, len) 10276349931Scy fr_info_t *fin; 10277349931Scy ip6_t *ip6; 10278349931Scy u_int32_t off; 10279349931Scy u_int32_t len; 10280349931Scy{ 10281349931Scy u_short *sp; 10282349931Scy u_int sum; 10283349931Scy 10284349931Scy sp = (u_short *)&ip6->ip6_src; 10285349931Scy sum = *sp++; /* ip6_src */ 10286349931Scy sum += *sp++; 10287349931Scy sum += *sp++; 10288349931Scy sum += *sp++; 10289349931Scy sum += *sp++; 10290349931Scy sum += *sp++; 10291349931Scy sum += *sp++; 10292349931Scy sum += *sp++; 10293349931Scy sum += *sp++; /* ip6_dst */ 10294349931Scy sum += *sp++; 10295349931Scy sum += *sp++; 10296349931Scy sum += *sp++; 10297349931Scy sum += *sp++; 10298349931Scy sum += *sp++; 10299349931Scy sum += *sp++; 10300349931Scy sum += *sp++; 10301349931Scy return(ipf_pcksum(fin, off, sum)); 10302349931Scy} 10303255332Scy#endif 10304349931Scy#endif 10305