diff --git a/sbin/ipf/libipf/interror.c b/sbin/ipf/libipf/interror.c index 04fc53c863fe..5b3836f36d60 100644 --- a/sbin/ipf/libipf/interror.c +++ b/sbin/ipf/libipf/interror.c @@ -1,580 +1,582 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: interror.c,v 1.9.2.12 2012/07/22 08:03:39 darren_r Exp $ */ #include "ipf.h" #include #include typedef struct { int iee_number; char *iee_text; } ipf_error_entry_t; static ipf_error_entry_t *find_error(int); #define IPF_NUM_ERRORS sizeof(ipf_errors) / sizeof(ipf_error_entry_t) /* * NO REUSE OF NUMBERS! * * IF YOU WANT TO ADD AN ERROR TO THIS TABLE, _ADD_ A NEW NUMBER. * DO _NOT_ USE AN EMPTY NUMBER OR FILL IN A GAP. */ static ipf_error_entry_t ipf_errors[] = { { 1, "auth table locked/full" }, { 2, "" }, { 3, "copyinptr received bad address" }, { 4, "copyoutptr received bad address" }, { 5, "" }, { 6, "cannot load a rule with FR_T_BUILTIN flag set" }, { 7, "internal rule without FR_T_BUILDINT flag set" }, { 8, "no data provided with filter rule" }, { 9, "invalid ioctl for rule" }, { 10, "rule protocol is not 4 or 6" }, { 11, "cannot find rule function" }, { 12, "cannot find rule group" }, { 13, "group in/out does not match rule in/out" }, { 14, "rule without in/out does not belong to a group" }, { 15, "cannot determine where to append rule" }, { 16, "malloc for rule data failed" }, { 17, "copyin for rule data failed" }, { 18, "" }, { 19, "zero data size for BPF rule" }, { 20, "BPF validation failed" }, { 21, "incorrect data size for IPF rule" }, { 22, "'keep state' rule included 'with oow'" }, { 23, "bad interface index with dynamic source address" }, { 24, "bad interface index with dynamic dest. address" }, { 25, "match array verif failed for filter rule" }, { 26, "bad filter rule type" }, { 27, "rule not found for zero'stats" }, { 28, "copyout failed for zero'ing stats" }, { 29, "rule not found for removing" }, { 30, "cannot remove internal rule" }, { 31, "rule in use" }, { 32, "rule already exists" }, { 33, "no memory for another rule" }, { 34, "could not find function" }, { 35, "copyout failed for resolving function name -> addr" }, { 36, "copyout failed for resolving function addr -> name" }, { 37, "function name/addr resolving search failed" }, { 38, "group map cannot find it's hash table" }, { 39, "group map hash-table in/out do not match rule" }, { 40, "bcopyout failed for SIOCIPFINTERROR" }, { 41, "" }, { 42, "ipfilter not enabled for NAT ioctl" }, { 43, "ipfilter not enabled for state ioctl" }, { 44, "ipfilter not enabled for auth ioctl" }, { 45, "ipfilter not enabled for sync ioctl" }, { 46, "ipfilter not enabled for scan ioctl" }, { 47, "ipfilter not enabled for lookup ioctl" }, { 48, "unrecognised device minor number for ioctl" }, { 49, "unrecognised object type for copying in ipfobj" }, { 50, "mismatching object type for copying in ipfobj" }, { 51, "object size too small for copying in ipfobj" }, { 52, "object size mismatch for copying in ipfobj" }, { 53, "compat object size too small for copying in ipfobj" }, { 54, "compat object size mismatch for copying in ipfobj" }, { 55, "error doing copyin of data for in ipfobj" }, { 56, "unrecognised object type for size copy in ipfobj" }, { 57, "object size too small for size copy in ipfobj" }, { 58, "mismatching object type for size copy in ipfobj" }, { 59, "object size mismatch for size copy in ipfobj" }, { 60, "compat object size mismatch for size copy in ipfobj" }, { 61, "error doing size copyin of data for in ipfobj" }, { 62, "bad object type for size copy out ipfobj" }, { 63, "mismatching object type for size copy out ipfobj" }, { 64, "object size mismatch for size copy out ipfobj" }, { 65, "compat object size wrong for size copy out ipfobj" }, { 66, "error doing size copyout of data for out ipfobj" }, { 67, "unrecognised object type for copying out ipfobj" }, { 68, "mismatching object type for copying out ipfobj" }, { 69, "object size too small for copying out ipfobj" }, { 70, "object size mismatch for copying out ipfobj" }, { 71, "compat object size too small for copying out ipfobj" }, { 72, "compat object size mismatch for copying out ipfobj" }, { 73, "error doing copyout of data for out ipfobj" }, { 74, "attempt to add existing tunable name" }, { 75, "cannot find tunable name to delete" }, { 76, "internal data too big for next tunable" }, { 77, "could not find tunable" }, { 78, "tunable can only be changed when ipfilter disabled" }, { 79, "new tunable value outside accepted range" }, { 80, "ipftune called for unrecognised ioctl" }, { 81, "" }, { 82, "could not find token to delete" }, { 83, "" }, { 84, "attempt to get next rule when no more exist" }, { 85, "value for iri_inout outside accepted range" }, { 86, "value for iri_active outside accepted range" }, { 87, "value for iri_nrules is 0" }, { 88, "NULL pointer specified for where to copy rule to" }, { 89, "copyout of rule failed" }, { 90, "" }, { 91, "could not get token for rule iteration" }, { 92, "unrecognised generic iterator" }, { 93, "could not find token for generic iterator" }, { 94, "need write permissions to disable/enable ipfilter" }, { 95, "error copying in enable/disable value" }, { 96, "need write permissions to set ipf tunable" }, { 97, "need write permissions to set ipf flags" }, { 98, "error doing copyin of ipf flags" }, { 99, "error doing copyout of ipf flags" }, { 100, "need write permissions to add another rule" }, { 101, "need write permissions to insert another rule" }, { 102, "need write permissions to swap active rule set" }, { 103, "error copying out current active rule set" }, { 104, "need write permissions to zero ipf stats" }, { 105, "need write permissions to flush ipf v4 rules" }, { 106, "error copying out v4 flush results" }, { 107, "error copying in v4 flush command" }, { 108, "need write permissions to flush ipf v6 rules" }, { 109, "error copying out v6 flush results" }, { 110, "error copying in v6 flush command" }, { 111, "error copying in new lock state for ipfilter" }, { 112, "need write permissions to flush ipf logs" }, { 113, "error copying out results of log flush" }, { 114, "need write permissions to resync ipf" }, { 115, "unrecognised ipf ioctl" }, { 116, "error copying in match array" }, { 117, "match array type is not IPFOBJ_IPFEXPR" }, { 118, "bad size for match array" }, { 119, "cannot allocate memory for match aray" }, { 120, "error copying in match array" }, { 121, "error verifying contents of match array" }, { 122, "need write permissions to set ipf lock status" }, { 123, "error copying in data for function resolution" }, { 124, "error copying in ipfobj structure" }, { 125, "error copying in ipfobj structure" }, { 126, "error copying in ipfobj structure" }, { 127, "error copying in ipfobj structure" }, { 128, "no memory for filter rule comment" }, { 129, "error copying in filter rule comment" }, { 130, "error copying out filter rule comment" }, { 131, "no memory for new rule alloc buffer" }, { 132, "cannot find source lookup pool" }, { 133, "unknown source address type" }, { 134, "cannot find destination lookup pool" }, { 135, "unknown destination address type" }, { 136, "icmp head group name index incorrect" }, { 137, "group head name index incorrect" }, { 138, "group name index incorrect" }, { 139, "to interface name index incorrect" }, { 140, "dup-to interface name index incorrect" }, { 141, "reply-to interface name index incorrect" }, { 142, "could not initialise call now function" }, { 143, "could not initialise call function" }, { 144, "could not find destination list" }, { 145, "auth rules cannot have dup/to/fastroute" }, { 146, "incorrect size for object to copy out" }, { 147, "object type out of bounds for kernel copyout" }, { 148, "object size too small for kernel copyout" }, { 149, "object size validation failed for kernel copyout" }, { 150, "error copying data out for kernel copyout" }, { 151, "version mismatch for kernel copyout" }, /* -------------------------------------------------------------------------- */ { 10001, "could not find token for auth iterator" }, { 10002, "write permissions require to add/remove auth rule" }, { 10003, "need write permissions to set auth lock" }, { 10004, "error copying out results of auth flush" }, { 10005, "unknown auth ioctl" }, { 10006, "can only append or remove preauth rules" }, { 10007, "NULL pointers passed in for preauth remove" }, { 10008, "preauth rule not found to remove" }, { 10009, "could not malloc memory for preauth entry" }, { 10010, "unrecognised preauth rule ioctl command" }, { 10011, "iterator data supplied with NULL pointer" }, { 10012, "unknown auth iterator type" }, { 10013, "iterator error copying out auth data" }, { 10014, "sleep waiting for auth packet interrupted" }, { 10015, "bad index supplied in auth reply" }, { 10016, "error injecting outbound packet back into kernel" }, { 10017, "error injecting inbound packet back into kernel" }, { 10018, "could not attempt to inject packet back into kernel" }, { 10019, "packet id does not match" }, /* -------------------------------------------------------------------------- */ { 20001, "invalid frag token data pointer supplied" }, { 20002, "error copying out frag token data" }, { 20003, "can only copy one fragment state entry at a time" }, /* -------------------------------------------------------------------------- */ { 30001, "incorrect object size to get hash table stats" }, { 30002, "could not malloc memory for new hash table" }, { 30003, "error coping in hash table structure" }, { 30004, "hash table already exists" }, { 30005, "mismatch between new hash table and operation unit" }, { 30006, "could not malloc memory for hash table base" }, { 30007, "could not find hash table" }, { 30008, "mismatch between hash table and operation unit" }, { 30009, "could not find hash table for iterators next node" }, { 30010, "unknown iterator tpe" }, { 30011, "iterator error copying out hash table" }, { 30012, "iterator error copying out hash table entry" }, { 30013, "error copying out hash table statistics" }, { 30014, "table node delete structure wrong size" }, { 30015, "error copying in node to delete" }, { 30016, "table to delete node from does not exist" }, { 30017, "could not find table to remove node from" }, { 30018, "table node add structure wrong size" }, { 30019, "error copying in node to add" }, { 30020, "could not find table to add node to" }, { 30021, "node already exists in the table" }, { 30022, "could not find node to delete in table" }, { 30023, "uid mismatch on node to delete" }, { 30024, "object size incorrect for hash table" }, { 30025, "hash table size must be at least 1"}, { 30026, "cannot allocate memory for hash table context" }, + { 30027, "hash table larger than maximum allowed" }, + { 30028, "hash table multiplication overflow" }, /* -------------------------------------------------------------------------- */ { 40001, "invalid minor device number for log read" }, { 40002, "read size too small" }, { 40003, "interrupted waiting for log data to read" }, { 40004, "interrupted waiting for log data to read" }, { 40005, "read size too large" }, { 40006, "uiomove for read operation failed" }, /* -------------------------------------------------------------------------- */ { 50001, "unknown lookup ioctl" }, { 50002, "error copying in object data for add node" }, { 50003, "invalid unit for lookup add node" }, { 50004, "incorrect size for adding a pool node" }, { 50005, "error copying in pool node structure" }, { 50006, "mismatch in pool node address/mask families" }, { 50007, "could not find pool name" }, { 50008, "node already exists in pool" }, { 50009, "incorrect size for adding a hash node" }, { 50010, "error copying in hash node structure" }, { 50011, "could not find hash table name" }, { 50012, "unrecognised object type for lookup add node" }, { 50013, "invalid unit for lookup delete node" }, { 50014, "incorrect size for deleting a pool node" }, { 50015, "error copying in pool node structure" }, { 50016, "could not find pool name" }, { 50017, "could not find pool node" }, { 50018, "incorrect size for removing a hash node" }, { 50019, "error copying in hash node structure" }, { 50020, "could not find hash table name" }, { 50021, "unrecognised object type for lookup delete node" }, { 50022, "error copying in add table data" }, { 50023, "invalid unit for lookup add table" }, { 50024, "pool name already exists" }, { 50025, "hash table name already exists" }, { 50026, "unrecognised object type for lookup add table" }, { 50027, "error copying table data back out" }, { 50028, "error copying in remove table data" }, { 50029, "invalid unit for lookup remove table" }, { 50030, "unrecognised object type for lookup remove table" }, { 50031, "error copying in lookup stats structure" }, { 50032, "invalid unit for lookup stats" }, { 50033, "unrecognised object type for lookup stats" }, { 50034, "error copying in flush lookup data" }, { 50035, "invalid unit for lookup flush" }, { 50036, "incorrect table type for lookup flush" }, { 50037, "error copying out lookup flush results" }, { 50038, "invalid unit for lookup iterator" }, { 50039, "invalid unit for lookup iterator" }, { 50040, "could not find token for lookup iterator" }, { 50041, "unrecognised object type for lookup iterator" }, { 50042, "error copying in lookup delete node operation" }, /* -------------------------------------------------------------------------- */ { 60001, "insufficient privilege for NAT write operation" }, { 60002, "need write permissions to flush NAT logs" }, { 60003, "need write permissions to turn NAT logging on/off" }, { 60004, "error copying out current NAT log setting" }, { 60005, "error copying out bytes waiting to be read in NAT \ log" }, { 60006, "need write permissions to add NAT rule" }, { 60007, "NAT rule already exists" }, { 60008, "could not allocate memory for NAT rule" }, { 60009, "need write permissions to remove NAT rule" }, { 60010, "NAT rule could not be found" }, { 60011, "could not find NAT entry for redirect lookup" }, { 60012, "need write permissions to flush NAT table" }, { 60013, "error copying in NAT flush command" }, { 60014, "need write permissions to do matching NAT flush" }, { 60015, "need write permissions to set NAT lock" }, { 60016, "need write permissions to add entry to NAT table" }, { 60017, "NAT not locked for size retrieval" }, { 60018, "NAT not locked for fetching NAT table entry" }, { 60019, "error copying in NAT token data for deletion" }, { 60020, "unknown NAT ioctl" }, { 60021, "" }, { 60022, "resolving proxy name in NAT rule failed" }, { 60023, "only reply age specified in NAT rule" }, { 60024, "error doing copyin to determine NAT entry size" }, { 60025, "error copying out NAT size of 0" }, { 60026, "NAT entry not found" }, { 60027, "error doing copyout of NAT entry size" }, { 60028, "invalid data size for getting NAT entry" }, { 60029, "could not malloc temporary space for NAT entry" }, { 60030, "no NAT table entries present" }, { 60031, "NAT entry to get next from not found" }, { 60032, "not enough space for proxy structure" }, { 60033, "not enough space for private proxy data" }, { 60034, "NAT entry size is too large" }, { 60035, "could not malloc memory for NAT entry sratch space" }, { 60036, "" }, { 60037, "could not malloc memory for NAT entry" }, { 60038, "could not malloc memory for NAT entry rule" }, { 60039, "could not resolve NAT entry rule's proxy" }, { 60040, "cannot add outbound duplicate NAT entry" }, { 60041, "cannot add inbound duplicate NAT entry" }, { 60042, "cannot add NAT entry that is neither IN nor OUT" }, { 60043, "could not malloc memory for NAT proxy data" }, { 60044, "proxy data size too big" }, { 60045, "could not malloc proxy private data for NAT entry" }, { 60046, "could not malloc memory for new NAT filter rule" }, { 60047, "could not find existing filter rule for NAT entry" }, { 60048, "insertion into NAT table failed" }, { 60049, "iterator error copying out hostmap data" }, { 60050, "iterator error copying out NAT rule data" }, { 60051, "iterator error copying out NAT entry data" }, { 60052, "iterator data supplied with NULL pointer" }, { 60053, "unknown NAT iterator type" }, { 60054, "unknown next address type" }, { 60055, "iterator suppled with unknown type for get-next" }, { 60056, "unknown lookup group for next address" }, { 60057, "error copying out NAT log flush results" }, { 60058, "bucket table type is incorrect" }, { 60059, "error copying out NAT bucket table" }, { 60060, "function not found for lookup" }, { 60061, "address family not supported with SIOCSTPUT" }, { 60062, "unknown timeout name" }, { 60063, "cannot allocate new inbound NAT entry table" }, { 60064, "cannot allocate new outbound NAT entry table" }, { 60065, "cannot allocate new inbound NAT bucketlen table" }, { 60066, "cannot allocate new outbound NAT bucketlen table" }, { 60067, "cannot allocate new NAT rules table" }, { 60068, "cannot allocate new NAT hostmap table" }, { 60069, "new source lookup type is not dstlist" }, { 60070, "cannot allocate NAT rule scratch space" }, { 60071, "new destination lookup type is not dstlist" }, { 60072, "function not found for lookup (ipv6)" }, { 60073, "unknown lookup group for next address (ipv6)" }, { 60074, "unknown next address type (ipv6)" }, { 60075, "one object at a time must be copied" }, { 60076, "NAT ioctl denied in jail without VNET" }, /* -------------------------------------------------------------------------- */ { 70001, "incorrect object size to get pool stats" }, { 70002, "could not malloc memory for new pool node" }, { 70003, "invalid address length for new pool node" }, { 70004, "invalid mask length for new pool node" }, { 70005, "error adding node to pool" }, { 70006, "pool already exists" }, { 70007, "could not malloc memory for new pool" }, { 70008, "could not allocate radix tree for new pool" }, { 70009, "could not find pool" }, { 70010, "unknown pool name for iteration" }, { 70011, "unknown pool iterator" }, { 70012, "error copying out pool head" }, { 70013, "error copying out pool node" }, { 70014, "add node size incorrect" }, { 70015, "error copying in pool node" }, { 70016, "" }, { 70017, "cannot find pool for node" }, { 70018, "node entry already present in pool" }, { 70019, "delete node size incorrect" }, { 70020, "error copying in node to delete" }, { 70021, "cannot find pool to delete node from" }, { 70022, "cannot find node to delete in pool" }, { 70023, "pool name already exists" }, { 70024, "uid mismatch for node removal" }, { 70025, "stats device unit is invalid" }, { 70026, "error copying out statistics" }, { 70027, "could not remove node from radix tree" }, { 70028, "incorrect address length in pool node add" }, { 70029, "incorrect mask length in pool node add" }, { 70030, "incorrect address length in pool node remove" }, { 70031, "incorrect mask length in pool node remove" }, { 70032, "cannot allocate memory for pool context" }, { 70033, "cannot allocate memory for radix tree context" }, { 70034, "adding IPv6 node with incorrect address length" }, { 70035, "IPv4 address not masked" }, { 70036, "IPv6 address not masked" }, { 70037, "removing IPv6 node with incorrect address length" }, /* -------------------------------------------------------------------------- */ { 80001, "could not find proxy" }, { 80002, "proxy does not support control operations" }, { 80003, "could not allocate data to hold proxy operation" }, { 80004, "unknown proxy ioctl" }, { 80005, "could not copyin proxy control structure" }, { 80006, "DNS proxy could not find rule to delete" }, { 80007, "DNS proxy found existing matching rule" }, { 80008, "DNS proxy could not allocate memory for new rule" }, { 80009, "DNS proxy unknown command request" }, /* -------------------------------------------------------------------------- */ { 90001, "could not malloc space for new scan structure" }, { 90002, "scan tag already exists" }, { 90003, "scan structure in use" }, { 90004, "could not find matching scan tag for filter rule" }, { 90005, "could not copyout scan statistics" }, /* -------------------------------------------------------------------------- */ { 100001, "cannot find matching state entry to remove" }, { 100002, "error copying in v4 state flush command" }, { 100003, "error copying out v4 state flush results" }, { 100004, "error copying in v6 state flush command" }, { 100005, "error copying out v6 state flush results" }, { 100006, "" }, { 100007, "" }, { 100008, "need write permissions to flush state log" }, { 100009, "error copyout results of flushing state log" }, { 100010, "need write permissions to turn state logging on/off" }, { 100011, "error copying in new state logging state" }, { 100012, "error copying out current state logging state" }, { 100013, "error copying out bytes waiting to be read in state \ log" }, { 100014, "need write permissions to set state lock" }, { 100015, "need write permissions to add entry to state table" }, { 100016, "state not locked for size retrieval" }, { 100017, "error copying out hash table bucket lengths" }, { 100018, "could not find token for state iterator" }, { 100019, "error copying in state token data for deletion" }, { 100020, "unknown state ioctl" }, { 100021, "no state table entries present" }, { 100022, "state entry to get next from not found" }, { 100023, "could not malloc memory for state entry" }, { 100024, "could not malloc memory for state entry rule" }, { 100025, "could not copy back state entry to user space" }, { 100026, "iterator data supplied with NULL pointer" }, { 100027, "iterator supplied with 0 item count" }, { 100028, "iterator type is incorrect" }, { 100029, "invalid state token data pointer supplied" }, { 100030, "error copying out next state entry" }, { 100031, "unrecognised table request" }, { 100032, "error copying out bucket length data" }, { 100033, "could not find existing filter rule for state entry" }, { 100034, "could not find timeout name" }, { 100035, "could not allocate new state table" }, { 100036, "could not allocate new state bucket length table" }, /* -------------------------------------------------------------------------- */ { 110001, "sync write header magic number is incorrect" }, { 110002, "sync write header protocol is incorrect" }, { 110003, "sync write header command is incorrect" }, { 110004, "sync write header table number is incorrect" }, { 110005, "data structure too small for sync write operation" }, { 110006, "zero length data with sync write header" }, { 110007, "insufficient data for sync write" }, { 110008, "bad sync read size" }, { 110009, "interrupted sync read (solaris)" }, { 110010, "interrupted sync read (hpux)" }, { 110011, "interrupted sync read (osf)" }, { 110012, "interrupted sync read" }, { 110013, "could not malloc memory for sync'd state" }, { 110014, "could not malloc memory for sync-state list item" }, { 110015, "sync update could not find state" }, { 110016, "unrecognised sync state command" }, { 110017, "could not malloc memory for new sync'd NAT entry" }, { 110018, "could not malloc memory for sync-NAT list item" }, { 110019, "sync update could not find NAT entry" }, { 110020, "unrecognised sync NAT command" }, { 110021, "ioctls are not handled with sync" }, /* -------------------------------------------------------------------------- */ { 120001, "null data pointer for iterator" }, { 120002, "unit outside of acceptable range" }, { 120003, "unknown iterator subtype" }, { 120004, "cannot find dest. list for iteration" }, { 120005, "error copying out destination iteration list" }, { 120006, "error copying out destination iteration node" }, { 120007, "wrong size for frdest_t structure" }, { 120008, "cannot allocate memory for new destination node" }, { 120009, "error copying in destination node to add" }, { 120010, "could not find destination list to add node to" }, { 120011, "error copying in destination node to remove" }, { 120012, "could not find dest. list to remove node from" }, { 120013, "destination list already exists" }, { 120014, "could not allocate new destination table" }, { 120015, "could not find destination list to remove" }, { 120016, "destination list cannot be removed - it is busy" }, { 120017, "error copying in names for destination" }, { 120018, "destination name is too long/short" }, { 120019, "unrecognised address family in destination" }, { 120020, "" }, { 120021, "error copying in new destination table" }, { 120022, "cannot allocate memory for node table" }, { 120023, "stats object size is incorrect for dest. lists" }, { 120024, "stats device unit is invalid for dest. lists" }, { 120025, "error copying out dest. list statistics" }, { 120026, "cannot allocate memory for destination node" }, { 120027, "error copying in destination node" }, { 120028, "cannot allocate memory for destination context " }, /* -------------------------------------------------------------------------- */ { 130001, "ioctl denied by system security level" }, { 130002, "ioctl operation on invalid minor device" }, { 130003, "ioctl on device denied, ipfitler is disabled" }, { 130004, "ioctl command not allowed when disabled" }, { 130005, "ioctl denied due to insufficient authorisation" }, { 130006, "cannot read while ipfilter is disabled" }, { 130007, "read on minor device not supported" }, { 130008, "cannot write while ipfilter is disabled" }, { 130009, "write on minor device not supported" }, { 130010, "poll on minor device is not supported" }, { 130011, "error removing IPv4 filter hooks" }, { 130012, "error removing IPv6 filter hooks" }, { 130013, "attaching IPv4 hook failed" }, { 130014, "attaching IPv6 hook failed" }, { 130015, "ipf_init_all failed" }, { 130016, "finding pfil head failed" }, { 130017, "ipfilter is already initialised and running" }, { 130018, "ioctl denied in jail without VNET" }, }; static ipf_error_entry_t * find_error(int errnum) { ipf_error_entry_t *ie; int l = -1, r = IPF_NUM_ERRORS + 1, step; step = (r - l) / 2;; while (step != 0) { ie = ipf_errors + l + step; if (ie->iee_number == errnum) return (ie); step = l + step; if (ie->iee_number > errnum) r = step; else l = step; step = (r - l) / 2;; } return (NULL); } char * ipf_geterror(int fd, ioctlfunc_t *func) { static char text[80]; ipf_error_entry_t *ie; int errnum; if ((*func)(fd, SIOCIPFINTERROR, &errnum) == 0) { ie = find_error(errnum); if (ie != NULL) return (ie->iee_text); snprintf(text, sizeof(text), "unknown error %d", errnum); } else { snprintf(text, sizeof(text), "retrieving error number failed (%d)", errno); } return (text); } char * ipf_strerror(int errnum) { static char text[80]; ipf_error_entry_t *ie; ie = find_error(errnum); if (ie != NULL) return (ie->iee_text); snprintf(text, sizeof(text), "unknown error %d", errnum); return (text); } diff --git a/sys/netpfil/ipfilter/netinet/ip_htable.c b/sys/netpfil/ipfilter/netinet/ip_htable.c index 94741d589601..ca9e96698245 100644 --- a/sys/netpfil/ipfilter/netinet/ip_htable.c +++ b/sys/netpfil/ipfilter/netinet/ip_htable.c @@ -1,1410 +1,1419 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # define _KERNEL # include # undef _KERNEL #endif #include #if defined(__FreeBSD__) # include #endif #if defined(__FreeBSD__) # include # include #endif #if !defined(__SVR4) # include #endif #if defined(_KERNEL) # include #else # include "ipf.h" #endif #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_lookup.h" #include "netinet/ip_htable.h" /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif # ifdef USE_INET6 static iphtent_t *ipf_iphmfind6(iphtable_t *, i6addr_t *); # endif static iphtent_t *ipf_iphmfind(iphtable_t *, struct in_addr *); static int ipf_iphmfindip(ipf_main_softc_t *, void *, int, void *, u_int); static int ipf_htable_clear(ipf_main_softc_t *, void *, iphtable_t *); static int ipf_htable_create(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_htable_deref(ipf_main_softc_t *, void *, void *); static int ipf_htable_destroy(ipf_main_softc_t *, void *, int, char *); static void *ipf_htable_exists(void *, int, char *); static size_t ipf_htable_flush(ipf_main_softc_t *, void *, iplookupflush_t *); static void ipf_htable_free(void *, iphtable_t *); static int ipf_htable_iter_deref(ipf_main_softc_t *, void *, int, int, void *); static int ipf_htable_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, ipflookupiter_t *); static int ipf_htable_node_add(ipf_main_softc_t *, void *, iplookupop_t *, int); static int ipf_htable_node_del(ipf_main_softc_t *, void *, iplookupop_t *, int); static int ipf_htable_remove(ipf_main_softc_t *, void *, iphtable_t *); static void *ipf_htable_soft_create(ipf_main_softc_t *); static void ipf_htable_soft_destroy(ipf_main_softc_t *, void *); static int ipf_htable_soft_init(ipf_main_softc_t *, void *); static void ipf_htable_soft_fini(ipf_main_softc_t *, void *); static int ipf_htable_stats_get(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_htable_table_add(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_htable_table_del(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_htent_deref(void *, iphtent_t *); static iphtent_t *ipf_htent_find(iphtable_t *, iphtent_t *); static int ipf_htent_insert(ipf_main_softc_t *, void *, iphtable_t *, iphtent_t *); static int ipf_htent_remove(ipf_main_softc_t *, void *, iphtable_t *, iphtent_t *); static void *ipf_htable_select_add_ref(void *, int, char *); static void ipf_htable_expire(ipf_main_softc_t *, void *); typedef struct ipf_htable_softc_s { u_long ipht_nomem[LOOKUP_POOL_SZ]; u_long ipf_nhtables[LOOKUP_POOL_SZ]; u_long ipf_nhtnodes[LOOKUP_POOL_SZ]; iphtable_t *ipf_htables[LOOKUP_POOL_SZ]; iphtent_t *ipf_node_explist; ipftuneable_t *ipf_htable_tune; u_int ipf_htable_size_max; } ipf_htable_softc_t; ipf_lookup_t ipf_htable_backend = { IPLT_HASH, ipf_htable_soft_create, ipf_htable_soft_destroy, ipf_htable_soft_init, ipf_htable_soft_fini, ipf_iphmfindip, ipf_htable_flush, ipf_htable_iter_deref, ipf_htable_iter_next, ipf_htable_node_add, ipf_htable_node_del, ipf_htable_stats_get, ipf_htable_table_add, ipf_htable_table_del, ipf_htable_deref, ipf_htable_exists, ipf_htable_select_add_ref, NULL, ipf_htable_expire, NULL }; static ipftuneable_t ipf_htable_tuneables[] = { { { (void *)offsetof(ipf_htable_softc_t, ipf_htable_size_max) }, "htable_size_max", 1, 0x7fffffff, stsizeof(ipf_htable_softc_t, ipf_htable_size_max), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise the routing table data structures where required. */ /* ------------------------------------------------------------------------ */ static void * ipf_htable_soft_create(ipf_main_softc_t *softc) { ipf_htable_softc_t *softh; KMALLOC(softh, ipf_htable_softc_t *); if (softh == NULL) { IPFERROR(30026); return (NULL); } bzero((char *)softh, sizeof(*softh)); softh->ipf_htable_tune = ipf_tune_array_copy(softh, sizeof(ipf_htable_tuneables), ipf_htable_tuneables); if (softh->ipf_htable_tune == NULL) { ipf_htable_soft_destroy(softc, softh); return (NULL); } if (ipf_tune_array_link(softc, softh->ipf_htable_tune) == -1) { ipf_htable_soft_destroy(softc, softh); return (NULL); } return (softh); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Clean up the pool by free'ing the radix tree associated with it and free */ /* up the pool context too. */ /* ------------------------------------------------------------------------ */ static void ipf_htable_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_htable_softc_t *softh = arg; if (softh->ipf_htable_tune != NULL) { ipf_tune_array_unlink(softc, softh->ipf_htable_tune); KFREES(softh->ipf_htable_tune, sizeof(ipf_htable_tuneables)); softh->ipf_htable_tune = NULL; } KFREE(softh); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash table ready for use. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_htable_softc_t *softh = arg; bzero((char *)softh, sizeof(*softh)); softh->ipf_htable_size_max = IPHTABLE_MAX_SIZE; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* Locks: WRITE(ipf_global) */ /* */ /* Clean up all the pool data structures allocated and call the cleanup */ /* function for the radix tree that supports the pools. ipf_pool_destroy is */ /* used to delete the pools one by one to ensure they're properly freed up. */ /* ------------------------------------------------------------------------ */ static void ipf_htable_soft_fini(ipf_main_softc_t *softc, void *arg) { iplookupflush_t fop; fop.iplf_type = IPLT_HASH; fop.iplf_unit = IPL_LOGALL; fop.iplf_arg = 0; fop.iplf_count = 0; *fop.iplf_name = '\0'; ipf_htable_flush(softc, arg, &fop); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_stats_get */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Copy the relevant statistics out of internal structures and into the */ /* structure used to export statistics. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_stats_get(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { ipf_htable_softc_t *softh = arg; iphtstat_t stats; int err; if (op->iplo_size != sizeof(stats)) { IPFERROR(30001); return (EINVAL); } bzero(&stats, sizeof(stats)); stats.iphs_tables = softh->ipf_htables[op->iplo_unit + 1]; stats.iphs_numtables = softh->ipf_nhtables[op->iplo_unit + 1]; stats.iphs_numnodes = softh->ipf_nhtnodes[op->iplo_unit + 1]; stats.iphs_nomem = softh->ipht_nomem[op->iplo_unit + 1]; err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); if (err != 0) { IPFERROR(30013); return (EFAULT); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_create */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Create a new hash table using the template passed. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_create(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { ipf_htable_softc_t *softh = arg; iphtable_t htab, *iph, *oiph; char name[FR_GROUPLEN]; int err, i, unit; if (op->iplo_size != sizeof(htab)) { IPFERROR(30024); return (EINVAL); } err = COPYIN(op->iplo_struct, &htab, sizeof(htab)); if (err != 0) { IPFERROR(30003); return (EFAULT); } unit = op->iplo_unit; if (htab.iph_unit != unit) { IPFERROR(30005); return (EINVAL); } if (htab.iph_size < 1) { IPFERROR(30025); return (EINVAL); } if ((op->iplo_arg & IPHASH_ANON) == 0) { iph = ipf_htable_exists(softh, unit, op->iplo_name); if (iph != NULL) { if ((iph->iph_flags & IPHASH_DELETE) == 0) { IPFERROR(30004); return (EEXIST); } iph->iph_flags &= ~IPHASH_DELETE; iph->iph_ref++; return (0); } } KMALLOC(iph, iphtable_t *); if (iph == NULL) { softh->ipht_nomem[op->iplo_unit + 1]++; IPFERROR(30002); return (ENOMEM); } *iph = htab; if ((op->iplo_arg & IPHASH_ANON) != 0) { i = IPHASH_ANON; do { i++; (void)snprintf(name, sizeof(name), "%u", i); for (oiph = softh->ipf_htables[unit + 1]; oiph != NULL; oiph = oiph->iph_next) if (strncmp(oiph->iph_name, name, sizeof(oiph->iph_name)) == 0) break; } while (oiph != NULL); (void)strncpy(iph->iph_name, name, sizeof(iph->iph_name)); (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); iph->iph_type |= IPHASH_ANON; } else { (void)strncpy(iph->iph_name, op->iplo_name, sizeof(iph->iph_name)); iph->iph_name[sizeof(iph->iph_name) - 1] = '\0'; } + if ((iph->iph_size == 0) || + (iph->iph_size > softh->ipf_htable_size_max)) { + IPFERROR(30027); + return (EINVAL); + } + if (iph->iph_size > ( SIZE_MAX / sizeof(*iph->iph_table))) { + IPFERROR(30028); + return (EINVAL); + } KMALLOCS(iph->iph_table, iphtent_t **, iph->iph_size * sizeof(*iph->iph_table)); if (iph->iph_table == NULL) { KFREE(iph); softh->ipht_nomem[unit + 1]++; IPFERROR(30006); return (ENOMEM); } bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); iph->iph_maskset[0] = 0; iph->iph_maskset[1] = 0; iph->iph_maskset[2] = 0; iph->iph_maskset[3] = 0; iph->iph_ref = 1; iph->iph_list = NULL; iph->iph_tail = &iph->iph_list; iph->iph_next = softh->ipf_htables[unit + 1]; iph->iph_pnext = &softh->ipf_htables[unit + 1]; if (softh->ipf_htables[unit + 1] != NULL) softh->ipf_htables[unit + 1]->iph_pnext = &iph->iph_next; softh->ipf_htables[unit + 1] = iph; softh->ipf_nhtables[unit + 1]++; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_table_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_table_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { return (ipf_htable_destroy(softc, arg, op->iplo_unit, op->iplo_name)); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_destroy */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Find the hash table that belongs to the relevant part of ipfilter with a */ /* matching name and attempt to destroy it. If it is in use, empty it out */ /* and mark it for deletion so that when all the references disappear, it */ /* can be removed. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_destroy(ipf_main_softc_t *softc, void *arg, int unit, char *name) { iphtable_t *iph; iph = ipf_htable_find(arg, unit, name); if (iph == NULL) { IPFERROR(30007); return (ESRCH); } if (iph->iph_unit != unit) { IPFERROR(30008); return (EINVAL); } if (iph->iph_ref != 0) { ipf_htable_clear(softc, arg, iph); iph->iph_flags |= IPHASH_DELETE; return (0); } ipf_htable_remove(softc, arg, iph); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_clear */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* Clean out the hash table by walking the list of entries and removing */ /* each one, one by one. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_clear(ipf_main_softc_t *softc, void *arg, iphtable_t *iph) { iphtent_t *ipe; while ((ipe = iph->iph_list) != NULL) if (ipf_htent_remove(softc, arg, iph, ipe) != 0) return (1); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_free */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* ------------------------------------------------------------------------ */ static void ipf_htable_free(void *arg, iphtable_t *iph) { ipf_htable_softc_t *softh = arg; if (iph->iph_next != NULL) iph->iph_next->iph_pnext = iph->iph_pnext; if (iph->iph_pnext != NULL) *iph->iph_pnext = iph->iph_next; iph->iph_pnext = NULL; iph->iph_next = NULL; softh->ipf_nhtables[iph->iph_unit + 1]--; KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); KFREE(iph); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_remove */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* It is necessary to unlink here as well as free (called by deref) so that */ /* the while loop in ipf_htable_flush() functions properly. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_remove(ipf_main_softc_t *softc, void *arg, iphtable_t *iph) { if (ipf_htable_clear(softc, arg, iph) != 0) return (1); if (iph->iph_pnext != NULL) *iph->iph_pnext = iph->iph_next; if (iph->iph_next != NULL) iph->iph_next->iph_pnext = iph->iph_pnext; iph->iph_pnext = NULL; iph->iph_next = NULL; return (ipf_htable_deref(softc, arg, iph)); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - real uid of process doing operation */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_node_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, int uid) { iphtable_t *iph; iphtent_t hte, *ent; int err; if (op->iplo_size != sizeof(hte)) { IPFERROR(30014); return (EINVAL); } err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); if (err != 0) { IPFERROR(30015); return (EFAULT); } iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); if (iph == NULL) { IPFERROR(30016); return (ESRCH); } ent = ipf_htent_find(iph, &hte); if (ent == NULL) { IPFERROR(30022); return (ESRCH); } if ((uid != 0) && (ent->ipe_uid != uid)) { IPFERROR(30023); return (EACCES); } err = ipf_htent_remove(softc, arg, iph, ent); return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_table_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { int err; if (ipf_htable_find(arg, op->iplo_unit, op->iplo_name) != NULL) { IPFERROR(30017); err = EEXIST; } else { err = ipf_htable_create(softc, arg, op); } return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_remove */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table */ /* ipe(I) - pointer to hash table entry to remove */ /* */ /* Delete an entry from a hash table. */ /* ------------------------------------------------------------------------ */ static int ipf_htent_remove(ipf_main_softc_t *softc, void *arg, iphtable_t *iph, iphtent_t *ipe) { if (iph->iph_tail == &ipe->ipe_next) iph->iph_tail = ipe->ipe_pnext; if (ipe->ipe_hnext != NULL) ipe->ipe_hnext->ipe_phnext = ipe->ipe_phnext; if (ipe->ipe_phnext != NULL) *ipe->ipe_phnext = ipe->ipe_hnext; ipe->ipe_phnext = NULL; ipe->ipe_hnext = NULL; if (ipe->ipe_dnext != NULL) ipe->ipe_dnext->ipe_pdnext = ipe->ipe_pdnext; if (ipe->ipe_pdnext != NULL) *ipe->ipe_pdnext = ipe->ipe_dnext; ipe->ipe_pdnext = NULL; ipe->ipe_dnext = NULL; if (ipe->ipe_next != NULL) ipe->ipe_next->ipe_pnext = ipe->ipe_pnext; if (ipe->ipe_pnext != NULL) *ipe->ipe_pnext = ipe->ipe_next; ipe->ipe_pnext = NULL; ipe->ipe_next = NULL; switch (iph->iph_type & ~IPHASH_ANON) { case IPHASH_GROUPMAP : if (ipe->ipe_ptr != NULL) ipf_group_del(softc, ipe->ipe_ptr, NULL); break; default : ipe->ipe_ptr = NULL; ipe->ipe_value = 0; break; } return (ipf_htent_deref(arg, ipe)); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* object(I) - pointer to hash table */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_deref(ipf_main_softc_t *softc, void *arg, void *object) { ipf_htable_softc_t *softh = arg; iphtable_t *iph = object; int refs; iph->iph_ref--; refs = iph->iph_ref; if (iph->iph_ref == 0) { ipf_htable_free(softh, iph); } return (refs); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_deref */ /* Parameters: arg(I) - pointer to local context to use */ /* ipe(I) - */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htent_deref(void *arg, iphtent_t *ipe) { ipf_htable_softc_t *softh = arg; ipe->ipe_ref--; if (ipe->ipe_ref == 0) { softh->ipf_nhtnodes[ipe->ipe_unit + 1]--; KFREE(ipe); return (0); } return (ipe->ipe_ref); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_exists */ /* Parameters: arg(I) - pointer to local context to use */ /* */ /* ------------------------------------------------------------------------ */ static void * ipf_htable_exists(void *arg, int unit, char *name) { ipf_htable_softc_t *softh = arg; iphtable_t *iph; if (unit == IPL_LOGALL) { int i; for (i = 0; i <= LOOKUP_POOL_MAX; i++) { for (iph = softh->ipf_htables[i]; iph != NULL; iph = iph->iph_next) { if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) break; } if (iph != NULL) break; } } else { for (iph = softh->ipf_htables[unit + 1]; iph != NULL; iph = iph->iph_next) { if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) break; } } return (iph); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_select_add_ref */ /* Returns: void * - NULL = failure, else pointer to the hash table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the hash table */ /* */ /* ------------------------------------------------------------------------ */ static void * ipf_htable_select_add_ref(void *arg, int unit, char *name) { iphtable_t *iph; iph = ipf_htable_exists(arg, unit, name); if (iph != NULL) { ATOMIC_INC32(iph->iph_ref); } return (iph); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_find */ /* Returns: void * - NULL = failure, else pointer to the hash table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the hash table */ /* */ /* This function is exposed becaues it is used in the group-map feature. */ /* ------------------------------------------------------------------------ */ iphtable_t * ipf_htable_find(void *arg, int unit, char *name) { iphtable_t *iph; iph = ipf_htable_exists(arg, unit, name); if ((iph != NULL) && (iph->iph_flags & IPHASH_DELETE) == 0) return (iph); return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_flush */ /* Returns: size_t - number of entries flushed */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static size_t ipf_htable_flush(ipf_main_softc_t *softc, void *arg, iplookupflush_t *op) { ipf_htable_softc_t *softh = arg; iphtable_t *iph; size_t freed; int i; freed = 0; for (i = -1; i <= IPL_LOGMAX; i++) { if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) { while ((iph = softh->ipf_htables[i + 1]) != NULL) { if (ipf_htable_remove(softc, arg, iph) == 0) { freed++; } else { iph->iph_flags |= IPHASH_DELETE; } } } } return (freed); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - real uid of process doing operation */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_node_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, int uid) { iphtable_t *iph; iphtent_t hte; int err; if (op->iplo_size != sizeof(hte)) { IPFERROR(30018); return (EINVAL); } err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); if (err != 0) { IPFERROR(30019); return (EFAULT); } hte.ipe_uid = uid; iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); if (iph == NULL) { IPFERROR(30020); return (ESRCH); } if (ipf_htent_find(iph, &hte) != NULL) { IPFERROR(30021); return (EEXIST); } err = ipf_htent_insert(softc, arg, iph, &hte); return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_insert */ /* Returns: int - 0 = success, -1 = error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* ipeo(I) - */ /* */ /* Add an entry to a hash table. */ /* ------------------------------------------------------------------------ */ static int ipf_htent_insert(ipf_main_softc_t *softc, void *arg, iphtable_t *iph, iphtent_t *ipeo) { ipf_htable_softc_t *softh = arg; iphtent_t *ipe; u_int hv; int bits; KMALLOC(ipe, iphtent_t *); if (ipe == NULL) return (-1); bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe)); ipe->ipe_addr.i6[0] &= ipe->ipe_mask.i6[0]; if (ipe->ipe_family == AF_INET) { bits = count4bits(ipe->ipe_mask.in4_addr); ipe->ipe_addr.i6[1] = 0; ipe->ipe_addr.i6[2] = 0; ipe->ipe_addr.i6[3] = 0; ipe->ipe_mask.i6[1] = 0; ipe->ipe_mask.i6[2] = 0; ipe->ipe_mask.i6[3] = 0; hv = IPE_V4_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr, iph->iph_size); } else #ifdef USE_INET6 if (ipe->ipe_family == AF_INET6) { ipe->ipe_addr.i6[1] &= ipe->ipe_mask.i6[1]; ipe->ipe_addr.i6[2] &= ipe->ipe_mask.i6[2]; ipe->ipe_addr.i6[3] &= ipe->ipe_mask.i6[3]; bits = count6bits(ipe->ipe_mask.i6); hv = IPE_V6_HASH_FN(ipe->ipe_addr.i6, ipe->ipe_mask.i6, iph->iph_size); } else #endif { KFREE(ipe); return (-1); } ipe->ipe_owner = iph; ipe->ipe_ref = 1; ipe->ipe_hnext = iph->iph_table[hv]; ipe->ipe_phnext = iph->iph_table + hv; if (iph->iph_table[hv] != NULL) iph->iph_table[hv]->ipe_phnext = &ipe->ipe_hnext; iph->iph_table[hv] = ipe; ipe->ipe_pnext = iph->iph_tail; *iph->iph_tail = ipe; iph->iph_tail = &ipe->ipe_next; ipe->ipe_next = NULL; if (ipe->ipe_die != 0) { /* * If the new node has a given expiration time, insert it * into the list of expiring nodes with the ones to be * removed first added to the front of the list. The * insertion is O(n) but it is kept sorted for quick scans * at expiration interval checks. */ iphtent_t *n; ipe->ipe_die = softc->ipf_ticks + IPF_TTLVAL(ipe->ipe_die); for (n = softh->ipf_node_explist; n != NULL; n = n->ipe_dnext) { if (ipe->ipe_die < n->ipe_die) break; if (n->ipe_dnext == NULL) { /* * We've got to the last node and everything * wanted to be expired before this new node, * so we have to tack it on the end... */ n->ipe_dnext = ipe; ipe->ipe_pdnext = &n->ipe_dnext; n = NULL; break; } } if (softh->ipf_node_explist == NULL) { softh->ipf_node_explist = ipe; ipe->ipe_pdnext = &softh->ipf_node_explist; } else if (n != NULL) { ipe->ipe_dnext = n; ipe->ipe_pdnext = n->ipe_pdnext; n->ipe_pdnext = &ipe->ipe_dnext; } } if (ipe->ipe_family == AF_INET) { ipf_inet_mask_add(bits, &iph->iph_v4_masks); } #ifdef USE_INET6 else if (ipe->ipe_family == AF_INET6) { ipf_inet6_mask_add(bits, &ipe->ipe_mask, &iph->iph_v6_masks); } #endif switch (iph->iph_type & ~IPHASH_ANON) { case IPHASH_GROUPMAP : ipe->ipe_ptr = ipf_group_add(softc, ipe->ipe_group, NULL, iph->iph_flags, IPL_LOGIPF, softc->ipf_active); break; default : ipe->ipe_ptr = NULL; ipe->ipe_value = 0; break; } ipe->ipe_unit = iph->iph_unit; softh->ipf_nhtnodes[ipe->ipe_unit + 1]++; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_find */ /* Returns: int - 0 = success, else error */ /* Parameters: iph(I) - pointer to table to search */ /* ipeo(I) - pointer to entry to find */ /* */ /* While it isn't absolutely necessary to for the address and mask to be */ /* passed in through an iphtent_t structure, one is always present when it */ /* is time to call this function, so it is just more convenient. */ /* ------------------------------------------------------------------------ */ static iphtent_t * ipf_htent_find(iphtable_t *iph, iphtent_t *ipeo) { iphtent_t ipe, *ent; u_int hv; bcopy((char *)ipeo, (char *)&ipe, sizeof(ipe)); ipe.ipe_addr.i6[0] &= ipe.ipe_mask.i6[0]; ipe.ipe_addr.i6[1] &= ipe.ipe_mask.i6[1]; ipe.ipe_addr.i6[2] &= ipe.ipe_mask.i6[2]; ipe.ipe_addr.i6[3] &= ipe.ipe_mask.i6[3]; if (ipe.ipe_family == AF_INET) { ipe.ipe_addr.i6[1] = 0; ipe.ipe_addr.i6[2] = 0; ipe.ipe_addr.i6[3] = 0; ipe.ipe_mask.i6[1] = 0; ipe.ipe_mask.i6[2] = 0; ipe.ipe_mask.i6[3] = 0; hv = IPE_V4_HASH_FN(ipe.ipe_addr.in4_addr, ipe.ipe_mask.in4_addr, iph->iph_size); } else #ifdef USE_INET6 if (ipe.ipe_family == AF_INET6) { hv = IPE_V6_HASH_FN(ipe.ipe_addr.i6, ipe.ipe_mask.i6, iph->iph_size); } else #endif return (NULL); for (ent = iph->iph_table[hv]; ent != NULL; ent = ent->ipe_hnext) { if (ent->ipe_family != ipe.ipe_family) continue; if (IP6_NEQ(&ipe.ipe_addr, &ent->ipe_addr)) continue; if (IP6_NEQ(&ipe.ipe_mask, &ent->ipe_mask)) continue; break; } return (ent); } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindgroup */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tptr(I) - */ /* aptr(I) - */ /* */ /* Search a hash table for a matching entry and return the pointer stored */ /* in it for use as the next group of rules to search. */ /* */ /* This function is exposed becaues it is used in the group-map feature. */ /* ------------------------------------------------------------------------ */ void * ipf_iphmfindgroup(ipf_main_softc_t *softc, void *tptr, void *aptr) { struct in_addr *addr; iphtable_t *iph; iphtent_t *ipe; void *rval; READ_ENTER(&softc->ipf_poolrw); iph = tptr; addr = aptr; ipe = ipf_iphmfind(iph, addr); if (ipe != NULL) rval = ipe->ipe_ptr; else rval = NULL; RWLOCK_EXIT(&softc->ipf_poolrw); return (rval); } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindip */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tptr(I) - pointer to the pool to search */ /* ipversion(I) - IP protocol version (4 or 6) */ /* aptr(I) - pointer to address information */ /* bytes(I) - packet length */ /* */ /* Search the hash table for a given address and return a search result. */ /* ------------------------------------------------------------------------ */ static int ipf_iphmfindip(ipf_main_softc_t *softc, void *tptr, int ipversion, void *aptr, u_int bytes) { struct in_addr *addr; iphtable_t *iph; iphtent_t *ipe; int rval; if (tptr == NULL || aptr == NULL) return (-1); iph = tptr; addr = aptr; READ_ENTER(&softc->ipf_poolrw); if (ipversion == 4) { ipe = ipf_iphmfind(iph, addr); #ifdef USE_INET6 } else if (ipversion == 6) { ipe = ipf_iphmfind6(iph, (i6addr_t *)addr); #endif } else { ipe = NULL; } if (ipe != NULL) { rval = 0; ipe->ipe_hits++; ipe->ipe_bytes += bytes; } else { rval = 1; } RWLOCK_EXIT(&softc->ipf_poolrw); return (rval); } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindip */ /* Parameters: iph(I) - pointer to hash table */ /* addr(I) - pointer to IPv4 address */ /* Locks: ipf_poolrw */ /* */ /* ------------------------------------------------------------------------ */ static iphtent_t * ipf_iphmfind(iphtable_t *iph, struct in_addr *addr) { u_32_t msk, ips; iphtent_t *ipe; u_int hv; int i; i = 0; maskloop: msk = iph->iph_v4_masks.imt4_active[i]; ips = addr->s_addr & msk; hv = IPE_V4_HASH_FN(ips, msk, iph->iph_size); for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_hnext) { if ((ipe->ipe_family != AF_INET) || (ipe->ipe_mask.in4_addr != msk) || (ipe->ipe_addr.in4_addr != ips)) { continue; } break; } if (ipe == NULL) { i++; if (i < iph->iph_v4_masks.imt4_max) goto maskloop; } return (ipe); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_iter_next */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* token(I) - */ /* ilp(I) - */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_iter_next(ipf_main_softc_t *softc, void *arg, ipftoken_t *token, ipflookupiter_t *ilp) { ipf_htable_softc_t *softh = arg; iphtent_t *node, zn, *nextnode; iphtable_t *iph, zp, *nextiph; void *hnext; int err; err = 0; iph = NULL; node = NULL; nextiph = NULL; nextnode = NULL; READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : iph = token->ipt_data; if (iph == NULL) { nextiph = softh->ipf_htables[(int)ilp->ili_unit + 1]; } else { nextiph = iph->iph_next; } if (nextiph != NULL) { ATOMIC_INC(nextiph->iph_ref); token->ipt_data = nextiph; } else { bzero((char *)&zp, sizeof(zp)); nextiph = &zp; token->ipt_data = NULL; } hnext = nextiph->iph_next; break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { iph = ipf_htable_find(arg, ilp->ili_unit, ilp->ili_name); if (iph == NULL) { IPFERROR(30009); err = ESRCH; } else { nextnode = iph->iph_list; } } else { nextnode = node->ipe_next; } if (nextnode != NULL) { ATOMIC_INC(nextnode->ipe_ref); token->ipt_data = nextnode; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; } hnext = nextnode->ipe_next; break; default : IPFERROR(30010); err = EINVAL; hnext = NULL; break; } RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return (err); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : err = COPYOUT(nextiph, ilp->ili_data, sizeof(*nextiph)); if (err != 0) { IPFERROR(30011); err = EFAULT; } if (iph != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_htable_deref(softc, softh, iph); RWLOCK_EXIT(&softc->ipf_poolrw); } break; case IPFLOOKUPITER_NODE : err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); if (err != 0) { IPFERROR(30012); err = EFAULT; } if (node != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_htent_deref(softc, node); RWLOCK_EXIT(&softc->ipf_poolrw); } break; } if (hnext == NULL) ipf_token_mark_complete(token); return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_iter_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* otype(I) - which data structure type is being walked */ /* unit(I) - ipfilter device to which we are working on */ /* data(I) - pointer to old data structure */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_htable_iter_deref(ipf_main_softc_t *softc, void *arg, int otype, int unit, void *data) { if (data == NULL) return (EFAULT); if (unit < -1 || unit > IPL_LOGMAX) return (EINVAL); switch (otype) { case IPFLOOKUPITER_LIST : ipf_htable_deref(softc, arg, (iphtable_t *)data); break; case IPFLOOKUPITER_NODE : ipf_htent_deref(arg, (iphtent_t *)data); break; default : break; } return (0); } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfind6 */ /* Parameters: iph(I) - pointer to hash table */ /* addr(I) - pointer to IPv6 address */ /* Locks: ipf_poolrw */ /* */ /* ------------------------------------------------------------------------ */ static iphtent_t * ipf_iphmfind6(iphtable_t *iph, i6addr_t *addr) { i6addr_t *msk, ips; iphtent_t *ipe; u_int hv; int i; i = 0; maskloop: msk = iph->iph_v6_masks.imt6_active + i; ips.i6[0] = addr->i6[0] & msk->i6[0]; ips.i6[1] = addr->i6[1] & msk->i6[1]; ips.i6[2] = addr->i6[2] & msk->i6[2]; ips.i6[3] = addr->i6[3] & msk->i6[3]; hv = IPE_V6_HASH_FN(ips.i6, msk->i6, iph->iph_size); for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_next) { if ((ipe->ipe_family != AF_INET6) || IP6_NEQ(&ipe->ipe_mask, msk) || IP6_NEQ(&ipe->ipe_addr, &ips)) { continue; } break; } if (ipe == NULL) { i++; if (i < iph->iph_v6_masks.imt6_max) goto maskloop; } return (ipe); } #endif static void ipf_htable_expire(ipf_main_softc_t *softc, void *arg) { ipf_htable_softc_t *softh = arg; iphtent_t *n; while ((n = softh->ipf_node_explist) != NULL) { if (n->ipe_die > softc->ipf_ticks) break; ipf_htent_remove(softc, softh, n->ipe_owner, n); } } #ifndef _KERNEL /* ------------------------------------------------------------------------ */ /* */ /* ------------------------------------------------------------------------ */ void ipf_htable_dump(ipf_main_softc_t *softc, void *arg) { ipf_htable_softc_t *softh = arg; iphtable_t *iph; int i; printf("List of configured hash tables\n"); for (i = 0; i < IPL_LOGSIZE; i++) for (iph = softh->ipf_htables[i]; iph != NULL; iph = iph->iph_next) printhash(iph, bcopywrap, NULL, opts, NULL); } #endif