ip_nat.c revision 0e01ff8b60eacad199d1851b2e00217729e81640
/*
* Copyright (C) 1995-2003 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"$
# define KERNEL 1
# define _KERNEL 1
#endif
defined(_KERNEL)
# include "opt_ipfilter_log.h"
#endif
#if !defined(_KERNEL)
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# define _KERNEL
# ifdef __OpenBSD__
struct file;
# endif
#endif
#else
#endif
#if !defined(AIX)
#endif
#if !defined(linux)
#endif
#if defined(_KERNEL)
# endif
#endif
# include <sys/byteorder.h>
# ifdef _KERNEL
# include <sys/dditypes.h>
# endif
#endif
#if __FreeBSD_version >= 300000
#endif
#if __FreeBSD_version >= 300000
# if defined(_KERNEL) && !defined(IPFILTER_LKM)
# include "opt_ipfilter.h"
# endif
#endif
#ifdef sun
#endif
#include <netinet/in_systm.h>
#ifdef RFC1825
#endif
#if !defined(linux)
#endif
#include "netinet/ip_compat.h"
#include "netinet/ip_state.h"
#include "netinet/ip_proxy.h"
#include "netinet/ipf_stack.h"
#ifdef IPFILTER_SYNC
#endif
#if (__FreeBSD_version >= 300000)
#endif
/* END OF INCLUDES */
#define SOCKADDR_IN struct sockaddr_in
#if !defined(lint)
#endif
/* ======================================================================== */
/* How the NAT is organised and works. */
/* */
/* Inside (interface y) NAT Outside (interface x) */
/* -------------------- -+- ------------------------------------- */
/* Packet going | out, processsed by fr_checknatout() for x */
/* ------------> | ------------> */
/* src=10.1.1.1 | src=192.1.1.1 */
/* | */
/* | in, processed by fr_checknatin() for x */
/* <------------ | <------------ */
/* dst=10.1.1.1 | dst=192.1.1.1 */
/* -------------------- -+- ------------------------------------- */
/* fr_checknatout() - changes ip_src and if required, sport */
/* - creates a new mapping, if required. */
/* fr_checknatin() - changes ip_dst and if required, dport */
/* */
/* In the NAT table, internal source is recorded as "in" and externally */
/* seen as "out". */
/* ======================================================================== */
ipf_stack_t *));
ipf_stack_t *));
ipf_stack_t *));
/*
* Below we declare a list of constants used only in the nat_extraflush()
* routine. We are placing it here, instead of in nat_extraflush() itself,
* because we want to make it visible to tools such as mdb, nm etc., so the
* values can easily be altered during debugging.
*/
static const int idletime_tab[] = {
};
/* ------------------------------------------------------------------------ */
/* Function: fr_natinit */
/* Returns: int - 0 == success, -1 == failure */
/* Parameters: Nil */
/* */
/* Initialise all of the NAT locks, tables and other structures. */
/* ------------------------------------------------------------------------ */
int fr_natinit(ifs)
{
int i;
else
return -1;
else
return -2;
else
return -3;
else
return -4;
else
return -5;
return -1;
return -1;
if (ifs->ifs_fr_nat_maxbucket == 0) {
}
/*
* Increase this because we may have "keep state" following this too
* and packet storms can occur if this is removed too quickly.
*/
for (i = 0; i < IPF_TCP_NSTATES; i++) {
#ifdef LARGE_NAT
#endif
}
/*
* Increase this because we may have "keep state" following
* this too and packet storms can occur if this is removed
* too quickly.
*/
return 0;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_addrdr */
/* Returns: Nil */
/* Parameters: n(I) - pointer to NAT rule to add */
/* */
/* Adds a redirect rule to the hash table of redirect rules and the list of */
/* loaded NAT rules. Updates the bitmask indicating which netmasks are in */
/* use by redirect rules. */
/* ------------------------------------------------------------------------ */
static void nat_addrdr(n, ifs)
ipnat_t *n;
{
u_32_t j;
int k;
k = count4bits(n->in_outmsk);
if ((k >= 0) && (k != 32))
*np = n;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_addnat */
/* Returns: Nil */
/* Parameters: n(I) - pointer to NAT rule to add */
/* */
/* Adds a NAT map rule to the hash table of rules and the list of loaded */
/* NAT rules. Updates the bitmask indicating which netmasks are in use by */
/* redirect rules. */
/* ------------------------------------------------------------------------ */
static void nat_addnat(n, ifs)
ipnat_t *n;
{
u_32_t j;
int k;
k = count4bits(n->in_inmsk);
if ((k >= 0) && (k != 32))
*np = n;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_delrdr */
/* Returns: Nil */
/* Parameters: n(I) - pointer to NAT rule to delete */
/* */
/* Removes a redirect rule from the hash table of redirect rules. */
/* ------------------------------------------------------------------------ */
static void nat_delrdr(n)
ipnat_t *n;
{
if (n->in_rnext)
}
/* ------------------------------------------------------------------------ */
/* Function: nat_delnat */
/* Returns: Nil */
/* Parameters: n(I) - pointer to NAT rule to delete */
/* */
/* Removes a NAT map rule from the hash table of NAT map rules. */
/* ------------------------------------------------------------------------ */
static void nat_delnat(n)
ipnat_t *n;
{
}
/* ------------------------------------------------------------------------ */
/* Function: nat_hostmap */
/* Returns: struct hostmap* - NULL if no hostmap could be created, */
/* else a pointer to the hostmapping to use */
/* Parameters: np(I) - pointer to NAT rule */
/* real(I) - real IP address */
/* map(I) - mapped IP address */
/* port(I) - destination port number */
/* Write Locks: ipf_nat */
/* */
/* Check if an ip address has already been allocated for a given mapping */
/* that is not doing port based translation. If is not yet allocated, then */
/* create a new entry if a non-NULL NAT rule pointer has been supplied. */
/* ------------------------------------------------------------------------ */
{
hv %= HOSTMAP_SIZE;
return hm;
}
return NULL;
if (hm) {
}
return hm;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_hostmapdel */
/* Returns: Nil */
/* Parameters: hm(I) - pointer to hostmap structure */
/* Write Locks: ipf_nat */
/* */
/* Decrement the references to this hostmap structure by one. If this */
/* reaches zero then remove it and free it. */
/* ------------------------------------------------------------------------ */
static void nat_hostmapdel(hm)
{
}
}
void fr_hostmapderef(hmp)
{
}
/* ------------------------------------------------------------------------ */
/* Function: fix_outcksum */
/* Returns: Nil */
/* Parameters: sp(I) - location of 16bit checksum to update */
/* n((I) - amount to adjust checksum by */
/* */
/* Adjusts the 16bit checksum by "n" for packets going out. */
/* ------------------------------------------------------------------------ */
void fix_outcksum(sp, n)
u_32_t n;
{
if (n == 0)
return;
sum1 += (n);
/* Again */
}
/* ------------------------------------------------------------------------ */
/* Function: fix_incksum */
/* Returns: Nil */
/* Parameters: sp(I) - location of 16bit checksum to update */
/* n((I) - amount to adjust checksum by */
/* */
/* Adjusts the 16bit checksum by "n" for packets going in. */
/* ------------------------------------------------------------------------ */
void fix_incksum(sp, n)
u_32_t n;
{
if (n == 0)
return;
sum1 += ~(n) & 0xffff;
/* Again */
}
/* ------------------------------------------------------------------------ */
/* Function: fix_datacksum */
/* Returns: Nil */
/* Parameters: sp(I) - location of 16bit checksum to update */
/* n((I) - amount to adjust checksum by */
/* */
/* Fix_datacksum is used *only* for the adjustments of checksums in the */
/* data section of an IP packet. */
/* */
/* The only situation in which you need to do this is when NAT'ing an */
/* ICMP error message. Such a message, contains in its body the IP header */
/* of the original IP packet, that causes the error. */
/* */
/* You can't use fix_incksum or fix_outcksum in that case, because for the */
/* kernel the data section of the ICMP error is just data, and no special */
/* processing like hardware cksum or ntohs processing have been done by the */
/* kernel on the data section. */
/* ------------------------------------------------------------------------ */
void fix_datacksum(sp, n)
u_32_t n;
{
if (n == 0)
return;
sum1 += (n);
/* Again */
}
/* ------------------------------------------------------------------------ */
/* Function: fr_nat_ioctl */
/* Returns: int - 0 == success, != 0 == failure */
/* Parameters: data(I) - pointer to ioctl data */
/* cmd(I) - ioctl command integer */
/* mode(I) - file mode bits used with open */
/* */
/* Processes an ioctl call made to operate on the IP Filter NAT device. */
/* ------------------------------------------------------------------------ */
void *ctx;
{
return EPERM;
#endif
getlock = 0;
#else
#endif
} else {
}
if (mode & NAT_SYSSPACE) {
error = 0;
} else {
}
}
if (error != 0)
goto done;
/*
*/
}
break;
}
switch (cmd)
{
case SIOCGENITER :
{
if (error != 0)
break;
else
break;
}
#ifdef IPFILTER_LOG
case SIOCIPFFB :
{
int tmp;
else {
}
break;
}
case SIOCSETLG :
else {
(char *)&ifs->ifs_nat_logging,
sizeof(ifs->ifs_nat_logging));
}
break;
case SIOCGETLG :
sizeof(ifs->ifs_nat_logging));
break;
case FIONREAD :
break;
#endif
case SIOCADNAT :
} else if (n != NULL) {
}
if (error != 0) {
break;
}
if (error == 0)
break;
case SIOCRMNAT :
n = NULL;
} else if (n == NULL) {
}
if (error != 0) {
break;
}
n = NULL;
break;
case SIOCGNATS :
break;
case SIOCGNATL :
{
if (getlock) {
}
if (error == 0) {
} else {
}
}
if (getlock) {
}
break;
}
case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */
break;
}
if (getlock) {
}
error = 0;
if (arg == 0)
else if (arg == 1)
else
if (getlock) {
}
if (error == 0) {
}
break;
case SIOCPROXY :
break;
case SIOCSTLCK :
} else {
}
break;
case SIOCSTPUT :
} else {
}
break;
case SIOCSTGSZ :
if (ifs->ifs_fr_nat_lock) {
if (getlock) {
}
if (getlock) {
}
} else
break;
case SIOCSTGET :
if (ifs->ifs_fr_nat_lock) {
if (getlock) {
}
if (getlock) {
}
} else
break;
case SIOCIPFDELTOK :
break;
default :
break;
}
done:
if (nt)
return error;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_siocaddnat */
/* Returns: int - 0 == success, != 0 == failure */
/* Parameters: n(I) - pointer to new NAT rule */
/* np(I) - pointer to where to insert new NAT rule */
/* getlock(I) - flag indicating if lock on ipf_nat is held */
/* Mutex Locks: ipf_natio */
/* */
/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */
/* from information passed to the kernel, then add it to the appropriate */
/* NAT rule table(s). */
/* ------------------------------------------------------------------------ */
int getlock;
{
int error = 0, i, j;
if (nat_resolverule(n, ifs) != 0)
return ENOENT;
return EINVAL;
n->in_use = 0;
if (n->in_redir & NAT_MAPBLK)
else if (n->in_flags & IPN_AUTOPORTMAP)
else if (n->in_flags & IPN_IPRANGE)
n->in_space = 2;
else if (n->in_outmsk != 0)
else
n->in_space = 1;
/*
* Calculate the number of valid IP addresses in the output
* mapping range. In all cases, the range is inclusive of
* the start and ending IP addresses.
* If to a CIDR address, lose 2: broadcast + network address
* (so subtract 1)
* If to a range, add one.
* If to a single IP address, set to 1.
*/
if (n->in_space) {
if ((n->in_flags & IPN_IPRANGE) != 0)
n->in_space += 1;
else
n->in_space -= 1;
} else
n->in_space = 1;
(n->in_redir & NAT_REDIRECT))
else
/*
* Multiply by the number of ports made available.
*/
/*
* Because two different sources can map to
* different destinations but use the same
* local IP#/port #.
* If the result is smaller than in_space, then
* we may have wrapped around 32bits.
*/
i = n->in_inmsk;
if ((i != 0) && (i != 0xffffffff)) {
if (j >= n->in_space)
n->in_space = j;
else
n->in_space = 0xffffffff;
}
}
/*
* If no protocol is specified, multiple by 256 to allow for
* at least one IP:IP mapping per protocol.
*/
if ((n->in_flags & IPN_TCPUDPICMP) == 0) {
j = n->in_space * 256;
if (j >= n->in_space)
n->in_space = j;
else
n->in_space = 0xffffffff;
}
}
/* Otherwise, these fields are preset */
if (getlock) {
}
*np = n;
if (n->in_age[0] != 0)
if (n->in_age[1] != 0)
if (n->in_redir & NAT_REDIRECT) {
n->in_flags &= ~IPN_NOTDST;
nat_addrdr(n, ifs);
}
n->in_flags &= ~IPN_NOTSRC;
nat_addnat(n, ifs);
}
n = NULL;
if (getlock) {
}
return error;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_resolvrule */
/* Returns: int - 0 == success, -1 == failure */
/* Parameters: n(I) - pointer to NAT rule */
/* */
/* Resolve some of the details inside the NAT rule. Includes resolving */
/* any specified interfaces and proxy labels, and determines whether or not */
/* all proxy labels are correctly specified. */
/* */
/* Called by nat_siocaddnat() (SIOCADNAT) and fr_natputent (SIOCSTPUT). */
/* ------------------------------------------------------------------------ */
static int nat_resolverule(n, ifs)
ipnat_t *n;
{
} else {
}
if (n->in_plabel[0] != '\0') {
return -1;
}
return 0;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_siocdelnat */
/* Returns: int - 0 == success, != 0 == failure */
/* Parameters: n(I) - pointer to new NAT rule */
/* np(I) - pointer to where to insert new NAT rule */
/* getlock(I) - flag indicating if lock on ipf_nat is held */
/* Mutex Locks: ipf_natio */
/* */
/* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */
/* from information passed to the kernel, then add it to the appropriate */
/* NAT rule table(s). */
/* ------------------------------------------------------------------------ */
int getlock;
{
if (getlock) {
}
if (n->in_redir & NAT_REDIRECT)
nat_delrdr(n);
nat_delnat(n);
ifs->ifs_nat_masks = 0;
ifs->ifs_rdr_masks = 0;
}
if (n->in_tqehead[0] != NULL) {
if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
}
}
}
}
if (n->in_use == 0) {
if (n->in_apr)
KFREE(n);
} else {
n->in_flags |= IPN_DELETE;
}
if (getlock) {
}
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natgetsz */
/* Returns: int - 0 == success, != 0 is the error value. */
/* Parameters: data(I) - pointer to natget structure with kernel pointer */
/* get the size of. */
/* */
/* Handle SIOCSTGSZ. */
/* Return the size of the nat list entry to be copied back to user space. */
/* The size of the entry is stored in the ng_sz field and the enture natget */
/* structure is copied back to the user. */
/* ------------------------------------------------------------------------ */
{
if (!nat) {
/*
* Empty list so the size returned is 0. Simple.
*/
return 0;
}
} else {
/*
* Make sure the pointer we're copying from exists in the
* current list of entries. Security precaution to prevent
* copying of random kernel data.
*/
if (n == nat)
break;
if (!n)
return ESRCH;
}
/*
* Incluse any space required for proxy data structures.
*/
}
return 0;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natgetent */
/* Returns: int - 0 == success, != 0 is the error value. */
/* Parameters: data(I) - pointer to natget structure with kernel pointer */
/* to NAT structure to copy out. */
/* */
/* Handle SIOCSTGET. */
/* Copies out NAT entry to user space. Any additional data held for a */
/* proxy is also copied, as to is the NAT rule which was responsible for it */
/* ------------------------------------------------------------------------ */
{
if (error != 0)
return error;
return EINVAL;
return ENOMEM;
goto finished;
}
} else {
/*
* Make sure the pointer we're copying from exists in the
* current list of entries. Security precaution to prevent
* copying of random kernel data.
*/
if (n == nat)
break;
if (n == NULL) {
goto finished;
}
}
/*
* Copy the NAT structure.
*/
/*
* If we have a pointer to the NAT rule it belongs to, save that too.
*/
/*
* If we also know the NAT entry has an associated filter rule,
* save that too.
*/
/*
* Last but not least, if there is an application proxy session set
* up for this NAT entry, then copy that out too, including any
* private data saved along side it by the proxy.
*/
char *s;
goto finished;
}
s += sizeof(*aps);
else
}
if (error == 0) {
}
}
return error;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natputent */
/* Returns: int - 0 == success, != 0 is the error value. */
/* Parameters: data(I) - pointer to natget structure with NAT */
/* structure information to load into the kernel */
/* getlock(I) - flag indicating whether or not a write lock */
/* on ipf_nat is already held. */
/* */
/* Handle SIOCSTPUT. */
/* Loads a NAT table entry from user space, including a NAT rule, proxy and */
/* firewall rule data structures, if pointers to them indicate so. */
/* ------------------------------------------------------------------------ */
int getlock;
{
int error;
if (error != 0)
return error;
/*
* Trigger automatic call to nat_extraflush() if the
* table has reached capcity specified by hi watermark.
*/
/*
* Initialise early because of code at junkput label.
*/
/*
* New entry, copy in the rest of the NAT entry if it's size is more
* than just the nat_t structure.
*/
goto junkput;
}
return ENOMEM;
if (error != 0) {
goto junkput;
}
} else
goto junkput;
}
/*
* Initialize all these so that nat_delete() doesn't cause a crash.
*/
/*
* Restore the rule associated with this nat session
*/
goto junkput;
}
goto junkput;
}
}
/*
* Check that the NAT entry doesn't already exist in the kernel.
*/
if (getlock) {
}
if (getlock) {
}
if (n != NULL) {
goto junkput;
}
if (getlock) {
}
if (getlock) {
}
if (n != NULL) {
goto junkput;
}
} else {
goto junkput;
}
/*
* Restore ap_session_t structure. Include the private data allocated
* if it was there.
*/
goto junkput;
}
else
goto junkput;
}
goto junkput;
}
} else {
}
}
/*
* If there was a filtering rule associated with this entry then
* build up a new one.
*/
goto junkput;
}
} else {
if (getlock) {
}
break;
if (n != NULL) {
}
if (getlock) {
}
if (!n) {
goto junkput;
}
}
}
}
if (getlock) {
}
}
if (getlock) {
}
if (error == 0)
return 0;
}
}
}
}
}
return error;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_delete */
/* Returns: Nil */
/* Parameters: natd(I) - pointer to NAT structure to delete */
/* logtype(I) - type of LOG record to create before deleting */
/* Write Lock: ipf_nat */
/* */
/* Delete a nat entry from the various lists and table. If NAT logging is */
/* enabled then generate a NAT log record for this event. */
/* ------------------------------------------------------------------------ */
int logtype;
{
/*
* Take it as a general indication that all the pointers are set if
* nat_pnext is set.
*/
}
}
}
}
}
return;
}
/*
* At this point, nat_ref is 1, doing "--" would make it 0..
*/
#ifdef IPFILTER_SYNC
#endif
/*
* If there is an active reference from the nat entry to its parent
* rule, decrement the rule's reference count and free it too if no
* longer being used.
*/
}
}
/*
* If there's a fragment table entry too for this nat entry, then
* dereference that as well. This is after nat_lock is released
* because of Tru64.
*/
}
/* ------------------------------------------------------------------------ */
/* Function: nat_flushtable */
/* Returns: int - number of NAT rules deleted */
/* Parameters: Nil */
/* */
/* Deletes all currently active NAT sessions. In deleting each NAT entry a */
/* log record should be emitted in nat_delete() if NAT logging is enabled. */
/* ------------------------------------------------------------------------ */
/*
* nat_flushtable - clear the NAT table of all mapping entries.
*/
static int nat_flushtable(ifs)
{
int j = 0;
/*
* ALL NAT mappings deleted, so lets just make the deletions
* quicker.
*/
j++;
}
return j;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_clearlist */
/* Parameters: Nil */
/* */
/* Delete all rules in the current list of rules. There is nothing elegant */
/* about this cleanup: simply free all entries on the list of rules and */
/* clear out the tables used for hashed NAT rule lookups. */
/* ------------------------------------------------------------------------ */
static int nat_clearlist(ifs)
{
int i = 0;
if (n->in_use == 0) {
KFREE(n);
} else {
n->in_flags |= IPN_DELETE;
}
i++;
}
ifs->ifs_nat_masks = 0;
ifs->ifs_rdr_masks = 0;
return i;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_newmap */
/* Returns: int - -1 == error, 0 == success */
/* Parameters: fin(I) - pointer to packet information */
/* nat(I) - pointer to NAT entry */
/* ni(I) - pointer to structure with misc. information needed */
/* to create new NAT entry. */
/* */
/* Given an empty NAT structure, populate it with new information about a */
/* new NAT session, as defined by the matching NAT rule. */
/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
/* to the new IP address for the translation. */
/* ------------------------------------------------------------------------ */
{
int l;
/*
* If it's an outbound packet which doesn't match any existing
* record, then create a new port
*/
l = 0;
/*
* Do a loop until we either run out of entries to try or we find
* a NAT mapping that isn't currently being used. This is done
* because the change to the source is not (usually) being fixed.
*/
do {
port = 0;
if (l == 0) {
/*
* Check to see if there is an existing NAT
* setup for this IP address pair.
*/
}
if (l > 0)
return -1;
}
/*
* map the address block in a 1:1 fashion
*/
!(flags & IPN_TCPUDP)))
return -1;
/*
* map-block - Calculate destination address.
*/
/*
* Calculate destination port.
*/
if ((flags & IPN_TCPUDP) &&
port += MAPBLK_MINPORT;
}
/*
* 0/32 - use the interface's IP address.
*/
if ((l > 0) ||
return -1;
/*
*/
if (l > 0)
return -1;
if ((flags & IPN_TCPUDP) &&
/*
* "ports auto" (without map-block)
*/
return -1;
}
port += MAPBLK_MINPORT;
}
/*
* Standard port translation. Select next port.
*/
}
}
} else {
}
/*
* Here we do a lookup of the connection as seen from
* the outside. If an IP# pair already exists, try
* again. So if you have A->B becomes C->B, you can
* also have D->E become C->E but not D->B causing
* another C->B. Also take protocol and ports into
* account when determining whether a pre-existing
* NAT setup will cause an external conflict where
* this is appropriate.
*/
/*
* Has the search wrapped around and come back to the
* start ?
*/
return -1;
l++;
/* Setup the NAT table */
/*
* The ICMP checksum does not have a pseudo header containing
* the IP addresses
*/
if ((flags & IPN_TCPUDP)) {
}
if (flags & IPN_TCPUDP) {
} else if (flags & IPN_ICMPQUERY) {
}
return 0;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_newrdr */
/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */
/* allow rule to be moved if IPN_ROUNDR is set. */
/* Parameters: fin(I) - pointer to packet information */
/* nat(I) - pointer to NAT entry */
/* ni(I) - pointer to structure with misc. information needed */
/* to create new NAT entry. */
/* */
/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
/* to the new IP address for the translation. */
/* ------------------------------------------------------------------------ */
{
int move;
move = 1;
/*
* If the matching rule has IPN_STICKY set, then we want to have the
* same rule kick in as before. Why would this happen? If you have
* a collection of rdr rules with "round-robin sticky", the current
* packet might match a different one to the previous connection but
* we want the same destination to be used.
*/
(IPN_ROUNDR|IPN_STICKY)) {
move = 0;
}
}
/*
* Otherwise, it's an inbound packet. Most likely, we don't
* want to rewrite source ports and source addresses. Instead,
* we want to rewrite to a fixed internal address and fixed
* internal port.
*/
move = 0;
}
}
move = 0;
} else {
}
}
/*
* 0/32 - use the interface's IP address.
*/
return -1;
/*
*/
/*
* map the address block in a 1:1 fashion
*/
} else {
}
else {
/*
* Whilst not optimized for the case where
* pmin == pmax, the gain is not significant.
*/
} else
}
/*
* When the redirect-to address is set to 0.0.0.0, just
* assume a blank `forwarding' of the packet. We don't
* setup any translation for this either.
*/
return -1;
}
if (flags & IPN_TCPUDP) {
} else if (flags & IPN_ICMPQUERY) {
}
return move;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_new */
/* Returns: nat_t* - NULL == failure to create new NAT structure, */
/* else pointer to new NAT structure */
/* Parameters: fin(I) - pointer to packet information */
/* np(I) - pointer to NAT rule */
/* natsave(I) - pointer to where to store NAT struct pointer */
/* flags(I) - flags describing the current packet */
/* Write Lock: ipf_nat */
/* */
/* Attempts to create a new NAT entry. Does not actually change the packet */
/* in any way. */
/* */
/* This fucntion is in three main parts: (1) deal with creating a new NAT */
/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */
/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
/* and (3) building that structure and putting it into the NAT table(s). */
/* ------------------------------------------------------------------------ */
int direction;
{
int move;
/*
* Trigger automatic call to nat_extraflush() if the
* table has reached capcity specified by hi watermark.
*/
return NULL;
}
move = 1;
nflags &= NAT_FROMRULE;
/* Give me a new nat */
/*
* Try to automatically tune the max # of entries in the
* table allowed to be less than what will cause kmem_alloc()
* to fail and try to eliminate panics due to out of memory
* conditions arising.
*/
printf("ipf_nattable_max reduced to %d\n",
}
return NULL;
}
if (flags & IPN_TCPUDP) {
} else if (flags & IPN_ICMPQUERY) {
/*
* In the ICMP query NAT code, we translate the ICMP id fields
* to make them unique. This is indepedent of the ICMP type
* (e.g. in the unlikely event that a host sends an echo and
* an tstamp request with the same id, both packets will have
*/
/* The icmp_id field is used by the sender to identify the
* process making the icmp request. (the receiver justs
* copies it back in its response). So, it closely matches
* the concept of source port. We overlay sport, so we can
* maximally reuse the existing code.
*/
}
}
/*
* Search the current table for a match.
*/
if (direction == NAT_OUTBOUND) {
/*
* We can now arrange to call this for the same connection
* because ipf_nat_new doesn't protect the code path into
* this function.
*/
goto done;
}
if (move == -1)
goto badnat;
} else {
/*
* NAT_INBOUND is used only for redirects rules
*/
goto done;
}
if (move == -1)
goto badnat;
}
nat_delrdr(np);
nat_delnat(np);
}
}
if (flags & IPN_TCPUDP) {
} else if (flags & IPN_ICMPQUERY) {
dport = 0;
}
/*
* nat_sumd[0] stores adjustment value including both IP address and
* port number changes. nat_sumd[1] stores adjustment value only for
* IP address changes, to be used for pseudo header adjustment, in
* case hardware partial checksum offload is offered.
*/
if (flags & IPN_TCPUDP) {
if (direction == NAT_OUTBOUND)
else
} else
#endif
if (direction == NAT_OUTBOUND)
else
} else {
if (!(flags & IPN_TCPUDPICMP)) {
}
}
goto badnat;
}
goto done;
done:
}
return nat;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_finalise */
/* Returns: int - 0 == sucess, -1 == failure */
/* Parameters: fin(I) - pointer to packet information */
/* nat(I) - pointer to NAT entry */
/* ni(I) - pointer to structure with misc. information needed */
/* to create new NAT entry. */
/* Write Lock: ipf_nat */
/* */
/* This is the tail end of constructing a new NAT entry and is the same */
/* for both IPv4 and IPv6. */
/* ------------------------------------------------------------------------ */
/*ARGSUSED*/
int direction;
{
#ifdef IPFILTER_SYNC
#endif
return -1;
if (ifs->ifs_nat_logging)
}
return 0;
}
/*
* nat_insert failed, so cleanup time...
*/
return -1;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_insert */
/* Returns: int - 0 == sucess, -1 == failure */
/* Parameters: nat(I) - pointer to NAT structure */
/* Write Lock: ipf_nat */
/* */
/* Insert a NAT entry into the hash tables for searching and add it to the */
/* list of active NAT entries. Adjust global counters when complete. */
/* ------------------------------------------------------------------------ */
int rev;
{
/*
* Try and return an error as early as possible, so calculate the hash
* entry numbers first and then proceed.
*/
0xffffffff);
0xffffffff);
} else {
}
return -1;
}
} else {
}
if (ifs->ifs_nat_instances)
if (*natp)
if (*natp)
return 0;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_icmperrorlookup */
/* Returns: nat_t* - point to matching NAT structure */
/* Parameters: fin(I) - pointer to packet information */
/* */
/* Check if the ICMP error message is related to an existing TCP, UDP or */
/* ICMP query nat entry. It is assumed that the packet is already of the */
/* the required length. */
/* ------------------------------------------------------------------------ */
int dir;
{
u_int p;
/*
* Does it at least have the return (basic) IP header ?
* Only a basic IP header (no options) should be with an ICMP error
* header. Also, if it's not an error type, then return.
*/
return NULL;
/*
* Check packet size
*/
return NULL;
/*
* Is the buffer big enough for all of it ? It's the size of the IP
* header claimed in the encapsulated part which is of concern. It
* may be too big to be in this buffer but not so big that it's
* outside the ICMP packet, leading to TCP deref's causing problems.
* This is possible because we don't know how big oip_hl is when we
* do the pullup early in fr_check() and thus can't gaurantee it is
* all here now.
*/
#ifdef _KERNEL
{
mb_t *m;
# if defined(MENTAT)
return NULL;
# else
return NULL;
# endif
}
#endif
return NULL;
if (p == IPPROTO_TCP)
else if (p == IPPROTO_UDP)
else if (p == IPPROTO_ICMP) {
/* see if this is related to an ICMP query */
/*
* NOTE : dir refers to the direction of the original
* ip packet. By definition the icmp error
* message flows in the opposite direction.
*/
if (dir == NAT_INBOUND)
else
return nat;
}
}
if (flags & IPN_TCPUDP) {
return NULL;
if (dir == NAT_INBOUND) {
} else {
}
return nat;
}
if (dir == NAT_INBOUND)
else
}
/* ------------------------------------------------------------------------ */
/* Function: nat_icmperror */
/* Returns: nat_t* - point to matching NAT structure */
/* Parameters: fin(I) - pointer to packet information */
/* nflags(I) - NAT flags for this packet */
/* */
/* Fix up an ICMP packet which is an error message for an existing NAT */
/* session. This will correct both packet header data and checksums. */
/* */
/* This should *ONLY* be used for incoming ICMP error packets to make sure */
/* a NAT'd ICMP packet gets correctly recognised. */
/* ------------------------------------------------------------------------ */
int dir;
{
int dlen;
return NULL;
/*
* nat_icmperrorlookup() looks up nat entry associated with the
* offending IP packet and returns pointer to the entry, or NULL
* if packet wasn't natted or for `defective' packets.
*/
return NULL;
sumd2 = 0;
*nflags = IPN_ICMPERR;
/*
* Need to adjust ICMP header to include the real IP#'s and
* port #'s. There are three steps required.
*
* Step 1
* Fix the IP addresses in the offending IP packet and update
* ip header checksum to compensate for the change.
*
* No update needed here for icmp_cksum because the ICMP checksum
* is calculated over the complete ICMP packet, which includes the
* changed oip IP addresses and oip->ip_sum. These two changes
* cancel each other out (if the delta for the IP address is x,
* then the delta for ip_sum is minus x).
*/
} else {
}
/*
* Step 2
* Perform other adjustments based on protocol of offending packet.
*/
case IPPROTO_TCP :
case IPPROTO_UDP :
/*
* based on the NAT specification.
*
* Advance notice : Now it becomes complicated :-)
*
* Since the port and IP addresse fields are both part
* we need to adjust that checksum as well.
*
* may not be present. We must check to see if the
* length of the data portion is big enough to hold
* the checksum. In the UDP case, a test to determine
* if the checksum is even set is also required.
*
* Any changes to an IP address, port or checksum within
* the ICMP packet requires a change to icmp_cksum.
*
* Be extremely careful here ... The change is dependent
*
* compensate for checksum modification resulting from
* IP address change only. Port change and resulting
* data checksum adjustments cancel each other out.
*
* compensate for port change only. The IP address
* change does not modify anything else in this case.
*/
psum1 = 0;
psum2 = 0;
psumd = 0;
/*
* Translate the source port.
*/
/*
* Translate the destination port.
*/
}
/*
* TCP checksum present.
*
* Adjust data checksum and icmp checksum to
* compensate for any IP address change.
*/
/*
* Also make data checksum adjustment to
* compensate for any port change.
*/
}
/*
* The UDP checksum is present and set.
*
* Adjust data checksum and icmp checksum to
* compensate for any IP address change.
*/
/*
* Also make data checksum adjustment to
* compensate for any port change.
*/
}
} else {
/*
* Data checksum was not present.
*
* Compensate for any port change.
*/
}
break;
case IPPROTO_ICMP :
(dlen >= 8)) {
/*
* Fix ICMP checksum (of the offening ICMP
* query packet) to compensate the change
* in the ICMP id of the offending ICMP
* packet.
*
* Since you modify orgicmp->icmp_id with
* a delta (say x) and you compensate that
* in origicmp->icmp_cksum with a delta
* minus x, you don't have to adjust the
* overall icmp->icmp_cksum
*/
} /* nat_dir can't be NAT_INBOUND for icmp queries */
break;
default :
break;
} /* switch (oip->ip_p) */
/*
* Step 3
* Make the adjustments to icmp checksum.
*/
if (sumd2 != 0) {
}
return nat;
}
/*
* NB: these lookups don't lock access to the list, it assumed that it has
* already been done!
*/
/* ------------------------------------------------------------------------ */
/* Function: nat_inlookup */
/* Returns: nat_t* - NULL == no match, */
/* else pointer to matching NAT entry */
/* Parameters: fin(I) - pointer to packet information */
/* flags(I) - NAT flags for this packet */
/* p(I) - protocol for this packet */
/* src(I) - source IP address */
/* mapdst(I) - destination IP address */
/* */
/* we're looking for a table entry, based on the destination address. */
/* */
/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */
/* */
/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */
/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */
/* */
/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */
/* the packet is of said protocol */
/* ------------------------------------------------------------------------ */
{
int nflags;
void *ifp;
else
sport = 0;
dport = 0;
switch (p)
{
case IPPROTO_TCP :
case IPPROTO_UDP :
break;
case IPPROTO_ICMP :
if (flags & IPN_ICMPERR)
else
break;
default :
break;
}
goto find_in_wild_ports;
continue;
(((p == 0) &&
switch (p)
{
#if 0
case IPPROTO_GRE :
continue;
break;
#endif
case IPPROTO_ICMP :
if ((flags & IPN_ICMPERR) != 0) {
continue;
} else {
continue;
}
break;
case IPPROTO_TCP :
case IPPROTO_UDP :
continue;
continue;
break;
default :
break;
}
continue;
return nat;
}
}
/*
* So if we didn't find it but there are wildcard members in the hash
* table, go back and look for them. We do this search and update here
* because it is modifying the NAT table and we want to do this only
* for the first packet that matches. The exception, of course, is
* for "dummy" (FI_IGNORE) lookups.
*/
return NULL;
return NULL;
continue;
continue;
continue;
continue;
NAT_INBOUND) == 1) {
break;
break;
} else {
}
break;
}
}
return nat;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_tabmove */
/* Returns: Nil */
/* Parameters: nat(I) - pointer to NAT structure */
/* Write Lock: ipf_nat */
/* */
/* original was placed in the table without hashing on the ports and we now */
/* want to include hashing on port numbers. */
/* ------------------------------------------------------------------------ */
{
return;
/*
* Remove the NAT entry from the old location
*/
/*
* Add into the NAT table in the new position
*/
if (*natp)
if (*natp)
}
/* ------------------------------------------------------------------------ */
/* Function: nat_outlookup */
/* Returns: nat_t* - NULL == no match, */
/* else pointer to matching NAT entry */
/* Parameters: fin(I) - pointer to packet information */
/* flags(I) - NAT flags for this packet */
/* p(I) - protocol for this packet */
/* src(I) - source IP address */
/* dst(I) - destination IP address */
/* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */
/* */
/* we're looking for a table entry, based on the source address. */
/* */
/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */
/* */
/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */
/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */
/* */
/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */
/* the packet is of said protocol */
/* ------------------------------------------------------------------------ */
{
int nflags;
void *ifp;
sport = 0;
dport = 0;
switch (p)
{
case IPPROTO_TCP :
case IPPROTO_UDP :
break;
case IPPROTO_ICMP :
if (flags & IPN_ICMPERR)
else
break;
default :
break;
}
goto find_out_wild_ports;
continue;
switch (p)
{
#if 0
case IPPROTO_GRE :
continue;
break;
#endif
case IPPROTO_TCP :
case IPPROTO_UDP :
continue;
continue;
break;
default :
break;
}
continue;
return nat;
}
}
/*
* So if we didn't find it but there are wildcard members in the hash
* table, go back and look for them. We do this search and update here
* because it is modifying the NAT table and we want to do this only
* for the first packet that matches. The exception, of course, is
* for "dummy" (FI_IGNORE) lookups.
*/
return NULL;
return NULL;
continue;
continue;
continue;
continue;
NAT_OUTBOUND) == 1) {
break;
break;
} else {
}
if (nat->nat_outport == 0)
break;
}
}
return nat;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_lookupredir */
/* Returns: nat_t* - NULL == no match, */
/* else pointer to matching NAT entry */
/* Parameters: np(I) - pointer to description of packet to find NAT table */
/* entry for. */
/* */
/* Lookup the NAT tables to search for a matching redirect */
/* ------------------------------------------------------------------------ */
{
} else {
}
/*
* We can do two sorts of lookups:
* - IPN_IN: we have the `real' and `out' address, look for `in'.
* - default: we have the `in' and `out' address, look for `real'.
*/
}
} else {
/*
* If nl_inip is non null, this is a lookup based on the real
* ip address. Else, we use the fake.
*/
}
}
}
}
return nat;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_match */
/* Returns: int - 0 == no match, 1 == match */
/* Parameters: fin(I) - pointer to packet information */
/* np(I) - pointer to NAT rule */
/* */
/* Pull the matching of a packet against a NAT rule out of that complex */
/* loop inside fr_checknatin() and lay it out properly in its own function. */
/* ------------------------------------------------------------------------ */
{
return 0;
return 0;
return 0;
return 0;
return 0;
} else {
return 0;
return 0;
return 0;
}
return 0;
return 1;
}
}
/* ------------------------------------------------------------------------ */
/* Function: nat_update */
/* Returns: Nil */
/* Parameters: nat(I) - pointer to NAT structure */
/* np(I) - pointer to NAT rule */
/* */
/* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */
/* called with fin_rev updated - i.e. after calling nat_proto(). */
/* ------------------------------------------------------------------------ */
{
/*
* We allow over-riding of NAT timeouts from NAT rules, even for
* TCP, however, if it is TCP and there is no rule timeout set,
* then do not update the timeout here.
*/
else
} else {
else
}
}
}
/* ------------------------------------------------------------------------ */
/* Function: fr_checknatout */
/* Returns: int - -1 == packet failed NAT checks so block it, */
/* 0 == no packet translation occurred, */
/* 1 == packet was successfully translated. */
/* Parameters: fin(I) - pointer to packet information */
/* passp(I) - pointer to filtering result flags */
/* */
/* Check to see if an outcoming packet should be changed. ICMP packets are */
/* first checked to see if they match an existing entry (if an error), */
/* otherwise a search of the current NAT table is made. If neither results */
/* in a match then a search for a matching NAT rule is made. Create a new */
/* NAT entry if a we matched a NAT rule. Lastly, actually change the */
/* packet header(s) as required. */
/* ------------------------------------------------------------------------ */
{
int natadd = 1;
return 0;
natfailed = 0;
{
case IPPROTO_TCP :
break;
case IPPROTO_UDP :
break;
case IPPROTO_ICMP :
/*
* This is an incoming packet, so the destination is
* the icmp_id and the source port equals 0
*/
break;
default :
break;
}
if ((nflags & IPN_TCPUDP))
}
/*EMPTY*/;
natadd = 0;
} else {
/*
* If there is no current entry in the nat table for this IP#,
* create one for it (if there is a matching rule).
*/
msk = 0xffffffff;
{
continue;
continue;
continue;
continue;
continue;
continue;
continue;
continue;
continue;
}
NAT_OUTBOUND))) {
break;
} else
natfailed = -1;
}
while (nmsk) {
msk <<= 1;
if (nmsk & 0x80000000)
break;
nmsk <<= 1;
}
if (nmsk != 0) {
nmsk <<= 1;
goto maskloop;
}
}
}
if (rval == 1) {
}
} else
if (rval == -1) {
}
return rval;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natout */
/* Returns: int - -1 == packet failed NAT checks so block it, */
/* 1 == packet was successfully translated. */
/* Parameters: fin(I) - pointer to packet information */
/* nat(I) - pointer to NAT structure */
/* natadd(I) - flag indicating if it is safe to add frag cache */
/* nflags(I) - NAT flags set for this packet */
/* */
/* Translate a packet coming "out" on an interface. */
/* ------------------------------------------------------------------------ */
int natadd;
{
int i;
else
#endif
/*
* Fix up checksums, not by recalculating them, but
* simply computing adjustments.
* This is only done for STREAMS based IP implementations where the
* checksum has already been calculated by IP. In all other cases,
* IPFilter is called before the checksum needs calculating so there
* is no call to modify whatever is in the header now.
*/
if (nflags == IPN_ICMPERR) {
}
defined(linux) || defined(BRIDGE_IPF)
else {
/*
* Strictly speaking, this isn't necessary on BSD
* kernels because they do checksum calculation after
* this code has run BUT if ipfilter is being used
* to do NAT as a bridge, that code doesn't exist.
*/
nat->nat_ipsumd);
else
nat->nat_ipsumd);
}
#endif
}
}
}
}
/*
* The above comments do not hold for layer 4 (or higher) checksums...
*/
if (nflags & IPN_TCPUDP &&
else
else
}
#ifdef IPFILTER_SYNC
#endif
/* ------------------------------------------------------------- */
/* A few quick notes: */
/* Following are test conditions prior to calling the */
/* appr_check routine. */
/* */
/* with a redirect rule, we attempt to match the packet's */
/* source port against in_dport, otherwise we'd compare the */
/* packet's destination. */
/* ------------------------------------------------------------- */
if (i == 0)
i = 1;
} else
i = 1;
return i;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_checknatin */
/* Returns: int - -1 == packet failed NAT checks so block it, */
/* 0 == no packet translation occurred, */
/* 1 == packet was successfully translated. */
/* Parameters: fin(I) - pointer to packet information */
/* passp(I) - pointer to filtering result flags */
/* */
/* Check to see if an incoming packet should be changed. ICMP packets are */
/* first checked to see if they match an existing entry (if an error), */
/* otherwise a search of the current NAT table is made. If neither results */
/* in a match then a search for a matching NAT rule is made. Create a new */
/* NAT entry if a we matched a NAT rule. Lastly, actually change the */
/* packet header(s) as required. */
/* ------------------------------------------------------------------------ */
{
return 0;
dport = 0;
natadd = 1;
nflags = 0;
natfailed = 0;
{
case IPPROTO_TCP :
break;
case IPPROTO_UDP :
break;
case IPPROTO_ICMP :
/*
* This is an incoming packet, so the destination is
* the icmp_id and the source port equals 0
*/
} break;
default :
break;
}
if ((nflags & IPN_TCPUDP)) {
}
}
/*EMPTY*/;
natadd = 0;
} else {
msk = 0xffffffff;
/*
* If there is no current entry in the nat table for this IP#,
* create one for it (if there is a matching rule).
*/
continue;
continue;
continue;
continue;
continue;
} else {
continue;
continue;
}
continue;
}
}
break;
} else
natfailed = -1;
}
while (rmsk) {
msk <<= 1;
if (rmsk & 0x80000000)
break;
rmsk <<= 1;
}
if (rmsk != 0) {
rmsk <<= 1;
goto maskloop;
}
}
}
if (rval == 1) {
}
} else
if (rval == -1) {
}
return rval;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natin */
/* Returns: int - -1 == packet failed NAT checks so block it, */
/* 1 == packet was successfully translated. */
/* Parameters: fin(I) - pointer to packet information */
/* nat(I) - pointer to NAT structure */
/* natadd(I) - flag indicating if it is safe to add frag cache */
/* nflags(I) - NAT flags set for this packet */
/* Locks Held: ipf_nat (READ) */
/* */
/* Translate a packet coming "in" on an interface. */
/* ------------------------------------------------------------------------ */
int natadd;
{
int i;
else
#endif
/* ------------------------------------------------------------- */
/* A few quick notes: */
/* Following are test conditions prior to calling the */
/* appr_check routine. */
/* */
/* with a map rule, we attempt to match the packet's */
/* source port against in_dport, otherwise we'd compare the */
/* packet's destination. */
/* ------------------------------------------------------------- */
if (i == -1) {
return -1;
}
}
}
#ifdef IPFILTER_SYNC
#endif
if (nflags & IPN_TCPUDP)
/*
* Fix up checksums, not by recalculating them, but
* simply computing adjustments.
* Why only do this for some platforms on inbound packets ?
* Because for those that it is done, IP processing is yet to happen
* and so the IPv4 header checksum has not yet been evaluated.
* Perhaps it should always be done for the benefit of things like
* fast forwarding (so that it doesn't need to be recomputed) but with
* header checksum offloading, perhaps it is a moot point.
*/
else
#endif
}
}
}
if (nflags & IPN_TCPUDP &&
else
}
} else
#endif
/*
* Inbound packets always need to have their address adjusted in case
* code following this validates it.
*/
else
}
return 1;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_proto */
/* Returns: u_short* - pointer to transport header checksum to update, */
/* NULL if the transport protocol is not recognised */
/* as needing a checksum update. */
/* Parameters: fin(I) - pointer to packet information */
/* nat(I) - pointer to NAT structure */
/* nflags(I) - NAT flags set for this packet */
/* */
/* Return the pointer to the checksum field for each protocol so understood.*/
/* If support for making other changes to a protocol header is required, */
/* that is not strictly 'address' translation, such as clamping the MSS in */
/* TCP down to a specific value, then do it from here. */
/* ------------------------------------------------------------------------ */
{
} else {
}
{
case IPPROTO_TCP :
/*
* Do a MSS CLAMPING on a SYN packet,
* only deal IPv4 for now.
*/
break;
case IPPROTO_UDP :
break;
case IPPROTO_ICMP :
if ((nflags & IPN_ICMPQUERY) != 0) {
if (icmp->icmp_cksum != 0)
}
break;
}
return csump;
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natunload */
/* Returns: Nil */
/* Parameters: Nil */
/* */
/* Free all memory used by NAT structures allocated at runtime. */
/* ------------------------------------------------------------------------ */
void fr_natunload(ifs)
{
(void) nat_clearlist(ifs);
(void) nat_flushtable(ifs);
/*
* Proxy timeout queues are not cleaned here because although they
* exist on the NAT list, appr_unload is called after fr_natunload
* and the proxies actually are responsible for them being created.
* Should the proxy timeouts have their own list? There's no real
* justification as this is the only complication.
*/
(fr_deletetimeoutqueue(ifq) == 0))
}
}
}
}
}
}
}
}
ifs->ifs_fr_nat_maxbucket = 0;
ifs->ifs_fr_nat_init = 0;
}
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natexpire */
/* Returns: Nil */
/* Parameters: Nil */
/* */
/* Check all of the timeout queues for entries at the top which need to be */
/* expired. */
/* ------------------------------------------------------------------------ */
void fr_natexpire(ifs)
{
int i;
SPL_INT(s);
SPL_NET(s);
break;
}
}
break;
}
}
}
}
if (ifs->ifs_nat_doflush != 0) {
ifs->ifs_nat_doflush = 0;
}
SPL_X(s);
}
/* ------------------------------------------------------------------------ */
/* Function: fr_nataddrsync */
/* Returns: Nil */
/* Parameters: ifp(I) - pointer to network interface */
/* addr(I) - pointer to new network address */
/* */
/* Walk through all of the currently active NAT sessions, looking for those */
/* which need to have their translated address updated (where the interface */
/* matches the one passed in) and change it, recalculating the checksum sum */
/* difference too. */
/* ------------------------------------------------------------------------ */
void *ifp;
{
SPL_INT(s);
if (ifs->ifs_fr_running <= 0)
return;
SPL_NET(s);
if (ifs->ifs_fr_running <= 0) {
return;
}
/*
* Change IP addresses for NAT sessions for any protocol except TCP
* since it will break the TCP connection anyway. The only rules
* which will get changed are those which are "map ... -> 0/32",
* where the rule specifies the address is taken from the interface.
*/
continue;
continue;
/*
* Change the map-to address to be the same as the
* new one.
*/
/*
* Change the map-to address to be the same as the
* new one.
*/
} else {
continue;
}
continue;
/*
* Readjust the checksum adjustment to take into
* account the new IP#.
*/
/* XXX - dont change for TCP when solaris does
* hardware checksumming.
*/
}
SPL_X(s);
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natifpsync */
/* Returns: Nil */
/* Parameters: action(I) - how we are syncing */
/* ifp(I) - pointer to network interface */
/* name(I) - name of interface to sync to */
/* */
/* This function is used to resync the mapping of interface names and their */
/* respective 'pointers'. For "action == IPFSYNC_RESYNC", resync all */
/* interfaces by doing a new lookup of name to 'pointer'. For "action == */
/* IPFSYNC_NEWIFP", treat ifp as the new pointer value associated with */
/* "name" and for "action == IPFSYNC_OLDIFP", ifp is a pointer for which */
/* there is no longer any interface associated with it. */
/* ------------------------------------------------------------------------ */
int action;
void *ifp;
char *name;
{
int s;
#endif
ipnat_t *n;
if (ifs->ifs_fr_running <= 0)
return;
SPL_NET(s);
if (ifs->ifs_fr_running <= 0) {
return;
}
switch (action)
{
case IPFSYNC_RESYNC :
}
}
}
n->in_ifps[0] == (void *)-1) {
n->in_ifps[0] =
}
n->in_ifps[1] =
}
}
break;
case IPFSYNC_NEWIFP :
sizeof(nat->nat_ifnames[0])))
}
sizeof(n->in_ifnames[0])))
sizeof(n->in_ifnames[1])))
}
break;
case IPFSYNC_OLDIFP :
}
n->in_ifps[0] = (void *)-1;
}
break;
}
SPL_X(s);
}
/* ------------------------------------------------------------------------ */
/* Function: nat_icmpquerytype4 */
/* Returns: int - 1 == success, 0 == failure */
/* Parameters: icmptype(I) - ICMP type number */
/* */
/* not. */
/* ------------------------------------------------------------------------ */
int icmptype;
{
/*
* For the ICMP query NAT code, it is essential that both the query
* and the reply match on the NAT rule. Because the NAT structure
* does not keep track of the icmptype, and a single NAT structure
* is used for all icmp types with the same src, dest and id, we
* simply define the replies as queries as well. The funny thing is,
* altough it seems silly to call a reply a query, this is exactly
* as it is defined in the IPv4 specification
*/
switch (icmptype)
{
case ICMP_ECHOREPLY:
case ICMP_ECHO:
/* route aedvertisement/solliciation is currently unsupported: */
/* it would require rewriting the ICMP data section */
case ICMP_TSTAMP:
case ICMP_TSTAMPREPLY:
case ICMP_IREQ:
case ICMP_IREQREPLY:
case ICMP_MASKREQ:
case ICMP_MASKREPLY:
return 1;
default:
return 0;
}
}
/* ------------------------------------------------------------------------ */
/* Function: nat_log */
/* Returns: Nil */
/* Parameters: nat(I) - pointer to NAT structure */
/* type(I) - type of log entry to create */
/* */
/* Creates a NAT log entry. */
/* ------------------------------------------------------------------------ */
{
#ifdef IPFILTER_LOG
# ifndef LARGE_NAT
int rulen;
# endif
void *items[1];
int types[1];
# ifndef LARGE_NAT
break;
}
}
# endif
types[0] = 0;
#endif
}
#if defined(__OpenBSD__)
/* ------------------------------------------------------------------------ */
/* Function: nat_ifdetach */
/* Returns: Nil */
/* Parameters: ifp(I) - pointer to network interface */
/* */
/* Compatibility interface for OpenBSD to trigger the correct updating of */
/* interface references within IPFilter. */
/* ------------------------------------------------------------------------ */
void *ifp;
{
return;
}
#endif
/* ------------------------------------------------------------------------ */
/* Function: fr_ipnatderef */
/* Returns: Nil */
/* Parameters: isp(I) - pointer to pointer to NAT rule */
/* Write Locks: ipf_nat */
/* */
/* ------------------------------------------------------------------------ */
{
#ifdef notdef
#if SOLARIS
#endif
#endif
}
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natderef */
/* Returns: Nil */
/* Parameters: isp(I) - pointer to pointer to NAT table entry */
/* */
/* Decrement the reference counter for this NAT table entry and free it if */
/* there are no more things using it. */
/* */
/* IF nat_ref == 1 when this function is called, then we have an orphan nat */
/* structure *because* it only gets called on paths _after_ nat_ref has been*/
/* incremented. If nat_ref == 1 then we shouldn't decrement it here */
/* because nat_delete() will do that and send nat_ref to -1. */
/* */
/* Holding the lock on nat_lock is required to serialise nat_delete() being */
/* called from a NAT flush ioctl with a deref happening because of a packet.*/
/* ------------------------------------------------------------------------ */
{
return;
}
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natclone */
/* Returns: ipstate_t* - NULL == cloning failed, */
/* else pointer to new state structure */
/* Parameters: fin(I) - pointer to packet information */
/* is(I) - pointer to master state structure */
/* Write Lock: ipf_nat */
/* */
/* Create a "duplcate" state table entry from the master. */
/* ------------------------------------------------------------------------ */
{
return NULL;
/*
* Initialize all these so that nat_delete() doesn't cause a crash.
*/
return NULL;
}
if (ifs->ifs_nat_logging)
}
}
/*
* Because the clone is created outside the normal loop of things and
* TCP has special needs in terms of state, initialise the timeout
* state of the new NAT from here.
*/
}
#ifdef IPFILTER_SYNC
#endif
if (ifs->ifs_nat_logging)
return clone;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_wildok */
/* Returns: int - 1 == packet's ports match wildcards */
/* 0 == packet's ports don't match wildcards */
/* Parameters: nat(I) - NAT entry */
/* sport(I) - source port */
/* dport(I) - destination port */
/* flags(I) - wildcard flags */
/* dir(I) - packet direction */
/* */
/* Use NAT entry and packet direction to determine which combination of */
/* wildcard flags should be used. */
/* ------------------------------------------------------------------------ */
int sport;
int dport;
int flags;
int dir;
{
/*
* When called by dir is set to
* nat_inlookup NAT_INBOUND (0)
* nat_outlookup NAT_OUTBOUND (1)
*
* We simply combine the packet's direction in dir with the original
* "intended" direction of that NAT entry in nat->nat_dir to decide
* which combination of wildcard flags to allow.
*/
{
case 3: /* outbound packet / outbound entry */
(flags & SI_W_SPORT)) &&
(flags & SI_W_DPORT)))
return 1;
break;
case 2: /* outbound packet / inbound entry */
(flags & SI_W_DPORT)) &&
(flags & SI_W_SPORT)))
return 1;
break;
case 1: /* inbound packet / outbound entry */
(flags & SI_W_DPORT)) &&
(flags & SI_W_SPORT)))
return 1;
break;
case 0: /* inbound packet / inbound entry */
(flags & SI_W_SPORT)) &&
(flags & SI_W_DPORT)))
return 1;
break;
default:
break;
}
return(0);
}
/* ------------------------------------------------------------------------ */
/* Function: nat_mssclamp */
/* Returns: Nil */
/* Parameters: tcp(I) - pointer to TCP header */
/* maxmss(I) - value to clamp the TCP MSS to */
/* csump(I) - pointer to TCP checksum */
/* */
/* Check for MSS option and clamp it if necessary. If found and changed, */
/* then the TCP header checksum will be updated to reflect the change in */
/* the MSS. */
/* ------------------------------------------------------------------------ */
{
if (opt == TCPOPT_EOL)
break;
else if (opt == TCPOPT_NOP) {
cp++;
continue;
}
break;
break;
switch (opt)
{
case TCPOPT_MAXSEG:
if (advance != 4)
break;
}
break;
default:
/* ignore unknown options */
break;
}
}
}
}
/* ------------------------------------------------------------------------ */
/* Function: fr_setnatqueue */
/* Returns: Nil */
/* Parameters: nat(I)- pointer to NAT structure */
/* rev(I) - forward(0) or reverse(1) direction */
/* Locks: ipf_nat (read or write) */
/* */
/* Put the NAT entry on its default queue entry, using rev as a helped in */
/* determining which queue it should be placed on. */
/* ------------------------------------------------------------------------ */
int rev;
{
else
{
case IPPROTO_UDP :
break;
case IPPROTO_ICMP :
break;
case IPPROTO_TCP :
break;
default :
break;
}
}
/*
* If it's currently on a timeout queue, move it from one queue to
* another, else put it on the end of the newly determined queue.
*/
else
return;
}
/* Function: nat_getnext */
/* Returns: int - 0 == ok, else error */
/* Parameters: t(I) - pointer to ipftoken structure */
/* itp(I) - pointer to ipfgeniter_t structure */
/* */
/* copy it out to the storage space pointed to by itp_data. The next item */
/* in the list to look at is put back in the ipftoken struture. */
/* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/
/* ipf_freetoken will call a deref function for us and we dont want to call */
/* that twice (second time would be in the second switch statement below. */
/* ------------------------------------------------------------------------ */
ipftoken_t *t;
{
int error = 0;
{
case IPFGENITER_HOSTMAP :
} else {
}
t->ipt_alive = 0;
ipf_unlinktoken(t, ifs);
KFREE(t);
} else {
/*MUTEX_ENTER(&nexthm->hm_lock);*/
/*MUTEX_EXIT(&nextipnat->hm_lock);*/
}
} else {
ipf_freetoken(t, ifs);
}
break;
case IPFGENITER_IPNAT :
} else {
}
t->ipt_alive = 0;
ipf_unlinktoken(t, ifs);
KFREE(t);
} else {
/* MUTEX_ENTER(&nextipnat->in_lock); */
/* MUTEX_EXIT(&nextipnat->in_lock); */
}
} else {
ipf_freetoken(t, ifs);
}
break;
case IPFGENITER_NAT :
} else {
}
t->ipt_alive = 0;
ipf_unlinktoken(t, ifs);
KFREE(t);
} else {
}
} else {
ipf_freetoken(t, ifs);
}
break;
}
{
case IPFGENITER_HOSTMAP :
}
if (error != 0)
break;
case IPFGENITER_IPNAT :
if (error != 0)
break;
case IPFGENITER_NAT :
if (error != 0)
break;
}
return error;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_iterator */
/* Returns: int - 0 == ok, else error */
/* Parameters: token(I) - pointer to ipftoken structure */
/* itp(I) - pointer to ipfgeniter_t structure */
/* */
/* This function acts as a handler for the SIOCGENITER ioctls that use a */
/* generic structure to iterate through a list. There are three different */
/* linked lists of NAT related information to go through: NAT rules, active */
/* NAT mappings and the NAT fragment cache. */
/* ------------------------------------------------------------------------ */
{
int error;
return EFAULT;
{
case IPFGENITER_HOSTMAP :
case IPFGENITER_IPNAT :
case IPFGENITER_NAT :
break;
case IPFGENITER_NATFRAG :
break;
default :
break;
}
return error;
}
/* -------------------------------------------------------------------- */
/* Function: nat_earlydrop */
/* Parameters: ifq - pointer to queue with entries to be processed */
/* maxidle - entry must be idle this long to be dropped */
/* ifs - ipf stack instance */
/* */
/* Function is invoked from nat_extraflush() only. Removes entries */
/* form specified timeout queue, based on how long they've sat idle, */
/* without waiting for it to happen on its own. */
/* -------------------------------------------------------------------- */
int maxidle;
{
unsigned int dropped;
int droptick;
return (0);
dropped = 0;
/*
* Determine the tick representing the idle time we're interested
* in. If an entry exists in the queue, and it was touched before
* that tick, then it's been idle longer than maxidle ... remove it.
*/
dropped++;
}
return (dropped);
}
/* --------------------------------------------------------------------- */
/* Function: nat_flushclosing */
/* Returns: int - number of NAT entries deleted */
/* Parameters: stateval(I) - State at which to start removing entries */
/* ifs - ipf stack instance */
/* */
/* Remove nat table entries for TCP connections which are in the process */
/* of closing, and are in (or "beyond") state specified by 'stateval'. */
/* --------------------------------------------------------------------- */
int stateval;
{
int dropped;
dropped = 0;
/*
* Start by deleting any entries in specific timeout queues.
*/
}
/*
* Next, look through user defined queues for closing entries.
*/
continue;
dropped++;
}
}
}
return (dropped);
}
/* --------------------------------------------------------------------- */
/* Function: nat_extraflush */
/* Returns: int - number of NAT entries deleted */
/* Parameters: which(I) - how to flush the active NAT table */
/* ifs - ipf stack instance */
/* Write Locks: ipf_nat */
/* */
/* Flush nat tables. Three actions currently defined: */
/* */
/* which == 0 : Flush all nat table entries. */
/* */
/* which == 1 : Flush entries with TCP connections which have started */
/* to close on both ends. */
/* */
/* which == 2 : First, flush entries which are "almost" closed. If that */
/* does not take us below specified threshold in the table, */
/* we want to flush entries with TCP connections which have */
/* been idle for a long time. Start with connections idle */
/* over 12 hours, and then work backwards in half hour */
/* increments to at most 30 minutes idle, and finally work */
/* back in 30 second increments to at most 30 seconds. */
/* --------------------------------------------------------------------- */
int which;
{
SPL_INT(s);
removed = 0;
SPL_NET(s);
switch (which)
{
case 0:
removed++;
}
break;
case 1:
break;
case 2:
/*
* Be sure we haven't done this in the last 10 seconds.
*/
IPF_TTLVAL(10))
break;
/*
* Determine initial threshold for minimum idle time based on
* how long ipfilter has been running. Ipfilter needs to have
* been up as long as the smallest interval to continue on.
*
* Minimum idle times stored in idletime_tab and indexed by
* idle_idx. Start at upper end of array and work backwards.
*
* Once the index is found, set the initial idle time to the
* first interval before the current ipfilter run time.
*/
break; /* switch */
} else {
while ((idle_idx > 0) &&
idle_idx--;
idletime_tab[idle_idx]) *
}
while ((idle_idx >= 0) &&
/*
* Start with appropriate timeout queue.
*/
removed += nat_earlydrop(
/*
* Make sure we haven't already deleted enough
* entries before checking the user defined queues.
*/
if (NAT_TAB_WATER_LEVEL(ifs) <=
break;
/*
* Next, look through the user defined queues.
*/
}
/*
* Adjust the granularity of idle time.
*
* If we reach an interval boundary, we need to
* either adjust the idle time accordingly or exit
* the loop altogether (if this is very last check).
*/
if (idle_idx != 0) {
idle_idx--;
} else {
break; /* while */
}
}
}
break;
default:
break;
}
SPL_X(s);
return (removed);
}