src/uts/common/inet/ipf/solaris.c: kernel driver, which provides the entry points to - attach/detach the filter (see ip_fil_solaris.c::ipl{attach|detach}), - read/write (see ip_fil_solaris.c::ipl{read|write}) for logging as well as - the ioctl (see ip_fil_solaris.c::iplioctl), which can be used to control/call ipfilter functions. It is mainly used by ipf and related command line tools to add/modify/remove rules and retrieve required infos. src/uts/common/inet/ipf/ip_fil_solaris.c: on iplattach() several hooks (see src/uts/common/sys/hook.h) are registered so that appropriate actions can be taken, if the ip stack changes (e.g. an interface comes up/goes down/gets a new IP address etc.) or a packet comes in/goes out (see Event 'identification in' src/uts/common/sys/neti.h). The most important is the ipf_hook(), which gets called, if a packet goes in or out. This in turn calls src/uts/common/inet/ipf/fil.c::fr_check(), which actually does the filtering and kstat updates. See also hook_nic_event(9S), hook_pkt_event(9S) src/uts/common/inet/ipf/fil.c fr_check() is usually the entry point for filtering. Basically it: - extracts all required ipheader information into a more compact structure via fr_makefrip() - verifies the source address and ttl, - checks packets auth data (if available) - determines for incomining packets, whether NAT is needed (see src/uts/common/inet/ipf/ip_nat.c::fr_checknatin) and translates if so - checks an incoming packet against accounting rules via fr_acctpkt() if there are any (accouting rules are kept in a separate rule set) - checks the fragment cache for a match via fr_knownfrag() - checks the state table for a match - calls fr_firewall(), which basically determines the rule set to use (v4|v6,in|out) and calls fr_scanlist(), which actually processes the packet wrt. to the determined rule set. Note that fr_scanlist() makes recursive calls if a rule branches to another group. However, a hard coded limit of 16 calls to itselfs avoid inifinite recursion. The most important thing in fr_scanlist() is the call to fr_ipfcheck(), which actually does the check against the passed rule. fr_ipfcheck() does the check in the following order until nomatch or EOF: - check src address via fr->fr_srcptr() or direct if non-pool address - check dst address via fr->fr_dstptr() or direct if non-pool address - proto|port checks via fr_tcpudpchk() - compare destination ports via fr_portcheck() - compare source ports via fr_portcheck() - check tcp flags using ft->ftu_tcpf and ft->ftu_tcpfm and returns on match the corresponding rule. - checks an outgoing packet against accounting rules via fr_acctpkt() - determines for outgoing packets, whether NAT is needed (see src/uts/common/inet/ipf/ip_nat.c::fr_checknatout) and translates if so - logs the packet if required - duplicates the packet if required - creates and sends appropriate icmp/reset message if the packet gets blocked if desired - invokes fr_fastroute() if appropriate - and finally copies remaining buffered modifications back to the packet Finally the control gets passed back to the network protocol handler, which called the ipf_hook() callback function. How are rules added? rules from ipf.conf are parsed in by src/cmd/ipf/tools/ipf_y.y and added via its ipf_addrule(). Under the hood it is using the ipf kernel driver's ioctl functions (see src/uts/common/inet/ipf/ip_fil_solaris.c::iplioctl()) to do the required work. This usually ends up in src/uts/common/inet/ipf/fil.c::frrequest(), which copies the required rules/date into the kernel space. Ipfilter maintains 3 groups (units) of filter sets: one for authentification, one for accounting and one for filtering. The one interesting part here is fr_resolvelookup(), which binds the appropriate hash|tree search function to the rule, if the address lookup type parameter indicates the use of such a pool (see src/uts/common/inet/ipf/ip_pool.c::ip_pool_find() and ip_pool_search() as well as src/uts/common/inet/ipf/ip_htable.c::fr_findhtable() and fr_iphmfindip() ). .