| 1 | /* $NetBSD: fil.c,v 1.19 2016/08/05 09:06:52 christos Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (C) 2012 by Darren Reed. |
| 5 | * |
| 6 | * See the IPFILTER.LICENCE file for details on licencing. |
| 7 | * |
| 8 | * Id: fil.c,v 1.1.1.2 2012/07/22 13:45:07 darrenr Exp $ |
| 9 | * |
| 10 | */ |
| 11 | #if defined(KERNEL) || defined(_KERNEL) |
| 12 | # undef KERNEL |
| 13 | # undef _KERNEL |
| 14 | # define KERNEL 1 |
| 15 | # define _KERNEL 1 |
| 16 | #endif |
| 17 | #include <sys/errno.h> |
| 18 | #include <sys/types.h> |
| 19 | #include <sys/param.h> |
| 20 | #include <sys/time.h> |
| 21 | #if defined(_KERNEL) && defined(__FreeBSD_version) && \ |
| 22 | (__FreeBSD_version >= 220000) |
| 23 | # if (__FreeBSD_version >= 400000) |
| 24 | # if !defined(IPFILTER_LKM) |
| 25 | # include "opt_inet6.h" |
| 26 | # endif |
| 27 | # if (__FreeBSD_version == 400019) |
| 28 | # define CSUM_DELAY_DATA |
| 29 | # endif |
| 30 | # endif |
| 31 | # include <sys/filio.h> |
| 32 | #else |
| 33 | # include <sys/ioctl.h> |
| 34 | #endif |
| 35 | #if (defined(__SVR4) || defined(__svr4__)) && defined(sun) |
| 36 | # include <sys/filio.h> |
| 37 | #endif |
| 38 | #if !defined(_AIX51) |
| 39 | # include <sys/fcntl.h> |
| 40 | #endif |
| 41 | #if defined(_KERNEL) |
| 42 | # include <sys/systm.h> |
| 43 | # include <sys/file.h> |
| 44 | #else |
| 45 | # include <stdio.h> |
| 46 | # include <string.h> |
| 47 | # include <stdlib.h> |
| 48 | # include <stddef.h> |
| 49 | # include <sys/file.h> |
| 50 | # define _KERNEL |
| 51 | # ifdef __OpenBSD__ |
| 52 | struct file; |
| 53 | # endif |
| 54 | # include <sys/uio.h> |
| 55 | # undef _KERNEL |
| 56 | #endif |
| 57 | #if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux) && \ |
| 58 | !defined(linux) |
| 59 | # include <sys/mbuf.h> |
| 60 | #else |
| 61 | # if !defined(linux) |
| 62 | # include <sys/byteorder.h> |
| 63 | # endif |
| 64 | # if (SOLARIS2 < 5) && defined(sun) |
| 65 | # include <sys/dditypes.h> |
| 66 | # endif |
| 67 | #endif |
| 68 | #ifdef __hpux |
| 69 | # define _NET_ROUTE_INCLUDED |
| 70 | #endif |
| 71 | #if !defined(linux) |
| 72 | # include <sys/protosw.h> |
| 73 | #endif |
| 74 | #include <sys/socket.h> |
| 75 | #include <net/if.h> |
| 76 | #ifdef sun |
| 77 | # include <net/af.h> |
| 78 | #endif |
| 79 | #include <netinet/in.h> |
| 80 | #include <netinet/in_systm.h> |
| 81 | #include <netinet/ip.h> |
| 82 | #if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ |
| 83 | # include <sys/hashing.h> |
| 84 | # include <netinet/in_var.h> |
| 85 | #endif |
| 86 | #include <netinet/tcp.h> |
| 87 | #if (!defined(__sgi) && !defined(AIX)) || defined(_KERNEL) |
| 88 | # include <netinet/udp.h> |
| 89 | # include <netinet/ip_icmp.h> |
| 90 | #endif |
| 91 | #ifdef __hpux |
| 92 | # undef _NET_ROUTE_INCLUDED |
| 93 | #endif |
| 94 | #ifdef __osf__ |
| 95 | # undef _RADIX_H_ |
| 96 | #endif |
| 97 | #include "netinet/ip_compat.h" |
| 98 | #ifdef USE_INET6 |
| 99 | # include <netinet/icmp6.h> |
| 100 | # if !SOLARIS && defined(_KERNEL) && !defined(__osf__) && !defined(__hpux) |
| 101 | # include <netinet6/in6_var.h> |
| 102 | # endif |
| 103 | #endif |
| 104 | #include "netinet/ip_fil.h" |
| 105 | #include "netinet/ip_nat.h" |
| 106 | #include "netinet/ip_frag.h" |
| 107 | #include "netinet/ip_state.h" |
| 108 | #include "netinet/ip_proxy.h" |
| 109 | #include "netinet/ip_auth.h" |
| 110 | #ifdef IPFILTER_SCAN |
| 111 | # include "netinet/ip_scan.h" |
| 112 | #endif |
| 113 | #include "netinet/ip_sync.h" |
| 114 | #include "netinet/ip_lookup.h" |
| 115 | #include "netinet/ip_pool.h" |
| 116 | #include "netinet/ip_htable.h" |
| 117 | #ifdef IPFILTER_COMPILED |
| 118 | # include "netinet/ip_rules.h" |
| 119 | #endif |
| 120 | #if defined(IPFILTER_BPF) && defined(_KERNEL) |
| 121 | # include <net/bpf.h> |
| 122 | #endif |
| 123 | #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) |
| 124 | # include <sys/malloc.h> |
| 125 | #endif |
| 126 | #include "netinet/ipl.h" |
| 127 | |
| 128 | #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) |
| 129 | # include <sys/callout.h> |
| 130 | extern struct callout ipf_slowtimer_ch; |
| 131 | #endif |
| 132 | #if defined(__OpenBSD__) |
| 133 | # include <sys/timeout.h> |
| 134 | extern struct timeout ipf_slowtimer_ch; |
| 135 | #endif |
| 136 | /* END OF INCLUDES */ |
| 137 | |
| 138 | #if !defined(lint) |
| 139 | #if defined(__NetBSD__) |
| 140 | #include <sys/cdefs.h> |
| 141 | __KERNEL_RCSID(0, "$NetBSD: fil.c,v 1.19 2016/08/05 09:06:52 christos Exp $" ); |
| 142 | #else |
| 143 | static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed" ; |
| 144 | static const char rcsid[] = "@(#)Id: fil.c,v 1.1.1.2 2012/07/22 13:45:07 darrenr Exp $" ; |
| 145 | #endif |
| 146 | #endif |
| 147 | |
| 148 | #ifndef _KERNEL |
| 149 | # include "ipf.h" |
| 150 | # include "ipt.h" |
| 151 | extern int opts; |
| 152 | extern int blockreason; |
| 153 | #endif /* _KERNEL */ |
| 154 | |
| 155 | #define LBUMP(x) softc->x++ |
| 156 | #define LBUMPD(x, y) do { softc->x.y++; DT(y); } while (0) |
| 157 | |
| 158 | static INLINE int ipf_check_ipf(fr_info_t *, frentry_t *, int); |
| 159 | static u_32_t ipf_checkcipso(fr_info_t *, u_char *, int); |
| 160 | static u_32_t ipf_checkripso(u_char *); |
| 161 | static u_32_t ipf_decaps(fr_info_t *, u_32_t, int); |
| 162 | #ifdef IPFILTER_LOG |
| 163 | static frentry_t *ipf_dolog(fr_info_t *, u_32_t *); |
| 164 | #endif |
| 165 | static int ipf_flushlist(ipf_main_softc_t *, int *, frentry_t **); |
| 166 | static int ipf_flush_groups(ipf_main_softc_t *, frgroup_t **, int); |
| 167 | static ipfunc_t ipf_findfunc(ipfunc_t); |
| 168 | static void *ipf_findlookup(ipf_main_softc_t *, int, frentry_t *, |
| 169 | i6addr_t *, i6addr_t *); |
| 170 | static frentry_t *ipf_firewall(fr_info_t *, u_32_t *); |
| 171 | static int ipf_fr_matcharray(fr_info_t *, int *); |
| 172 | static int ipf_frruleiter(ipf_main_softc_t *, void *, int, void *); |
| 173 | static void ipf_funcfini(ipf_main_softc_t *, frentry_t *);; |
| 174 | static int ipf_funcinit(ipf_main_softc_t *, frentry_t *); |
| 175 | static int ipf_geniter(ipf_main_softc_t *, ipftoken_t *, |
| 176 | ipfgeniter_t *); |
| 177 | static void ipf_getstat(ipf_main_softc_t *, |
| 178 | struct friostat *, int); |
| 179 | static int ipf_group_flush(ipf_main_softc_t *, frgroup_t *); |
| 180 | static void ipf_group_free(frgroup_t *); |
| 181 | static int ipf_grpmapfini(struct ipf_main_softc_s *, frentry_t *); |
| 182 | static int ipf_grpmapinit(struct ipf_main_softc_s *, frentry_t *); |
| 183 | static frentry_t *ipf_nextrule(ipf_main_softc_t *, int, int, |
| 184 | frentry_t *, int); |
| 185 | static int ipf_portcheck(frpcmp_t *, u_32_t); |
| 186 | static INLINE int ipf_pr_ah(fr_info_t *); |
| 187 | static INLINE void ipf_pr_esp(fr_info_t *); |
| 188 | static INLINE void ipf_pr_gre(fr_info_t *); |
| 189 | static INLINE void ipf_pr_udp(fr_info_t *); |
| 190 | static INLINE void ipf_pr_tcp(fr_info_t *); |
| 191 | static INLINE void ipf_pr_icmp(fr_info_t *); |
| 192 | static INLINE void ipf_pr_ipv4hdr(fr_info_t *); |
| 193 | static INLINE void ipf_pr_short(fr_info_t *, int); |
| 194 | static INLINE int ipf_pr_tcpcommon(fr_info_t *); |
| 195 | static INLINE int ipf_pr_udpcommon(fr_info_t *); |
| 196 | static void ipf_rule_delete(ipf_main_softc_t *, frentry_t *f, |
| 197 | int, int); |
| 198 | static void ipf_rule_expire_insert(ipf_main_softc_t *, |
| 199 | frentry_t *, int); |
| 200 | static int ipf_synclist(ipf_main_softc_t *, frentry_t *, void *); |
| 201 | static void ipf_token_flush(ipf_main_softc_t *); |
| 202 | static void ipf_token_unlink(ipf_main_softc_t *, ipftoken_t *); |
| 203 | static ipftuneable_t *ipf_tune_findbyname(ipftuneable_t *, const char *); |
| 204 | static ipftuneable_t *ipf_tune_findbycookie(ipftuneable_t **, void *, |
| 205 | void **); |
| 206 | static int ipf_updateipid(fr_info_t *); |
| 207 | static int ipf_settimeout(struct ipf_main_softc_s *, |
| 208 | struct ipftuneable *, ipftuneval_t *); |
| 209 | |
| 210 | |
| 211 | /* |
| 212 | * bit values for identifying presence of individual IP options |
| 213 | * All of these tables should be ordered by increasing key value on the left |
| 214 | * hand side to allow for binary searching of the array and include a trailer |
| 215 | * with a 0 for the bitmask for linear searches to easily find the end with. |
| 216 | */ |
| 217 | static const struct optlist ipopts[20] = { |
| 218 | { IPOPT_NOP, 0x000001 }, |
| 219 | { IPOPT_RR, 0x000002 }, |
| 220 | { IPOPT_ZSU, 0x000004 }, |
| 221 | { IPOPT_MTUP, 0x000008 }, |
| 222 | { IPOPT_MTUR, 0x000010 }, |
| 223 | { IPOPT_ENCODE, 0x000020 }, |
| 224 | { IPOPT_TS, 0x000040 }, |
| 225 | { IPOPT_TR, 0x000080 }, |
| 226 | { IPOPT_SECURITY, 0x000100 }, |
| 227 | { IPOPT_LSRR, 0x000200 }, |
| 228 | { IPOPT_E_SEC, 0x000400 }, |
| 229 | { IPOPT_CIPSO, 0x000800 }, |
| 230 | { IPOPT_SATID, 0x001000 }, |
| 231 | { IPOPT_SSRR, 0x002000 }, |
| 232 | { IPOPT_ADDEXT, 0x004000 }, |
| 233 | { IPOPT_VISA, 0x008000 }, |
| 234 | { IPOPT_IMITD, 0x010000 }, |
| 235 | { IPOPT_EIP, 0x020000 }, |
| 236 | { IPOPT_FINN, 0x040000 }, |
| 237 | { 0, 0x000000 } |
| 238 | }; |
| 239 | |
| 240 | #ifdef USE_INET6 |
| 241 | static const struct optlist ip6exthdr[] = { |
| 242 | { IPPROTO_HOPOPTS, 0x000001 }, |
| 243 | { IPPROTO_IPV6, 0x000002 }, |
| 244 | { IPPROTO_ROUTING, 0x000004 }, |
| 245 | { IPPROTO_FRAGMENT, 0x000008 }, |
| 246 | { IPPROTO_ESP, 0x000010 }, |
| 247 | { IPPROTO_AH, 0x000020 }, |
| 248 | { IPPROTO_NONE, 0x000040 }, |
| 249 | { IPPROTO_DSTOPTS, 0x000080 }, |
| 250 | { IPPROTO_MOBILITY, 0x000100 }, |
| 251 | { 0, 0 } |
| 252 | }; |
| 253 | #endif |
| 254 | |
| 255 | /* |
| 256 | * bit values for identifying presence of individual IP security options |
| 257 | */ |
| 258 | static const struct optlist secopt[8] = { |
| 259 | { IPSO_CLASS_RES4, 0x01 }, |
| 260 | { IPSO_CLASS_TOPS, 0x02 }, |
| 261 | { IPSO_CLASS_SECR, 0x04 }, |
| 262 | { IPSO_CLASS_RES3, 0x08 }, |
| 263 | { IPSO_CLASS_CONF, 0x10 }, |
| 264 | { IPSO_CLASS_UNCL, 0x20 }, |
| 265 | { IPSO_CLASS_RES2, 0x40 }, |
| 266 | { IPSO_CLASS_RES1, 0x80 } |
| 267 | }; |
| 268 | |
| 269 | char ipfilter_version[] = IPL_VERSION; |
| 270 | |
| 271 | int ipf_features = 0 |
| 272 | #ifdef IPFILTER_LKM |
| 273 | | IPF_FEAT_LKM |
| 274 | #endif |
| 275 | #ifdef IPFILTER_LOG |
| 276 | | IPF_FEAT_LOG |
| 277 | #endif |
| 278 | | IPF_FEAT_LOOKUP |
| 279 | #ifdef IPFILTER_BPF |
| 280 | | IPF_FEAT_BPF |
| 281 | #endif |
| 282 | #ifdef IPFILTER_COMPILED |
| 283 | | IPF_FEAT_COMPILED |
| 284 | #endif |
| 285 | #ifdef IPFILTER_CKSUM |
| 286 | | IPF_FEAT_CKSUM |
| 287 | #endif |
| 288 | | IPF_FEAT_SYNC |
| 289 | #ifdef IPFILTER_SCAN |
| 290 | | IPF_FEAT_SCAN |
| 291 | #endif |
| 292 | #ifdef USE_INET6 |
| 293 | | IPF_FEAT_IPV6 |
| 294 | #endif |
| 295 | ; |
| 296 | |
| 297 | |
| 298 | /* |
| 299 | * Table of functions available for use with call rules. |
| 300 | */ |
| 301 | static ipfunc_resolve_t ipf_availfuncs[] = { |
| 302 | { "srcgrpmap" , ipf_srcgrpmap, ipf_grpmapinit, ipf_grpmapfini }, |
| 303 | { "dstgrpmap" , ipf_dstgrpmap, ipf_grpmapinit, ipf_grpmapfini }, |
| 304 | { "" , NULL, NULL, NULL } |
| 305 | }; |
| 306 | |
| 307 | static ipftuneable_t ipf_main_tuneables[] = { |
| 308 | { { (void *)offsetof(struct ipf_main_softc_s, ipf_flags) }, |
| 309 | "ipf_flags" , 0, 0xffffffff, |
| 310 | stsizeof(ipf_main_softc_t, ipf_flags), |
| 311 | 0, NULL, NULL }, |
| 312 | { { (void *)offsetof(struct ipf_main_softc_s, ipf_active) }, |
| 313 | "active" , 0, 0, |
| 314 | stsizeof(ipf_main_softc_t, ipf_active), |
| 315 | IPFT_RDONLY, NULL, NULL }, |
| 316 | { { (void *)offsetof(ipf_main_softc_t, ipf_control_forwarding) }, |
| 317 | "control_forwarding" , 0, 1, |
| 318 | stsizeof(ipf_main_softc_t, ipf_control_forwarding), |
| 319 | 0, NULL, NULL }, |
| 320 | { { (void *)offsetof(ipf_main_softc_t, ipf_update_ipid) }, |
| 321 | "update_ipid" , 0, 1, |
| 322 | stsizeof(ipf_main_softc_t, ipf_update_ipid), |
| 323 | 0, NULL, NULL }, |
| 324 | { { (void *)offsetof(ipf_main_softc_t, ipf_chksrc) }, |
| 325 | "chksrc" , 0, 1, |
| 326 | stsizeof(ipf_main_softc_t, ipf_chksrc), |
| 327 | 0, NULL, NULL }, |
| 328 | { { (void *)offsetof(ipf_main_softc_t, ipf_minttl) }, |
| 329 | "min_ttl" , 0, 1, |
| 330 | stsizeof(ipf_main_softc_t, ipf_minttl), |
| 331 | 0, NULL, NULL }, |
| 332 | { { (void *)offsetof(ipf_main_softc_t, ipf_icmpminfragmtu) }, |
| 333 | "icmp_minfragmtu" , 0, 1, |
| 334 | stsizeof(ipf_main_softc_t, ipf_icmpminfragmtu), |
| 335 | 0, NULL, NULL }, |
| 336 | { { (void *)offsetof(ipf_main_softc_t, ipf_pass) }, |
| 337 | "default_pass" , 0, 0xffffffff, |
| 338 | stsizeof(ipf_main_softc_t, ipf_pass), |
| 339 | 0, NULL, NULL }, |
| 340 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcpidletimeout) }, |
| 341 | "tcp_idle_timeout" , 1, 0x7fffffff, |
| 342 | stsizeof(ipf_main_softc_t, ipf_tcpidletimeout), |
| 343 | 0, NULL, ipf_settimeout }, |
| 344 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosewait) }, |
| 345 | "tcp_close_wait" , 1, 0x7fffffff, |
| 346 | stsizeof(ipf_main_softc_t, ipf_tcpclosewait), |
| 347 | 0, NULL, ipf_settimeout }, |
| 348 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcplastack) }, |
| 349 | "tcp_last_ack" , 1, 0x7fffffff, |
| 350 | stsizeof(ipf_main_softc_t, ipf_tcplastack), |
| 351 | 0, NULL, ipf_settimeout }, |
| 352 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimeout) }, |
| 353 | "tcp_timeout" , 1, 0x7fffffff, |
| 354 | stsizeof(ipf_main_softc_t, ipf_tcptimeout), |
| 355 | 0, NULL, ipf_settimeout }, |
| 356 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynsent) }, |
| 357 | "tcp_syn_sent" , 1, 0x7fffffff, |
| 358 | stsizeof(ipf_main_softc_t, ipf_tcpsynsent), |
| 359 | 0, NULL, ipf_settimeout }, |
| 360 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynrecv) }, |
| 361 | "tcp_syn_received" , 1, 0x7fffffff, |
| 362 | stsizeof(ipf_main_softc_t, ipf_tcpsynrecv), |
| 363 | 0, NULL, ipf_settimeout }, |
| 364 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosed) }, |
| 365 | "tcp_closed" , 1, 0x7fffffff, |
| 366 | stsizeof(ipf_main_softc_t, ipf_tcpclosed), |
| 367 | 0, NULL, ipf_settimeout }, |
| 368 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcphalfclosed) }, |
| 369 | "tcp_half_closed" , 1, 0x7fffffff, |
| 370 | stsizeof(ipf_main_softc_t, ipf_tcphalfclosed), |
| 371 | 0, NULL, ipf_settimeout }, |
| 372 | { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimewait) }, |
| 373 | "tcp_time_wait" , 1, 0x7fffffff, |
| 374 | stsizeof(ipf_main_softc_t, ipf_tcptimewait), |
| 375 | 0, NULL, ipf_settimeout }, |
| 376 | { { (void *)offsetof(ipf_main_softc_t, ipf_udptimeout) }, |
| 377 | "udp_timeout" , 1, 0x7fffffff, |
| 378 | stsizeof(ipf_main_softc_t, ipf_udptimeout), |
| 379 | 0, NULL, ipf_settimeout }, |
| 380 | { { (void *)offsetof(ipf_main_softc_t, ipf_udpacktimeout) }, |
| 381 | "udp_ack_timeout" , 1, 0x7fffffff, |
| 382 | stsizeof(ipf_main_softc_t, ipf_udpacktimeout), |
| 383 | 0, NULL, ipf_settimeout }, |
| 384 | { { (void *)offsetof(ipf_main_softc_t, ipf_icmptimeout) }, |
| 385 | "icmp_timeout" , 1, 0x7fffffff, |
| 386 | stsizeof(ipf_main_softc_t, ipf_icmptimeout), |
| 387 | 0, NULL, ipf_settimeout }, |
| 388 | { { (void *)offsetof(ipf_main_softc_t, ipf_icmpacktimeout) }, |
| 389 | "icmp_ack_timeout" , 1, 0x7fffffff, |
| 390 | stsizeof(ipf_main_softc_t, ipf_icmpacktimeout), |
| 391 | 0, NULL, ipf_settimeout }, |
| 392 | { { (void *)offsetof(ipf_main_softc_t, ipf_iptimeout) }, |
| 393 | "ip_timeout" , 1, 0x7fffffff, |
| 394 | stsizeof(ipf_main_softc_t, ipf_iptimeout), |
| 395 | 0, NULL, ipf_settimeout }, |
| 396 | #if defined(INSTANCES) && defined(_KERNEL) |
| 397 | { { (void *)offsetof(ipf_main_softc_t, ipf_get_loopback) }, |
| 398 | "intercept_loopback" , 0, 1, |
| 399 | stsizeof(ipf_main_softc_t, ipf_get_loopback), |
| 400 | 0, NULL, ipf_set_loopback }, |
| 401 | #endif |
| 402 | { { 0 }, |
| 403 | NULL, 0, 0, |
| 404 | 0, |
| 405 | 0, NULL, NULL } |
| 406 | }; |
| 407 | |
| 408 | |
| 409 | /* |
| 410 | * The next section of code is a a collection of small routines that set |
| 411 | * fields in the fr_info_t structure passed based on properties of the |
| 412 | * current packet. There are different routines for the same protocol |
| 413 | * for each of IPv4 and IPv6. Adding a new protocol, for which there |
| 414 | * will "special" inspection for setup, is now more easily done by adding |
| 415 | * a new routine and expanding the ipf_pr_ipinit*() function rather than by |
| 416 | * adding more code to a growing switch statement. |
| 417 | */ |
| 418 | #ifdef USE_INET6 |
| 419 | static INLINE int ipf_pr_ah6(fr_info_t *); |
| 420 | static INLINE void ipf_pr_esp6(fr_info_t *); |
| 421 | static INLINE void ipf_pr_gre6(fr_info_t *); |
| 422 | static INLINE void ipf_pr_udp6(fr_info_t *); |
| 423 | static INLINE void ipf_pr_tcp6(fr_info_t *); |
| 424 | static INLINE void ipf_pr_icmp6(fr_info_t *); |
| 425 | static INLINE void ipf_pr_ipv6hdr(fr_info_t *); |
| 426 | static INLINE void ipf_pr_short6(fr_info_t *, int); |
| 427 | static INLINE int ipf_pr_hopopts6(fr_info_t *); |
| 428 | static INLINE int ipf_pr_mobility6(fr_info_t *); |
| 429 | static INLINE int ipf_pr_routing6(fr_info_t *); |
| 430 | static INLINE int ipf_pr_dstopts6(fr_info_t *); |
| 431 | static INLINE int ipf_pr_fragment6(fr_info_t *); |
| 432 | static INLINE struct ip6_ext *ipf_pr_ipv6exthdr(fr_info_t *, int, int); |
| 433 | |
| 434 | |
| 435 | /* ------------------------------------------------------------------------ */ |
| 436 | /* Function: ipf_pr_short6 */ |
| 437 | /* Returns: void */ |
| 438 | /* Parameters: fin(I) - pointer to packet information */ |
| 439 | /* xmin(I) - minimum header size */ |
| 440 | /* */ |
| 441 | /* IPv6 Only */ |
| 442 | /* This is function enforces the 'is a packet too short to be legit' rule */ |
| 443 | /* for IPv6 and marks the packet with FI_SHORT if so. See function comment */ |
| 444 | /* for ipf_pr_short() for more details. */ |
| 445 | /* ------------------------------------------------------------------------ */ |
| 446 | static INLINE void |
| 447 | ipf_pr_short6(fr_info_t *fin, int xmin) |
| 448 | { |
| 449 | |
| 450 | if (fin->fin_dlen < xmin) |
| 451 | fin->fin_flx |= FI_SHORT; |
| 452 | } |
| 453 | |
| 454 | |
| 455 | /* ------------------------------------------------------------------------ */ |
| 456 | /* Function: ipf_pr_ipv6hdr */ |
| 457 | /* Returns: void */ |
| 458 | /* Parameters: fin(I) - pointer to packet information */ |
| 459 | /* */ |
| 460 | /* IPv6 Only */ |
| 461 | /* Copy values from the IPv6 header into the fr_info_t struct and call the */ |
| 462 | /* per-protocol analyzer if it exists. In validating the packet, a protocol*/ |
| 463 | /* analyzer may pullup or free the packet itself so we need to be vigiliant */ |
| 464 | /* of that possibility arising. */ |
| 465 | /* ------------------------------------------------------------------------ */ |
| 466 | static INLINE void |
| 467 | ipf_pr_ipv6hdr(fr_info_t *fin) |
| 468 | { |
| 469 | ip6_t *ip6 = (ip6_t *)fin->fin_ip; |
| 470 | int p, go = 1, i, hdrcount; |
| 471 | fr_ip_t *fi = &fin->fin_fi; |
| 472 | |
| 473 | fin->fin_off = 0; |
| 474 | |
| 475 | fi->fi_tos = 0; |
| 476 | fi->fi_optmsk = 0; |
| 477 | fi->fi_secmsk = 0; |
| 478 | fi->fi_auth = 0; |
| 479 | |
| 480 | p = ip6->ip6_nxt; |
| 481 | fin->fin_crc = p; |
| 482 | fi->fi_ttl = ip6->ip6_hlim; |
| 483 | fi->fi_src.in6 = ip6->ip6_src; |
| 484 | fin->fin_crc += fi->fi_src.i6[0]; |
| 485 | fin->fin_crc += fi->fi_src.i6[1]; |
| 486 | fin->fin_crc += fi->fi_src.i6[2]; |
| 487 | fin->fin_crc += fi->fi_src.i6[3]; |
| 488 | fi->fi_dst.in6 = ip6->ip6_dst; |
| 489 | fin->fin_crc += fi->fi_dst.i6[0]; |
| 490 | fin->fin_crc += fi->fi_dst.i6[1]; |
| 491 | fin->fin_crc += fi->fi_dst.i6[2]; |
| 492 | fin->fin_crc += fi->fi_dst.i6[3]; |
| 493 | fin->fin_id = 0; |
| 494 | if (IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) |
| 495 | fin->fin_flx |= FI_MULTICAST|FI_MBCAST; |
| 496 | |
| 497 | hdrcount = 0; |
| 498 | while (go && !(fin->fin_flx & FI_SHORT)) { |
| 499 | switch (p) |
| 500 | { |
| 501 | case IPPROTO_UDP : |
| 502 | ipf_pr_udp6(fin); |
| 503 | go = 0; |
| 504 | break; |
| 505 | |
| 506 | case IPPROTO_TCP : |
| 507 | ipf_pr_tcp6(fin); |
| 508 | go = 0; |
| 509 | break; |
| 510 | |
| 511 | case IPPROTO_ICMPV6 : |
| 512 | ipf_pr_icmp6(fin); |
| 513 | go = 0; |
| 514 | break; |
| 515 | |
| 516 | case IPPROTO_GRE : |
| 517 | ipf_pr_gre6(fin); |
| 518 | go = 0; |
| 519 | break; |
| 520 | |
| 521 | case IPPROTO_HOPOPTS : |
| 522 | p = ipf_pr_hopopts6(fin); |
| 523 | break; |
| 524 | |
| 525 | case IPPROTO_MOBILITY : |
| 526 | p = ipf_pr_mobility6(fin); |
| 527 | break; |
| 528 | |
| 529 | case IPPROTO_DSTOPTS : |
| 530 | p = ipf_pr_dstopts6(fin); |
| 531 | break; |
| 532 | |
| 533 | case IPPROTO_ROUTING : |
| 534 | p = ipf_pr_routing6(fin); |
| 535 | break; |
| 536 | |
| 537 | case IPPROTO_AH : |
| 538 | p = ipf_pr_ah6(fin); |
| 539 | break; |
| 540 | |
| 541 | case IPPROTO_ESP : |
| 542 | ipf_pr_esp6(fin); |
| 543 | go = 0; |
| 544 | break; |
| 545 | |
| 546 | case IPPROTO_IPV6 : |
| 547 | for (i = 0; ip6exthdr[i].ol_bit != 0; i++) |
| 548 | if (ip6exthdr[i].ol_val == p) { |
| 549 | fin->fin_flx |= ip6exthdr[i].ol_bit; |
| 550 | break; |
| 551 | } |
| 552 | go = 0; |
| 553 | break; |
| 554 | |
| 555 | case IPPROTO_NONE : |
| 556 | go = 0; |
| 557 | break; |
| 558 | |
| 559 | case IPPROTO_FRAGMENT : |
| 560 | p = ipf_pr_fragment6(fin); |
| 561 | /* |
| 562 | * Given that the only fragments we want to let through |
| 563 | * (where fin_off != 0) are those where the non-first |
| 564 | * fragments only have data, we can safely stop looking |
| 565 | * at headers if this is a non-leading fragment. |
| 566 | */ |
| 567 | if (fin->fin_off != 0) |
| 568 | go = 0; |
| 569 | break; |
| 570 | |
| 571 | default : |
| 572 | go = 0; |
| 573 | break; |
| 574 | } |
| 575 | hdrcount++; |
| 576 | |
| 577 | /* |
| 578 | * It is important to note that at this point, for the |
| 579 | * extension headers (go != 0), the entire header may not have |
| 580 | * been pulled up when the code gets to this point. This is |
| 581 | * only done for "go != 0" because the other header handlers |
| 582 | * will all pullup their complete header. The other indicator |
| 583 | * of an incomplete packet is that this was just an extension |
| 584 | * header. |
| 585 | */ |
| 586 | if ((go != 0) && (p != IPPROTO_NONE) && |
| 587 | (ipf_pr_pullup(fin, 0) == -1)) { |
| 588 | p = IPPROTO_NONE; |
| 589 | break; |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | /* |
| 594 | * Some of the above functions, like ipf_pr_esp6(), can call ipf_pullup |
| 595 | * and destroy whatever packet was here. The caller of this function |
| 596 | * expects us to return if there is a problem with ipf_pullup. |
| 597 | */ |
| 598 | if (fin->fin_m == NULL) { |
| 599 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 600 | |
| 601 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_bad); |
| 602 | return; |
| 603 | } |
| 604 | |
| 605 | fi->fi_p = p; |
| 606 | |
| 607 | /* |
| 608 | * IPv6 fragment case 1 - see comment for ipf_pr_fragment6(). |
| 609 | * "go != 0" imples the above loop hasn't arrived at a layer 4 header. |
| 610 | */ |
| 611 | if ((go != 0) && (fin->fin_flx & FI_FRAG) && (fin->fin_off == 0)) { |
| 612 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 613 | |
| 614 | fin->fin_flx |= FI_BAD; |
| 615 | DT2(ipf_fi_bad_ipv6_frag_1, fr_info_t *, fin, int, go); |
| 616 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_badfrag); |
| 617 | LBUMP(ipf_stats[fin->fin_out].fr_v6_bad); |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | |
| 622 | /* ------------------------------------------------------------------------ */ |
| 623 | /* Function: ipf_pr_ipv6exthdr */ |
| 624 | /* Returns: struct ip6_ext * - pointer to the start of the next header */ |
| 625 | /* or NULL if there is a prolblem. */ |
| 626 | /* Parameters: fin(I) - pointer to packet information */ |
| 627 | /* multiple(I) - flag indicating yes/no if multiple occurances */ |
| 628 | /* of this extension header are allowed. */ |
| 629 | /* proto(I) - protocol number for this extension header */ |
| 630 | /* */ |
| 631 | /* IPv6 Only */ |
| 632 | /* This function embodies a number of common checks that all IPv6 extension */ |
| 633 | /* headers must be subjected to. For example, making sure the packet is */ |
| 634 | /* big enough for it to be in, checking if it is repeated and setting a */ |
| 635 | /* flag to indicate its presence. */ |
| 636 | /* ------------------------------------------------------------------------ */ |
| 637 | static INLINE struct ip6_ext * |
| 638 | ipf_pr_ipv6exthdr(fr_info_t *fin, int multiple, int proto) |
| 639 | { |
| 640 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 641 | struct ip6_ext *hdr; |
| 642 | u_short shift; |
| 643 | int i; |
| 644 | |
| 645 | fin->fin_flx |= FI_V6EXTHDR; |
| 646 | |
| 647 | /* 8 is default length of extension hdr */ |
| 648 | if ((fin->fin_dlen - 8) < 0) { |
| 649 | fin->fin_flx |= FI_SHORT; |
| 650 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_short); |
| 651 | return NULL; |
| 652 | } |
| 653 | |
| 654 | if (ipf_pr_pullup(fin, 8) == -1) { |
| 655 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_pullup); |
| 656 | return NULL; |
| 657 | } |
| 658 | |
| 659 | hdr = fin->fin_dp; |
| 660 | switch (proto) |
| 661 | { |
| 662 | case IPPROTO_FRAGMENT : |
| 663 | shift = 8; |
| 664 | break; |
| 665 | default : |
| 666 | shift = 8 + (hdr->ip6e_len << 3); |
| 667 | break; |
| 668 | } |
| 669 | |
| 670 | if (shift > fin->fin_dlen) { /* Nasty extension header length? */ |
| 671 | fin->fin_flx |= FI_BAD; |
| 672 | DT3(ipf_fi_bad_pr_ipv6exthdr_len, fr_info_t *, fin, u_short, shift, u_short, fin->fin_dlen); |
| 673 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_hlen); |
| 674 | return NULL; |
| 675 | } |
| 676 | |
| 677 | fin->fin_dp = (char *)fin->fin_dp + shift; |
| 678 | fin->fin_dlen -= shift; |
| 679 | |
| 680 | /* |
| 681 | * If we have seen a fragment header, do not set any flags to indicate |
| 682 | * the presence of this extension header as it has no impact on the |
| 683 | * end result until after it has been defragmented. |
| 684 | */ |
| 685 | if (fin->fin_flx & FI_FRAG) |
| 686 | return hdr; |
| 687 | |
| 688 | for (i = 0; ip6exthdr[i].ol_bit != 0; i++) |
| 689 | if (ip6exthdr[i].ol_val == proto) { |
| 690 | /* |
| 691 | * Most IPv6 extension headers are only allowed once. |
| 692 | */ |
| 693 | if ((multiple == 0) && |
| 694 | ((fin->fin_optmsk & ip6exthdr[i].ol_bit) != 0)) { |
| 695 | fin->fin_flx |= FI_BAD; |
| 696 | DT2(ipf_fi_bad_ipv6exthdr_once, fr_info_t *, fin, u_int, (fin->fin_optmsk & ip6exthdr[i].ol_bit)); |
| 697 | } else |
| 698 | fin->fin_optmsk |= ip6exthdr[i].ol_bit; |
| 699 | break; |
| 700 | } |
| 701 | |
| 702 | return hdr; |
| 703 | } |
| 704 | |
| 705 | |
| 706 | /* ------------------------------------------------------------------------ */ |
| 707 | /* Function: ipf_pr_hopopts6 */ |
| 708 | /* Returns: int - value of the next header or IPPROTO_NONE if error */ |
| 709 | /* Parameters: fin(I) - pointer to packet information */ |
| 710 | /* */ |
| 711 | /* IPv6 Only */ |
| 712 | /* This is function checks pending hop by hop options extension header */ |
| 713 | /* ------------------------------------------------------------------------ */ |
| 714 | static INLINE int |
| 715 | ipf_pr_hopopts6(fr_info_t *fin) |
| 716 | { |
| 717 | struct ip6_ext *hdr; |
| 718 | |
| 719 | hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); |
| 720 | if (hdr == NULL) |
| 721 | return IPPROTO_NONE; |
| 722 | return hdr->ip6e_nxt; |
| 723 | } |
| 724 | |
| 725 | |
| 726 | /* ------------------------------------------------------------------------ */ |
| 727 | /* Function: ipf_pr_mobility6 */ |
| 728 | /* Returns: int - value of the next header or IPPROTO_NONE if error */ |
| 729 | /* Parameters: fin(I) - pointer to packet information */ |
| 730 | /* */ |
| 731 | /* IPv6 Only */ |
| 732 | /* This is function checks the IPv6 mobility extension header */ |
| 733 | /* ------------------------------------------------------------------------ */ |
| 734 | static INLINE int |
| 735 | ipf_pr_mobility6(fr_info_t *fin) |
| 736 | { |
| 737 | struct ip6_ext *hdr; |
| 738 | |
| 739 | hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); |
| 740 | if (hdr == NULL) |
| 741 | return IPPROTO_NONE; |
| 742 | return hdr->ip6e_nxt; |
| 743 | } |
| 744 | |
| 745 | |
| 746 | /* ------------------------------------------------------------------------ */ |
| 747 | /* Function: ipf_pr_routing6 */ |
| 748 | /* Returns: int - value of the next header or IPPROTO_NONE if error */ |
| 749 | /* Parameters: fin(I) - pointer to packet information */ |
| 750 | /* */ |
| 751 | /* IPv6 Only */ |
| 752 | /* This is function checks pending routing extension header */ |
| 753 | /* ------------------------------------------------------------------------ */ |
| 754 | static INLINE int |
| 755 | ipf_pr_routing6(fr_info_t *fin) |
| 756 | { |
| 757 | struct ip6_routing *hdr; |
| 758 | |
| 759 | hdr = (struct ip6_routing *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_ROUTING); |
| 760 | if (hdr == NULL) |
| 761 | return IPPROTO_NONE; |
| 762 | |
| 763 | switch (hdr->ip6r_type) |
| 764 | { |
| 765 | case 0 : |
| 766 | /* |
| 767 | * Nasty extension header length? |
| 768 | */ |
| 769 | if (((hdr->ip6r_len >> 1) < hdr->ip6r_segleft) || |
| 770 | (hdr->ip6r_segleft && (hdr->ip6r_len & 1))) { |
| 771 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 772 | |
| 773 | fin->fin_flx |= FI_BAD; |
| 774 | DT1(ipf_fi_bad_routing6, fr_info_t *, fin); |
| 775 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_rh_bad); |
| 776 | return IPPROTO_NONE; |
| 777 | } |
| 778 | break; |
| 779 | |
| 780 | default : |
| 781 | break; |
| 782 | } |
| 783 | |
| 784 | return hdr->ip6r_nxt; |
| 785 | } |
| 786 | |
| 787 | |
| 788 | /* ------------------------------------------------------------------------ */ |
| 789 | /* Function: ipf_pr_fragment6 */ |
| 790 | /* Returns: int - value of the next header or IPPROTO_NONE if error */ |
| 791 | /* Parameters: fin(I) - pointer to packet information */ |
| 792 | /* */ |
| 793 | /* IPv6 Only */ |
| 794 | /* Examine the IPv6 fragment header and extract fragment offset information.*/ |
| 795 | /* */ |
| 796 | /* Fragments in IPv6 are extraordinarily difficult to deal with - much more */ |
| 797 | /* so than in IPv4. There are 5 cases of fragments with IPv6 that all */ |
| 798 | /* packets with a fragment header can fit into. They are as follows: */ |
| 799 | /* */ |
| 800 | /* 1. [IPv6][0-n EH][FH][0-n EH] (no L4HDR present) */ |
| 801 | /* 2. [IPV6][0-n EH][FH][0-n EH][L4HDR part] (short) */ |
| 802 | /* 3. [IPV6][0-n EH][FH][L4HDR part][0-n data] (short) */ |
| 803 | /* 4. [IPV6][0-n EH][FH][0-n EH][L4HDR][0-n data] */ |
| 804 | /* 5. [IPV6][0-n EH][FH][data] */ |
| 805 | /* */ |
| 806 | /* IPV6 = IPv6 header, FH = Fragment Header, */ |
| 807 | /* 0-n EH = 0 or more extension headers, 0-n data = 0 or more bytes of data */ |
| 808 | /* */ |
| 809 | /* Packets that match 1, 2, 3 will be dropped as the only reasonable */ |
| 810 | /* scenario in which they happen is in extreme circumstances that are most */ |
| 811 | /* likely to be an indication of an attack rather than normal traffic. */ |
| 812 | /* A type 3 packet may be sent by an attacked after a type 4 packet. There */ |
| 813 | /* are two rules that can be used to guard against type 3 packets: L4 */ |
| 814 | /* headers must always be in a packet that has the offset field set to 0 */ |
| 815 | /* and no packet is allowed to overlay that where offset = 0. */ |
| 816 | /* ------------------------------------------------------------------------ */ |
| 817 | static INLINE int |
| 818 | ipf_pr_fragment6(fr_info_t *fin) |
| 819 | { |
| 820 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 821 | struct ip6_frag *frag; |
| 822 | |
| 823 | fin->fin_flx |= FI_FRAG; |
| 824 | |
| 825 | frag = (struct ip6_frag *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT); |
| 826 | if (frag == NULL) { |
| 827 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_frag_bad); |
| 828 | return IPPROTO_NONE; |
| 829 | } |
| 830 | |
| 831 | if ((frag->ip6f_offlg & IP6F_MORE_FRAG) != 0) { |
| 832 | /* |
| 833 | * Any fragment that isn't the last fragment must have its |
| 834 | * length as a multiple of 8. |
| 835 | */ |
| 836 | if ((fin->fin_plen & 7) != 0) { |
| 837 | fin->fin_flx |= FI_BAD; |
| 838 | DT2(ipf_fi_bad_frag_not_8, fr_info_t *, fin, u_int, (fin->fin_plen & 7)); |
| 839 | } |
| 840 | } |
| 841 | |
| 842 | fin->fin_fraghdr = frag; |
| 843 | fin->fin_id = frag->ip6f_ident; |
| 844 | fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK); |
| 845 | if (fin->fin_off != 0) |
| 846 | fin->fin_flx |= FI_FRAGBODY; |
| 847 | |
| 848 | /* |
| 849 | * Jumbograms aren't handled, so the max. length is 64k |
| 850 | */ |
| 851 | if ((fin->fin_off << 3) + fin->fin_dlen > 65535) { |
| 852 | fin->fin_flx |= FI_BAD; |
| 853 | DT2(ipf_fi_bad_jumbogram, fr_info_t *, fin, u_int, ((fin->fin_off << 3) + fin->fin_dlen)); |
| 854 | } |
| 855 | |
| 856 | /* |
| 857 | * We don't know where the transport layer header (or whatever is next |
| 858 | * is), as it could be behind destination options (amongst others) so |
| 859 | * return the fragment header as the type of packet this is. Note that |
| 860 | * this effectively disables the fragment cache for > 1 protocol at a |
| 861 | * time. |
| 862 | */ |
| 863 | return frag->ip6f_nxt; |
| 864 | } |
| 865 | |
| 866 | |
| 867 | /* ------------------------------------------------------------------------ */ |
| 868 | /* Function: ipf_pr_dstopts6 */ |
| 869 | /* Returns: int - value of the next header or IPPROTO_NONE if error */ |
| 870 | /* Parameters: fin(I) - pointer to packet information */ |
| 871 | /* */ |
| 872 | /* IPv6 Only */ |
| 873 | /* This is function checks pending destination options extension header */ |
| 874 | /* ------------------------------------------------------------------------ */ |
| 875 | static INLINE int |
| 876 | ipf_pr_dstopts6(fr_info_t *fin) |
| 877 | { |
| 878 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 879 | struct ip6_ext *hdr; |
| 880 | |
| 881 | hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_DSTOPTS); |
| 882 | if (hdr == NULL) { |
| 883 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_dst_bad); |
| 884 | return IPPROTO_NONE; |
| 885 | } |
| 886 | return hdr->ip6e_nxt; |
| 887 | } |
| 888 | |
| 889 | |
| 890 | /* ------------------------------------------------------------------------ */ |
| 891 | /* Function: ipf_pr_icmp6 */ |
| 892 | /* Returns: void */ |
| 893 | /* Parameters: fin(I) - pointer to packet information */ |
| 894 | /* */ |
| 895 | /* IPv6 Only */ |
| 896 | /* This routine is mainly concerned with determining the minimum valid size */ |
| 897 | /* for an ICMPv6 packet. */ |
| 898 | /* ------------------------------------------------------------------------ */ |
| 899 | static INLINE void |
| 900 | ipf_pr_icmp6(fr_info_t *fin) |
| 901 | { |
| 902 | int minicmpsz = sizeof(struct icmp6_hdr); |
| 903 | struct icmp6_hdr *icmp6; |
| 904 | |
| 905 | if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) { |
| 906 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 907 | |
| 908 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_icmp6_pullup); |
| 909 | return; |
| 910 | } |
| 911 | |
| 912 | if (fin->fin_dlen > 1) { |
| 913 | ip6_t *ip6; |
| 914 | |
| 915 | icmp6 = fin->fin_dp; |
| 916 | |
| 917 | fin->fin_data[0] = *(u_short *)icmp6; |
| 918 | |
| 919 | if ((icmp6->icmp6_type & ICMP6_INFOMSG_MASK) != 0) |
| 920 | fin->fin_flx |= FI_ICMPQUERY; |
| 921 | |
| 922 | switch (icmp6->icmp6_type) |
| 923 | { |
| 924 | case ICMP6_ECHO_REPLY : |
| 925 | case ICMP6_ECHO_REQUEST : |
| 926 | if (fin->fin_dlen >= 6) |
| 927 | fin->fin_data[1] = icmp6->icmp6_id; |
| 928 | minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t); |
| 929 | break; |
| 930 | |
| 931 | case ICMP6_DST_UNREACH : |
| 932 | case ICMP6_PACKET_TOO_BIG : |
| 933 | case ICMP6_TIME_EXCEEDED : |
| 934 | case ICMP6_PARAM_PROB : |
| 935 | fin->fin_flx |= FI_ICMPERR; |
| 936 | minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t); |
| 937 | if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) |
| 938 | break; |
| 939 | |
| 940 | if (M_LEN(fin->fin_m) < fin->fin_plen) { |
| 941 | if (ipf_coalesce(fin) != 1) |
| 942 | return; |
| 943 | } |
| 944 | |
| 945 | if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN) == -1) |
| 946 | return; |
| 947 | |
| 948 | /* |
| 949 | * If the destination of this packet doesn't match the |
| 950 | * source of the original packet then this packet is |
| 951 | * not correct. |
| 952 | */ |
| 953 | icmp6 = fin->fin_dp; |
| 954 | ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN); |
| 955 | if (IP6_NEQ(&fin->fin_fi.fi_dst, |
| 956 | &ip6->ip6_src)) { |
| 957 | fin->fin_flx |= FI_BAD; |
| 958 | DT1(ipf_fi_bad_icmp6, fr_info_t *, fin); |
| 959 | } |
| 960 | break; |
| 961 | default : |
| 962 | break; |
| 963 | } |
| 964 | } |
| 965 | |
| 966 | ipf_pr_short6(fin, minicmpsz); |
| 967 | if ((fin->fin_flx & (FI_SHORT|FI_BAD)) == 0) { |
| 968 | u_char p = fin->fin_p; |
| 969 | |
| 970 | fin->fin_p = IPPROTO_ICMPV6; |
| 971 | ipf_checkv6sum(fin); |
| 972 | fin->fin_p = p; |
| 973 | } |
| 974 | } |
| 975 | |
| 976 | |
| 977 | /* ------------------------------------------------------------------------ */ |
| 978 | /* Function: ipf_pr_udp6 */ |
| 979 | /* Returns: void */ |
| 980 | /* Parameters: fin(I) - pointer to packet information */ |
| 981 | /* */ |
| 982 | /* IPv6 Only */ |
| 983 | /* Analyse the packet for IPv6/UDP properties. */ |
| 984 | /* Is not expected to be called for fragmented packets. */ |
| 985 | /* ------------------------------------------------------------------------ */ |
| 986 | static INLINE void |
| 987 | ipf_pr_udp6(fr_info_t *fin) |
| 988 | { |
| 989 | |
| 990 | if (ipf_pr_udpcommon(fin) == 0) { |
| 991 | u_char p = fin->fin_p; |
| 992 | |
| 993 | fin->fin_p = IPPROTO_UDP; |
| 994 | ipf_checkv6sum(fin); |
| 995 | fin->fin_p = p; |
| 996 | } |
| 997 | } |
| 998 | |
| 999 | |
| 1000 | /* ------------------------------------------------------------------------ */ |
| 1001 | /* Function: ipf_pr_tcp6 */ |
| 1002 | /* Returns: void */ |
| 1003 | /* Parameters: fin(I) - pointer to packet information */ |
| 1004 | /* */ |
| 1005 | /* IPv6 Only */ |
| 1006 | /* Analyse the packet for IPv6/TCP properties. */ |
| 1007 | /* Is not expected to be called for fragmented packets. */ |
| 1008 | /* ------------------------------------------------------------------------ */ |
| 1009 | static INLINE void |
| 1010 | ipf_pr_tcp6(fr_info_t *fin) |
| 1011 | { |
| 1012 | |
| 1013 | if (ipf_pr_tcpcommon(fin) == 0) { |
| 1014 | u_char p = fin->fin_p; |
| 1015 | |
| 1016 | fin->fin_p = IPPROTO_TCP; |
| 1017 | ipf_checkv6sum(fin); |
| 1018 | fin->fin_p = p; |
| 1019 | } |
| 1020 | } |
| 1021 | |
| 1022 | |
| 1023 | /* ------------------------------------------------------------------------ */ |
| 1024 | /* Function: ipf_pr_esp6 */ |
| 1025 | /* Returns: void */ |
| 1026 | /* Parameters: fin(I) - pointer to packet information */ |
| 1027 | /* */ |
| 1028 | /* IPv6 Only */ |
| 1029 | /* Analyse the packet for ESP properties. */ |
| 1030 | /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ |
| 1031 | /* even though the newer ESP packets must also have a sequence number that */ |
| 1032 | /* is 32bits as well, it is not possible(?) to determine the version from a */ |
| 1033 | /* simple packet header. */ |
| 1034 | /* ------------------------------------------------------------------------ */ |
| 1035 | static INLINE void |
| 1036 | ipf_pr_esp6(fr_info_t *fin) |
| 1037 | { |
| 1038 | |
| 1039 | if ((fin->fin_off == 0) && (ipf_pr_pullup(fin, 8) == -1)) { |
| 1040 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1041 | |
| 1042 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_esp_pullup); |
| 1043 | return; |
| 1044 | } |
| 1045 | } |
| 1046 | |
| 1047 | |
| 1048 | /* ------------------------------------------------------------------------ */ |
| 1049 | /* Function: ipf_pr_ah6 */ |
| 1050 | /* Returns: int - value of the next header or IPPROTO_NONE if error */ |
| 1051 | /* Parameters: fin(I) - pointer to packet information */ |
| 1052 | /* */ |
| 1053 | /* IPv6 Only */ |
| 1054 | /* Analyse the packet for AH properties. */ |
| 1055 | /* The minimum length is taken to be the combination of all fields in the */ |
| 1056 | /* header being present and no authentication data (null algorithm used.) */ |
| 1057 | /* ------------------------------------------------------------------------ */ |
| 1058 | static INLINE int |
| 1059 | ipf_pr_ah6(fr_info_t *fin) |
| 1060 | { |
| 1061 | authhdr_t *ah; |
| 1062 | |
| 1063 | fin->fin_flx |= FI_AH; |
| 1064 | |
| 1065 | ah = (authhdr_t *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); |
| 1066 | if (ah == NULL) { |
| 1067 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1068 | |
| 1069 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_ah_bad); |
| 1070 | return IPPROTO_NONE; |
| 1071 | } |
| 1072 | |
| 1073 | ipf_pr_short6(fin, sizeof(*ah)); |
| 1074 | |
| 1075 | /* |
| 1076 | * No need for another pullup, ipf_pr_ipv6exthdr() will pullup |
| 1077 | * enough data to satisfy ah_next (the very first one.) |
| 1078 | */ |
| 1079 | return ah->ah_next; |
| 1080 | } |
| 1081 | |
| 1082 | |
| 1083 | /* ------------------------------------------------------------------------ */ |
| 1084 | /* Function: ipf_pr_gre6 */ |
| 1085 | /* Returns: void */ |
| 1086 | /* Parameters: fin(I) - pointer to packet information */ |
| 1087 | /* */ |
| 1088 | /* Analyse the packet for GRE properties. */ |
| 1089 | /* ------------------------------------------------------------------------ */ |
| 1090 | static INLINE void |
| 1091 | ipf_pr_gre6(fr_info_t *fin) |
| 1092 | { |
| 1093 | grehdr_t *gre; |
| 1094 | |
| 1095 | if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { |
| 1096 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1097 | |
| 1098 | LBUMPD(ipf_stats[fin->fin_out], fr_v6_gre_pullup); |
| 1099 | return; |
| 1100 | } |
| 1101 | |
| 1102 | gre = fin->fin_dp; |
| 1103 | if (GRE_REV(gre->gr_flags) == 1) |
| 1104 | fin->fin_data[0] = gre->gr_call; |
| 1105 | } |
| 1106 | #endif /* USE_INET6 */ |
| 1107 | |
| 1108 | |
| 1109 | /* ------------------------------------------------------------------------ */ |
| 1110 | /* Function: ipf_pr_pullup */ |
| 1111 | /* Returns: int - 0 == pullup succeeded, -1 == failure */ |
| 1112 | /* Parameters: fin(I) - pointer to packet information */ |
| 1113 | /* plen(I) - length (excluding L3 header) to pullup */ |
| 1114 | /* */ |
| 1115 | /* Short inline function to cut down on code duplication to perform a call */ |
| 1116 | /* to ipf_pullup to ensure there is the required amount of data, */ |
| 1117 | /* consecutively in the packet buffer. */ |
| 1118 | /* */ |
| 1119 | /* This function pulls up 'extra' data at the location of fin_dp. fin_dp */ |
| 1120 | /* points to the first byte after the complete layer 3 header, which will */ |
| 1121 | /* include all of the known extension headers for IPv6 or options for IPv4. */ |
| 1122 | /* */ |
| 1123 | /* Since fr_pullup() expects the total length of bytes to be pulled up, it */ |
| 1124 | /* is necessary to add those we can already assume to be pulled up (fin_dp */ |
| 1125 | /* - fin_ip) to what is passed through. */ |
| 1126 | /* ------------------------------------------------------------------------ */ |
| 1127 | int |
| 1128 | ipf_pr_pullup(fr_info_t *fin, int plen) |
| 1129 | { |
| 1130 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1131 | |
| 1132 | if (fin->fin_m != NULL) { |
| 1133 | if (fin->fin_dp != NULL) |
| 1134 | plen += (char *)fin->fin_dp - |
| 1135 | ((char *)fin->fin_ip + fin->fin_hlen); |
| 1136 | plen += fin->fin_hlen; |
| 1137 | if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) { |
| 1138 | #if defined(_KERNEL) |
| 1139 | if (ipf_pullup(fin->fin_m, fin, plen) == NULL) { |
| 1140 | DT(ipf_pullup_fail); |
| 1141 | LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); |
| 1142 | return -1; |
| 1143 | } |
| 1144 | LBUMP(ipf_stats[fin->fin_out].fr_pull[0]); |
| 1145 | #else |
| 1146 | LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); |
| 1147 | /* |
| 1148 | * Fake ipf_pullup failing |
| 1149 | */ |
| 1150 | fin->fin_reason = FRB_PULLUP; |
| 1151 | *fin->fin_mp = NULL; |
| 1152 | fin->fin_m = NULL; |
| 1153 | fin->fin_ip = NULL; |
| 1154 | return -1; |
| 1155 | #endif |
| 1156 | } |
| 1157 | } |
| 1158 | return 0; |
| 1159 | } |
| 1160 | |
| 1161 | |
| 1162 | /* ------------------------------------------------------------------------ */ |
| 1163 | /* Function: ipf_pr_short */ |
| 1164 | /* Returns: void */ |
| 1165 | /* Parameters: fin(I) - pointer to packet information */ |
| 1166 | /* xmin(I) - minimum header size */ |
| 1167 | /* */ |
| 1168 | /* Check if a packet is "short" as defined by xmin. The rule we are */ |
| 1169 | /* applying here is that the packet must not be fragmented within the layer */ |
| 1170 | /* 4 header. That is, it must not be a fragment that has its offset set to */ |
| 1171 | /* start within the layer 4 header (hdrmin) or if it is at offset 0, the */ |
| 1172 | /* entire layer 4 header must be present (min). */ |
| 1173 | /* ------------------------------------------------------------------------ */ |
| 1174 | static INLINE void |
| 1175 | ipf_pr_short(fr_info_t *fin, int xmin) |
| 1176 | { |
| 1177 | |
| 1178 | if (fin->fin_off == 0) { |
| 1179 | if (fin->fin_dlen < xmin) |
| 1180 | fin->fin_flx |= FI_SHORT; |
| 1181 | } else if (fin->fin_off < xmin) { |
| 1182 | fin->fin_flx |= FI_SHORT; |
| 1183 | } |
| 1184 | } |
| 1185 | |
| 1186 | |
| 1187 | /* ------------------------------------------------------------------------ */ |
| 1188 | /* Function: ipf_pr_icmp */ |
| 1189 | /* Returns: void */ |
| 1190 | /* Parameters: fin(I) - pointer to packet information */ |
| 1191 | /* */ |
| 1192 | /* IPv4 Only */ |
| 1193 | /* Do a sanity check on the packet for ICMP (v4). In nearly all cases, */ |
| 1194 | /* except extrememly bad packets, both type and code will be present. */ |
| 1195 | /* The expected minimum size of an ICMP packet is very much dependent on */ |
| 1196 | /* the type of it. */ |
| 1197 | /* */ |
| 1198 | /* XXX - other ICMP sanity checks? */ |
| 1199 | /* ------------------------------------------------------------------------ */ |
| 1200 | static INLINE void |
| 1201 | ipf_pr_icmp(fr_info_t *fin) |
| 1202 | { |
| 1203 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1204 | int minicmpsz = sizeof(struct icmp); |
| 1205 | icmphdr_t *icmp; |
| 1206 | ip_t *oip; |
| 1207 | |
| 1208 | ipf_pr_short(fin, ICMPERR_ICMPHLEN); |
| 1209 | |
| 1210 | if (fin->fin_off != 0) { |
| 1211 | LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_frag); |
| 1212 | return; |
| 1213 | } |
| 1214 | |
| 1215 | if (ipf_pr_pullup(fin, ICMPERR_ICMPHLEN) == -1) { |
| 1216 | LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_pullup); |
| 1217 | return; |
| 1218 | } |
| 1219 | |
| 1220 | icmp = fin->fin_dp; |
| 1221 | |
| 1222 | fin->fin_data[0] = *(u_short *)icmp; |
| 1223 | fin->fin_data[1] = icmp->icmp_id; |
| 1224 | |
| 1225 | switch (icmp->icmp_type) |
| 1226 | { |
| 1227 | case ICMP_ECHOREPLY : |
| 1228 | case ICMP_ECHO : |
| 1229 | /* Router discovery messaes - RFC 1256 */ |
| 1230 | case ICMP_ROUTERADVERT : |
| 1231 | case ICMP_ROUTERSOLICIT : |
| 1232 | fin->fin_flx |= FI_ICMPQUERY; |
| 1233 | minicmpsz = ICMP_MINLEN; |
| 1234 | break; |
| 1235 | /* |
| 1236 | * type(1) + code(1) + cksum(2) + id(2) seq(2) + |
| 1237 | * 3 * timestamp(3 * 4) |
| 1238 | */ |
| 1239 | case ICMP_TSTAMP : |
| 1240 | case ICMP_TSTAMPREPLY : |
| 1241 | fin->fin_flx |= FI_ICMPQUERY; |
| 1242 | minicmpsz = 20; |
| 1243 | break; |
| 1244 | /* |
| 1245 | * type(1) + code(1) + cksum(2) + id(2) seq(2) + |
| 1246 | * mask(4) |
| 1247 | */ |
| 1248 | case ICMP_IREQ : |
| 1249 | case ICMP_IREQREPLY : |
| 1250 | case ICMP_MASKREQ : |
| 1251 | case ICMP_MASKREPLY : |
| 1252 | fin->fin_flx |= FI_ICMPQUERY; |
| 1253 | minicmpsz = 12; |
| 1254 | break; |
| 1255 | /* |
| 1256 | * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) |
| 1257 | */ |
| 1258 | case ICMP_UNREACH : |
| 1259 | #ifdef icmp_nextmtu |
| 1260 | if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { |
| 1261 | if (icmp->icmp_nextmtu < softc->ipf_icmpminfragmtu) { |
| 1262 | fin->fin_flx |= FI_BAD; |
| 1263 | DT3(ipf_fi_bad_icmp_nextmtu, fr_info_t *, fin, u_int, icmp->icmp_nextmtu, u_int, softc->ipf_icmpminfragmtu); |
| 1264 | } |
| 1265 | } |
| 1266 | #endif |
| 1267 | case ICMP_SOURCEQUENCH : |
| 1268 | case ICMP_REDIRECT : |
| 1269 | case ICMP_TIMXCEED : |
| 1270 | case ICMP_PARAMPROB : |
| 1271 | fin->fin_flx |= FI_ICMPERR; |
| 1272 | if (ipf_coalesce(fin) != 1) { |
| 1273 | LBUMPD(ipf_stats[fin->fin_out], fr_icmp_coalesce); |
| 1274 | return; |
| 1275 | } |
| 1276 | |
| 1277 | /* |
| 1278 | * ICMP error packets should not be generated for IP |
| 1279 | * packets that are a fragment that isn't the first |
| 1280 | * fragment. |
| 1281 | */ |
| 1282 | oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); |
| 1283 | if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) { |
| 1284 | fin->fin_flx |= FI_BAD; |
| 1285 | DT2(ipf_fi_bad_icmp_err, fr_info_t, fin, u_int, (ntohs(oip->ip_off) & IP_OFFMASK)); |
| 1286 | } |
| 1287 | |
| 1288 | /* |
| 1289 | * If the destination of this packet doesn't match the |
| 1290 | * source of the original packet then this packet is |
| 1291 | * not correct. |
| 1292 | */ |
| 1293 | if (oip->ip_src.s_addr != fin->fin_daddr) { |
| 1294 | fin->fin_flx |= FI_BAD; |
| 1295 | DT1(ipf_fi_bad_src_ne_dst, fr_info_t *, fin); |
| 1296 | } |
| 1297 | break; |
| 1298 | default : |
| 1299 | break; |
| 1300 | } |
| 1301 | |
| 1302 | ipf_pr_short(fin, minicmpsz); |
| 1303 | |
| 1304 | ipf_checkv4sum(fin); |
| 1305 | } |
| 1306 | |
| 1307 | |
| 1308 | /* ------------------------------------------------------------------------ */ |
| 1309 | /* Function: ipf_pr_tcpcommon */ |
| 1310 | /* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */ |
| 1311 | /* Parameters: fin(I) - pointer to packet information */ |
| 1312 | /* */ |
| 1313 | /* TCP header sanity checking. Look for bad combinations of TCP flags, */ |
| 1314 | /* and make some checks with how they interact with other fields. */ |
| 1315 | /* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is */ |
| 1316 | /* valid and mark the packet as bad if not. */ |
| 1317 | /* ------------------------------------------------------------------------ */ |
| 1318 | static INLINE int |
| 1319 | ipf_pr_tcpcommon(fr_info_t *fin) |
| 1320 | { |
| 1321 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1322 | int flags, tlen; |
| 1323 | tcphdr_t *tcp; |
| 1324 | |
| 1325 | fin->fin_flx |= FI_TCPUDP; |
| 1326 | if (fin->fin_off != 0) { |
| 1327 | LBUMPD(ipf_stats[fin->fin_out], fr_tcp_frag); |
| 1328 | return 0; |
| 1329 | } |
| 1330 | |
| 1331 | if (ipf_pr_pullup(fin, sizeof(*tcp)) == -1) { |
| 1332 | LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); |
| 1333 | return -1; |
| 1334 | } |
| 1335 | |
| 1336 | tcp = fin->fin_dp; |
| 1337 | if (fin->fin_dlen > 3) { |
| 1338 | fin->fin_sport = ntohs(tcp->th_sport); |
| 1339 | fin->fin_dport = ntohs(tcp->th_dport); |
| 1340 | } |
| 1341 | |
| 1342 | if ((fin->fin_flx & FI_SHORT) != 0) { |
| 1343 | LBUMPD(ipf_stats[fin->fin_out], fr_tcp_short); |
| 1344 | return 1; |
| 1345 | } |
| 1346 | |
| 1347 | /* |
| 1348 | * Use of the TCP data offset *must* result in a value that is at |
| 1349 | * least the same size as the TCP header. |
| 1350 | */ |
| 1351 | tlen = TCP_OFF(tcp) << 2; |
| 1352 | if (tlen < sizeof(tcphdr_t)) { |
| 1353 | LBUMPD(ipf_stats[fin->fin_out], fr_tcp_small); |
| 1354 | fin->fin_flx |= FI_BAD; |
| 1355 | DT3(ipf_fi_bad_tlen, fr_info_t, fin, u_int, tlen, u_int, sizeof(tcphdr_t)); |
| 1356 | return 1; |
| 1357 | } |
| 1358 | |
| 1359 | flags = tcp->th_flags; |
| 1360 | fin->fin_tcpf = tcp->th_flags; |
| 1361 | |
| 1362 | /* |
| 1363 | * If the urgent flag is set, then the urgent pointer must |
| 1364 | * also be set and vice versa. Good TCP packets do not have |
| 1365 | * just one of these set. |
| 1366 | */ |
| 1367 | if ((flags & TH_URG) != 0 && (tcp->th_urp == 0)) { |
| 1368 | fin->fin_flx |= FI_BAD; |
| 1369 | DT3(ipf_fi_bad_th_urg, fr_info_t*, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); |
| 1370 | #if 0 |
| 1371 | } else if ((flags & TH_URG) == 0 && (tcp->th_urp != 0)) { |
| 1372 | /* |
| 1373 | * Ignore this case (#if 0) as it shows up in "real" |
| 1374 | * traffic with bogus values in the urgent pointer field. |
| 1375 | */ |
| 1376 | fin->fin_flx |= FI_BAD; |
| 1377 | DT3(ipf_fi_bad_th_urg0, fr_info_t *, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); |
| 1378 | #endif |
| 1379 | } else if (((flags & (TH_SYN|TH_FIN)) != 0) && |
| 1380 | ((flags & (TH_RST|TH_ACK)) == TH_RST)) { |
| 1381 | /* TH_FIN|TH_RST|TH_ACK seems to appear "naturally" */ |
| 1382 | fin->fin_flx |= FI_BAD; |
| 1383 | DT1(ipf_fi_bad_th_fin_rst_ack, fr_info_t, fin); |
| 1384 | #if 1 |
| 1385 | } else if (((flags & TH_SYN) != 0) && |
| 1386 | ((flags & (TH_URG|TH_PUSH)) != 0)) { |
| 1387 | /* |
| 1388 | * SYN with URG and PUSH set is not for normal TCP but it is |
| 1389 | * possible(?) with T/TCP...but who uses T/TCP? |
| 1390 | */ |
| 1391 | fin->fin_flx |= FI_BAD; |
| 1392 | DT1(ipf_fi_bad_th_syn_urg_psh, fr_info_t *, fin); |
| 1393 | #endif |
| 1394 | } else if (!(flags & TH_ACK)) { |
| 1395 | /* |
| 1396 | * If the ack bit isn't set, then either the SYN or |
| 1397 | * RST bit must be set. If the SYN bit is set, then |
| 1398 | * we expect the ACK field to be 0. If the ACK is |
| 1399 | * not set and if URG, PSH or FIN are set, consdier |
| 1400 | * that to indicate a bad TCP packet. |
| 1401 | */ |
| 1402 | if ((flags == TH_SYN) && (tcp->th_ack != 0)) { |
| 1403 | /* |
| 1404 | * Cisco PIX sets the ACK field to a random value. |
| 1405 | * In light of this, do not set FI_BAD until a patch |
| 1406 | * is available from Cisco to ensure that |
| 1407 | * interoperability between existing systems is |
| 1408 | * achieved. |
| 1409 | */ |
| 1410 | /*fin->fin_flx |= FI_BAD*/; |
| 1411 | /*DT1(ipf_fi_bad_th_syn_ack, fr_info_t *, fin);*/ |
| 1412 | } else if (!(flags & (TH_RST|TH_SYN))) { |
| 1413 | fin->fin_flx |= FI_BAD; |
| 1414 | DT1(ipf_fi_bad_th_rst_syn, fr_info_t *, fin); |
| 1415 | } else if ((flags & (TH_URG|TH_PUSH|TH_FIN)) != 0) { |
| 1416 | fin->fin_flx |= FI_BAD; |
| 1417 | DT1(ipf_fi_bad_th_urg_push_fin, fr_info_t *, fin); |
| 1418 | } |
| 1419 | } |
| 1420 | if (fin->fin_flx & FI_BAD) { |
| 1421 | LBUMPD(ipf_stats[fin->fin_out], fr_tcp_bad_flags); |
| 1422 | return 1; |
| 1423 | } |
| 1424 | |
| 1425 | /* |
| 1426 | * At this point, it's not exactly clear what is to be gained by |
| 1427 | * marking up which TCP options are and are not present. The one we |
| 1428 | * are most interested in is the TCP window scale. This is only in |
| 1429 | * a SYN packet [RFC1323] so we don't need this here...? |
| 1430 | * Now if we were to analyse the header for passive fingerprinting, |
| 1431 | * then that might add some weight to adding this... |
| 1432 | */ |
| 1433 | if (tlen == sizeof(tcphdr_t)) { |
| 1434 | return 0; |
| 1435 | } |
| 1436 | |
| 1437 | if (ipf_pr_pullup(fin, tlen) == -1) { |
| 1438 | LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); |
| 1439 | return -1; |
| 1440 | } |
| 1441 | |
| 1442 | #if 0 |
| 1443 | tcp = fin->fin_dp; |
| 1444 | ip = fin->fin_ip; |
| 1445 | s = (u_char *)(tcp + 1); |
| 1446 | off = IP_HL(ip) << 2; |
| 1447 | # ifdef _KERNEL |
| 1448 | if (fin->fin_mp != NULL) { |
| 1449 | mb_t *m = *fin->fin_mp; |
| 1450 | |
| 1451 | if (off + tlen > M_LEN(m)) |
| 1452 | return; |
| 1453 | } |
| 1454 | # endif |
| 1455 | for (tlen -= (int)sizeof(*tcp); tlen > 0; ) { |
| 1456 | opt = *s; |
| 1457 | if (opt == '\0') |
| 1458 | break; |
| 1459 | else if (opt == TCPOPT_NOP) |
| 1460 | ol = 1; |
| 1461 | else { |
| 1462 | if (tlen < 2) |
| 1463 | break; |
| 1464 | ol = (int)*(s + 1); |
| 1465 | if (ol < 2 || ol > tlen) |
| 1466 | break; |
| 1467 | } |
| 1468 | |
| 1469 | for (i = 9, mv = 4; mv >= 0; ) { |
| 1470 | op = ipopts + i; |
| 1471 | if (opt == (u_char)op->ol_val) { |
| 1472 | optmsk |= op->ol_bit; |
| 1473 | break; |
| 1474 | } |
| 1475 | } |
| 1476 | tlen -= ol; |
| 1477 | s += ol; |
| 1478 | } |
| 1479 | #endif /* 0 */ |
| 1480 | |
| 1481 | return 0; |
| 1482 | } |
| 1483 | |
| 1484 | |
| 1485 | |
| 1486 | /* ------------------------------------------------------------------------ */ |
| 1487 | /* Function: ipf_pr_udpcommon */ |
| 1488 | /* Returns: int - 0 = header ok, 1 = bad packet */ |
| 1489 | /* Parameters: fin(I) - pointer to packet information */ |
| 1490 | /* */ |
| 1491 | /* Extract the UDP source and destination ports, if present. If compiled */ |
| 1492 | /* with IPFILTER_CKSUM, check to see if the UDP checksum is valid. */ |
| 1493 | /* ------------------------------------------------------------------------ */ |
| 1494 | static INLINE int |
| 1495 | ipf_pr_udpcommon(fr_info_t *fin) |
| 1496 | { |
| 1497 | udphdr_t *udp; |
| 1498 | |
| 1499 | fin->fin_flx |= FI_TCPUDP; |
| 1500 | |
| 1501 | if (!fin->fin_off && (fin->fin_dlen > 3)) { |
| 1502 | if (ipf_pr_pullup(fin, sizeof(*udp)) == -1) { |
| 1503 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1504 | |
| 1505 | fin->fin_flx |= FI_SHORT; |
| 1506 | LBUMPD(ipf_stats[fin->fin_out], fr_udp_pullup); |
| 1507 | return 1; |
| 1508 | } |
| 1509 | |
| 1510 | udp = fin->fin_dp; |
| 1511 | |
| 1512 | fin->fin_sport = ntohs(udp->uh_sport); |
| 1513 | fin->fin_dport = ntohs(udp->uh_dport); |
| 1514 | } |
| 1515 | |
| 1516 | return 0; |
| 1517 | } |
| 1518 | |
| 1519 | |
| 1520 | /* ------------------------------------------------------------------------ */ |
| 1521 | /* Function: ipf_pr_tcp */ |
| 1522 | /* Returns: void */ |
| 1523 | /* Parameters: fin(I) - pointer to packet information */ |
| 1524 | /* */ |
| 1525 | /* IPv4 Only */ |
| 1526 | /* Analyse the packet for IPv4/TCP properties. */ |
| 1527 | /* ------------------------------------------------------------------------ */ |
| 1528 | static INLINE void |
| 1529 | ipf_pr_tcp(fr_info_t *fin) |
| 1530 | { |
| 1531 | |
| 1532 | ipf_pr_short(fin, sizeof(tcphdr_t)); |
| 1533 | |
| 1534 | if (ipf_pr_tcpcommon(fin) == 0) |
| 1535 | ipf_checkv4sum(fin); |
| 1536 | } |
| 1537 | |
| 1538 | |
| 1539 | /* ------------------------------------------------------------------------ */ |
| 1540 | /* Function: ipf_pr_udp */ |
| 1541 | /* Returns: void */ |
| 1542 | /* Parameters: fin(I) - pointer to packet information */ |
| 1543 | /* */ |
| 1544 | /* IPv4 Only */ |
| 1545 | /* Analyse the packet for IPv4/UDP properties. */ |
| 1546 | /* ------------------------------------------------------------------------ */ |
| 1547 | static INLINE void |
| 1548 | ipf_pr_udp(fr_info_t *fin) |
| 1549 | { |
| 1550 | |
| 1551 | ipf_pr_short(fin, sizeof(udphdr_t)); |
| 1552 | |
| 1553 | if (ipf_pr_udpcommon(fin) == 0) |
| 1554 | ipf_checkv4sum(fin); |
| 1555 | } |
| 1556 | |
| 1557 | |
| 1558 | /* ------------------------------------------------------------------------ */ |
| 1559 | /* Function: ipf_pr_esp */ |
| 1560 | /* Returns: void */ |
| 1561 | /* Parameters: fin(I) - pointer to packet information */ |
| 1562 | /* */ |
| 1563 | /* Analyse the packet for ESP properties. */ |
| 1564 | /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ |
| 1565 | /* even though the newer ESP packets must also have a sequence number that */ |
| 1566 | /* is 32bits as well, it is not possible(?) to determine the version from a */ |
| 1567 | /* simple packet header. */ |
| 1568 | /* ------------------------------------------------------------------------ */ |
| 1569 | static INLINE void |
| 1570 | ipf_pr_esp(fr_info_t *fin) |
| 1571 | { |
| 1572 | |
| 1573 | if (fin->fin_off == 0) { |
| 1574 | ipf_pr_short(fin, 8); |
| 1575 | if (ipf_pr_pullup(fin, 8) == -1) { |
| 1576 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1577 | |
| 1578 | LBUMPD(ipf_stats[fin->fin_out], fr_v4_esp_pullup); |
| 1579 | } |
| 1580 | } |
| 1581 | } |
| 1582 | |
| 1583 | |
| 1584 | /* ------------------------------------------------------------------------ */ |
| 1585 | /* Function: ipf_pr_ah */ |
| 1586 | /* Returns: int - value of the next header or IPPROTO_NONE if error */ |
| 1587 | /* Parameters: fin(I) - pointer to packet information */ |
| 1588 | /* */ |
| 1589 | /* Analyse the packet for AH properties. */ |
| 1590 | /* The minimum length is taken to be the combination of all fields in the */ |
| 1591 | /* header being present and no authentication data (null algorithm used.) */ |
| 1592 | /* ------------------------------------------------------------------------ */ |
| 1593 | static INLINE int |
| 1594 | ipf_pr_ah(fr_info_t *fin) |
| 1595 | { |
| 1596 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1597 | authhdr_t *ah; |
| 1598 | int len; |
| 1599 | |
| 1600 | fin->fin_flx |= FI_AH; |
| 1601 | ipf_pr_short(fin, sizeof(*ah)); |
| 1602 | |
| 1603 | if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) { |
| 1604 | LBUMPD(ipf_stats[fin->fin_out], fr_v4_ah_bad); |
| 1605 | return IPPROTO_NONE; |
| 1606 | } |
| 1607 | |
| 1608 | if (ipf_pr_pullup(fin, sizeof(*ah)) == -1) { |
| 1609 | DT(fr_v4_ah_pullup_1); |
| 1610 | LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); |
| 1611 | return IPPROTO_NONE; |
| 1612 | } |
| 1613 | |
| 1614 | ah = (authhdr_t *)fin->fin_dp; |
| 1615 | |
| 1616 | len = (ah->ah_plen + 2) << 2; |
| 1617 | ipf_pr_short(fin, len); |
| 1618 | if (ipf_pr_pullup(fin, len) == -1) { |
| 1619 | DT(fr_v4_ah_pullup_2); |
| 1620 | LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); |
| 1621 | return IPPROTO_NONE; |
| 1622 | } |
| 1623 | |
| 1624 | /* |
| 1625 | * Adjust fin_dp and fin_dlen for skipping over the authentication |
| 1626 | * header. |
| 1627 | */ |
| 1628 | fin->fin_dp = (char *)fin->fin_dp + len; |
| 1629 | fin->fin_dlen -= len; |
| 1630 | return ah->ah_next; |
| 1631 | } |
| 1632 | |
| 1633 | |
| 1634 | /* ------------------------------------------------------------------------ */ |
| 1635 | /* Function: ipf_pr_gre */ |
| 1636 | /* Returns: void */ |
| 1637 | /* Parameters: fin(I) - pointer to packet information */ |
| 1638 | /* */ |
| 1639 | /* Analyse the packet for GRE properties. */ |
| 1640 | /* ------------------------------------------------------------------------ */ |
| 1641 | static INLINE void |
| 1642 | ipf_pr_gre(fr_info_t *fin) |
| 1643 | { |
| 1644 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1645 | grehdr_t *gre; |
| 1646 | |
| 1647 | ipf_pr_short(fin, sizeof(grehdr_t)); |
| 1648 | |
| 1649 | if (fin->fin_off != 0) { |
| 1650 | LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_frag); |
| 1651 | return; |
| 1652 | } |
| 1653 | |
| 1654 | if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { |
| 1655 | LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_pullup); |
| 1656 | return; |
| 1657 | } |
| 1658 | |
| 1659 | gre = fin->fin_dp; |
| 1660 | if (GRE_REV(gre->gr_flags) == 1) |
| 1661 | fin->fin_data[0] = gre->gr_call; |
| 1662 | } |
| 1663 | |
| 1664 | |
| 1665 | /* ------------------------------------------------------------------------ */ |
| 1666 | /* Function: ipf_pr_ipv4hdr */ |
| 1667 | /* Returns: void */ |
| 1668 | /* Parameters: fin(I) - pointer to packet information */ |
| 1669 | /* */ |
| 1670 | /* IPv4 Only */ |
| 1671 | /* Analyze the IPv4 header and set fields in the fr_info_t structure. */ |
| 1672 | /* Check all options present and flag their presence if any exist. */ |
| 1673 | /* ------------------------------------------------------------------------ */ |
| 1674 | static INLINE void |
| 1675 | ipf_pr_ipv4hdr(fr_info_t *fin) |
| 1676 | { |
| 1677 | u_short optmsk = 0, secmsk = 0, auth = 0; |
| 1678 | int hlen, ol, mv, p, i; |
| 1679 | const struct optlist *op; |
| 1680 | u_char *s, opt; |
| 1681 | u_short off; |
| 1682 | fr_ip_t *fi; |
| 1683 | ip_t *ip; |
| 1684 | |
| 1685 | fi = &fin->fin_fi; |
| 1686 | hlen = fin->fin_hlen; |
| 1687 | |
| 1688 | ip = fin->fin_ip; |
| 1689 | p = ip->ip_p; |
| 1690 | fi->fi_p = p; |
| 1691 | fin->fin_crc = p; |
| 1692 | fi->fi_tos = ip->ip_tos; |
| 1693 | fin->fin_id = ip->ip_id; |
| 1694 | off = ntohs(ip->ip_off); |
| 1695 | |
| 1696 | /* Get both TTL and protocol */ |
| 1697 | fi->fi_p = ip->ip_p; |
| 1698 | fi->fi_ttl = ip->ip_ttl; |
| 1699 | |
| 1700 | /* Zero out bits not used in IPv6 address */ |
| 1701 | fi->fi_src.i6[1] = 0; |
| 1702 | fi->fi_src.i6[2] = 0; |
| 1703 | fi->fi_src.i6[3] = 0; |
| 1704 | fi->fi_dst.i6[1] = 0; |
| 1705 | fi->fi_dst.i6[2] = 0; |
| 1706 | fi->fi_dst.i6[3] = 0; |
| 1707 | |
| 1708 | fi->fi_saddr = ip->ip_src.s_addr; |
| 1709 | fin->fin_crc += fi->fi_saddr; |
| 1710 | fi->fi_daddr = ip->ip_dst.s_addr; |
| 1711 | fin->fin_crc += fi->fi_daddr; |
| 1712 | if (IN_CLASSD(ntohl(fi->fi_daddr))) |
| 1713 | fin->fin_flx |= FI_MULTICAST|FI_MBCAST; |
| 1714 | |
| 1715 | /* |
| 1716 | * set packet attribute flags based on the offset and |
| 1717 | * calculate the byte offset that it represents. |
| 1718 | */ |
| 1719 | off &= IP_MF|IP_OFFMASK; |
| 1720 | if (off != 0) { |
| 1721 | int morefrag = off & IP_MF; |
| 1722 | |
| 1723 | fi->fi_flx |= FI_FRAG; |
| 1724 | off &= IP_OFFMASK; |
| 1725 | if (off != 0) { |
| 1726 | fin->fin_flx |= FI_FRAGBODY; |
| 1727 | off <<= 3; |
| 1728 | if ((off + fin->fin_dlen > 65535) || |
| 1729 | (fin->fin_dlen == 0) || |
| 1730 | ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) { |
| 1731 | /* |
| 1732 | * The length of the packet, starting at its |
| 1733 | * offset cannot exceed 65535 (0xffff) as the |
| 1734 | * length of an IP packet is only 16 bits. |
| 1735 | * |
| 1736 | * Any fragment that isn't the last fragment |
| 1737 | * must have a length greater than 0 and it |
| 1738 | * must be an even multiple of 8. |
| 1739 | */ |
| 1740 | fi->fi_flx |= FI_BAD; |
| 1741 | DT1(ipf_fi_bad_fragbody_gt_65535, fr_info_t *, fin); |
| 1742 | } |
| 1743 | } |
| 1744 | } |
| 1745 | fin->fin_off = off; |
| 1746 | |
| 1747 | /* |
| 1748 | * Call per-protocol setup and checking |
| 1749 | */ |
| 1750 | if (p == IPPROTO_AH) { |
| 1751 | /* |
| 1752 | * Treat AH differently because we expect there to be another |
| 1753 | * layer 4 header after it. |
| 1754 | */ |
| 1755 | p = ipf_pr_ah(fin); |
| 1756 | } |
| 1757 | |
| 1758 | switch (p) |
| 1759 | { |
| 1760 | case IPPROTO_UDP : |
| 1761 | ipf_pr_udp(fin); |
| 1762 | break; |
| 1763 | case IPPROTO_TCP : |
| 1764 | ipf_pr_tcp(fin); |
| 1765 | break; |
| 1766 | case IPPROTO_ICMP : |
| 1767 | ipf_pr_icmp(fin); |
| 1768 | break; |
| 1769 | case IPPROTO_ESP : |
| 1770 | ipf_pr_esp(fin); |
| 1771 | break; |
| 1772 | case IPPROTO_GRE : |
| 1773 | ipf_pr_gre(fin); |
| 1774 | break; |
| 1775 | } |
| 1776 | |
| 1777 | ip = fin->fin_ip; |
| 1778 | if (ip == NULL) |
| 1779 | return; |
| 1780 | |
| 1781 | /* |
| 1782 | * If it is a standard IP header (no options), set the flag fields |
| 1783 | * which relate to options to 0. |
| 1784 | */ |
| 1785 | if (hlen == sizeof(*ip)) { |
| 1786 | fi->fi_optmsk = 0; |
| 1787 | fi->fi_secmsk = 0; |
| 1788 | fi->fi_auth = 0; |
| 1789 | return; |
| 1790 | } |
| 1791 | |
| 1792 | /* |
| 1793 | * So the IP header has some IP options attached. Walk the entire |
| 1794 | * list of options present with this packet and set flags to indicate |
| 1795 | * which ones are here and which ones are not. For the somewhat out |
| 1796 | * of date and obscure security classification options, set a flag to |
| 1797 | * represent which classification is present. |
| 1798 | */ |
| 1799 | fi->fi_flx |= FI_OPTIONS; |
| 1800 | |
| 1801 | for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { |
| 1802 | opt = *s; |
| 1803 | if (opt == '\0') |
| 1804 | break; |
| 1805 | else if (opt == IPOPT_NOP) |
| 1806 | ol = 1; |
| 1807 | else { |
| 1808 | if (hlen < 2) |
| 1809 | break; |
| 1810 | ol = (int)*(s + 1); |
| 1811 | if (ol < 2 || ol > hlen) |
| 1812 | break; |
| 1813 | } |
| 1814 | for (i = 9, mv = 4; mv >= 0; ) { |
| 1815 | op = ipopts + i; |
| 1816 | |
| 1817 | if ((opt == (u_char)op->ol_val) && (ol > 4)) { |
| 1818 | u_32_t doi; |
| 1819 | |
| 1820 | switch (opt) |
| 1821 | { |
| 1822 | case IPOPT_SECURITY : |
| 1823 | if (optmsk & op->ol_bit) { |
| 1824 | fin->fin_flx |= FI_BAD; |
| 1825 | DT2(ipf_fi_bad_ipopt_security, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); |
| 1826 | } else { |
| 1827 | doi = ipf_checkripso(s); |
| 1828 | secmsk = doi >> 16; |
| 1829 | auth = doi & 0xffff; |
| 1830 | } |
| 1831 | break; |
| 1832 | |
| 1833 | case IPOPT_CIPSO : |
| 1834 | |
| 1835 | if (optmsk & op->ol_bit) { |
| 1836 | fin->fin_flx |= FI_BAD; |
| 1837 | DT2(ipf_fi_bad_ipopt_cipso, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); |
| 1838 | } else { |
| 1839 | doi = ipf_checkcipso(fin, |
| 1840 | s, ol); |
| 1841 | secmsk = doi >> 16; |
| 1842 | auth = doi & 0xffff; |
| 1843 | } |
| 1844 | break; |
| 1845 | } |
| 1846 | optmsk |= op->ol_bit; |
| 1847 | } |
| 1848 | |
| 1849 | if (opt < op->ol_val) |
| 1850 | i -= mv; |
| 1851 | else |
| 1852 | i += mv; |
| 1853 | mv--; |
| 1854 | } |
| 1855 | hlen -= ol; |
| 1856 | s += ol; |
| 1857 | } |
| 1858 | |
| 1859 | /* |
| 1860 | * |
| 1861 | */ |
| 1862 | if (auth && !(auth & 0x0100)) |
| 1863 | auth &= 0xff00; |
| 1864 | fi->fi_optmsk = optmsk; |
| 1865 | fi->fi_secmsk = secmsk; |
| 1866 | fi->fi_auth = auth; |
| 1867 | } |
| 1868 | |
| 1869 | |
| 1870 | /* ------------------------------------------------------------------------ */ |
| 1871 | /* Function: ipf_checkripso */ |
| 1872 | /* Returns: void */ |
| 1873 | /* Parameters: s(I) - pointer to start of RIPSO option */ |
| 1874 | /* */ |
| 1875 | /* ------------------------------------------------------------------------ */ |
| 1876 | static u_32_t |
| 1877 | ipf_checkripso(u_char *s) |
| 1878 | { |
| 1879 | const struct optlist *sp; |
| 1880 | u_short secmsk = 0, auth = 0; |
| 1881 | u_char sec; |
| 1882 | int j, m; |
| 1883 | |
| 1884 | sec = *(s + 2); /* classification */ |
| 1885 | for (j = 3, m = 2; m >= 0; ) { |
| 1886 | sp = secopt + j; |
| 1887 | if (sec == sp->ol_val) { |
| 1888 | secmsk |= sp->ol_bit; |
| 1889 | auth = *(s + 3); |
| 1890 | auth *= 256; |
| 1891 | auth += *(s + 4); |
| 1892 | break; |
| 1893 | } |
| 1894 | if (sec < sp->ol_val) |
| 1895 | j -= m; |
| 1896 | else |
| 1897 | j += m; |
| 1898 | m--; |
| 1899 | } |
| 1900 | |
| 1901 | return (secmsk << 16) | auth; |
| 1902 | } |
| 1903 | |
| 1904 | |
| 1905 | /* ------------------------------------------------------------------------ */ |
| 1906 | /* Function: ipf_checkcipso */ |
| 1907 | /* Returns: u_32_t - 0 = failure, else the doi from the header */ |
| 1908 | /* Parameters: fin(IO) - pointer to packet information */ |
| 1909 | /* s(I) - pointer to start of CIPSO option */ |
| 1910 | /* ol(I) - length of CIPSO option field */ |
| 1911 | /* */ |
| 1912 | /* This function returns the domain of integrity (DOI) field from the CIPSO */ |
| 1913 | /* header and returns that whilst also storing the highest sensitivity */ |
| 1914 | /* value found in the fr_info_t structure. */ |
| 1915 | /* */ |
| 1916 | /* No attempt is made to extract the category bitmaps as these are defined */ |
| 1917 | /* by the user (rather than the protocol) and can be rather numerous on the */ |
| 1918 | /* end nodes. */ |
| 1919 | /* ------------------------------------------------------------------------ */ |
| 1920 | static u_32_t |
| 1921 | ipf_checkcipso(fr_info_t *fin, u_char *s, int ol) |
| 1922 | { |
| 1923 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 1924 | fr_ip_t *fi; |
| 1925 | u_32_t doi; |
| 1926 | u_char *t, tag, tlen, sensitivity; |
| 1927 | int len; |
| 1928 | |
| 1929 | if (ol < 6 || ol > 40) { |
| 1930 | LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_bad); |
| 1931 | fin->fin_flx |= FI_BAD; |
| 1932 | DT2(ipf_fi_bad_checkcipso_ol, fr_info_t *, fin, u_int, ol); |
| 1933 | return 0; |
| 1934 | } |
| 1935 | |
| 1936 | fi = &fin->fin_fi; |
| 1937 | fi->fi_sensitivity = 0; |
| 1938 | /* |
| 1939 | * The DOI field MUST be there. |
| 1940 | */ |
| 1941 | bcopy(s + 2, &doi, sizeof(doi)); |
| 1942 | |
| 1943 | t = (u_char *)s + 6; |
| 1944 | for (len = ol - 6; len >= 2; len -= tlen, t+= tlen) { |
| 1945 | tag = *t; |
| 1946 | tlen = *(t + 1); |
| 1947 | if (tlen > len || tlen < 4 || tlen > 34) { |
| 1948 | LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_tlen); |
| 1949 | fin->fin_flx |= FI_BAD; |
| 1950 | DT2(ipf_fi_bad_checkcipso_tlen, fr_info_t *, fin, u_int, tlen); |
| 1951 | return 0; |
| 1952 | } |
| 1953 | |
| 1954 | sensitivity = 0; |
| 1955 | /* |
| 1956 | * Tag numbers 0, 1, 2, 5 are laid out in the CIPSO Internet |
| 1957 | * draft (16 July 1992) that has expired. |
| 1958 | */ |
| 1959 | if (tag == 0) { |
| 1960 | fin->fin_flx |= FI_BAD; |
| 1961 | DT2(ipf_fi_bad_checkcipso_tag, fr_info_t *, fin, u_int, tag); |
| 1962 | continue; |
| 1963 | } else if (tag == 1) { |
| 1964 | if (*(t + 2) != 0) { |
| 1965 | fin->fin_flx |= FI_BAD; |
| 1966 | DT2(ipf_fi_bad_checkcipso_tag1_t2, fr_info_t *, fin, u_int, (*t + 2)); |
| 1967 | continue; |
| 1968 | } |
| 1969 | sensitivity = *(t + 3); |
| 1970 | /* Category bitmap for categories 0-239 */ |
| 1971 | |
| 1972 | } else if (tag == 4) { |
| 1973 | if (*(t + 2) != 0) { |
| 1974 | fin->fin_flx |= FI_BAD; |
| 1975 | DT2(ipf_fi_bad_checkcipso_tag4_t2, fr_info_t *, fin, u_int, (*t + 2)); |
| 1976 | continue; |
| 1977 | } |
| 1978 | sensitivity = *(t + 3); |
| 1979 | /* Enumerated categories, 16bits each, upto 15 */ |
| 1980 | |
| 1981 | } else if (tag == 5) { |
| 1982 | if (*(t + 2) != 0) { |
| 1983 | fin->fin_flx |= FI_BAD; |
| 1984 | DT2(ipf_fi_bad_checkcipso_tag5_t2, fr_info_t *, fin, u_int, (*t + 2)); |
| 1985 | continue; |
| 1986 | } |
| 1987 | sensitivity = *(t + 3); |
| 1988 | /* Range of categories (2*16bits), up to 7 pairs */ |
| 1989 | |
| 1990 | } else if (tag > 127) { |
| 1991 | /* Custom defined DOI */ |
| 1992 | ; |
| 1993 | } else { |
| 1994 | DT2(ipf_fi_bad_checkcipso_tag127, fr_info_t *, fin, u_int, tag); |
| 1995 | fin->fin_flx |= FI_BAD; |
| 1996 | continue; |
| 1997 | } |
| 1998 | |
| 1999 | if (sensitivity > fi->fi_sensitivity) |
| 2000 | fi->fi_sensitivity = sensitivity; |
| 2001 | } |
| 2002 | |
| 2003 | return doi; |
| 2004 | } |
| 2005 | |
| 2006 | |
| 2007 | /* ------------------------------------------------------------------------ */ |
| 2008 | /* Function: ipf_makefrip */ |
| 2009 | /* Returns: int - 0 == packet ok, -1 == packet freed */ |
| 2010 | /* Parameters: hlen(I) - length of IP packet header */ |
| 2011 | /* ip(I) - pointer to the IP header */ |
| 2012 | /* fin(IO) - pointer to packet information */ |
| 2013 | /* */ |
| 2014 | /* Compact the IP header into a structure which contains just the info. */ |
| 2015 | /* which is useful for comparing IP headers with and store this information */ |
| 2016 | /* in the fr_info_t structure pointer to by fin. At present, it is assumed */ |
| 2017 | /* this function will be called with either an IPv4 or IPv6 packet. */ |
| 2018 | /* ------------------------------------------------------------------------ */ |
| 2019 | int |
| 2020 | ipf_makefrip(int hlen, ip_t *ip, fr_info_t *fin) |
| 2021 | { |
| 2022 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 2023 | int v; |
| 2024 | |
| 2025 | fin->fin_depth = 0; |
| 2026 | fin->fin_hlen = (u_short)hlen; |
| 2027 | fin->fin_ip = ip; |
| 2028 | fin->fin_rule = 0xffffffff; |
| 2029 | fin->fin_group[0] = -1; |
| 2030 | fin->fin_group[1] = '\0'; |
| 2031 | fin->fin_dp = (char *)ip + hlen; |
| 2032 | |
| 2033 | v = fin->fin_v; |
| 2034 | if (v == 4) { |
| 2035 | fin->fin_plen = ntohs(ip->ip_len); |
| 2036 | fin->fin_dlen = fin->fin_plen - hlen; |
| 2037 | ipf_pr_ipv4hdr(fin); |
| 2038 | #ifdef USE_INET6 |
| 2039 | } else if (v == 6) { |
| 2040 | fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen); |
| 2041 | fin->fin_dlen = fin->fin_plen; |
| 2042 | fin->fin_plen += hlen; |
| 2043 | |
| 2044 | ipf_pr_ipv6hdr(fin); |
| 2045 | #endif |
| 2046 | } |
| 2047 | if (fin->fin_ip == NULL) { |
| 2048 | LBUMP(ipf_stats[fin->fin_out].fr_ip_freed); |
| 2049 | return -1; |
| 2050 | } |
| 2051 | return 0; |
| 2052 | } |
| 2053 | |
| 2054 | |
| 2055 | /* ------------------------------------------------------------------------ */ |
| 2056 | /* Function: ipf_portcheck */ |
| 2057 | /* Returns: int - 1 == port matched, 0 == port match failed */ |
| 2058 | /* Parameters: frp(I) - pointer to port check `expression' */ |
| 2059 | /* pop(I) - port number to evaluate */ |
| 2060 | /* */ |
| 2061 | /* Perform a comparison of a port number against some other(s), using a */ |
| 2062 | /* structure with compare information stored in it. */ |
| 2063 | /* ------------------------------------------------------------------------ */ |
| 2064 | static INLINE int |
| 2065 | ipf_portcheck(frpcmp_t *frp, u_32_t pop) |
| 2066 | { |
| 2067 | int err = 1; |
| 2068 | u_32_t po; |
| 2069 | |
| 2070 | po = frp->frp_port; |
| 2071 | |
| 2072 | /* |
| 2073 | * Do opposite test to that required and continue if that succeeds. |
| 2074 | */ |
| 2075 | switch (frp->frp_cmp) |
| 2076 | { |
| 2077 | case FR_EQUAL : |
| 2078 | if (pop != po) /* EQUAL */ |
| 2079 | err = 0; |
| 2080 | break; |
| 2081 | case FR_NEQUAL : |
| 2082 | if (pop == po) /* NOTEQUAL */ |
| 2083 | err = 0; |
| 2084 | break; |
| 2085 | case FR_LESST : |
| 2086 | if (pop >= po) /* LESSTHAN */ |
| 2087 | err = 0; |
| 2088 | break; |
| 2089 | case FR_GREATERT : |
| 2090 | if (pop <= po) /* GREATERTHAN */ |
| 2091 | err = 0; |
| 2092 | break; |
| 2093 | case FR_LESSTE : |
| 2094 | if (pop > po) /* LT or EQ */ |
| 2095 | err = 0; |
| 2096 | break; |
| 2097 | case FR_GREATERTE : |
| 2098 | if (pop < po) /* GT or EQ */ |
| 2099 | err = 0; |
| 2100 | break; |
| 2101 | case FR_OUTRANGE : |
| 2102 | if (pop >= po && pop <= frp->frp_top) /* Out of range */ |
| 2103 | err = 0; |
| 2104 | break; |
| 2105 | case FR_INRANGE : |
| 2106 | if (pop <= po || pop >= frp->frp_top) /* In range */ |
| 2107 | err = 0; |
| 2108 | break; |
| 2109 | case FR_INCRANGE : |
| 2110 | if (pop < po || pop > frp->frp_top) /* Inclusive range */ |
| 2111 | err = 0; |
| 2112 | break; |
| 2113 | default : |
| 2114 | break; |
| 2115 | } |
| 2116 | return err; |
| 2117 | } |
| 2118 | |
| 2119 | |
| 2120 | /* ------------------------------------------------------------------------ */ |
| 2121 | /* Function: ipf_tcpudpchk */ |
| 2122 | /* Returns: int - 1 == protocol matched, 0 == check failed */ |
| 2123 | /* Parameters: fda(I) - pointer to packet information */ |
| 2124 | /* ft(I) - pointer to structure with comparison data */ |
| 2125 | /* */ |
| 2126 | /* Compares the current pcket (assuming it is TCP/UDP) information with a */ |
| 2127 | /* structure containing information that we want to match against. */ |
| 2128 | /* ------------------------------------------------------------------------ */ |
| 2129 | int |
| 2130 | ipf_tcpudpchk(fr_ip_t *fi, frtuc_t *ft) |
| 2131 | { |
| 2132 | int err = 1; |
| 2133 | |
| 2134 | /* |
| 2135 | * Both ports should *always* be in the first fragment. |
| 2136 | * So far, I cannot find any cases where they can not be. |
| 2137 | * |
| 2138 | * compare destination ports |
| 2139 | */ |
| 2140 | if (ft->ftu_dcmp) |
| 2141 | err = ipf_portcheck(&ft->ftu_dst, fi->fi_ports[1]); |
| 2142 | |
| 2143 | /* |
| 2144 | * compare source ports |
| 2145 | */ |
| 2146 | if (err && ft->ftu_scmp) |
| 2147 | err = ipf_portcheck(&ft->ftu_src, fi->fi_ports[0]); |
| 2148 | |
| 2149 | /* |
| 2150 | * If we don't have all the TCP/UDP header, then how can we |
| 2151 | * expect to do any sort of match on it ? If we were looking for |
| 2152 | * TCP flags, then NO match. If not, then match (which should |
| 2153 | * satisfy the "short" class too). |
| 2154 | */ |
| 2155 | if (err && (fi->fi_p == IPPROTO_TCP)) { |
| 2156 | if (fi->fi_flx & FI_SHORT) |
| 2157 | return !(ft->ftu_tcpf | ft->ftu_tcpfm); |
| 2158 | /* |
| 2159 | * Match the flags ? If not, abort this match. |
| 2160 | */ |
| 2161 | if (ft->ftu_tcpfm && |
| 2162 | ft->ftu_tcpf != (fi->fi_tcpf & ft->ftu_tcpfm)) { |
| 2163 | FR_DEBUG(("f. %#x & %#x != %#x\n" , fi->fi_tcpf, |
| 2164 | ft->ftu_tcpfm, ft->ftu_tcpf)); |
| 2165 | err = 0; |
| 2166 | } |
| 2167 | } |
| 2168 | return err; |
| 2169 | } |
| 2170 | |
| 2171 | |
| 2172 | /* ------------------------------------------------------------------------ */ |
| 2173 | /* Function: ipf_check_ipf */ |
| 2174 | /* Returns: int - 0 == match, else no match */ |
| 2175 | /* Parameters: fin(I) - pointer to packet information */ |
| 2176 | /* fr(I) - pointer to filter rule */ |
| 2177 | /* portcmp(I) - flag indicating whether to attempt matching on */ |
| 2178 | /* TCP/UDP port data. */ |
| 2179 | /* */ |
| 2180 | /* Check to see if a packet matches an IPFilter rule. Checks of addresses, */ |
| 2181 | /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */ |
| 2182 | /* this function. */ |
| 2183 | /* ------------------------------------------------------------------------ */ |
| 2184 | static INLINE int |
| 2185 | ipf_check_ipf(fr_info_t *fin, frentry_t *fr, int portcmp) |
| 2186 | { |
| 2187 | u_32_t *ld, *lm, *lip; |
| 2188 | fripf_t *fri; |
| 2189 | fr_ip_t *fi; |
| 2190 | int i; |
| 2191 | |
| 2192 | fi = &fin->fin_fi; |
| 2193 | fri = fr->fr_ipf; |
| 2194 | lip = (u_32_t *)fi; |
| 2195 | lm = (u_32_t *)&fri->fri_mip; |
| 2196 | ld = (u_32_t *)&fri->fri_ip; |
| 2197 | |
| 2198 | /* |
| 2199 | * first 32 bits to check coversion: |
| 2200 | * IP version, TOS, TTL, protocol |
| 2201 | */ |
| 2202 | i = ((*lip & *lm) != *ld); |
| 2203 | FR_DEBUG(("0. %#08x & %#08x != %#08x\n" , |
| 2204 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2205 | if (i) |
| 2206 | return 1; |
| 2207 | |
| 2208 | /* |
| 2209 | * Next 32 bits is a constructed bitmask indicating which IP options |
| 2210 | * are present (if any) in this packet. |
| 2211 | */ |
| 2212 | lip++, lm++, ld++; |
| 2213 | i = ((*lip & *lm) != *ld); |
| 2214 | FR_DEBUG(("1. %#08x & %#08x != %#08x\n" , |
| 2215 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2216 | if (i != 0) |
| 2217 | return 1; |
| 2218 | |
| 2219 | lip++, lm++, ld++; |
| 2220 | /* |
| 2221 | * Unrolled loops (4 each, for 32 bits) for address checks. |
| 2222 | */ |
| 2223 | /* |
| 2224 | * Check the source address. |
| 2225 | */ |
| 2226 | if (fr->fr_satype == FRI_LOOKUP) { |
| 2227 | i = (*fr->fr_srcfunc)(fin->fin_main_soft, fr->fr_srcptr, |
| 2228 | fi->fi_v, lip, fin->fin_plen); |
| 2229 | if (i == -1) |
| 2230 | return 1; |
| 2231 | lip += 3; |
| 2232 | lm += 3; |
| 2233 | ld += 3; |
| 2234 | } else { |
| 2235 | i = ((*lip & *lm) != *ld); |
| 2236 | FR_DEBUG(("2a. %#08x & %#08x != %#08x\n" , |
| 2237 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2238 | if (fi->fi_v == 6) { |
| 2239 | lip++, lm++, ld++; |
| 2240 | i |= ((*lip & *lm) != *ld); |
| 2241 | FR_DEBUG(("2b. %#08x & %#08x != %#08x\n" , |
| 2242 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2243 | lip++, lm++, ld++; |
| 2244 | i |= ((*lip & *lm) != *ld); |
| 2245 | FR_DEBUG(("2c. %#08x & %#08x != %#08x\n" , |
| 2246 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2247 | lip++, lm++, ld++; |
| 2248 | i |= ((*lip & *lm) != *ld); |
| 2249 | FR_DEBUG(("2d. %#08x & %#08x != %#08x\n" , |
| 2250 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2251 | } else { |
| 2252 | lip += 3; |
| 2253 | lm += 3; |
| 2254 | ld += 3; |
| 2255 | } |
| 2256 | } |
| 2257 | i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6; |
| 2258 | if (i != 0) |
| 2259 | return 1; |
| 2260 | |
| 2261 | /* |
| 2262 | * Check the destination address. |
| 2263 | */ |
| 2264 | lip++, lm++, ld++; |
| 2265 | if (fr->fr_datype == FRI_LOOKUP) { |
| 2266 | i = (*fr->fr_dstfunc)(fin->fin_main_soft, fr->fr_dstptr, |
| 2267 | fi->fi_v, lip, fin->fin_plen); |
| 2268 | if (i == -1) |
| 2269 | return 1; |
| 2270 | lip += 3; |
| 2271 | lm += 3; |
| 2272 | ld += 3; |
| 2273 | } else { |
| 2274 | i = ((*lip & *lm) != *ld); |
| 2275 | FR_DEBUG(("3a. %#08x & %#08x != %#08x\n" , |
| 2276 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2277 | if (fi->fi_v == 6) { |
| 2278 | lip++, lm++, ld++; |
| 2279 | i |= ((*lip & *lm) != *ld); |
| 2280 | FR_DEBUG(("3b. %#08x & %#08x != %#08x\n" , |
| 2281 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2282 | lip++, lm++, ld++; |
| 2283 | i |= ((*lip & *lm) != *ld); |
| 2284 | FR_DEBUG(("3c. %#08x & %#08x != %#08x\n" , |
| 2285 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2286 | lip++, lm++, ld++; |
| 2287 | i |= ((*lip & *lm) != *ld); |
| 2288 | FR_DEBUG(("3d. %#08x & %#08x != %#08x\n" , |
| 2289 | ntohl(*lip), ntohl(*lm), ntohl(*ld))); |
| 2290 | } else { |
| 2291 | lip += 3; |
| 2292 | lm += 3; |
| 2293 | ld += 3; |
| 2294 | } |
| 2295 | } |
| 2296 | i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7; |
| 2297 | if (i != 0) |
| 2298 | return 1; |
| 2299 | /* |
| 2300 | * IP addresses matched. The next 32bits contains: |
| 2301 | * mast of old IP header security & authentication bits. |
| 2302 | */ |
| 2303 | lip++, lm++, ld++; |
| 2304 | i = (*ld - (*lip & *lm)); |
| 2305 | FR_DEBUG(("4. %#08x & %#08x != %#08x\n" , *lip, *lm, *ld)); |
| 2306 | |
| 2307 | /* |
| 2308 | * Next we have 32 bits of packet flags. |
| 2309 | */ |
| 2310 | lip++, lm++, ld++; |
| 2311 | i |= (*ld - (*lip & *lm)); |
| 2312 | FR_DEBUG(("5. %#08x & %#08x != %#08x\n" , *lip, *lm, *ld)); |
| 2313 | |
| 2314 | if (i == 0) { |
| 2315 | /* |
| 2316 | * If a fragment, then only the first has what we're |
| 2317 | * looking for here... |
| 2318 | */ |
| 2319 | if (portcmp) { |
| 2320 | if (!ipf_tcpudpchk(&fin->fin_fi, &fr->fr_tuc)) |
| 2321 | i = 1; |
| 2322 | } else { |
| 2323 | if (fr->fr_dcmp || fr->fr_scmp || |
| 2324 | fr->fr_tcpf || fr->fr_tcpfm) |
| 2325 | i = 1; |
| 2326 | if (fr->fr_icmpm || fr->fr_icmp) { |
| 2327 | if (((fi->fi_p != IPPROTO_ICMP) && |
| 2328 | (fi->fi_p != IPPROTO_ICMPV6)) || |
| 2329 | fin->fin_off || (fin->fin_dlen < 2)) |
| 2330 | i = 1; |
| 2331 | else if ((fin->fin_data[0] & fr->fr_icmpm) != |
| 2332 | fr->fr_icmp) { |
| 2333 | FR_DEBUG(("i. %#x & %#x != %#x\n" , |
| 2334 | fin->fin_data[0], |
| 2335 | fr->fr_icmpm, fr->fr_icmp)); |
| 2336 | i = 1; |
| 2337 | } |
| 2338 | } |
| 2339 | } |
| 2340 | } |
| 2341 | return i; |
| 2342 | } |
| 2343 | |
| 2344 | |
| 2345 | /* ------------------------------------------------------------------------ */ |
| 2346 | /* Function: ipf_scanlist */ |
| 2347 | /* Returns: int - result flags of scanning filter list */ |
| 2348 | /* Parameters: fin(I) - pointer to packet information */ |
| 2349 | /* pass(I) - default result to return for filtering */ |
| 2350 | /* */ |
| 2351 | /* Check the input/output list of rules for a match to the current packet. */ |
| 2352 | /* If a match is found, the value of fr_flags from the rule becomes the */ |
| 2353 | /* return value and fin->fin_fr points to the matched rule. */ |
| 2354 | /* */ |
| 2355 | /* This function may be called recusively upto 16 times (limit inbuilt.) */ |
| 2356 | /* When unwinding, it should finish up with fin_depth as 0. */ |
| 2357 | /* */ |
| 2358 | /* Could be per interface, but this gets real nasty when you don't have, */ |
| 2359 | /* or can't easily change, the kernel source code to . */ |
| 2360 | /* ------------------------------------------------------------------------ */ |
| 2361 | int |
| 2362 | ipf_scanlist(fr_info_t *fin, u_32_t pass) |
| 2363 | { |
| 2364 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 2365 | int rulen, portcmp, off, skip; |
| 2366 | struct frentry *fr, *fnext; |
| 2367 | u_32_t passt, passo; |
| 2368 | |
| 2369 | /* |
| 2370 | * Do not allow nesting deeper than 16 levels. |
| 2371 | */ |
| 2372 | if (fin->fin_depth >= 16) |
| 2373 | return pass; |
| 2374 | |
| 2375 | fr = fin->fin_fr; |
| 2376 | |
| 2377 | /* |
| 2378 | * If there are no rules in this list, return now. |
| 2379 | */ |
| 2380 | if (fr == NULL) |
| 2381 | return pass; |
| 2382 | |
| 2383 | skip = 0; |
| 2384 | portcmp = 0; |
| 2385 | fin->fin_depth++; |
| 2386 | fin->fin_fr = NULL; |
| 2387 | off = fin->fin_off; |
| 2388 | |
| 2389 | if ((fin->fin_flx & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) |
| 2390 | portcmp = 1; |
| 2391 | |
| 2392 | for (rulen = 0; fr; fr = fnext, rulen++) { |
| 2393 | fnext = fr->fr_next; |
| 2394 | if (skip != 0) { |
| 2395 | FR_VERBOSE(("SKIP %d (%#x)\n" , skip, fr->fr_flags)); |
| 2396 | skip--; |
| 2397 | continue; |
| 2398 | } |
| 2399 | |
| 2400 | /* |
| 2401 | * In all checks below, a null (zero) value in the |
| 2402 | * filter struture is taken to mean a wildcard. |
| 2403 | * |
| 2404 | * check that we are working for the right interface |
| 2405 | */ |
| 2406 | #ifdef _KERNEL |
| 2407 | if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) |
| 2408 | continue; |
| 2409 | #else |
| 2410 | if (opts & (OPT_VERBOSE|OPT_DEBUG)) |
| 2411 | printf("\n" ); |
| 2412 | FR_VERBOSE(("%c" , FR_ISSKIP(pass) ? 's' : |
| 2413 | FR_ISPASS(pass) ? 'p' : |
| 2414 | FR_ISACCOUNT(pass) ? 'A' : |
| 2415 | FR_ISAUTH(pass) ? 'a' : |
| 2416 | (pass & FR_NOMATCH) ? 'n' :'b')); |
| 2417 | if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) |
| 2418 | continue; |
| 2419 | FR_VERBOSE((":i" )); |
| 2420 | #endif |
| 2421 | |
| 2422 | switch (fr->fr_type) |
| 2423 | { |
| 2424 | case FR_T_IPF : |
| 2425 | case FR_T_IPF_BUILTIN : |
| 2426 | if (ipf_check_ipf(fin, fr, portcmp)) |
| 2427 | continue; |
| 2428 | break; |
| 2429 | #if defined(IPFILTER_BPF) |
| 2430 | case FR_T_BPFOPC : |
| 2431 | case FR_T_BPFOPC_BUILTIN : |
| 2432 | { |
| 2433 | u_char *mc; |
| 2434 | int wlen; |
| 2435 | |
| 2436 | if (*fin->fin_mp == NULL) |
| 2437 | continue; |
| 2438 | if (fin->fin_family != fr->fr_family) |
| 2439 | continue; |
| 2440 | mc = (u_char *)fin->fin_m; |
| 2441 | wlen = fin->fin_dlen + fin->fin_hlen; |
| 2442 | if (!bpf_filter(fr->fr_data, mc, wlen, 0)) |
| 2443 | continue; |
| 2444 | break; |
| 2445 | } |
| 2446 | #endif |
| 2447 | case FR_T_CALLFUNC_BUILTIN : |
| 2448 | { |
| 2449 | frentry_t *f; |
| 2450 | |
| 2451 | f = (*fr->fr_func)(fin, &pass); |
| 2452 | if (f != NULL) |
| 2453 | fr = f; |
| 2454 | else |
| 2455 | continue; |
| 2456 | break; |
| 2457 | } |
| 2458 | |
| 2459 | case FR_T_IPFEXPR : |
| 2460 | case FR_T_IPFEXPR_BUILTIN : |
| 2461 | if (fin->fin_family != fr->fr_family) |
| 2462 | continue; |
| 2463 | if (ipf_fr_matcharray(fin, fr->fr_data) == 0) |
| 2464 | continue; |
| 2465 | break; |
| 2466 | |
| 2467 | default : |
| 2468 | break; |
| 2469 | } |
| 2470 | |
| 2471 | if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { |
| 2472 | if (fin->fin_nattag == NULL) |
| 2473 | continue; |
| 2474 | if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) |
| 2475 | continue; |
| 2476 | } |
| 2477 | FR_VERBOSE(("=%d/%d.%d *" , fr->fr_grhead, fr->fr_group, rulen)); |
| 2478 | |
| 2479 | passt = fr->fr_flags; |
| 2480 | |
| 2481 | /* |
| 2482 | * If the rule is a "call now" rule, then call the function |
| 2483 | * in the rule, if it exists and use the results from that. |
| 2484 | * If the function pointer is bad, just make like we ignore |
| 2485 | * it, except for increasing the hit counter. |
| 2486 | */ |
| 2487 | if ((passt & FR_CALLNOW) != 0) { |
| 2488 | frentry_t *frs; |
| 2489 | |
| 2490 | ATOMIC_INC64(fr->fr_hits); |
| 2491 | if ((fr->fr_func == NULL) || |
| 2492 | (fr->fr_func == (ipfunc_t)-1)) |
| 2493 | continue; |
| 2494 | |
| 2495 | frs = fin->fin_fr; |
| 2496 | fin->fin_fr = fr; |
| 2497 | fr = (*fr->fr_func)(fin, &passt); |
| 2498 | if (fr == NULL) { |
| 2499 | fin->fin_fr = frs; |
| 2500 | continue; |
| 2501 | } |
| 2502 | passt = fr->fr_flags; |
| 2503 | } |
| 2504 | fin->fin_fr = fr; |
| 2505 | |
| 2506 | #ifdef IPFILTER_LOG |
| 2507 | /* |
| 2508 | * Just log this packet... |
| 2509 | */ |
| 2510 | if ((passt & FR_LOGMASK) == FR_LOG) { |
| 2511 | if (ipf_log_pkt(fin, passt) == -1) { |
| 2512 | if (passt & FR_LOGORBLOCK) { |
| 2513 | DT(frb_logfail); |
| 2514 | passt &= ~FR_CMDMASK; |
| 2515 | passt |= FR_BLOCK|FR_QUICK; |
| 2516 | fin->fin_reason = FRB_LOGFAIL; |
| 2517 | } |
| 2518 | } |
| 2519 | } |
| 2520 | #endif /* IPFILTER_LOG */ |
| 2521 | |
| 2522 | MUTEX_ENTER(&fr->fr_lock); |
| 2523 | fr->fr_bytes += (U_QUAD_T)fin->fin_plen; |
| 2524 | fr->fr_hits++; |
| 2525 | MUTEX_EXIT(&fr->fr_lock); |
| 2526 | fin->fin_rule = rulen; |
| 2527 | |
| 2528 | passo = pass; |
| 2529 | if (FR_ISSKIP(passt)) { |
| 2530 | skip = fr->fr_arg; |
| 2531 | continue; |
| 2532 | } else if (((passt & FR_LOGMASK) != FR_LOG) && |
| 2533 | ((passt & FR_LOGMASK) != FR_DECAPSULATE)) { |
| 2534 | pass = passt; |
| 2535 | } |
| 2536 | |
| 2537 | if (passt & (FR_RETICMP|FR_FAKEICMP)) |
| 2538 | fin->fin_icode = fr->fr_icode; |
| 2539 | |
| 2540 | if (fr->fr_group != -1) { |
| 2541 | (void) strncpy(fin->fin_group, |
| 2542 | FR_NAME(fr, fr_group), |
| 2543 | strlen(FR_NAME(fr, fr_group))); |
| 2544 | } else { |
| 2545 | fin->fin_group[0] = '\0'; |
| 2546 | } |
| 2547 | |
| 2548 | FR_DEBUG(("pass %#x/%#x/%x\n" , passo, pass, passt)); |
| 2549 | |
| 2550 | if (fr->fr_grphead != NULL) { |
| 2551 | fin->fin_fr = fr->fr_grphead->fg_start; |
| 2552 | FR_VERBOSE(("group %s\n" , FR_NAME(fr, fr_grhead))); |
| 2553 | |
| 2554 | if (FR_ISDECAPS(passt)) |
| 2555 | passt = ipf_decaps(fin, pass, fr->fr_icode); |
| 2556 | else |
| 2557 | passt = ipf_scanlist(fin, pass); |
| 2558 | |
| 2559 | if (fin->fin_fr == NULL) { |
| 2560 | fin->fin_rule = rulen; |
| 2561 | if (fr->fr_group != -1) |
| 2562 | (void) strncpy(fin->fin_group, |
| 2563 | fr->fr_names + |
| 2564 | fr->fr_group, |
| 2565 | strlen(fr->fr_names + |
| 2566 | fr->fr_group)); |
| 2567 | fin->fin_fr = fr; |
| 2568 | passt = pass; |
| 2569 | } |
| 2570 | pass = passt; |
| 2571 | } |
| 2572 | |
| 2573 | if (pass & FR_QUICK) { |
| 2574 | /* |
| 2575 | * Finally, if we've asked to track state for this |
| 2576 | * packet, set it up. Add state for "quick" rules |
| 2577 | * here so that if the action fails we can consider |
| 2578 | * the rule to "not match" and keep on processing |
| 2579 | * filter rules. |
| 2580 | */ |
| 2581 | if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) && |
| 2582 | !(fin->fin_flx & FI_STATE)) { |
| 2583 | int out = fin->fin_out; |
| 2584 | |
| 2585 | fin->fin_fr = fr; |
| 2586 | if (ipf_state_add(softc, fin, NULL, 0) == 0) { |
| 2587 | LBUMPD(ipf_stats[out], fr_ads); |
| 2588 | } else { |
| 2589 | LBUMPD(ipf_stats[out], fr_bads); |
| 2590 | pass = passo; |
| 2591 | continue; |
| 2592 | } |
| 2593 | } |
| 2594 | break; |
| 2595 | } |
| 2596 | } |
| 2597 | fin->fin_depth--; |
| 2598 | return pass; |
| 2599 | } |
| 2600 | |
| 2601 | |
| 2602 | /* ------------------------------------------------------------------------ */ |
| 2603 | /* Function: ipf_acctpkt */ |
| 2604 | /* Returns: frentry_t* - always returns NULL */ |
| 2605 | /* Parameters: fin(I) - pointer to packet information */ |
| 2606 | /* passp(IO) - pointer to current/new filter decision (unused) */ |
| 2607 | /* */ |
| 2608 | /* Checks a packet against accounting rules, if there are any for the given */ |
| 2609 | /* IP protocol version. */ |
| 2610 | /* */ |
| 2611 | /* N.B.: this function returns NULL to match the prototype used by other */ |
| 2612 | /* functions called from the IPFilter "mainline" in ipf_check(). */ |
| 2613 | /* ------------------------------------------------------------------------ */ |
| 2614 | frentry_t * |
| 2615 | ipf_acctpkt(fr_info_t *fin, u_32_t *passp) |
| 2616 | { |
| 2617 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 2618 | char group[FR_GROUPLEN]; |
| 2619 | frentry_t *fr, *frsave; |
| 2620 | u_32_t pass, rulen; |
| 2621 | |
| 2622 | passp = passp; |
| 2623 | fr = softc->ipf_acct[fin->fin_out][softc->ipf_active]; |
| 2624 | |
| 2625 | if (fr != NULL) { |
| 2626 | frsave = fin->fin_fr; |
| 2627 | bcopy(fin->fin_group, group, FR_GROUPLEN); |
| 2628 | rulen = fin->fin_rule; |
| 2629 | fin->fin_fr = fr; |
| 2630 | pass = ipf_scanlist(fin, FR_NOMATCH); |
| 2631 | if (FR_ISACCOUNT(pass)) { |
| 2632 | LBUMPD(ipf_stats[0], fr_acct); |
| 2633 | } |
| 2634 | fin->fin_fr = frsave; |
| 2635 | bcopy(group, fin->fin_group, FR_GROUPLEN); |
| 2636 | fin->fin_rule = rulen; |
| 2637 | } |
| 2638 | return NULL; |
| 2639 | } |
| 2640 | |
| 2641 | |
| 2642 | /* ------------------------------------------------------------------------ */ |
| 2643 | /* Function: ipf_firewall */ |
| 2644 | /* Returns: frentry_t* - returns pointer to matched rule, if no matches */ |
| 2645 | /* were found, returns NULL. */ |
| 2646 | /* Parameters: fin(I) - pointer to packet information */ |
| 2647 | /* passp(IO) - pointer to current/new filter decision (unused) */ |
| 2648 | /* */ |
| 2649 | /* Applies an appropriate set of firewall rules to the packet, to see if */ |
| 2650 | /* there are any matches. The first check is to see if a match can be seen */ |
| 2651 | /* in the cache. If not, then search an appropriate list of rules. Once a */ |
| 2652 | /* matching rule is found, take any appropriate actions as defined by the */ |
| 2653 | /* rule - except logging. */ |
| 2654 | /* ------------------------------------------------------------------------ */ |
| 2655 | static frentry_t * |
| 2656 | ipf_firewall(fr_info_t *fin, u_32_t *passp) |
| 2657 | { |
| 2658 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 2659 | frentry_t *fr; |
| 2660 | u_32_t pass; |
| 2661 | int out; |
| 2662 | |
| 2663 | out = fin->fin_out; |
| 2664 | pass = *passp; |
| 2665 | |
| 2666 | /* |
| 2667 | * This rule cache will only affect packets that are not being |
| 2668 | * statefully filtered. |
| 2669 | */ |
| 2670 | fin->fin_fr = softc->ipf_rules[out][softc->ipf_active]; |
| 2671 | if (fin->fin_fr != NULL) |
| 2672 | pass = ipf_scanlist(fin, softc->ipf_pass); |
| 2673 | |
| 2674 | if ((pass & FR_NOMATCH)) { |
| 2675 | LBUMPD(ipf_stats[out], fr_nom); |
| 2676 | } |
| 2677 | fr = fin->fin_fr; |
| 2678 | |
| 2679 | /* |
| 2680 | * Apply packets per second rate-limiting to a rule as required. |
| 2681 | */ |
| 2682 | if ((fr != NULL) && (fr->fr_pps != 0) && |
| 2683 | !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) { |
| 2684 | DT2(frb_ppsrate, fr_info_t *, fin, frentry_t *, fr); |
| 2685 | pass &= ~(FR_CMDMASK|FR_RETICMP|FR_RETRST); |
| 2686 | pass |= FR_BLOCK; |
| 2687 | LBUMPD(ipf_stats[out], fr_ppshit); |
| 2688 | fin->fin_reason = FRB_PPSRATE; |
| 2689 | } |
| 2690 | |
| 2691 | /* |
| 2692 | * If we fail to add a packet to the authorization queue, then we |
| 2693 | * drop the packet later. However, if it was added then pretend |
| 2694 | * we've dropped it already. |
| 2695 | */ |
| 2696 | if (FR_ISAUTH(pass)) { |
| 2697 | if (ipf_auth_new(fin->fin_m, fin) != 0) { |
| 2698 | DT1(frb_authnew, fr_info_t *, fin); |
| 2699 | fin->fin_m = *fin->fin_mp = NULL; |
| 2700 | fin->fin_reason = FRB_AUTHNEW; |
| 2701 | fin->fin_error = 0; |
| 2702 | } else { |
| 2703 | IPFERROR(1); |
| 2704 | fin->fin_error = ENOSPC; |
| 2705 | } |
| 2706 | } |
| 2707 | |
| 2708 | if ((fr != NULL) && (fr->fr_func != NULL) && |
| 2709 | (fr->fr_func != (ipfunc_t)-1) && !(pass & FR_CALLNOW)) |
| 2710 | (void) (*fr->fr_func)(fin, &pass); |
| 2711 | |
| 2712 | /* |
| 2713 | * If a rule is a pre-auth rule, check again in the list of rules |
| 2714 | * loaded for authenticated use. It does not particulary matter |
| 2715 | * if this search fails because a "preauth" result, from a rule, |
| 2716 | * is treated as "not a pass", hence the packet is blocked. |
| 2717 | */ |
| 2718 | if (FR_ISPREAUTH(pass)) { |
| 2719 | pass = ipf_auth_pre_scanlist(softc, fin, pass); |
| 2720 | } |
| 2721 | |
| 2722 | /* |
| 2723 | * If the rule has "keep frag" and the packet is actually a fragment, |
| 2724 | * then create a fragment state entry. |
| 2725 | */ |
| 2726 | if ((pass & (FR_KEEPFRAG|FR_KEEPSTATE)) == FR_KEEPFRAG) { |
| 2727 | if (fin->fin_flx & FI_FRAG) { |
| 2728 | if (ipf_frag_new(softc, fin, pass) == -1) { |
| 2729 | LBUMP(ipf_stats[out].fr_bnfr); |
| 2730 | } else { |
| 2731 | LBUMP(ipf_stats[out].fr_nfr); |
| 2732 | } |
| 2733 | } else { |
| 2734 | LBUMP(ipf_stats[out].fr_cfr); |
| 2735 | } |
| 2736 | } |
| 2737 | |
| 2738 | fr = fin->fin_fr; |
| 2739 | *passp = pass; |
| 2740 | |
| 2741 | return fr; |
| 2742 | } |
| 2743 | |
| 2744 | |
| 2745 | /* ------------------------------------------------------------------------ */ |
| 2746 | /* Function: ipf_check */ |
| 2747 | /* Returns: int - 0 == packet allowed through, */ |
| 2748 | /* User space: */ |
| 2749 | /* -1 == packet blocked */ |
| 2750 | /* 1 == packet not matched */ |
| 2751 | /* -2 == requires authentication */ |
| 2752 | /* Kernel: */ |
| 2753 | /* > 0 == filter error # for packet */ |
| 2754 | /* Parameters: ip(I) - pointer to start of IPv4/6 packet */ |
| 2755 | /* hlen(I) - length of header */ |
| 2756 | /* ifp(I) - pointer to interface this packet is on */ |
| 2757 | /* out(I) - 0 == packet going in, 1 == packet going out */ |
| 2758 | /* mp(IO) - pointer to caller's buffer pointer that holds this */ |
| 2759 | /* IP packet. */ |
| 2760 | /* Solaris & HP-UX ONLY : */ |
| 2761 | /* qpi(I) - pointer to STREAMS queue information for this */ |
| 2762 | /* interface & direction. */ |
| 2763 | /* */ |
| 2764 | /* ipf_check() is the master function for all IPFilter packet processing. */ |
| 2765 | /* It orchestrates: Network Address Translation (NAT), checking for packet */ |
| 2766 | /* authorisation (or pre-authorisation), presence of related state info., */ |
| 2767 | /* generating log entries, IP packet accounting, routing of packets as */ |
| 2768 | /* directed by firewall rules and of course whether or not to allow the */ |
| 2769 | /* packet to be further processed by the kernel. */ |
| 2770 | /* */ |
| 2771 | /* For packets blocked, the contents of "mp" will be NULL'd and the buffer */ |
| 2772 | /* freed. Packets passed may be returned with the pointer pointed to by */ |
| 2773 | /* by "mp" changed to a new buffer. */ |
| 2774 | /* ------------------------------------------------------------------------ */ |
| 2775 | int |
| 2776 | ipf_check(void *ctx, ip_t *ip, int hlen, void *ifp, int out, |
| 2777 | #if defined(_KERNEL) && defined(MENTAT) |
| 2778 | void *qif, |
| 2779 | #endif |
| 2780 | mb_t **mp) |
| 2781 | { |
| 2782 | /* |
| 2783 | * The above really sucks, but short of writing a diff |
| 2784 | */ |
| 2785 | ipf_main_softc_t *softc = ctx; |
| 2786 | fr_info_t frinfo; |
| 2787 | fr_info_t *fin = &frinfo; |
| 2788 | u_32_t pass = softc->ipf_pass; |
| 2789 | frentry_t *fr = NULL; |
| 2790 | int v = IP_V(ip); |
| 2791 | mb_t *mc = NULL; |
| 2792 | mb_t *m; |
| 2793 | /* |
| 2794 | * The first part of ipf_check() deals with making sure that what goes |
| 2795 | * into the filtering engine makes some sense. Information about the |
| 2796 | * the packet is distilled, collected into a fr_info_t structure and |
| 2797 | * the an attempt to ensure the buffer the packet is in is big enough |
| 2798 | * to hold all the required packet headers. |
| 2799 | */ |
| 2800 | #ifdef _KERNEL |
| 2801 | # ifdef MENTAT |
| 2802 | qpktinfo_t *qpi = qif; |
| 2803 | |
| 2804 | # ifdef __sparc |
| 2805 | if ((u_int)ip & 0x3) |
| 2806 | return 2; |
| 2807 | # endif |
| 2808 | # else |
| 2809 | SPL_INT(s); |
| 2810 | # endif |
| 2811 | |
| 2812 | if (softc->ipf_running <= 0) { |
| 2813 | return 0; |
| 2814 | } |
| 2815 | |
| 2816 | bzero((char *)fin, sizeof(*fin)); |
| 2817 | |
| 2818 | # ifdef MENTAT |
| 2819 | if (qpi->qpi_flags & QF_BROADCAST) |
| 2820 | fin->fin_flx |= FI_MBCAST|FI_BROADCAST; |
| 2821 | if (qpi->qpi_flags & QF_MULTICAST) |
| 2822 | fin->fin_flx |= FI_MBCAST|FI_MULTICAST; |
| 2823 | m = qpi->qpi_m; |
| 2824 | fin->fin_qfm = m; |
| 2825 | fin->fin_qpi = qpi; |
| 2826 | # else /* MENTAT */ |
| 2827 | |
| 2828 | m = *mp; |
| 2829 | |
| 2830 | # if defined(M_MCAST) |
| 2831 | if ((m->m_flags & M_MCAST) != 0) |
| 2832 | fin->fin_flx |= FI_MBCAST|FI_MULTICAST; |
| 2833 | # endif |
| 2834 | # if defined(M_MLOOP) |
| 2835 | if ((m->m_flags & M_MLOOP) != 0) |
| 2836 | fin->fin_flx |= FI_MBCAST|FI_MULTICAST; |
| 2837 | # endif |
| 2838 | # if defined(M_BCAST) |
| 2839 | if ((m->m_flags & M_BCAST) != 0) |
| 2840 | fin->fin_flx |= FI_MBCAST|FI_BROADCAST; |
| 2841 | # endif |
| 2842 | # ifdef M_CANFASTFWD |
| 2843 | /* |
| 2844 | * XXX For now, IP Filter and fast-forwarding of cached flows |
| 2845 | * XXX are mutually exclusive. Eventually, IP Filter should |
| 2846 | * XXX get a "can-fast-forward" filter rule. |
| 2847 | */ |
| 2848 | m->m_flags &= ~M_CANFASTFWD; |
| 2849 | # endif /* M_CANFASTFWD */ |
| 2850 | # if defined(CSUM_DELAY_DATA) && (!defined(__FreeBSD_version) || \ |
| 2851 | (__FreeBSD_version < 501108)) |
| 2852 | /* |
| 2853 | * disable delayed checksums. |
| 2854 | */ |
| 2855 | if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { |
| 2856 | in_delayed_cksum(m); |
| 2857 | m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; |
| 2858 | } |
| 2859 | # endif /* CSUM_DELAY_DATA */ |
| 2860 | # endif /* MENTAT */ |
| 2861 | #else |
| 2862 | bzero((char *)fin, sizeof(*fin)); |
| 2863 | m = *mp; |
| 2864 | # if defined(M_MCAST) |
| 2865 | if ((m->m_flags & M_MCAST) != 0) |
| 2866 | fin->fin_flx |= FI_MBCAST|FI_MULTICAST; |
| 2867 | # endif |
| 2868 | # if defined(M_MLOOP) |
| 2869 | if ((m->m_flags & M_MLOOP) != 0) |
| 2870 | fin->fin_flx |= FI_MBCAST|FI_MULTICAST; |
| 2871 | # endif |
| 2872 | # if defined(M_BCAST) |
| 2873 | if ((m->m_flags & M_BCAST) != 0) |
| 2874 | fin->fin_flx |= FI_MBCAST|FI_BROADCAST; |
| 2875 | # endif |
| 2876 | #endif /* _KERNEL */ |
| 2877 | |
| 2878 | fin->fin_v = v; |
| 2879 | fin->fin_m = m; |
| 2880 | fin->fin_ip = ip; |
| 2881 | fin->fin_mp = mp; |
| 2882 | fin->fin_out = out; |
| 2883 | fin->fin_ifp = ifp; |
| 2884 | fin->fin_error = ENETUNREACH; |
| 2885 | fin->fin_hlen = (u_short)hlen; |
| 2886 | fin->fin_dp = (char *)ip + hlen; |
| 2887 | fin->fin_main_soft = softc; |
| 2888 | |
| 2889 | fin->fin_ipoff = (char *)ip - MTOD(m, char *); |
| 2890 | |
| 2891 | SPL_NET(s); |
| 2892 | |
| 2893 | #ifdef USE_INET6 |
| 2894 | if (v == 6) { |
| 2895 | LBUMP(ipf_stats[out].fr_ipv6); |
| 2896 | /* |
| 2897 | * Jumbo grams are quite likely too big for internal buffer |
| 2898 | * structures to handle comfortably, for now, so just drop |
| 2899 | * them. |
| 2900 | */ |
| 2901 | if (((ip6_t *)ip)->ip6_plen == 0) { |
| 2902 | DT1(frb_jumbo, ip6_t *, (ip6_t *)ip); |
| 2903 | pass = FR_BLOCK|FR_NOMATCH; |
| 2904 | fin->fin_reason = FRB_JUMBO; |
| 2905 | goto finished; |
| 2906 | } |
| 2907 | fin->fin_family = AF_INET6; |
| 2908 | } else |
| 2909 | #endif |
| 2910 | { |
| 2911 | fin->fin_family = AF_INET; |
| 2912 | } |
| 2913 | |
| 2914 | if (ipf_makefrip(hlen, ip, fin) == -1) { |
| 2915 | DT1(frb_makefrip, fr_info_t *, fin); |
| 2916 | pass = FR_BLOCK|FR_NOMATCH; |
| 2917 | fin->fin_reason = FRB_MAKEFRIP; |
| 2918 | goto finished; |
| 2919 | } |
| 2920 | |
| 2921 | /* |
| 2922 | * For at least IPv6 packets, if a m_pullup() fails then this pointer |
| 2923 | * becomes NULL and so we have no packet to free. |
| 2924 | */ |
| 2925 | if (*fin->fin_mp == NULL) |
| 2926 | goto finished; |
| 2927 | |
| 2928 | if (!out) { |
| 2929 | if (v == 4) { |
| 2930 | if (softc->ipf_chksrc && !ipf_verifysrc(fin)) { |
| 2931 | LBUMPD(ipf_stats[0], fr_v4_badsrc); |
| 2932 | fin->fin_flx |= FI_BADSRC; |
| 2933 | } |
| 2934 | if (fin->fin_ip->ip_ttl < softc->ipf_minttl) { |
| 2935 | LBUMPD(ipf_stats[0], fr_v4_badttl); |
| 2936 | fin->fin_flx |= FI_LOWTTL; |
| 2937 | } |
| 2938 | } |
| 2939 | #ifdef USE_INET6 |
| 2940 | else if (v == 6) { |
| 2941 | if (((ip6_t *)ip)->ip6_hlim < softc->ipf_minttl) { |
| 2942 | LBUMPD(ipf_stats[0], fr_v6_badttl); |
| 2943 | fin->fin_flx |= FI_LOWTTL; |
| 2944 | } |
| 2945 | } |
| 2946 | #endif |
| 2947 | } |
| 2948 | |
| 2949 | if (fin->fin_flx & FI_SHORT) { |
| 2950 | LBUMPD(ipf_stats[out], fr_short); |
| 2951 | } |
| 2952 | |
| 2953 | READ_ENTER(&softc->ipf_mutex); |
| 2954 | |
| 2955 | if (!out) { |
| 2956 | switch (fin->fin_v) |
| 2957 | { |
| 2958 | case 4 : |
| 2959 | if (ipf_nat_checkin(fin, &pass) == -1) { |
| 2960 | goto filterdone; |
| 2961 | } |
| 2962 | break; |
| 2963 | #ifdef USE_INET6 |
| 2964 | case 6 : |
| 2965 | if (ipf_nat6_checkin(fin, &pass) == -1) { |
| 2966 | goto filterdone; |
| 2967 | } |
| 2968 | break; |
| 2969 | #endif |
| 2970 | default : |
| 2971 | break; |
| 2972 | } |
| 2973 | } |
| 2974 | /* |
| 2975 | * Check auth now. |
| 2976 | * If a packet is found in the auth table, then skip checking |
| 2977 | * the access lists for permission but we do need to consider |
| 2978 | * the result as if it were from the ACL's. In addition, being |
| 2979 | * found in the auth table means it has been seen before, so do |
| 2980 | * not pass it through accounting (again), lest it be counted twice. |
| 2981 | */ |
| 2982 | fr = ipf_auth_check(fin, &pass); |
| 2983 | if (!out && (fr == NULL)) |
| 2984 | (void) ipf_acctpkt(fin, NULL); |
| 2985 | |
| 2986 | if (fr == NULL) { |
| 2987 | if ((fin->fin_flx & FI_FRAG) != 0) |
| 2988 | fr = ipf_frag_known(fin, &pass); |
| 2989 | |
| 2990 | if (fr == NULL) |
| 2991 | fr = ipf_state_check(fin, &pass); |
| 2992 | } |
| 2993 | |
| 2994 | if ((pass & FR_NOMATCH) || (fr == NULL)) |
| 2995 | fr = ipf_firewall(fin, &pass); |
| 2996 | |
| 2997 | /* |
| 2998 | * If we've asked to track state for this packet, set it up. |
| 2999 | * Here rather than ipf_firewall because ipf_checkauth may decide |
| 3000 | * to return a packet for "keep state" |
| 3001 | */ |
| 3002 | if ((pass & FR_KEEPSTATE) && (fin->fin_m != NULL) && |
| 3003 | !(fin->fin_flx & FI_STATE)) { |
| 3004 | if (ipf_state_add(softc, fin, NULL, 0) == 0) { |
| 3005 | LBUMP(ipf_stats[out].fr_ads); |
| 3006 | } else { |
| 3007 | LBUMP(ipf_stats[out].fr_bads); |
| 3008 | if (FR_ISPASS(pass)) { |
| 3009 | DT(frb_stateadd); |
| 3010 | pass &= ~FR_CMDMASK; |
| 3011 | pass |= FR_BLOCK; |
| 3012 | fin->fin_reason = FRB_STATEADD; |
| 3013 | } |
| 3014 | } |
| 3015 | } |
| 3016 | |
| 3017 | fin->fin_fr = fr; |
| 3018 | if ((fr != NULL) && !(fin->fin_flx & FI_STATE)) { |
| 3019 | fin->fin_dif = &fr->fr_dif; |
| 3020 | fin->fin_tif = &fr->fr_tifs[fin->fin_rev]; |
| 3021 | } |
| 3022 | |
| 3023 | /* |
| 3024 | * Only count/translate packets which will be passed on, out the |
| 3025 | * interface. |
| 3026 | */ |
| 3027 | if (out && FR_ISPASS(pass)) { |
| 3028 | (void) ipf_acctpkt(fin, NULL); |
| 3029 | |
| 3030 | switch (fin->fin_v) |
| 3031 | { |
| 3032 | case 4 : |
| 3033 | if (ipf_nat_checkout(fin, &pass) == -1) { |
| 3034 | ; |
| 3035 | } else if ((softc->ipf_update_ipid != 0) && (v == 4)) { |
| 3036 | if (ipf_updateipid(fin) == -1) { |
| 3037 | DT(frb_updateipid); |
| 3038 | LBUMP(ipf_stats[1].fr_ipud); |
| 3039 | pass &= ~FR_CMDMASK; |
| 3040 | pass |= FR_BLOCK; |
| 3041 | fin->fin_reason = FRB_UPDATEIPID; |
| 3042 | } else { |
| 3043 | LBUMP(ipf_stats[0].fr_ipud); |
| 3044 | } |
| 3045 | } |
| 3046 | break; |
| 3047 | #ifdef USE_INET6 |
| 3048 | case 6 : |
| 3049 | (void) ipf_nat6_checkout(fin, &pass); |
| 3050 | break; |
| 3051 | #endif |
| 3052 | default : |
| 3053 | break; |
| 3054 | } |
| 3055 | } |
| 3056 | |
| 3057 | filterdone: |
| 3058 | #ifdef IPFILTER_LOG |
| 3059 | if ((softc->ipf_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { |
| 3060 | (void) ipf_dolog(fin, &pass); |
| 3061 | } |
| 3062 | #endif |
| 3063 | |
| 3064 | /* |
| 3065 | * The FI_STATE flag is cleared here so that calling ipf_state_check |
| 3066 | * will work when called from inside of fr_fastroute. Although |
| 3067 | * there is a similar flag, FI_NATED, for NAT, it does have the same |
| 3068 | * impact on code execution. |
| 3069 | */ |
| 3070 | fin->fin_flx &= ~FI_STATE; |
| 3071 | |
| 3072 | #if defined(FASTROUTE_RECURSION) |
| 3073 | /* |
| 3074 | * Up the reference on fr_lock and exit ipf_mutex. The generation of |
| 3075 | * a packet below can sometimes cause a recursive call into IPFilter. |
| 3076 | * On those platforms where that does happen, we need to hang onto |
| 3077 | * the filter rule just in case someone decides to remove or flush it |
| 3078 | * in the meantime. |
| 3079 | */ |
| 3080 | if (fr != NULL) { |
| 3081 | MUTEX_ENTER(&fr->fr_lock); |
| 3082 | fr->fr_ref++; |
| 3083 | MUTEX_EXIT(&fr->fr_lock); |
| 3084 | } |
| 3085 | |
| 3086 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 3087 | #endif |
| 3088 | |
| 3089 | if ((pass & FR_RETMASK) != 0) { |
| 3090 | /* |
| 3091 | * Should we return an ICMP packet to indicate error |
| 3092 | * status passing through the packet filter ? |
| 3093 | * WARNING: ICMP error packets AND TCP RST packets should |
| 3094 | * ONLY be sent in repsonse to incoming packets. Sending |
| 3095 | * them in response to outbound packets can result in a |
| 3096 | * panic on some operating systems. |
| 3097 | */ |
| 3098 | if (!out) { |
| 3099 | if (pass & FR_RETICMP) { |
| 3100 | int dst; |
| 3101 | |
| 3102 | if ((pass & FR_RETMASK) == FR_FAKEICMP) |
| 3103 | dst = 1; |
| 3104 | else |
| 3105 | dst = 0; |
| 3106 | (void) ipf_send_icmp_err(ICMP_UNREACH, fin, |
| 3107 | dst); |
| 3108 | LBUMP(ipf_stats[0].fr_ret); |
| 3109 | } else if (((pass & FR_RETMASK) == FR_RETRST) && |
| 3110 | !(fin->fin_flx & FI_SHORT)) { |
| 3111 | if (((fin->fin_flx & FI_OOW) != 0) || |
| 3112 | (ipf_send_reset(fin) == 0)) { |
| 3113 | LBUMP(ipf_stats[1].fr_ret); |
| 3114 | } |
| 3115 | } |
| 3116 | |
| 3117 | /* |
| 3118 | * When using return-* with auth rules, the auth code |
| 3119 | * takes over disposing of this packet. |
| 3120 | */ |
| 3121 | if (FR_ISAUTH(pass) && (fin->fin_m != NULL)) { |
| 3122 | DT1(frb_authcapture, fr_info_t *, fin); |
| 3123 | fin->fin_m = *fin->fin_mp = NULL; |
| 3124 | fin->fin_reason = FRB_AUTHCAPTURE; |
| 3125 | m = NULL; |
| 3126 | } |
| 3127 | } else { |
| 3128 | if (pass & FR_RETRST) { |
| 3129 | fin->fin_error = ECONNRESET; |
| 3130 | } |
| 3131 | } |
| 3132 | } |
| 3133 | |
| 3134 | /* |
| 3135 | * After the above so that ICMP unreachables and TCP RSTs get |
| 3136 | * created properly. |
| 3137 | */ |
| 3138 | if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) |
| 3139 | ipf_nat_uncreate(fin); |
| 3140 | |
| 3141 | /* |
| 3142 | * If we didn't drop off the bottom of the list of rules (and thus |
| 3143 | * the 'current' rule fr is not NULL), then we may have some extra |
| 3144 | * instructions about what to do with a packet. |
| 3145 | * Once we're finished return to our caller, freeing the packet if |
| 3146 | * we are dropping it. |
| 3147 | */ |
| 3148 | if (fr != NULL) { |
| 3149 | frdest_t *fdp; |
| 3150 | |
| 3151 | /* |
| 3152 | * Generate a duplicated packet first because ipf_fastroute |
| 3153 | * can lead to fin_m being free'd... not good. |
| 3154 | */ |
| 3155 | fdp = fin->fin_dif; |
| 3156 | if ((fdp != NULL) && (fdp->fd_ptr != NULL) && |
| 3157 | (fdp->fd_ptr != (void *)-1) && (fin->fin_m != NULL)) { |
| 3158 | mc = M_COPY(fin->fin_m); |
| 3159 | if (mc != NULL) |
| 3160 | ipf_fastroute(mc, &mc, fin, fdp); |
| 3161 | } |
| 3162 | |
| 3163 | fdp = fin->fin_tif; |
| 3164 | if (!out && (pass & FR_FASTROUTE)) { |
| 3165 | /* |
| 3166 | * For fastroute rule, no destination interface defined |
| 3167 | * so pass NULL as the frdest_t parameter |
| 3168 | */ |
| 3169 | (void) ipf_fastroute(fin->fin_m, mp, fin, NULL); |
| 3170 | m = *mp = NULL; |
| 3171 | } else if ((fdp != NULL) && (fdp->fd_ptr != NULL) && |
| 3172 | (fdp->fd_ptr != (struct ifnet *)-1)) { |
| 3173 | /* this is for to rules: */ |
| 3174 | ipf_fastroute(fin->fin_m, mp, fin, fdp); |
| 3175 | m = *mp = NULL; |
| 3176 | } |
| 3177 | |
| 3178 | #if defined(FASTROUTE_RECURSION) |
| 3179 | (void) ipf_derefrule(softc, &fr); |
| 3180 | #endif |
| 3181 | } |
| 3182 | #if !defined(FASTROUTE_RECURSION) |
| 3183 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 3184 | #endif |
| 3185 | |
| 3186 | finished: |
| 3187 | if (!FR_ISPASS(pass)) { |
| 3188 | LBUMP(ipf_stats[out].fr_block); |
| 3189 | if (*mp != NULL) { |
| 3190 | #ifdef _KERNEL |
| 3191 | FREE_MB_T(*mp); |
| 3192 | #endif |
| 3193 | m = *mp = NULL; |
| 3194 | } |
| 3195 | } else { |
| 3196 | LBUMP(ipf_stats[out].fr_pass); |
| 3197 | #if defined(_KERNEL) && defined(__sgi) |
| 3198 | if ((fin->fin_hbuf != NULL) && |
| 3199 | (mtod(fin->fin_m, struct ip *) != fin->fin_ip)) { |
| 3200 | COPYBACK(fin->fin_m, 0, fin->fin_plen, fin->fin_hbuf); |
| 3201 | } |
| 3202 | #endif |
| 3203 | } |
| 3204 | |
| 3205 | SPL_X(s); |
| 3206 | |
| 3207 | #ifdef _KERNEL |
| 3208 | if (FR_ISPASS(pass)) |
| 3209 | return 0; |
| 3210 | LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); |
| 3211 | return fin->fin_error; |
| 3212 | #else /* _KERNEL */ |
| 3213 | if (*mp != NULL) |
| 3214 | (*mp)->mb_ifp = fin->fin_ifp; |
| 3215 | blockreason = fin->fin_reason; |
| 3216 | FR_VERBOSE(("fin_flx %#x pass %#x " , fin->fin_flx, pass)); |
| 3217 | /*if ((pass & FR_CMDMASK) == (softc->ipf_pass & FR_CMDMASK))*/ |
| 3218 | if ((pass & FR_NOMATCH) != 0) |
| 3219 | return 1; |
| 3220 | |
| 3221 | if ((pass & FR_RETMASK) != 0) |
| 3222 | switch (pass & FR_RETMASK) |
| 3223 | { |
| 3224 | case FR_RETRST : |
| 3225 | return 3; |
| 3226 | case FR_RETICMP : |
| 3227 | return 4; |
| 3228 | case FR_FAKEICMP : |
| 3229 | return 5; |
| 3230 | } |
| 3231 | |
| 3232 | switch (pass & FR_CMDMASK) |
| 3233 | { |
| 3234 | case FR_PASS : |
| 3235 | return 0; |
| 3236 | case FR_BLOCK : |
| 3237 | return -1; |
| 3238 | case FR_AUTH : |
| 3239 | return -2; |
| 3240 | case FR_ACCOUNT : |
| 3241 | return -3; |
| 3242 | case FR_PREAUTH : |
| 3243 | return -4; |
| 3244 | } |
| 3245 | return 2; |
| 3246 | #endif /* _KERNEL */ |
| 3247 | } |
| 3248 | |
| 3249 | |
| 3250 | #ifdef IPFILTER_LOG |
| 3251 | /* ------------------------------------------------------------------------ */ |
| 3252 | /* Function: ipf_dolog */ |
| 3253 | /* Returns: frentry_t* - returns contents of fin_fr (no change made) */ |
| 3254 | /* Parameters: fin(I) - pointer to packet information */ |
| 3255 | /* passp(IO) - pointer to current/new filter decision (unused) */ |
| 3256 | /* */ |
| 3257 | /* Checks flags set to see how a packet should be logged, if it is to be */ |
| 3258 | /* logged. Adjust statistics based on its success or not. */ |
| 3259 | /* ------------------------------------------------------------------------ */ |
| 3260 | frentry_t * |
| 3261 | ipf_dolog(fr_info_t *fin, u_32_t *passp) |
| 3262 | { |
| 3263 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 3264 | u_32_t pass; |
| 3265 | int out; |
| 3266 | |
| 3267 | out = fin->fin_out; |
| 3268 | pass = *passp; |
| 3269 | |
| 3270 | if ((softc->ipf_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { |
| 3271 | pass |= FF_LOGNOMATCH; |
| 3272 | LBUMPD(ipf_stats[out], fr_npkl); |
| 3273 | goto logit; |
| 3274 | |
| 3275 | } else if (((pass & FR_LOGMASK) == FR_LOGP) || |
| 3276 | (FR_ISPASS(pass) && (softc->ipf_flags & FF_LOGPASS))) { |
| 3277 | if ((pass & FR_LOGMASK) != FR_LOGP) |
| 3278 | pass |= FF_LOGPASS; |
| 3279 | LBUMPD(ipf_stats[out], fr_ppkl); |
| 3280 | goto logit; |
| 3281 | |
| 3282 | } else if (((pass & FR_LOGMASK) == FR_LOGB) || |
| 3283 | (FR_ISBLOCK(pass) && (softc->ipf_flags & FF_LOGBLOCK))) { |
| 3284 | if ((pass & FR_LOGMASK) != FR_LOGB) |
| 3285 | pass |= FF_LOGBLOCK; |
| 3286 | LBUMPD(ipf_stats[out], fr_bpkl); |
| 3287 | |
| 3288 | logit: |
| 3289 | if (ipf_log_pkt(fin, pass) == -1) { |
| 3290 | /* |
| 3291 | * If the "or-block" option has been used then |
| 3292 | * block the packet if we failed to log it. |
| 3293 | */ |
| 3294 | if ((pass & FR_LOGORBLOCK) && FR_ISPASS(pass)) { |
| 3295 | DT1(frb_logfail2, u_int, pass); |
| 3296 | pass &= ~FR_CMDMASK; |
| 3297 | pass |= FR_BLOCK; |
| 3298 | fin->fin_reason = FRB_LOGFAIL2; |
| 3299 | } |
| 3300 | } |
| 3301 | *passp = pass; |
| 3302 | } |
| 3303 | |
| 3304 | return fin->fin_fr; |
| 3305 | } |
| 3306 | #endif /* IPFILTER_LOG */ |
| 3307 | |
| 3308 | |
| 3309 | /* ------------------------------------------------------------------------ */ |
| 3310 | /* Function: ipf_cksum */ |
| 3311 | /* Returns: u_short - IP header checksum */ |
| 3312 | /* Parameters: addr(I) - pointer to start of buffer to checksum */ |
| 3313 | /* len(I) - length of buffer in bytes */ |
| 3314 | /* */ |
| 3315 | /* Calculate the two's complement 16 bit checksum of the buffer passed. */ |
| 3316 | /* */ |
| 3317 | /* N.B.: addr should be 16bit aligned. */ |
| 3318 | /* ------------------------------------------------------------------------ */ |
| 3319 | u_short |
| 3320 | ipf_cksum(u_short *addr, int len) |
| 3321 | { |
| 3322 | u_32_t sum = 0; |
| 3323 | |
| 3324 | for (sum = 0; len > 1; len -= 2) |
| 3325 | sum += *addr++; |
| 3326 | |
| 3327 | /* mop up an odd byte, if necessary */ |
| 3328 | if (len == 1) |
| 3329 | sum += *(u_char *)addr; |
| 3330 | |
| 3331 | /* |
| 3332 | * add back carry outs from top 16 bits to low 16 bits |
| 3333 | */ |
| 3334 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ |
| 3335 | sum += (sum >> 16); /* add carry */ |
| 3336 | return (u_short)(~sum); |
| 3337 | } |
| 3338 | |
| 3339 | |
| 3340 | /* ------------------------------------------------------------------------ */ |
| 3341 | /* Function: fr_cksum */ |
| 3342 | /* Returns: u_short - layer 4 checksum */ |
| 3343 | /* Parameters: fin(I) - pointer to packet information */ |
| 3344 | /* ip(I) - pointer to IP header */ |
| 3345 | /* l4proto(I) - protocol to caclulate checksum for */ |
| 3346 | /* l4hdr(I) - pointer to layer 4 header */ |
| 3347 | /* */ |
| 3348 | /* Calculates the TCP checksum for the packet held in "m", using the data */ |
| 3349 | /* in the IP header "ip" to seed it. */ |
| 3350 | /* */ |
| 3351 | /* NB: This function assumes we've pullup'd enough for all of the IP header */ |
| 3352 | /* and the TCP header. We also assume that data blocks aren't allocated in */ |
| 3353 | /* odd sizes. */ |
| 3354 | /* */ |
| 3355 | /* Expects ip_len and ip_off to be in network byte order when called. */ |
| 3356 | /* ------------------------------------------------------------------------ */ |
| 3357 | u_short |
| 3358 | fr_cksum(fr_info_t *fin, ip_t *ip, int l4proto, void *l4hdr) |
| 3359 | { |
| 3360 | u_short *sp, slen, sumsave, *csump; |
| 3361 | u_int sum, sum2; |
| 3362 | int hlen; |
| 3363 | int off; |
| 3364 | #ifdef USE_INET6 |
| 3365 | ip6_t *ip6; |
| 3366 | #endif |
| 3367 | |
| 3368 | csump = NULL; |
| 3369 | sumsave = 0; |
| 3370 | sp = NULL; |
| 3371 | slen = 0; |
| 3372 | hlen = 0; |
| 3373 | sum = 0; |
| 3374 | |
| 3375 | sum = htons((u_short)l4proto); |
| 3376 | /* |
| 3377 | * Add up IP Header portion |
| 3378 | */ |
| 3379 | #ifdef USE_INET6 |
| 3380 | if (IP_V(ip) == 4) { |
| 3381 | #endif |
| 3382 | hlen = IP_HL(ip) << 2; |
| 3383 | off = hlen; |
| 3384 | sp = (u_short *)&ip->ip_src; |
| 3385 | sum += *sp++; /* ip_src */ |
| 3386 | sum += *sp++; |
| 3387 | sum += *sp++; /* ip_dst */ |
| 3388 | sum += *sp++; |
| 3389 | #ifdef USE_INET6 |
| 3390 | } else if (IP_V(ip) == 6) { |
| 3391 | ip6 = (ip6_t *)ip; |
| 3392 | hlen = sizeof(*ip6); |
| 3393 | off = ((char *)fin->fin_dp - (char *)fin->fin_ip); |
| 3394 | sp = (u_short *)&ip6->ip6_src; |
| 3395 | sum += *sp++; /* ip6_src */ |
| 3396 | sum += *sp++; |
| 3397 | sum += *sp++; |
| 3398 | sum += *sp++; |
| 3399 | sum += *sp++; |
| 3400 | sum += *sp++; |
| 3401 | sum += *sp++; |
| 3402 | sum += *sp++; |
| 3403 | /* This needs to be routing header aware. */ |
| 3404 | sum += *sp++; /* ip6_dst */ |
| 3405 | sum += *sp++; |
| 3406 | sum += *sp++; |
| 3407 | sum += *sp++; |
| 3408 | sum += *sp++; |
| 3409 | sum += *sp++; |
| 3410 | sum += *sp++; |
| 3411 | sum += *sp++; |
| 3412 | } else { |
| 3413 | return 0xffff; |
| 3414 | } |
| 3415 | #endif |
| 3416 | slen = fin->fin_plen - off; |
| 3417 | sum += htons(slen); |
| 3418 | |
| 3419 | switch (l4proto) |
| 3420 | { |
| 3421 | case IPPROTO_UDP : |
| 3422 | csump = &((udphdr_t *)l4hdr)->uh_sum; |
| 3423 | break; |
| 3424 | |
| 3425 | case IPPROTO_TCP : |
| 3426 | csump = &((tcphdr_t *)l4hdr)->th_sum; |
| 3427 | break; |
| 3428 | case IPPROTO_ICMP : |
| 3429 | csump = &((icmphdr_t *)l4hdr)->icmp_cksum; |
| 3430 | sum = 0; /* Pseudo-checksum is not included */ |
| 3431 | break; |
| 3432 | #ifdef USE_INET6 |
| 3433 | case IPPROTO_ICMPV6 : |
| 3434 | csump = &((struct icmp6_hdr *)l4hdr)->icmp6_cksum; |
| 3435 | break; |
| 3436 | #endif |
| 3437 | default : |
| 3438 | break; |
| 3439 | } |
| 3440 | |
| 3441 | if (csump != NULL) { |
| 3442 | sumsave = *csump; |
| 3443 | *csump = 0; |
| 3444 | } |
| 3445 | |
| 3446 | sum2 = ipf_pcksum(fin, off, sum); |
| 3447 | if (csump != NULL) |
| 3448 | *csump = sumsave; |
| 3449 | return sum2; |
| 3450 | } |
| 3451 | |
| 3452 | |
| 3453 | /* ------------------------------------------------------------------------ */ |
| 3454 | /* Function: ipf_findgroup */ |
| 3455 | /* Returns: frgroup_t * - NULL = group not found, else pointer to group */ |
| 3456 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 3457 | /* group(I) - group name to search for */ |
| 3458 | /* unit(I) - device to which this group belongs */ |
| 3459 | /* set(I) - which set of rules (inactive/inactive) this is */ |
| 3460 | /* fgpp(O) - pointer to place to store pointer to the pointer */ |
| 3461 | /* to where to add the next (last) group or where */ |
| 3462 | /* to delete group from. */ |
| 3463 | /* */ |
| 3464 | /* Search amongst the defined groups for a particular group number. */ |
| 3465 | /* ------------------------------------------------------------------------ */ |
| 3466 | frgroup_t * |
| 3467 | ipf_findgroup(ipf_main_softc_t *softc, char *group, minor_t unit, int set, |
| 3468 | frgroup_t ***fgpp) |
| 3469 | { |
| 3470 | frgroup_t *fg, **fgp; |
| 3471 | |
| 3472 | /* |
| 3473 | * Which list of groups to search in is dependent on which list of |
| 3474 | * rules are being operated on. |
| 3475 | */ |
| 3476 | fgp = &softc->ipf_groups[unit][set]; |
| 3477 | |
| 3478 | while ((fg = *fgp) != NULL) { |
| 3479 | if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0) |
| 3480 | break; |
| 3481 | else |
| 3482 | fgp = &fg->fg_next; |
| 3483 | } |
| 3484 | if (fgpp != NULL) |
| 3485 | *fgpp = fgp; |
| 3486 | return fg; |
| 3487 | } |
| 3488 | |
| 3489 | |
| 3490 | /* ------------------------------------------------------------------------ */ |
| 3491 | /* Function: ipf_group_add */ |
| 3492 | /* Returns: frgroup_t * - NULL == did not create group, */ |
| 3493 | /* != NULL == pointer to the group */ |
| 3494 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 3495 | /* num(I) - group number to add */ |
| 3496 | /* head(I) - rule pointer that is using this as the head */ |
| 3497 | /* flags(I) - rule flags which describe the type of rule it is */ |
| 3498 | /* unit(I) - device to which this group will belong to */ |
| 3499 | /* set(I) - which set of rules (inactive/inactive) this is */ |
| 3500 | /* Write Locks: ipf_mutex */ |
| 3501 | /* */ |
| 3502 | /* Add a new group head, or if it already exists, increase the reference */ |
| 3503 | /* count to it. */ |
| 3504 | /* ------------------------------------------------------------------------ */ |
| 3505 | frgroup_t * |
| 3506 | ipf_group_add(ipf_main_softc_t *softc, char *group, void *head, u_32_t flags, |
| 3507 | minor_t unit, int set) |
| 3508 | { |
| 3509 | frgroup_t *fg, **fgp; |
| 3510 | u_32_t gflags; |
| 3511 | |
| 3512 | if (group == NULL) |
| 3513 | return NULL; |
| 3514 | |
| 3515 | if (unit == IPL_LOGIPF && *group == '\0') |
| 3516 | return NULL; |
| 3517 | |
| 3518 | fgp = NULL; |
| 3519 | gflags = flags & FR_INOUT; |
| 3520 | |
| 3521 | fg = ipf_findgroup(softc, group, unit, set, &fgp); |
| 3522 | if (fg != NULL) { |
| 3523 | if (fg->fg_head == NULL && head != NULL) |
| 3524 | fg->fg_head = head; |
| 3525 | if (fg->fg_flags == 0) |
| 3526 | fg->fg_flags = gflags; |
| 3527 | else if (gflags != fg->fg_flags) |
| 3528 | return NULL; |
| 3529 | fg->fg_ref++; |
| 3530 | return fg; |
| 3531 | } |
| 3532 | |
| 3533 | KMALLOC(fg, frgroup_t *); |
| 3534 | if (fg != NULL) { |
| 3535 | fg->fg_head = head; |
| 3536 | fg->fg_start = NULL; |
| 3537 | fg->fg_next = *fgp; |
| 3538 | bcopy(group, fg->fg_name, strlen(group) + 1); |
| 3539 | fg->fg_flags = gflags; |
| 3540 | fg->fg_ref = 1; |
| 3541 | fg->fg_set = &softc->ipf_groups[unit][set]; |
| 3542 | *fgp = fg; |
| 3543 | } |
| 3544 | return fg; |
| 3545 | } |
| 3546 | |
| 3547 | |
| 3548 | /* ------------------------------------------------------------------------ */ |
| 3549 | /* Function: ipf_group_del */ |
| 3550 | /* Returns: int - number of rules deleted */ |
| 3551 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 3552 | /* group(I) - group name to delete */ |
| 3553 | /* fr(I) - filter rule from which group is referenced */ |
| 3554 | /* Write Locks: ipf_mutex */ |
| 3555 | /* */ |
| 3556 | /* This function is called whenever a reference to a group is to be dropped */ |
| 3557 | /* and thus its reference count needs to be lowered and the group free'd if */ |
| 3558 | /* the reference count reaches zero. Passing in fr is really for the sole */ |
| 3559 | /* purpose of knowing when the head rule is being deleted. */ |
| 3560 | /* ------------------------------------------------------------------------ */ |
| 3561 | void |
| 3562 | ipf_group_del(ipf_main_softc_t *softc, frgroup_t *group, frentry_t *fr) |
| 3563 | { |
| 3564 | |
| 3565 | if (group->fg_head == fr) |
| 3566 | group->fg_head = NULL; |
| 3567 | |
| 3568 | group->fg_ref--; |
| 3569 | if ((group->fg_ref == 0) && (group->fg_start == NULL)) |
| 3570 | ipf_group_free(group); |
| 3571 | } |
| 3572 | |
| 3573 | |
| 3574 | /* ------------------------------------------------------------------------ */ |
| 3575 | /* Function: ipf_group_free */ |
| 3576 | /* Returns: Nil */ |
| 3577 | /* Parameters: group(I) - pointer to filter rule group */ |
| 3578 | /* */ |
| 3579 | /* Remove the group from the list of groups and free it. */ |
| 3580 | /* ------------------------------------------------------------------------ */ |
| 3581 | static void |
| 3582 | ipf_group_free(frgroup_t *group) |
| 3583 | { |
| 3584 | frgroup_t **gp; |
| 3585 | |
| 3586 | for (gp = group->fg_set; *gp != NULL; gp = &(*gp)->fg_next) { |
| 3587 | if (*gp == group) { |
| 3588 | *gp = group->fg_next; |
| 3589 | break; |
| 3590 | } |
| 3591 | } |
| 3592 | KFREE(group); |
| 3593 | } |
| 3594 | |
| 3595 | |
| 3596 | /* ------------------------------------------------------------------------ */ |
| 3597 | /* Function: ipf_group_flush */ |
| 3598 | /* Returns: int - number of rules flush from group */ |
| 3599 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 3600 | /* Parameters: group(I) - pointer to filter rule group */ |
| 3601 | /* */ |
| 3602 | /* Remove all of the rules that currently are listed under the given group. */ |
| 3603 | /* ------------------------------------------------------------------------ */ |
| 3604 | static int |
| 3605 | ipf_group_flush(ipf_main_softc_t *softc, frgroup_t *group) |
| 3606 | { |
| 3607 | int gone = 0; |
| 3608 | |
| 3609 | (void) ipf_flushlist(softc, &gone, &group->fg_start); |
| 3610 | |
| 3611 | return gone; |
| 3612 | } |
| 3613 | |
| 3614 | |
| 3615 | /* ------------------------------------------------------------------------ */ |
| 3616 | /* Function: ipf_getrulen */ |
| 3617 | /* Returns: frentry_t * - NULL == not found, else pointer to rule n */ |
| 3618 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 3619 | /* Parameters: unit(I) - device for which to count the rule's number */ |
| 3620 | /* flags(I) - which set of rules to find the rule in */ |
| 3621 | /* group(I) - group name */ |
| 3622 | /* n(I) - rule number to find */ |
| 3623 | /* */ |
| 3624 | /* Find rule # n in group # g and return a pointer to it. Return NULl if */ |
| 3625 | /* group # g doesn't exist or there are less than n rules in the group. */ |
| 3626 | /* ------------------------------------------------------------------------ */ |
| 3627 | frentry_t * |
| 3628 | ipf_getrulen(ipf_main_softc_t *softc, int unit, char *group, u_32_t n) |
| 3629 | { |
| 3630 | frentry_t *fr; |
| 3631 | frgroup_t *fg; |
| 3632 | |
| 3633 | fg = ipf_findgroup(softc, group, unit, softc->ipf_active, NULL); |
| 3634 | if (fg == NULL) |
| 3635 | return NULL; |
| 3636 | for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--) |
| 3637 | ; |
| 3638 | if (n != 0) |
| 3639 | return NULL; |
| 3640 | return fr; |
| 3641 | } |
| 3642 | |
| 3643 | |
| 3644 | /* ------------------------------------------------------------------------ */ |
| 3645 | /* Function: ipf_flushlist */ |
| 3646 | /* Returns: int - >= 0 - number of flushed rules */ |
| 3647 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 3648 | /* nfreedp(O) - pointer to int where flush count is stored */ |
| 3649 | /* listp(I) - pointer to list to flush pointer */ |
| 3650 | /* Write Locks: ipf_mutex */ |
| 3651 | /* */ |
| 3652 | /* Recursively flush rules from the list, descending groups as they are */ |
| 3653 | /* encountered. if a rule is the head of a group and it has lost all its */ |
| 3654 | /* group members, then also delete the group reference. nfreedp is needed */ |
| 3655 | /* to store the accumulating count of rules removed, whereas the returned */ |
| 3656 | /* value is just the number removed from the current list. The latter is */ |
| 3657 | /* needed to correctly adjust reference counts on rules that define groups. */ |
| 3658 | /* */ |
| 3659 | /* NOTE: Rules not loaded from user space cannot be flushed. */ |
| 3660 | /* ------------------------------------------------------------------------ */ |
| 3661 | static int |
| 3662 | ipf_flushlist(ipf_main_softc_t *softc, int *nfreedp, frentry_t **listp) |
| 3663 | { |
| 3664 | int freed = 0; |
| 3665 | frentry_t *fp; |
| 3666 | |
| 3667 | while ((fp = *listp) != NULL) { |
| 3668 | if ((fp->fr_type & FR_T_BUILTIN) || |
| 3669 | !(fp->fr_flags & FR_COPIED)) { |
| 3670 | listp = &fp->fr_next; |
| 3671 | continue; |
| 3672 | } |
| 3673 | *listp = fp->fr_next; |
| 3674 | if (fp->fr_next != NULL) |
| 3675 | fp->fr_next->fr_pnext = fp->fr_pnext; |
| 3676 | fp->fr_pnext = NULL; |
| 3677 | |
| 3678 | if (fp->fr_grphead != NULL) { |
| 3679 | freed += ipf_group_flush(softc, fp->fr_grphead); |
| 3680 | fp->fr_names[fp->fr_grhead] = '\0'; |
| 3681 | } |
| 3682 | |
| 3683 | if (fp->fr_icmpgrp != NULL) { |
| 3684 | freed += ipf_group_flush(softc, fp->fr_icmpgrp); |
| 3685 | fp->fr_names[fp->fr_icmphead] = '\0'; |
| 3686 | } |
| 3687 | |
| 3688 | if (fp->fr_srctrack.ht_max_nodes) |
| 3689 | ipf_rb_ht_flush(&fp->fr_srctrack); |
| 3690 | |
| 3691 | fp->fr_next = NULL; |
| 3692 | |
| 3693 | ASSERT(fp->fr_ref > 0); |
| 3694 | if (ipf_derefrule(softc, &fp) == 0) |
| 3695 | freed++; |
| 3696 | } |
| 3697 | *nfreedp += freed; |
| 3698 | return freed; |
| 3699 | } |
| 3700 | |
| 3701 | |
| 3702 | /* ------------------------------------------------------------------------ */ |
| 3703 | /* Function: ipf_flush */ |
| 3704 | /* Returns: int - >= 0 - number of flushed rules */ |
| 3705 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 3706 | /* unit(I) - device for which to flush rules */ |
| 3707 | /* flags(I) - which set of rules to flush */ |
| 3708 | /* */ |
| 3709 | /* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */ |
| 3710 | /* and IPv6) as defined by the value of flags. */ |
| 3711 | /* ------------------------------------------------------------------------ */ |
| 3712 | int |
| 3713 | ipf_flush(ipf_main_softc_t *softc, minor_t unit, int flags) |
| 3714 | { |
| 3715 | int flushed = 0, set; |
| 3716 | |
| 3717 | WRITE_ENTER(&softc->ipf_mutex); |
| 3718 | |
| 3719 | set = softc->ipf_active; |
| 3720 | if ((flags & FR_INACTIVE) == FR_INACTIVE) |
| 3721 | set = 1 - set; |
| 3722 | |
| 3723 | if (flags & FR_OUTQUE) { |
| 3724 | ipf_flushlist(softc, &flushed, &softc->ipf_rules[1][set]); |
| 3725 | ipf_flushlist(softc, &flushed, &softc->ipf_acct[1][set]); |
| 3726 | } |
| 3727 | if (flags & FR_INQUE) { |
| 3728 | ipf_flushlist(softc, &flushed, &softc->ipf_rules[0][set]); |
| 3729 | ipf_flushlist(softc, &flushed, &softc->ipf_acct[0][set]); |
| 3730 | } |
| 3731 | |
| 3732 | flushed += ipf_flush_groups(softc, &softc->ipf_groups[unit][set], |
| 3733 | flags & (FR_INQUE|FR_OUTQUE)); |
| 3734 | |
| 3735 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 3736 | |
| 3737 | if (unit == IPL_LOGIPF) { |
| 3738 | int tmp; |
| 3739 | |
| 3740 | tmp = ipf_flush(softc, IPL_LOGCOUNT, flags); |
| 3741 | if (tmp >= 0) |
| 3742 | flushed += tmp; |
| 3743 | } |
| 3744 | return flushed; |
| 3745 | } |
| 3746 | |
| 3747 | |
| 3748 | /* ------------------------------------------------------------------------ */ |
| 3749 | /* Function: ipf_flush_groups */ |
| 3750 | /* Returns: int - >= 0 - number of flushed rules */ |
| 3751 | /* Parameters: softc(I) - soft context pointerto work with */ |
| 3752 | /* grhead(I) - pointer to the start of the group list to flush */ |
| 3753 | /* flags(I) - which set of rules to flush */ |
| 3754 | /* */ |
| 3755 | /* Walk through all of the groups under the given group head and remove all */ |
| 3756 | /* of those that match the flags passed in. The for loop here is bit more */ |
| 3757 | /* complicated than usual because the removal of a rule with ipf_derefrule */ |
| 3758 | /* may end up removing not only the structure pointed to by "fg" but also */ |
| 3759 | /* what is fg_next and fg_next after that. So if a filter rule is actually */ |
| 3760 | /* removed from the group then it is necessary to start again. */ |
| 3761 | /* ------------------------------------------------------------------------ */ |
| 3762 | static int |
| 3763 | ipf_flush_groups( ipf_main_softc_t *softc, frgroup_t **grhead, int flags) |
| 3764 | { |
| 3765 | frentry_t *fr, **frp; |
| 3766 | frgroup_t *fg, **fgp; |
| 3767 | int flushed = 0; |
| 3768 | int removed = 0; |
| 3769 | |
| 3770 | for (fgp = grhead; (fg = *fgp) != NULL; ) { |
| 3771 | while ((fg != NULL) && ((fg->fg_flags & flags) == 0)) |
| 3772 | fg = fg->fg_next; |
| 3773 | if (fg == NULL) |
| 3774 | break; |
| 3775 | removed = 0; |
| 3776 | frp = &fg->fg_start; |
| 3777 | while ((removed == 0) && ((fr = *frp) != NULL)) { |
| 3778 | if ((fr->fr_flags & flags) == 0) { |
| 3779 | frp = &fr->fr_next; |
| 3780 | } else { |
| 3781 | if (fr->fr_next != NULL) |
| 3782 | fr->fr_next->fr_pnext = fr->fr_pnext; |
| 3783 | *frp = fr->fr_next; |
| 3784 | fr->fr_pnext = NULL; |
| 3785 | fr->fr_next = NULL; |
| 3786 | (void) ipf_derefrule(softc, &fr); |
| 3787 | flushed++; |
| 3788 | removed++; |
| 3789 | } |
| 3790 | } |
| 3791 | if (removed == 0) |
| 3792 | fgp = &fg->fg_next; |
| 3793 | } |
| 3794 | return flushed; |
| 3795 | } |
| 3796 | |
| 3797 | |
| 3798 | /* ------------------------------------------------------------------------ */ |
| 3799 | /* Function: memstr */ |
| 3800 | /* Returns: char * - NULL if failed, != NULL pointer to matching bytes */ |
| 3801 | /* Parameters: src(I) - pointer to byte sequence to match */ |
| 3802 | /* dst(I) - pointer to byte sequence to search */ |
| 3803 | /* slen(I) - match length */ |
| 3804 | /* dlen(I) - length available to search in */ |
| 3805 | /* */ |
| 3806 | /* Search dst for a sequence of bytes matching those at src and extend for */ |
| 3807 | /* slen bytes. */ |
| 3808 | /* ------------------------------------------------------------------------ */ |
| 3809 | char * |
| 3810 | memstr(const char *src, char *dst, size_t slen, size_t dlen) |
| 3811 | { |
| 3812 | char *s = NULL; |
| 3813 | |
| 3814 | while (dlen >= slen) { |
| 3815 | if (memcmp(src, dst, slen) == 0) { |
| 3816 | s = dst; |
| 3817 | break; |
| 3818 | } |
| 3819 | dst++; |
| 3820 | dlen--; |
| 3821 | } |
| 3822 | return s; |
| 3823 | } |
| 3824 | |
| 3825 | |
| 3826 | /* ------------------------------------------------------------------------ */ |
| 3827 | /* Function: ipf_fixskip */ |
| 3828 | /* Returns: Nil */ |
| 3829 | /* Parameters: listp(IO) - pointer to start of list with skip rule */ |
| 3830 | /* rp(I) - rule added/removed with skip in it. */ |
| 3831 | /* addremove(I) - adjustment (-1/+1) to make to skip count, */ |
| 3832 | /* depending on whether a rule was just added */ |
| 3833 | /* or removed. */ |
| 3834 | /* */ |
| 3835 | /* Adjust all the rules in a list which would have skip'd past the position */ |
| 3836 | /* where we are inserting to skip to the right place given the change. */ |
| 3837 | /* ------------------------------------------------------------------------ */ |
| 3838 | void |
| 3839 | ipf_fixskip(frentry_t **listp, frentry_t *rp, int addremove) |
| 3840 | { |
| 3841 | int rules, rn; |
| 3842 | frentry_t *fp; |
| 3843 | |
| 3844 | rules = 0; |
| 3845 | for (fp = *listp; (fp != NULL) && (fp != rp); fp = fp->fr_next) |
| 3846 | rules++; |
| 3847 | |
| 3848 | if (!fp) |
| 3849 | return; |
| 3850 | |
| 3851 | for (rn = 0, fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) |
| 3852 | if (FR_ISSKIP(fp->fr_flags) && (rn + fp->fr_arg >= rules)) |
| 3853 | fp->fr_arg += addremove; |
| 3854 | } |
| 3855 | |
| 3856 | |
| 3857 | #ifdef _KERNEL |
| 3858 | /* ------------------------------------------------------------------------ */ |
| 3859 | /* Function: count4bits */ |
| 3860 | /* Returns: int - >= 0 - number of consecutive bits in input */ |
| 3861 | /* Parameters: ip(I) - 32bit IP address */ |
| 3862 | /* */ |
| 3863 | /* IPv4 ONLY */ |
| 3864 | /* count consecutive 1's in bit mask. If the mask generated by counting */ |
| 3865 | /* consecutive 1's is different to that passed, return -1, else return # */ |
| 3866 | /* of bits. */ |
| 3867 | /* ------------------------------------------------------------------------ */ |
| 3868 | int |
| 3869 | count4bits(u_32_t ip) |
| 3870 | { |
| 3871 | u_32_t ipn; |
| 3872 | int cnt = 0, i, j; |
| 3873 | |
| 3874 | ip = ipn = ntohl(ip); |
| 3875 | for (i = 32; i; i--, ipn *= 2) |
| 3876 | if (ipn & 0x80000000) |
| 3877 | cnt++; |
| 3878 | else |
| 3879 | break; |
| 3880 | ipn = 0; |
| 3881 | for (i = 32, j = cnt; i; i--, j--) { |
| 3882 | ipn *= 2; |
| 3883 | if (j > 0) |
| 3884 | ipn++; |
| 3885 | } |
| 3886 | if (ipn == ip) |
| 3887 | return cnt; |
| 3888 | return -1; |
| 3889 | } |
| 3890 | |
| 3891 | |
| 3892 | /* ------------------------------------------------------------------------ */ |
| 3893 | /* Function: count6bits */ |
| 3894 | /* Returns: int - >= 0 - number of consecutive bits in input */ |
| 3895 | /* Parameters: msk(I) - pointer to start of IPv6 bitmask */ |
| 3896 | /* */ |
| 3897 | /* IPv6 ONLY */ |
| 3898 | /* count consecutive 1's in bit mask. */ |
| 3899 | /* ------------------------------------------------------------------------ */ |
| 3900 | # ifdef USE_INET6 |
| 3901 | int |
| 3902 | count6bits(u_32_t *msk) |
| 3903 | { |
| 3904 | int i = 0, k; |
| 3905 | u_32_t j; |
| 3906 | |
| 3907 | for (k = 3; k >= 0; k--) |
| 3908 | if (msk[k] == 0xffffffff) |
| 3909 | i += 32; |
| 3910 | else { |
| 3911 | for (j = msk[k]; j; j <<= 1) |
| 3912 | if (j & 0x80000000) |
| 3913 | i++; |
| 3914 | } |
| 3915 | return i; |
| 3916 | } |
| 3917 | # endif |
| 3918 | #endif /* _KERNEL */ |
| 3919 | |
| 3920 | |
| 3921 | /* ------------------------------------------------------------------------ */ |
| 3922 | /* Function: ipf_synclist */ |
| 3923 | /* Returns: int - 0 = no failures, else indication of first failure */ |
| 3924 | /* Parameters: fr(I) - start of filter list to sync interface names for */ |
| 3925 | /* ifp(I) - interface pointer for limiting sync lookups */ |
| 3926 | /* Write Locks: ipf_mutex */ |
| 3927 | /* */ |
| 3928 | /* Walk through a list of filter rules and resolve any interface names into */ |
| 3929 | /* pointers. Where dynamic addresses are used, also update the IP address */ |
| 3930 | /* used in the rule. The interface pointer is used to limit the lookups to */ |
| 3931 | /* a specific set of matching names if it is non-NULL. */ |
| 3932 | /* Errors can occur when resolving the destination name of to/dup-to fields */ |
| 3933 | /* when the name points to a pool and that pool doest not exist. If this */ |
| 3934 | /* does happen then it is necessary to check if there are any lookup refs */ |
| 3935 | /* that need to be dropped before returning with an error. */ |
| 3936 | /* ------------------------------------------------------------------------ */ |
| 3937 | static int |
| 3938 | ipf_synclist(ipf_main_softc_t *softc, frentry_t *fr, void *ifp) |
| 3939 | { |
| 3940 | frentry_t *frt, *start = fr; |
| 3941 | frdest_t *fdp; |
| 3942 | char *name; |
| 3943 | int error; |
| 3944 | void *ifa; |
| 3945 | int v, i; |
| 3946 | |
| 3947 | error = 0; |
| 3948 | |
| 3949 | for (; fr; fr = fr->fr_next) { |
| 3950 | if (fr->fr_family == AF_INET) |
| 3951 | v = 4; |
| 3952 | else if (fr->fr_family == AF_INET6) |
| 3953 | v = 6; |
| 3954 | else |
| 3955 | v = 0; |
| 3956 | |
| 3957 | /* |
| 3958 | * Lookup all the interface names that are part of the rule. |
| 3959 | */ |
| 3960 | for (i = 0; i < 4; i++) { |
| 3961 | if ((ifp != NULL) && (fr->fr_ifas[i] != ifp)) |
| 3962 | continue; |
| 3963 | if (fr->fr_ifnames[i] == -1) |
| 3964 | continue; |
| 3965 | name = FR_NAME(fr, fr_ifnames[i]); |
| 3966 | fr->fr_ifas[i] = ipf_resolvenic(softc, name, v); |
| 3967 | } |
| 3968 | |
| 3969 | if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { |
| 3970 | if (fr->fr_satype != FRI_NORMAL && |
| 3971 | fr->fr_satype != FRI_LOOKUP) { |
| 3972 | ifa = ipf_resolvenic(softc, fr->fr_names + |
| 3973 | fr->fr_sifpidx, v); |
| 3974 | ipf_ifpaddr(softc, v, fr->fr_satype, ifa, |
| 3975 | &fr->fr_src6, &fr->fr_smsk6); |
| 3976 | } |
| 3977 | if (fr->fr_datype != FRI_NORMAL && |
| 3978 | fr->fr_datype != FRI_LOOKUP) { |
| 3979 | ifa = ipf_resolvenic(softc, fr->fr_names + |
| 3980 | fr->fr_sifpidx, v); |
| 3981 | ipf_ifpaddr(softc, v, fr->fr_datype, ifa, |
| 3982 | &fr->fr_dst6, &fr->fr_dmsk6); |
| 3983 | } |
| 3984 | } |
| 3985 | |
| 3986 | fdp = &fr->fr_tifs[0]; |
| 3987 | if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { |
| 3988 | error = ipf_resolvedest(softc, fr->fr_names, fdp, v); |
| 3989 | if (error != 0) |
| 3990 | goto unwind; |
| 3991 | } |
| 3992 | |
| 3993 | fdp = &fr->fr_tifs[1]; |
| 3994 | if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { |
| 3995 | error = ipf_resolvedest(softc, fr->fr_names, fdp, v); |
| 3996 | if (error != 0) |
| 3997 | goto unwind; |
| 3998 | } |
| 3999 | |
| 4000 | fdp = &fr->fr_dif; |
| 4001 | if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { |
| 4002 | error = ipf_resolvedest(softc, fr->fr_names, fdp, v); |
| 4003 | if (error != 0) |
| 4004 | goto unwind; |
| 4005 | } |
| 4006 | |
| 4007 | if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && |
| 4008 | (fr->fr_satype == FRI_LOOKUP) && (fr->fr_srcptr == NULL)) { |
| 4009 | fr->fr_srcptr = ipf_lookup_res_num(softc, |
| 4010 | fr->fr_srctype, |
| 4011 | IPL_LOGIPF, |
| 4012 | fr->fr_srcnum, |
| 4013 | &fr->fr_srcfunc); |
| 4014 | } |
| 4015 | if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && |
| 4016 | (fr->fr_datype == FRI_LOOKUP) && (fr->fr_dstptr == NULL)) { |
| 4017 | fr->fr_dstptr = ipf_lookup_res_num(softc, |
| 4018 | fr->fr_dsttype, |
| 4019 | IPL_LOGIPF, |
| 4020 | fr->fr_dstnum, |
| 4021 | &fr->fr_dstfunc); |
| 4022 | } |
| 4023 | } |
| 4024 | return 0; |
| 4025 | |
| 4026 | unwind: |
| 4027 | for (frt = start; frt != fr; fr = fr->fr_next) { |
| 4028 | if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && |
| 4029 | (frt->fr_satype == FRI_LOOKUP) && (frt->fr_srcptr != NULL)) |
| 4030 | ipf_lookup_deref(softc, frt->fr_srctype, |
| 4031 | frt->fr_srcptr); |
| 4032 | if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && |
| 4033 | (frt->fr_datype == FRI_LOOKUP) && (frt->fr_dstptr != NULL)) |
| 4034 | ipf_lookup_deref(softc, frt->fr_dsttype, |
| 4035 | frt->fr_dstptr); |
| 4036 | } |
| 4037 | return error; |
| 4038 | } |
| 4039 | |
| 4040 | |
| 4041 | /* ------------------------------------------------------------------------ */ |
| 4042 | /* Function: ipf_sync */ |
| 4043 | /* Returns: void */ |
| 4044 | /* Parameters: Nil */ |
| 4045 | /* */ |
| 4046 | /* ipf_sync() is called when we suspect that the interface list or */ |
| 4047 | /* information about interfaces (like IP#) has changed. Go through all */ |
| 4048 | /* filter rules, NAT entries and the state table and check if anything */ |
| 4049 | /* needs to be changed/updated. */ |
| 4050 | /* ------------------------------------------------------------------------ */ |
| 4051 | int |
| 4052 | ipf_sync(ipf_main_softc_t *softc, void *ifp) |
| 4053 | { |
| 4054 | int i; |
| 4055 | |
| 4056 | # if !SOLARIS |
| 4057 | ipf_nat_sync(softc, ifp); |
| 4058 | ipf_state_sync(softc, ifp); |
| 4059 | ipf_lookup_sync(softc, ifp); |
| 4060 | # endif |
| 4061 | |
| 4062 | WRITE_ENTER(&softc->ipf_mutex); |
| 4063 | (void) ipf_synclist(softc, softc->ipf_acct[0][softc->ipf_active], ifp); |
| 4064 | (void) ipf_synclist(softc, softc->ipf_acct[1][softc->ipf_active], ifp); |
| 4065 | (void) ipf_synclist(softc, softc->ipf_rules[0][softc->ipf_active], ifp); |
| 4066 | (void) ipf_synclist(softc, softc->ipf_rules[1][softc->ipf_active], ifp); |
| 4067 | |
| 4068 | for (i = 0; i < IPL_LOGSIZE; i++) { |
| 4069 | frgroup_t *g; |
| 4070 | |
| 4071 | for (g = softc->ipf_groups[i][0]; g != NULL; g = g->fg_next) |
| 4072 | (void) ipf_synclist(softc, g->fg_start, ifp); |
| 4073 | for (g = softc->ipf_groups[i][1]; g != NULL; g = g->fg_next) |
| 4074 | (void) ipf_synclist(softc, g->fg_start, ifp); |
| 4075 | } |
| 4076 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 4077 | |
| 4078 | return 0; |
| 4079 | } |
| 4080 | |
| 4081 | |
| 4082 | /* |
| 4083 | * In the functions below, bcopy() is called because the pointer being |
| 4084 | * copied _from_ in this instance is a pointer to a char buf (which could |
| 4085 | * end up being unaligned) and on the kernel's local stack. |
| 4086 | */ |
| 4087 | /* ------------------------------------------------------------------------ */ |
| 4088 | /* Function: copyinptr */ |
| 4089 | /* Returns: int - 0 = success, else failure */ |
| 4090 | /* Parameters: src(I) - pointer to the source address */ |
| 4091 | /* dst(I) - destination address */ |
| 4092 | /* size(I) - number of bytes to copy */ |
| 4093 | /* */ |
| 4094 | /* Copy a block of data in from user space, given a pointer to the pointer */ |
| 4095 | /* to start copying from (src) and a pointer to where to store it (dst). */ |
| 4096 | /* NB: src - pointer to user space pointer, dst - kernel space pointer */ |
| 4097 | /* ------------------------------------------------------------------------ */ |
| 4098 | int |
| 4099 | copyinptr(ipf_main_softc_t *softc, void *src, void *dst, size_t size) |
| 4100 | { |
| 4101 | void *ca; |
| 4102 | int error; |
| 4103 | |
| 4104 | # if SOLARIS |
| 4105 | error = COPYIN(src, &ca, sizeof(ca)); |
| 4106 | if (error != 0) |
| 4107 | return error; |
| 4108 | # else |
| 4109 | bcopy(src, (void *)&ca, sizeof(ca)); |
| 4110 | # endif |
| 4111 | error = COPYIN(ca, dst, size); |
| 4112 | if (error != 0) { |
| 4113 | IPFERROR(3); |
| 4114 | error = EFAULT; |
| 4115 | } |
| 4116 | return error; |
| 4117 | } |
| 4118 | |
| 4119 | |
| 4120 | /* ------------------------------------------------------------------------ */ |
| 4121 | /* Function: copyoutptr */ |
| 4122 | /* Returns: int - 0 = success, else failure */ |
| 4123 | /* Parameters: src(I) - pointer to the source address */ |
| 4124 | /* dst(I) - destination address */ |
| 4125 | /* size(I) - number of bytes to copy */ |
| 4126 | /* */ |
| 4127 | /* Copy a block of data out to user space, given a pointer to the pointer */ |
| 4128 | /* to start copying from (src) and a pointer to where to store it (dst). */ |
| 4129 | /* NB: src - kernel space pointer, dst - pointer to user space pointer. */ |
| 4130 | /* ------------------------------------------------------------------------ */ |
| 4131 | int |
| 4132 | copyoutptr(ipf_main_softc_t *softc, void *src, void *dst, size_t size) |
| 4133 | { |
| 4134 | void *ca; |
| 4135 | int error; |
| 4136 | |
| 4137 | bcopy(dst, &ca, sizeof(ca)); |
| 4138 | error = COPYOUT(src, ca, size); |
| 4139 | if (error != 0) { |
| 4140 | IPFERROR(4); |
| 4141 | error = EFAULT; |
| 4142 | } |
| 4143 | return error; |
| 4144 | } |
| 4145 | #ifdef _KERNEL |
| 4146 | #endif |
| 4147 | |
| 4148 | |
| 4149 | /* ------------------------------------------------------------------------ */ |
| 4150 | /* Function: ipf_lock */ |
| 4151 | /* Returns: int - 0 = success, else error */ |
| 4152 | /* Parameters: data(I) - pointer to lock value to set */ |
| 4153 | /* lockp(O) - pointer to location to store old lock value */ |
| 4154 | /* */ |
| 4155 | /* Get the new value for the lock integer, set it and return the old value */ |
| 4156 | /* in *lockp. */ |
| 4157 | /* ------------------------------------------------------------------------ */ |
| 4158 | int |
| 4159 | ipf_lock(void *data, int *lockp) |
| 4160 | { |
| 4161 | int arg, err; |
| 4162 | |
| 4163 | err = BCOPYIN(data, &arg, sizeof(arg)); |
| 4164 | if (err != 0) |
| 4165 | return EFAULT; |
| 4166 | err = BCOPYOUT(lockp, data, sizeof(*lockp)); |
| 4167 | if (err != 0) |
| 4168 | return EFAULT; |
| 4169 | *lockp = arg; |
| 4170 | return 0; |
| 4171 | } |
| 4172 | |
| 4173 | |
| 4174 | /* ------------------------------------------------------------------------ */ |
| 4175 | /* Function: ipf_getstat */ |
| 4176 | /* Returns: Nil */ |
| 4177 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 4178 | /* fiop(I) - pointer to ipfilter stats structure */ |
| 4179 | /* rev(I) - version claim by program doing ioctl */ |
| 4180 | /* */ |
| 4181 | /* Stores a copy of current pointers, counters, etc, in the friostat */ |
| 4182 | /* structure. */ |
| 4183 | /* If IPFILTER_COMPAT is compiled, we pretend to be whatever version the */ |
| 4184 | /* program is looking for. This ensure that validation of the version it */ |
| 4185 | /* expects will always succeed. Thus kernels with IPFILTER_COMPAT will */ |
| 4186 | /* allow older binaries to work but kernels without it will not. */ |
| 4187 | /* ------------------------------------------------------------------------ */ |
| 4188 | /*ARGSUSED*/ |
| 4189 | static void |
| 4190 | ipf_getstat(ipf_main_softc_t *softc, friostat_t *fiop, int rev) |
| 4191 | { |
| 4192 | int i; |
| 4193 | |
| 4194 | bcopy((char *)softc->ipf_stats, (char *)fiop->f_st, |
| 4195 | sizeof(ipf_statistics_t) * 2); |
| 4196 | fiop->f_locks[IPL_LOGSTATE] = -1; |
| 4197 | fiop->f_locks[IPL_LOGNAT] = -1; |
| 4198 | fiop->f_locks[IPL_LOGIPF] = -1; |
| 4199 | fiop->f_locks[IPL_LOGAUTH] = -1; |
| 4200 | |
| 4201 | fiop->f_ipf[0][0] = softc->ipf_rules[0][0]; |
| 4202 | fiop->f_acct[0][0] = softc->ipf_acct[0][0]; |
| 4203 | fiop->f_ipf[0][1] = softc->ipf_rules[0][1]; |
| 4204 | fiop->f_acct[0][1] = softc->ipf_acct[0][1]; |
| 4205 | fiop->f_ipf[1][0] = softc->ipf_rules[1][0]; |
| 4206 | fiop->f_acct[1][0] = softc->ipf_acct[1][0]; |
| 4207 | fiop->f_ipf[1][1] = softc->ipf_rules[1][1]; |
| 4208 | fiop->f_acct[1][1] = softc->ipf_acct[1][1]; |
| 4209 | |
| 4210 | fiop->f_ticks = softc->ipf_ticks; |
| 4211 | fiop->f_active = softc->ipf_active; |
| 4212 | fiop->f_froute[0] = softc->ipf_frouteok[0]; |
| 4213 | fiop->f_froute[1] = softc->ipf_frouteok[1]; |
| 4214 | fiop->f_rb_no_mem = softc->ipf_rb_no_mem; |
| 4215 | fiop->f_rb_node_max = softc->ipf_rb_node_max; |
| 4216 | |
| 4217 | fiop->f_running = softc->ipf_running; |
| 4218 | for (i = 0; i < IPL_LOGSIZE; i++) { |
| 4219 | fiop->f_groups[i][0] = softc->ipf_groups[i][0]; |
| 4220 | fiop->f_groups[i][1] = softc->ipf_groups[i][1]; |
| 4221 | } |
| 4222 | #ifdef IPFILTER_LOG |
| 4223 | fiop->f_log_ok = ipf_log_logok(softc, IPL_LOGIPF); |
| 4224 | fiop->f_log_fail = ipf_log_failures(softc, IPL_LOGIPF); |
| 4225 | fiop->f_logging = 1; |
| 4226 | #else |
| 4227 | fiop->f_log_ok = 0; |
| 4228 | fiop->f_log_fail = 0; |
| 4229 | fiop->f_logging = 0; |
| 4230 | #endif |
| 4231 | fiop->f_defpass = softc->ipf_pass; |
| 4232 | fiop->f_features = ipf_features; |
| 4233 | |
| 4234 | #ifdef IPFILTER_COMPAT |
| 4235 | snprintf(fiop->f_version, sizeof(fiop->f_version), |
| 4236 | "IP Filter: v%d.%d.%d" , (rev / 1000000) % 100, |
| 4237 | (rev / 10000) % 100, (rev / 100) % 100); |
| 4238 | #else |
| 4239 | rev = rev; |
| 4240 | (void) strncpy(fiop->f_version, ipfilter_version, |
| 4241 | sizeof(fiop->f_version)); |
| 4242 | fiop->f_version[sizeof(fiop->f_version) - 1] = '\0'; |
| 4243 | #endif |
| 4244 | } |
| 4245 | |
| 4246 | |
| 4247 | #ifdef USE_INET6 |
| 4248 | int icmptoicmp6types[ICMP_MAXTYPE+1] = { |
| 4249 | ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */ |
| 4250 | -1, /* 1: UNUSED */ |
| 4251 | -1, /* 2: UNUSED */ |
| 4252 | ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */ |
| 4253 | -1, /* 4: ICMP_SOURCEQUENCH */ |
| 4254 | ND_REDIRECT, /* 5: ICMP_REDIRECT */ |
| 4255 | -1, /* 6: UNUSED */ |
| 4256 | -1, /* 7: UNUSED */ |
| 4257 | ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */ |
| 4258 | -1, /* 9: UNUSED */ |
| 4259 | -1, /* 10: UNUSED */ |
| 4260 | ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */ |
| 4261 | ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */ |
| 4262 | -1, /* 13: ICMP_TSTAMP */ |
| 4263 | -1, /* 14: ICMP_TSTAMPREPLY */ |
| 4264 | -1, /* 15: ICMP_IREQ */ |
| 4265 | -1, /* 16: ICMP_IREQREPLY */ |
| 4266 | -1, /* 17: ICMP_MASKREQ */ |
| 4267 | -1, /* 18: ICMP_MASKREPLY */ |
| 4268 | }; |
| 4269 | |
| 4270 | |
| 4271 | int icmptoicmp6unreach[ICMP_MAX_UNREACH] = { |
| 4272 | ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */ |
| 4273 | ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */ |
| 4274 | -1, /* 2: ICMP_UNREACH_PROTOCOL */ |
| 4275 | ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */ |
| 4276 | -1, /* 4: ICMP_UNREACH_NEEDFRAG */ |
| 4277 | ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */ |
| 4278 | ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */ |
| 4279 | ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */ |
| 4280 | -1, /* 8: ICMP_UNREACH_ISOLATED */ |
| 4281 | ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */ |
| 4282 | ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */ |
| 4283 | -1, /* 11: ICMP_UNREACH_TOSNET */ |
| 4284 | -1, /* 12: ICMP_UNREACH_TOSHOST */ |
| 4285 | ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */ |
| 4286 | }; |
| 4287 | int icmpreplytype6[ICMP6_MAXTYPE + 1]; |
| 4288 | #endif |
| 4289 | |
| 4290 | int icmpreplytype4[ICMP_MAXTYPE + 1]; |
| 4291 | |
| 4292 | |
| 4293 | /* ------------------------------------------------------------------------ */ |
| 4294 | /* Function: ipf_matchicmpqueryreply */ |
| 4295 | /* Returns: int - 1 if "icmp" is a valid reply to "ic" else 0. */ |
| 4296 | /* Parameters: v(I) - IP protocol version (4 or 6) */ |
| 4297 | /* ic(I) - ICMP information */ |
| 4298 | /* icmp(I) - ICMP packet header */ |
| 4299 | /* rev(I) - direction (0 = forward/1 = reverse) of packet */ |
| 4300 | /* */ |
| 4301 | /* Check if the ICMP packet defined by the header pointed to by icmp is a */ |
| 4302 | /* reply to one as described by what's in ic. If it is a match, return 1, */ |
| 4303 | /* else return 0 for no match. */ |
| 4304 | /* ------------------------------------------------------------------------ */ |
| 4305 | int |
| 4306 | ipf_matchicmpqueryreply(int v, icmpinfo_t *ic, icmphdr_t *icmp, int rev) |
| 4307 | { |
| 4308 | int ictype; |
| 4309 | |
| 4310 | ictype = ic->ici_type; |
| 4311 | |
| 4312 | if (v == 4) { |
| 4313 | /* |
| 4314 | * If we matched its type on the way in, then when going out |
| 4315 | * it will still be the same type. |
| 4316 | */ |
| 4317 | if ((!rev && (icmp->icmp_type == ictype)) || |
| 4318 | (rev && (icmpreplytype4[ictype] == icmp->icmp_type))) { |
| 4319 | if (icmp->icmp_type != ICMP_ECHOREPLY) |
| 4320 | return 1; |
| 4321 | if (icmp->icmp_id == ic->ici_id) |
| 4322 | return 1; |
| 4323 | } |
| 4324 | } |
| 4325 | #ifdef USE_INET6 |
| 4326 | else if (v == 6) { |
| 4327 | if ((!rev && (icmp->icmp_type == ictype)) || |
| 4328 | (rev && (icmpreplytype6[ictype] == icmp->icmp_type))) { |
| 4329 | if (icmp->icmp_type != ICMP6_ECHO_REPLY) |
| 4330 | return 1; |
| 4331 | if (icmp->icmp_id == ic->ici_id) |
| 4332 | return 1; |
| 4333 | } |
| 4334 | } |
| 4335 | #endif |
| 4336 | return 0; |
| 4337 | } |
| 4338 | |
| 4339 | /* ------------------------------------------------------------------------ */ |
| 4340 | /* Function: ipf_rule_compare */ |
| 4341 | /* Parameters: fr1(I) - first rule structure to compare */ |
| 4342 | /* fr2(I) - second rule structure to compare */ |
| 4343 | /* Returns: int - 0 == rules are the same, else mismatch */ |
| 4344 | /* */ |
| 4345 | /* Compare two rules and return 0 if they match or a number indicating */ |
| 4346 | /* which of the individual checks failed. */ |
| 4347 | /* ------------------------------------------------------------------------ */ |
| 4348 | static int |
| 4349 | ipf_rule_compare(frentry_t *fr1, frentry_t *fr2) |
| 4350 | { |
| 4351 | if (fr1->fr_cksum != fr2->fr_cksum) |
| 4352 | return 1; |
| 4353 | if (fr1->fr_size != fr2->fr_size) |
| 4354 | return 2; |
| 4355 | if (fr1->fr_dsize != fr2->fr_dsize) |
| 4356 | return 3; |
| 4357 | if (memcmp(&fr1->fr_func, &fr2->fr_func, |
| 4358 | fr1->fr_size - offsetof(struct frentry, fr_func)) != 0) |
| 4359 | return 4; |
| 4360 | if (fr1->fr_data && !fr2->fr_data) |
| 4361 | return 5; |
| 4362 | if (!fr1->fr_data && fr2->fr_data) |
| 4363 | return 6; |
| 4364 | if (fr1->fr_data) { |
| 4365 | if (memcmp(fr1->fr_caddr, fr2->fr_caddr, fr1->fr_dsize)) |
| 4366 | return 7; |
| 4367 | } |
| 4368 | return 0; |
| 4369 | } |
| 4370 | |
| 4371 | |
| 4372 | /* ------------------------------------------------------------------------ */ |
| 4373 | /* Function: frrequest */ |
| 4374 | /* Returns: int - 0 == success, > 0 == errno value */ |
| 4375 | /* Parameters: unit(I) - device for which this is for */ |
| 4376 | /* req(I) - ioctl command (SIOC*) */ |
| 4377 | /* data(I) - pointr to ioctl data */ |
| 4378 | /* set(I) - 1 or 0 (filter set) */ |
| 4379 | /* makecopy(I) - flag indicating whether data points to a rule */ |
| 4380 | /* in kernel space & hence doesn't need copying. */ |
| 4381 | /* */ |
| 4382 | /* This function handles all the requests which operate on the list of */ |
| 4383 | /* filter rules. This includes adding, deleting, insertion. It is also */ |
| 4384 | /* responsible for creating groups when a "head" rule is loaded. Interface */ |
| 4385 | /* names are resolved here and other sanity checks are made on the content */ |
| 4386 | /* of the rule structure being loaded. If a rule has user defined timeouts */ |
| 4387 | /* then make sure they are created and initialised before exiting. */ |
| 4388 | /* ------------------------------------------------------------------------ */ |
| 4389 | int |
| 4390 | frrequest(ipf_main_softc_t *softc, int unit, ioctlcmd_t req, void *data, |
| 4391 | int set, int makecopy) |
| 4392 | { |
| 4393 | int error = 0, in, family, addrem, need_free = 0; |
| 4394 | frentry_t frd, *fp, *f, **fprev, **ftail; |
| 4395 | void *ptr, *uptr; |
| 4396 | u_int *p, *pp; |
| 4397 | frgroup_t *fg; |
| 4398 | char *group; |
| 4399 | |
| 4400 | ptr = NULL; |
| 4401 | fg = NULL; |
| 4402 | fp = &frd; |
| 4403 | if (makecopy != 0) { |
| 4404 | bzero(fp, sizeof(frd)); |
| 4405 | error = ipf_inobj(softc, data, NULL, fp, IPFOBJ_FRENTRY); |
| 4406 | if (error) { |
| 4407 | return error; |
| 4408 | } |
| 4409 | if ((fp->fr_type & FR_T_BUILTIN) != 0) { |
| 4410 | IPFERROR(6); |
| 4411 | return EINVAL; |
| 4412 | } |
| 4413 | KMALLOCS(f, frentry_t *, fp->fr_size); |
| 4414 | if (f == NULL) { |
| 4415 | IPFERROR(131); |
| 4416 | return ENOMEM; |
| 4417 | } |
| 4418 | bzero(f, fp->fr_size); |
| 4419 | error = ipf_inobjsz(softc, data, f, IPFOBJ_FRENTRY, |
| 4420 | fp->fr_size); |
| 4421 | if (error) { |
| 4422 | KFREES(f, fp->fr_size); |
| 4423 | return error; |
| 4424 | } |
| 4425 | |
| 4426 | fp = f; |
| 4427 | f = NULL; |
| 4428 | fp->fr_next = NULL; |
| 4429 | fp->fr_dnext = NULL; |
| 4430 | fp->fr_pnext = NULL; |
| 4431 | fp->fr_pdnext = NULL; |
| 4432 | fp->fr_grp = NULL; |
| 4433 | fp->fr_grphead = NULL; |
| 4434 | fp->fr_icmpgrp = NULL; |
| 4435 | fp->fr_isc = (void *)-1; |
| 4436 | fp->fr_ptr = NULL; |
| 4437 | fp->fr_ref = 0; |
| 4438 | fp->fr_flags |= FR_COPIED; |
| 4439 | } else { |
| 4440 | fp = (frentry_t *)data; |
| 4441 | if ((fp->fr_type & FR_T_BUILTIN) == 0) { |
| 4442 | IPFERROR(7); |
| 4443 | return EINVAL; |
| 4444 | } |
| 4445 | fp->fr_flags &= ~FR_COPIED; |
| 4446 | } |
| 4447 | |
| 4448 | if (((fp->fr_dsize == 0) && (fp->fr_data != NULL)) || |
| 4449 | ((fp->fr_dsize != 0) && (fp->fr_data == NULL))) { |
| 4450 | IPFERROR(8); |
| 4451 | error = EINVAL; |
| 4452 | goto donenolock; |
| 4453 | } |
| 4454 | |
| 4455 | family = fp->fr_family; |
| 4456 | uptr = fp->fr_data; |
| 4457 | |
| 4458 | if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR || |
| 4459 | req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) |
| 4460 | addrem = 0; |
| 4461 | else if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) |
| 4462 | addrem = 1; |
| 4463 | else if (req == (ioctlcmd_t)SIOCZRLST) |
| 4464 | addrem = 2; |
| 4465 | else { |
| 4466 | IPFERROR(9); |
| 4467 | error = EINVAL; |
| 4468 | goto donenolock; |
| 4469 | } |
| 4470 | |
| 4471 | /* |
| 4472 | * Only filter rules for IPv4 or IPv6 are accepted. |
| 4473 | */ |
| 4474 | if (family == AF_INET) { |
| 4475 | /*EMPTY*/; |
| 4476 | #ifdef USE_INET6 |
| 4477 | } else if (family == AF_INET6) { |
| 4478 | /*EMPTY*/; |
| 4479 | #endif |
| 4480 | } else if (family != 0) { |
| 4481 | IPFERROR(10); |
| 4482 | error = EINVAL; |
| 4483 | goto donenolock; |
| 4484 | } |
| 4485 | |
| 4486 | /* |
| 4487 | * If the rule is being loaded from user space, i.e. we had to copy it |
| 4488 | * into kernel space, then do not trust the function pointer in the |
| 4489 | * rule. |
| 4490 | */ |
| 4491 | if ((makecopy == 1) && (fp->fr_func != NULL)) { |
| 4492 | if (ipf_findfunc(fp->fr_func) == NULL) { |
| 4493 | IPFERROR(11); |
| 4494 | error = ESRCH; |
| 4495 | goto donenolock; |
| 4496 | } |
| 4497 | |
| 4498 | if (addrem == 0) { |
| 4499 | error = ipf_funcinit(softc, fp); |
| 4500 | if (error != 0) |
| 4501 | goto donenolock; |
| 4502 | } |
| 4503 | } |
| 4504 | if ((fp->fr_flags & FR_CALLNOW) && |
| 4505 | ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { |
| 4506 | IPFERROR(142); |
| 4507 | error = ESRCH; |
| 4508 | goto donenolock; |
| 4509 | } |
| 4510 | if (((fp->fr_flags & FR_CMDMASK) == FR_CALL) && |
| 4511 | ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { |
| 4512 | IPFERROR(143); |
| 4513 | error = ESRCH; |
| 4514 | goto donenolock; |
| 4515 | } |
| 4516 | |
| 4517 | ptr = NULL; |
| 4518 | |
| 4519 | if (FR_ISACCOUNT(fp->fr_flags)) |
| 4520 | unit = IPL_LOGCOUNT; |
| 4521 | |
| 4522 | /* |
| 4523 | * Check that each group name in the rule has a start index that |
| 4524 | * is valid. |
| 4525 | */ |
| 4526 | if (fp->fr_icmphead != -1) { |
| 4527 | if ((fp->fr_icmphead < 0) || |
| 4528 | (fp->fr_icmphead >= fp->fr_namelen)) { |
| 4529 | IPFERROR(136); |
| 4530 | error = EINVAL; |
| 4531 | goto donenolock; |
| 4532 | } |
| 4533 | if (!strcmp(FR_NAME(fp, fr_icmphead), "0" )) |
| 4534 | fp->fr_names[fp->fr_icmphead] = '\0'; |
| 4535 | } |
| 4536 | |
| 4537 | if (fp->fr_grhead != -1) { |
| 4538 | if ((fp->fr_grhead < 0) || |
| 4539 | (fp->fr_grhead >= fp->fr_namelen)) { |
| 4540 | IPFERROR(137); |
| 4541 | error = EINVAL; |
| 4542 | goto donenolock; |
| 4543 | } |
| 4544 | if (!strcmp(FR_NAME(fp, fr_grhead), "0" )) |
| 4545 | fp->fr_names[fp->fr_grhead] = '\0'; |
| 4546 | } |
| 4547 | |
| 4548 | if (fp->fr_group != -1) { |
| 4549 | if ((fp->fr_group < 0) || |
| 4550 | (fp->fr_group >= fp->fr_namelen)) { |
| 4551 | IPFERROR(138); |
| 4552 | error = EINVAL; |
| 4553 | goto donenolock; |
| 4554 | } |
| 4555 | if ((req != (int)SIOCZRLST) && (fp->fr_group != -1)) { |
| 4556 | /* |
| 4557 | * Allow loading rules that are in groups to cause |
| 4558 | * them to be created if they don't already exit. |
| 4559 | */ |
| 4560 | group = FR_NAME(fp, fr_group); |
| 4561 | if (addrem == 0) { |
| 4562 | fg = ipf_group_add(softc, group, NULL, |
| 4563 | fp->fr_flags, unit, set); |
| 4564 | if (fg == NULL) { |
| 4565 | IPFERROR(152); |
| 4566 | error = ESRCH; |
| 4567 | goto donenolock; |
| 4568 | } |
| 4569 | fp->fr_grp = fg; |
| 4570 | } else { |
| 4571 | fg = ipf_findgroup(softc, group, unit, |
| 4572 | set, NULL); |
| 4573 | if (fg == NULL) { |
| 4574 | IPFERROR(12); |
| 4575 | error = ESRCH; |
| 4576 | goto donenolock; |
| 4577 | } |
| 4578 | } |
| 4579 | |
| 4580 | if (fg->fg_flags == 0) { |
| 4581 | fg->fg_flags = fp->fr_flags & FR_INOUT; |
| 4582 | } else if (fg->fg_flags != (fp->fr_flags & FR_INOUT)) { |
| 4583 | IPFERROR(13); |
| 4584 | error = ESRCH; |
| 4585 | goto donenolock; |
| 4586 | } |
| 4587 | } |
| 4588 | } else { |
| 4589 | /* |
| 4590 | * If a rule is going to be part of a group then it does |
| 4591 | * not matter whether it is an in or out rule, but if it |
| 4592 | * isn't in a group, then it does... |
| 4593 | */ |
| 4594 | if ((fp->fr_flags & (FR_INQUE|FR_OUTQUE)) == 0) { |
| 4595 | IPFERROR(14); |
| 4596 | error = EINVAL; |
| 4597 | goto donenolock; |
| 4598 | } |
| 4599 | } |
| 4600 | in = (fp->fr_flags & FR_INQUE) ? 0 : 1; |
| 4601 | |
| 4602 | /* |
| 4603 | * Work out which rule list this change is being applied to. |
| 4604 | */ |
| 4605 | ftail = NULL; |
| 4606 | fprev = NULL; |
| 4607 | if (unit == IPL_LOGAUTH) { |
| 4608 | if ((fp->fr_tifs[0].fd_ptr != NULL) || |
| 4609 | (fp->fr_tifs[1].fd_ptr != NULL) || |
| 4610 | (fp->fr_dif.fd_ptr != NULL) || |
| 4611 | (fp->fr_flags & FR_FASTROUTE)) { |
| 4612 | IPFERROR(145); |
| 4613 | error = EINVAL; |
| 4614 | goto donenolock; |
| 4615 | } |
| 4616 | fprev = ipf_auth_rulehead(softc); |
| 4617 | } else { |
| 4618 | if (FR_ISACCOUNT(fp->fr_flags)) |
| 4619 | fprev = &softc->ipf_acct[in][set]; |
| 4620 | else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) |
| 4621 | fprev = &softc->ipf_rules[in][set]; |
| 4622 | } |
| 4623 | if (fprev == NULL) { |
| 4624 | IPFERROR(15); |
| 4625 | error = ESRCH; |
| 4626 | goto donenolock; |
| 4627 | } |
| 4628 | |
| 4629 | if (fg != NULL) |
| 4630 | fprev = &fg->fg_start; |
| 4631 | |
| 4632 | /* |
| 4633 | * Copy in extra data for the rule. |
| 4634 | */ |
| 4635 | if (fp->fr_dsize != 0) { |
| 4636 | if (makecopy != 0) { |
| 4637 | KMALLOCS(ptr, void *, fp->fr_dsize); |
| 4638 | if (ptr == NULL) { |
| 4639 | IPFERROR(16); |
| 4640 | error = ENOMEM; |
| 4641 | goto donenolock; |
| 4642 | } |
| 4643 | |
| 4644 | /* |
| 4645 | * The bcopy case is for when the data is appended |
| 4646 | * to the rule by ipf_in_compat(). |
| 4647 | */ |
| 4648 | if (uptr >= (void *)fp && |
| 4649 | uptr < (void *)((char *)fp + fp->fr_size)) { |
| 4650 | bcopy(uptr, ptr, fp->fr_dsize); |
| 4651 | error = 0; |
| 4652 | } else { |
| 4653 | error = COPYIN(uptr, ptr, fp->fr_dsize); |
| 4654 | if (error != 0) { |
| 4655 | IPFERROR(17); |
| 4656 | error = EFAULT; |
| 4657 | goto donenolock; |
| 4658 | } |
| 4659 | } |
| 4660 | } else { |
| 4661 | ptr = uptr; |
| 4662 | } |
| 4663 | fp->fr_data = ptr; |
| 4664 | } else { |
| 4665 | fp->fr_data = NULL; |
| 4666 | } |
| 4667 | |
| 4668 | /* |
| 4669 | * Perform per-rule type sanity checks of their members. |
| 4670 | * All code after this needs to be aware that allocated memory |
| 4671 | * may need to be free'd before exiting. |
| 4672 | */ |
| 4673 | switch (fp->fr_type & ~FR_T_BUILTIN) |
| 4674 | { |
| 4675 | #if defined(IPFILTER_BPF) |
| 4676 | case FR_T_BPFOPC : |
| 4677 | if (fp->fr_dsize == 0) { |
| 4678 | IPFERROR(19); |
| 4679 | error = EINVAL; |
| 4680 | break; |
| 4681 | } |
| 4682 | if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) { |
| 4683 | IPFERROR(20); |
| 4684 | error = EINVAL; |
| 4685 | break; |
| 4686 | } |
| 4687 | break; |
| 4688 | #endif |
| 4689 | case FR_T_IPF : |
| 4690 | /* |
| 4691 | * Preparation for error case at the bottom of this function. |
| 4692 | */ |
| 4693 | if (fp->fr_datype == FRI_LOOKUP) |
| 4694 | fp->fr_dstptr = NULL; |
| 4695 | if (fp->fr_satype == FRI_LOOKUP) |
| 4696 | fp->fr_srcptr = NULL; |
| 4697 | |
| 4698 | if (fp->fr_dsize != sizeof(fripf_t)) { |
| 4699 | IPFERROR(21); |
| 4700 | error = EINVAL; |
| 4701 | break; |
| 4702 | } |
| 4703 | |
| 4704 | /* |
| 4705 | * Allowing a rule with both "keep state" and "with oow" is |
| 4706 | * pointless because adding a state entry to the table will |
| 4707 | * fail with the out of window (oow) flag set. |
| 4708 | */ |
| 4709 | if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW)) { |
| 4710 | IPFERROR(22); |
| 4711 | error = EINVAL; |
| 4712 | break; |
| 4713 | } |
| 4714 | |
| 4715 | switch (fp->fr_satype) |
| 4716 | { |
| 4717 | case FRI_BROADCAST : |
| 4718 | case FRI_DYNAMIC : |
| 4719 | case FRI_NETWORK : |
| 4720 | case FRI_NETMASKED : |
| 4721 | case FRI_PEERADDR : |
| 4722 | if (fp->fr_sifpidx < 0) { |
| 4723 | IPFERROR(23); |
| 4724 | error = EINVAL; |
| 4725 | } |
| 4726 | break; |
| 4727 | case FRI_LOOKUP : |
| 4728 | fp->fr_srcptr = ipf_findlookup(softc, unit, fp, |
| 4729 | &fp->fr_src6, |
| 4730 | &fp->fr_smsk6); |
| 4731 | if (fp->fr_srcfunc == NULL) { |
| 4732 | IPFERROR(132); |
| 4733 | error = ESRCH; |
| 4734 | break; |
| 4735 | } |
| 4736 | break; |
| 4737 | case FRI_NORMAL : |
| 4738 | break; |
| 4739 | default : |
| 4740 | IPFERROR(133); |
| 4741 | error = EINVAL; |
| 4742 | break; |
| 4743 | } |
| 4744 | if (error != 0) |
| 4745 | break; |
| 4746 | |
| 4747 | switch (fp->fr_datype) |
| 4748 | { |
| 4749 | case FRI_BROADCAST : |
| 4750 | case FRI_DYNAMIC : |
| 4751 | case FRI_NETWORK : |
| 4752 | case FRI_NETMASKED : |
| 4753 | case FRI_PEERADDR : |
| 4754 | if (fp->fr_difpidx < 0) { |
| 4755 | IPFERROR(24); |
| 4756 | error = EINVAL; |
| 4757 | } |
| 4758 | break; |
| 4759 | case FRI_LOOKUP : |
| 4760 | fp->fr_dstptr = ipf_findlookup(softc, unit, fp, |
| 4761 | &fp->fr_dst6, |
| 4762 | &fp->fr_dmsk6); |
| 4763 | if (fp->fr_dstfunc == NULL) { |
| 4764 | IPFERROR(134); |
| 4765 | error = ESRCH; |
| 4766 | } |
| 4767 | break; |
| 4768 | case FRI_NORMAL : |
| 4769 | break; |
| 4770 | default : |
| 4771 | IPFERROR(135); |
| 4772 | error = EINVAL; |
| 4773 | } |
| 4774 | break; |
| 4775 | |
| 4776 | case FR_T_NONE : |
| 4777 | case FR_T_CALLFUNC : |
| 4778 | case FR_T_COMPIPF : |
| 4779 | break; |
| 4780 | |
| 4781 | case FR_T_IPFEXPR : |
| 4782 | if (ipf_matcharray_verify(fp->fr_data, fp->fr_dsize) == -1) { |
| 4783 | IPFERROR(25); |
| 4784 | error = EINVAL; |
| 4785 | } |
| 4786 | break; |
| 4787 | |
| 4788 | default : |
| 4789 | IPFERROR(26); |
| 4790 | error = EINVAL; |
| 4791 | break; |
| 4792 | } |
| 4793 | if (error != 0) |
| 4794 | goto donenolock; |
| 4795 | |
| 4796 | if (fp->fr_tif.fd_name != -1) { |
| 4797 | if ((fp->fr_tif.fd_name < 0) || |
| 4798 | (fp->fr_tif.fd_name >= fp->fr_namelen)) { |
| 4799 | IPFERROR(139); |
| 4800 | error = EINVAL; |
| 4801 | goto donenolock; |
| 4802 | } |
| 4803 | } |
| 4804 | |
| 4805 | if (fp->fr_dif.fd_name != -1) { |
| 4806 | if ((fp->fr_dif.fd_name < 0) || |
| 4807 | (fp->fr_dif.fd_name >= fp->fr_namelen)) { |
| 4808 | IPFERROR(140); |
| 4809 | error = EINVAL; |
| 4810 | goto donenolock; |
| 4811 | } |
| 4812 | } |
| 4813 | |
| 4814 | if (fp->fr_rif.fd_name != -1) { |
| 4815 | if ((fp->fr_rif.fd_name < 0) || |
| 4816 | (fp->fr_rif.fd_name >= fp->fr_namelen)) { |
| 4817 | IPFERROR(141); |
| 4818 | error = EINVAL; |
| 4819 | goto donenolock; |
| 4820 | } |
| 4821 | } |
| 4822 | |
| 4823 | /* |
| 4824 | * Lookup all the interface names that are part of the rule. |
| 4825 | */ |
| 4826 | error = ipf_synclist(softc, fp, NULL); |
| 4827 | if (error != 0) |
| 4828 | goto donenolock; |
| 4829 | fp->fr_statecnt = 0; |
| 4830 | if (fp->fr_srctrack.ht_max_nodes != 0) |
| 4831 | ipf_rb_ht_init(&fp->fr_srctrack); |
| 4832 | |
| 4833 | /* |
| 4834 | * Look for an existing matching filter rule, but don't include the |
| 4835 | * next or interface pointer in the comparison (fr_next, fr_ifa). |
| 4836 | * This elminates rules which are indentical being loaded. Checksum |
| 4837 | * the constant part of the filter rule to make comparisons quicker |
| 4838 | * (this meaning no pointers are included). |
| 4839 | */ |
| 4840 | for (fp->fr_cksum = 0, p = (u_int *)&fp->fr_func, pp = &fp->fr_cksum; |
| 4841 | p < pp; p++) |
| 4842 | fp->fr_cksum += *p; |
| 4843 | pp = (u_int *)((char *)fp->fr_caddr + fp->fr_dsize); |
| 4844 | for (p = (u_int *)fp->fr_data; p < pp; p++) |
| 4845 | fp->fr_cksum += *p; |
| 4846 | |
| 4847 | WRITE_ENTER(&softc->ipf_mutex); |
| 4848 | |
| 4849 | /* |
| 4850 | * Now that the filter rule lists are locked, we can walk the |
| 4851 | * chain of them without fear. |
| 4852 | */ |
| 4853 | ftail = fprev; |
| 4854 | for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) { |
| 4855 | if (fp->fr_collect <= f->fr_collect) { |
| 4856 | ftail = fprev; |
| 4857 | f = NULL; |
| 4858 | break; |
| 4859 | } |
| 4860 | fprev = ftail; |
| 4861 | } |
| 4862 | |
| 4863 | for (; (f = *ftail) != NULL; ftail = &f->fr_next) { |
| 4864 | DT2(rule_cmp, frentry_t *, fp, frentry_t *, f); |
| 4865 | if (ipf_rule_compare(fp, f) == 0) |
| 4866 | break; |
| 4867 | } |
| 4868 | |
| 4869 | /* |
| 4870 | * If zero'ing statistics, copy current to caller and zero. |
| 4871 | */ |
| 4872 | if (addrem == 2) { |
| 4873 | if (f == NULL) { |
| 4874 | IPFERROR(27); |
| 4875 | error = ESRCH; |
| 4876 | } else { |
| 4877 | /* |
| 4878 | * Copy and reduce lock because of impending copyout. |
| 4879 | * Well we should, but if we do then the atomicity of |
| 4880 | * this call and the correctness of fr_hits and |
| 4881 | * fr_bytes cannot be guaranteed. As it is, this code |
| 4882 | * only resets them to 0 if they are successfully |
| 4883 | * copied out into user space. |
| 4884 | */ |
| 4885 | bcopy((char *)f, (char *)fp, f->fr_size); |
| 4886 | /* MUTEX_DOWNGRADE(&softc->ipf_mutex); */ |
| 4887 | |
| 4888 | /* |
| 4889 | * When we copy this rule back out, set the data |
| 4890 | * pointer to be what it was in user space. |
| 4891 | */ |
| 4892 | fp->fr_data = uptr; |
| 4893 | error = ipf_outobj(softc, data, fp, IPFOBJ_FRENTRY); |
| 4894 | |
| 4895 | if (error == 0) { |
| 4896 | if ((f->fr_dsize != 0) && (uptr != NULL)) |
| 4897 | error = COPYOUT(f->fr_data, uptr, |
| 4898 | f->fr_dsize); |
| 4899 | if (error != 0) { |
| 4900 | IPFERROR(28); |
| 4901 | error = EFAULT; |
| 4902 | } |
| 4903 | if (error == 0) { |
| 4904 | f->fr_hits = 0; |
| 4905 | f->fr_bytes = 0; |
| 4906 | } |
| 4907 | } |
| 4908 | } |
| 4909 | |
| 4910 | if (makecopy != 0) { |
| 4911 | if (ptr != NULL) { |
| 4912 | KFREES(ptr, fp->fr_dsize); |
| 4913 | } |
| 4914 | KFREES(fp, fp->fr_size); |
| 4915 | } |
| 4916 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 4917 | return error; |
| 4918 | } |
| 4919 | |
| 4920 | if (!f) { |
| 4921 | /* |
| 4922 | * At the end of this, ftail must point to the place where the |
| 4923 | * new rule is to be saved/inserted/added. |
| 4924 | * For SIOCAD*FR, this should be the last rule in the group of |
| 4925 | * rules that have equal fr_collect fields. |
| 4926 | * For SIOCIN*FR, ... |
| 4927 | */ |
| 4928 | if (req == (ioctlcmd_t)SIOCADAFR || |
| 4929 | req == (ioctlcmd_t)SIOCADIFR) { |
| 4930 | |
| 4931 | for (ftail = fprev; (f = *ftail) != NULL; ) { |
| 4932 | if (f->fr_collect > fp->fr_collect) |
| 4933 | break; |
| 4934 | ftail = &f->fr_next; |
| 4935 | fprev = ftail; |
| 4936 | } |
| 4937 | ftail = fprev; |
| 4938 | f = NULL; |
| 4939 | ptr = NULL; |
| 4940 | } else if (req == (ioctlcmd_t)SIOCINAFR || |
| 4941 | req == (ioctlcmd_t)SIOCINIFR) { |
| 4942 | while ((f = *fprev) != NULL) { |
| 4943 | if (f->fr_collect >= fp->fr_collect) |
| 4944 | break; |
| 4945 | fprev = &f->fr_next; |
| 4946 | } |
| 4947 | ftail = fprev; |
| 4948 | if (fp->fr_hits != 0) { |
| 4949 | while (fp->fr_hits && (f = *ftail)) { |
| 4950 | if (f->fr_collect != fp->fr_collect) |
| 4951 | break; |
| 4952 | fprev = ftail; |
| 4953 | ftail = &f->fr_next; |
| 4954 | fp->fr_hits--; |
| 4955 | } |
| 4956 | } |
| 4957 | f = NULL; |
| 4958 | ptr = NULL; |
| 4959 | } |
| 4960 | } |
| 4961 | |
| 4962 | /* |
| 4963 | * Request to remove a rule. |
| 4964 | */ |
| 4965 | if (addrem == 1) { |
| 4966 | if (!f) { |
| 4967 | IPFERROR(29); |
| 4968 | error = ESRCH; |
| 4969 | } else { |
| 4970 | /* |
| 4971 | * Do not allow activity from user space to interfere |
| 4972 | * with rules not loaded that way. |
| 4973 | */ |
| 4974 | if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) { |
| 4975 | IPFERROR(30); |
| 4976 | error = EPERM; |
| 4977 | goto done; |
| 4978 | } |
| 4979 | |
| 4980 | /* |
| 4981 | * Return EBUSY if the rule is being reference by |
| 4982 | * something else (eg state information.) |
| 4983 | */ |
| 4984 | if (f->fr_ref > 1) { |
| 4985 | IPFERROR(31); |
| 4986 | error = EBUSY; |
| 4987 | goto done; |
| 4988 | } |
| 4989 | #ifdef IPFILTER_SCAN |
| 4990 | if (f->fr_isctag != -1 && |
| 4991 | (f->fr_isc != (struct ipscan *)-1)) |
| 4992 | ipf_scan_detachfr(f); |
| 4993 | #endif |
| 4994 | |
| 4995 | if (unit == IPL_LOGAUTH) { |
| 4996 | error = ipf_auth_precmd(softc, req, f, ftail); |
| 4997 | goto done; |
| 4998 | } |
| 4999 | |
| 5000 | ipf_rule_delete(softc, f, unit, set); |
| 5001 | |
| 5002 | need_free = makecopy; |
| 5003 | } |
| 5004 | } else { |
| 5005 | /* |
| 5006 | * Not removing, so we must be adding/inserting a rule. |
| 5007 | */ |
| 5008 | if (f != NULL) { |
| 5009 | IPFERROR(32); |
| 5010 | error = EEXIST; |
| 5011 | goto done; |
| 5012 | } |
| 5013 | if (unit == IPL_LOGAUTH) { |
| 5014 | error = ipf_auth_precmd(softc, req, fp, ftail); |
| 5015 | goto done; |
| 5016 | } |
| 5017 | |
| 5018 | MUTEX_NUKE(&fp->fr_lock); |
| 5019 | MUTEX_INIT(&fp->fr_lock, "filter rule lock" ); |
| 5020 | if (fp->fr_die != 0) |
| 5021 | ipf_rule_expire_insert(softc, fp, set); |
| 5022 | |
| 5023 | fp->fr_hits = 0; |
| 5024 | if (makecopy != 0) |
| 5025 | fp->fr_ref = 1; |
| 5026 | fp->fr_pnext = ftail; |
| 5027 | fp->fr_next = *ftail; |
| 5028 | if (fp->fr_next != NULL) |
| 5029 | fp->fr_next->fr_pnext = &fp->fr_next; |
| 5030 | *ftail = fp; |
| 5031 | if (addrem == 0) |
| 5032 | ipf_fixskip(ftail, fp, 1); |
| 5033 | |
| 5034 | fp->fr_icmpgrp = NULL; |
| 5035 | if (fp->fr_icmphead != -1) { |
| 5036 | group = FR_NAME(fp, fr_icmphead); |
| 5037 | fg = ipf_group_add(softc, group, fp, 0, unit, set); |
| 5038 | fp->fr_icmpgrp = fg; |
| 5039 | } |
| 5040 | |
| 5041 | fp->fr_grphead = NULL; |
| 5042 | if (fp->fr_grhead != -1) { |
| 5043 | group = FR_NAME(fp, fr_grhead); |
| 5044 | fg = ipf_group_add(softc, group, fp, fp->fr_flags, |
| 5045 | unit, set); |
| 5046 | fp->fr_grphead = fg; |
| 5047 | } |
| 5048 | } |
| 5049 | done: |
| 5050 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 5051 | donenolock: |
| 5052 | if (need_free || (error != 0)) { |
| 5053 | if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { |
| 5054 | if ((fp->fr_satype == FRI_LOOKUP) && |
| 5055 | (fp->fr_srcptr != NULL)) |
| 5056 | ipf_lookup_deref(softc, fp->fr_srctype, |
| 5057 | fp->fr_srcptr); |
| 5058 | if ((fp->fr_datype == FRI_LOOKUP) && |
| 5059 | (fp->fr_dstptr != NULL)) |
| 5060 | ipf_lookup_deref(softc, fp->fr_dsttype, |
| 5061 | fp->fr_dstptr); |
| 5062 | } |
| 5063 | if (fp->fr_grp != NULL) { |
| 5064 | WRITE_ENTER(&softc->ipf_mutex); |
| 5065 | ipf_group_del(softc, fp->fr_grp, fp); |
| 5066 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 5067 | } |
| 5068 | if ((ptr != NULL) && (makecopy != 0)) { |
| 5069 | KFREES(ptr, fp->fr_dsize); |
| 5070 | } |
| 5071 | KFREES(fp, fp->fr_size); |
| 5072 | } |
| 5073 | return (error); |
| 5074 | } |
| 5075 | |
| 5076 | |
| 5077 | /* ------------------------------------------------------------------------ */ |
| 5078 | /* Function: ipf_rule_delete */ |
| 5079 | /* Returns: Nil */ |
| 5080 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 5081 | /* f(I) - pointer to the rule being deleted */ |
| 5082 | /* ftail(I) - pointer to the pointer to f */ |
| 5083 | /* unit(I) - device for which this is for */ |
| 5084 | /* set(I) - 1 or 0 (filter set) */ |
| 5085 | /* */ |
| 5086 | /* This function attempts to do what it can to delete a filter rule: remove */ |
| 5087 | /* it from any linked lists and remove any groups it is responsible for. */ |
| 5088 | /* But in the end, removing a rule can only drop the reference count - we */ |
| 5089 | /* must use that as the guide for whether or not it can be freed. */ |
| 5090 | /* ------------------------------------------------------------------------ */ |
| 5091 | static void |
| 5092 | ipf_rule_delete(ipf_main_softc_t *softc, frentry_t *f, int unit, int set) |
| 5093 | { |
| 5094 | |
| 5095 | /* |
| 5096 | * If fr_pdnext is set, then the rule is on the expire list, so |
| 5097 | * remove it from there. |
| 5098 | */ |
| 5099 | if (f->fr_pdnext != NULL) { |
| 5100 | *f->fr_pdnext = f->fr_dnext; |
| 5101 | if (f->fr_dnext != NULL) |
| 5102 | f->fr_dnext->fr_pdnext = f->fr_pdnext; |
| 5103 | f->fr_pdnext = NULL; |
| 5104 | f->fr_dnext = NULL; |
| 5105 | } |
| 5106 | |
| 5107 | ipf_fixskip(f->fr_pnext, f, -1); |
| 5108 | if (f->fr_pnext != NULL) |
| 5109 | *f->fr_pnext = f->fr_next; |
| 5110 | if (f->fr_next != NULL) |
| 5111 | f->fr_next->fr_pnext = f->fr_pnext; |
| 5112 | f->fr_pnext = NULL; |
| 5113 | f->fr_next = NULL; |
| 5114 | |
| 5115 | (void) ipf_derefrule(softc, &f); |
| 5116 | } |
| 5117 | |
| 5118 | /* ------------------------------------------------------------------------ */ |
| 5119 | /* Function: ipf_rule_expire_insert */ |
| 5120 | /* Returns: Nil */ |
| 5121 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 5122 | /* f(I) - pointer to rule to be added to expire list */ |
| 5123 | /* set(I) - 1 or 0 (filter set) */ |
| 5124 | /* */ |
| 5125 | /* If the new rule has a given expiration time, insert it into the list of */ |
| 5126 | /* expiring rules with the ones to be removed first added to the front of */ |
| 5127 | /* the list. The insertion is O(n) but it is kept sorted for quick scans at */ |
| 5128 | /* expiration interval checks. */ |
| 5129 | /* ------------------------------------------------------------------------ */ |
| 5130 | static void |
| 5131 | ipf_rule_expire_insert(ipf_main_softc_t *softc, frentry_t *f, int set) |
| 5132 | { |
| 5133 | frentry_t *fr; |
| 5134 | |
| 5135 | /* |
| 5136 | */ |
| 5137 | |
| 5138 | f->fr_die = softc->ipf_ticks + IPF_TTLVAL(f->fr_die); |
| 5139 | for (fr = softc->ipf_rule_explist[set]; fr != NULL; |
| 5140 | fr = fr->fr_dnext) { |
| 5141 | if (f->fr_die < fr->fr_die) |
| 5142 | break; |
| 5143 | if (fr->fr_dnext == NULL) { |
| 5144 | /* |
| 5145 | * We've got to the last rule and everything |
| 5146 | * wanted to be expired before this new node, |
| 5147 | * so we have to tack it on the end... |
| 5148 | */ |
| 5149 | fr->fr_dnext = f; |
| 5150 | f->fr_pdnext = &fr->fr_dnext; |
| 5151 | fr = NULL; |
| 5152 | break; |
| 5153 | } |
| 5154 | } |
| 5155 | |
| 5156 | if (softc->ipf_rule_explist[set] == NULL) { |
| 5157 | softc->ipf_rule_explist[set] = f; |
| 5158 | f->fr_pdnext = &softc->ipf_rule_explist[set]; |
| 5159 | } else if (fr != NULL) { |
| 5160 | f->fr_dnext = fr; |
| 5161 | f->fr_pdnext = fr->fr_pdnext; |
| 5162 | fr->fr_pdnext = &f->fr_dnext; |
| 5163 | } |
| 5164 | } |
| 5165 | |
| 5166 | |
| 5167 | /* ------------------------------------------------------------------------ */ |
| 5168 | /* Function: ipf_findlookup */ |
| 5169 | /* Returns: NULL = failure, else success */ |
| 5170 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 5171 | /* unit(I) - ipf device we want to find match for */ |
| 5172 | /* fp(I) - rule for which lookup is for */ |
| 5173 | /* addrp(I) - pointer to lookup information in address struct */ |
| 5174 | /* maskp(O) - pointer to lookup information for storage */ |
| 5175 | /* */ |
| 5176 | /* When using pools and hash tables to store addresses for matching in */ |
| 5177 | /* rules, it is necessary to resolve both the object referred to by the */ |
| 5178 | /* name or address (and return that pointer) and also provide the means by */ |
| 5179 | /* which to determine if an address belongs to that object to make the */ |
| 5180 | /* packet matching quicker. */ |
| 5181 | /* ------------------------------------------------------------------------ */ |
| 5182 | static void * |
| 5183 | ipf_findlookup(ipf_main_softc_t *softc, int unit, frentry_t *fr, |
| 5184 | i6addr_t *addrp, i6addr_t *maskp) |
| 5185 | { |
| 5186 | void *ptr = NULL; |
| 5187 | |
| 5188 | switch (addrp->iplookupsubtype) |
| 5189 | { |
| 5190 | case 0 : |
| 5191 | ptr = ipf_lookup_res_num(softc, unit, addrp->iplookuptype, |
| 5192 | addrp->iplookupnum, |
| 5193 | &maskp->iplookupfunc); |
| 5194 | break; |
| 5195 | case 1 : |
| 5196 | if (addrp->iplookupname < 0) |
| 5197 | break; |
| 5198 | if (addrp->iplookupname >= fr->fr_namelen) |
| 5199 | break; |
| 5200 | ptr = ipf_lookup_res_name(softc, unit, addrp->iplookuptype, |
| 5201 | fr->fr_names + addrp->iplookupname, |
| 5202 | &maskp->iplookupfunc); |
| 5203 | break; |
| 5204 | default : |
| 5205 | break; |
| 5206 | } |
| 5207 | |
| 5208 | return ptr; |
| 5209 | } |
| 5210 | |
| 5211 | |
| 5212 | /* ------------------------------------------------------------------------ */ |
| 5213 | /* Function: ipf_funcinit */ |
| 5214 | /* Returns: int - 0 == success, else ESRCH: cannot resolve rule details */ |
| 5215 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 5216 | /* fr(I) - pointer to filter rule */ |
| 5217 | /* */ |
| 5218 | /* If a rule is a call rule, then check if the function it points to needs */ |
| 5219 | /* an init function to be called now the rule has been loaded. */ |
| 5220 | /* ------------------------------------------------------------------------ */ |
| 5221 | static int |
| 5222 | ipf_funcinit(ipf_main_softc_t *softc, frentry_t *fr) |
| 5223 | { |
| 5224 | ipfunc_resolve_t *ft; |
| 5225 | int err; |
| 5226 | |
| 5227 | IPFERROR(34); |
| 5228 | err = ESRCH; |
| 5229 | |
| 5230 | for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) |
| 5231 | if (ft->ipfu_addr == fr->fr_func) { |
| 5232 | err = 0; |
| 5233 | if (ft->ipfu_init != NULL) |
| 5234 | err = (*ft->ipfu_init)(softc, fr); |
| 5235 | break; |
| 5236 | } |
| 5237 | return err; |
| 5238 | } |
| 5239 | |
| 5240 | |
| 5241 | /* ------------------------------------------------------------------------ */ |
| 5242 | /* Function: ipf_funcfini */ |
| 5243 | /* Returns: Nil */ |
| 5244 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 5245 | /* fr(I) - pointer to filter rule */ |
| 5246 | /* */ |
| 5247 | /* For a given filter rule, call the matching "fini" function if the rule */ |
| 5248 | /* is using a known function that would have resulted in the "init" being */ |
| 5249 | /* called for ealier. */ |
| 5250 | /* ------------------------------------------------------------------------ */ |
| 5251 | static void |
| 5252 | ipf_funcfini(ipf_main_softc_t *softc, frentry_t *fr) |
| 5253 | { |
| 5254 | ipfunc_resolve_t *ft; |
| 5255 | |
| 5256 | for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) |
| 5257 | if (ft->ipfu_addr == fr->fr_func) { |
| 5258 | if (ft->ipfu_fini != NULL) |
| 5259 | (void) (*ft->ipfu_fini)(softc, fr); |
| 5260 | break; |
| 5261 | } |
| 5262 | } |
| 5263 | |
| 5264 | |
| 5265 | /* ------------------------------------------------------------------------ */ |
| 5266 | /* Function: ipf_findfunc */ |
| 5267 | /* Returns: ipfunc_t - pointer to function if found, else NULL */ |
| 5268 | /* Parameters: funcptr(I) - function pointer to lookup */ |
| 5269 | /* */ |
| 5270 | /* Look for a function in the table of known functions. */ |
| 5271 | /* ------------------------------------------------------------------------ */ |
| 5272 | static ipfunc_t |
| 5273 | ipf_findfunc(ipfunc_t funcptr) |
| 5274 | { |
| 5275 | ipfunc_resolve_t *ft; |
| 5276 | |
| 5277 | for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) |
| 5278 | if (ft->ipfu_addr == funcptr) |
| 5279 | return funcptr; |
| 5280 | return NULL; |
| 5281 | } |
| 5282 | |
| 5283 | |
| 5284 | /* ------------------------------------------------------------------------ */ |
| 5285 | /* Function: ipf_resolvefunc */ |
| 5286 | /* Returns: int - 0 == success, else error */ |
| 5287 | /* Parameters: data(IO) - ioctl data pointer to ipfunc_resolve_t struct */ |
| 5288 | /* */ |
| 5289 | /* Copy in a ipfunc_resolve_t structure and then fill in the missing field. */ |
| 5290 | /* This will either be the function name (if the pointer is set) or the */ |
| 5291 | /* function pointer if the name is set. When found, fill in the other one */ |
| 5292 | /* so that the entire, complete, structure can be copied back to user space.*/ |
| 5293 | /* ------------------------------------------------------------------------ */ |
| 5294 | int |
| 5295 | ipf_resolvefunc(ipf_main_softc_t *softc, void *data) |
| 5296 | { |
| 5297 | ipfunc_resolve_t res, *ft; |
| 5298 | int error; |
| 5299 | |
| 5300 | error = BCOPYIN(data, &res, sizeof(res)); |
| 5301 | if (error != 0) { |
| 5302 | IPFERROR(123); |
| 5303 | return EFAULT; |
| 5304 | } |
| 5305 | |
| 5306 | if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') { |
| 5307 | for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) |
| 5308 | if (strncmp(res.ipfu_name, ft->ipfu_name, |
| 5309 | sizeof(res.ipfu_name)) == 0) { |
| 5310 | res.ipfu_addr = ft->ipfu_addr; |
| 5311 | res.ipfu_init = ft->ipfu_init; |
| 5312 | if (COPYOUT(&res, data, sizeof(res)) != 0) { |
| 5313 | IPFERROR(35); |
| 5314 | return EFAULT; |
| 5315 | } |
| 5316 | return 0; |
| 5317 | } |
| 5318 | } |
| 5319 | if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') { |
| 5320 | for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) |
| 5321 | if (ft->ipfu_addr == res.ipfu_addr) { |
| 5322 | (void) strncpy(res.ipfu_name, ft->ipfu_name, |
| 5323 | sizeof(res.ipfu_name)); |
| 5324 | res.ipfu_init = ft->ipfu_init; |
| 5325 | if (COPYOUT(&res, data, sizeof(res)) != 0) { |
| 5326 | IPFERROR(36); |
| 5327 | return EFAULT; |
| 5328 | } |
| 5329 | return 0; |
| 5330 | } |
| 5331 | } |
| 5332 | IPFERROR(37); |
| 5333 | return ESRCH; |
| 5334 | } |
| 5335 | |
| 5336 | |
| 5337 | #if !defined(_KERNEL) || (!defined(__NetBSD__) && !defined(__OpenBSD__) && \ |
| 5338 | !defined(__FreeBSD__)) || \ |
| 5339 | FREEBSD_LT_REV(501000) || NETBSD_LT_REV(105000000) || \ |
| 5340 | OPENBSD_LT_REV(200006) |
| 5341 | /* |
| 5342 | * From: NetBSD |
| 5343 | * ppsratecheck(): packets (or events) per second limitation. |
| 5344 | */ |
| 5345 | int |
| 5346 | ppsratecheck(lasttime, curpps, maxpps) |
| 5347 | struct timeval *lasttime; |
| 5348 | int *curpps; |
| 5349 | int maxpps; /* maximum pps allowed */ |
| 5350 | { |
| 5351 | struct timeval tv, delta; |
| 5352 | int rv; |
| 5353 | |
| 5354 | GETKTIME(&tv); |
| 5355 | |
| 5356 | delta.tv_sec = tv.tv_sec - lasttime->tv_sec; |
| 5357 | delta.tv_usec = tv.tv_usec - lasttime->tv_usec; |
| 5358 | if (delta.tv_usec < 0) { |
| 5359 | delta.tv_sec--; |
| 5360 | delta.tv_usec += 1000000; |
| 5361 | } |
| 5362 | |
| 5363 | /* |
| 5364 | * check for 0,0 is so that the message will be seen at least once. |
| 5365 | * if more than one second have passed since the last update of |
| 5366 | * lasttime, reset the counter. |
| 5367 | * |
| 5368 | * we do increment *curpps even in *curpps < maxpps case, as some may |
| 5369 | * try to use *curpps for stat purposes as well. |
| 5370 | */ |
| 5371 | if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || |
| 5372 | delta.tv_sec >= 1) { |
| 5373 | *lasttime = tv; |
| 5374 | *curpps = 0; |
| 5375 | rv = 1; |
| 5376 | } else if (maxpps < 0) |
| 5377 | rv = 1; |
| 5378 | else if (*curpps < maxpps) |
| 5379 | rv = 1; |
| 5380 | else |
| 5381 | rv = 0; |
| 5382 | *curpps = *curpps + 1; |
| 5383 | |
| 5384 | return (rv); |
| 5385 | } |
| 5386 | #endif |
| 5387 | |
| 5388 | |
| 5389 | /* ------------------------------------------------------------------------ */ |
| 5390 | /* Function: ipf_derefrule */ |
| 5391 | /* Returns: int - 0 == rule freed up, else rule not freed */ |
| 5392 | /* Parameters: fr(I) - pointer to filter rule */ |
| 5393 | /* */ |
| 5394 | /* Decrement the reference counter to a rule by one. If it reaches zero, */ |
| 5395 | /* free it and any associated storage space being used by it. */ |
| 5396 | /* ------------------------------------------------------------------------ */ |
| 5397 | int |
| 5398 | ipf_derefrule(ipf_main_softc_t *softc, frentry_t **frp) |
| 5399 | { |
| 5400 | frentry_t *fr; |
| 5401 | frdest_t *fdp; |
| 5402 | |
| 5403 | fr = *frp; |
| 5404 | *frp = NULL; |
| 5405 | |
| 5406 | MUTEX_ENTER(&fr->fr_lock); |
| 5407 | fr->fr_ref--; |
| 5408 | if (fr->fr_ref == 0) { |
| 5409 | MUTEX_EXIT(&fr->fr_lock); |
| 5410 | MUTEX_DESTROY(&fr->fr_lock); |
| 5411 | |
| 5412 | ipf_funcfini(softc, fr); |
| 5413 | |
| 5414 | fdp = &fr->fr_tif; |
| 5415 | if (fdp->fd_type == FRD_DSTLIST) |
| 5416 | ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); |
| 5417 | |
| 5418 | fdp = &fr->fr_rif; |
| 5419 | if (fdp->fd_type == FRD_DSTLIST) |
| 5420 | ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); |
| 5421 | |
| 5422 | fdp = &fr->fr_dif; |
| 5423 | if (fdp->fd_type == FRD_DSTLIST) |
| 5424 | ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); |
| 5425 | |
| 5426 | if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && |
| 5427 | fr->fr_satype == FRI_LOOKUP) |
| 5428 | ipf_lookup_deref(softc, fr->fr_srctype, fr->fr_srcptr); |
| 5429 | if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && |
| 5430 | fr->fr_datype == FRI_LOOKUP) |
| 5431 | ipf_lookup_deref(softc, fr->fr_dsttype, fr->fr_dstptr); |
| 5432 | |
| 5433 | if (fr->fr_grp != NULL) |
| 5434 | ipf_group_del(softc, fr->fr_grp, fr); |
| 5435 | |
| 5436 | if (fr->fr_grphead != NULL) |
| 5437 | ipf_group_del(softc, fr->fr_grphead, fr); |
| 5438 | |
| 5439 | if (fr->fr_icmpgrp != NULL) |
| 5440 | ipf_group_del(softc, fr->fr_icmpgrp, fr); |
| 5441 | |
| 5442 | if ((fr->fr_flags & FR_COPIED) != 0) { |
| 5443 | if (fr->fr_dsize) { |
| 5444 | KFREES(fr->fr_data, fr->fr_dsize); |
| 5445 | } |
| 5446 | KFREES(fr, fr->fr_size); |
| 5447 | return 0; |
| 5448 | } |
| 5449 | return 1; |
| 5450 | } else { |
| 5451 | MUTEX_EXIT(&fr->fr_lock); |
| 5452 | } |
| 5453 | return -1; |
| 5454 | } |
| 5455 | |
| 5456 | |
| 5457 | /* ------------------------------------------------------------------------ */ |
| 5458 | /* Function: ipf_grpmapinit */ |
| 5459 | /* Returns: int - 0 == success, else ESRCH because table entry not found*/ |
| 5460 | /* Parameters: fr(I) - pointer to rule to find hash table for */ |
| 5461 | /* */ |
| 5462 | /* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr. */ |
| 5463 | /* fr_ptr is later used by ipf_srcgrpmap and ipf_dstgrpmap. */ |
| 5464 | /* ------------------------------------------------------------------------ */ |
| 5465 | static int |
| 5466 | ipf_grpmapinit(ipf_main_softc_t *softc, frentry_t *fr) |
| 5467 | { |
| 5468 | char name[FR_GROUPLEN]; |
| 5469 | iphtable_t *iph; |
| 5470 | |
| 5471 | (void) snprintf(name, sizeof(name), "%d" , fr->fr_arg); |
| 5472 | iph = ipf_lookup_find_htable(softc, IPL_LOGIPF, name); |
| 5473 | if (iph == NULL) { |
| 5474 | IPFERROR(38); |
| 5475 | return ESRCH; |
| 5476 | } |
| 5477 | if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT)) { |
| 5478 | IPFERROR(39); |
| 5479 | return ESRCH; |
| 5480 | } |
| 5481 | iph->iph_ref++; |
| 5482 | fr->fr_ptr = iph; |
| 5483 | return 0; |
| 5484 | } |
| 5485 | |
| 5486 | |
| 5487 | /* ------------------------------------------------------------------------ */ |
| 5488 | /* Function: ipf_grpmapfini */ |
| 5489 | /* Returns: int - 0 == success, else ESRCH because table entry not found*/ |
| 5490 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 5491 | /* fr(I) - pointer to rule to release hash table for */ |
| 5492 | /* */ |
| 5493 | /* For rules that have had ipf_grpmapinit called, ipf_lookup_deref needs to */ |
| 5494 | /* be called to undo what ipf_grpmapinit caused to be done. */ |
| 5495 | /* ------------------------------------------------------------------------ */ |
| 5496 | static int |
| 5497 | ipf_grpmapfini(ipf_main_softc_t *softc, frentry_t *fr) |
| 5498 | { |
| 5499 | iphtable_t *iph; |
| 5500 | iph = fr->fr_ptr; |
| 5501 | if (iph != NULL) |
| 5502 | ipf_lookup_deref(softc, IPLT_HASH, iph); |
| 5503 | return 0; |
| 5504 | } |
| 5505 | |
| 5506 | |
| 5507 | /* ------------------------------------------------------------------------ */ |
| 5508 | /* Function: ipf_srcgrpmap */ |
| 5509 | /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ |
| 5510 | /* Parameters: fin(I) - pointer to packet information */ |
| 5511 | /* passp(IO) - pointer to current/new filter decision (unused) */ |
| 5512 | /* */ |
| 5513 | /* Look for a rule group head in a hash table, using the source address as */ |
| 5514 | /* the key, and descend into that group and continue matching rules against */ |
| 5515 | /* the packet. */ |
| 5516 | /* ------------------------------------------------------------------------ */ |
| 5517 | frentry_t * |
| 5518 | ipf_srcgrpmap(fr_info_t *fin, u_32_t *passp) |
| 5519 | { |
| 5520 | frgroup_t *fg; |
| 5521 | void *rval; |
| 5522 | |
| 5523 | rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, |
| 5524 | &fin->fin_src); |
| 5525 | if (rval == NULL) |
| 5526 | return NULL; |
| 5527 | |
| 5528 | fg = rval; |
| 5529 | fin->fin_fr = fg->fg_start; |
| 5530 | (void) ipf_scanlist(fin, *passp); |
| 5531 | return fin->fin_fr; |
| 5532 | } |
| 5533 | |
| 5534 | |
| 5535 | /* ------------------------------------------------------------------------ */ |
| 5536 | /* Function: ipf_dstgrpmap */ |
| 5537 | /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ |
| 5538 | /* Parameters: fin(I) - pointer to packet information */ |
| 5539 | /* passp(IO) - pointer to current/new filter decision (unused) */ |
| 5540 | /* */ |
| 5541 | /* Look for a rule group head in a hash table, using the destination */ |
| 5542 | /* address as the key, and descend into that group and continue matching */ |
| 5543 | /* rules against the packet. */ |
| 5544 | /* ------------------------------------------------------------------------ */ |
| 5545 | frentry_t * |
| 5546 | ipf_dstgrpmap(fr_info_t *fin, u_32_t *passp) |
| 5547 | { |
| 5548 | frgroup_t *fg; |
| 5549 | void *rval; |
| 5550 | |
| 5551 | rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, |
| 5552 | &fin->fin_dst); |
| 5553 | if (rval == NULL) |
| 5554 | return NULL; |
| 5555 | |
| 5556 | fg = rval; |
| 5557 | fin->fin_fr = fg->fg_start; |
| 5558 | (void) ipf_scanlist(fin, *passp); |
| 5559 | return fin->fin_fr; |
| 5560 | } |
| 5561 | |
| 5562 | /* |
| 5563 | * Queue functions |
| 5564 | * =============== |
| 5565 | * These functions manage objects on queues for efficient timeouts. There |
| 5566 | * are a number of system defined queues as well as user defined timeouts. |
| 5567 | * It is expected that a lock is held in the domain in which the queue |
| 5568 | * belongs (i.e. either state or NAT) when calling any of these functions |
| 5569 | * that prevents ipf_freetimeoutqueue() from being called at the same time |
| 5570 | * as any other. |
| 5571 | */ |
| 5572 | |
| 5573 | |
| 5574 | /* ------------------------------------------------------------------------ */ |
| 5575 | /* Function: ipf_addtimeoutqueue */ |
| 5576 | /* Returns: struct ifqtq * - NULL if malloc fails, else pointer to */ |
| 5577 | /* timeout queue with given interval. */ |
| 5578 | /* Parameters: parent(I) - pointer to pointer to parent node of this list */ |
| 5579 | /* of interface queues. */ |
| 5580 | /* seconds(I) - timeout value in seconds for this queue. */ |
| 5581 | /* */ |
| 5582 | /* This routine first looks for a timeout queue that matches the interval */ |
| 5583 | /* being requested. If it finds one, increments the reference counter and */ |
| 5584 | /* returns a pointer to it. If none are found, it allocates a new one and */ |
| 5585 | /* inserts it at the top of the list. */ |
| 5586 | /* */ |
| 5587 | /* Locking. */ |
| 5588 | /* It is assumed that the caller of this function has an appropriate lock */ |
| 5589 | /* held (exclusively) in the domain that encompases 'parent'. */ |
| 5590 | /* ------------------------------------------------------------------------ */ |
| 5591 | ipftq_t * |
| 5592 | ipf_addtimeoutqueue(ipf_main_softc_t *softc, ipftq_t **parent, u_int seconds) |
| 5593 | { |
| 5594 | ipftq_t *ifq; |
| 5595 | u_int period; |
| 5596 | |
| 5597 | period = seconds * IPF_HZ_DIVIDE; |
| 5598 | |
| 5599 | MUTEX_ENTER(&softc->ipf_timeoutlock); |
| 5600 | for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) { |
| 5601 | if (ifq->ifq_ttl == period) { |
| 5602 | /* |
| 5603 | * Reset the delete flag, if set, so the structure |
| 5604 | * gets reused rather than freed and reallocated. |
| 5605 | */ |
| 5606 | MUTEX_ENTER(&ifq->ifq_lock); |
| 5607 | ifq->ifq_flags &= ~IFQF_DELETE; |
| 5608 | ifq->ifq_ref++; |
| 5609 | MUTEX_EXIT(&ifq->ifq_lock); |
| 5610 | MUTEX_EXIT(&softc->ipf_timeoutlock); |
| 5611 | |
| 5612 | return ifq; |
| 5613 | } |
| 5614 | } |
| 5615 | |
| 5616 | KMALLOC(ifq, ipftq_t *); |
| 5617 | if (ifq != NULL) { |
| 5618 | MUTEX_NUKE(&ifq->ifq_lock); |
| 5619 | IPFTQ_INIT(ifq, period, "ipftq mutex" ); |
| 5620 | ifq->ifq_next = *parent; |
| 5621 | ifq->ifq_pnext = parent; |
| 5622 | ifq->ifq_flags = IFQF_USER; |
| 5623 | ifq->ifq_ref++; |
| 5624 | *parent = ifq; |
| 5625 | softc->ipf_userifqs++; |
| 5626 | } |
| 5627 | MUTEX_EXIT(&softc->ipf_timeoutlock); |
| 5628 | return ifq; |
| 5629 | } |
| 5630 | |
| 5631 | |
| 5632 | /* ------------------------------------------------------------------------ */ |
| 5633 | /* Function: ipf_deletetimeoutqueue */ |
| 5634 | /* Returns: int - new reference count value of the timeout queue */ |
| 5635 | /* Parameters: ifq(I) - timeout queue which is losing a reference. */ |
| 5636 | /* Locks: ifq->ifq_lock */ |
| 5637 | /* */ |
| 5638 | /* This routine must be called when we're discarding a pointer to a timeout */ |
| 5639 | /* queue object, taking care of the reference counter. */ |
| 5640 | /* */ |
| 5641 | /* Now that this just sets a DELETE flag, it requires the expire code to */ |
| 5642 | /* check the list of user defined timeout queues and call the free function */ |
| 5643 | /* below (currently commented out) to stop memory leaking. It is done this */ |
| 5644 | /* way because the locking may not be sufficient to safely do a free when */ |
| 5645 | /* this function is called. */ |
| 5646 | /* ------------------------------------------------------------------------ */ |
| 5647 | int |
| 5648 | ipf_deletetimeoutqueue(ipftq_t *ifq) |
| 5649 | { |
| 5650 | |
| 5651 | ifq->ifq_ref--; |
| 5652 | if ((ifq->ifq_ref == 0) && ((ifq->ifq_flags & IFQF_USER) != 0)) { |
| 5653 | ifq->ifq_flags |= IFQF_DELETE; |
| 5654 | } |
| 5655 | |
| 5656 | return ifq->ifq_ref; |
| 5657 | } |
| 5658 | |
| 5659 | |
| 5660 | /* ------------------------------------------------------------------------ */ |
| 5661 | /* Function: ipf_freetimeoutqueue */ |
| 5662 | /* Parameters: ifq(I) - timeout queue which is losing a reference. */ |
| 5663 | /* Returns: Nil */ |
| 5664 | /* */ |
| 5665 | /* Locking: */ |
| 5666 | /* It is assumed that the caller of this function has an appropriate lock */ |
| 5667 | /* held (exclusively) in the domain that encompases the callers "domain". */ |
| 5668 | /* The ifq_lock for this structure should not be held. */ |
| 5669 | /* */ |
| 5670 | /* Remove a user defined timeout queue from the list of queues it is in and */ |
| 5671 | /* tidy up after this is done. */ |
| 5672 | /* ------------------------------------------------------------------------ */ |
| 5673 | void |
| 5674 | ipf_freetimeoutqueue(ipf_main_softc_t *softc, ipftq_t *ifq) |
| 5675 | { |
| 5676 | |
| 5677 | if (((ifq->ifq_flags & IFQF_DELETE) == 0) || (ifq->ifq_ref != 0) || |
| 5678 | ((ifq->ifq_flags & IFQF_USER) == 0)) { |
| 5679 | printf("ipf_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n" , |
| 5680 | (u_long)ifq, ifq->ifq_flags, ifq->ifq_ttl, |
| 5681 | ifq->ifq_ref); |
| 5682 | return; |
| 5683 | } |
| 5684 | |
| 5685 | /* |
| 5686 | * Remove from its position in the list. |
| 5687 | */ |
| 5688 | *ifq->ifq_pnext = ifq->ifq_next; |
| 5689 | if (ifq->ifq_next != NULL) |
| 5690 | ifq->ifq_next->ifq_pnext = ifq->ifq_pnext; |
| 5691 | ifq->ifq_next = NULL; |
| 5692 | ifq->ifq_pnext = NULL; |
| 5693 | |
| 5694 | MUTEX_DESTROY(&ifq->ifq_lock); |
| 5695 | ATOMIC_DEC(softc->ipf_userifqs); |
| 5696 | KFREE(ifq); |
| 5697 | } |
| 5698 | |
| 5699 | |
| 5700 | /* ------------------------------------------------------------------------ */ |
| 5701 | /* Function: ipf_deletequeueentry */ |
| 5702 | /* Returns: Nil */ |
| 5703 | /* Parameters: tqe(I) - timeout queue entry to delete */ |
| 5704 | /* */ |
| 5705 | /* Remove a tail queue entry from its queue and make it an orphan. */ |
| 5706 | /* ipf_deletetimeoutqueue is called to make sure the reference count on the */ |
| 5707 | /* queue is correct. We can't, however, call ipf_freetimeoutqueue because */ |
| 5708 | /* the correct lock(s) may not be held that would make it safe to do so. */ |
| 5709 | /* ------------------------------------------------------------------------ */ |
| 5710 | void |
| 5711 | ipf_deletequeueentry(ipftqent_t *tqe) |
| 5712 | { |
| 5713 | ipftq_t *ifq; |
| 5714 | |
| 5715 | ifq = tqe->tqe_ifq; |
| 5716 | |
| 5717 | MUTEX_ENTER(&ifq->ifq_lock); |
| 5718 | |
| 5719 | if (tqe->tqe_pnext != NULL) { |
| 5720 | *tqe->tqe_pnext = tqe->tqe_next; |
| 5721 | if (tqe->tqe_next != NULL) |
| 5722 | tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; |
| 5723 | else /* we must be the tail anyway */ |
| 5724 | ifq->ifq_tail = tqe->tqe_pnext; |
| 5725 | |
| 5726 | tqe->tqe_pnext = NULL; |
| 5727 | tqe->tqe_ifq = NULL; |
| 5728 | } |
| 5729 | |
| 5730 | (void) ipf_deletetimeoutqueue(ifq); |
| 5731 | ASSERT(ifq->ifq_ref > 0); |
| 5732 | |
| 5733 | MUTEX_EXIT(&ifq->ifq_lock); |
| 5734 | } |
| 5735 | |
| 5736 | |
| 5737 | /* ------------------------------------------------------------------------ */ |
| 5738 | /* Function: ipf_queuefront */ |
| 5739 | /* Returns: Nil */ |
| 5740 | /* Parameters: tqe(I) - pointer to timeout queue entry */ |
| 5741 | /* */ |
| 5742 | /* Move a queue entry to the front of the queue, if it isn't already there. */ |
| 5743 | /* ------------------------------------------------------------------------ */ |
| 5744 | void |
| 5745 | ipf_queuefront(ipftqent_t *tqe) |
| 5746 | { |
| 5747 | ipftq_t *ifq; |
| 5748 | |
| 5749 | ifq = tqe->tqe_ifq; |
| 5750 | if (ifq == NULL) |
| 5751 | return; |
| 5752 | |
| 5753 | MUTEX_ENTER(&ifq->ifq_lock); |
| 5754 | if (ifq->ifq_head != tqe) { |
| 5755 | *tqe->tqe_pnext = tqe->tqe_next; |
| 5756 | if (tqe->tqe_next) |
| 5757 | tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; |
| 5758 | else |
| 5759 | ifq->ifq_tail = tqe->tqe_pnext; |
| 5760 | |
| 5761 | tqe->tqe_next = ifq->ifq_head; |
| 5762 | ifq->ifq_head->tqe_pnext = &tqe->tqe_next; |
| 5763 | ifq->ifq_head = tqe; |
| 5764 | tqe->tqe_pnext = &ifq->ifq_head; |
| 5765 | } |
| 5766 | MUTEX_EXIT(&ifq->ifq_lock); |
| 5767 | } |
| 5768 | |
| 5769 | |
| 5770 | /* ------------------------------------------------------------------------ */ |
| 5771 | /* Function: ipf_queueback */ |
| 5772 | /* Returns: Nil */ |
| 5773 | /* Parameters: ticks(I) - ipf tick time to use with this call */ |
| 5774 | /* tqe(I) - pointer to timeout queue entry */ |
| 5775 | /* */ |
| 5776 | /* Move a queue entry to the back of the queue, if it isn't already there. */ |
| 5777 | /* We use use ticks to calculate the expiration and mark for when we last */ |
| 5778 | /* touched the structure. */ |
| 5779 | /* ------------------------------------------------------------------------ */ |
| 5780 | void |
| 5781 | ipf_queueback(u_long ticks, ipftqent_t *tqe) |
| 5782 | { |
| 5783 | ipftq_t *ifq; |
| 5784 | |
| 5785 | ifq = tqe->tqe_ifq; |
| 5786 | if (ifq == NULL) |
| 5787 | return; |
| 5788 | tqe->tqe_die = ticks + ifq->ifq_ttl; |
| 5789 | tqe->tqe_touched = ticks; |
| 5790 | |
| 5791 | MUTEX_ENTER(&ifq->ifq_lock); |
| 5792 | if (tqe->tqe_next != NULL) { /* at the end already ? */ |
| 5793 | /* |
| 5794 | * Remove from list |
| 5795 | */ |
| 5796 | *tqe->tqe_pnext = tqe->tqe_next; |
| 5797 | tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; |
| 5798 | |
| 5799 | /* |
| 5800 | * Make it the last entry. |
| 5801 | */ |
| 5802 | tqe->tqe_next = NULL; |
| 5803 | tqe->tqe_pnext = ifq->ifq_tail; |
| 5804 | *ifq->ifq_tail = tqe; |
| 5805 | ifq->ifq_tail = &tqe->tqe_next; |
| 5806 | } |
| 5807 | MUTEX_EXIT(&ifq->ifq_lock); |
| 5808 | } |
| 5809 | |
| 5810 | |
| 5811 | /* ------------------------------------------------------------------------ */ |
| 5812 | /* Function: ipf_queueappend */ |
| 5813 | /* Returns: Nil */ |
| 5814 | /* Parameters: ticks(I) - ipf tick time to use with this call */ |
| 5815 | /* tqe(I) - pointer to timeout queue entry */ |
| 5816 | /* ifq(I) - pointer to timeout queue */ |
| 5817 | /* parent(I) - owing object pointer */ |
| 5818 | /* */ |
| 5819 | /* Add a new item to this queue and put it on the very end. */ |
| 5820 | /* We use use ticks to calculate the expiration and mark for when we last */ |
| 5821 | /* touched the structure. */ |
| 5822 | /* ------------------------------------------------------------------------ */ |
| 5823 | void |
| 5824 | ipf_queueappend(u_long ticks, ipftqent_t *tqe, ipftq_t *ifq, void *parent) |
| 5825 | { |
| 5826 | |
| 5827 | MUTEX_ENTER(&ifq->ifq_lock); |
| 5828 | tqe->tqe_parent = parent; |
| 5829 | tqe->tqe_pnext = ifq->ifq_tail; |
| 5830 | *ifq->ifq_tail = tqe; |
| 5831 | ifq->ifq_tail = &tqe->tqe_next; |
| 5832 | tqe->tqe_next = NULL; |
| 5833 | tqe->tqe_ifq = ifq; |
| 5834 | tqe->tqe_die = ticks + ifq->ifq_ttl; |
| 5835 | tqe->tqe_touched = ticks; |
| 5836 | ifq->ifq_ref++; |
| 5837 | MUTEX_EXIT(&ifq->ifq_lock); |
| 5838 | } |
| 5839 | |
| 5840 | |
| 5841 | /* ------------------------------------------------------------------------ */ |
| 5842 | /* Function: ipf_movequeue */ |
| 5843 | /* Returns: Nil */ |
| 5844 | /* Parameters: tq(I) - pointer to timeout queue information */ |
| 5845 | /* oifp(I) - old timeout queue entry was on */ |
| 5846 | /* nifp(I) - new timeout queue to put entry on */ |
| 5847 | /* */ |
| 5848 | /* Move a queue entry from one timeout queue to another timeout queue. */ |
| 5849 | /* If it notices that the current entry is already last and does not need */ |
| 5850 | /* to move queue, the return. */ |
| 5851 | /* ------------------------------------------------------------------------ */ |
| 5852 | void |
| 5853 | ipf_movequeue(u_long ticks, ipftqent_t *tqe, ipftq_t *oifq, ipftq_t *nifq) |
| 5854 | { |
| 5855 | |
| 5856 | /* |
| 5857 | * If the queue hasn't changed and we last touched this entry at the |
| 5858 | * same ipf time, then we're not going to achieve anything by either |
| 5859 | * changing the ttl or moving it on the queue. |
| 5860 | */ |
| 5861 | if (oifq == nifq && tqe->tqe_touched == ticks) |
| 5862 | return; |
| 5863 | |
| 5864 | /* |
| 5865 | * For any of this to be outside the lock, there is a risk that two |
| 5866 | * packets entering simultaneously, with one changing to a different |
| 5867 | * queue and one not, could end up with things in a bizarre state. |
| 5868 | */ |
| 5869 | MUTEX_ENTER(&oifq->ifq_lock); |
| 5870 | |
| 5871 | tqe->tqe_touched = ticks; |
| 5872 | tqe->tqe_die = ticks + nifq->ifq_ttl; |
| 5873 | /* |
| 5874 | * Is the operation here going to be a no-op ? |
| 5875 | */ |
| 5876 | if (oifq == nifq) { |
| 5877 | if ((tqe->tqe_next == NULL) || |
| 5878 | (tqe->tqe_next->tqe_die == tqe->tqe_die)) { |
| 5879 | MUTEX_EXIT(&oifq->ifq_lock); |
| 5880 | return; |
| 5881 | } |
| 5882 | } |
| 5883 | |
| 5884 | /* |
| 5885 | * Remove from the old queue |
| 5886 | */ |
| 5887 | *tqe->tqe_pnext = tqe->tqe_next; |
| 5888 | if (tqe->tqe_next) |
| 5889 | tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; |
| 5890 | else |
| 5891 | oifq->ifq_tail = tqe->tqe_pnext; |
| 5892 | tqe->tqe_next = NULL; |
| 5893 | |
| 5894 | /* |
| 5895 | * If we're moving from one queue to another, release the |
| 5896 | * lock on the old queue and get a lock on the new queue. |
| 5897 | * For user defined queues, if we're moving off it, call |
| 5898 | * delete in case it can now be freed. |
| 5899 | */ |
| 5900 | if (oifq != nifq) { |
| 5901 | tqe->tqe_ifq = NULL; |
| 5902 | |
| 5903 | (void) ipf_deletetimeoutqueue(oifq); |
| 5904 | |
| 5905 | MUTEX_EXIT(&oifq->ifq_lock); |
| 5906 | |
| 5907 | MUTEX_ENTER(&nifq->ifq_lock); |
| 5908 | |
| 5909 | tqe->tqe_ifq = nifq; |
| 5910 | nifq->ifq_ref++; |
| 5911 | } |
| 5912 | |
| 5913 | /* |
| 5914 | * Add to the bottom of the new queue |
| 5915 | */ |
| 5916 | tqe->tqe_pnext = nifq->ifq_tail; |
| 5917 | *nifq->ifq_tail = tqe; |
| 5918 | nifq->ifq_tail = &tqe->tqe_next; |
| 5919 | MUTEX_EXIT(&nifq->ifq_lock); |
| 5920 | } |
| 5921 | |
| 5922 | |
| 5923 | /* ------------------------------------------------------------------------ */ |
| 5924 | /* Function: ipf_updateipid */ |
| 5925 | /* Returns: int - 0 == success, -1 == error (packet should be droppped) */ |
| 5926 | /* Parameters: fin(I) - pointer to packet information */ |
| 5927 | /* */ |
| 5928 | /* When we are doing NAT, change the IP of every packet to represent a */ |
| 5929 | /* single sequence of packets coming from the host, hiding any host */ |
| 5930 | /* specific sequencing that might otherwise be revealed. If the packet is */ |
| 5931 | /* a fragment, then store the 'new' IPid in the fragment cache and look up */ |
| 5932 | /* the fragment cache for non-leading fragments. If a non-leading fragment */ |
| 5933 | /* has no match in the cache, return an error. */ |
| 5934 | /* ------------------------------------------------------------------------ */ |
| 5935 | static int |
| 5936 | ipf_updateipid(fr_info_t *fin) |
| 5937 | { |
| 5938 | u_short id, ido, sums; |
| 5939 | u_32_t sumd, sum; |
| 5940 | ip_t *ip; |
| 5941 | |
| 5942 | if (fin->fin_off != 0) { |
| 5943 | sum = ipf_frag_ipidknown(fin); |
| 5944 | if (sum == 0xffffffff) |
| 5945 | return -1; |
| 5946 | sum &= 0xffff; |
| 5947 | id = (u_short)sum; |
| 5948 | } else { |
| 5949 | id = ipf_nextipid(fin); |
| 5950 | if (fin->fin_off == 0 && (fin->fin_flx & FI_FRAG) != 0) |
| 5951 | (void) ipf_frag_ipidnew(fin, (u_32_t)id); |
| 5952 | } |
| 5953 | |
| 5954 | ip = fin->fin_ip; |
| 5955 | ido = ntohs(ip->ip_id); |
| 5956 | if (id == ido) |
| 5957 | return 0; |
| 5958 | ip->ip_id = htons(id); |
| 5959 | CALC_SUMD(ido, id, sumd); /* DESTRUCTIVE MACRO! id,ido change */ |
| 5960 | sum = (~ntohs(ip->ip_sum)) & 0xffff; |
| 5961 | sum += sumd; |
| 5962 | sum = (sum >> 16) + (sum & 0xffff); |
| 5963 | sum = (sum >> 16) + (sum & 0xffff); |
| 5964 | sums = ~(u_short)sum; |
| 5965 | ip->ip_sum = htons(sums); |
| 5966 | return 0; |
| 5967 | } |
| 5968 | |
| 5969 | |
| 5970 | #ifdef NEED_FRGETIFNAME |
| 5971 | /* ------------------------------------------------------------------------ */ |
| 5972 | /* Function: ipf_getifname */ |
| 5973 | /* Returns: char * - pointer to interface name */ |
| 5974 | /* Parameters: ifp(I) - pointer to network interface */ |
| 5975 | /* buffer(O) - pointer to where to store interface name */ |
| 5976 | /* */ |
| 5977 | /* Constructs an interface name in the buffer passed. The buffer passed is */ |
| 5978 | /* expected to be at least LIFNAMSIZ in bytes big. If buffer is passed in */ |
| 5979 | /* as a NULL pointer then return a pointer to a static array. */ |
| 5980 | /* ------------------------------------------------------------------------ */ |
| 5981 | char * |
| 5982 | ipf_getifname(ifp, buffer) |
| 5983 | struct ifnet *ifp; |
| 5984 | char *buffer; |
| 5985 | { |
| 5986 | static char namebuf[LIFNAMSIZ]; |
| 5987 | # if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \ |
| 5988 | defined(__sgi) || defined(linux) || defined(_AIX51) || \ |
| 5989 | (defined(sun) && !defined(__SVR4) && !defined(__svr4__)) |
| 5990 | int unit, space; |
| 5991 | char temp[20]; |
| 5992 | char *s; |
| 5993 | # endif |
| 5994 | |
| 5995 | if (buffer == NULL) |
| 5996 | buffer = namebuf; |
| 5997 | (void) strncpy(buffer, ifp->if_name, LIFNAMSIZ); |
| 5998 | buffer[LIFNAMSIZ - 1] = '\0'; |
| 5999 | # if defined(MENTAT) || defined(__FreeBSD__) || defined(__osf__) || \ |
| 6000 | defined(__sgi) || defined(_AIX51) || \ |
| 6001 | (defined(sun) && !defined(__SVR4) && !defined(__svr4__)) |
| 6002 | for (s = buffer; *s; s++) |
| 6003 | ; |
| 6004 | unit = ifp->if_unit; |
| 6005 | space = LIFNAMSIZ - (s - buffer); |
| 6006 | if ((space > 0) && (unit >= 0)) { |
| 6007 | snprintf(temp, sizeof(temp), "%d" , unit); |
| 6008 | (void) strncpy(s, temp, space); |
| 6009 | s[space - 1] = '\0'; |
| 6010 | } |
| 6011 | # endif |
| 6012 | return buffer; |
| 6013 | } |
| 6014 | #endif |
| 6015 | |
| 6016 | |
| 6017 | /* ------------------------------------------------------------------------ */ |
| 6018 | /* Function: ipf_ioctlswitch */ |
| 6019 | /* Returns: int - -1 continue processing, else ioctl return value */ |
| 6020 | /* Parameters: unit(I) - device unit opened */ |
| 6021 | /* data(I) - pointer to ioctl data */ |
| 6022 | /* cmd(I) - ioctl command */ |
| 6023 | /* mode(I) - mode value */ |
| 6024 | /* uid(I) - uid making the ioctl call */ |
| 6025 | /* ctx(I) - pointer to context data */ |
| 6026 | /* */ |
| 6027 | /* Based on the value of unit, call the appropriate ioctl handler or return */ |
| 6028 | /* EIO if ipfilter is not running. Also checks if write perms are req'd */ |
| 6029 | /* for the device in order to execute the ioctl. A special case is made */ |
| 6030 | /* SIOCIPFINTERROR so that the same code isn't required in every handler. */ |
| 6031 | /* The context data pointer is passed through as this is used as the key */ |
| 6032 | /* for locating a matching token for continued access for walking lists, */ |
| 6033 | /* etc. */ |
| 6034 | /* ------------------------------------------------------------------------ */ |
| 6035 | int |
| 6036 | ipf_ioctlswitch(ipf_main_softc_t *softc, int unit, void *data, ioctlcmd_t cmd, |
| 6037 | int mode, int uid, void *ctx) |
| 6038 | { |
| 6039 | int error = 0; |
| 6040 | |
| 6041 | switch (cmd) |
| 6042 | { |
| 6043 | case SIOCIPFINTERROR : |
| 6044 | error = BCOPYOUT(&softc->ipf_interror, data, |
| 6045 | sizeof(softc->ipf_interror)); |
| 6046 | if (error != 0) { |
| 6047 | IPFERROR(40); |
| 6048 | error = EFAULT; |
| 6049 | } |
| 6050 | return error; |
| 6051 | default : |
| 6052 | break; |
| 6053 | } |
| 6054 | |
| 6055 | switch (unit) |
| 6056 | { |
| 6057 | case IPL_LOGIPF : |
| 6058 | error = ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx); |
| 6059 | break; |
| 6060 | case IPL_LOGNAT : |
| 6061 | if (softc->ipf_running > 0) { |
| 6062 | error = ipf_nat_ioctl(softc, data, cmd, mode, |
| 6063 | uid, ctx); |
| 6064 | } else { |
| 6065 | IPFERROR(42); |
| 6066 | error = EIO; |
| 6067 | } |
| 6068 | break; |
| 6069 | case IPL_LOGSTATE : |
| 6070 | if (softc->ipf_running > 0) { |
| 6071 | error = ipf_state_ioctl(softc, data, cmd, mode, |
| 6072 | uid, ctx); |
| 6073 | } else { |
| 6074 | IPFERROR(43); |
| 6075 | error = EIO; |
| 6076 | } |
| 6077 | break; |
| 6078 | case IPL_LOGAUTH : |
| 6079 | if (softc->ipf_running > 0) { |
| 6080 | error = ipf_auth_ioctl(softc, data, cmd, mode, |
| 6081 | uid, ctx); |
| 6082 | } else { |
| 6083 | IPFERROR(44); |
| 6084 | error = EIO; |
| 6085 | } |
| 6086 | break; |
| 6087 | case IPL_LOGSYNC : |
| 6088 | if (softc->ipf_running > 0) { |
| 6089 | error = ipf_sync_ioctl(softc, data, cmd, mode, |
| 6090 | uid, ctx); |
| 6091 | } else { |
| 6092 | error = EIO; |
| 6093 | IPFERROR(45); |
| 6094 | } |
| 6095 | break; |
| 6096 | case IPL_LOGSCAN : |
| 6097 | #ifdef IPFILTER_SCAN |
| 6098 | if (softc->ipf_running > 0) |
| 6099 | error = ipf_scan_ioctl(softc, data, cmd, mode, |
| 6100 | uid, ctx); |
| 6101 | else |
| 6102 | #endif |
| 6103 | { |
| 6104 | error = EIO; |
| 6105 | IPFERROR(46); |
| 6106 | } |
| 6107 | break; |
| 6108 | case IPL_LOGLOOKUP : |
| 6109 | if (softc->ipf_running > 0) { |
| 6110 | error = ipf_lookup_ioctl(softc, data, cmd, mode, |
| 6111 | uid, ctx); |
| 6112 | } else { |
| 6113 | error = EIO; |
| 6114 | IPFERROR(47); |
| 6115 | } |
| 6116 | break; |
| 6117 | default : |
| 6118 | IPFERROR(48); |
| 6119 | error = EIO; |
| 6120 | break; |
| 6121 | } |
| 6122 | |
| 6123 | return error; |
| 6124 | } |
| 6125 | |
| 6126 | |
| 6127 | /* |
| 6128 | * This array defines the expected size of objects coming into the kernel |
| 6129 | * for the various recognised object types. The first column is flags (see |
| 6130 | * below), 2nd column is current size, 3rd column is the version number of |
| 6131 | * when the current size became current. |
| 6132 | * Flags: |
| 6133 | * 1 = minimum size, not absolute size |
| 6134 | */ |
| 6135 | static int ipf_objbytes[IPFOBJ_COUNT][3] = { |
| 6136 | { 1, sizeof(struct frentry), 5010000 }, /* 0 */ |
| 6137 | { 1, sizeof(struct friostat), 5010000 }, |
| 6138 | { 0, sizeof(struct fr_info), 5010000 }, |
| 6139 | { 0, sizeof(struct ipf_authstat), 4010100 }, |
| 6140 | { 0, sizeof(struct ipfrstat), 5010000 }, |
| 6141 | { 1, sizeof(struct ipnat), 5010000 }, /* 5 */ |
| 6142 | { 0, sizeof(struct natstat), 5010000 }, |
| 6143 | { 0, sizeof(struct ipstate_save), 5010000 }, |
| 6144 | { 1, sizeof(struct nat_save), 5010000 }, |
| 6145 | { 0, sizeof(struct natlookup), 5010000 }, |
| 6146 | { 1, sizeof(struct ipstate), 5010000 }, /* 10 */ |
| 6147 | { 0, sizeof(struct ips_stat), 5010000 }, |
| 6148 | { 0, sizeof(struct frauth), 5010000 }, |
| 6149 | { 0, sizeof(struct ipftune), 4010100 }, |
| 6150 | { 0, sizeof(struct nat), 5010000 }, |
| 6151 | { 0, sizeof(struct ipfruleiter), 4011400 }, /* 15 */ |
| 6152 | { 0, sizeof(struct ipfgeniter), 4011400 }, |
| 6153 | { 0, sizeof(struct ipftable), 4011400 }, |
| 6154 | { 0, sizeof(struct ipflookupiter), 4011400 }, |
| 6155 | { 0, sizeof(struct ipftq) * IPF_TCP_NSTATES }, |
| 6156 | { 1, 0, 0 }, /* IPFEXPR */ |
| 6157 | { 0, 0, 0 }, /* PROXYCTL */ |
| 6158 | { 0, sizeof (struct fripf), 5010000 } |
| 6159 | }; |
| 6160 | |
| 6161 | |
| 6162 | /* ------------------------------------------------------------------------ */ |
| 6163 | /* Function: ipf_inobj */ |
| 6164 | /* Returns: int - 0 = success, else failure */ |
| 6165 | /* Parameters: softc(I) - soft context pointerto work with */ |
| 6166 | /* data(I) - pointer to ioctl data */ |
| 6167 | /* objp(O) - where to store ipfobj structure */ |
| 6168 | /* ptr(I) - pointer to data to copy out */ |
| 6169 | /* type(I) - type of structure being moved */ |
| 6170 | /* */ |
| 6171 | /* Copy in the contents of what the ipfobj_t points to. In future, we */ |
| 6172 | /* add things to check for version numbers, sizes, etc, to make it backward */ |
| 6173 | /* compatible at the ABI for user land. */ |
| 6174 | /* If objp is not NULL then we assume that the caller wants to see what is */ |
| 6175 | /* in the ipfobj_t structure being copied in. As an example, this can tell */ |
| 6176 | /* the caller what version of ipfilter the ioctl program was written to. */ |
| 6177 | /* ------------------------------------------------------------------------ */ |
| 6178 | int |
| 6179 | ipf_inobj(ipf_main_softc_t *softc, void *data, ipfobj_t *objp, void *ptr, |
| 6180 | int type) |
| 6181 | { |
| 6182 | ipfobj_t obj; |
| 6183 | int error; |
| 6184 | int size; |
| 6185 | |
| 6186 | if ((type < 0) || (type >= IPFOBJ_COUNT)) { |
| 6187 | IPFERROR(49); |
| 6188 | return EINVAL; |
| 6189 | } |
| 6190 | |
| 6191 | if (objp == NULL) |
| 6192 | objp = &obj; |
| 6193 | error = BCOPYIN(data, objp, sizeof(*objp)); |
| 6194 | if (error != 0) { |
| 6195 | IPFERROR(124); |
| 6196 | return EFAULT; |
| 6197 | } |
| 6198 | |
| 6199 | if (objp->ipfo_type != type) { |
| 6200 | IPFERROR(50); |
| 6201 | return EINVAL; |
| 6202 | } |
| 6203 | |
| 6204 | if (objp->ipfo_rev >= ipf_objbytes[type][2]) { |
| 6205 | if ((ipf_objbytes[type][0] & 1) != 0) { |
| 6206 | if (objp->ipfo_size < ipf_objbytes[type][1]) { |
| 6207 | IPFERROR(51); |
| 6208 | return EINVAL; |
| 6209 | } |
| 6210 | size = ipf_objbytes[type][1]; |
| 6211 | } else if (objp->ipfo_size == ipf_objbytes[type][1]) { |
| 6212 | size = objp->ipfo_size; |
| 6213 | } else { |
| 6214 | IPFERROR(52); |
| 6215 | return EINVAL; |
| 6216 | } |
| 6217 | error = COPYIN(objp->ipfo_ptr, ptr, size); |
| 6218 | if (error != 0) { |
| 6219 | IPFERROR(55); |
| 6220 | error = EFAULT; |
| 6221 | } |
| 6222 | } else { |
| 6223 | #ifdef IPFILTER_COMPAT |
| 6224 | error = ipf_in_compat(softc, objp, ptr, 0); |
| 6225 | #else |
| 6226 | IPFERROR(54); |
| 6227 | error = EINVAL; |
| 6228 | #endif |
| 6229 | } |
| 6230 | return error; |
| 6231 | } |
| 6232 | |
| 6233 | |
| 6234 | /* ------------------------------------------------------------------------ */ |
| 6235 | /* Function: ipf_inobjsz */ |
| 6236 | /* Returns: int - 0 = success, else failure */ |
| 6237 | /* Parameters: softc(I) - soft context pointerto work with */ |
| 6238 | /* data(I) - pointer to ioctl data */ |
| 6239 | /* ptr(I) - pointer to store real data in */ |
| 6240 | /* type(I) - type of structure being moved */ |
| 6241 | /* sz(I) - size of data to copy */ |
| 6242 | /* */ |
| 6243 | /* As per ipf_inobj, except the size of the object to copy in is passed in */ |
| 6244 | /* but it must not be smaller than the size defined for the type and the */ |
| 6245 | /* type must allow for varied sized objects. The extra requirement here is */ |
| 6246 | /* that sz must match the size of the object being passed in - this is not */ |
| 6247 | /* not possible nor required in ipf_inobj(). */ |
| 6248 | /* ------------------------------------------------------------------------ */ |
| 6249 | int |
| 6250 | ipf_inobjsz(ipf_main_softc_t *softc, void *data, void *ptr, int type, int sz) |
| 6251 | { |
| 6252 | ipfobj_t obj; |
| 6253 | int error; |
| 6254 | |
| 6255 | if ((type < 0) || (type >= IPFOBJ_COUNT)) { |
| 6256 | IPFERROR(56); |
| 6257 | return EINVAL; |
| 6258 | } |
| 6259 | |
| 6260 | error = BCOPYIN(data, &obj, sizeof(obj)); |
| 6261 | if (error != 0) { |
| 6262 | IPFERROR(125); |
| 6263 | return EFAULT; |
| 6264 | } |
| 6265 | |
| 6266 | if (obj.ipfo_type != type) { |
| 6267 | IPFERROR(58); |
| 6268 | return EINVAL; |
| 6269 | } |
| 6270 | |
| 6271 | if (obj.ipfo_rev >= ipf_objbytes[type][2]) { |
| 6272 | if (((ipf_objbytes[type][0] & 1) == 0) || |
| 6273 | (sz < ipf_objbytes[type][1])) { |
| 6274 | IPFERROR(57); |
| 6275 | return EINVAL; |
| 6276 | } |
| 6277 | error = COPYIN(obj.ipfo_ptr, ptr, sz); |
| 6278 | if (error != 0) { |
| 6279 | IPFERROR(61); |
| 6280 | error = EFAULT; |
| 6281 | } |
| 6282 | } else { |
| 6283 | #ifdef IPFILTER_COMPAT |
| 6284 | error = ipf_in_compat(softc, &obj, ptr, sz); |
| 6285 | #else |
| 6286 | IPFERROR(60); |
| 6287 | error = EINVAL; |
| 6288 | #endif |
| 6289 | } |
| 6290 | return error; |
| 6291 | } |
| 6292 | |
| 6293 | |
| 6294 | /* ------------------------------------------------------------------------ */ |
| 6295 | /* Function: ipf_outobjsz */ |
| 6296 | /* Returns: int - 0 = success, else failure */ |
| 6297 | /* Parameters: data(I) - pointer to ioctl data */ |
| 6298 | /* ptr(I) - pointer to store real data in */ |
| 6299 | /* type(I) - type of structure being moved */ |
| 6300 | /* sz(I) - size of data to copy */ |
| 6301 | /* */ |
| 6302 | /* As per ipf_outobj, except the size of the object to copy out is passed in*/ |
| 6303 | /* but it must not be smaller than the size defined for the type and the */ |
| 6304 | /* type must allow for varied sized objects. The extra requirement here is */ |
| 6305 | /* that sz must match the size of the object being passed in - this is not */ |
| 6306 | /* not possible nor required in ipf_outobj(). */ |
| 6307 | /* ------------------------------------------------------------------------ */ |
| 6308 | int |
| 6309 | ipf_outobjsz(ipf_main_softc_t *softc, void *data, void *ptr, int type, int sz) |
| 6310 | { |
| 6311 | ipfobj_t obj; |
| 6312 | int error; |
| 6313 | |
| 6314 | if ((type < 0) || (type >= IPFOBJ_COUNT)) { |
| 6315 | IPFERROR(62); |
| 6316 | return EINVAL; |
| 6317 | } |
| 6318 | |
| 6319 | error = BCOPYIN(data, &obj, sizeof(obj)); |
| 6320 | if (error != 0) { |
| 6321 | IPFERROR(127); |
| 6322 | return EFAULT; |
| 6323 | } |
| 6324 | |
| 6325 | if (obj.ipfo_type != type) { |
| 6326 | IPFERROR(63); |
| 6327 | return EINVAL; |
| 6328 | } |
| 6329 | |
| 6330 | if (obj.ipfo_rev >= ipf_objbytes[type][2]) { |
| 6331 | if (((ipf_objbytes[type][0] & 1) == 0) || |
| 6332 | (sz < ipf_objbytes[type][1])) { |
| 6333 | IPFERROR(146); |
| 6334 | return EINVAL; |
| 6335 | } |
| 6336 | error = COPYOUT(ptr, obj.ipfo_ptr, sz); |
| 6337 | if (error != 0) { |
| 6338 | IPFERROR(66); |
| 6339 | error = EFAULT; |
| 6340 | } |
| 6341 | } else { |
| 6342 | #ifdef IPFILTER_COMPAT |
| 6343 | error = ipf_out_compat(softc, &obj, ptr); |
| 6344 | #else |
| 6345 | IPFERROR(65); |
| 6346 | error = EINVAL; |
| 6347 | #endif |
| 6348 | } |
| 6349 | return error; |
| 6350 | } |
| 6351 | |
| 6352 | |
| 6353 | /* ------------------------------------------------------------------------ */ |
| 6354 | /* Function: ipf_outobj */ |
| 6355 | /* Returns: int - 0 = success, else failure */ |
| 6356 | /* Parameters: data(I) - pointer to ioctl data */ |
| 6357 | /* ptr(I) - pointer to store real data in */ |
| 6358 | /* type(I) - type of structure being moved */ |
| 6359 | /* */ |
| 6360 | /* Copy out the contents of what ptr is to where ipfobj points to. In */ |
| 6361 | /* future, we add things to check for version numbers, sizes, etc, to make */ |
| 6362 | /* it backward compatible at the ABI for user land. */ |
| 6363 | /* ------------------------------------------------------------------------ */ |
| 6364 | int |
| 6365 | ipf_outobj(ipf_main_softc_t *softc, void *data, void *ptr, int type) |
| 6366 | { |
| 6367 | ipfobj_t obj; |
| 6368 | int error; |
| 6369 | |
| 6370 | if ((type < 0) || (type >= IPFOBJ_COUNT)) { |
| 6371 | IPFERROR(67); |
| 6372 | return EINVAL; |
| 6373 | } |
| 6374 | |
| 6375 | error = BCOPYIN(data, &obj, sizeof(obj)); |
| 6376 | if (error != 0) { |
| 6377 | IPFERROR(126); |
| 6378 | return EFAULT; |
| 6379 | } |
| 6380 | |
| 6381 | if (obj.ipfo_type != type) { |
| 6382 | IPFERROR(68); |
| 6383 | return EINVAL; |
| 6384 | } |
| 6385 | |
| 6386 | if (obj.ipfo_rev >= ipf_objbytes[type][2]) { |
| 6387 | if ((ipf_objbytes[type][0] & 1) != 0) { |
| 6388 | if (obj.ipfo_size < ipf_objbytes[type][1]) { |
| 6389 | IPFERROR(69); |
| 6390 | return EINVAL; |
| 6391 | } |
| 6392 | } else if (obj.ipfo_size != ipf_objbytes[type][1]) { |
| 6393 | IPFERROR(70); |
| 6394 | return EINVAL; |
| 6395 | } |
| 6396 | |
| 6397 | error = COPYOUT(ptr, obj.ipfo_ptr, obj.ipfo_size); |
| 6398 | if (error != 0) { |
| 6399 | IPFERROR(73); |
| 6400 | error = EFAULT; |
| 6401 | } |
| 6402 | } else { |
| 6403 | #ifdef IPFILTER_COMPAT |
| 6404 | error = ipf_out_compat(softc, &obj, ptr); |
| 6405 | #else |
| 6406 | IPFERROR(72); |
| 6407 | error = EINVAL; |
| 6408 | #endif |
| 6409 | } |
| 6410 | return error; |
| 6411 | } |
| 6412 | |
| 6413 | |
| 6414 | /* ------------------------------------------------------------------------ */ |
| 6415 | /* Function: ipf_outobjk */ |
| 6416 | /* Returns: int - 0 = success, else failure */ |
| 6417 | /* Parameters: obj(I) - pointer to data description structure */ |
| 6418 | /* ptr(I) - pointer to kernel data to copy out */ |
| 6419 | /* */ |
| 6420 | /* In the above functions, the ipfobj_t structure is copied into the kernel,*/ |
| 6421 | /* telling ipfilter how to copy out data. In this instance, the ipfobj_t is */ |
| 6422 | /* already populated with information and now we just need to use it. */ |
| 6423 | /* There is no need for this function to have a "type" parameter as there */ |
| 6424 | /* is no point in validating information that comes from the kernel with */ |
| 6425 | /* itself. */ |
| 6426 | /* ------------------------------------------------------------------------ */ |
| 6427 | int |
| 6428 | ipf_outobjk(ipf_main_softc_t *softc, ipfobj_t *obj, void *ptr) |
| 6429 | { |
| 6430 | int type = obj->ipfo_type; |
| 6431 | int error; |
| 6432 | |
| 6433 | if ((type < 0) || (type >= IPFOBJ_COUNT)) { |
| 6434 | IPFERROR(147); |
| 6435 | return EINVAL; |
| 6436 | } |
| 6437 | |
| 6438 | if (obj->ipfo_rev >= ipf_objbytes[type][2]) { |
| 6439 | if ((ipf_objbytes[type][0] & 1) != 0) { |
| 6440 | if (obj->ipfo_size < ipf_objbytes[type][1]) { |
| 6441 | IPFERROR(148); |
| 6442 | return EINVAL; |
| 6443 | } |
| 6444 | |
| 6445 | } else if (obj->ipfo_size != ipf_objbytes[type][1]) { |
| 6446 | IPFERROR(149); |
| 6447 | return EINVAL; |
| 6448 | } |
| 6449 | |
| 6450 | error = COPYOUT(ptr, obj->ipfo_ptr, obj->ipfo_size); |
| 6451 | if (error != 0) { |
| 6452 | IPFERROR(150); |
| 6453 | error = EFAULT; |
| 6454 | } |
| 6455 | } else { |
| 6456 | #ifdef IPFILTER_COMPAT |
| 6457 | error = ipf_out_compat(softc, obj, ptr); |
| 6458 | #else |
| 6459 | IPFERROR(151); |
| 6460 | error = EINVAL; |
| 6461 | #endif |
| 6462 | } |
| 6463 | return error; |
| 6464 | } |
| 6465 | |
| 6466 | |
| 6467 | /* ------------------------------------------------------------------------ */ |
| 6468 | /* Function: ipf_checkl4sum */ |
| 6469 | /* Returns: int - 0 = good, -1 = bad, 1 = cannot check */ |
| 6470 | /* Parameters: fin(I) - pointer to packet information */ |
| 6471 | /* */ |
| 6472 | /* If possible, calculate the layer 4 checksum for the packet. If this is */ |
| 6473 | /* not possible, return without indicating a failure or success but in a */ |
| 6474 | /* way that is ditinguishable. This function should only be called by the */ |
| 6475 | /* ipf_checkv6sum() for each platform. */ |
| 6476 | /* ------------------------------------------------------------------------ */ |
| 6477 | int |
| 6478 | ipf_checkl4sum(fr_info_t *fin) |
| 6479 | { |
| 6480 | u_short sum, hdrsum, *csump; |
| 6481 | udphdr_t *udp; |
| 6482 | int dosum; |
| 6483 | |
| 6484 | /* |
| 6485 | * If the TCP packet isn't a fragment, isn't too short and otherwise |
| 6486 | * isn't already considered "bad", then validate the checksum. If |
| 6487 | * this check fails then considered the packet to be "bad". |
| 6488 | */ |
| 6489 | if ((fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) != 0) |
| 6490 | return 1; |
| 6491 | |
| 6492 | csump = NULL; |
| 6493 | hdrsum = 0; |
| 6494 | dosum = 0; |
| 6495 | sum = 0; |
| 6496 | |
| 6497 | switch (fin->fin_p) |
| 6498 | { |
| 6499 | case IPPROTO_TCP : |
| 6500 | csump = &((tcphdr_t *)fin->fin_dp)->th_sum; |
| 6501 | dosum = 1; |
| 6502 | break; |
| 6503 | |
| 6504 | case IPPROTO_UDP : |
| 6505 | udp = fin->fin_dp; |
| 6506 | if (udp->uh_sum != 0) { |
| 6507 | csump = &udp->uh_sum; |
| 6508 | dosum = 1; |
| 6509 | } |
| 6510 | break; |
| 6511 | |
| 6512 | #ifdef USE_INET6 |
| 6513 | case IPPROTO_ICMPV6 : |
| 6514 | csump = &((struct icmp6_hdr *)fin->fin_dp)->icmp6_cksum; |
| 6515 | dosum = 1; |
| 6516 | break; |
| 6517 | #endif |
| 6518 | |
| 6519 | case IPPROTO_ICMP : |
| 6520 | csump = &((struct icmp *)fin->fin_dp)->icmp_cksum; |
| 6521 | dosum = 1; |
| 6522 | break; |
| 6523 | |
| 6524 | default : |
| 6525 | return 1; |
| 6526 | /*NOTREACHED*/ |
| 6527 | } |
| 6528 | |
| 6529 | if (csump != NULL) |
| 6530 | hdrsum = *csump; |
| 6531 | |
| 6532 | if (dosum) { |
| 6533 | sum = fr_cksum(fin, fin->fin_ip, fin->fin_p, fin->fin_dp); |
| 6534 | } |
| 6535 | #if !defined(_KERNEL) |
| 6536 | if (sum == hdrsum) { |
| 6537 | FR_DEBUG(("checkl4sum: %hx == %hx\n" , sum, hdrsum)); |
| 6538 | } else { |
| 6539 | FR_DEBUG(("checkl4sum: %hx != %hx\n" , sum, hdrsum)); |
| 6540 | } |
| 6541 | #endif |
| 6542 | DT2(l4sums, u_short, hdrsum, u_short, sum); |
| 6543 | if (hdrsum == sum) { |
| 6544 | fin->fin_cksum = FI_CK_SUMOK; |
| 6545 | return 0; |
| 6546 | } |
| 6547 | fin->fin_cksum = FI_CK_BAD; |
| 6548 | return -1; |
| 6549 | } |
| 6550 | |
| 6551 | |
| 6552 | /* ------------------------------------------------------------------------ */ |
| 6553 | /* Function: ipf_ifpfillv4addr */ |
| 6554 | /* Returns: int - 0 = address update, -1 = address not updated */ |
| 6555 | /* Parameters: atype(I) - type of network address update to perform */ |
| 6556 | /* sin(I) - pointer to source of address information */ |
| 6557 | /* mask(I) - pointer to source of netmask information */ |
| 6558 | /* inp(I) - pointer to destination address store */ |
| 6559 | /* inpmask(I) - pointer to destination netmask store */ |
| 6560 | /* */ |
| 6561 | /* Given a type of network address update (atype) to perform, copy */ |
| 6562 | /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ |
| 6563 | /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ |
| 6564 | /* which case the operation fails. For all values of atype other than */ |
| 6565 | /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ |
| 6566 | /* value. */ |
| 6567 | /* ------------------------------------------------------------------------ */ |
| 6568 | int |
| 6569 | ipf_ifpfillv4addr(int atype, struct sockaddr_in *sin, struct sockaddr_in *mask, |
| 6570 | struct in_addr *inp, struct in_addr *inpmask) |
| 6571 | { |
| 6572 | if (inpmask != NULL && atype != FRI_NETMASKED) |
| 6573 | inpmask->s_addr = 0xffffffff; |
| 6574 | |
| 6575 | if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { |
| 6576 | if (atype == FRI_NETMASKED) { |
| 6577 | if (inpmask == NULL) |
| 6578 | return -1; |
| 6579 | inpmask->s_addr = mask->sin_addr.s_addr; |
| 6580 | } |
| 6581 | inp->s_addr = sin->sin_addr.s_addr & mask->sin_addr.s_addr; |
| 6582 | } else { |
| 6583 | inp->s_addr = sin->sin_addr.s_addr; |
| 6584 | } |
| 6585 | return 0; |
| 6586 | } |
| 6587 | |
| 6588 | |
| 6589 | #ifdef USE_INET6 |
| 6590 | /* ------------------------------------------------------------------------ */ |
| 6591 | /* Function: ipf_ifpfillv6addr */ |
| 6592 | /* Returns: int - 0 = address update, -1 = address not updated */ |
| 6593 | /* Parameters: atype(I) - type of network address update to perform */ |
| 6594 | /* sin(I) - pointer to source of address information */ |
| 6595 | /* mask(I) - pointer to source of netmask information */ |
| 6596 | /* inp(I) - pointer to destination address store */ |
| 6597 | /* inpmask(I) - pointer to destination netmask store */ |
| 6598 | /* */ |
| 6599 | /* Given a type of network address update (atype) to perform, copy */ |
| 6600 | /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ |
| 6601 | /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ |
| 6602 | /* which case the operation fails. For all values of atype other than */ |
| 6603 | /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ |
| 6604 | /* value. */ |
| 6605 | /* ------------------------------------------------------------------------ */ |
| 6606 | int |
| 6607 | ipf_ifpfillv6addr(int atype, struct sockaddr_in6 *sin, |
| 6608 | struct sockaddr_in6 *mask, i6addr_t *inp, i6addr_t *inpmask) |
| 6609 | { |
| 6610 | i6addr_t *src, *and; |
| 6611 | |
| 6612 | src = (i6addr_t *)&sin->sin6_addr; |
| 6613 | and = (i6addr_t *)&mask->sin6_addr; |
| 6614 | |
| 6615 | if (inpmask != NULL && atype != FRI_NETMASKED) { |
| 6616 | inpmask->i6[0] = 0xffffffff; |
| 6617 | inpmask->i6[1] = 0xffffffff; |
| 6618 | inpmask->i6[2] = 0xffffffff; |
| 6619 | inpmask->i6[3] = 0xffffffff; |
| 6620 | } |
| 6621 | |
| 6622 | if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { |
| 6623 | if (atype == FRI_NETMASKED) { |
| 6624 | if (inpmask == NULL) |
| 6625 | return -1; |
| 6626 | inpmask->i6[0] = and->i6[0]; |
| 6627 | inpmask->i6[1] = and->i6[1]; |
| 6628 | inpmask->i6[2] = and->i6[2]; |
| 6629 | inpmask->i6[3] = and->i6[3]; |
| 6630 | } |
| 6631 | |
| 6632 | inp->i6[0] = src->i6[0] & and->i6[0]; |
| 6633 | inp->i6[1] = src->i6[1] & and->i6[1]; |
| 6634 | inp->i6[2] = src->i6[2] & and->i6[2]; |
| 6635 | inp->i6[3] = src->i6[3] & and->i6[3]; |
| 6636 | } else { |
| 6637 | inp->i6[0] = src->i6[0]; |
| 6638 | inp->i6[1] = src->i6[1]; |
| 6639 | inp->i6[2] = src->i6[2]; |
| 6640 | inp->i6[3] = src->i6[3]; |
| 6641 | } |
| 6642 | return 0; |
| 6643 | } |
| 6644 | #endif |
| 6645 | |
| 6646 | |
| 6647 | /* ------------------------------------------------------------------------ */ |
| 6648 | /* Function: ipf_matchtag */ |
| 6649 | /* Returns: 0 == mismatch, 1 == match. */ |
| 6650 | /* Parameters: tag1(I) - pointer to first tag to compare */ |
| 6651 | /* tag2(I) - pointer to second tag to compare */ |
| 6652 | /* */ |
| 6653 | /* Returns true (non-zero) or false(0) if the two tag structures can be */ |
| 6654 | /* considered to be a match or not match, respectively. The tag is 16 */ |
| 6655 | /* bytes long (16 characters) but that is overlayed with 4 32bit ints so */ |
| 6656 | /* compare the ints instead, for speed. tag1 is the master of the */ |
| 6657 | /* comparison. This function should only be called with both tag1 and tag2 */ |
| 6658 | /* as non-NULL pointers. */ |
| 6659 | /* ------------------------------------------------------------------------ */ |
| 6660 | int |
| 6661 | ipf_matchtag(ipftag_t *tag1, ipftag_t *tag2) |
| 6662 | { |
| 6663 | if (tag1 == tag2) |
| 6664 | return 1; |
| 6665 | |
| 6666 | if ((tag1->ipt_num[0] == 0) && (tag2->ipt_num[0] == 0)) |
| 6667 | return 1; |
| 6668 | |
| 6669 | if ((tag1->ipt_num[0] == tag2->ipt_num[0]) && |
| 6670 | (tag1->ipt_num[1] == tag2->ipt_num[1]) && |
| 6671 | (tag1->ipt_num[2] == tag2->ipt_num[2]) && |
| 6672 | (tag1->ipt_num[3] == tag2->ipt_num[3])) |
| 6673 | return 1; |
| 6674 | return 0; |
| 6675 | } |
| 6676 | |
| 6677 | |
| 6678 | /* ------------------------------------------------------------------------ */ |
| 6679 | /* Function: ipf_coalesce */ |
| 6680 | /* Returns: 1 == success, -1 == failure, 0 == no change */ |
| 6681 | /* Parameters: fin(I) - pointer to packet information */ |
| 6682 | /* */ |
| 6683 | /* Attempt to get all of the packet data into a single, contiguous buffer. */ |
| 6684 | /* If this call returns a failure then the buffers have also been freed. */ |
| 6685 | /* ------------------------------------------------------------------------ */ |
| 6686 | int |
| 6687 | ipf_coalesce(fr_info_t *fin) |
| 6688 | { |
| 6689 | |
| 6690 | if ((fin->fin_flx & FI_COALESCE) != 0) |
| 6691 | return 1; |
| 6692 | |
| 6693 | /* |
| 6694 | * If the mbuf pointers indicate that there is no mbuf to work with, |
| 6695 | * return but do not indicate success or failure. |
| 6696 | */ |
| 6697 | if (fin->fin_m == NULL || fin->fin_mp == NULL) |
| 6698 | return 0; |
| 6699 | |
| 6700 | #if defined(_KERNEL) |
| 6701 | if (ipf_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) { |
| 6702 | ipf_main_softc_t *softc = fin->fin_main_soft; |
| 6703 | |
| 6704 | DT1(frb_coalesce, fr_info_t *, fin); |
| 6705 | LBUMP(ipf_stats[fin->fin_out].fr_badcoalesces); |
| 6706 | # ifdef MENTAT |
| 6707 | FREE_MB_T(*fin->fin_mp); |
| 6708 | # endif |
| 6709 | fin->fin_reason = FRB_COALESCE; |
| 6710 | *fin->fin_mp = NULL; |
| 6711 | fin->fin_m = NULL; |
| 6712 | return -1; |
| 6713 | } |
| 6714 | #else |
| 6715 | fin = fin; /* LINT */ |
| 6716 | #endif |
| 6717 | return 1; |
| 6718 | } |
| 6719 | |
| 6720 | |
| 6721 | /* |
| 6722 | * The following table lists all of the tunable variables that can be |
| 6723 | * accessed via SIOCIPFGET/SIOCIPFSET/SIOCIPFGETNEXt. The format of each row |
| 6724 | * in the table below is as follows: |
| 6725 | * |
| 6726 | * pointer to value, name of value, minimum, maximum, size of the value's |
| 6727 | * container, value attribute flags |
| 6728 | * |
| 6729 | * For convienience, IPFT_RDONLY means the value is read-only, IPFT_WRDISABLED |
| 6730 | * means the value can only be written to when IPFilter is loaded but disabled. |
| 6731 | * The obvious implication is if neither of these are set then the value can be |
| 6732 | * changed at any time without harm. |
| 6733 | */ |
| 6734 | |
| 6735 | |
| 6736 | /* ------------------------------------------------------------------------ */ |
| 6737 | /* Function: ipf_tune_findbycookie */ |
| 6738 | /* Returns: NULL = search failed, else pointer to tune struct */ |
| 6739 | /* Parameters: cookie(I) - cookie value to search for amongst tuneables */ |
| 6740 | /* next(O) - pointer to place to store the cookie for the */ |
| 6741 | /* "next" tuneable, if it is desired. */ |
| 6742 | /* */ |
| 6743 | /* This function is used to walk through all of the existing tunables with */ |
| 6744 | /* successive calls. It searches the known tunables for the one which has */ |
| 6745 | /* a matching value for "cookie" - ie its address. When returning a match, */ |
| 6746 | /* the next one to be found may be returned inside next. */ |
| 6747 | /* ------------------------------------------------------------------------ */ |
| 6748 | static ipftuneable_t * |
| 6749 | ipf_tune_findbycookie(ipftuneable_t **ptop, void *cookie, void **next) |
| 6750 | { |
| 6751 | ipftuneable_t *ta, **tap; |
| 6752 | |
| 6753 | for (ta = *ptop; ta->ipft_name != NULL; ta++) |
| 6754 | if (ta == cookie) { |
| 6755 | if (next != NULL) { |
| 6756 | /* |
| 6757 | * If the next entry in the array has a name |
| 6758 | * present, then return a pointer to it for |
| 6759 | * where to go next, else return a pointer to |
| 6760 | * the dynaminc list as a key to search there |
| 6761 | * next. This facilitates a weak linking of |
| 6762 | * the two "lists" together. |
| 6763 | */ |
| 6764 | if ((ta + 1)->ipft_name != NULL) |
| 6765 | *next = ta + 1; |
| 6766 | else |
| 6767 | *next = ptop; |
| 6768 | } |
| 6769 | return ta; |
| 6770 | } |
| 6771 | |
| 6772 | for (tap = ptop; (ta = *tap) != NULL; tap = &ta->ipft_next) |
| 6773 | if (tap == cookie) { |
| 6774 | if (next != NULL) |
| 6775 | *next = &ta->ipft_next; |
| 6776 | return ta; |
| 6777 | } |
| 6778 | |
| 6779 | if (next != NULL) |
| 6780 | *next = NULL; |
| 6781 | return NULL; |
| 6782 | } |
| 6783 | |
| 6784 | |
| 6785 | /* ------------------------------------------------------------------------ */ |
| 6786 | /* Function: ipf_tune_findbyname */ |
| 6787 | /* Returns: NULL = search failed, else pointer to tune struct */ |
| 6788 | /* Parameters: name(I) - name of the tuneable entry to find. */ |
| 6789 | /* */ |
| 6790 | /* Search the static array of tuneables and the list of dynamic tuneables */ |
| 6791 | /* for an entry with a matching name. If we can find one, return a pointer */ |
| 6792 | /* to the matching structure. */ |
| 6793 | /* ------------------------------------------------------------------------ */ |
| 6794 | static ipftuneable_t * |
| 6795 | ipf_tune_findbyname(ipftuneable_t *top, const char *name) |
| 6796 | { |
| 6797 | ipftuneable_t *ta; |
| 6798 | |
| 6799 | for (ta = top; ta != NULL; ta = ta->ipft_next) |
| 6800 | if (!strcmp(ta->ipft_name, name)) { |
| 6801 | return ta; |
| 6802 | } |
| 6803 | |
| 6804 | return NULL; |
| 6805 | } |
| 6806 | |
| 6807 | |
| 6808 | /* ------------------------------------------------------------------------ */ |
| 6809 | /* Function: ipf_tune_add_array */ |
| 6810 | /* Returns: int - 0 == success, else failure */ |
| 6811 | /* Parameters: newtune - pointer to new tune array to add to tuneables */ |
| 6812 | /* */ |
| 6813 | /* Appends tune structures from the array passed in (newtune) to the end of */ |
| 6814 | /* the current list of "dynamic" tuneable parameters. */ |
| 6815 | /* If any entry to be added is already present (by name) then the operation */ |
| 6816 | /* is aborted - entries that have been added are removed before returning. */ |
| 6817 | /* An entry with no name (NULL) is used as the indication that the end of */ |
| 6818 | /* the array has been reached. */ |
| 6819 | /* ------------------------------------------------------------------------ */ |
| 6820 | int |
| 6821 | ipf_tune_add_array(ipf_main_softc_t *softc, ipftuneable_t *newtune) |
| 6822 | { |
| 6823 | ipftuneable_t *nt, *dt; |
| 6824 | int error = 0; |
| 6825 | |
| 6826 | for (nt = newtune; nt->ipft_name != NULL; nt++) { |
| 6827 | error = ipf_tune_add(softc, nt); |
| 6828 | if (error != 0) { |
| 6829 | for (dt = newtune; dt != nt; dt++) { |
| 6830 | (void) ipf_tune_del(softc, dt); |
| 6831 | } |
| 6832 | } |
| 6833 | } |
| 6834 | |
| 6835 | return error; |
| 6836 | } |
| 6837 | |
| 6838 | |
| 6839 | /* ------------------------------------------------------------------------ */ |
| 6840 | /* Function: ipf_tune_array_link */ |
| 6841 | /* Returns: 0 == success, -1 == failure */ |
| 6842 | /* Parameters: softc(I) - soft context pointerto work with */ |
| 6843 | /* array(I) - pointer to an array of tuneables */ |
| 6844 | /* */ |
| 6845 | /* Given an array of tunables (array), append them to the current list of */ |
| 6846 | /* tuneables for this context (softc->ipf_tuners.) To properly prepare the */ |
| 6847 | /* the array for being appended to the list, initialise all of the next */ |
| 6848 | /* pointers so we don't need to walk parts of it with ++ and others with */ |
| 6849 | /* next. The array is expected to have an entry with a NULL name as the */ |
| 6850 | /* terminator. Trying to add an array with no non-NULL names will return as */ |
| 6851 | /* a failure. */ |
| 6852 | /* ------------------------------------------------------------------------ */ |
| 6853 | int |
| 6854 | ipf_tune_array_link(ipf_main_softc_t *softc, ipftuneable_t *array) |
| 6855 | { |
| 6856 | ipftuneable_t *t, **p; |
| 6857 | |
| 6858 | t = array; |
| 6859 | if (t->ipft_name == NULL) |
| 6860 | return -1; |
| 6861 | |
| 6862 | for (; t[1].ipft_name != NULL; t++) |
| 6863 | t[0].ipft_next = &t[1]; |
| 6864 | t->ipft_next = NULL; |
| 6865 | |
| 6866 | /* |
| 6867 | * Since a pointer to the last entry isn't kept, we need to find it |
| 6868 | * each time we want to add new variables to the list. |
| 6869 | */ |
| 6870 | for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) |
| 6871 | if (t->ipft_name == NULL) |
| 6872 | break; |
| 6873 | *p = array; |
| 6874 | |
| 6875 | return 0; |
| 6876 | } |
| 6877 | |
| 6878 | |
| 6879 | /* ------------------------------------------------------------------------ */ |
| 6880 | /* Function: ipf_tune_array_unlink */ |
| 6881 | /* Returns: 0 == success, -1 == failure */ |
| 6882 | /* Parameters: softc(I) - soft context pointerto work with */ |
| 6883 | /* array(I) - pointer to an array of tuneables */ |
| 6884 | /* */ |
| 6885 | /* ------------------------------------------------------------------------ */ |
| 6886 | int |
| 6887 | ipf_tune_array_unlink(ipf_main_softc_t *softc, ipftuneable_t *array) |
| 6888 | { |
| 6889 | ipftuneable_t *t, **p; |
| 6890 | |
| 6891 | for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) |
| 6892 | if (t == array) |
| 6893 | break; |
| 6894 | if (t == NULL) |
| 6895 | return -1; |
| 6896 | |
| 6897 | for (; t[1].ipft_name != NULL; t++) |
| 6898 | ; |
| 6899 | |
| 6900 | *p = t->ipft_next; |
| 6901 | |
| 6902 | return 0; |
| 6903 | } |
| 6904 | |
| 6905 | |
| 6906 | /* ------------------------------------------------------------------------ */ |
| 6907 | /* Function: ipf_tune_array_copy */ |
| 6908 | /* Returns: NULL = failure, else pointer to new array */ |
| 6909 | /* Parameters: base(I) - pointer to structure base */ |
| 6910 | /* size(I) - size of the array at template */ |
| 6911 | /* template(I) - original array to copy */ |
| 6912 | /* */ |
| 6913 | /* Allocate memory for a new set of tuneable values and copy everything */ |
| 6914 | /* from template into the new region of memory. The new region is full of */ |
| 6915 | /* uninitialised pointers (ipft_next) so set them up. Now, ipftp_offset... */ |
| 6916 | /* */ |
| 6917 | /* NOTE: the following assumes that sizeof(long) == sizeof(void *) */ |
| 6918 | /* In the array template, ipftp_offset is the offset (in bytes) of the */ |
| 6919 | /* location of the tuneable value inside the structure pointed to by base. */ |
| 6920 | /* As ipftp_offset is a union over the pointers to the tuneable values, if */ |
| 6921 | /* we add base to the copy's ipftp_offset, copy ends up with a pointer in */ |
| 6922 | /* ipftp_void that points to the stored value. */ |
| 6923 | /* ------------------------------------------------------------------------ */ |
| 6924 | ipftuneable_t * |
| 6925 | ipf_tune_array_copy(void *base, size_t size, ipftuneable_t *template) |
| 6926 | { |
| 6927 | ipftuneable_t *copy; |
| 6928 | int i; |
| 6929 | |
| 6930 | |
| 6931 | KMALLOCS(copy, ipftuneable_t *, size); |
| 6932 | if (copy == NULL) { |
| 6933 | return NULL; |
| 6934 | } |
| 6935 | bcopy(template, copy, size); |
| 6936 | |
| 6937 | for (i = 0; copy[i].ipft_name; i++) { |
| 6938 | copy[i].ipft_una.ipftp_offset += (u_long)base; |
| 6939 | copy[i].ipft_next = copy + i + 1; |
| 6940 | } |
| 6941 | |
| 6942 | return copy; |
| 6943 | } |
| 6944 | |
| 6945 | |
| 6946 | /* ------------------------------------------------------------------------ */ |
| 6947 | /* Function: ipf_tune_add */ |
| 6948 | /* Returns: int - 0 == success, else failure */ |
| 6949 | /* Parameters: newtune - pointer to new tune entry to add to tuneables */ |
| 6950 | /* */ |
| 6951 | /* Appends tune structures from the array passed in (newtune) to the end of */ |
| 6952 | /* the current list of "dynamic" tuneable parameters. Once added, the */ |
| 6953 | /* owner of the object is not expected to ever change "ipft_next". */ |
| 6954 | /* ------------------------------------------------------------------------ */ |
| 6955 | int |
| 6956 | ipf_tune_add(ipf_main_softc_t *softc, ipftuneable_t *newtune) |
| 6957 | { |
| 6958 | ipftuneable_t *ta, **tap; |
| 6959 | |
| 6960 | ta = ipf_tune_findbyname(softc->ipf_tuners, newtune->ipft_name); |
| 6961 | if (ta != NULL) { |
| 6962 | IPFERROR(74); |
| 6963 | return EEXIST; |
| 6964 | } |
| 6965 | |
| 6966 | for (tap = &softc->ipf_tuners; *tap != NULL; tap = &(*tap)->ipft_next) |
| 6967 | ; |
| 6968 | |
| 6969 | newtune->ipft_next = NULL; |
| 6970 | *tap = newtune; |
| 6971 | return 0; |
| 6972 | } |
| 6973 | |
| 6974 | |
| 6975 | /* ------------------------------------------------------------------------ */ |
| 6976 | /* Function: ipf_tune_del */ |
| 6977 | /* Returns: int - 0 == success, else failure */ |
| 6978 | /* Parameters: oldtune - pointer to tune entry to remove from the list of */ |
| 6979 | /* current dynamic tuneables */ |
| 6980 | /* */ |
| 6981 | /* Search for the tune structure, by pointer, in the list of those that are */ |
| 6982 | /* dynamically added at run time. If found, adjust the list so that this */ |
| 6983 | /* structure is no longer part of it. */ |
| 6984 | /* ------------------------------------------------------------------------ */ |
| 6985 | int |
| 6986 | ipf_tune_del(ipf_main_softc_t *softc, ipftuneable_t *oldtune) |
| 6987 | { |
| 6988 | ipftuneable_t *ta, **tap; |
| 6989 | int error = 0; |
| 6990 | |
| 6991 | for (tap = &softc->ipf_tuners; (ta = *tap) != NULL; |
| 6992 | tap = &ta->ipft_next) { |
| 6993 | if (ta == oldtune) { |
| 6994 | *tap = oldtune->ipft_next; |
| 6995 | oldtune->ipft_next = NULL; |
| 6996 | break; |
| 6997 | } |
| 6998 | } |
| 6999 | |
| 7000 | if (ta == NULL) { |
| 7001 | error = ESRCH; |
| 7002 | IPFERROR(75); |
| 7003 | } |
| 7004 | return error; |
| 7005 | } |
| 7006 | |
| 7007 | |
| 7008 | /* ------------------------------------------------------------------------ */ |
| 7009 | /* Function: ipf_tune_del_array */ |
| 7010 | /* Returns: int - 0 == success, else failure */ |
| 7011 | /* Parameters: oldtune - pointer to tuneables array */ |
| 7012 | /* */ |
| 7013 | /* Remove each tuneable entry in the array from the list of "dynamic" */ |
| 7014 | /* tunables. If one entry should fail to be found, an error will be */ |
| 7015 | /* returned and no further ones removed. */ |
| 7016 | /* An entry with a NULL name is used as the indicator of the last entry in */ |
| 7017 | /* the array. */ |
| 7018 | /* ------------------------------------------------------------------------ */ |
| 7019 | int |
| 7020 | ipf_tune_del_array(ipf_main_softc_t *softc, ipftuneable_t *oldtune) |
| 7021 | { |
| 7022 | ipftuneable_t *ot; |
| 7023 | int error = 0; |
| 7024 | |
| 7025 | for (ot = oldtune; ot->ipft_name != NULL; ot++) { |
| 7026 | error = ipf_tune_del(softc, ot); |
| 7027 | if (error != 0) |
| 7028 | break; |
| 7029 | } |
| 7030 | |
| 7031 | return error; |
| 7032 | |
| 7033 | } |
| 7034 | |
| 7035 | |
| 7036 | /* ------------------------------------------------------------------------ */ |
| 7037 | /* Function: ipf_tune */ |
| 7038 | /* Returns: int - 0 == success, else failure */ |
| 7039 | /* Parameters: cmd(I) - ioctl command number */ |
| 7040 | /* data(I) - pointer to ioctl data structure */ |
| 7041 | /* */ |
| 7042 | /* Implement handling of SIOCIPFGETNEXT, SIOCIPFGET and SIOCIPFSET. These */ |
| 7043 | /* three ioctls provide the means to access and control global variables */ |
| 7044 | /* within IPFilter, allowing (for example) timeouts and table sizes to be */ |
| 7045 | /* changed without rebooting, reloading or recompiling. The initialisation */ |
| 7046 | /* and 'destruction' routines of the various components of ipfilter are all */ |
| 7047 | /* each responsible for handling their own values being too big. */ |
| 7048 | /* ------------------------------------------------------------------------ */ |
| 7049 | int |
| 7050 | ipf_ipftune(ipf_main_softc_t *softc, ioctlcmd_t cmd, void *data) |
| 7051 | { |
| 7052 | ipftuneable_t *ta; |
| 7053 | ipftune_t tu; |
| 7054 | void *cookie; |
| 7055 | int error; |
| 7056 | |
| 7057 | error = ipf_inobj(softc, data, NULL, &tu, IPFOBJ_TUNEABLE); |
| 7058 | if (error != 0) |
| 7059 | return error; |
| 7060 | |
| 7061 | tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0'; |
| 7062 | cookie = tu.ipft_cookie; |
| 7063 | ta = NULL; |
| 7064 | |
| 7065 | switch (cmd) |
| 7066 | { |
| 7067 | case SIOCIPFGETNEXT : |
| 7068 | /* |
| 7069 | * If cookie is non-NULL, assume it to be a pointer to the last |
| 7070 | * entry we looked at, so find it (if possible) and return a |
| 7071 | * pointer to the next one after it. The last entry in the |
| 7072 | * the table is a NULL entry, so when we get to it, set cookie |
| 7073 | * to NULL and return that, indicating end of list, erstwhile |
| 7074 | * if we come in with cookie set to NULL, we are starting anew |
| 7075 | * at the front of the list. |
| 7076 | */ |
| 7077 | if (cookie != NULL) { |
| 7078 | ta = ipf_tune_findbycookie(&softc->ipf_tuners, |
| 7079 | cookie, &tu.ipft_cookie); |
| 7080 | } else { |
| 7081 | ta = softc->ipf_tuners; |
| 7082 | tu.ipft_cookie = ta + 1; |
| 7083 | } |
| 7084 | if (ta != NULL) { |
| 7085 | /* |
| 7086 | * Entry found, but does the data pointed to by that |
| 7087 | * row fit in what we can return? |
| 7088 | */ |
| 7089 | if (ta->ipft_sz > sizeof(tu.ipft_un)) { |
| 7090 | IPFERROR(76); |
| 7091 | return EINVAL; |
| 7092 | } |
| 7093 | |
| 7094 | tu.ipft_vlong = 0; |
| 7095 | if (ta->ipft_sz == sizeof(u_long)) |
| 7096 | tu.ipft_vlong = *ta->ipft_plong; |
| 7097 | else if (ta->ipft_sz == sizeof(u_int)) |
| 7098 | tu.ipft_vint = *ta->ipft_pint; |
| 7099 | else if (ta->ipft_sz == sizeof(u_short)) |
| 7100 | tu.ipft_vshort = *ta->ipft_pshort; |
| 7101 | else if (ta->ipft_sz == sizeof(u_char)) |
| 7102 | tu.ipft_vchar = *ta->ipft_pchar; |
| 7103 | |
| 7104 | tu.ipft_sz = ta->ipft_sz; |
| 7105 | tu.ipft_min = ta->ipft_min; |
| 7106 | tu.ipft_max = ta->ipft_max; |
| 7107 | tu.ipft_flags = ta->ipft_flags; |
| 7108 | bcopy(ta->ipft_name, tu.ipft_name, |
| 7109 | MIN(sizeof(tu.ipft_name), |
| 7110 | strlen(ta->ipft_name) + 1)); |
| 7111 | } |
| 7112 | error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); |
| 7113 | break; |
| 7114 | |
| 7115 | case SIOCIPFGET : |
| 7116 | case SIOCIPFSET : |
| 7117 | /* |
| 7118 | * Search by name or by cookie value for a particular entry |
| 7119 | * in the tuning paramter table. |
| 7120 | */ |
| 7121 | IPFERROR(77); |
| 7122 | error = ESRCH; |
| 7123 | if (cookie != NULL) { |
| 7124 | ta = ipf_tune_findbycookie(&softc->ipf_tuners, |
| 7125 | cookie, NULL); |
| 7126 | if (ta != NULL) |
| 7127 | error = 0; |
| 7128 | } else if (tu.ipft_name[0] != '\0') { |
| 7129 | ta = ipf_tune_findbyname(softc->ipf_tuners, |
| 7130 | tu.ipft_name); |
| 7131 | if (ta != NULL) |
| 7132 | error = 0; |
| 7133 | } |
| 7134 | if (error != 0) |
| 7135 | break; |
| 7136 | |
| 7137 | if (cmd == (ioctlcmd_t)SIOCIPFGET) { |
| 7138 | /* |
| 7139 | * Fetch the tuning parameters for a particular value |
| 7140 | */ |
| 7141 | tu.ipft_vlong = 0; |
| 7142 | if (ta->ipft_sz == sizeof(u_long)) |
| 7143 | tu.ipft_vlong = *ta->ipft_plong; |
| 7144 | else if (ta->ipft_sz == sizeof(u_int)) |
| 7145 | tu.ipft_vint = *ta->ipft_pint; |
| 7146 | else if (ta->ipft_sz == sizeof(u_short)) |
| 7147 | tu.ipft_vshort = *ta->ipft_pshort; |
| 7148 | else if (ta->ipft_sz == sizeof(u_char)) |
| 7149 | tu.ipft_vchar = *ta->ipft_pchar; |
| 7150 | tu.ipft_cookie = ta; |
| 7151 | tu.ipft_sz = ta->ipft_sz; |
| 7152 | tu.ipft_min = ta->ipft_min; |
| 7153 | tu.ipft_max = ta->ipft_max; |
| 7154 | tu.ipft_flags = ta->ipft_flags; |
| 7155 | error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); |
| 7156 | |
| 7157 | } else if (cmd == (ioctlcmd_t)SIOCIPFSET) { |
| 7158 | /* |
| 7159 | * Set an internal parameter. The hard part here is |
| 7160 | * getting the new value safely and correctly out of |
| 7161 | * the kernel (given we only know its size, not type.) |
| 7162 | */ |
| 7163 | u_long in; |
| 7164 | |
| 7165 | if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) && |
| 7166 | (softc->ipf_running > 0)) { |
| 7167 | IPFERROR(78); |
| 7168 | error = EBUSY; |
| 7169 | break; |
| 7170 | } |
| 7171 | |
| 7172 | in = tu.ipft_vlong; |
| 7173 | if (in < ta->ipft_min || in > ta->ipft_max) { |
| 7174 | IPFERROR(79); |
| 7175 | error = EINVAL; |
| 7176 | break; |
| 7177 | } |
| 7178 | |
| 7179 | if (ta->ipft_func != NULL) { |
| 7180 | SPL_INT(s); |
| 7181 | |
| 7182 | SPL_NET(s); |
| 7183 | error = (*ta->ipft_func)(softc, ta, |
| 7184 | &tu.ipft_un); |
| 7185 | SPL_X(s); |
| 7186 | |
| 7187 | } else if (ta->ipft_sz == sizeof(u_long)) { |
| 7188 | tu.ipft_vlong = *ta->ipft_plong; |
| 7189 | *ta->ipft_plong = in; |
| 7190 | |
| 7191 | } else if (ta->ipft_sz == sizeof(u_int)) { |
| 7192 | tu.ipft_vint = *ta->ipft_pint; |
| 7193 | *ta->ipft_pint = (u_int)(in & 0xffffffff); |
| 7194 | |
| 7195 | } else if (ta->ipft_sz == sizeof(u_short)) { |
| 7196 | tu.ipft_vshort = *ta->ipft_pshort; |
| 7197 | *ta->ipft_pshort = (u_short)(in & 0xffff); |
| 7198 | |
| 7199 | } else if (ta->ipft_sz == sizeof(u_char)) { |
| 7200 | tu.ipft_vchar = *ta->ipft_pchar; |
| 7201 | *ta->ipft_pchar = (u_char)(in & 0xff); |
| 7202 | } |
| 7203 | error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); |
| 7204 | } |
| 7205 | break; |
| 7206 | |
| 7207 | default : |
| 7208 | IPFERROR(80); |
| 7209 | error = EINVAL; |
| 7210 | break; |
| 7211 | } |
| 7212 | |
| 7213 | return error; |
| 7214 | } |
| 7215 | |
| 7216 | |
| 7217 | /* ------------------------------------------------------------------------ */ |
| 7218 | /* Function: ipf_zerostats */ |
| 7219 | /* Returns: int - 0 = success, else failure */ |
| 7220 | /* Parameters: data(O) - pointer to pointer for copying data back to */ |
| 7221 | /* */ |
| 7222 | /* Copies the current statistics out to userspace and then zero's the */ |
| 7223 | /* current ones in the kernel. The lock is only held across the bzero() as */ |
| 7224 | /* the copyout may result in paging (ie network activity.) */ |
| 7225 | /* ------------------------------------------------------------------------ */ |
| 7226 | int |
| 7227 | ipf_zerostats(ipf_main_softc_t *softc, void *data) |
| 7228 | { |
| 7229 | friostat_t fio; |
| 7230 | ipfobj_t obj; |
| 7231 | int error; |
| 7232 | |
| 7233 | error = ipf_inobj(softc, data, &obj, &fio, IPFOBJ_IPFSTAT); |
| 7234 | if (error != 0) |
| 7235 | return error; |
| 7236 | ipf_getstat(softc, &fio, obj.ipfo_rev); |
| 7237 | error = ipf_outobj(softc, data, &fio, IPFOBJ_IPFSTAT); |
| 7238 | if (error != 0) |
| 7239 | return error; |
| 7240 | |
| 7241 | WRITE_ENTER(&softc->ipf_mutex); |
| 7242 | bzero(&softc->ipf_stats, sizeof(softc->ipf_stats)); |
| 7243 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 7244 | |
| 7245 | return 0; |
| 7246 | } |
| 7247 | |
| 7248 | |
| 7249 | /* ------------------------------------------------------------------------ */ |
| 7250 | /* Function: ipf_resolvedest */ |
| 7251 | /* Returns: Nil */ |
| 7252 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 7253 | /* base(I) - where strings are stored */ |
| 7254 | /* fdp(IO) - pointer to destination information to resolve */ |
| 7255 | /* v(I) - IP protocol version to match */ |
| 7256 | /* */ |
| 7257 | /* Looks up an interface name in the frdest structure pointed to by fdp and */ |
| 7258 | /* if a matching name can be found for the particular IP protocol version */ |
| 7259 | /* then store the interface pointer in the frdest struct. If no match is */ |
| 7260 | /* found, then set the interface pointer to be -1 as NULL is considered to */ |
| 7261 | /* indicate there is no information at all in the structure. */ |
| 7262 | /* ------------------------------------------------------------------------ */ |
| 7263 | int |
| 7264 | ipf_resolvedest(ipf_main_softc_t *softc, char *base, frdest_t *fdp, int v) |
| 7265 | { |
| 7266 | int errval = 0; |
| 7267 | void *ifp; |
| 7268 | |
| 7269 | ifp = NULL; |
| 7270 | |
| 7271 | if (fdp->fd_name != -1) { |
| 7272 | if (fdp->fd_type == FRD_DSTLIST) { |
| 7273 | ifp = ipf_lookup_res_name(softc, IPL_LOGIPF, |
| 7274 | IPLT_DSTLIST, |
| 7275 | base + fdp->fd_name, |
| 7276 | NULL); |
| 7277 | if (ifp == NULL) { |
| 7278 | IPFERROR(144); |
| 7279 | errval = ESRCH; |
| 7280 | } |
| 7281 | } else { |
| 7282 | ifp = GETIFP(base + fdp->fd_name, v); |
| 7283 | if (ifp == NULL) |
| 7284 | ifp = (void *)-1; |
| 7285 | if ((ifp != NULL) && (ifp != (void *)-1)) |
| 7286 | fdp->fd_local = ipf_deliverlocal(softc, v, ifp, |
| 7287 | &fdp->fd_ip6); |
| 7288 | } |
| 7289 | } |
| 7290 | fdp->fd_ptr = ifp; |
| 7291 | |
| 7292 | return errval; |
| 7293 | } |
| 7294 | |
| 7295 | |
| 7296 | /* ------------------------------------------------------------------------ */ |
| 7297 | /* Function: ipf_resolvenic */ |
| 7298 | /* Returns: void* - NULL = wildcard name, -1 = failed to find NIC, else */ |
| 7299 | /* pointer to interface structure for NIC */ |
| 7300 | /* Parameters: softc(I)- pointer to soft context main structure */ |
| 7301 | /* name(I) - complete interface name */ |
| 7302 | /* v(I) - IP protocol version */ |
| 7303 | /* */ |
| 7304 | /* Look for a network interface structure that firstly has a matching name */ |
| 7305 | /* to that passed in and that is also being used for that IP protocol */ |
| 7306 | /* version (necessary on some platforms where there are separate listings */ |
| 7307 | /* for both IPv4 and IPv6 on the same physical NIC. */ |
| 7308 | /* */ |
| 7309 | /* ------------------------------------------------------------------------ */ |
| 7310 | void * |
| 7311 | ipf_resolvenic(ipf_main_softc_t *softc, char *name, int v) |
| 7312 | { |
| 7313 | void *nic; |
| 7314 | |
| 7315 | softc = softc; /* gcc -Wextra */ |
| 7316 | if (name[0] == '\0') |
| 7317 | return NULL; |
| 7318 | |
| 7319 | if ((name[1] == '\0') && ((name[0] == '-') || (name[0] == '*'))) { |
| 7320 | return NULL; |
| 7321 | } |
| 7322 | |
| 7323 | nic = GETIFP(name, v); |
| 7324 | if (nic == NULL) |
| 7325 | nic = (void *)-1; |
| 7326 | return nic; |
| 7327 | } |
| 7328 | |
| 7329 | |
| 7330 | /* ------------------------------------------------------------------------ */ |
| 7331 | /* Function: ipf_token_expire */ |
| 7332 | /* Returns: None. */ |
| 7333 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 7334 | /* */ |
| 7335 | /* This function is run every ipf tick to see if there are any tokens that */ |
| 7336 | /* have been held for too long and need to be freed up. */ |
| 7337 | /* ------------------------------------------------------------------------ */ |
| 7338 | void |
| 7339 | ipf_token_expire(ipf_main_softc_t *softc) |
| 7340 | { |
| 7341 | ipftoken_t *it; |
| 7342 | |
| 7343 | WRITE_ENTER(&softc->ipf_tokens); |
| 7344 | while ((it = softc->ipf_token_head) != NULL) { |
| 7345 | if (it->ipt_die > softc->ipf_ticks) |
| 7346 | break; |
| 7347 | |
| 7348 | ipf_token_deref(softc, it); |
| 7349 | } |
| 7350 | RWLOCK_EXIT(&softc->ipf_tokens); |
| 7351 | } |
| 7352 | |
| 7353 | |
| 7354 | /* ------------------------------------------------------------------------ */ |
| 7355 | /* Function: ipf_token_flush */ |
| 7356 | /* Returns: None. */ |
| 7357 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 7358 | /* */ |
| 7359 | /* Loop through all of the existing tokens and call deref to see if they */ |
| 7360 | /* can be freed. Normally a function like this might just loop on */ |
| 7361 | /* ipf_token_head but there is a chance that a token might have a ref count */ |
| 7362 | /* of greater than one and in that case the the reference would drop twice */ |
| 7363 | /* by code that is only entitled to drop it once. */ |
| 7364 | /* ------------------------------------------------------------------------ */ |
| 7365 | static void |
| 7366 | ipf_token_flush(ipf_main_softc_t *softc) |
| 7367 | { |
| 7368 | ipftoken_t *it, *next; |
| 7369 | |
| 7370 | WRITE_ENTER(&softc->ipf_tokens); |
| 7371 | for (it = softc->ipf_token_head; it != NULL; it = next) { |
| 7372 | next = it->ipt_next; |
| 7373 | (void) ipf_token_deref(softc, it); |
| 7374 | } |
| 7375 | RWLOCK_EXIT(&softc->ipf_tokens); |
| 7376 | } |
| 7377 | |
| 7378 | |
| 7379 | /* ------------------------------------------------------------------------ */ |
| 7380 | /* Function: ipf_token_del */ |
| 7381 | /* Returns: int - 0 = success, else error */ |
| 7382 | /* Parameters: softc(I)- pointer to soft context main structure */ |
| 7383 | /* type(I) - the token type to match */ |
| 7384 | /* uid(I) - uid owning the token */ |
| 7385 | /* ptr(I) - context pointer for the token */ |
| 7386 | /* */ |
| 7387 | /* This function looks for a a token in the current list that matches up */ |
| 7388 | /* the fields (type, uid, ptr). If none is found, ESRCH is returned, else */ |
| 7389 | /* call ipf_token_dewref() to remove it from the list. In the event that */ |
| 7390 | /* the token has a reference held elsewhere, setting ipt_complete to 2 */ |
| 7391 | /* enables debugging to distinguish between the two paths that ultimately */ |
| 7392 | /* lead to a token to be deleted. */ |
| 7393 | /* ------------------------------------------------------------------------ */ |
| 7394 | int |
| 7395 | ipf_token_del(ipf_main_softc_t *softc, int type, int uid, void *ptr) |
| 7396 | { |
| 7397 | ipftoken_t *it; |
| 7398 | int error; |
| 7399 | |
| 7400 | IPFERROR(82); |
| 7401 | error = ESRCH; |
| 7402 | |
| 7403 | WRITE_ENTER(&softc->ipf_tokens); |
| 7404 | for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { |
| 7405 | if (ptr == it->ipt_ctx && type == it->ipt_type && |
| 7406 | uid == it->ipt_uid) { |
| 7407 | it->ipt_complete = 2; |
| 7408 | ipf_token_deref(softc, it); |
| 7409 | error = 0; |
| 7410 | break; |
| 7411 | } |
| 7412 | } |
| 7413 | RWLOCK_EXIT(&softc->ipf_tokens); |
| 7414 | |
| 7415 | return error; |
| 7416 | } |
| 7417 | |
| 7418 | |
| 7419 | /* ------------------------------------------------------------------------ */ |
| 7420 | /* Function: ipf_token_mark_complete */ |
| 7421 | /* Returns: None. */ |
| 7422 | /* Parameters: token(I) - pointer to token structure */ |
| 7423 | /* */ |
| 7424 | /* Mark a token as being ineligable for being found with ipf_token_find. */ |
| 7425 | /* ------------------------------------------------------------------------ */ |
| 7426 | void |
| 7427 | ipf_token_mark_complete(ipftoken_t *token) |
| 7428 | { |
| 7429 | if (token->ipt_complete == 0) |
| 7430 | token->ipt_complete = 1; |
| 7431 | } |
| 7432 | |
| 7433 | |
| 7434 | /* ------------------------------------------------------------------------ */ |
| 7435 | /* Function: ipf_token_find */ |
| 7436 | /* Returns: ipftoken_t * - NULL if no memory, else pointer to token */ |
| 7437 | /* Parameters: softc(I)- pointer to soft context main structure */ |
| 7438 | /* type(I) - the token type to match */ |
| 7439 | /* uid(I) - uid owning the token */ |
| 7440 | /* ptr(I) - context pointer for the token */ |
| 7441 | /* */ |
| 7442 | /* This function looks for a live token in the list of current tokens that */ |
| 7443 | /* matches the tuple (type, uid, ptr). If one cannot be found then one is */ |
| 7444 | /* allocated. If one is found then it is moved to the top of the list of */ |
| 7445 | /* currently active tokens. */ |
| 7446 | /* ------------------------------------------------------------------------ */ |
| 7447 | ipftoken_t * |
| 7448 | ipf_token_find(ipf_main_softc_t *softc, int type, int uid, void *ptr) |
| 7449 | { |
| 7450 | ipftoken_t *it, *new; |
| 7451 | |
| 7452 | KMALLOC(new, ipftoken_t *); |
| 7453 | if (new != NULL) |
| 7454 | bzero((char *)new, sizeof(*new)); |
| 7455 | |
| 7456 | WRITE_ENTER(&softc->ipf_tokens); |
| 7457 | for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { |
| 7458 | if ((ptr == it->ipt_ctx) && (type == it->ipt_type) && |
| 7459 | (uid == it->ipt_uid) && (it->ipt_complete < 2)) |
| 7460 | break; |
| 7461 | } |
| 7462 | |
| 7463 | if (it == NULL) { |
| 7464 | it = new; |
| 7465 | new = NULL; |
| 7466 | if (it == NULL) { |
| 7467 | RWLOCK_EXIT(&softc->ipf_tokens); |
| 7468 | return NULL; |
| 7469 | } |
| 7470 | it->ipt_ctx = ptr; |
| 7471 | it->ipt_uid = uid; |
| 7472 | it->ipt_type = type; |
| 7473 | it->ipt_ref = 1; |
| 7474 | } else { |
| 7475 | if (new != NULL) { |
| 7476 | KFREE(new); |
| 7477 | new = NULL; |
| 7478 | } |
| 7479 | |
| 7480 | if (it->ipt_complete > 0) |
| 7481 | it = NULL; |
| 7482 | else |
| 7483 | ipf_token_unlink(softc, it); |
| 7484 | } |
| 7485 | |
| 7486 | if (it != NULL) { |
| 7487 | it->ipt_pnext = softc->ipf_token_tail; |
| 7488 | *softc->ipf_token_tail = it; |
| 7489 | softc->ipf_token_tail = &it->ipt_next; |
| 7490 | it->ipt_next = NULL; |
| 7491 | it->ipt_ref++; |
| 7492 | |
| 7493 | it->ipt_die = softc->ipf_ticks + 20; |
| 7494 | } |
| 7495 | |
| 7496 | RWLOCK_EXIT(&softc->ipf_tokens); |
| 7497 | |
| 7498 | return it; |
| 7499 | } |
| 7500 | |
| 7501 | |
| 7502 | /* ------------------------------------------------------------------------ */ |
| 7503 | /* Function: ipf_token_unlink */ |
| 7504 | /* Returns: None. */ |
| 7505 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 7506 | /* token(I) - pointer to token structure */ |
| 7507 | /* Write Locks: ipf_tokens */ |
| 7508 | /* */ |
| 7509 | /* This function unlinks a token structure from the linked list of tokens */ |
| 7510 | /* that "own" it. The head pointer never needs to be explicitly adjusted */ |
| 7511 | /* but the tail does due to the linked list implementation. */ |
| 7512 | /* ------------------------------------------------------------------------ */ |
| 7513 | static void |
| 7514 | ipf_token_unlink(ipf_main_softc_t *softc, ipftoken_t *token) |
| 7515 | { |
| 7516 | |
| 7517 | if (softc->ipf_token_tail == &token->ipt_next) |
| 7518 | softc->ipf_token_tail = token->ipt_pnext; |
| 7519 | |
| 7520 | *token->ipt_pnext = token->ipt_next; |
| 7521 | if (token->ipt_next != NULL) |
| 7522 | token->ipt_next->ipt_pnext = token->ipt_pnext; |
| 7523 | token->ipt_next = NULL; |
| 7524 | token->ipt_pnext = NULL; |
| 7525 | } |
| 7526 | |
| 7527 | |
| 7528 | /* ------------------------------------------------------------------------ */ |
| 7529 | /* Function: ipf_token_deref */ |
| 7530 | /* Returns: int - 0 == token freed, else reference count */ |
| 7531 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 7532 | /* token(I) - pointer to token structure */ |
| 7533 | /* Write Locks: ipf_tokens */ |
| 7534 | /* */ |
| 7535 | /* Drop the reference count on the token structure and if it drops to zero, */ |
| 7536 | /* call the dereference function for the token type because it is then */ |
| 7537 | /* possible to free the token data structure. */ |
| 7538 | /* ------------------------------------------------------------------------ */ |
| 7539 | int |
| 7540 | ipf_token_deref(ipf_main_softc_t *softc, ipftoken_t *token) |
| 7541 | { |
| 7542 | void *data, **datap; |
| 7543 | |
| 7544 | ASSERT(token->ipt_ref > 0); |
| 7545 | token->ipt_ref--; |
| 7546 | if (token->ipt_ref > 0) |
| 7547 | return token->ipt_ref; |
| 7548 | |
| 7549 | data = token->ipt_data; |
| 7550 | datap = &data; |
| 7551 | |
| 7552 | if ((data != NULL) && (data != (void *)-1)) { |
| 7553 | switch (token->ipt_type) |
| 7554 | { |
| 7555 | case IPFGENITER_IPF : |
| 7556 | (void) ipf_derefrule(softc, (frentry_t **)datap); |
| 7557 | break; |
| 7558 | case IPFGENITER_IPNAT : |
| 7559 | WRITE_ENTER(&softc->ipf_nat); |
| 7560 | ipf_nat_rule_deref(softc, (ipnat_t **)datap); |
| 7561 | RWLOCK_EXIT(&softc->ipf_nat); |
| 7562 | break; |
| 7563 | case IPFGENITER_NAT : |
| 7564 | ipf_nat_deref(softc, (nat_t **)datap); |
| 7565 | break; |
| 7566 | case IPFGENITER_STATE : |
| 7567 | ipf_state_deref(softc, (ipstate_t **)datap); |
| 7568 | break; |
| 7569 | case IPFGENITER_FRAG : |
| 7570 | ipf_frag_pkt_deref(softc, (ipfr_t **)datap); |
| 7571 | break; |
| 7572 | case IPFGENITER_NATFRAG : |
| 7573 | ipf_frag_nat_deref(softc, (ipfr_t **)datap); |
| 7574 | break; |
| 7575 | case IPFGENITER_HOSTMAP : |
| 7576 | WRITE_ENTER(&softc->ipf_nat); |
| 7577 | ipf_nat_hostmapdel(softc, (hostmap_t **)datap); |
| 7578 | RWLOCK_EXIT(&softc->ipf_nat); |
| 7579 | break; |
| 7580 | default : |
| 7581 | ipf_lookup_iterderef(softc, token->ipt_type, data); |
| 7582 | break; |
| 7583 | } |
| 7584 | } |
| 7585 | |
| 7586 | ipf_token_unlink(softc, token); |
| 7587 | KFREE(token); |
| 7588 | return 0; |
| 7589 | } |
| 7590 | |
| 7591 | |
| 7592 | /* ------------------------------------------------------------------------ */ |
| 7593 | /* Function: ipf_nextrule */ |
| 7594 | /* Returns: frentry_t * - NULL == no more rules, else pointer to next */ |
| 7595 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 7596 | /* fr(I) - pointer to filter rule */ |
| 7597 | /* out(I) - 1 == out rules, 0 == input rules */ |
| 7598 | /* */ |
| 7599 | /* Starting with "fr", find the next rule to visit. This includes visiting */ |
| 7600 | /* the list of rule groups if either fr is NULL (empty list) or it is the */ |
| 7601 | /* last rule in the list. When walking rule lists, it is either input or */ |
| 7602 | /* output rules that are returned, never both. */ |
| 7603 | /* ------------------------------------------------------------------------ */ |
| 7604 | static frentry_t * |
| 7605 | ipf_nextrule(ipf_main_softc_t *softc, int active, int unit, |
| 7606 | frentry_t *fr, int out) |
| 7607 | { |
| 7608 | frentry_t *next; |
| 7609 | frgroup_t *fg; |
| 7610 | |
| 7611 | if (fr != NULL && fr->fr_group != -1) { |
| 7612 | fg = ipf_findgroup(softc, fr->fr_names + fr->fr_group, |
| 7613 | unit, active, NULL); |
| 7614 | if (fg != NULL) |
| 7615 | fg = fg->fg_next; |
| 7616 | } else { |
| 7617 | fg = softc->ipf_groups[unit][active]; |
| 7618 | } |
| 7619 | |
| 7620 | while (fg != NULL) { |
| 7621 | next = fg->fg_start; |
| 7622 | while (next != NULL) { |
| 7623 | if (out) { |
| 7624 | if (next->fr_flags & FR_OUTQUE) |
| 7625 | return next; |
| 7626 | } else if (next->fr_flags & FR_INQUE) { |
| 7627 | return next; |
| 7628 | } |
| 7629 | next = next->fr_next; |
| 7630 | } |
| 7631 | if (next == NULL) |
| 7632 | fg = fg->fg_next; |
| 7633 | } |
| 7634 | |
| 7635 | return NULL; |
| 7636 | } |
| 7637 | |
| 7638 | /* ------------------------------------------------------------------------ */ |
| 7639 | /* Function: ipf_getnextrule */ |
| 7640 | /* Returns: int - 0 = success, else error */ |
| 7641 | /* Parameters: softc(I)- pointer to soft context main structure */ |
| 7642 | /* t(I) - pointer to destination information to resolve */ |
| 7643 | /* ptr(I) - pointer to ipfobj_t to copyin from user space */ |
| 7644 | /* */ |
| 7645 | /* This function's first job is to bring in the ipfruleiter_t structure via */ |
| 7646 | /* the ipfobj_t structure to determine what should be the next rule to */ |
| 7647 | /* return. Once the ipfruleiter_t has been brought in, it then tries to */ |
| 7648 | /* find the 'next rule'. This may include searching rule group lists or */ |
| 7649 | /* just be as simple as looking at the 'next' field in the rule structure. */ |
| 7650 | /* When we have found the rule to return, increase its reference count and */ |
| 7651 | /* if we used an existing rule to get here, decrease its reference count. */ |
| 7652 | /* ------------------------------------------------------------------------ */ |
| 7653 | int |
| 7654 | ipf_getnextrule(ipf_main_softc_t *softc, ipftoken_t *t, void *ptr) |
| 7655 | { |
| 7656 | frentry_t *fr, *next, zero; |
| 7657 | ipfruleiter_t it; |
| 7658 | int error, out; |
| 7659 | frgroup_t *fg; |
| 7660 | ipfobj_t obj; |
| 7661 | int predict; |
| 7662 | char *dst; |
| 7663 | int unit; |
| 7664 | |
| 7665 | if (t == NULL || ptr == NULL) { |
| 7666 | IPFERROR(84); |
| 7667 | return EFAULT; |
| 7668 | } |
| 7669 | |
| 7670 | error = ipf_inobj(softc, ptr, &obj, &it, IPFOBJ_IPFITER); |
| 7671 | if (error != 0) |
| 7672 | return error; |
| 7673 | |
| 7674 | if ((it.iri_inout < 0) || (it.iri_inout > 3)) { |
| 7675 | IPFERROR(85); |
| 7676 | return EINVAL; |
| 7677 | } |
| 7678 | if ((it.iri_active != 0) && (it.iri_active != 1)) { |
| 7679 | IPFERROR(86); |
| 7680 | return EINVAL; |
| 7681 | } |
| 7682 | if (it.iri_nrules == 0) { |
| 7683 | IPFERROR(87); |
| 7684 | return ENOSPC; |
| 7685 | } |
| 7686 | if (it.iri_rule == NULL) { |
| 7687 | IPFERROR(88); |
| 7688 | return EFAULT; |
| 7689 | } |
| 7690 | |
| 7691 | fg = NULL; |
| 7692 | fr = t->ipt_data; |
| 7693 | if ((it.iri_inout & F_OUT) != 0) |
| 7694 | out = 1; |
| 7695 | else |
| 7696 | out = 0; |
| 7697 | if ((it.iri_inout & F_ACIN) != 0) |
| 7698 | unit = IPL_LOGCOUNT; |
| 7699 | else |
| 7700 | unit = IPL_LOGIPF; |
| 7701 | |
| 7702 | READ_ENTER(&softc->ipf_mutex); |
| 7703 | if (fr == NULL) { |
| 7704 | if (*it.iri_group == '\0') { |
| 7705 | if (unit == IPL_LOGCOUNT) { |
| 7706 | next = softc->ipf_acct[out][it.iri_active]; |
| 7707 | } else { |
| 7708 | next = softc->ipf_rules[out][it.iri_active]; |
| 7709 | } |
| 7710 | if (next == NULL) |
| 7711 | next = ipf_nextrule(softc, it.iri_active, |
| 7712 | unit, NULL, out); |
| 7713 | } else { |
| 7714 | fg = ipf_findgroup(softc, it.iri_group, unit, |
| 7715 | it.iri_active, NULL); |
| 7716 | if (fg != NULL) |
| 7717 | next = fg->fg_start; |
| 7718 | else |
| 7719 | next = NULL; |
| 7720 | } |
| 7721 | } else { |
| 7722 | next = fr->fr_next; |
| 7723 | if (next == NULL) |
| 7724 | next = ipf_nextrule(softc, it.iri_active, unit, |
| 7725 | fr, out); |
| 7726 | } |
| 7727 | |
| 7728 | if (next != NULL && next->fr_next != NULL) |
| 7729 | predict = 1; |
| 7730 | else if (ipf_nextrule(softc, it.iri_active, unit, next, out) != NULL) |
| 7731 | predict = 1; |
| 7732 | else |
| 7733 | predict = 0; |
| 7734 | |
| 7735 | if (fr != NULL) |
| 7736 | (void) ipf_derefrule(softc, &fr); |
| 7737 | |
| 7738 | obj.ipfo_type = IPFOBJ_FRENTRY; |
| 7739 | dst = (char *)it.iri_rule; |
| 7740 | |
| 7741 | if (next != NULL) { |
| 7742 | obj.ipfo_size = next->fr_size; |
| 7743 | MUTEX_ENTER(&next->fr_lock); |
| 7744 | next->fr_ref++; |
| 7745 | MUTEX_EXIT(&next->fr_lock); |
| 7746 | t->ipt_data = next; |
| 7747 | } else { |
| 7748 | obj.ipfo_size = sizeof(frentry_t); |
| 7749 | bzero(&zero, sizeof(zero)); |
| 7750 | next = &zero; |
| 7751 | t->ipt_data = NULL; |
| 7752 | } |
| 7753 | it.iri_rule = predict ? next : NULL; |
| 7754 | if (predict == 0) |
| 7755 | ipf_token_mark_complete(t); |
| 7756 | |
| 7757 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 7758 | |
| 7759 | obj.ipfo_ptr = dst; |
| 7760 | error = ipf_outobjk(softc, &obj, next); |
| 7761 | if (error == 0 && t->ipt_data != NULL) { |
| 7762 | dst += obj.ipfo_size; |
| 7763 | if (next->fr_data != NULL) { |
| 7764 | ipfobj_t dobj; |
| 7765 | |
| 7766 | if (next->fr_type == FR_T_IPFEXPR) |
| 7767 | dobj.ipfo_type = IPFOBJ_IPFEXPR; |
| 7768 | else |
| 7769 | dobj.ipfo_type = IPFOBJ_FRIPF; |
| 7770 | dobj.ipfo_size = next->fr_dsize; |
| 7771 | dobj.ipfo_rev = obj.ipfo_rev; |
| 7772 | dobj.ipfo_ptr = dst; |
| 7773 | error = ipf_outobjk(softc, &dobj, next->fr_data); |
| 7774 | } |
| 7775 | } |
| 7776 | |
| 7777 | if ((fr != NULL) && (next == &zero)) |
| 7778 | (void) ipf_derefrule(softc, &fr); |
| 7779 | |
| 7780 | return error; |
| 7781 | } |
| 7782 | |
| 7783 | |
| 7784 | /* ------------------------------------------------------------------------ */ |
| 7785 | /* Function: ipf_frruleiter */ |
| 7786 | /* Returns: int - 0 = success, else error */ |
| 7787 | /* Parameters: softc(I)- pointer to soft context main structure */ |
| 7788 | /* data(I) - the token type to match */ |
| 7789 | /* uid(I) - uid owning the token */ |
| 7790 | /* ptr(I) - context pointer for the token */ |
| 7791 | /* */ |
| 7792 | /* This function serves as a stepping stone between ipf_ipf_ioctl and */ |
| 7793 | /* ipf_getnextrule. It's role is to find the right token in the kernel for */ |
| 7794 | /* the process doing the ioctl and use that to ask for the next rule. */ |
| 7795 | /* ------------------------------------------------------------------------ */ |
| 7796 | static int |
| 7797 | ipf_frruleiter(ipf_main_softc_t *softc, void *data, int uid, void *ctx) |
| 7798 | { |
| 7799 | ipftoken_t *token; |
| 7800 | ipfruleiter_t it; |
| 7801 | ipfobj_t obj; |
| 7802 | int error; |
| 7803 | |
| 7804 | token = ipf_token_find(softc, IPFGENITER_IPF, uid, ctx); |
| 7805 | if (token != NULL) { |
| 7806 | error = ipf_getnextrule(softc, token, data); |
| 7807 | WRITE_ENTER(&softc->ipf_tokens); |
| 7808 | ipf_token_deref(softc, token); |
| 7809 | RWLOCK_EXIT(&softc->ipf_tokens); |
| 7810 | } else { |
| 7811 | error = ipf_inobj(softc, data, &obj, &it, IPFOBJ_IPFITER); |
| 7812 | if (error != 0) |
| 7813 | return error; |
| 7814 | it.iri_rule = NULL; |
| 7815 | error = ipf_outobj(softc, data, &it, IPFOBJ_IPFITER); |
| 7816 | } |
| 7817 | |
| 7818 | return error; |
| 7819 | } |
| 7820 | |
| 7821 | |
| 7822 | /* ------------------------------------------------------------------------ */ |
| 7823 | /* Function: ipf_geniter */ |
| 7824 | /* Returns: int - 0 = success, else error */ |
| 7825 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 7826 | /* token(I) - pointer to ipftoken_t structure */ |
| 7827 | /* itp(I) - pointer to iterator data */ |
| 7828 | /* */ |
| 7829 | /* Decide which iterator function to call using information passed through */ |
| 7830 | /* the ipfgeniter_t structure at itp. */ |
| 7831 | /* ------------------------------------------------------------------------ */ |
| 7832 | static int |
| 7833 | ipf_geniter(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) |
| 7834 | { |
| 7835 | int error; |
| 7836 | |
| 7837 | switch (itp->igi_type) |
| 7838 | { |
| 7839 | case IPFGENITER_FRAG : |
| 7840 | error = ipf_frag_pkt_next(softc, token, itp); |
| 7841 | break; |
| 7842 | default : |
| 7843 | IPFERROR(92); |
| 7844 | error = EINVAL; |
| 7845 | break; |
| 7846 | } |
| 7847 | |
| 7848 | return error; |
| 7849 | } |
| 7850 | |
| 7851 | |
| 7852 | /* ------------------------------------------------------------------------ */ |
| 7853 | /* Function: ipf_genericiter */ |
| 7854 | /* Returns: int - 0 = success, else error */ |
| 7855 | /* Parameters: softc(I)- pointer to soft context main structure */ |
| 7856 | /* data(I) - the token type to match */ |
| 7857 | /* uid(I) - uid owning the token */ |
| 7858 | /* ptr(I) - context pointer for the token */ |
| 7859 | /* */ |
| 7860 | /* Handle the SIOCGENITER ioctl for the ipfilter device. The primary role */ |
| 7861 | /* ------------------------------------------------------------------------ */ |
| 7862 | int |
| 7863 | ipf_genericiter(ipf_main_softc_t *softc, void *data, int uid, void *ctx) |
| 7864 | { |
| 7865 | ipftoken_t *token; |
| 7866 | ipfgeniter_t iter; |
| 7867 | int error; |
| 7868 | |
| 7869 | error = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_GENITER); |
| 7870 | if (error != 0) |
| 7871 | return error; |
| 7872 | |
| 7873 | token = ipf_token_find(softc, iter.igi_type, uid, ctx); |
| 7874 | if (token != NULL) { |
| 7875 | token->ipt_subtype = iter.igi_type; |
| 7876 | error = ipf_geniter(softc, token, &iter); |
| 7877 | WRITE_ENTER(&softc->ipf_tokens); |
| 7878 | ipf_token_deref(softc, token); |
| 7879 | RWLOCK_EXIT(&softc->ipf_tokens); |
| 7880 | } else { |
| 7881 | IPFERROR(93); |
| 7882 | error = 0; |
| 7883 | } |
| 7884 | |
| 7885 | return error; |
| 7886 | } |
| 7887 | |
| 7888 | |
| 7889 | /* ------------------------------------------------------------------------ */ |
| 7890 | /* Function: ipf_ipf_ioctl */ |
| 7891 | /* Returns: int - 0 = success, else error */ |
| 7892 | /* Parameters: softc(I)- pointer to soft context main structure */ |
| 7893 | /* data(I) - the token type to match */ |
| 7894 | /* cmd(I) - the ioctl command number */ |
| 7895 | /* mode(I) - mode flags for the ioctl */ |
| 7896 | /* uid(I) - uid owning the token */ |
| 7897 | /* ptr(I) - context pointer for the token */ |
| 7898 | /* */ |
| 7899 | /* This function handles all of the ioctl command that are actually isssued */ |
| 7900 | /* to the /dev/ipl device. */ |
| 7901 | /* ------------------------------------------------------------------------ */ |
| 7902 | int |
| 7903 | ipf_ipf_ioctl(ipf_main_softc_t *softc, void *data, ioctlcmd_t cmd, int mode, |
| 7904 | int uid, void *ctx) |
| 7905 | { |
| 7906 | friostat_t fio; |
| 7907 | int error, tmp; |
| 7908 | ipfobj_t obj; |
| 7909 | SPL_INT(s); |
| 7910 | |
| 7911 | switch (cmd) |
| 7912 | { |
| 7913 | case SIOCFRENB : |
| 7914 | if (!(mode & FWRITE)) { |
| 7915 | IPFERROR(94); |
| 7916 | error = EPERM; |
| 7917 | } else { |
| 7918 | error = BCOPYIN(data, &tmp, sizeof(tmp)); |
| 7919 | if (error != 0) { |
| 7920 | IPFERROR(95); |
| 7921 | error = EFAULT; |
| 7922 | break; |
| 7923 | } |
| 7924 | |
| 7925 | WRITE_ENTER(&softc->ipf_global); |
| 7926 | if (tmp) { |
| 7927 | if (softc->ipf_running > 0) |
| 7928 | error = 0; |
| 7929 | else |
| 7930 | error = ipfattach(softc); |
| 7931 | if (error == 0) |
| 7932 | softc->ipf_running = 1; |
| 7933 | else |
| 7934 | (void) ipfdetach(softc); |
| 7935 | } else { |
| 7936 | if (softc->ipf_running == 1) |
| 7937 | error = ipfdetach(softc); |
| 7938 | else |
| 7939 | error = 0; |
| 7940 | if (error == 0) |
| 7941 | softc->ipf_running = -1; |
| 7942 | } |
| 7943 | RWLOCK_EXIT(&softc->ipf_global); |
| 7944 | } |
| 7945 | break; |
| 7946 | |
| 7947 | case SIOCIPFSET : |
| 7948 | if (!(mode & FWRITE)) { |
| 7949 | IPFERROR(96); |
| 7950 | error = EPERM; |
| 7951 | break; |
| 7952 | } |
| 7953 | /* FALLTHRU */ |
| 7954 | case SIOCIPFGETNEXT : |
| 7955 | case SIOCIPFGET : |
| 7956 | error = ipf_ipftune(softc, cmd, (void *)data); |
| 7957 | break; |
| 7958 | |
| 7959 | case SIOCSETFF : |
| 7960 | if (!(mode & FWRITE)) { |
| 7961 | IPFERROR(97); |
| 7962 | error = EPERM; |
| 7963 | } else { |
| 7964 | error = BCOPYIN(data, &softc->ipf_flags, |
| 7965 | sizeof(softc->ipf_flags)); |
| 7966 | if (error != 0) { |
| 7967 | IPFERROR(98); |
| 7968 | error = EFAULT; |
| 7969 | } |
| 7970 | } |
| 7971 | break; |
| 7972 | |
| 7973 | case SIOCGETFF : |
| 7974 | error = BCOPYOUT(&softc->ipf_flags, data, |
| 7975 | sizeof(softc->ipf_flags)); |
| 7976 | if (error != 0) { |
| 7977 | IPFERROR(99); |
| 7978 | error = EFAULT; |
| 7979 | } |
| 7980 | break; |
| 7981 | |
| 7982 | case SIOCFUNCL : |
| 7983 | error = ipf_resolvefunc(softc, (void *)data); |
| 7984 | break; |
| 7985 | |
| 7986 | case SIOCINAFR : |
| 7987 | case SIOCRMAFR : |
| 7988 | case SIOCADAFR : |
| 7989 | case SIOCZRLST : |
| 7990 | if (!(mode & FWRITE)) { |
| 7991 | IPFERROR(100); |
| 7992 | error = EPERM; |
| 7993 | } else { |
| 7994 | error = frrequest(softc, IPL_LOGIPF, cmd, data, |
| 7995 | softc->ipf_active, 1); |
| 7996 | } |
| 7997 | break; |
| 7998 | |
| 7999 | case SIOCINIFR : |
| 8000 | case SIOCRMIFR : |
| 8001 | case SIOCADIFR : |
| 8002 | if (!(mode & FWRITE)) { |
| 8003 | IPFERROR(101); |
| 8004 | error = EPERM; |
| 8005 | } else { |
| 8006 | error = frrequest(softc, IPL_LOGIPF, cmd, data, |
| 8007 | 1 - softc->ipf_active, 1); |
| 8008 | } |
| 8009 | break; |
| 8010 | |
| 8011 | case SIOCSWAPA : |
| 8012 | if (!(mode & FWRITE)) { |
| 8013 | IPFERROR(102); |
| 8014 | error = EPERM; |
| 8015 | } else { |
| 8016 | WRITE_ENTER(&softc->ipf_mutex); |
| 8017 | error = BCOPYOUT(&softc->ipf_active, data, |
| 8018 | sizeof(softc->ipf_active)); |
| 8019 | if (error != 0) { |
| 8020 | IPFERROR(103); |
| 8021 | error = EFAULT; |
| 8022 | } else { |
| 8023 | softc->ipf_active = 1 - softc->ipf_active; |
| 8024 | } |
| 8025 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 8026 | } |
| 8027 | break; |
| 8028 | |
| 8029 | case SIOCGETFS : |
| 8030 | error = ipf_inobj(softc, (void *)data, &obj, &fio, |
| 8031 | IPFOBJ_IPFSTAT); |
| 8032 | if (error != 0) |
| 8033 | break; |
| 8034 | ipf_getstat(softc, &fio, obj.ipfo_rev); |
| 8035 | error = ipf_outobj(softc, (void *)data, &fio, IPFOBJ_IPFSTAT); |
| 8036 | break; |
| 8037 | |
| 8038 | case SIOCFRZST : |
| 8039 | if (!(mode & FWRITE)) { |
| 8040 | IPFERROR(104); |
| 8041 | error = EPERM; |
| 8042 | } else |
| 8043 | error = ipf_zerostats(softc, data); |
| 8044 | break; |
| 8045 | |
| 8046 | case SIOCIPFFL : |
| 8047 | if (!(mode & FWRITE)) { |
| 8048 | IPFERROR(105); |
| 8049 | error = EPERM; |
| 8050 | } else { |
| 8051 | error = BCOPYIN(data, &tmp, sizeof(tmp)); |
| 8052 | if (!error) { |
| 8053 | tmp = ipf_flush(softc, IPL_LOGIPF, tmp); |
| 8054 | error = BCOPYOUT(&tmp, data, sizeof(tmp)); |
| 8055 | if (error != 0) { |
| 8056 | IPFERROR(106); |
| 8057 | error = EFAULT; |
| 8058 | } |
| 8059 | } else { |
| 8060 | IPFERROR(107); |
| 8061 | error = EFAULT; |
| 8062 | } |
| 8063 | } |
| 8064 | break; |
| 8065 | |
| 8066 | #ifdef USE_INET6 |
| 8067 | case SIOCIPFL6 : |
| 8068 | if (!(mode & FWRITE)) { |
| 8069 | IPFERROR(108); |
| 8070 | error = EPERM; |
| 8071 | } else { |
| 8072 | error = BCOPYIN(data, &tmp, sizeof(tmp)); |
| 8073 | if (!error) { |
| 8074 | tmp = ipf_flush(softc, IPL_LOGIPF, tmp); |
| 8075 | error = BCOPYOUT(&tmp, data, sizeof(tmp)); |
| 8076 | if (error != 0) { |
| 8077 | IPFERROR(109); |
| 8078 | error = EFAULT; |
| 8079 | } |
| 8080 | } else { |
| 8081 | IPFERROR(110); |
| 8082 | error = EFAULT; |
| 8083 | } |
| 8084 | } |
| 8085 | break; |
| 8086 | #endif |
| 8087 | |
| 8088 | case SIOCSTLCK : |
| 8089 | if (!(mode & FWRITE)) { |
| 8090 | IPFERROR(122); |
| 8091 | error = EPERM; |
| 8092 | } else { |
| 8093 | error = BCOPYIN(data, &tmp, sizeof(tmp)); |
| 8094 | if (error == 0) { |
| 8095 | ipf_state_setlock(softc->ipf_state_soft, tmp); |
| 8096 | ipf_nat_setlock(softc->ipf_nat_soft, tmp); |
| 8097 | ipf_frag_setlock(softc->ipf_frag_soft, tmp); |
| 8098 | ipf_auth_setlock(softc->ipf_auth_soft, tmp); |
| 8099 | } else { |
| 8100 | IPFERROR(111); |
| 8101 | error = EFAULT; |
| 8102 | } |
| 8103 | } |
| 8104 | break; |
| 8105 | |
| 8106 | #ifdef IPFILTER_LOG |
| 8107 | case SIOCIPFFB : |
| 8108 | if (!(mode & FWRITE)) { |
| 8109 | IPFERROR(112); |
| 8110 | error = EPERM; |
| 8111 | } else { |
| 8112 | tmp = ipf_log_clear(softc, IPL_LOGIPF); |
| 8113 | error = BCOPYOUT(&tmp, data, sizeof(tmp)); |
| 8114 | if (error) { |
| 8115 | IPFERROR(113); |
| 8116 | error = EFAULT; |
| 8117 | } |
| 8118 | } |
| 8119 | break; |
| 8120 | #endif /* IPFILTER_LOG */ |
| 8121 | |
| 8122 | case SIOCFRSYN : |
| 8123 | if (!(mode & FWRITE)) { |
| 8124 | IPFERROR(114); |
| 8125 | error = EPERM; |
| 8126 | } else { |
| 8127 | WRITE_ENTER(&softc->ipf_global); |
| 8128 | #if (defined(MENTAT) && defined(_KERNEL)) && !defined(INSTANCES) |
| 8129 | error = ipfsync(); |
| 8130 | #else |
| 8131 | ipf_sync(softc, NULL); |
| 8132 | error = 0; |
| 8133 | #endif |
| 8134 | RWLOCK_EXIT(&softc->ipf_global); |
| 8135 | |
| 8136 | } |
| 8137 | break; |
| 8138 | |
| 8139 | case SIOCGFRST : |
| 8140 | error = ipf_outobj(softc, (void *)data, |
| 8141 | ipf_frag_stats(softc->ipf_frag_soft), |
| 8142 | IPFOBJ_FRAGSTAT); |
| 8143 | break; |
| 8144 | |
| 8145 | #ifdef IPFILTER_LOG |
| 8146 | case FIONREAD : |
| 8147 | tmp = ipf_log_bytesused(softc, IPL_LOGIPF); |
| 8148 | error = BCOPYOUT(&tmp, data, sizeof(tmp)); |
| 8149 | break; |
| 8150 | #endif |
| 8151 | |
| 8152 | case SIOCIPFITER : |
| 8153 | SPL_SCHED(s); |
| 8154 | error = ipf_frruleiter(softc, data, uid, ctx); |
| 8155 | SPL_X(s); |
| 8156 | break; |
| 8157 | |
| 8158 | case SIOCGENITER : |
| 8159 | SPL_SCHED(s); |
| 8160 | error = ipf_genericiter(softc, data, uid, ctx); |
| 8161 | SPL_X(s); |
| 8162 | break; |
| 8163 | |
| 8164 | case SIOCIPFDELTOK : |
| 8165 | error = BCOPYIN(data, &tmp, sizeof(tmp)); |
| 8166 | if (error == 0) { |
| 8167 | SPL_SCHED(s); |
| 8168 | error = ipf_token_del(softc, tmp, uid, ctx); |
| 8169 | SPL_X(s); |
| 8170 | } |
| 8171 | break; |
| 8172 | |
| 8173 | default : |
| 8174 | IPFERROR(115); |
| 8175 | error = EINVAL; |
| 8176 | break; |
| 8177 | } |
| 8178 | |
| 8179 | return error; |
| 8180 | } |
| 8181 | |
| 8182 | |
| 8183 | /* ------------------------------------------------------------------------ */ |
| 8184 | /* Function: ipf_decaps */ |
| 8185 | /* Returns: int - -1 == decapsulation failed, else bit mask of */ |
| 8186 | /* flags indicating packet filtering decision. */ |
| 8187 | /* Parameters: fin(I) - pointer to packet information */ |
| 8188 | /* pass(I) - IP protocol version to match */ |
| 8189 | /* l5proto(I) - layer 5 protocol to decode UDP data as. */ |
| 8190 | /* */ |
| 8191 | /* This function is called for packets that are wrapt up in other packets, */ |
| 8192 | /* for example, an IP packet that is the entire data segment for another IP */ |
| 8193 | /* packet. If the basic constraints for this are satisfied, change the */ |
| 8194 | /* buffer to point to the start of the inner packet and start processing */ |
| 8195 | /* rules belonging to the head group this rule specifies. */ |
| 8196 | /* ------------------------------------------------------------------------ */ |
| 8197 | u_32_t |
| 8198 | ipf_decaps(fr_info_t *fin, u_32_t pass, int l5proto) |
| 8199 | { |
| 8200 | fr_info_t fin2, *fino = NULL; |
| 8201 | int elen, hlen, nh; |
| 8202 | grehdr_t gre; |
| 8203 | ip_t *ip; |
| 8204 | mb_t *m; |
| 8205 | |
| 8206 | if ((fin->fin_flx & FI_COALESCE) == 0) |
| 8207 | if (ipf_coalesce(fin) == -1) |
| 8208 | goto cantdecaps; |
| 8209 | |
| 8210 | m = fin->fin_m; |
| 8211 | hlen = fin->fin_hlen; |
| 8212 | |
| 8213 | switch (fin->fin_p) |
| 8214 | { |
| 8215 | case IPPROTO_UDP : |
| 8216 | /* |
| 8217 | * In this case, the specific protocol being decapsulated |
| 8218 | * inside UDP frames comes from the rule. |
| 8219 | */ |
| 8220 | nh = fin->fin_fr->fr_icode; |
| 8221 | break; |
| 8222 | |
| 8223 | case IPPROTO_GRE : /* 47 */ |
| 8224 | bcopy(fin->fin_dp, (char *)&gre, sizeof(gre)); |
| 8225 | hlen += sizeof(grehdr_t); |
| 8226 | if (gre.gr_R|gre.gr_s) |
| 8227 | goto cantdecaps; |
| 8228 | if (gre.gr_C) |
| 8229 | hlen += 4; |
| 8230 | if (gre.gr_K) |
| 8231 | hlen += 4; |
| 8232 | if (gre.gr_S) |
| 8233 | hlen += 4; |
| 8234 | |
| 8235 | nh = IPPROTO_IP; |
| 8236 | |
| 8237 | /* |
| 8238 | * If the routing options flag is set, validate that it is |
| 8239 | * there and bounce over it. |
| 8240 | */ |
| 8241 | #if 0 |
| 8242 | /* This is really heavy weight and lots of room for error, */ |
| 8243 | /* so for now, put it off and get the simple stuff right. */ |
| 8244 | if (gre.gr_R) { |
| 8245 | u_char off, len, *s; |
| 8246 | u_short af; |
| 8247 | int end; |
| 8248 | |
| 8249 | end = 0; |
| 8250 | s = fin->fin_dp; |
| 8251 | s += hlen; |
| 8252 | aplen = fin->fin_plen - hlen; |
| 8253 | while (aplen > 3) { |
| 8254 | af = (s[0] << 8) | s[1]; |
| 8255 | off = s[2]; |
| 8256 | len = s[3]; |
| 8257 | aplen -= 4; |
| 8258 | s += 4; |
| 8259 | if (af == 0 && len == 0) { |
| 8260 | end = 1; |
| 8261 | break; |
| 8262 | } |
| 8263 | if (aplen < len) |
| 8264 | break; |
| 8265 | s += len; |
| 8266 | aplen -= len; |
| 8267 | } |
| 8268 | if (end != 1) |
| 8269 | goto cantdecaps; |
| 8270 | hlen = s - (u_char *)fin->fin_dp; |
| 8271 | } |
| 8272 | #endif |
| 8273 | break; |
| 8274 | |
| 8275 | #ifdef IPPROTO_IPIP |
| 8276 | case IPPROTO_IPIP : /* 4 */ |
| 8277 | #endif |
| 8278 | nh = IPPROTO_IP; |
| 8279 | break; |
| 8280 | |
| 8281 | default : /* Includes ESP, AH is special for IPv4 */ |
| 8282 | goto cantdecaps; |
| 8283 | } |
| 8284 | |
| 8285 | switch (nh) |
| 8286 | { |
| 8287 | case IPPROTO_IP : |
| 8288 | case IPPROTO_IPV6 : |
| 8289 | break; |
| 8290 | default : |
| 8291 | goto cantdecaps; |
| 8292 | } |
| 8293 | |
| 8294 | bcopy((char *)fin, (char *)&fin2, sizeof(fin2)); |
| 8295 | fino = fin; |
| 8296 | fin = &fin2; |
| 8297 | elen = hlen; |
| 8298 | #if defined(MENTAT) && defined(_KERNEL) |
| 8299 | m->b_rptr += elen; |
| 8300 | #else |
| 8301 | m->m_data += elen; |
| 8302 | m->m_len -= elen; |
| 8303 | #endif |
| 8304 | fin->fin_plen -= elen; |
| 8305 | |
| 8306 | ip = (ip_t *)((char *)fin->fin_ip + elen); |
| 8307 | |
| 8308 | /* |
| 8309 | * Make sure we have at least enough data for the network layer |
| 8310 | * header. |
| 8311 | */ |
| 8312 | if (IP_V(ip) == 4) |
| 8313 | hlen = IP_HL(ip) << 2; |
| 8314 | #ifdef USE_INET6 |
| 8315 | else if (IP_V(ip) == 6) |
| 8316 | hlen = sizeof(ip6_t); |
| 8317 | #endif |
| 8318 | else |
| 8319 | goto cantdecaps2; |
| 8320 | |
| 8321 | if (fin->fin_plen < hlen) |
| 8322 | goto cantdecaps2; |
| 8323 | |
| 8324 | fin->fin_dp = (char *)ip + hlen; |
| 8325 | |
| 8326 | if (IP_V(ip) == 4) { |
| 8327 | /* |
| 8328 | * Perform IPv4 header checksum validation. |
| 8329 | */ |
| 8330 | if (ipf_cksum((u_short *)ip, hlen)) |
| 8331 | goto cantdecaps2; |
| 8332 | } |
| 8333 | |
| 8334 | if (ipf_makefrip(hlen, ip, fin) == -1) { |
| 8335 | cantdecaps2: |
| 8336 | if (m != NULL) { |
| 8337 | #if defined(MENTAT) && defined(_KERNEL) |
| 8338 | m->b_rptr -= elen; |
| 8339 | #else |
| 8340 | m->m_data -= elen; |
| 8341 | m->m_len += elen; |
| 8342 | #endif |
| 8343 | } |
| 8344 | cantdecaps: |
| 8345 | DT1(frb_decapfrip, fr_info_t *, fin); |
| 8346 | pass &= ~FR_CMDMASK; |
| 8347 | pass |= FR_BLOCK|FR_QUICK; |
| 8348 | fin->fin_reason = FRB_DECAPFRIP; |
| 8349 | return -1; |
| 8350 | } |
| 8351 | |
| 8352 | pass = ipf_scanlist(fin, pass); |
| 8353 | |
| 8354 | /* |
| 8355 | * Copy the packet filter "result" fields out of the fr_info_t struct |
| 8356 | * that is local to the decapsulation processing and back into the |
| 8357 | * one we were called with. |
| 8358 | */ |
| 8359 | fino->fin_flx = fin->fin_flx; |
| 8360 | fino->fin_rev = fin->fin_rev; |
| 8361 | fino->fin_icode = fin->fin_icode; |
| 8362 | fino->fin_rule = fin->fin_rule; |
| 8363 | (void) strncpy(fino->fin_group, fin->fin_group, FR_GROUPLEN); |
| 8364 | fino->fin_fr = fin->fin_fr; |
| 8365 | fino->fin_error = fin->fin_error; |
| 8366 | fino->fin_mp = fin->fin_mp; |
| 8367 | fino->fin_m = fin->fin_m; |
| 8368 | m = fin->fin_m; |
| 8369 | if (m != NULL) { |
| 8370 | #if defined(MENTAT) && defined(_KERNEL) |
| 8371 | m->b_rptr -= elen; |
| 8372 | #else |
| 8373 | m->m_data -= elen; |
| 8374 | m->m_len += elen; |
| 8375 | #endif |
| 8376 | } |
| 8377 | return pass; |
| 8378 | } |
| 8379 | |
| 8380 | |
| 8381 | /* ------------------------------------------------------------------------ */ |
| 8382 | /* Function: ipf_matcharray_load */ |
| 8383 | /* Returns: int - 0 = success, else error */ |
| 8384 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 8385 | /* data(I) - pointer to ioctl data */ |
| 8386 | /* objp(I) - ipfobj_t structure to load data into */ |
| 8387 | /* arrayptr(I) - pointer to location to store array pointer */ |
| 8388 | /* */ |
| 8389 | /* This function loads in a mathing array through the ipfobj_t struct that */ |
| 8390 | /* describes it. Sanity checking and array size limitations are enforced */ |
| 8391 | /* in this function to prevent userspace from trying to load in something */ |
| 8392 | /* that is insanely big. Once the size of the array is known, the memory */ |
| 8393 | /* required is malloc'd and returned through changing *arrayptr. The */ |
| 8394 | /* contents of the array are verified before returning. Only in the event */ |
| 8395 | /* of a successful call is the caller required to free up the malloc area. */ |
| 8396 | /* ------------------------------------------------------------------------ */ |
| 8397 | int |
| 8398 | ipf_matcharray_load(ipf_main_softc_t *softc, void *data, ipfobj_t *objp, |
| 8399 | int **arrayptr) |
| 8400 | { |
| 8401 | int arraysize, *array, error; |
| 8402 | |
| 8403 | *arrayptr = NULL; |
| 8404 | |
| 8405 | error = BCOPYIN(data, objp, sizeof(*objp)); |
| 8406 | if (error != 0) { |
| 8407 | IPFERROR(116); |
| 8408 | return EFAULT; |
| 8409 | } |
| 8410 | |
| 8411 | if (objp->ipfo_type != IPFOBJ_IPFEXPR) { |
| 8412 | IPFERROR(117); |
| 8413 | return EINVAL; |
| 8414 | } |
| 8415 | |
| 8416 | if (((objp->ipfo_size & 3) != 0) || (objp->ipfo_size == 0) || |
| 8417 | (objp->ipfo_size > 1024)) { |
| 8418 | IPFERROR(118); |
| 8419 | return EINVAL; |
| 8420 | } |
| 8421 | |
| 8422 | arraysize = objp->ipfo_size * sizeof(*array); |
| 8423 | KMALLOCS(array, int *, arraysize); |
| 8424 | if (array == NULL) { |
| 8425 | IPFERROR(119); |
| 8426 | return ENOMEM; |
| 8427 | } |
| 8428 | |
| 8429 | error = COPYIN(objp->ipfo_ptr, array, arraysize); |
| 8430 | if (error != 0) { |
| 8431 | KFREES(array, arraysize); |
| 8432 | IPFERROR(120); |
| 8433 | return EFAULT; |
| 8434 | } |
| 8435 | |
| 8436 | if (ipf_matcharray_verify(array, arraysize) != 0) { |
| 8437 | KFREES(array, arraysize); |
| 8438 | IPFERROR(121); |
| 8439 | return EINVAL; |
| 8440 | } |
| 8441 | |
| 8442 | *arrayptr = array; |
| 8443 | return 0; |
| 8444 | } |
| 8445 | |
| 8446 | |
| 8447 | /* ------------------------------------------------------------------------ */ |
| 8448 | /* Function: ipf_matcharray_verify */ |
| 8449 | /* Returns: Nil */ |
| 8450 | /* Parameters: array(I) - pointer to matching array */ |
| 8451 | /* arraysize(I) - number of elements in the array */ |
| 8452 | /* */ |
| 8453 | /* Verify the contents of a matching array by stepping through each element */ |
| 8454 | /* in it. The actual commands in the array are not verified for */ |
| 8455 | /* correctness, only that all of the sizes are correctly within limits. */ |
| 8456 | /* ------------------------------------------------------------------------ */ |
| 8457 | int |
| 8458 | ipf_matcharray_verify(int *array, int arraysize) |
| 8459 | { |
| 8460 | int i, nelem, maxidx; |
| 8461 | ipfexp_t *e; |
| 8462 | |
| 8463 | nelem = arraysize / sizeof(*array); |
| 8464 | |
| 8465 | /* |
| 8466 | * Currently, it makes no sense to have an array less than 6 |
| 8467 | * elements long - the initial size at the from, a single operation |
| 8468 | * (minimum 4 in length) and a trailer, for a total of 6. |
| 8469 | */ |
| 8470 | if ((array[0] < 6) || (arraysize < 24) || (arraysize > 4096)) { |
| 8471 | return -1; |
| 8472 | } |
| 8473 | |
| 8474 | /* |
| 8475 | * Verify the size of data pointed to by array with how long |
| 8476 | * the array claims to be itself. |
| 8477 | */ |
| 8478 | if (array[0] * sizeof(*array) != arraysize) { |
| 8479 | return -1; |
| 8480 | } |
| 8481 | |
| 8482 | maxidx = nelem - 1; |
| 8483 | /* |
| 8484 | * The last opcode in this array should be an IPF_EXP_END. |
| 8485 | */ |
| 8486 | if (array[maxidx] != IPF_EXP_END) { |
| 8487 | return -1; |
| 8488 | } |
| 8489 | |
| 8490 | for (i = 1; i < maxidx; ) { |
| 8491 | e = (ipfexp_t *)(array + i); |
| 8492 | |
| 8493 | /* |
| 8494 | * The length of the bits to check must be at least 1 |
| 8495 | * (or else there is nothing to comapre with!) and it |
| 8496 | * cannot exceed the length of the data present. |
| 8497 | */ |
| 8498 | if ((e->ipfe_size < 1 ) || |
| 8499 | (e->ipfe_size + i > maxidx)) { |
| 8500 | return -1; |
| 8501 | } |
| 8502 | i += e->ipfe_size; |
| 8503 | } |
| 8504 | return 0; |
| 8505 | } |
| 8506 | |
| 8507 | |
| 8508 | /* ------------------------------------------------------------------------ */ |
| 8509 | /* Function: ipf_fr_matcharray */ |
| 8510 | /* Returns: int - 0 = match failed, else positive match */ |
| 8511 | /* Parameters: fin(I) - pointer to packet information */ |
| 8512 | /* array(I) - pointer to matching array */ |
| 8513 | /* */ |
| 8514 | /* This function is used to apply a matching array against a packet and */ |
| 8515 | /* return an indication of whether or not the packet successfully matches */ |
| 8516 | /* all of the commands in it. */ |
| 8517 | /* ------------------------------------------------------------------------ */ |
| 8518 | static int |
| 8519 | ipf_fr_matcharray(fr_info_t *fin, int *array) |
| 8520 | { |
| 8521 | int i, n, *x, rv, p; |
| 8522 | ipfexp_t *e; |
| 8523 | |
| 8524 | rv = 0; |
| 8525 | n = array[0]; |
| 8526 | x = array + 1; |
| 8527 | |
| 8528 | for (; n > 0; x += 3 + x[3], rv = 0) { |
| 8529 | e = (ipfexp_t *)x; |
| 8530 | if (e->ipfe_cmd == IPF_EXP_END) |
| 8531 | break; |
| 8532 | n -= e->ipfe_size; |
| 8533 | |
| 8534 | /* |
| 8535 | * The upper 16 bits currently store the protocol value. |
| 8536 | * This is currently used with TCP and UDP port compares and |
| 8537 | * allows "tcp.port = 80" without requiring an explicit |
| 8538 | " "ip.pr = tcp" first. |
| 8539 | */ |
| 8540 | p = e->ipfe_cmd >> 16; |
| 8541 | if ((p != 0) && (p != fin->fin_p)) |
| 8542 | break; |
| 8543 | |
| 8544 | switch (e->ipfe_cmd) |
| 8545 | { |
| 8546 | case IPF_EXP_IP_PR : |
| 8547 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8548 | rv |= (fin->fin_p == e->ipfe_arg0[i]); |
| 8549 | } |
| 8550 | break; |
| 8551 | |
| 8552 | case IPF_EXP_IP_SRCADDR : |
| 8553 | if (fin->fin_v != 4) |
| 8554 | break; |
| 8555 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8556 | rv |= ((fin->fin_saddr & |
| 8557 | e->ipfe_arg0[i * 2 + 1]) == |
| 8558 | e->ipfe_arg0[i * 2]); |
| 8559 | } |
| 8560 | break; |
| 8561 | |
| 8562 | case IPF_EXP_IP_DSTADDR : |
| 8563 | if (fin->fin_v != 4) |
| 8564 | break; |
| 8565 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8566 | rv |= ((fin->fin_daddr & |
| 8567 | e->ipfe_arg0[i * 2 + 1]) == |
| 8568 | e->ipfe_arg0[i * 2]); |
| 8569 | } |
| 8570 | break; |
| 8571 | |
| 8572 | case IPF_EXP_IP_ADDR : |
| 8573 | if (fin->fin_v != 4) |
| 8574 | break; |
| 8575 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8576 | rv |= ((fin->fin_saddr & |
| 8577 | e->ipfe_arg0[i * 2 + 1]) == |
| 8578 | e->ipfe_arg0[i * 2]) || |
| 8579 | ((fin->fin_daddr & |
| 8580 | e->ipfe_arg0[i * 2 + 1]) == |
| 8581 | e->ipfe_arg0[i * 2]); |
| 8582 | } |
| 8583 | break; |
| 8584 | |
| 8585 | #ifdef USE_INET6 |
| 8586 | case IPF_EXP_IP6_SRCADDR : |
| 8587 | if (fin->fin_v != 6) |
| 8588 | break; |
| 8589 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8590 | rv |= IP6_MASKEQ(&fin->fin_src6, |
| 8591 | &e->ipfe_arg0[i * 8 + 4], |
| 8592 | &e->ipfe_arg0[i * 8]); |
| 8593 | } |
| 8594 | break; |
| 8595 | |
| 8596 | case IPF_EXP_IP6_DSTADDR : |
| 8597 | if (fin->fin_v != 6) |
| 8598 | break; |
| 8599 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8600 | rv |= IP6_MASKEQ(&fin->fin_dst6, |
| 8601 | &e->ipfe_arg0[i * 8 + 4], |
| 8602 | &e->ipfe_arg0[i * 8]); |
| 8603 | } |
| 8604 | break; |
| 8605 | |
| 8606 | case IPF_EXP_IP6_ADDR : |
| 8607 | if (fin->fin_v != 6) |
| 8608 | break; |
| 8609 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8610 | rv |= IP6_MASKEQ(&fin->fin_src6, |
| 8611 | &e->ipfe_arg0[i * 8 + 4], |
| 8612 | &e->ipfe_arg0[i * 8]) || |
| 8613 | IP6_MASKEQ(&fin->fin_dst6, |
| 8614 | &e->ipfe_arg0[i * 8 + 4], |
| 8615 | &e->ipfe_arg0[i * 8]); |
| 8616 | } |
| 8617 | break; |
| 8618 | #endif |
| 8619 | |
| 8620 | case IPF_EXP_UDP_PORT : |
| 8621 | case IPF_EXP_TCP_PORT : |
| 8622 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8623 | rv |= (fin->fin_sport == e->ipfe_arg0[i]) || |
| 8624 | (fin->fin_dport == e->ipfe_arg0[i]); |
| 8625 | } |
| 8626 | break; |
| 8627 | |
| 8628 | case IPF_EXP_UDP_SPORT : |
| 8629 | case IPF_EXP_TCP_SPORT : |
| 8630 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8631 | rv |= (fin->fin_sport == e->ipfe_arg0[i]); |
| 8632 | } |
| 8633 | break; |
| 8634 | |
| 8635 | case IPF_EXP_UDP_DPORT : |
| 8636 | case IPF_EXP_TCP_DPORT : |
| 8637 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8638 | rv |= (fin->fin_dport == e->ipfe_arg0[i]); |
| 8639 | } |
| 8640 | break; |
| 8641 | |
| 8642 | case IPF_EXP_TCP_FLAGS : |
| 8643 | for (i = 0; !rv && i < e->ipfe_narg; i++) { |
| 8644 | rv |= ((fin->fin_tcpf & |
| 8645 | e->ipfe_arg0[i * 2 + 1]) == |
| 8646 | e->ipfe_arg0[i * 2]); |
| 8647 | } |
| 8648 | break; |
| 8649 | } |
| 8650 | rv ^= e->ipfe_not; |
| 8651 | |
| 8652 | if (rv == 0) |
| 8653 | break; |
| 8654 | } |
| 8655 | |
| 8656 | return rv; |
| 8657 | } |
| 8658 | |
| 8659 | |
| 8660 | /* ------------------------------------------------------------------------ */ |
| 8661 | /* Function: ipf_queueflush */ |
| 8662 | /* Returns: int - number of entries flushed (0 = none) */ |
| 8663 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 8664 | /* deletefn(I) - function to call to delete entry */ |
| 8665 | /* ipfqs(I) - top of the list of ipf internal queues */ |
| 8666 | /* userqs(I) - top of the list of user defined timeouts */ |
| 8667 | /* */ |
| 8668 | /* This fucntion gets called when the state/NAT hash tables fill up and we */ |
| 8669 | /* need to try a bit harder to free up some space. The algorithm used here */ |
| 8670 | /* split into two parts but both halves have the same goal: to reduce the */ |
| 8671 | /* number of connections considered to be "active" to the low watermark. */ |
| 8672 | /* There are two steps in doing this: */ |
| 8673 | /* 1) Remove any TCP connections that are already considered to be "closed" */ |
| 8674 | /* but have not yet been removed from the state table. The two states */ |
| 8675 | /* TCPS_TIME_WAIT and TCPS_CLOSED are considered to be the perfect */ |
| 8676 | /* candidates for this style of removal. If freeing up entries in */ |
| 8677 | /* CLOSED or both CLOSED and TIME_WAIT brings us to the low watermark, */ |
| 8678 | /* we do not go on to step 2. */ |
| 8679 | /* */ |
| 8680 | /* 2) Look for the oldest entries on each timeout queue and free them if */ |
| 8681 | /* they are within the given window we are considering. Where the */ |
| 8682 | /* window starts and the steps taken to increase its size depend upon */ |
| 8683 | /* how long ipf has been running (ipf_ticks.) Anything modified in the */ |
| 8684 | /* last 30 seconds is not touched. */ |
| 8685 | /* touched */ |
| 8686 | /* die ipf_ticks 30*1.5 1800*1.5 | 43200*1.5 */ |
| 8687 | /* | | | | | | */ |
| 8688 | /* future <--+----------+--------+-----------+-----+-----+-----------> past */ |
| 8689 | /* now \_int=30s_/ \_int=1hr_/ \_int=12hr */ |
| 8690 | /* */ |
| 8691 | /* Points to note: */ |
| 8692 | /* - tqe_die is the time, in the future, when entries die. */ |
| 8693 | /* - tqe_die - ipf_ticks is how long left the connection has to live in ipf */ |
| 8694 | /* ticks. */ |
| 8695 | /* - tqe_touched is when the entry was last used by NAT/state */ |
| 8696 | /* - the closer tqe_touched is to ipf_ticks, the further tqe_die will be */ |
| 8697 | /* ipf_ticks any given timeout queue and vice versa. */ |
| 8698 | /* - both tqe_die and tqe_touched increase over time */ |
| 8699 | /* - timeout queues are sorted with the highest value of tqe_die at the */ |
| 8700 | /* bottom and therefore the smallest values of each are at the top */ |
| 8701 | /* - the pointer passed in as ipfqs should point to an array of timeout */ |
| 8702 | /* queues representing each of the TCP states */ |
| 8703 | /* */ |
| 8704 | /* We start by setting up a maximum range to scan for things to move of */ |
| 8705 | /* iend (newest) to istart (oldest) in chunks of "interval". If nothing is */ |
| 8706 | /* found in that range, "interval" is adjusted (so long as it isn't 30) and */ |
| 8707 | /* we start again with a new value for "iend" and "istart". This is */ |
| 8708 | /* continued until we either finish the scan of 30 second intervals or the */ |
| 8709 | /* low water mark is reached. */ |
| 8710 | /* ------------------------------------------------------------------------ */ |
| 8711 | int |
| 8712 | ipf_queueflush(ipf_main_softc_t *softc, ipftq_delete_fn_t deletefn, |
| 8713 | ipftq_t *ipfqs, ipftq_t *userqs, u_int *activep, int size, int low) |
| 8714 | { |
| 8715 | u_long interval, istart, iend; |
| 8716 | ipftq_t *ifq, *ifqnext; |
| 8717 | ipftqent_t *tqe, *tqn; |
| 8718 | int removed = 0; |
| 8719 | |
| 8720 | for (tqn = ipfqs[IPF_TCPS_CLOSED].ifq_head; ((tqe = tqn) != NULL); ) { |
| 8721 | tqn = tqe->tqe_next; |
| 8722 | if ((*deletefn)(softc, tqe->tqe_parent) == 0) |
| 8723 | removed++; |
| 8724 | } |
| 8725 | if ((*activep * 100 / size) > low) { |
| 8726 | for (tqn = ipfqs[IPF_TCPS_TIME_WAIT].ifq_head; |
| 8727 | ((tqe = tqn) != NULL); ) { |
| 8728 | tqn = tqe->tqe_next; |
| 8729 | if ((*deletefn)(softc, tqe->tqe_parent) == 0) |
| 8730 | removed++; |
| 8731 | } |
| 8732 | } |
| 8733 | |
| 8734 | if ((*activep * 100 / size) <= low) { |
| 8735 | return removed; |
| 8736 | } |
| 8737 | |
| 8738 | /* |
| 8739 | * NOTE: Use of "* 15 / 10" is required here because if "* 1.5" is |
| 8740 | * used then the operations are upgraded to floating point |
| 8741 | * and kernels don't like floating point... |
| 8742 | */ |
| 8743 | if (softc->ipf_ticks > IPF_TTLVAL(43200 * 15 / 10)) { |
| 8744 | istart = IPF_TTLVAL(86400 * 4); |
| 8745 | interval = IPF_TTLVAL(43200); |
| 8746 | } else if (softc->ipf_ticks > IPF_TTLVAL(1800 * 15 / 10)) { |
| 8747 | istart = IPF_TTLVAL(43200); |
| 8748 | interval = IPF_TTLVAL(1800); |
| 8749 | } else if (softc->ipf_ticks > IPF_TTLVAL(30 * 15 / 10)) { |
| 8750 | istart = IPF_TTLVAL(1800); |
| 8751 | interval = IPF_TTLVAL(30); |
| 8752 | } else { |
| 8753 | return 0; |
| 8754 | } |
| 8755 | if (istart > softc->ipf_ticks) { |
| 8756 | if (softc->ipf_ticks - interval < interval) |
| 8757 | istart = interval; |
| 8758 | else |
| 8759 | istart = (softc->ipf_ticks / interval) * interval; |
| 8760 | } |
| 8761 | |
| 8762 | iend = softc->ipf_ticks - interval; |
| 8763 | |
| 8764 | while ((*activep * 100 / size) > low) { |
| 8765 | u_long try; |
| 8766 | |
| 8767 | try = softc->ipf_ticks - istart; |
| 8768 | |
| 8769 | for (ifq = ipfqs; ifq != NULL; ifq = ifq->ifq_next) { |
| 8770 | for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { |
| 8771 | if (try < tqe->tqe_touched) |
| 8772 | break; |
| 8773 | tqn = tqe->tqe_next; |
| 8774 | if ((*deletefn)(softc, tqe->tqe_parent) == 0) |
| 8775 | removed++; |
| 8776 | } |
| 8777 | } |
| 8778 | |
| 8779 | for (ifq = userqs; ifq != NULL; ifq = ifqnext) { |
| 8780 | ifqnext = ifq->ifq_next; |
| 8781 | |
| 8782 | for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { |
| 8783 | if (try < tqe->tqe_touched) |
| 8784 | break; |
| 8785 | tqn = tqe->tqe_next; |
| 8786 | if ((*deletefn)(softc, tqe->tqe_parent) == 0) |
| 8787 | removed++; |
| 8788 | } |
| 8789 | } |
| 8790 | |
| 8791 | if (try >= iend) { |
| 8792 | if (interval == IPF_TTLVAL(43200)) { |
| 8793 | interval = IPF_TTLVAL(1800); |
| 8794 | } else if (interval == IPF_TTLVAL(1800)) { |
| 8795 | interval = IPF_TTLVAL(30); |
| 8796 | } else { |
| 8797 | break; |
| 8798 | } |
| 8799 | if (interval >= softc->ipf_ticks) |
| 8800 | break; |
| 8801 | |
| 8802 | iend = softc->ipf_ticks - interval; |
| 8803 | } |
| 8804 | istart -= interval; |
| 8805 | } |
| 8806 | |
| 8807 | return removed; |
| 8808 | } |
| 8809 | |
| 8810 | |
| 8811 | /* ------------------------------------------------------------------------ */ |
| 8812 | /* Function: ipf_deliverlocal */ |
| 8813 | /* Returns: int - 1 = local address, 0 = non-local address */ |
| 8814 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 8815 | /* ipversion(I) - IP protocol version (4 or 6) */ |
| 8816 | /* ifp(I) - network interface pointer */ |
| 8817 | /* ipaddr(I) - IPv4/6 destination address */ |
| 8818 | /* */ |
| 8819 | /* This fucntion is used to determine in the address "ipaddr" belongs to */ |
| 8820 | /* the network interface represented by ifp. */ |
| 8821 | /* ------------------------------------------------------------------------ */ |
| 8822 | int |
| 8823 | ipf_deliverlocal(ipf_main_softc_t *softc, int ipversion, void *ifp, |
| 8824 | i6addr_t *ipaddr) |
| 8825 | { |
| 8826 | i6addr_t addr; |
| 8827 | int islocal = 0; |
| 8828 | |
| 8829 | if (ipversion == 4) { |
| 8830 | if (ipf_ifpaddr(softc, 4, FRI_NORMAL, ifp, &addr, NULL) == 0) { |
| 8831 | if (addr.in4.s_addr == ipaddr->in4.s_addr) |
| 8832 | islocal = 1; |
| 8833 | } |
| 8834 | |
| 8835 | #ifdef USE_INET6 |
| 8836 | } else if (ipversion == 6) { |
| 8837 | if (ipf_ifpaddr(softc, 6, FRI_NORMAL, ifp, &addr, NULL) == 0) { |
| 8838 | if (IP6_EQ(&addr, ipaddr)) |
| 8839 | islocal = 1; |
| 8840 | } |
| 8841 | #endif |
| 8842 | } |
| 8843 | |
| 8844 | return islocal; |
| 8845 | } |
| 8846 | |
| 8847 | |
| 8848 | /* ------------------------------------------------------------------------ */ |
| 8849 | /* Function: ipf_settimeout */ |
| 8850 | /* Returns: int - 0 = success, -1 = failure */ |
| 8851 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 8852 | /* t(I) - pointer to tuneable array entry */ |
| 8853 | /* p(I) - pointer to values passed in to apply */ |
| 8854 | /* */ |
| 8855 | /* This function is called to set the timeout values for each distinct */ |
| 8856 | /* queue timeout that is available. When called, it calls into both the */ |
| 8857 | /* state and NAT code, telling them to update their timeout queues. */ |
| 8858 | /* ------------------------------------------------------------------------ */ |
| 8859 | static int |
| 8860 | ipf_settimeout(struct ipf_main_softc_s *softc, ipftuneable_t *t, |
| 8861 | ipftuneval_t *p) |
| 8862 | { |
| 8863 | |
| 8864 | /* |
| 8865 | * ipf_interror should be set by the functions called here, not |
| 8866 | * by this function - it's just a middle man. |
| 8867 | */ |
| 8868 | if (ipf_state_settimeout(softc, t, p) == -1) |
| 8869 | return -1; |
| 8870 | if (ipf_nat_settimeout(softc, t, p) == -1) |
| 8871 | return -1; |
| 8872 | return 0; |
| 8873 | } |
| 8874 | |
| 8875 | |
| 8876 | /* ------------------------------------------------------------------------ */ |
| 8877 | /* Function: ipf_apply_timeout */ |
| 8878 | /* Returns: int - 0 = success, -1 = failure */ |
| 8879 | /* Parameters: head(I) - pointer to tuneable array entry */ |
| 8880 | /* seconds(I) - pointer to values passed in to apply */ |
| 8881 | /* */ |
| 8882 | /* This function applies a timeout of "seconds" to the timeout queue that */ |
| 8883 | /* is pointed to by "head". All entries on this list have an expiration */ |
| 8884 | /* set to be the current tick value of ipf plus the ttl. Given that this */ |
| 8885 | /* function should only be called when the delta is non-zero, the task is */ |
| 8886 | /* to walk the entire list and apply the change. The sort order will not */ |
| 8887 | /* change. The only catch is that this is O(n) across the list, so if the */ |
| 8888 | /* queue has lots of entries (10s of thousands or 100s of thousands), it */ |
| 8889 | /* could take a relatively long time to work through them all. */ |
| 8890 | /* ------------------------------------------------------------------------ */ |
| 8891 | void |
| 8892 | ipf_apply_timeout(ipftq_t *head, u_int seconds) |
| 8893 | { |
| 8894 | u_int oldtimeout, newtimeout; |
| 8895 | ipftqent_t *tqe; |
| 8896 | int delta; |
| 8897 | |
| 8898 | MUTEX_ENTER(&head->ifq_lock); |
| 8899 | oldtimeout = head->ifq_ttl; |
| 8900 | newtimeout = IPF_TTLVAL(seconds); |
| 8901 | delta = oldtimeout - newtimeout; |
| 8902 | |
| 8903 | head->ifq_ttl = newtimeout; |
| 8904 | |
| 8905 | for (tqe = head->ifq_head; tqe != NULL; tqe = tqe->tqe_next) { |
| 8906 | tqe->tqe_die += delta; |
| 8907 | } |
| 8908 | MUTEX_EXIT(&head->ifq_lock); |
| 8909 | } |
| 8910 | |
| 8911 | |
| 8912 | /* ------------------------------------------------------------------------ */ |
| 8913 | /* Function: ipf_settimeout_tcp */ |
| 8914 | /* Returns: int - 0 = successfully applied, -1 = failed */ |
| 8915 | /* Parameters: t(I) - pointer to tuneable to change */ |
| 8916 | /* p(I) - pointer to new timeout information */ |
| 8917 | /* tab(I) - pointer to table of TCP queues */ |
| 8918 | /* */ |
| 8919 | /* This function applies the new timeout (p) to the TCP tunable (t) and */ |
| 8920 | /* updates all of the entries on the relevant timeout queue by calling */ |
| 8921 | /* ipf_apply_timeout(). */ |
| 8922 | /* ------------------------------------------------------------------------ */ |
| 8923 | int |
| 8924 | ipf_settimeout_tcp(ipftuneable_t *t, ipftuneval_t *p, ipftq_t *tab) |
| 8925 | { |
| 8926 | if (!strcmp(t->ipft_name, "tcp_idle_timeout" ) || |
| 8927 | !strcmp(t->ipft_name, "tcp_established" )) { |
| 8928 | ipf_apply_timeout(&tab[IPF_TCPS_ESTABLISHED], p->ipftu_int); |
| 8929 | } else if (!strcmp(t->ipft_name, "tcp_close_wait" )) { |
| 8930 | ipf_apply_timeout(&tab[IPF_TCPS_CLOSE_WAIT], p->ipftu_int); |
| 8931 | } else if (!strcmp(t->ipft_name, "tcp_last_ack" )) { |
| 8932 | ipf_apply_timeout(&tab[IPF_TCPS_LAST_ACK], p->ipftu_int); |
| 8933 | } else if (!strcmp(t->ipft_name, "tcp_timeout" )) { |
| 8934 | ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); |
| 8935 | ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); |
| 8936 | ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); |
| 8937 | } else if (!strcmp(t->ipft_name, "tcp_listen" )) { |
| 8938 | ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); |
| 8939 | } else if (!strcmp(t->ipft_name, "tcp_half_established" )) { |
| 8940 | ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); |
| 8941 | } else if (!strcmp(t->ipft_name, "tcp_closing" )) { |
| 8942 | ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); |
| 8943 | } else if (!strcmp(t->ipft_name, "tcp_syn_received" )) { |
| 8944 | ipf_apply_timeout(&tab[IPF_TCPS_SYN_RECEIVED], p->ipftu_int); |
| 8945 | } else if (!strcmp(t->ipft_name, "tcp_syn_sent" )) { |
| 8946 | ipf_apply_timeout(&tab[IPF_TCPS_SYN_SENT], p->ipftu_int); |
| 8947 | } else if (!strcmp(t->ipft_name, "tcp_closed" )) { |
| 8948 | ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); |
| 8949 | } else if (!strcmp(t->ipft_name, "tcp_half_closed" )) { |
| 8950 | ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); |
| 8951 | } else if (!strcmp(t->ipft_name, "tcp_time_wait" )) { |
| 8952 | ipf_apply_timeout(&tab[IPF_TCPS_TIME_WAIT], p->ipftu_int); |
| 8953 | } else { |
| 8954 | /* |
| 8955 | * ipf_interror isn't set here because it should be set |
| 8956 | * by whatever called this function. |
| 8957 | */ |
| 8958 | return -1; |
| 8959 | } |
| 8960 | return 0; |
| 8961 | } |
| 8962 | |
| 8963 | |
| 8964 | /* ------------------------------------------------------------------------ */ |
| 8965 | /* Function: ipf_main_soft_create */ |
| 8966 | /* Returns: NULL = failure, else success */ |
| 8967 | /* Parameters: arg(I) - pointer to soft context structure if already allocd */ |
| 8968 | /* */ |
| 8969 | /* Create the foundation soft context structure. In circumstances where it */ |
| 8970 | /* is not required to dynamically allocate the context, a pointer can be */ |
| 8971 | /* passed in (rather than NULL) to a structure to be initialised. */ |
| 8972 | /* The main thing of interest is that a number of locks are initialised */ |
| 8973 | /* here instead of in the where might be expected - in the relevant create */ |
| 8974 | /* function elsewhere. This is done because the current locking design has */ |
| 8975 | /* some areas where these locks are used outside of their module. */ |
| 8976 | /* Possibly the most important exercise that is done here is setting of all */ |
| 8977 | /* the timeout values, allowing them to be changed before init(). */ |
| 8978 | /* ------------------------------------------------------------------------ */ |
| 8979 | void * |
| 8980 | ipf_main_soft_create(void *arg) |
| 8981 | { |
| 8982 | ipf_main_softc_t *softc; |
| 8983 | |
| 8984 | if (arg == NULL) { |
| 8985 | KMALLOC(softc, ipf_main_softc_t *); |
| 8986 | if (softc == NULL) |
| 8987 | return NULL; |
| 8988 | } else { |
| 8989 | softc = arg; |
| 8990 | } |
| 8991 | |
| 8992 | bzero((char *)softc, sizeof(*softc)); |
| 8993 | |
| 8994 | /* |
| 8995 | * This serves as a flag as to whether or not the softc should be |
| 8996 | * free'd when _destroy is called. |
| 8997 | */ |
| 8998 | softc->ipf_dynamic_softc = (arg == NULL) ? 1 : 0; |
| 8999 | |
| 9000 | softc->ipf_tuners = ipf_tune_array_copy(softc, |
| 9001 | sizeof(ipf_main_tuneables), |
| 9002 | ipf_main_tuneables); |
| 9003 | if (softc->ipf_tuners == NULL) { |
| 9004 | ipf_main_soft_destroy(softc); |
| 9005 | return NULL; |
| 9006 | } |
| 9007 | |
| 9008 | MUTEX_INIT(&softc->ipf_rw, "ipf rw mutex" ); |
| 9009 | MUTEX_INIT(&softc->ipf_timeoutlock, "ipf timeout lock" ); |
| 9010 | RWLOCK_INIT(&softc->ipf_global, "ipf filter load/unload mutex" ); |
| 9011 | RWLOCK_INIT(&softc->ipf_mutex, "ipf filter rwlock" ); |
| 9012 | RWLOCK_INIT(&softc->ipf_tokens, "ipf token rwlock" ); |
| 9013 | RWLOCK_INIT(&softc->ipf_state, "ipf state rwlock" ); |
| 9014 | RWLOCK_INIT(&softc->ipf_nat, "ipf IP NAT rwlock" ); |
| 9015 | RWLOCK_INIT(&softc->ipf_poolrw, "ipf pool rwlock" ); |
| 9016 | RWLOCK_INIT(&softc->ipf_frag, "ipf frag rwlock" ); |
| 9017 | |
| 9018 | softc->ipf_token_head = NULL; |
| 9019 | softc->ipf_token_tail = &softc->ipf_token_head; |
| 9020 | |
| 9021 | softc->ipf_tcpidletimeout = FIVE_DAYS; |
| 9022 | softc->ipf_tcpclosewait = IPF_TTLVAL(2 * TCP_MSL); |
| 9023 | softc->ipf_tcplastack = IPF_TTLVAL(30); |
| 9024 | softc->ipf_tcptimewait = IPF_TTLVAL(2 * TCP_MSL); |
| 9025 | softc->ipf_tcptimeout = IPF_TTLVAL(2 * TCP_MSL); |
| 9026 | softc->ipf_tcpsynsent = IPF_TTLVAL(2 * TCP_MSL); |
| 9027 | softc->ipf_tcpsynrecv = IPF_TTLVAL(2 * TCP_MSL); |
| 9028 | softc->ipf_tcpclosed = IPF_TTLVAL(30); |
| 9029 | softc->ipf_tcphalfclosed = IPF_TTLVAL(2 * 3600); |
| 9030 | softc->ipf_udptimeout = IPF_TTLVAL(120); |
| 9031 | softc->ipf_udpacktimeout = IPF_TTLVAL(12); |
| 9032 | softc->ipf_icmptimeout = IPF_TTLVAL(60); |
| 9033 | softc->ipf_icmpacktimeout = IPF_TTLVAL(6); |
| 9034 | softc->ipf_iptimeout = IPF_TTLVAL(60); |
| 9035 | |
| 9036 | #if defined(IPFILTER_DEFAULT_BLOCK) |
| 9037 | softc->ipf_pass = FR_BLOCK|FR_NOMATCH; |
| 9038 | #else |
| 9039 | softc->ipf_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH; |
| 9040 | #endif |
| 9041 | softc->ipf_minttl = 4; |
| 9042 | softc->ipf_icmpminfragmtu = 68; |
| 9043 | softc->ipf_flags = IPF_LOGGING; |
| 9044 | |
| 9045 | return softc; |
| 9046 | } |
| 9047 | |
| 9048 | /* ------------------------------------------------------------------------ */ |
| 9049 | /* Function: ipf_main_soft_init */ |
| 9050 | /* Returns: 0 = success, -1 = failure */ |
| 9051 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 9052 | /* */ |
| 9053 | /* A null-op function that exists as a placeholder so that the flow in */ |
| 9054 | /* other functions is obvious. */ |
| 9055 | /* ------------------------------------------------------------------------ */ |
| 9056 | /*ARGSUSED*/ |
| 9057 | int |
| 9058 | ipf_main_soft_init(ipf_main_softc_t *softc) |
| 9059 | { |
| 9060 | return 0; |
| 9061 | } |
| 9062 | |
| 9063 | |
| 9064 | /* ------------------------------------------------------------------------ */ |
| 9065 | /* Function: ipf_main_soft_destroy */ |
| 9066 | /* Returns: void */ |
| 9067 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 9068 | /* */ |
| 9069 | /* Undo everything that we did in ipf_main_soft_create. */ |
| 9070 | /* */ |
| 9071 | /* The most important check that needs to be made here is whether or not */ |
| 9072 | /* the structure was allocated by ipf_main_soft_create() by checking what */ |
| 9073 | /* value is stored in ipf_dynamic_main. */ |
| 9074 | /* ------------------------------------------------------------------------ */ |
| 9075 | /*ARGSUSED*/ |
| 9076 | void |
| 9077 | ipf_main_soft_destroy(ipf_main_softc_t *softc) |
| 9078 | { |
| 9079 | |
| 9080 | RW_DESTROY(&softc->ipf_frag); |
| 9081 | RW_DESTROY(&softc->ipf_poolrw); |
| 9082 | RW_DESTROY(&softc->ipf_nat); |
| 9083 | RW_DESTROY(&softc->ipf_state); |
| 9084 | RW_DESTROY(&softc->ipf_tokens); |
| 9085 | RW_DESTROY(&softc->ipf_mutex); |
| 9086 | RW_DESTROY(&softc->ipf_global); |
| 9087 | MUTEX_DESTROY(&softc->ipf_timeoutlock); |
| 9088 | MUTEX_DESTROY(&softc->ipf_rw); |
| 9089 | |
| 9090 | if (softc->ipf_tuners != NULL) { |
| 9091 | KFREES(softc->ipf_tuners, sizeof(ipf_main_tuneables)); |
| 9092 | } |
| 9093 | if (softc->ipf_dynamic_softc == 1) { |
| 9094 | KFREE(softc); |
| 9095 | } |
| 9096 | } |
| 9097 | |
| 9098 | |
| 9099 | /* ------------------------------------------------------------------------ */ |
| 9100 | /* Function: ipf_main_soft_fini */ |
| 9101 | /* Returns: 0 = success, -1 = failure */ |
| 9102 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 9103 | /* */ |
| 9104 | /* Clean out the rules which have been added since _init was last called, */ |
| 9105 | /* the only dynamic part of the mainline. */ |
| 9106 | /* ------------------------------------------------------------------------ */ |
| 9107 | int |
| 9108 | ipf_main_soft_fini(ipf_main_softc_t *softc) |
| 9109 | { |
| 9110 | (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE|FR_INACTIVE); |
| 9111 | (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE); |
| 9112 | (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE|FR_INACTIVE); |
| 9113 | (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE); |
| 9114 | |
| 9115 | return 0; |
| 9116 | } |
| 9117 | |
| 9118 | |
| 9119 | /* ------------------------------------------------------------------------ */ |
| 9120 | /* Function: ipf_main_load */ |
| 9121 | /* Returns: 0 = success, -1 = failure */ |
| 9122 | /* Parameters: none */ |
| 9123 | /* */ |
| 9124 | /* Handle global initialisation that needs to be done for the base part of */ |
| 9125 | /* IPFilter. At present this just amounts to initialising some ICMP lookup */ |
| 9126 | /* arrays that get used by the state/NAT code. */ |
| 9127 | /* ------------------------------------------------------------------------ */ |
| 9128 | int |
| 9129 | ipf_main_load(void) |
| 9130 | { |
| 9131 | int i; |
| 9132 | |
| 9133 | /* fill icmp reply type table */ |
| 9134 | for (i = 0; i <= ICMP_MAXTYPE; i++) |
| 9135 | icmpreplytype4[i] = -1; |
| 9136 | icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; |
| 9137 | icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; |
| 9138 | icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; |
| 9139 | icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; |
| 9140 | |
| 9141 | #ifdef USE_INET6 |
| 9142 | /* fill icmp reply type table */ |
| 9143 | for (i = 0; i <= ICMP6_MAXTYPE; i++) |
| 9144 | icmpreplytype6[i] = -1; |
| 9145 | icmpreplytype6[ICMP6_ECHO_REQUEST] = ICMP6_ECHO_REPLY; |
| 9146 | icmpreplytype6[ICMP6_MEMBERSHIP_QUERY] = ICMP6_MEMBERSHIP_REPORT; |
| 9147 | icmpreplytype6[ICMP6_NI_QUERY] = ICMP6_NI_REPLY; |
| 9148 | icmpreplytype6[ND_ROUTER_SOLICIT] = ND_ROUTER_ADVERT; |
| 9149 | icmpreplytype6[ND_NEIGHBOR_SOLICIT] = ND_NEIGHBOR_ADVERT; |
| 9150 | #endif |
| 9151 | |
| 9152 | return 0; |
| 9153 | } |
| 9154 | |
| 9155 | |
| 9156 | /* ------------------------------------------------------------------------ */ |
| 9157 | /* Function: ipf_main_unload */ |
| 9158 | /* Returns: 0 = success, -1 = failure */ |
| 9159 | /* Parameters: none */ |
| 9160 | /* */ |
| 9161 | /* A null-op function that exists as a placeholder so that the flow in */ |
| 9162 | /* other functions is obvious. */ |
| 9163 | /* ------------------------------------------------------------------------ */ |
| 9164 | int |
| 9165 | ipf_main_unload(void) |
| 9166 | { |
| 9167 | return 0; |
| 9168 | } |
| 9169 | |
| 9170 | |
| 9171 | /* ------------------------------------------------------------------------ */ |
| 9172 | /* Function: ipf_load_all */ |
| 9173 | /* Returns: 0 = success, -1 = failure */ |
| 9174 | /* Parameters: none */ |
| 9175 | /* */ |
| 9176 | /* Work through all of the subsystems inside IPFilter and call the load */ |
| 9177 | /* function for each in an order that won't lead to a crash :) */ |
| 9178 | /* ------------------------------------------------------------------------ */ |
| 9179 | int |
| 9180 | ipf_load_all(void) |
| 9181 | { |
| 9182 | if (ipf_main_load() == -1) |
| 9183 | return -1; |
| 9184 | |
| 9185 | if (ipf_state_main_load() == -1) |
| 9186 | return -1; |
| 9187 | |
| 9188 | if (ipf_nat_main_load() == -1) |
| 9189 | return -1; |
| 9190 | |
| 9191 | if (ipf_frag_main_load() == -1) |
| 9192 | return -1; |
| 9193 | |
| 9194 | if (ipf_auth_main_load() == -1) |
| 9195 | return -1; |
| 9196 | |
| 9197 | if (ipf_proxy_main_load() == -1) |
| 9198 | return -1; |
| 9199 | |
| 9200 | return 0; |
| 9201 | } |
| 9202 | |
| 9203 | |
| 9204 | /* ------------------------------------------------------------------------ */ |
| 9205 | /* Function: ipf_unload_all */ |
| 9206 | /* Returns: 0 = success, -1 = failure */ |
| 9207 | /* Parameters: none */ |
| 9208 | /* */ |
| 9209 | /* Work through all of the subsystems inside IPFilter and call the unload */ |
| 9210 | /* function for each in an order that won't lead to a crash :) */ |
| 9211 | /* ------------------------------------------------------------------------ */ |
| 9212 | int |
| 9213 | ipf_unload_all(void) |
| 9214 | { |
| 9215 | if (ipf_proxy_main_unload() == -1) |
| 9216 | return -1; |
| 9217 | |
| 9218 | if (ipf_auth_main_unload() == -1) |
| 9219 | return -1; |
| 9220 | |
| 9221 | if (ipf_frag_main_unload() == -1) |
| 9222 | return -1; |
| 9223 | |
| 9224 | if (ipf_nat_main_unload() == -1) |
| 9225 | return -1; |
| 9226 | |
| 9227 | if (ipf_state_main_unload() == -1) |
| 9228 | return -1; |
| 9229 | |
| 9230 | if (ipf_main_unload() == -1) |
| 9231 | return -1; |
| 9232 | |
| 9233 | return 0; |
| 9234 | } |
| 9235 | |
| 9236 | |
| 9237 | /* ------------------------------------------------------------------------ */ |
| 9238 | /* Function: ipf_create_all */ |
| 9239 | /* Returns: NULL = failure, else success */ |
| 9240 | /* Parameters: arg(I) - pointer to soft context main structure */ |
| 9241 | /* */ |
| 9242 | /* Work through all of the subsystems inside IPFilter and call the create */ |
| 9243 | /* function for each in an order that won't lead to a crash :) */ |
| 9244 | /* ------------------------------------------------------------------------ */ |
| 9245 | ipf_main_softc_t * |
| 9246 | ipf_create_all(void *arg) |
| 9247 | { |
| 9248 | ipf_main_softc_t *softc; |
| 9249 | |
| 9250 | softc = ipf_main_soft_create(arg); |
| 9251 | if (softc == NULL) |
| 9252 | return NULL; |
| 9253 | |
| 9254 | #ifdef IPFILTER_LOG |
| 9255 | softc->ipf_log_soft = ipf_log_soft_create(softc); |
| 9256 | if (softc->ipf_log_soft == NULL) { |
| 9257 | ipf_destroy_all(softc); |
| 9258 | return NULL; |
| 9259 | } |
| 9260 | #endif |
| 9261 | |
| 9262 | softc->ipf_lookup_soft = ipf_lookup_soft_create(softc); |
| 9263 | if (softc->ipf_lookup_soft == NULL) { |
| 9264 | ipf_destroy_all(softc); |
| 9265 | return NULL; |
| 9266 | } |
| 9267 | |
| 9268 | softc->ipf_sync_soft = ipf_sync_soft_create(softc); |
| 9269 | if (softc->ipf_sync_soft == NULL) { |
| 9270 | ipf_destroy_all(softc); |
| 9271 | return NULL; |
| 9272 | } |
| 9273 | |
| 9274 | softc->ipf_state_soft = ipf_state_soft_create(softc); |
| 9275 | if (softc->ipf_state_soft == NULL) { |
| 9276 | ipf_destroy_all(softc); |
| 9277 | return NULL; |
| 9278 | } |
| 9279 | |
| 9280 | softc->ipf_nat_soft = ipf_nat_soft_create(softc); |
| 9281 | if (softc->ipf_nat_soft == NULL) { |
| 9282 | ipf_destroy_all(softc); |
| 9283 | return NULL; |
| 9284 | } |
| 9285 | |
| 9286 | softc->ipf_frag_soft = ipf_frag_soft_create(softc); |
| 9287 | if (softc->ipf_frag_soft == NULL) { |
| 9288 | ipf_destroy_all(softc); |
| 9289 | return NULL; |
| 9290 | } |
| 9291 | |
| 9292 | softc->ipf_auth_soft = ipf_auth_soft_create(softc); |
| 9293 | if (softc->ipf_auth_soft == NULL) { |
| 9294 | ipf_destroy_all(softc); |
| 9295 | return NULL; |
| 9296 | } |
| 9297 | |
| 9298 | softc->ipf_proxy_soft = ipf_proxy_soft_create(softc); |
| 9299 | if (softc->ipf_proxy_soft == NULL) { |
| 9300 | ipf_destroy_all(softc); |
| 9301 | return NULL; |
| 9302 | } |
| 9303 | |
| 9304 | return softc; |
| 9305 | } |
| 9306 | |
| 9307 | |
| 9308 | /* ------------------------------------------------------------------------ */ |
| 9309 | /* Function: ipf_destroy_all */ |
| 9310 | /* Returns: void */ |
| 9311 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 9312 | /* */ |
| 9313 | /* Work through all of the subsystems inside IPFilter and call the destroy */ |
| 9314 | /* function for each in an order that won't lead to a crash :) */ |
| 9315 | /* */ |
| 9316 | /* Every one of these functions is expected to succeed, so there is no */ |
| 9317 | /* checking of return values. */ |
| 9318 | /* ------------------------------------------------------------------------ */ |
| 9319 | void |
| 9320 | ipf_destroy_all(ipf_main_softc_t *softc) |
| 9321 | { |
| 9322 | |
| 9323 | if (softc->ipf_state_soft != NULL) { |
| 9324 | ipf_state_soft_destroy(softc, softc->ipf_state_soft); |
| 9325 | softc->ipf_state_soft = NULL; |
| 9326 | } |
| 9327 | |
| 9328 | if (softc->ipf_nat_soft != NULL) { |
| 9329 | ipf_nat_soft_destroy(softc, softc->ipf_nat_soft); |
| 9330 | softc->ipf_nat_soft = NULL; |
| 9331 | } |
| 9332 | |
| 9333 | if (softc->ipf_frag_soft != NULL) { |
| 9334 | ipf_frag_soft_destroy(softc, softc->ipf_frag_soft); |
| 9335 | softc->ipf_frag_soft = NULL; |
| 9336 | } |
| 9337 | |
| 9338 | if (softc->ipf_auth_soft != NULL) { |
| 9339 | ipf_auth_soft_destroy(softc, softc->ipf_auth_soft); |
| 9340 | softc->ipf_auth_soft = NULL; |
| 9341 | } |
| 9342 | |
| 9343 | if (softc->ipf_proxy_soft != NULL) { |
| 9344 | ipf_proxy_soft_destroy(softc, softc->ipf_proxy_soft); |
| 9345 | softc->ipf_proxy_soft = NULL; |
| 9346 | } |
| 9347 | |
| 9348 | if (softc->ipf_sync_soft != NULL) { |
| 9349 | ipf_sync_soft_destroy(softc, softc->ipf_sync_soft); |
| 9350 | softc->ipf_sync_soft = NULL; |
| 9351 | } |
| 9352 | |
| 9353 | if (softc->ipf_lookup_soft != NULL) { |
| 9354 | ipf_lookup_soft_destroy(softc, softc->ipf_lookup_soft); |
| 9355 | softc->ipf_lookup_soft = NULL; |
| 9356 | } |
| 9357 | |
| 9358 | #ifdef IPFILTER_LOG |
| 9359 | if (softc->ipf_log_soft != NULL) { |
| 9360 | ipf_log_soft_destroy(softc, softc->ipf_log_soft); |
| 9361 | softc->ipf_log_soft = NULL; |
| 9362 | } |
| 9363 | #endif |
| 9364 | |
| 9365 | ipf_main_soft_destroy(softc); |
| 9366 | } |
| 9367 | |
| 9368 | |
| 9369 | /* ------------------------------------------------------------------------ */ |
| 9370 | /* Function: ipf_init_all */ |
| 9371 | /* Returns: 0 = success, -1 = failure */ |
| 9372 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 9373 | /* */ |
| 9374 | /* Work through all of the subsystems inside IPFilter and call the init */ |
| 9375 | /* function for each in an order that won't lead to a crash :) */ |
| 9376 | /* ------------------------------------------------------------------------ */ |
| 9377 | int |
| 9378 | ipf_init_all(ipf_main_softc_t *softc) |
| 9379 | { |
| 9380 | |
| 9381 | if (ipf_main_soft_init(softc) == -1) |
| 9382 | return -1; |
| 9383 | |
| 9384 | #ifdef IPFILTER_LOG |
| 9385 | if (ipf_log_soft_init(softc, softc->ipf_log_soft) == -1) |
| 9386 | return -1; |
| 9387 | #endif |
| 9388 | |
| 9389 | if (ipf_lookup_soft_init(softc, softc->ipf_lookup_soft) == -1) |
| 9390 | return -1; |
| 9391 | |
| 9392 | if (ipf_sync_soft_init(softc, softc->ipf_sync_soft) == -1) |
| 9393 | return -1; |
| 9394 | |
| 9395 | if (ipf_state_soft_init(softc, softc->ipf_state_soft) == -1) |
| 9396 | return -1; |
| 9397 | |
| 9398 | if (ipf_nat_soft_init(softc, softc->ipf_nat_soft) == -1) |
| 9399 | return -1; |
| 9400 | |
| 9401 | if (ipf_frag_soft_init(softc, softc->ipf_frag_soft) == -1) |
| 9402 | return -1; |
| 9403 | |
| 9404 | if (ipf_auth_soft_init(softc, softc->ipf_auth_soft) == -1) |
| 9405 | return -1; |
| 9406 | |
| 9407 | if (ipf_proxy_soft_init(softc, softc->ipf_proxy_soft) == -1) |
| 9408 | return -1; |
| 9409 | |
| 9410 | return 0; |
| 9411 | } |
| 9412 | |
| 9413 | |
| 9414 | /* ------------------------------------------------------------------------ */ |
| 9415 | /* Function: ipf_fini_all */ |
| 9416 | /* Returns: 0 = success, -1 = failure */ |
| 9417 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 9418 | /* */ |
| 9419 | /* Work through all of the subsystems inside IPFilter and call the fini */ |
| 9420 | /* function for each in an order that won't lead to a crash :) */ |
| 9421 | /* ------------------------------------------------------------------------ */ |
| 9422 | int |
| 9423 | ipf_fini_all(ipf_main_softc_t *softc) |
| 9424 | { |
| 9425 | |
| 9426 | ipf_token_flush(softc); |
| 9427 | |
| 9428 | if (ipf_proxy_soft_fini(softc, softc->ipf_proxy_soft) == -1) |
| 9429 | return -1; |
| 9430 | |
| 9431 | if (ipf_auth_soft_fini(softc, softc->ipf_auth_soft) == -1) |
| 9432 | return -1; |
| 9433 | |
| 9434 | if (ipf_frag_soft_fini(softc, softc->ipf_frag_soft) == -1) |
| 9435 | return -1; |
| 9436 | |
| 9437 | if (ipf_nat_soft_fini(softc, softc->ipf_nat_soft) == -1) |
| 9438 | return -1; |
| 9439 | |
| 9440 | if (ipf_state_soft_fini(softc, softc->ipf_state_soft) == -1) |
| 9441 | return -1; |
| 9442 | |
| 9443 | if (ipf_sync_soft_fini(softc, softc->ipf_sync_soft) == -1) |
| 9444 | return -1; |
| 9445 | |
| 9446 | if (ipf_lookup_soft_fini(softc, softc->ipf_lookup_soft) == -1) |
| 9447 | return -1; |
| 9448 | |
| 9449 | #ifdef IPFILTER_LOG |
| 9450 | if (ipf_log_soft_fini(softc, softc->ipf_log_soft) == -1) |
| 9451 | return -1; |
| 9452 | #endif |
| 9453 | |
| 9454 | if (ipf_main_soft_fini(softc) == -1) |
| 9455 | return -1; |
| 9456 | |
| 9457 | return 0; |
| 9458 | } |
| 9459 | |
| 9460 | |
| 9461 | /* ------------------------------------------------------------------------ */ |
| 9462 | /* Function: ipf_rule_expire */ |
| 9463 | /* Returns: Nil */ |
| 9464 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 9465 | /* */ |
| 9466 | /* At present this function exists just to support temporary addition of */ |
| 9467 | /* firewall rules. Both inactive and active lists are scanned for items to */ |
| 9468 | /* purge, as by rights, the expiration is computed as soon as the rule is */ |
| 9469 | /* loaded in. */ |
| 9470 | /* ------------------------------------------------------------------------ */ |
| 9471 | void |
| 9472 | ipf_rule_expire(ipf_main_softc_t *softc) |
| 9473 | { |
| 9474 | frentry_t *fr; |
| 9475 | |
| 9476 | if ((softc->ipf_rule_explist[0] == NULL) && |
| 9477 | (softc->ipf_rule_explist[1] == NULL)) |
| 9478 | return; |
| 9479 | |
| 9480 | WRITE_ENTER(&softc->ipf_mutex); |
| 9481 | |
| 9482 | while ((fr = softc->ipf_rule_explist[0]) != NULL) { |
| 9483 | /* |
| 9484 | * Because the list is kept sorted on insertion, the fist |
| 9485 | * one that dies in the future means no more work to do. |
| 9486 | */ |
| 9487 | if (fr->fr_die > softc->ipf_ticks) |
| 9488 | break; |
| 9489 | ipf_rule_delete(softc, fr, IPL_LOGIPF, 0); |
| 9490 | } |
| 9491 | |
| 9492 | while ((fr = softc->ipf_rule_explist[1]) != NULL) { |
| 9493 | /* |
| 9494 | * Because the list is kept sorted on insertion, the fist |
| 9495 | * one that dies in the future means no more work to do. |
| 9496 | */ |
| 9497 | if (fr->fr_die > softc->ipf_ticks) |
| 9498 | break; |
| 9499 | ipf_rule_delete(softc, fr, IPL_LOGIPF, 1); |
| 9500 | } |
| 9501 | |
| 9502 | RWLOCK_EXIT(&softc->ipf_mutex); |
| 9503 | } |
| 9504 | |
| 9505 | |
| 9506 | static int ipf_ht_node_cmp(const struct host_node_s *, const struct host_node_s *); |
| 9507 | static void ipf_ht_node_make_key(host_track_t *, host_node_t *, int, |
| 9508 | i6addr_t *); |
| 9509 | |
| 9510 | RBI_CODE(ipf_rb, host_node_t, hn_entry, ipf_ht_node_cmp) |
| 9511 | |
| 9512 | |
| 9513 | /* ------------------------------------------------------------------------ */ |
| 9514 | /* Function: ipf_ht_node_cmp */ |
| 9515 | /* Returns: int - 0 == nodes are the same, .. */ |
| 9516 | /* Parameters: k1(I) - pointer to first key to compare */ |
| 9517 | /* k2(I) - pointer to second key to compare */ |
| 9518 | /* */ |
| 9519 | /* The "key" for the node is a combination of two fields: the address */ |
| 9520 | /* family and the address itself. */ |
| 9521 | /* */ |
| 9522 | /* Because we're not actually interpreting the address data, it isn't */ |
| 9523 | /* necessary to convert them to/from network/host byte order. The mask is */ |
| 9524 | /* just used to remove bits that aren't significant - it doesn't matter */ |
| 9525 | /* where they are, as long as they're always in the same place. */ |
| 9526 | /* */ |
| 9527 | /* As with IP6_EQ, comparing IPv6 addresses starts at the bottom because */ |
| 9528 | /* this is where individual ones will differ the most - but not true for */ |
| 9529 | /* for /48's, etc. */ |
| 9530 | /* ------------------------------------------------------------------------ */ |
| 9531 | static int |
| 9532 | ipf_ht_node_cmp(const struct host_node_s *k1, const struct host_node_s *k2) |
| 9533 | { |
| 9534 | int i; |
| 9535 | |
| 9536 | i = (k2->hn_addr.adf_family - k1->hn_addr.adf_family); |
| 9537 | if (i != 0) |
| 9538 | return i; |
| 9539 | |
| 9540 | if (k1->hn_addr.adf_family == AF_INET) |
| 9541 | return (k2->hn_addr.adf_addr.in4.s_addr - |
| 9542 | k1->hn_addr.adf_addr.in4.s_addr); |
| 9543 | |
| 9544 | i = k2->hn_addr.adf_addr.i6[3] - k1->hn_addr.adf_addr.i6[3]; |
| 9545 | if (i != 0) |
| 9546 | return i; |
| 9547 | i = k2->hn_addr.adf_addr.i6[2] - k1->hn_addr.adf_addr.i6[2]; |
| 9548 | if (i != 0) |
| 9549 | return i; |
| 9550 | i = k2->hn_addr.adf_addr.i6[1] - k1->hn_addr.adf_addr.i6[1]; |
| 9551 | if (i != 0) |
| 9552 | return i; |
| 9553 | i = k2->hn_addr.adf_addr.i6[0] - k1->hn_addr.adf_addr.i6[0]; |
| 9554 | return i; |
| 9555 | } |
| 9556 | |
| 9557 | |
| 9558 | /* ------------------------------------------------------------------------ */ |
| 9559 | /* Function: ipf_ht_node_make_key */ |
| 9560 | /* Returns: Nil */ |
| 9561 | /* parameters: htp(I) - pointer to address tracking structure */ |
| 9562 | /* key(I) - where to store masked address for lookup */ |
| 9563 | /* family(I) - protocol family of address */ |
| 9564 | /* addr(I) - pointer to network address */ |
| 9565 | /* */ |
| 9566 | /* Using the "netmask" (number of bits) stored parent host tracking struct, */ |
| 9567 | /* copy the address passed in into the key structure whilst masking out the */ |
| 9568 | /* bits that we don't want. */ |
| 9569 | /* */ |
| 9570 | /* Because the parser will set ht_netmask to 128 if there is no protocol */ |
| 9571 | /* specified (the parser doesn't know if it should be a v4 or v6 rule), we */ |
| 9572 | /* have to be wary of that and not allow 32-128 to happen. */ |
| 9573 | /* ------------------------------------------------------------------------ */ |
| 9574 | static void |
| 9575 | ipf_ht_node_make_key(host_track_t *htp, host_node_t *key, int family, |
| 9576 | i6addr_t *addr) |
| 9577 | { |
| 9578 | key->hn_addr.adf_family = family; |
| 9579 | if (family == AF_INET) { |
| 9580 | u_32_t mask; |
| 9581 | int bits; |
| 9582 | |
| 9583 | key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in4); |
| 9584 | bits = htp->ht_netmask; |
| 9585 | if (bits >= 32) { |
| 9586 | mask = 0xffffffff; |
| 9587 | } else { |
| 9588 | mask = htonl(0xffffffff << (32 - bits)); |
| 9589 | } |
| 9590 | key->hn_addr.adf_addr.in4.s_addr = addr->in4.s_addr & mask; |
| 9591 | #ifdef USE_INET6 |
| 9592 | } else { |
| 9593 | int bits = htp->ht_netmask; |
| 9594 | |
| 9595 | key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in6); |
| 9596 | if (bits > 96) { |
| 9597 | key->hn_addr.adf_addr.i6[3] = addr->i6[3] & |
| 9598 | htonl(0xffffffff << (128 - bits)); |
| 9599 | key->hn_addr.adf_addr.i6[2] = addr->i6[2]; |
| 9600 | key->hn_addr.adf_addr.i6[1] = addr->i6[2]; |
| 9601 | key->hn_addr.adf_addr.i6[0] = addr->i6[2]; |
| 9602 | } else if (bits > 64) { |
| 9603 | key->hn_addr.adf_addr.i6[3] = 0; |
| 9604 | key->hn_addr.adf_addr.i6[2] = addr->i6[2] & |
| 9605 | htonl(0xffffffff << (96 - bits)); |
| 9606 | key->hn_addr.adf_addr.i6[1] = addr->i6[1]; |
| 9607 | key->hn_addr.adf_addr.i6[0] = addr->i6[0]; |
| 9608 | } else if (bits > 32) { |
| 9609 | key->hn_addr.adf_addr.i6[3] = 0; |
| 9610 | key->hn_addr.adf_addr.i6[2] = 0; |
| 9611 | key->hn_addr.adf_addr.i6[1] = addr->i6[1] & |
| 9612 | htonl(0xffffffff << (64 - bits)); |
| 9613 | key->hn_addr.adf_addr.i6[0] = addr->i6[0]; |
| 9614 | } else { |
| 9615 | key->hn_addr.adf_addr.i6[3] = 0; |
| 9616 | key->hn_addr.adf_addr.i6[2] = 0; |
| 9617 | key->hn_addr.adf_addr.i6[1] = 0; |
| 9618 | key->hn_addr.adf_addr.i6[0] = addr->i6[0] & |
| 9619 | htonl(0xffffffff << (32 - bits)); |
| 9620 | } |
| 9621 | #endif |
| 9622 | } |
| 9623 | } |
| 9624 | |
| 9625 | |
| 9626 | /* ------------------------------------------------------------------------ */ |
| 9627 | /* Function: ipf_ht_node_add */ |
| 9628 | /* Returns: int - 0 == success, -1 == failure */ |
| 9629 | /* Parameters: softc(I) - pointer to soft context main structure */ |
| 9630 | /* htp(I) - pointer to address tracking structure */ |
| 9631 | /* family(I) - protocol family of address */ |
| 9632 | /* addr(I) - pointer to network address */ |
| 9633 | /* */ |
| 9634 | /* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ |
| 9635 | /* ipf_ht_node_del FROM RUNNING CONCURRENTLY ON THE SAME htp. */ |
| 9636 | /* */ |
| 9637 | /* After preparing the key with the address information to find, look in */ |
| 9638 | /* the red-black tree to see if the address is known. A successful call to */ |
| 9639 | /* this function can mean one of two things: a new node was added to the */ |
| 9640 | /* tree or a matching node exists and we're able to bump up its activity. */ |
| 9641 | /* ------------------------------------------------------------------------ */ |
| 9642 | int |
| 9643 | ipf_ht_node_add(ipf_main_softc_t *softc, host_track_t *htp, int family, |
| 9644 | i6addr_t *addr) |
| 9645 | { |
| 9646 | host_node_t *h; |
| 9647 | host_node_t k; |
| 9648 | |
| 9649 | ipf_ht_node_make_key(htp, &k, family, addr); |
| 9650 | |
| 9651 | h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); |
| 9652 | if (h == NULL) { |
| 9653 | if (htp->ht_cur_nodes >= htp->ht_max_nodes) |
| 9654 | return -1; |
| 9655 | KMALLOC(h, host_node_t *); |
| 9656 | if (h == NULL) { |
| 9657 | DT(ipf_rb_no_mem); |
| 9658 | LBUMP(ipf_rb_no_mem); |
| 9659 | return -1; |
| 9660 | } |
| 9661 | |
| 9662 | /* |
| 9663 | * If there was a macro to initialise the RB node then that |
| 9664 | * would get used here, but there isn't... |
| 9665 | */ |
| 9666 | bzero((char *)h, sizeof(*h)); |
| 9667 | h->hn_addr = k.hn_addr; |
| 9668 | h->hn_addr.adf_family = k.hn_addr.adf_family; |
| 9669 | RBI_INSERT(ipf_rb, &htp->ht_root, h); |
| 9670 | htp->ht_cur_nodes++; |
| 9671 | } else { |
| 9672 | if ((htp->ht_max_per_node != 0) && |
| 9673 | (h->hn_active >= htp->ht_max_per_node)) { |
| 9674 | DT(ipf_rb_node_max); |
| 9675 | LBUMP(ipf_rb_node_max); |
| 9676 | return -1; |
| 9677 | } |
| 9678 | } |
| 9679 | |
| 9680 | h->hn_active++; |
| 9681 | |
| 9682 | return 0; |
| 9683 | } |
| 9684 | |
| 9685 | |
| 9686 | /* ------------------------------------------------------------------------ */ |
| 9687 | /* Function: ipf_ht_node_del */ |
| 9688 | /* Returns: int - 0 == success, -1 == failure */ |
| 9689 | /* parameters: htp(I) - pointer to address tracking structure */ |
| 9690 | /* family(I) - protocol family of address */ |
| 9691 | /* addr(I) - pointer to network address */ |
| 9692 | /* */ |
| 9693 | /* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ |
| 9694 | /* ipf_ht_node_add FROM RUNNING CONCURRENTLY ON THE SAME htp. */ |
| 9695 | /* */ |
| 9696 | /* Try and find the address passed in amongst the leaves on this tree to */ |
| 9697 | /* be friend. If found then drop the active account for that node drops by */ |
| 9698 | /* one. If that count reaches 0, it is time to free it all up. */ |
| 9699 | /* ------------------------------------------------------------------------ */ |
| 9700 | int |
| 9701 | ipf_ht_node_del(host_track_t *htp, int family, i6addr_t *addr) |
| 9702 | { |
| 9703 | host_node_t *h; |
| 9704 | host_node_t k; |
| 9705 | |
| 9706 | ipf_ht_node_make_key(htp, &k, family, addr); |
| 9707 | |
| 9708 | h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); |
| 9709 | if (h == NULL) { |
| 9710 | return -1; |
| 9711 | } else { |
| 9712 | h->hn_active--; |
| 9713 | if (h->hn_active == 0) { |
| 9714 | (void) RBI_DELETE(ipf_rb, &htp->ht_root, h); |
| 9715 | htp->ht_cur_nodes--; |
| 9716 | KFREE(h); |
| 9717 | } |
| 9718 | } |
| 9719 | |
| 9720 | return 0; |
| 9721 | } |
| 9722 | |
| 9723 | |
| 9724 | /* ------------------------------------------------------------------------ */ |
| 9725 | /* Function: ipf_rb_ht_init */ |
| 9726 | /* Returns: Nil */ |
| 9727 | /* Parameters: head(I) - pointer to host tracking structure */ |
| 9728 | /* */ |
| 9729 | /* Initialise the host tracking structure to be ready for use above. */ |
| 9730 | /* ------------------------------------------------------------------------ */ |
| 9731 | void |
| 9732 | ipf_rb_ht_init(host_track_t *head) |
| 9733 | { |
| 9734 | memset(head, 0, sizeof(*head)); |
| 9735 | RBI_INIT(ipf_rb, &head->ht_root); |
| 9736 | } |
| 9737 | |
| 9738 | |
| 9739 | /* ------------------------------------------------------------------------ */ |
| 9740 | /* Function: ipf_rb_ht_freenode */ |
| 9741 | /* Returns: Nil */ |
| 9742 | /* Parameters: head(I) - pointer to host tracking structure */ |
| 9743 | /* arg(I) - additional argument from walk caller */ |
| 9744 | /* */ |
| 9745 | /* Free an actual host_node_t structure. */ |
| 9746 | /* ------------------------------------------------------------------------ */ |
| 9747 | void |
| 9748 | ipf_rb_ht_freenode(host_node_t *node, void *arg) |
| 9749 | { |
| 9750 | KFREE(node); |
| 9751 | } |
| 9752 | |
| 9753 | |
| 9754 | /* ------------------------------------------------------------------------ */ |
| 9755 | /* Function: ipf_rb_ht_flush */ |
| 9756 | /* Returns: Nil */ |
| 9757 | /* Parameters: head(I) - pointer to host tracking structure */ |
| 9758 | /* */ |
| 9759 | /* Remove all of the nodes in the tree tracking hosts by calling a walker */ |
| 9760 | /* and free'ing each one. */ |
| 9761 | /* ------------------------------------------------------------------------ */ |
| 9762 | void |
| 9763 | ipf_rb_ht_flush(host_track_t *head) |
| 9764 | { |
| 9765 | /* XXX - May use node members after freeing the node. */ |
| 9766 | RBI_WALK(ipf_rb, &head->ht_root, ipf_rb_ht_freenode, NULL); |
| 9767 | } |
| 9768 | |
| 9769 | |
| 9770 | /* ------------------------------------------------------------------------ */ |
| 9771 | /* Function: ipf_slowtimer */ |
| 9772 | /* Returns: Nil */ |
| 9773 | /* Parameters: ptr(I) - pointer to main ipf soft context structure */ |
| 9774 | /* */ |
| 9775 | /* Slowly expire held state for fragments. Timeouts are set * in */ |
| 9776 | /* expectation of this being called twice per second. */ |
| 9777 | /* ------------------------------------------------------------------------ */ |
| 9778 | void |
| 9779 | ipf_slowtimer(ipf_main_softc_t *softc) |
| 9780 | { |
| 9781 | |
| 9782 | ipf_token_expire(softc); |
| 9783 | ipf_frag_expire(softc); |
| 9784 | ipf_state_expire(softc); |
| 9785 | ipf_nat_expire(softc); |
| 9786 | ipf_auth_expire(softc); |
| 9787 | ipf_lookup_expire(softc); |
| 9788 | ipf_rule_expire(softc); |
| 9789 | ipf_sync_expire(softc); |
| 9790 | softc->ipf_ticks++; |
| 9791 | # if defined(__OpenBSD__) |
| 9792 | timeout_add(&ipf_slowtimer_ch, hz/2); |
| 9793 | # endif |
| 9794 | } |
| 9795 | |
| 9796 | |
| 9797 | /* ------------------------------------------------------------------------ */ |
| 9798 | /* Function: ipf_inet_mask_add */ |
| 9799 | /* Returns: Nil */ |
| 9800 | /* Parameters: bits(I) - pointer to nat context information */ |
| 9801 | /* mtab(I) - pointer to mask hash table structure */ |
| 9802 | /* */ |
| 9803 | /* When called, bits represents the mask of a new NAT rule that has just */ |
| 9804 | /* been added. This function inserts a bitmask into the array of masks to */ |
| 9805 | /* search when searching for a matching NAT rule for a packet. */ |
| 9806 | /* Prevention of duplicate masks is achieved by checking the use count for */ |
| 9807 | /* a given netmask. */ |
| 9808 | /* ------------------------------------------------------------------------ */ |
| 9809 | void |
| 9810 | ipf_inet_mask_add(int bits, ipf_v4_masktab_t *mtab) |
| 9811 | { |
| 9812 | u_32_t mask; |
| 9813 | int i, j; |
| 9814 | |
| 9815 | mtab->imt4_masks[bits]++; |
| 9816 | if (mtab->imt4_masks[bits] > 1) |
| 9817 | return; |
| 9818 | |
| 9819 | if (bits == 0) |
| 9820 | mask = 0; |
| 9821 | else |
| 9822 | mask = 0xffffffff << (32 - bits); |
| 9823 | |
| 9824 | for (i = 0; i < 33; i++) { |
| 9825 | if (ntohl(mtab->imt4_active[i]) < mask) { |
| 9826 | for (j = 32; j > i; j--) |
| 9827 | mtab->imt4_active[j] = mtab->imt4_active[j - 1]; |
| 9828 | mtab->imt4_active[i] = htonl(mask); |
| 9829 | break; |
| 9830 | } |
| 9831 | } |
| 9832 | mtab->imt4_max++; |
| 9833 | } |
| 9834 | |
| 9835 | |
| 9836 | /* ------------------------------------------------------------------------ */ |
| 9837 | /* Function: ipf_inet_mask_del */ |
| 9838 | /* Returns: Nil */ |
| 9839 | /* Parameters: bits(I) - number of bits set in the netmask */ |
| 9840 | /* mtab(I) - pointer to mask hash table structure */ |
| 9841 | /* */ |
| 9842 | /* Remove the 32bit bitmask represented by "bits" from the collection of */ |
| 9843 | /* netmasks stored inside of mtab. */ |
| 9844 | /* ------------------------------------------------------------------------ */ |
| 9845 | void |
| 9846 | ipf_inet_mask_del(int bits, ipf_v4_masktab_t *mtab) |
| 9847 | { |
| 9848 | u_32_t mask; |
| 9849 | int i, j; |
| 9850 | |
| 9851 | mtab->imt4_masks[bits]--; |
| 9852 | if (mtab->imt4_masks[bits] > 0) |
| 9853 | return; |
| 9854 | |
| 9855 | mask = htonl(0xffffffff << (32 - bits)); |
| 9856 | for (i = 0; i < 33; i++) { |
| 9857 | if (mtab->imt4_active[i] == mask) { |
| 9858 | for (j = i + 1; j < 33; j++) |
| 9859 | mtab->imt4_active[j - 1] = mtab->imt4_active[j]; |
| 9860 | break; |
| 9861 | } |
| 9862 | } |
| 9863 | mtab->imt4_max--; |
| 9864 | ASSERT(mtab->imt4_max >= 0); |
| 9865 | } |
| 9866 | |
| 9867 | |
| 9868 | #ifdef USE_INET6 |
| 9869 | /* ------------------------------------------------------------------------ */ |
| 9870 | /* Function: ipf_inet6_mask_add */ |
| 9871 | /* Returns: Nil */ |
| 9872 | /* Parameters: bits(I) - number of bits set in mask */ |
| 9873 | /* mask(I) - pointer to mask to add */ |
| 9874 | /* mtab(I) - pointer to mask hash table structure */ |
| 9875 | /* */ |
| 9876 | /* When called, bitcount represents the mask of a IPv6 NAT map rule that */ |
| 9877 | /* has just been added. This function inserts a bitmask into the array of */ |
| 9878 | /* masks to search when searching for a matching NAT rule for a packet. */ |
| 9879 | /* Prevention of duplicate masks is achieved by checking the use count for */ |
| 9880 | /* a given netmask. */ |
| 9881 | /* ------------------------------------------------------------------------ */ |
| 9882 | void |
| 9883 | ipf_inet6_mask_add(int bits, i6addr_t *mask, ipf_v6_masktab_t *mtab) |
| 9884 | { |
| 9885 | i6addr_t zero; |
| 9886 | int i, j; |
| 9887 | |
| 9888 | mtab->imt6_masks[bits]++; |
| 9889 | if (mtab->imt6_masks[bits] > 1) |
| 9890 | return; |
| 9891 | |
| 9892 | if (bits == 0) { |
| 9893 | mask = &zero; |
| 9894 | zero.i6[0] = 0; |
| 9895 | zero.i6[1] = 0; |
| 9896 | zero.i6[2] = 0; |
| 9897 | zero.i6[3] = 0; |
| 9898 | } |
| 9899 | |
| 9900 | for (i = 0; i < 129; i++) { |
| 9901 | if (IP6_LT(&mtab->imt6_active[i], mask)) { |
| 9902 | for (j = 128; j > i; j--) |
| 9903 | mtab->imt6_active[j] = mtab->imt6_active[j - 1]; |
| 9904 | mtab->imt6_active[i] = *mask; |
| 9905 | break; |
| 9906 | } |
| 9907 | } |
| 9908 | mtab->imt6_max++; |
| 9909 | } |
| 9910 | |
| 9911 | |
| 9912 | /* ------------------------------------------------------------------------ */ |
| 9913 | /* Function: ipf_inet6_mask_del */ |
| 9914 | /* Returns: Nil */ |
| 9915 | /* Parameters: bits(I) - number of bits set in mask */ |
| 9916 | /* mask(I) - pointer to mask to remove */ |
| 9917 | /* mtab(I) - pointer to mask hash table structure */ |
| 9918 | /* */ |
| 9919 | /* Remove the 128bit bitmask represented by "bits" from the collection of */ |
| 9920 | /* netmasks stored inside of mtab. */ |
| 9921 | /* ------------------------------------------------------------------------ */ |
| 9922 | void |
| 9923 | ipf_inet6_mask_del(int bits, i6addr_t *mask, ipf_v6_masktab_t *mtab) |
| 9924 | { |
| 9925 | i6addr_t zero; |
| 9926 | int i, j; |
| 9927 | |
| 9928 | mtab->imt6_masks[bits]--; |
| 9929 | if (mtab->imt6_masks[bits] > 0) |
| 9930 | return; |
| 9931 | |
| 9932 | if (bits == 0) |
| 9933 | mask = &zero; |
| 9934 | zero.i6[0] = 0; |
| 9935 | zero.i6[1] = 0; |
| 9936 | zero.i6[2] = 0; |
| 9937 | zero.i6[3] = 0; |
| 9938 | |
| 9939 | for (i = 0; i < 129; i++) { |
| 9940 | if (IP6_EQ(&mtab->imt6_active[i], mask)) { |
| 9941 | for (j = i + 1; j < 129; j++) { |
| 9942 | mtab->imt6_active[j - 1] = mtab->imt6_active[j]; |
| 9943 | if (IP6_EQ(&mtab->imt6_active[j - 1], &zero)) |
| 9944 | break; |
| 9945 | } |
| 9946 | break; |
| 9947 | } |
| 9948 | } |
| 9949 | mtab->imt6_max--; |
| 9950 | ASSERT(mtab->imt6_max >= 0); |
| 9951 | } |
| 9952 | #endif |
| 9953 | |